#![allow(clippy::unwrap_used, clippy::panic)]
use std::{
sync::Arc,
thread,
time::{Duration, Instant},
};
use super::{WatchdogOutcome, WatchdogSignal};
#[test]
fn a_finished_invocation_wakes_the_watchdog_at_once() {
const ROUNDS: u32 = 10;
let mut total_wake_gap = Duration::ZERO;
for round in 0..ROUNDS {
let signal = Arc::new(WatchdogSignal::new());
let deadline = Instant::now() + Duration::from_mins(1);
let waiter = {
let signal = Arc::clone(&signal);
thread::spawn(move || {
let outcome = signal.wait_until(deadline);
(outcome, Instant::now())
})
};
thread::sleep(Duration::from_millis(11 + u64::from(round)));
let finished_at = Instant::now();
signal.finish();
let (outcome, woke_at) = waiter.join().unwrap();
assert_eq!(outcome, WatchdogOutcome::Finished, "round {round}");
total_wake_gap += woke_at.saturating_duration_since(finished_at);
}
assert!(
total_wake_gap < Duration::from_millis(10),
"the watchdog must wake on the signal, not on a poll interval — {ROUNDS} wakes \
took {total_wake_gap:?} in total after `finish` had already been called"
);
}
#[test]
fn an_unfinished_invocation_waits_out_its_deadline() {
let signal = WatchdogSignal::new();
let budget = Duration::from_millis(120);
let started = Instant::now();
let outcome = signal.wait_until(started + budget);
let waited = started.elapsed();
assert_eq!(outcome, WatchdogOutcome::DeadlineReached);
assert!(
waited >= budget,
"the watchdog must not fire early — waited {waited:?} of a {budget:?} budget"
);
assert!(
waited < budget * 4,
"…nor drift far past it: waited {waited:?} of a {budget:?} budget"
);
}
#[test]
fn a_completion_before_the_wait_is_not_lost() {
let signal = WatchdogSignal::new();
signal.finish();
let started = Instant::now();
let outcome = signal.wait_until(started + Duration::from_mins(1));
assert_eq!(outcome, WatchdogOutcome::Finished);
assert!(started.elapsed() < Duration::from_millis(50), "it must not wait at all");
}
#[test]
fn an_expired_deadline_returns_at_once() {
let signal = WatchdogSignal::new();
let started = Instant::now();
let outcome =
signal.wait_until(started.checked_sub(Duration::from_secs(1)).expect("past instant"));
assert_eq!(outcome, WatchdogOutcome::DeadlineReached);
assert!(started.elapsed() < Duration::from_millis(50));
}
#[test]
fn finishing_twice_and_finishing_late_are_both_safe() {
let signal = WatchdogSignal::new();
assert!(!signal.is_finished());
let outcome = signal.wait_until(Instant::now() + Duration::from_millis(20));
assert_eq!(outcome, WatchdogOutcome::DeadlineReached);
signal.finish();
signal.finish();
assert!(signal.is_finished());
}