use rustsim_crowd::error::CrowdError;
use rustsim_crowd::{
anticipation_velocity, collision_free_speed, generalized_centrifugal_force, optimal_steps,
social_force,
};
const DT: f64 = 0.05;
#[test]
fn all_defaults_validate_at_nominal_dt() {
social_force::Params::default().validate(DT).unwrap();
collision_free_speed::Params::default()
.validate(DT)
.unwrap();
generalized_centrifugal_force::Params::default()
.validate(DT)
.unwrap();
anticipation_velocity::Params::default()
.validate(DT)
.unwrap();
optimal_steps::Params::default().validate(DT).unwrap();
}
#[test]
fn rejects_non_finite_dt() {
let p = social_force::Params::default();
assert!(matches!(
p.validate(f64::NAN),
Err(CrowdError::InvalidDt { .. })
));
assert!(matches!(
p.validate(f64::INFINITY),
Err(CrowdError::InvalidDt { .. })
));
assert!(matches!(
p.validate(-0.01),
Err(CrowdError::InvalidDt { .. })
));
assert!(matches!(p.validate(0.0), Err(CrowdError::InvalidDt { .. })));
}
#[test]
fn rejects_non_positive_mass_in_sfm() {
let p = social_force::Params {
mass: 0.0,
..social_force::Params::default()
};
assert!(matches!(
p.validate(DT),
Err(CrowdError::NonPositiveParam { param: "mass", .. })
));
}
#[test]
fn rejects_negative_arrival_radius() {
let p = social_force::Params {
arrival_radius: -0.5,
..social_force::Params::default()
};
assert!(matches!(
p.validate(DT),
Err(CrowdError::NegativeParam {
param: "arrival_radius",
..
})
));
}
#[test]
fn flags_cfl_violation_when_dt_too_large() {
let p = social_force::Params::default();
let err = p.validate(0.13).unwrap_err();
match err {
CrowdError::CflViolation {
product, max_dt, ..
} => {
assert!(product > 2.5);
assert!((max_dt - 0.125).abs() < 1e-9);
}
other => panic!("expected CflViolation, got {other:?}"),
}
}
#[test]
fn cfl_violation_also_applies_to_gcf() {
let p = generalized_centrifugal_force::Params::default();
let err = p.validate(0.13).unwrap_err();
assert!(matches!(err, CrowdError::CflViolation { .. }));
}
#[test]
fn osm_rejects_zero_num_candidates() {
let p = optimal_steps::Params {
num_candidates: 0,
..optimal_steps::Params::default()
};
assert!(matches!(
p.validate(DT),
Err(CrowdError::ZeroCount {
param: "num_candidates",
..
})
));
}
#[test]
fn avm_rejects_zero_num_directions() {
let p = anticipation_velocity::Params {
num_directions: 0,
..anticipation_velocity::Params::default()
};
assert!(matches!(
p.validate(DT),
Err(CrowdError::ZeroCount {
param: "num_directions",
..
})
));
}
#[test]
fn error_message_mentions_model_name() {
let p = social_force::Params {
mass: 0.0,
..social_force::Params::default()
};
let msg = p.validate(DT).unwrap_err().to_string();
assert!(msg.contains("SocialForce"), "msg = {msg}");
assert!(msg.contains("mass"), "msg = {msg}");
}