term_test_support/
poll_sync.rs1use std::thread::sleep;
4use std::time::{Duration, Instant};
5
6pub const DEFAULT_POLL_INTERVAL: Duration = Duration::from_millis(20);
8
9pub fn wait_for<T>(deadline: Duration, desc: &str, mut probe: impl FnMut() -> Option<T>) -> T {
25 let start = Instant::now();
26 loop {
27 if let Some(value) = probe() {
28 return value;
29 }
30 assert!(
31 start.elapsed() < deadline,
32 "condition not met within {deadline:?}: {desc}"
33 );
34 sleep(DEFAULT_POLL_INTERVAL);
35 }
36}
37
38#[allow(clippy::unwrap_used)]
39#[cfg(test)]
40mod tests {
41 use super::*;
42 use std::sync::Arc;
43 use std::sync::atomic::{AtomicU64, Ordering};
44
45 #[test]
46 fn returns_probe_value_when_immediately_satisfied() {
47 let value = wait_for(Duration::from_secs(1), "always true", || Some(42));
48 assert_eq!(value, 42);
49 }
50
51 #[test]
52 fn polls_until_condition_becomes_true() {
53 let ticks = Arc::new(AtomicU64::new(0));
54 let seen = Arc::clone(&ticks);
55 let value = wait_for(Duration::from_secs(2), "third tick", move || {
56 let n = seen.fetch_add(1, Ordering::Release);
57 (n >= 2).then_some(n)
58 });
59 assert_eq!(value, 2);
60 }
61
62 #[test]
63 #[should_panic(expected = "condition not met within")]
64 fn panics_with_description_after_deadline() {
65 wait_for::<()>(Duration::from_millis(50), "impossible condition", || None);
66 }
67}