reifydb_testing/util/
wait.rs

1// Copyright (c) reifydb.com 2025
2// This file is licensed under the AGPL-3.0-or-later, see license.md file
3
4//! Wait utilities for testing
5//!
6//! Provides utilities for waiting on conditions in tests without using fixed
7//! sleeps, making tests both faster and more reliable.
8
9use std::time::{Duration, Instant};
10
11use tokio::time::sleep;
12
13/// Default timeout for wait operations (5 seconds)
14pub const DEFAULT_TIMEOSVT: Duration = Duration::from_secs(5);
15
16/// Default poll interval (1 millisecond)
17pub const DEFAULT_POLL_INTERVAL: Duration = Duration::from_millis(1);
18
19/// Wait for a condition to become true, polling at regular intervals
20///
21/// # Arguments
22/// * `condition` - A closure that returns true when the wait should end
23/// * `timeout` - Maximum time to wait before panicking
24/// * `poll_interval` - How often to check the condition
25/// * `timeout_message` - Message to display if timeout occurs
26///
27/// # Panics
28/// Panics if the condition doesn't become true within the timeout period
29pub async fn wait_for_condition<F>(condition: F, timeout: Duration, poll_interval: Duration, timeout_message: &str)
30where
31	F: Fn() -> bool,
32{
33	let start = Instant::now();
34
35	while !condition() {
36		if start.elapsed() > timeout {
37			panic!("Timeout after {:?}: {}", timeout, timeout_message);
38		}
39		sleep(poll_interval).await;
40	}
41}
42
43/// Wait for a condition with default timeout and poll interval
44///
45/// Uses a 1-second timeout and 1ms poll interval
46pub async fn wait_for<F>(condition: F, message: &str)
47where
48	F: Fn() -> bool,
49{
50	wait_for_condition(condition, DEFAULT_TIMEOSVT, DEFAULT_POLL_INTERVAL, message).await;
51}
52
53#[cfg(test)]
54mod tests {
55	use std::{
56		sync::{Arc, Mutex},
57		thread,
58	};
59
60	use super::*;
61
62	#[tokio::test]
63	async fn test_wait_for_immediate() {
64		// Condition is already true
65		wait_for(|| true, "Should not timeout").await;
66	}
67
68	#[tokio::test]
69	async fn test_wait_for_becomes_true() {
70		let counter = Arc::new(Mutex::new(0));
71		let counter_clone = counter.clone();
72
73		thread::spawn(move || {
74			std::thread::sleep(Duration::from_millis(50));
75			*counter_clone.lock().unwrap() = 5;
76		});
77
78		wait_for(|| *counter.lock().unwrap() == 5, "Counter should reach 5").await;
79
80		assert_eq!(*counter.lock().unwrap(), 5);
81	}
82
83	#[tokio::test]
84	#[should_panic(expected = "Timeout after")]
85	async fn test_wait_for_timeout() {
86		wait_for_condition(
87			|| false,
88			Duration::from_millis(10),
89			Duration::from_millis(1),
90			"Condition never becomes true",
91		)
92		.await;
93	}
94}