Skip to main content

windjammer_runtime/
timeout.rs

1//! Test timeout utilities
2//!
3//! Provides timeout functionality for tests.
4
5use std::panic;
6use std::sync::mpsc;
7use std::thread;
8use std::time::Duration;
9
10/// Run a test with a timeout
11///
12/// # Example
13/// ```
14/// use windjammer_runtime::timeout::with_timeout;
15/// use std::time::Duration;
16///
17/// let result = with_timeout(Duration::from_millis(100), || {
18///     // Test code that should complete quickly
19///     assert_eq!(1 + 1, 2);
20/// });
21///
22/// assert!(result.is_ok());
23/// ```
24pub fn with_timeout<F, R>(timeout: Duration, test_fn: F) -> Result<R, TimeoutError>
25where
26    F: FnOnce() -> R + Send + 'static,
27    R: Send + 'static,
28{
29    let (tx, rx) = mpsc::channel();
30
31    let _handle = thread::spawn(move || {
32        let result = panic::catch_unwind(panic::AssertUnwindSafe(test_fn));
33        let _ = tx.send(result);
34    });
35
36    match rx.recv_timeout(timeout) {
37        Ok(Ok(result)) => Ok(result),
38        Ok(Err(panic_err)) => {
39            // Test panicked (assertion failure)
40            panic::resume_unwind(panic_err);
41        }
42        Err(_) => {
43            // Timeout occurred
44            Err(TimeoutError { duration: timeout })
45        }
46    }
47}
48
49/// Timeout error
50#[derive(Debug, Clone)]
51pub struct TimeoutError {
52    pub duration: Duration,
53}
54
55impl std::fmt::Display for TimeoutError {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        write!(f, "Test timed out after {:?}", self.duration)
58    }
59}
60
61impl std::error::Error for TimeoutError {}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66    use std::thread::sleep;
67
68    #[test]
69    fn test_with_timeout_passes() {
70        let result = with_timeout(Duration::from_millis(100), || {
71            // Fast test
72            42
73        });
74
75        assert!(result.is_ok());
76        assert_eq!(result.unwrap(), 42);
77    }
78
79    #[test]
80    fn test_with_timeout_fails() {
81        let result = with_timeout(Duration::from_millis(50), || {
82            // Slow test
83            sleep(Duration::from_millis(200));
84            42
85        });
86
87        assert!(result.is_err());
88    }
89
90    #[test]
91    #[should_panic(expected = "assertion failed")]
92    fn test_with_timeout_panics() {
93        let _result = with_timeout(Duration::from_millis(100), || {
94            panic!("assertion failed");
95        });
96    }
97}