Skip to main content

aster_forge_test/
wait.rs

1//! Readiness polling helpers for integration tests.
2
3use std::future::Future;
4use std::time::{Duration, Instant};
5
6/// Polls `check` until it returns `true` or `timeout` elapses.
7///
8/// Returns whether the condition was observed before the deadline. A final attempt is made
9/// after the last sleep when the deadline passes between attempts.
10pub async fn wait_until<F, Fut>(timeout: Duration, interval: Duration, mut check: F) -> bool
11where
12    F: FnMut() -> Fut,
13    Fut: Future<Output = bool>,
14{
15    let deadline = Instant::now() + timeout;
16    loop {
17        if check().await {
18            return true;
19        }
20        if Instant::now() >= deadline {
21            return false;
22        }
23        tokio::time::sleep(interval).await;
24    }
25}
26
27#[cfg(test)]
28mod tests {
29    use super::wait_until;
30    use std::sync::Arc;
31    use std::sync::atomic::{AtomicUsize, Ordering};
32    use std::time::Duration;
33
34    #[tokio::test]
35    async fn wait_until_returns_true_once_condition_holds() {
36        let attempts = Arc::new(AtomicUsize::new(0));
37        let counter = attempts.clone();
38        let ready = wait_until(
39            Duration::from_secs(1),
40            Duration::from_millis(5),
41            move || {
42                let counter = counter.clone();
43                async move { counter.fetch_add(1, Ordering::SeqCst) + 1 >= 3 }
44            },
45        )
46        .await;
47
48        assert!(ready);
49        assert!(attempts.load(Ordering::SeqCst) >= 3);
50    }
51
52    #[tokio::test]
53    async fn wait_until_times_out_when_condition_never_holds() {
54        let ready = wait_until(
55            Duration::from_millis(30),
56            Duration::from_millis(5),
57            || async { false },
58        )
59        .await;
60
61        assert!(!ready);
62    }
63}