use std::cell::RefCell;
use std::rc::Rc;
use rand::RngCore;
use simu::SimEnv;
use simu::Resource;
type Log = Rc<RefCell<Vec<String>>>;
fn new_log() -> Log { Rc::new(RefCell::new(Vec::new())) }
fn run_job_shop(seed: u64, rng_durations: bool) -> (Vec<String>, f64) {
let mut env = SimEnv::with_seed(seed);
let h = env.handle();
let (trigger, awaitable) = env.event();
let machine = Resource::new(1);
let log = new_log();
{
let h = h.clone();
let log2 = log.clone();
env.spawn(async move {
h.timeout(3.0).await;
trigger.fire();
log2.borrow_mut().push(format!("gate:{}", h.now()));
});
}
for n in 1..=3_u32 {
let h = h.clone();
let machine = machine.clone();
let aw = awaitable.clone();
let log2 = log.clone();
let duration: f64 = if rng_durations {
1.0 + (h.rng().next_u64() % 4) as f64
} else {
2.0
};
env.spawn(async move {
aw.await;
let _guard = machine.request().await;
h.timeout(duration).await;
log2.borrow_mut().push(format!("job{}_done:{}", n, h.now()));
});
}
env.run();
let final_time = env.now();
(Rc::try_unwrap(log).unwrap().into_inner(), final_time)
}
#[test]
fn test_system_all_primitives() {
let (log, final_time) = run_job_shop(42, false);
assert_eq!(
log,
vec!["gate:3", "job1_done:5", "job2_done:7", "job3_done:9"],
);
assert_eq!(final_time, 9.0);
}
#[test]
fn test_system_determinism() {
let (log_42a, _) = run_job_shop(42, true);
let (log_42b, _) = run_job_shop(42, true);
let (log_99, _) = run_job_shop(99, true);
assert_eq!(log_42a, log_42b, "same seed must produce identical results");
assert_ne!(log_42a, log_99, "different seeds should produce different results");
}
#[test]
fn monte_carlo_run() {
let results = simu::monte_carlo::run(0..4, |seed| {
let mut env = SimEnv::with_seed(seed);
let h = env.handle();
env.spawn(async move { h.timeout(seed as f64).await; });
env.run();
env.now()
});
assert_eq!(results, vec![0.0, 1.0, 2.0, 3.0]);
}
#[test]
fn dropping_env_reclaims_suspended_processes() {
use std::rc::Weak;
let probe = Rc::new(());
let weak: Weak<()> = Rc::downgrade(&probe);
{
let mut env = SimEnv::with_seed(0);
let (_trigger, awaitable) = env.event();
env.spawn(async move {
let _held = probe; awaitable.await; });
env.run();
assert!(weak.upgrade().is_some(), "process should be alive during the run");
}
assert!(
weak.upgrade().is_none(),
"suspended process leaked: SimState↔process cycle not broken on drop"
);
}
#[test]
#[should_panic(expected = "worker boom")]
fn monte_carlo_propagates_worker_panic() {
let _ = simu::monte_carlo::run(0..8u64, |seed| {
if seed == 5 {
panic!("worker boom");
}
seed * 2
});
}
#[test]
fn monte_carlo_accepts_borrowing_closure() {
let offsets: Vec<u64> = vec![100, 200, 300];
let results = simu::monte_carlo::run(0..3u64, |seed| {
offsets[seed as usize] + seed
});
assert_eq!(results, vec![100, 201, 302]);
assert_eq!(offsets.len(), 3); }