Spaces:
Sleeping
Sleeping
| """ | |
| Unit tests for the Haversine driving time calculator in Location class. | |
| These tests verify that the driving time calculations correctly implement | |
| the Haversine formula for great-circle distance on Earth. | |
| """ | |
| from vehicle_routing.domain import Location | |
| class TestHaversineDrivingTime: | |
| """Tests for Location.driving_time_to() using Haversine formula.""" | |
| def test_same_location_returns_zero(self): | |
| """Same location should return 0 driving time.""" | |
| loc = Location(latitude=40.0, longitude=-75.0) | |
| assert loc.driving_time_to(loc) == 0 | |
| def test_same_coordinates_returns_zero(self): | |
| """Two locations with same coordinates should return 0.""" | |
| loc1 = Location(latitude=40.0, longitude=-75.0) | |
| loc2 = Location(latitude=40.0, longitude=-75.0) | |
| assert loc1.driving_time_to(loc2) == 0 | |
| def test_symmetric_distance(self): | |
| """Distance from A to B should equal distance from B to A.""" | |
| loc1 = Location(latitude=0, longitude=0) | |
| loc2 = Location(latitude=3, longitude=4) | |
| assert loc1.driving_time_to(loc2) == loc2.driving_time_to(loc1) | |
| def test_equator_one_degree_longitude(self): | |
| """ | |
| One degree of longitude at the equator is approximately 111.32 km. | |
| At 50 km/h, this should take about 2.2 hours = 7920 seconds. | |
| """ | |
| loc1 = Location(latitude=0, longitude=0) | |
| loc2 = Location(latitude=0, longitude=1) | |
| driving_time = loc1.driving_time_to(loc2) | |
| # Allow 5% tolerance for rounding | |
| assert 7500 < driving_time < 8500, f"Expected ~8000, got {driving_time}" | |
| def test_equator_one_degree_latitude(self): | |
| """ | |
| One degree of latitude is approximately 111.32 km everywhere. | |
| At 50 km/h, this should take about 2.2 hours = 7920 seconds. | |
| """ | |
| loc1 = Location(latitude=0, longitude=0) | |
| loc2 = Location(latitude=1, longitude=0) | |
| driving_time = loc1.driving_time_to(loc2) | |
| # Allow 5% tolerance for rounding | |
| assert 7500 < driving_time < 8500, f"Expected ~8000, got {driving_time}" | |
| def test_realistic_us_cities(self): | |
| """ | |
| Test driving time between realistic US city coordinates. | |
| Philadelphia (39.95, -75.17) to New York (40.71, -74.01) | |
| Distance is approximately 130 km, should take ~2.6 hours at 50 km/h. | |
| """ | |
| philadelphia = Location(latitude=39.95, longitude=-75.17) | |
| new_york = Location(latitude=40.71, longitude=-74.01) | |
| driving_time = philadelphia.driving_time_to(new_york) | |
| # Expected: ~130 km / 50 km/h * 3600 = ~9360 seconds | |
| # Allow reasonable tolerance | |
| assert 8500 < driving_time < 10500, f"Expected ~9400, got {driving_time}" | |
| def test_longer_distance(self): | |
| """ | |
| Test longer distance: Philadelphia to Hartford. | |
| Distance is approximately 290 km. | |
| """ | |
| philadelphia = Location(latitude=39.95, longitude=-75.17) | |
| hartford = Location(latitude=41.76, longitude=-72.68) | |
| driving_time = philadelphia.driving_time_to(hartford) | |
| # Expected: ~290 km / 50 km/h * 3600 = ~20880 seconds | |
| # Allow reasonable tolerance | |
| assert 19000 < driving_time < 23000, f"Expected ~21000, got {driving_time}" | |
| def test_known_values_from_test_data(self): | |
| """ | |
| Verify the exact values used in constraint tests. | |
| These values are calculated using the Haversine formula. | |
| """ | |
| LOCATION_1 = Location(latitude=0, longitude=0) | |
| LOCATION_2 = Location(latitude=3, longitude=4) | |
| LOCATION_3 = Location(latitude=-1, longitude=1) | |
| # These exact values are used in test_constraints.py | |
| assert LOCATION_1.driving_time_to(LOCATION_2) == 40018 | |
| assert LOCATION_2.driving_time_to(LOCATION_3) == 40025 | |
| assert LOCATION_1.driving_time_to(LOCATION_3) == 11322 | |
| def test_negative_coordinates(self): | |
| """Test with negative latitude and longitude (Southern/Western hemisphere).""" | |
| loc1 = Location(latitude=-33.87, longitude=151.21) # Sydney | |
| loc2 = Location(latitude=-37.81, longitude=144.96) # Melbourne | |
| driving_time = loc1.driving_time_to(loc2) | |
| # Distance is approximately 714 km | |
| # Expected: ~714 km / 50 km/h * 3600 = ~51408 seconds | |
| assert 48000 < driving_time < 55000, f"Expected ~51400, got {driving_time}" | |
| def test_cross_hemisphere(self): | |
| """Test crossing equator.""" | |
| loc1 = Location(latitude=10, longitude=0) | |
| loc2 = Location(latitude=-10, longitude=0) | |
| driving_time = loc1.driving_time_to(loc2) | |
| # 20 degrees of latitude = ~2226 km | |
| # Expected: ~2226 km / 50 km/h * 3600 = ~160272 seconds | |
| assert 155000 < driving_time < 165000, f"Expected ~160000, got {driving_time}" | |
| def test_cross_antimeridian(self): | |
| """Test crossing the antimeridian (date line).""" | |
| loc1 = Location(latitude=0, longitude=179) | |
| loc2 = Location(latitude=0, longitude=-179) | |
| driving_time = loc1.driving_time_to(loc2) | |
| # 2 degrees at equator = ~222 km | |
| # Expected: ~222 km / 50 km/h * 3600 = ~15984 seconds | |
| assert 15000 < driving_time < 17000, f"Expected ~16000, got {driving_time}" | |
| class TestHaversineInternalMethods: | |
| """Tests for internal Haversine calculation methods.""" | |
| def test_to_cartesian_equator_prime_meridian(self): | |
| """Test Cartesian conversion at equator/prime meridian intersection.""" | |
| loc = Location(latitude=0, longitude=0) | |
| x, y, z = loc._to_cartesian() | |
| # At (0, 0): x=0, y=0.5, z=0 | |
| assert abs(x - 0) < 0.001 | |
| assert abs(y - 0.5) < 0.001 | |
| assert abs(z - 0) < 0.001 | |
| def test_to_cartesian_north_pole(self): | |
| """Test Cartesian conversion at North Pole.""" | |
| loc = Location(latitude=90, longitude=0) | |
| x, y, z = loc._to_cartesian() | |
| # At North Pole: x=0, y=0, z=0.5 | |
| assert abs(x - 0) < 0.001 | |
| assert abs(y - 0) < 0.001 | |
| assert abs(z - 0.5) < 0.001 | |
| def test_meters_to_driving_seconds(self): | |
| """Test conversion from meters to driving seconds.""" | |
| # 50 km = 50000 m should take 1 hour = 3600 seconds at 50 km/h | |
| seconds = Location._meters_to_driving_seconds(50000) | |
| assert seconds == 3600 | |
| def test_meters_to_driving_seconds_zero(self): | |
| """Zero meters should return zero seconds.""" | |
| assert Location._meters_to_driving_seconds(0) == 0 | |
| def test_meters_to_driving_seconds_small(self): | |
| """Test small distances.""" | |
| # 1 km = 1000 m should take 72 seconds at 50 km/h | |
| seconds = Location._meters_to_driving_seconds(1000) | |
| assert seconds == 72 | |