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