def test_search_quick(self): """Perform search and check found caches""" rect = Rectangle(Point(49.73, 13.38), Point(49.74, 13.40)) caches = list(self.g.search_quick(rect)) strict_caches = list(self.g.search_quick(rect, strict=True)) precise_caches = list(self.g.search_quick(rect, precision=45.)) # Check for known geocaches expected = ["GC41FJC", "GC17E8Y", "GC5ND9F"] for i in expected: found = False for c in caches: if c.wp == i: found = True break with self.subTest("Check if {} is in results".format(c.wp)): self.assertTrue(found) with self.subTest("Precision is in assumed range"): self.assertLess(caches[0].location.precision, 49.5) self.assertGreater(caches[0].location.precision, 49.3) with self.subTest("Found roughly correct amount of caches"): # At time of writing, there were 108 caches inside inspected tile self.assertLess(len(caches), 130) self.assertGreater(len(caches), 90) with self.subTest("Strict handling of cache coordinates"): # ...but only 12 inside this stricter area self.assertLess(len(strict_caches), 16) self.assertGreater(len(strict_caches), 7) with self.subTest("Precision grows when asking for it"): self.assertLess(precise_caches[0].location.precision, 45.)
def test_search_quick(self): """Perform search and check found caches""" # at time of writing, there were exactly 16 caches in this area + one PM only expected_cache_num = 16 tolerance = 7 rect = Rectangle(Point(49.73, 13.38), Point(49.74, 13.40)) with self.subTest("normal"): res = [c.wp for c in self.g.search_quick(rect)] for wp in ["GC41FJC", "GC17E8Y", "GC5ND9F"]: self.assertIn(wp, res) # but 108 caches larger tile self.assertLess(len(res), 130) self.assertGreater(len(res), 90) with self.subTest("strict handling of cache coordinates"): res = list(self.g.search_quick(rect, strict=True)) self.assertLess(len(res), expected_cache_num + tolerance) self.assertGreater(len(res), expected_cache_num - tolerance) with self.subTest("larger zoom - more precise"): res1 = list(self.g.search_quick(rect, strict=True, zoom=15)) res2 = list(self.g.search_quick(rect, strict=True, zoom=14)) for res in res1, res2: self.assertLess(len(res), expected_cache_num + tolerance) self.assertGreater(len(res), expected_cache_num - tolerance) for c1, c2 in itertools.product(res1, res2): self.assertLess(c1.location.precision, c2.location.precision)
def test_search_quick(self): """Perform quick search and check found caches""" # at time of writing, there were exactly 16 caches in this area + one PM only expected_cache_num = 16 tolerance = 7 rect = Rectangle(Point(49.73, 13.38), Point(49.74, 13.40)) with self.subTest("normal"): with self.recorder.use_cassette('geocaching_quick_normal'): # Once this feature is fixed, the corresponding cassette will have to be deleted # and re-recorded. res = [c.wp for c in self.gc.search_quick(rect)] for wp in ["GC41FJC", "GC17E8Y", "GC383XN"]: self.assertIn(wp, res) # but 108 caches larger tile self.assertLess(len(res), 130) self.assertGreater(len(res), 90) with self.subTest("strict handling of cache coordinates"): with self.recorder.use_cassette('geocaching_quick_strictness'): res = list(self.gc.search_quick(rect, strict=True)) self.assertLess(len(res), expected_cache_num + tolerance) self.assertGreater(len(res), expected_cache_num - tolerance) with self.subTest("larger zoom - more precise"): with self.recorder.use_cassette('geocaching_quick_zoom'): res1 = list(self.gc.search_quick(rect, strict=True, zoom=15)) res2 = list(self.gc.search_quick(rect, strict=True, zoom=14)) for res in res1, res2: self.assertLess(len(res), expected_cache_num + tolerance) self.assertGreater(len(res), expected_cache_num - tolerance) for c1, c2 in itertools.product(res1, res2): self.assertLess(c1.location.precision, c2.location.precision)
def test_search(self): with self.subTest("normal"): expected = ["GC41FJC", "GC17E8Y", "GC5ND9F"] caches = self.g.search(Point(49.733867, 13.397091), 3) for cache in caches: self.assertIn(cache.wp, expected) with self.subTest("pagging"): caches = list(self.g.search(Point(49.733867, 13.397091), 25)) self.assertNotEqual(caches[0], caches[20])
def test_search_quick_match_load(self): """Test if search results matches exact cache locations.""" rect = Rectangle(Point(49.73, 13.38), Point(49.74, 13.39)) caches = list(self.g.search_quick(rect, strict=True, zoom=15)) for cache in caches: try: cache.load() self.assertIn(cache.location, rect) except PMOnlyException: pass
def test_from_string(self): with self.subTest("normal"): self.assertEqual(Point.from_string("N 49 45.123 E 013 22.123"), Point(49.75205, 13.36872)) with self.subTest("south and west"): self.assertEqual(Point.from_string("S 49 45.123 W 013 22.123"), Point(-48.24795, -12.63128)) with self.subTest("letter together"): self.assertEqual(Point.from_string("N49 45.123 E013 22.123"), Point(49.75205, 13.36872)) with self.subTest("letter after"): self.assertEqual(Point.from_string("49N 45.123 013E 22.123"), Point(49.75205, 13.36872)) with self.subTest("south and west letter after"): self.assertEqual(Point.from_string("49S 45.123 013W 22.123"), Point(-48.24795, -12.63128)) with self.subTest("decimal separator: coma"): self.assertEqual(Point.from_string("N 49 45,123 E 013 22,123"), Point(49.75205, 13.36872)) with self.subTest("degree symbol"): self.assertEqual(Point.from_string("N 49° 45.123 E 013° 22.123"), Point(49.75205, 13.36872)) with self.subTest("coma between lat and lon"): self.assertEqual(Point.from_string("N 49 45.123, E 013 22.123"), Point(49.75205, 13.36872)) with self.subTest("marginal values: zeroes"): self.assertEqual(Point.from_string("N 49 45.000 E 13 0.0"), Point(49.75, 13.0)) with self.assertRaises(ValueError): Point.from_string("123")
def test_calculate_initial_tiles(self): expect_tiles = [(2331, 1185, 12), (2331, 1186, 12), (2332, 1185, 12), (2332, 1186, 12)] expect_precision = 76.06702024121832 r = Rectangle(Point(60.15, 24.95), Point(60.17, 25.00)) tiles, starting_precision = self.g._calculate_initial_tiles(r) for t in tiles: with self.subTest("Tile {} expected as initial tile".format(t)): self.assertIn(t, expect_tiles) with self.subTest("Expected precision"): self.assertAlmostEqual(starting_precision, expect_precision)
def test_search(self): with self.subTest("normal"): expected = ["GC41FJC", "GC17E8Y", "GC1ZAQV"] caches = self.g.search(Point(49.733867, 13.397091), len(expected)) for wp, cache in zip(expected, caches): self.assertEqual(wp, cache.wp) with self.subTest("pagging"): caches = self.g.search(Point(49.733867, 13.397091), 25) res = [c for c in caches] self.assertNotEqual(res[0], res[20])
def test_search(self): with self.subTest("normal"): tolerance = 2 expected = {"GC5VJ0P", "GC41FJC", "GC50AQ6", "GC167Y7", "GC7RR74", "GC167Y7"} with self.recorder.use_cassette("geocaching_search"): found = {cache.wp for cache in self.gc.search(Point(49.733867, 13.397091), 20)} self.assertGreater(len(expected & found), len(expected) - tolerance) with self.subTest("pagging"): with self.recorder.use_cassette("geocaching_search_pagination"): caches = list(self.gc.search(Point(49.733867, 13.397091), 100)) self.assertNotEqual(caches[0], caches[50])
def test_search_quick_match_load(self): """Test if quick search results matches exact cache locations.""" rect = Rectangle(Point(49.73, 13.38), Point(49.74, 13.39)) with self.recorder.use_cassette('geocaching_matchload'): # at commit time, this test is an allowed failure. Once this feature is fixed, the # corresponding cassette will have to be deleted and re-recorded. caches = list(self.gc.search_quick(rect, strict=True, zoom=15)) for cache in caches: try: cache.load() self.assertIn(cache.location, rect) except PMOnlyException: pass
def test_to_map_tile(self): t = (8800, 5574, 14) point_in_t = Point(49.75, 13.36) with self.subTest("From tile and back"): self.assertEqual(Point.from_tile(*t).to_map_tile(t[-1]), t) with self.subTest("Random point"): self.assertEqual(point_in_t.to_map_tile(14), t) with self.subTest("Increase in latitude: decrease in y value"): self.assertLess(Point(50., 13.36).to_map_tile(14)[1], t[1]) with self.subTest("Increase in longitude: increase in x value"): self.assertGreater(Point(49.75, 14.).to_map_tile(14)[0], t[0])
def setUp(self): self.gc = Geocaching() self.t = Trackable("TB1234", self.gc, name="TrackMe") self.c = Cache("GC12345", self.gc, name="Testing", cache_type="Traditional Cache", location=Point(), state=True, found=False, size="micro", difficulty=1.5, terrain=5, author="human", hidden=date(2000, 1, 1), attributes={ "onehour": True, "kids": False, "available": True }, summary="text", description="long text", hint="rot13", favorites=0, pm_only=False, trackables=self.t)
def test_from_tile(self): """Test coordinate creation from tile""" p = Point.from_tile(8800, 5574, 14) p_pos = Point(49.752879934150215, 13.359375, 0.0) p2 = Point.from_tile(x=8801, y=5575, z=14) p_half = Point.from_tile(8800, 5574, 14, 1, 1, 2) # Check creation for att in ['latitude', 'longitude']: with self.subTest("Assumed location: {}".format(att)): self.assertAlmostEqual(getattr(p, att), getattr(p_pos, att)) with self.subTest("Fractional tiles: y-axis addition"): self.assertEqual(Point.from_tile(8800, 5574, 14, 0, 32, 32), Point.from_tile(x=8800, y=5575, z=14)) with self.subTest("Fractional tiles: x-axis addition"): self.assertAlmostEqual(Point.from_tile(8800, 5574, 14, 32, 0, 32), Point.from_tile(x=8801, y=5574, z=14)) with self.subTest("Fractional tiles: addition on both axes"): self.assertEqual(Point.from_tile(8800, 5574, 14, 32, 32, 32), p2) with self.subTest("y increases -> latitude decreases"): self.assertGreater(p.latitude, p_half.latitude) with self.subTest("x increases -> latitude increases"): self.assertLess(p.longitude, p_half.longitude)
def test_search(self): with self.subTest("normal"): tolerance = 2 expected = { "GC5VJ0P", "GC41FJC", "GC17E8Y", "GC14AV5", "GC50AQ6", "GC167Y7" } found = { cache.wp for cache in self.g.search(Point(49.733867, 13.397091), 20) } self.assertGreater(len(expected & found), len(expected) - tolerance) with self.subTest("pagging"): caches = list(self.g.search(Point(49.733867, 13.397091), 100)) self.assertNotEqual(caches[0], caches[50])
def test_recover_from_rate_limit(self): """Test recovering from API rate limit exception.""" rect = Rectangle(Point(50.74, 13.38), Point(49.73, 14.40)) # large rectangle with self.recorder.use_cassette("geocaching_api_rate_limit") as vcr: orig_wait_for = TooManyRequestsError.wait_for with patch.object(TooManyRequestsError, "wait_for", autospec=True) as wait_for: # If we are recording, we must perform real wait, otherwise we skip waiting wait_for.side_effect = orig_wait_for if vcr.current_cassette.is_recording() else None for i, _cache in enumerate(self.gc.search_rect(rect, per_query=1)): if wait_for.called: self.assertEqual(wait_for.call_count, 1) break if i > 20: # rate limit should be released after ~10 requests self.fail("API Rate Limit not released")
def setUp(self): self.gc = Geocaching() self.t = Trackable("TB123AB", self.gc, name="Testing", type="Travel Bug", location=Point(), owner="human", description="long text", goal="short text")
def test_search_rect(self): """Perform search by rect and check found caches.""" rect = Rectangle(Point(49.73, 13.38), Point(49.74, 13.39)) expected = {"GC1TYYG", "GC11PRW", "GC7JRR5", "GC161KR", "GC1GW54", "GC7KDWE", "GC93HA6", "GCZC5D"} orig_wait_for = TooManyRequestsError.wait_for with self.recorder.use_cassette("geocaching_search_rect") as vcr: with patch.object(TooManyRequestsError, "wait_for", autospec=True) as wait_for: wait_for.side_effect = orig_wait_for if vcr.current_cassette.is_recording() else None with self.subTest("default use"): caches = self.gc.search_rect(rect) waypoints = {cache.wp for cache in caches} self.assertSetEqual(waypoints, expected) with self.subTest("sort by distance"): with self.assertRaises(AssertionError): caches = list(self.gc.search_rect(rect, sort_by="distance")) origin = Point.from_string("N 49° 44.230 E 013° 22.858") caches = list(self.gc.search_rect(rect, sort_by=SortOrder.distance, origin=origin)) waypoints = {cache.wp for cache in caches} self.assertSetEqual(waypoints, expected) # Check if caches are sorted by distance to origin distances = [] for cache in caches: try: distances.append(great_circle(cache.location, origin).meters) except PMOnlyException: # can happend when getting accurate location continue self.assertEqual(distances, sorted(distances)) with self.subTest("sort by different criteria"): for sort_by in SortOrder: if sort_by is SortOrder.distance: continue caches = self.gc.search_rect(rect, sort_by=sort_by) waypoints = {cache.wp for cache in caches} self.assertSetEqual(waypoints, expected)
def test_location(self): self.assertEqual(self.c.location, Point()) with self.subTest("automatic str conversion"): self.c.location = "S 36 51.918 E 174 46.725" self.assertEqual(self.c.location, Point.from_string("S 36 51.918 E 174 46.725")) with self.subTest("filter invalid"): with self.assertRaises(ValueError): self.c.location = "somewhere"
def test_recover_from_rate_limit_without_sleep(self): """Test recovering from API rate limit exception without sleep.""" rect = Rectangle(Point(50.74, 13.38), Point(49.73, 14.40)) with self.recorder.use_cassette('geocaching_api_rate_limit_with_none') as vcr: with patch.object(TooManyRequestsError, 'wait_for', autospec=True) as wait_for: caches = self.gc.search_rect(rect, per_query=1, wait_sleep=False) for i, cache in enumerate(caches): if cache is None: import time while cache is None: if vcr.current_cassette.is_recording(): time.sleep(10) cache = next(caches) self.assertIsInstance(cache, Cache) break if i > 20: self.fail("API Rate Limit not released") self.assertEqual(wait_for.call_count, 0)
def test_search_rect(self): """Perform search by rect and check found caches.""" rect = Rectangle(Point(49.73, 13.38), Point(49.74, 13.39)) expected = {'GC1TYYG', 'GC11PRW', 'GC7JRR5', 'GC161KR', 'GC1GW54', 'GC7KDWE', 'GC8D303'} orig_wait_for = TooManyRequestsError.wait_for with self.recorder.use_cassette('geocaching_search_rect') as vcr: with patch.object(TooManyRequestsError, 'wait_for', autospec=True) as wait_for: wait_for.side_effect = orig_wait_for if vcr.current_cassette.is_recording() else None with self.subTest("default use"): caches = self.gc.search_rect(rect) waypoints = {cache.wp for cache in caches} self.assertSetEqual(waypoints, expected) with self.subTest("sort by distance"): with self.assertRaises(AssertionError): caches = list(self.gc.search_rect(rect, sort_by='distance')) origin = Point.from_string('N 49° 44.230 E 013° 22.858') caches = list(self.gc.search_rect(rect, sort_by=SortOrder.distance, origin=origin)) waypoints = [cache.wp for cache in caches] self.assertEqual(waypoints, [ 'GC11PRW', 'GC1TYYG', 'GC7JRR5', 'GC1GW54', 'GC161KR', 'GC7KDWE', 'GC8D303' ]) # Check if caches are sorted by distance to origin distances = [great_circle(cache.location, origin).meters for cache in caches] self.assertEqual(distances, sorted(distances)) with self.subTest("sort by different criteria"): for sort_by in SortOrder: if sort_by is SortOrder.distance: continue caches = self.gc.search_rect(rect, sort_by=sort_by) waypoints = {cache.wp for cache in caches} self.assertSetEqual(waypoints, expected)
def test_precision_from_tile_zoom(self): p = Point(49.75, 13.36) with self.subTest("Random point"): self.assertAlmostEqual(p.precision_from_tile_zoom(14), 6.173474613462484) with self.subTest("Precision is larger on greater Z values"): self.assertGreater(p.precision_from_tile_zoom(13), p.precision_from_tile_zoom(14)) with self.subTest("Precision is larger when tile is divided less"): self.assertGreater(p.precision_from_tile_zoom(14, 10), p.precision_from_tile_zoom(14, 100))
def test_geocode(self): pilsen = Point(49.74774, 13.37752) with self.subTest("existing location"): self.assertEqual(self.g.geocode("Pilsen"), pilsen) self.assertEqual(self.g.geocode("Plzeň"), pilsen) self.assertEqual(self.g.geocode("plzen"), pilsen) with self.subTest("non-existing location"): with self.assertRaises(GeocodeError): self.g.geocode("qwertzuiop") with self.subTest("empty request"): with self.assertRaises(GeocodeError): self.g.geocode("")
def test_get_zoom_by_distance(self): """Check that calculated zoom levels are correct""" with self.subTest("World map zoom level"): self.assertEqual(self.g._get_zoom_by_distance(40e6, 0., 1., 'le'), 0) with self.subTest("Next level"): self.assertEqual(self.g._get_zoom_by_distance(40e6, 0., 1., 'ge'), 1) with self.subTest("Tile width greater or equal to 1 km"): self.assertEqual(self.g._get_zoom_by_distance(1e3, 49., 1., 'le'), 14) with self.subTest("More accurate than 10 m"): self.assertEqual(self.g._get_zoom_by_distance(10., 49., 256, 'ge'), 14) with self.subTest("Previous test was correct"): p = Point(49., 13.) self.assertGreater(p.precision_from_tile_zoom(13), 10) self.assertLess(p.precision_from_tile_zoom(14), 10)
def test_geocode(self): ref_point = Point(50.08746, 14.42125) with self.recorder.use_cassette('geocaching_shortcut_geocode'): self.assertLess( great_circle(self.gc.geocode("Prague"), ref_point).miles, 10)
def test_distance(self): p1, p2 = Point(60.15, 24.95), Point(60.17, 25.00) self.assertAlmostEqual(p1.distance(p2), 3560.1077441805196)
def test_geocode(self): ref_point = Point(49.74774, 13.37752) self.assertLess(great_circle(self.g.geocode("Pilsen"), ref_point).miles, 10)