use std::future;
use std::time::Duration;
use async_trait::async_trait;
use moonpool_sim::{
SimContext, SimulationBuilder, SimulationResult, TaskProvider, TimeProvider, Workload,
};
struct SelfPerpetuatingTimerWorkload;
#[async_trait]
impl Workload for SelfPerpetuatingTimerWorkload {
fn name(&self) -> &'static str {
"self_perpetuating_timer"
}
async fn run(&mut self, ctx: &SimContext) -> SimulationResult<()> {
let time = ctx.time().clone();
ctx.task().spawn_task("keepalive", async move {
loop {
let _ = time.sleep(Duration::from_secs(30)).await;
}
});
future::pending::<()>().await;
Ok(())
}
}
#[test]
fn self_perpetuating_timer_is_caught_by_run_time_budget() {
let report = SimulationBuilder::new()
.workload(SelfPerpetuatingTimerWorkload)
.run_time_budget(Duration::from_mins(1))
.set_iterations(1)
.set_debug_seeds(vec![42])
.run();
assert!(
report.failed_runs >= 1,
"self-perpetuating timer should be reported as a deadlocked (failed) run; got report: {report:?}"
);
assert!(
report.seeds_failing.contains(&42),
"the failing seed should be surfaced for replay; got report: {report:?}"
);
}
struct WellBehavedWorkload;
#[async_trait]
impl Workload for WellBehavedWorkload {
fn name(&self) -> &'static str {
"well_behaved"
}
async fn run(&mut self, ctx: &SimContext) -> SimulationResult<()> {
for _ in 0..3 {
ctx.time().sleep(Duration::from_millis(10)).await.ok();
}
Ok(())
}
}
#[test]
fn well_behaved_workload_is_unaffected_by_budget() {
let report = SimulationBuilder::new()
.workload(WellBehavedWorkload)
.run_time_budget(Duration::from_secs(1))
.set_iterations(3)
.set_debug_seeds(vec![1, 2, 3])
.run();
assert_eq!(
report.failed_runs, 0,
"a workload that finishes inside the budget must not be flagged; got report: {report:?}"
);
assert_eq!(report.successful_runs, 3);
}