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 }