1#![forbid(unsafe_code)]
2
3use std::time::{Duration, Instant};
4
5pub fn drain_until<R>(rt: &R, timeout: Duration) -> usize
6where
7 R: TestDrain,
8{
9 let deadline = Instant::now() + timeout;
10 loop {
11 let n = rt.test_drain();
12 if n > 0 || Instant::now() >= deadline {
13 return n;
14 }
15 std::thread::sleep(Duration::from_millis(2));
16 }
17}
18
19pub fn run_until<R>(rt: &R, timeout: Duration, mut predicate: impl FnMut() -> bool) -> bool
20where
21 R: TestDrain,
22{
23 let deadline = Instant::now() + timeout;
24 while Instant::now() < deadline {
25 rt.test_drain();
26 if predicate() {
27 return true;
28 }
29 std::thread::sleep(Duration::from_millis(2));
30 }
31 rt.test_drain();
32 predicate()
33}
34
35pub trait TestDrain {
36 fn test_drain(&self) -> usize;
37}
38
39impl TestDrain for crate::runtime::AsyncRuntime {
40 fn test_drain(&self) -> usize {
41 self.drain()
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48
49 struct Counter {
50 n: std::cell::Cell<usize>,
51 fire_at: usize,
52 }
53
54 impl TestDrain for Counter {
55 fn test_drain(&self) -> usize {
56 let n = self.n.get() + 1;
57 self.n.set(n);
58 if n >= self.fire_at { 1 } else { 0 }
59 }
60 }
61
62 #[test]
63 fn drain_until_returns_on_fire() {
64 let c = Counter {
65 n: std::cell::Cell::new(0),
66 fire_at: 3,
67 };
68 assert_eq!(drain_until(&c, Duration::from_secs(2)), 1);
69 }
70
71 #[test]
72 fn run_until_respects_predicate() {
73 let c = Counter {
74 n: std::cell::Cell::new(0),
75 fire_at: 100,
76 };
77 assert!(!run_until(&c, Duration::from_millis(10), || false));
78 }
79}