File size: 3,822 Bytes
03e3b1b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use super::*;
use crate::data::data_seed::types::LocationData;
use solverforge_maps::{BoundingBox, Coord, NetworkConfig, RoadNetwork};

#[test]
fn parses_demo_data_ids_case_insensitively() {
    assert!(matches!(
        "philadelphia".parse::<DemoData>(),
        Ok(DemoData::Philadelphia)
    ));
    assert!(matches!(
        "HARTFORD".parse::<DemoData>(),
        Ok(DemoData::Hartford)
    ));
}

#[test]
fn generates_city_demo_plan() {
    for (demo, expected_stops, expected_name) in [
        (
            DemoData::Philadelphia,
            visit_count(philadelphia::VISIT_GROUPS),
            "Philadelphia",
        ),
        (
            DemoData::Hartford,
            visit_count(hartford::VISIT_GROUPS),
            "Hartford",
        ),
        (
            DemoData::Firenze,
            visit_count(firenze::VISIT_GROUPS),
            "Firenze",
        ),
    ] {
        let plan = generate(demo);
        assert_eq!(plan.name, expected_name);
        assert_eq!(plan.vehicles.len(), 10);
        assert_eq!(plan.deliveries.len(), expected_stops);
        assert!(plan
            .vehicles
            .iter()
            .all(|vehicle| vehicle.delivery_order.is_empty()));
    }
}

fn visit_count(groups: &[&[LocationData]]) -> usize {
    groups.iter().map(|group| group.len()).sum()
}

#[test]
fn demo_delivery_counts_scale_with_ten_vehicles() {
    assert_eq!(generate(DemoData::Philadelphia).deliveries.len(), 82);
    assert_eq!(generate(DemoData::Hartford).deliveries.len(), 50);
    assert_eq!(generate(DemoData::Firenze).deliveries.len(), 80);
}

#[test]
fn demo_plans_have_enough_vehicle_capacity() {
    for demo in [
        DemoData::Philadelphia,
        DemoData::Hartford,
        DemoData::Firenze,
    ] {
        let plan = generate(demo);
        let total_capacity: i32 = plan.vehicles.iter().map(|vehicle| vehicle.capacity).sum();
        let total_demand: i32 = plan.deliveries.iter().map(|delivery| delivery.demand).sum();

        assert!(
            total_capacity >= total_demand,
            "{demo:?} demo should be capacity-feasible before route ordering: capacity={total_capacity}, demand={total_demand}"
        );
    }
}

#[tokio::test]
async fn live_demo_locations_are_mutually_reachable_when_enabled() {
    if std::env::var("SOLVERFORGE_RUN_LIVE_TESTS").ok().as_deref() != Some("1") {
        return;
    }

    for demo in [
        DemoData::Philadelphia,
        DemoData::Hartford,
        DemoData::Firenze,
    ] {
        let plan = generate(demo);
        let mut named_coords = Vec::new();
        for delivery in &plan.deliveries {
            named_coords.push((
                format!("delivery {}", delivery.label),
                delivery.coord().unwrap(),
            ));
        }
        for vehicle in &plan.vehicles {
            named_coords.push((
                format!("depot {}", vehicle.name),
                vehicle.depot_coord().unwrap(),
            ));
        }

        let coords: Vec<Coord> = named_coords.iter().map(|(_, coord)| *coord).collect();
        let bbox = BoundingBox::from_coords(&coords).expand_for_routing(&coords);
        let network = RoadNetwork::load_or_fetch(&bbox, &NetworkConfig::default(), None)
            .await
            .unwrap();
        let matrix = network.compute_matrix(&coords, None).await;
        let unreachable = matrix
            .unreachable_pairs()
            .into_iter()
            .map(|(from_idx, to_idx)| {
                let from_name = &named_coords[from_idx].0;
                let to_name = &named_coords[to_idx].0;
                format!("{from_name} -> {to_name}")
            })
            .collect::<Vec<_>>();

        assert!(
            unreachable.is_empty(),
            "{demo:?} has unreachable directed routes: {unreachable:?}"
        );
    }
}