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>12fn 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>>44fn 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