1 #![warn(rust_2018_idioms)]
2 #![cfg(all(feature = "time", not(target_os = "wasi")))] // Wasi does not support panic recovery
3 #![cfg(panic = "unwind")]
4 
5 use parking_lot::{const_mutex, Mutex};
6 use std::error::Error;
7 use std::panic;
8 use std::sync::Arc;
9 use tokio::time::Duration;
10 use tokio_stream::{self as stream, StreamExt};
11 
test_panic<Func: FnOnce() + panic::UnwindSafe>(func: Func) -> Option<String>12 fn test_panic<Func: FnOnce() + panic::UnwindSafe>(func: Func) -> Option<String> {
13     static PANIC_MUTEX: Mutex<()> = const_mutex(());
14 
15     {
16         let _guard = PANIC_MUTEX.lock();
17         let panic_file: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
18 
19         let prev_hook = panic::take_hook();
20         {
21             let panic_file = panic_file.clone();
22             panic::set_hook(Box::new(move |panic_info| {
23                 let panic_location = panic_info.location().unwrap();
24                 panic_file
25                     .lock()
26                     .clone_from(&Some(panic_location.file().to_string()));
27             }));
28         }
29 
30         let result = panic::catch_unwind(func);
31         // Return to the previously set panic hook (maybe default) so that we get nice error
32         // messages in the tests.
33         panic::set_hook(prev_hook);
34 
35         if result.is_err() {
36             panic_file.lock().clone()
37         } else {
38             None
39         }
40     }
41 }
42 
43 #[test]
stream_chunks_timeout_panic_caller() -> Result<(), Box<dyn Error>>44 fn stream_chunks_timeout_panic_caller() -> Result<(), Box<dyn Error>> {
45     let panic_location_file = test_panic(|| {
46         let iter = vec![1, 2, 3].into_iter();
47         let stream0 = stream::iter(iter);
48 
49         let _chunk_stream = stream0.chunks_timeout(0, Duration::from_secs(2));
50     });
51 
52     // The panic location should be in this file
53     assert_eq!(&panic_location_file.unwrap(), file!());
54 
55     Ok(())
56 }
57