001    package ps2.test;
002    
003    import ps2.*;
004    import static ps2.test.TestValues.TOLERANCE;
005    
006    import junit.framework.*;
007    
008    
009    /**
010     * Unit tests for the GeoPoint class.
011     **/
012    public class GeoPointTest extends TestCase {
013    
014        /** A GeoPoint object with the same data as eq2 **/
015        private final GeoPoint eq1;
016    
017        /** A GeoPoint object with the same data as eq1 **/
018        private final GeoPoint eq2;
019    
020        /** A GeoPoint object with different data than eq1 **/
021        private final GeoPoint diff;
022    
023    
024        /** A GeoPoint representing the center of Seattle **/
025        private final GeoPoint p1;
026    
027    
028        /** A GeoPoint due north of Seattle **/
029        private final GeoPoint north;
030    
031        /** Heading, in degrees,  between p1 and north **/
032        private final double nHeading;
033    
034        /** Distance, in miles,  between p1 and north **/
035        private final double nDist;
036    
037    
038        /** A GeoPodouble due south of Seattle **/
039        private final GeoPoint south;
040    
041        /** Heading, in degrees,  between p1 and south **/
042        private final double sHeading;
043    
044        /** Distance, in miles,  between p1 and south **/
045        private final double sDist;
046    
047    
048        /** A GeoPoint due east of Seattle **/
049        private final GeoPoint east;
050    
051        /** Heading, in degrees,  between p1 and east **/
052        private final double eHeading;
053    
054        /** Distance, in miles,  between p1 and east **/
055        private final double eDist;
056    
057    
058        /** A GeoPoint due west of Seattle **/
059        private final GeoPoint west;
060    
061        /** Heading, in degrees,  between p1 and west **/
062        private final double wHeading;
063    
064        /** Distance, in miles,  between p1 and west **/
065        private final double wDist;
066    
067    
068        /** A GeoPoint due north east of Seattle **/
069        private final GeoPoint northEast;
070    
071        /** Heading, in degrees,  between p1 and north east **/
072        private final double neHeading;
073    
074        /** Distance, in miles,  between p1 and north east **/
075        private final double neDist;
076    
077    
078        /** A GeoPoint due south west of Seattle **/
079        private final GeoPoint southWest;
080    
081        /** Heading, in degrees,  between p1 and south west **/
082        private final double swHeading;
083    
084        /** Distance, in miles,  between p1 and south west **/
085        private final double swDist;
086    
087    
088        public GeoPointTest(String name) {
089            super(name);
090    
091            int eqLat = locationToInt(47, 36, 35);
092            int eqLong = locationToInt(-122, 19, 59);
093            eq1 = new GeoPoint(eqLat, eqLong);
094            eq2 = new GeoPoint(eqLat, eqLong);
095            diff = new GeoPoint(eqLat + 50, eqLong + 50);
096    
097    
098            // Center of Seattle:
099            p1 = new GeoPoint(locationToInt(47, 36, 35),
100                              locationToInt(-122, 19, 59));
101            // due north
102            north = new GeoPoint(locationToInt(47, 42, 35),
103                                 locationToInt(-122, 19, 59));
104            nHeading = 0;
105            nDist = 6.904;
106    
107            // due south
108            south = new GeoPoint(locationToInt(47, 30, 35),
109                                 locationToInt(-122, 19, 59));
110            sHeading = 180;
111            sDist = 6.904;
112    
113            // due east
114            east = new GeoPoint(locationToInt(47, 36, 35),
115                                locationToInt(-122, 13, 59));
116            eHeading = 90;
117            eDist = 4.6574;
118    
119            // due west
120            west = new GeoPoint(locationToInt(47, 36, 35),
121                                locationToInt(-122, 25, 59));
122            wHeading = 270;
123            wDist = 4.6574;
124    
125            // north east
126            northEast = new GeoPoint(locationToInt(47, 42, 35),
127                                     locationToInt(-122, 13, 59));
128            neHeading = 34.0033834;
129            neDist = 8.328060444;
130    
131            // south west
132            southWest = new GeoPoint(locationToInt(47, 30, 35),
133                                     locationToInt(-122, 25, 59));
134            swHeading = 214.0033834;
135            swDist = 8.328060444;
136        }
137    
138    
139        /**
140         * Convert a degree, minutes, seconds latitude or longitude value
141         * to its associated integer value (to millionths of a degree).
142         * Allows invalid locations to be specified for testing, but
143         * doesn't allow negative minute/second values.
144         **/
145        static int locationToInt(int deg, int min, int sec) {
146            if (min < 0 || sec < 0) {
147                throw new RuntimeException("min and sec must be positive!");
148            }
149    
150            double dmin = (double)min / 60;
151            double dsec = (double)sec / (60 * 60);
152            if (deg >= 0) {
153                return ((int)Math.round(1000000 * (deg + dmin + dsec)));
154            } else {
155                return ((int)Math.round(1000000 * (deg - dmin - dsec)));
156            }
157        }
158    
159    
160    
161        /**
162         * Sanity check for our locationToInt() method
163         **/
164        public void testLocationToIntMethod() {
165            int lat = locationToInt(42, 21, 30);
166            int lng = locationToInt(-71, 3, 37);
167            if (lat != 42358333) {
168                fail("42 21 30 latitude must be 42358333, is " + lat);
169            } else if (lng != -71060278) {
170                fail("-71 3 37 longitude must be -71060278, is " + lng);
171            }
172        }
173    
174    
175        /**
176         * JUnit calls this before each testXXX method is run
177         **/
178        protected void setUp() {
179            
180        }
181    
182    
183        ////////////////////////////////////////////////////
184        // TEST LEGAL/ILLEGAL VALUES IN GeoPoint CREATION //
185        ////////////////////////////////////////////////////
186    
187    
188        /**
189         * Tests that GeoPoints can be created with some legal latitude
190         * and longitude values that are in the greater Seattle area, and
191         * that the values it's provided are returned unchanged.
192         **/
193        public void testLegalValues1() {
194            int valLat = locationToInt(47, 36, 0);
195            int valLong = locationToInt(-122, 19, 50);
196            checkLegal(valLat, valLong);
197        }
198    
199    
200        /**
201         * Tests that GeoPoints can be created with some legal latitude
202         * and longitude values that are in the greater Seattle area, and
203         * that the values it's provided are returned unchanged.
204         **/
205        public void testLegalValues2() {
206            int valLat = locationToInt(47, 35, 58);
207            int valLong = locationToInt(-122, 20, 0);
208            checkLegal(valLat, valLong);
209        }
210    
211    
212        /**
213         * Helper method for asserting that a legal latitude/longitude
214         * pair can be used to create a GeoPoint and that those exact same
215         * values are then returned.
216         **/
217        void checkLegal(int lat, int lng) {
218            GeoPoint g = null;
219            try {
220                g = new GeoPoint(lat, lng);
221            } catch (Exception ex) {
222                // Failed
223                fail("Didn't allow legal (latitude,longitude) of (" +
224                     lat + ", " + lng + ")");
225                return;
226            }
227    
228            if (g.getLatitude() != lat) {
229                fail("Latitude should have been set to " + lat + 
230                     ", was set to " + g.getLatitude() + " instead.");
231    
232            } else if (g.getLongitude() != lng) {
233                fail("Longitude should have been set to " + lng + 
234                     ", was set to " + g.getLongitude() + " instead.");
235            }
236        }
237    
238    
239        //////////////////////////////
240        // TEST GeoPoint.distanceTo //
241        //////////////////////////////
242    
243        /**
244         * Test that distance 0 is handled correctly: same object
245         **/
246        public void testDistance01() {
247            assertEquals("Distance between a GeoPoint and itself must be 0",
248                         0.0, p1.distanceTo(p1), TOLERANCE);
249        }
250    
251    
252        /**
253         * Test that distance 0 is handled correctly: equal objects
254         **/
255        public void testDistance02() {
256            assertEquals("Distance between two equal GeoPoints must be 0",
257                         0.0, eq1.distanceTo(eq2), TOLERANCE);
258        }
259    
260    
261        /**
262         * Test that points due north are handled correctly
263         **/
264        public void testDistanceDueNorth() {
265            checkDist(p1, north, nDist);
266        }
267    
268    
269        /**
270         * Test that points due south are handled correctly
271         **/
272        public void testDistanceDueSouth() {
273            checkDist(p1, south, sDist);
274        }
275    
276    
277        /**
278         * Test that points due east are handled correctly
279         **/
280        public void testDistanceDueEast() {
281            checkDist(p1, east, eDist);
282        }
283    
284    
285        /**
286         * Test that points due west are handled correctly
287         **/
288        public void testDistanceDueWest() {
289            checkDist(p1, west, wDist);
290        }
291    
292    
293        /**
294         * Test that points north east are handled correctly
295         **/
296        public void testDistanceDueNorthEast() {
297            checkDist(p1, northEast, neDist);
298        }
299    
300    
301        /**
302         * Test that points south west are handled correctly
303         **/
304        public void testDistanceDueSouthWest() {
305            checkDist(p1, southWest, swDist);
306        }
307    
308    
309        /**
310         * Helper method that asserts that the distance between p1 and p2
311         * is dist AND that the distance between p2 and p1 is also dist.
312         **/
313        void checkDist(GeoPoint p1, GeoPoint p2, double dist) {
314            assertEquals("Distance between these two points is " +
315                         dist + ", not " + p1.distanceTo(p2),
316                         dist, p1.distanceTo(p2), TOLERANCE);
317            assertEquals("Reversing between these two points is " +
318                         dist + ", not " + p2.distanceTo(p1),
319                         dist, p2.distanceTo(p1), TOLERANCE);
320        }
321    
322    
323        /////////////////////////////
324        // TEST GeoPoint.headingTo //
325        /////////////////////////////
326    
327        /**
328         * Test that points due north are handled correctly
329         **/
330        public void testHeadingDueNorth() {
331            checkHeading(p1, north, nHeading);
332        }
333    
334    
335        /**
336         * Test that points due south are handled correctly
337         **/
338        public void testHeadingDueSouth() {
339            checkHeading(p1, south, sHeading);
340        }
341    
342    
343        /**
344         * Test that points due east are handled correctly
345         **/
346        public void testHeadingDueEast() {
347            checkHeading(p1, east, eHeading);
348        }
349    
350    
351        /**
352         * Test that points due west are handled correctly
353         **/
354        public void testHeadingDueWest() {
355            checkHeading(p1, west, wHeading);
356        }
357    
358    
359        /**
360         * Test that points north east are handled correctly
361         **/
362        public void testHeadingNorthEast() {
363            checkHeading(p1, northEast, neHeading);
364        }
365    
366    
367        /**
368         * Test that points south west are handled correctly
369         **/
370        public void testHeadingSouthWest() {
371            checkHeading(p1, southWest, swHeading);
372        }
373    
374    
375        /**
376         * Helper method that asserts that the heading between p1 and p2
377         * is heading AND that the distance between p2 and p1 is heading + 180.
378         **/
379        void checkHeading(GeoPoint p1, GeoPoint p2, double heading) {
380            assertEquals("Heading between these two points is " +
381                         heading + ", not " + p1.headingTo(p2),
382                         heading, p1.headingTo(p2), TOLERANCE);
383            assertEquals("Reversing between these two points is " +
384                         (heading + 180) + ", not " + p2.headingTo(p1),
385                         heading, ((p2.headingTo(p1) + 180) % 360), TOLERANCE);
386        }
387    
388    
389        //////////////////////////
390        // TEST GeoPoint.equals //
391        //////////////////////////
392    
393        /**
394         * Test positive case: same object
395         **/
396        public void testEquals1() {
397            assertTrue("A GeoPoint must be equal() to itself",
398                       eq1.equals(eq1));
399        }
400    
401    
402        /**
403         * Test positive case: equal objects
404         **/
405        public void testEquals2() {
406            assertTrue("Two GeoPoints with the same data must be equal()",
407                       eq1.equals(eq2));
408        }
409    
410    
411        /**
412         * Test that null values are handled correctly
413         **/
414        public void testNotEqualsNull() {
415            if(!eq1.equals(null))
416                return; // gp != null which is good.
417            fail("Null not handled: equals(null) returns true or causes other problems (NullPointerException?).");
418        }
419    
420    
421        /**
422         * Test that two GeoPoints with different data do not register as
423         * equal.
424         **/
425        public void testNotEqualsDiff() {
426            assertFalse("Two GeoPoints with different data must not be equal()",
427                        eq1.equals(diff));
428        }
429    
430    
431        /**
432         * Test that two GeoPoints with "swapped" latitude and longitude
433         * values (i.e., they both sum to the same value) do not register
434         * as equal.
435         **/
436        public void testNotEqualsSwapped() {
437            int lat = locationToInt(1, 2, 3);
438            int lng = locationToInt(4, 5, 6);
439            GeoPoint e1 = new GeoPoint(lat, lng);
440            GeoPoint e2 = new GeoPoint(lng, lat);
441            assertFalse("Two GeoPoints with swapped data must not be equal()",
442                        e1.equals(e2));
443        }
444    
445    
446        ////////////////////////////
447        // TEST GeoPoint.hashCode //
448        ////////////////////////////
449    
450        /**
451         * Test positive case: same object
452         **/
453        public void testHashCode1() {
454            assertEquals("A GeoPoint's hashCode must remain constant",
455                         eq1.hashCode(), eq1.hashCode());
456        }
457    
458    
459        /**
460         * Test positive case: equal objects
461         **/
462        public void testHashCode2() {
463            assertEquals("GeoPoints with same data must have the same hashCode",
464                         eq1.hashCode(), eq2.hashCode());
465        }
466    }