Skip to main content

holochain/test_utils/
wait_for.rs

1#![allow(missing_docs)]
2use std::time::Duration;
3
4#[macro_export]
5macro_rules! wait_for {
6    ($wait:expr, $test:expr, $check:expr, $assert:expr) => {{
7        let mut w = $wait;
8        let assert = $assert;
9        loop {
10            let o = $test;
11            let check = $check;
12            if !w.wait_any().await || check(&o) {
13                assert(o);
14                break;
15            }
16        }
17    }};
18}
19
20#[macro_export]
21macro_rules! wait_for_10s {
22    ($test:expr, $check:expr, $assert:expr) => {{
23        let wait_for = $crate::test_utils::WaitFor::ten_s();
24        $crate::wait_for!(wait_for, $test, $check, $assert)
25    }};
26}
27
28#[macro_export]
29macro_rules! wait_for_1m {
30    ($test:expr, $check:expr, $assert:expr) => {
31        let wait_for = $crate::test_utils::WaitFor::one_m();
32        $crate::wait_for!(wait_for, $test, $check, $assert)
33    };
34}
35
36#[macro_export]
37macro_rules! assert_retry {
38    ($wait:expr, $test:expr $(, $reason:literal)?) => {
39        $crate::wait_for!($wait, $test, |x: &bool| *x, |x: bool| assert!(x $(, $reason)?))
40    };
41}
42
43#[macro_export]
44macro_rules! assert_eq_retry {
45    ($wait:expr, $test:expr, $check:expr $(, $reason:literal)?) => {
46        $crate::wait_for!($wait, $test, |x| x == &$check, |x| assert_eq!(x, $check $(, $reason)?))
47    };
48}
49
50#[macro_export]
51macro_rules! assert_retry_10s {
52    ($test:expr $(, $reason:literal)? $(,)?) => {
53        let wait_for = $crate::test_utils::WaitFor::ten_s();
54        $crate::assert_retry!(wait_for, $test  $(, $reason:literal)?)
55    };
56}
57
58#[macro_export]
59macro_rules! assert_eq_retry_10s {
60    ($test:expr, $check:expr $(, $reason:literal)? $(,)?) => {
61        let wait_for = $crate::test_utils::WaitFor::ten_s();
62        $crate::assert_eq_retry!(wait_for, $test, $check  $(, $reason:literal)?)
63    };
64}
65
66#[macro_export]
67macro_rules! assert_retry_1m {
68    ($test:expr $(, $reason:literal)? $(,)?) => {
69        let wait_for = $crate::test_utils::WaitFor::one_m();
70        $crate::assert_retry!(wait_for, $test $(, $reason:literal)?)
71    };
72}
73
74#[macro_export]
75macro_rules! assert_eq_retry_1m {
76    ($test:expr, $check:expr $(, $reason:literal)? $(,)?) => {
77        let wait_for = $crate::test_utils::WaitFor::one_m();
78        $crate::assert_eq_retry!(wait_for, $test, $check  $(, $reason:literal)?)
79    };
80}
81
82#[macro_export]
83macro_rules! assert_eq_retry_5m {
84    ($test:expr, $check:expr $(, $reason:literal)? $(,)?) => {
85        let wait_for = $crate::test_utils::WaitFor::five_m();
86        $crate::assert_eq_retry!(wait_for, $test, $check  $(, $reason:literal)?)
87    };
88}
89
90/// Generic waiting for some test property to
91/// be true. This allows early exit from waiting when
92/// the condition becomes true but will wait up to a
93/// maximum if the condition is not true.
94#[derive(Debug, Clone)]
95pub struct WaitFor {
96    num_attempts: u32,
97    attempt: u32,
98    delay: Duration,
99}
100
101impl WaitFor {
102    /// Create a new wait for from a number of attempts and delay in between attempts
103    pub fn new(total: Duration, num_attempts: u32) -> Self {
104        Self {
105            num_attempts,
106            attempt: 0,
107            delay: total / num_attempts,
108        }
109    }
110
111    /// Wait for 1s checking every 100ms.
112    pub fn one_s() -> Self {
113        Self::new(std::time::Duration::from_secs(1), 10)
114    }
115
116    /// Wait for 10s checking every 100ms.
117    pub fn ten_s() -> Self {
118        Self::new(std::time::Duration::from_secs(10), 100)
119    }
120
121    /// Wait for 1 minute checking every 500ms.
122    pub fn one_m() -> Self {
123        Self::new(std::time::Duration::from_secs(60), 120)
124    }
125
126    /// Wait for 5 minutes checking every 1000ms.
127    pub fn five_m() -> Self {
128        Self::new(std::time::Duration::from_secs(5 * 60), 5 * 60)
129    }
130
131    /// Wait for some time before trying again.
132    /// Will return false when you should stop waiting.
133    #[cfg_attr(feature = "instrument", tracing::instrument(skip(self)))]
134    pub async fn wait_any(&mut self) -> bool {
135        if self.attempt >= self.num_attempts {
136            return false;
137        }
138        self.attempt += 1;
139        tracing::debug!(attempt = ?self.attempt, out_of = ?self.num_attempts, delaying_for = ?self.delay);
140        tokio::time::sleep(self.delay).await;
141        true
142    }
143}
144
145#[tokio::test]
146async fn wait_for_tests() {
147    wait_for!(WaitFor::one_s(), true, |&x| x, |x: bool| assert!(x));
148    wait_for!(WaitFor::one_s(), false, |&x| x, |x: bool| assert!(!x));
149}