windjammer_runtime/
timeout.rs1use std::panic;
6use std::sync::mpsc;
7use std::thread;
8use std::time::Duration;
9
10pub 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 panic::resume_unwind(panic_err);
41 }
42 Err(_) => {
43 Err(TimeoutError { duration: timeout })
45 }
46 }
47}
48
49#[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 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 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}