drumbeat/utils/
testing.rs

1use std::{sync::mpsc, thread, time::Duration};
2
3pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5);
4
5pub fn panic_after<T, F>(d: Duration, f: F) -> T
6where
7  T: Send + 'static,
8  F: FnOnce() -> T + Send + 'static,
9{
10  let (done_tx, done_rx) = mpsc::channel();
11  let handle = thread::Builder::new()
12    .name("testing-thread".to_owned())
13    .spawn(move || {
14      let val = f();
15      done_tx.send(()).expect("failed to send complete signal");
16      val
17    })
18    .unwrap();
19  match done_rx.recv_timeout(d) {
20    Ok(_) => handle.join().expect("thread panicked"),
21    Err(error) => match error {
22      mpsc::RecvTimeoutError::Timeout => panic!("thread took too long"),
23      mpsc::RecvTimeoutError::Disconnected => panic!("thread panicked"),
24    },
25  }
26}
27
28pub fn async_context<T, F>(f: F) -> T
29where
30  T: Send + 'static,
31  F: FnOnce() -> T + Send + 'static,
32{
33  panic_after(DEFAULT_TIMEOUT, f)
34}
35
36pub fn interrupt_after<T, F>(d: Duration, f: F) -> T
37where
38  T: Send + 'static,
39  F: FnOnce() -> T + Send + 'static,
40{
41  let (done_tx, done_rx) = mpsc::channel();
42  let handle = thread::Builder::new()
43    .name("testing-thread".to_owned())
44    .spawn(move || {
45      let val = f();
46      let _ = done_tx.send(());
47      val
48    })
49    .unwrap();
50  let _ = done_rx.recv_timeout(d);
51  handle.join().expect("thread panicked")
52}
53
54pub fn async_panic_context<T, F>(f: F) -> T
55where
56  T: Send + 'static,
57  F: FnOnce() -> T + Send + 'static,
58{
59  interrupt_after(DEFAULT_TIMEOUT, f)
60}
61
62#[cfg(test)]
63mod test {
64  use super::*;
65
66  #[test]
67  #[should_panic]
68  fn panic_after_timeout_test() {
69    panic_after(Duration::from_secs(0), || {
70      std::thread::sleep(Duration::from_secs(1));
71    });
72  }
73
74  #[test]
75  fn no_panic_after_timeout_test() {
76    interrupt_after(Duration::from_secs(0), || {
77      std::thread::sleep(Duration::from_secs(1));
78    });
79  }
80
81  #[test]
82  #[should_panic]
83  fn interrupt_panic_passthrough_test() {
84    interrupt_after(Duration::from_secs(1), || {
85      panic!("test");
86    });
87  }
88}