| | """ |
| | 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) |
| | |
| | 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) |
| | |
| | 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) |
| | |
| | |
| | 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) |
| | |
| | |
| | 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) |
| |
|
| | |
| | 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) |
| | loc2 = Location(latitude=-37.81, longitude=144.96) |
| | driving_time = loc1.driving_time_to(loc2) |
| | |
| | |
| | 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) |
| | |
| | |
| | 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) |
| | |
| | |
| | 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() |
| | |
| | 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() |
| | |
| | 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.""" |
| | |
| | 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.""" |
| | |
| | seconds = Location._meters_to_driving_seconds(1000) |
| | assert seconds == 72 |
| |
|
| |
|
| |
|