use autumn_web::sim::scenario::{apply_ops, ops_strategy};
use autumn_web::sim::sweep::{SweepOutcome, sweep_proptest};
const DEFAULT_SEED_COUNT: u64 = 256;
fn seed_range(start: Option<&str>, count: Option<&str>) -> std::ops::Range<u64> {
let start = start
.and_then(|value| value.trim().parse().ok())
.unwrap_or(0);
let count = count
.and_then(|value| value.trim().parse().ok())
.unwrap_or(DEFAULT_SEED_COUNT);
start..start.saturating_add(count)
}
fn replay_command(seed: u64) -> String {
format!(
" replay: AUTUMN_SIM_SEED_START={seed} AUTUMN_SIM_SEEDS=1 cargo run -p autumn-web --release --features sim-testing --bin sim-sweep",
)
}
fn main() {
let seeds = seed_range(
std::env::var("AUTUMN_SIM_SEED_START").ok().as_deref(),
std::env::var("AUTUMN_SIM_SEEDS").ok().as_deref(),
);
let count = seeds.end - seeds.start;
let strategy = ops_strategy();
println!(
"sim-sweep: sweeping {count} seed(s) ({}..{}) against the account demo scenario",
seeds.start, seeds.end,
);
match sweep_proptest(seeds, &strategy, |_sim, ops| apply_ops(ops)) {
SweepOutcome::Passed { seeds_run } => {
println!("sim-sweep: PASSED — {seeds_run} seed(s), non-vacuous");
}
SweepOutcome::Failed { seeds_run, failure } => {
eprintln!("sim-sweep: FAILED after {seeds_run} seed(s)");
eprintln!("{failure}");
eprintln!("{}", replay_command(failure.seed));
std::process::exit(1);
}
SweepOutcome::Vacuous {
seeds_run,
unsatisfied,
} => {
eprintln!(
"sim-sweep: VACUOUS — {seeds_run} seed(s) all passed, but sometimes! label(s) \
were observed and never satisfied across the whole sweep: {}",
unsatisfied.into_iter().collect::<Vec<_>>().join(", ")
);
std::process::exit(1);
}
SweepOutcome::Empty => {
eprintln!(
"sim-sweep: EMPTY — AUTUMN_SIM_SEEDS={count} swept zero seeds; nothing was tested"
);
std::process::exit(1);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn env_value<'a>(command: &'a str, key: &str) -> &'a str {
command
.split_whitespace()
.find_map(|word| word.strip_prefix(key)?.strip_prefix('='))
.unwrap_or_else(|| panic!("replay command must set {key}: {command}"))
}
#[test]
fn replay_command_sweeps_exactly_the_failing_seed() {
let command = replay_command(300);
let seeds = seed_range(
Some(env_value(&command, "AUTUMN_SIM_SEED_START")),
Some(env_value(&command, "AUTUMN_SIM_SEEDS")),
);
assert_eq!(seeds, 300..301, "{command}");
}
#[test]
fn replay_command_reaches_the_largest_sweepable_seed() {
let seed = u64::MAX - 1;
let command = replay_command(seed);
let seeds = seed_range(
Some(env_value(&command, "AUTUMN_SIM_SEED_START")),
Some(env_value(&command, "AUTUMN_SIM_SEEDS")),
);
assert_eq!(seeds, seed..u64::MAX, "{command}");
}
#[test]
fn seed_range_defaults_to_the_first_256_seeds() {
assert_eq!(seed_range(None, None), 0..DEFAULT_SEED_COUNT);
assert_eq!(
seed_range(Some("0x10"), Some("many")),
0..DEFAULT_SEED_COUNT,
"hex and garbage fall back to the defaults",
);
}
#[test]
fn seed_range_starts_where_asked() {
assert_eq!(seed_range(Some("40"), Some("8")), 40..48);
}
}