1use std::future::Future;
4use std::time::{Duration, Instant};
5
6pub 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}