use std::thread::sleep;
use std::time::{Duration, Instant};
pub const DEFAULT_POLL_INTERVAL: Duration = Duration::from_millis(20);
pub fn wait_for<T>(deadline: Duration, desc: &str, mut probe: impl FnMut() -> Option<T>) -> T {
let start = Instant::now();
loop {
if let Some(value) = probe() {
return value;
}
assert!(
start.elapsed() < deadline,
"condition not met within {deadline:?}: {desc}"
);
sleep(DEFAULT_POLL_INTERVAL);
}
}
#[allow(clippy::unwrap_used)]
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
#[test]
fn returns_probe_value_when_immediately_satisfied() {
let value = wait_for(Duration::from_secs(1), "always true", || Some(42));
assert_eq!(value, 42);
}
#[test]
fn polls_until_condition_becomes_true() {
let ticks = Arc::new(AtomicU64::new(0));
let seen = Arc::clone(&ticks);
let value = wait_for(Duration::from_secs(2), "third tick", move || {
let n = seen.fetch_add(1, Ordering::Release);
(n >= 2).then_some(n)
});
assert_eq!(value, 2);
}
#[test]
#[should_panic(expected = "condition not met within")]
fn panics_with_description_after_deadline() {
wait_for::<()>(Duration::from_millis(50), "impossible condition", || None);
}
}