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