blackopsrepl's picture
feat(app): add SolverForge deliveries tutorial app
03e3b1b
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:?}"
);
}
}