use std::cell::RefCell;
use std::rc::Rc;
use rand::RngCore;
use simu::SimEnv;
use simu::{all_of, any_of};
type Log = Rc<RefCell<Vec<String>>>;
fn new_log() -> Log { Rc::new(RefCell::new(Vec::new())) }
#[test]
fn single_timeout_advances_time() {
let mut env = SimEnv::with_seed(0);
let h = env.handle();
let log = new_log();
let log2 = log.clone();
env.spawn(async move {
h.timeout(10.0).await;
log2.borrow_mut().push(format!("{}", h.now()));
});
env.run();
assert_eq!(env.now(), 10.0);
assert_eq!(*log.borrow(), vec!["10"]);
}
#[test]
fn multiple_timeouts_fire_in_time_order() {
let mut env = SimEnv::with_seed(0);
let log = new_log();
for delay in [30.0_f64, 10.0, 20.0] {
let h = env.handle();
let log2 = log.clone();
env.spawn(async move {
h.timeout(delay).await;
log2.borrow_mut().push(format!("{}", h.now()));
});
}
env.run();
assert_eq!(*log.borrow(), vec!["10", "20", "30"]);
}
#[test]
fn run_until_stops_at_boundary() {
let mut env = SimEnv::with_seed(0);
let log = new_log();
for delay in [5.0_f64, 15.0] {
let h = env.handle();
let log2 = log.clone();
env.spawn(async move {
h.timeout(delay).await;
log2.borrow_mut().push(format!("{}", h.now()));
});
}
env.run_until(10.0);
assert_eq!(env.now(), 10.0);
assert_eq!(*log.borrow(), vec!["5"]);
}
#[test]
fn zero_delay_timeout() {
let mut env = SimEnv::with_seed(0);
let h = env.handle();
let log = new_log();
let log2 = log.clone();
env.spawn(async move {
h.timeout(0.0).await;
log2.borrow_mut().push(format!("{}", h.now()));
});
env.run();
assert_eq!(env.now(), 0.0);
assert_eq!(*log.borrow(), vec!["0"]);
}
#[test]
fn deterministic_tie_breaking() {
let mut env = SimEnv::with_seed(0);
let log = new_log();
for label in ["A", "B"] {
let h = env.handle();
let log2 = log.clone();
let label = label.to_string();
env.spawn(async move {
h.timeout(5.0).await;
log2.borrow_mut().push(label);
});
}
env.run();
assert_eq!(*log.borrow(), vec!["A", "B"]);
}
#[test]
fn simenv_new_creates_valid_env() {
let mut env = SimEnv::new();
let h = env.handle();
let log = new_log();
let log2 = log.clone();
env.spawn(async move {
h.timeout(1.0).await;
log2.borrow_mut().push("done".to_string());
});
env.run();
assert_eq!(env.now(), 1.0);
assert_eq!(*log.borrow(), vec!["done"]);
}
#[test]
fn simenv_timeout_method() {
let mut env = SimEnv::with_seed(0);
let t = env.timeout(5.0); let log = new_log();
let log2 = log.clone();
let h = env.handle();
env.spawn(async move {
t.await;
log2.borrow_mut().push(format!("{}", h.now()));
});
env.run();
assert_eq!(*log.borrow(), vec!["5"]);
}
#[test]
fn timeout_repoll_before_deadline_returns_pending() {
let mut env = SimEnv::with_seed(0);
let h = env.handle();
let log = new_log();
let log2 = log.clone();
env.spawn(async move {
all_of![h.timeout(1.0), h.timeout(3.0), h.timeout(5.0)].await;
log2.borrow_mut().push(format!("done:{}", h.now()));
});
env.run();
assert_eq!(env.now(), 5.0);
assert_eq!(*log.borrow(), vec!["done:5"]);
}
#[test]
fn any_of_first_pass_all_pending() {
let mut env = SimEnv::with_seed(0);
let h = env.handle();
let log = new_log();
let log2 = log.clone();
env.spawn(async move {
any_of![h.timeout(3.0), h.timeout(5.0), h.timeout(7.0)].await;
log2.borrow_mut().push(format!("first:{}", h.now()));
});
env.run();
assert_eq!(*log.borrow(), vec!["first:3"]);
}
#[test]
fn rng_guard_covers_all_rngcore_methods() {
let env = SimEnv::with_seed(0);
let h = env.handle();
let _ = h.rng().next_u32();
let _ = h.rng().next_u64();
let mut buf = [0u8; 8];
h.rng().fill_bytes(&mut buf);
assert_ne!(buf, [0u8; 8], "fill_bytes should write non-zero bytes");
}
#[test]
fn run_until_does_not_rewind_past_boundary() {
let mut env = SimEnv::with_seed(0);
let h = env.handle();
env.spawn(async move {
h.timeout(10.0).await;
});
env.run();
assert_eq!(env.now(), 10.0);
env.run_until(5.0);
assert_eq!(env.now(), 10.0, "run_until rewound the clock");
}
#[test]
fn run_until_advances_to_boundary_when_queue_empties_early() {
let mut env = SimEnv::with_seed(0);
let h = env.handle();
env.spawn(async move {
h.timeout(3.0).await;
});
env.run_until(10.0);
assert_eq!(env.now(), 10.0);
}
#[test]
#[should_panic(expected = "timeout delay must be finite and non-negative")]
fn negative_timeout_delay_panics() {
let mut env = SimEnv::with_seed(0);
let h = env.handle();
env.spawn(async move {
h.timeout(-5.0).await;
});
env.run();
}
#[test]
#[should_panic(expected = "timeout delay must be finite and non-negative")]
fn nan_timeout_delay_panics() {
let mut env = SimEnv::with_seed(0);
let h = env.handle();
env.spawn(async move {
h.timeout(f64::NAN).await;
});
env.run();
}
#[test]
#[should_panic(expected = "timeout delay must be finite and non-negative")]
fn infinite_timeout_delay_panics() {
let mut env = SimEnv::with_seed(0);
let h = env.handle();
env.spawn(async move {
h.timeout(f64::INFINITY).await;
});
env.run();
}
#[test]
fn zero_timeout_delay_is_allowed() {
let mut env = SimEnv::with_seed(0);
let log = new_log();
let l2 = log.clone();
let h = env.handle();
env.spawn(async move {
h.timeout(0.0).await;
l2.borrow_mut().push(format!("{}", h.now()));
});
env.run();
assert_eq!(*log.borrow(), vec!["0"]);
}