use crate::components::{RiderPhase, Route};
use crate::error::SimError;
use crate::events::Event;
use crate::ids::GroupId;
use crate::sim::Simulation;
use crate::stop::StopId;
use super::helpers::{default_config, scan};
fn run_until_arrived(sim: &mut Simulation, rider_id: crate::entity::EntityId) {
for _ in 0..10_000 {
sim.step();
if let Some(r) = sim.world().rider(rider_id)
&& r.phase() == RiderPhase::Arrived
{
return;
}
}
panic!("rider did not arrive within 10,000 ticks");
}
fn run_until_abandoned(sim: &mut Simulation, rider_id: crate::entity::EntityId) {
for _ in 0..10_000 {
sim.step();
if let Some(r) = sim.world().rider(rider_id)
&& r.phase() == RiderPhase::Abandoned
{
return;
}
}
panic!("rider did not abandon within 10,000 ticks");
}
#[test]
fn settle_from_arrived() {
let config = default_config();
let mut sim = Simulation::new(&config, scan()).unwrap();
let rider = sim
.spawn_rider_by_stop_id(StopId(0), StopId(2), 70.0)
.unwrap();
run_until_arrived(&mut sim, rider);
sim.settle_rider(rider).unwrap();
let r = sim.world().rider(rider).unwrap();
assert_eq!(r.phase(), RiderPhase::Resident);
assert!(r.current_stop().is_some());
let stop = r.current_stop().unwrap();
assert!(sim.residents_at(stop).any(|id| id == rider));
assert_eq!(sim.resident_count_at(stop), 1);
let events = sim.drain_events();
assert!(
events
.iter()
.any(|e| matches!(e, Event::RiderSettled { rider: r, .. } if *r == rider))
);
}
#[test]
fn settle_from_abandoned() {
let config = default_config();
let mut sim = Simulation::new(&config, scan()).unwrap();
let rider = sim
.spawn_rider_by_stop_id(StopId(0), StopId(2), 70.0)
.unwrap();
sim.world_mut().set_patience(
rider,
crate::components::Patience {
max_wait_ticks: 1,
waited_ticks: 0,
},
);
run_until_abandoned(&mut sim, rider);
sim.settle_rider(rider).unwrap();
let r = sim.world().rider(rider).unwrap();
assert_eq!(r.phase(), RiderPhase::Resident);
let stop = r.current_stop().unwrap();
assert!(sim.residents_at(stop).any(|id| id == rider));
assert!(!sim.abandoned_at(stop).any(|id| id == rider));
}
#[test]
fn settle_wrong_phase_returns_error() {
let config = default_config();
let mut sim = Simulation::new(&config, scan()).unwrap();
let rider = sim
.spawn_rider_by_stop_id(StopId(0), StopId(2), 70.0)
.unwrap();
let result = sim.settle_rider(rider);
assert!(matches!(result, Err(SimError::InvalidState { .. })));
}
#[test]
fn reroute_resident_to_waiting() {
let config = default_config();
let mut sim = Simulation::new(&config, scan()).unwrap();
let rider = sim
.spawn_rider_by_stop_id(StopId(0), StopId(2), 70.0)
.unwrap();
run_until_arrived(&mut sim, rider);
sim.settle_rider(rider).unwrap();
sim.drain_events();
let stop = sim.world().rider(rider).unwrap().current_stop().unwrap();
let dest = sim.stop_entity(StopId(0)).unwrap();
let route = Route::direct(stop, dest, GroupId(0));
sim.reroute_rider(rider, route).unwrap();
let r = sim.world().rider(rider).unwrap();
assert_eq!(r.phase(), RiderPhase::Waiting);
assert!(sim.waiting_at(stop).any(|id| id == rider));
assert!(!sim.residents_at(stop).any(|id| id == rider));
let events = sim.drain_events();
assert!(
events
.iter()
.any(|e| matches!(e, Event::RiderRerouted { rider: r, .. } if *r == rider))
);
}
#[test]
fn reroute_wrong_phase_returns_error() {
let config = default_config();
let mut sim = Simulation::new(&config, scan()).unwrap();
let rider = sim
.spawn_rider_by_stop_id(StopId(0), StopId(2), 70.0)
.unwrap();
let dest = sim.stop_entity(StopId(2)).unwrap();
let origin = sim.stop_entity(StopId(0)).unwrap();
let route = Route::direct(origin, dest, GroupId(0));
let result = sim.reroute_rider(rider, route);
assert!(matches!(result, Err(SimError::InvalidState { .. })));
}
#[test]
fn despawn_rider_removes_from_world_and_index() {
let config = default_config();
let mut sim = Simulation::new(&config, scan()).unwrap();
let rider = sim
.spawn_rider_by_stop_id(StopId(0), StopId(2), 70.0)
.unwrap();
run_until_arrived(&mut sim, rider);
sim.settle_rider(rider).unwrap();
let stop = sim.world().rider(rider).unwrap().current_stop().unwrap();
assert_eq!(sim.resident_count_at(stop), 1);
sim.drain_events();
sim.despawn_rider(rider).unwrap();
assert!(!sim.world().is_alive(rider));
assert_eq!(sim.resident_count_at(stop), 0);
let events = sim.drain_events();
assert!(
events
.iter()
.any(|e| matches!(e, Event::RiderDespawned { rider: r, .. } if *r == rider))
);
}
#[test]
fn despawn_riding_rider_cleans_elevator() {
let config = default_config();
let mut sim = Simulation::new(&config, scan()).unwrap();
let rider = sim
.spawn_rider_by_stop_id(StopId(0), StopId(2), 70.0)
.unwrap();
for _ in 0..10_000 {
sim.step();
if let Some(r) = sim.world().rider(rider)
&& matches!(r.phase(), RiderPhase::Riding(_))
{
break;
}
}
let riding_eid = match sim.world().rider(rider).unwrap().phase() {
RiderPhase::Riding(eid) => eid,
other => panic!("expected Riding, got {other}"),
};
assert!(
sim.world()
.elevator(riding_eid)
.unwrap()
.riders()
.contains(&rider)
);
sim.despawn_rider(rider).unwrap();
assert!(
!sim.world()
.elevator(riding_eid)
.unwrap()
.riders()
.contains(&rider)
);
}
#[test]
fn full_lifecycle_spawn_ride_settle_reroute_ride_despawn() {
let config = default_config();
let mut sim = Simulation::new(&config, scan()).unwrap();
let rider = sim
.spawn_rider_by_stop_id(StopId(0), StopId(2), 70.0)
.unwrap();
let origin = sim.stop_entity(StopId(0)).unwrap();
assert!(sim.waiting_at(origin).any(|id| id == rider));
run_until_arrived(&mut sim, rider);
sim.settle_rider(rider).unwrap();
let floor3 = sim.world().rider(rider).unwrap().current_stop().unwrap();
assert!(sim.residents_at(floor3).any(|id| id == rider));
assert_eq!(sim.metrics().total_settled(), 1);
let ground = sim.stop_entity(StopId(0)).unwrap();
let route = Route::direct(floor3, ground, GroupId(0));
sim.reroute_rider(rider, route).unwrap();
assert_eq!(sim.metrics().total_rerouted(), 1);
assert!(sim.waiting_at(floor3).any(|id| id == rider));
assert!(!sim.residents_at(floor3).any(|id| id == rider));
run_until_arrived(&mut sim, rider);
sim.despawn_rider(rider).unwrap();
assert!(!sim.world().is_alive(rider));
}
#[test]
fn resident_invisible_to_loading_system() {
let config = default_config();
let mut sim = Simulation::new(&config, scan()).unwrap();
let rider = sim
.spawn_rider_by_stop_id(StopId(0), StopId(2), 70.0)
.unwrap();
run_until_arrived(&mut sim, rider);
sim.settle_rider(rider).unwrap();
sim.drain_events();
for _ in 0..1000 {
sim.step();
}
let r = sim.world().rider(rider).unwrap();
assert_eq!(
r.phase(),
RiderPhase::Resident,
"Resident should not have boarded"
);
}
#[test]
fn dispatch_manifest_includes_resident_counts() {
let config = default_config();
let mut sim = Simulation::new(&config, scan()).unwrap();
let r1 = sim
.spawn_rider_by_stop_id(StopId(0), StopId(2), 70.0)
.unwrap();
run_until_arrived(&mut sim, r1);
sim.settle_rider(r1).unwrap();
let r2 = sim
.spawn_rider_by_stop_id(StopId(0), StopId(2), 70.0)
.unwrap();
run_until_arrived(&mut sim, r2);
sim.settle_rider(r2).unwrap();
let floor3 = sim.world().rider(r1).unwrap().current_stop().unwrap();
assert_eq!(sim.resident_count_at(floor3), 2);
}
#[test]
fn patience_reset_on_reroute() {
let config = default_config();
let mut sim = Simulation::new(&config, scan()).unwrap();
let rider = sim
.spawn_rider_by_stop_id(StopId(0), StopId(2), 70.0)
.unwrap();
sim.world_mut().set_patience(
rider,
crate::components::Patience {
max_wait_ticks: 1000,
waited_ticks: 500,
},
);
run_until_arrived(&mut sim, rider);
sim.settle_rider(rider).unwrap();
let stop = sim.world().rider(rider).unwrap().current_stop().unwrap();
let dest = sim.stop_entity(StopId(0)).unwrap();
let route = Route::direct(stop, dest, GroupId(0));
sim.reroute_rider(rider, route).unwrap();
let patience = sim.world().patience(rider).unwrap();
assert_eq!(patience.waited_ticks, 0);
}
#[test]
fn snapshot_roundtrip_preserves_residents() {
let config = default_config();
let mut sim = Simulation::new(&config, scan()).unwrap();
let rider = sim
.spawn_rider_by_stop_id(StopId(0), StopId(2), 70.0)
.unwrap();
run_until_arrived(&mut sim, rider);
sim.settle_rider(rider).unwrap();
let stop = sim.world().rider(rider).unwrap().current_stop().unwrap();
assert_eq!(sim.resident_count_at(stop), 1);
let snapshot = sim.snapshot();
let restored = snapshot.restore(None);
let resident_riders: Vec<_> = restored
.world()
.iter_riders()
.filter(|(_, r)| r.phase() == RiderPhase::Resident)
.collect();
assert_eq!(resident_riders.len(), 1);
let (new_rider_id, r) = resident_riders[0];
let new_stop = r.current_stop().unwrap();
assert_eq!(restored.resident_count_at(new_stop), 1);
assert!(restored.residents_at(new_stop).any(|id| id == new_rider_id));
}