#![warn(rust_2018_idioms)] #![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support panic recovery use parking_lot::{const_mutex, Mutex}; use std::error::Error; use std::panic; use std::sync::Arc; use tokio::runtime::Runtime; use tokio::sync::mpsc::channel; use tokio::time::{Duration, Instant}; use tokio_test::task; use tokio_util::io::SyncIoBridge; use tokio_util::sync::PollSender; use tokio_util::task::LocalPoolHandle; use tokio_util::time::DelayQueue; // Taken from tokio-util::time::wheel, if that changes then const MAX_DURATION_MS: u64 = (1 << (36)) - 1; fn test_panic(func: Func) -> Option { static PANIC_MUTEX: Mutex<()> = const_mutex(()); { let _guard = PANIC_MUTEX.lock(); let panic_file: Arc>> = Arc::new(Mutex::new(None)); let prev_hook = panic::take_hook(); { let panic_file = panic_file.clone(); panic::set_hook(Box::new(move |panic_info| { let panic_location = panic_info.location().unwrap(); panic_file .lock() .clone_from(&Some(panic_location.file().to_string())); })); } let result = panic::catch_unwind(func); // Return to the previously set panic hook (maybe default) so that we get nice error // messages in the tests. panic::set_hook(prev_hook); if result.is_err() { panic_file.lock().clone() } else { None } } } #[test] fn sync_bridge_new_panic_caller() -> Result<(), Box> { let panic_location_file = test_panic(|| { let _ = SyncIoBridge::new(tokio::io::empty()); }); // The panic location should be in this file assert_eq!(&panic_location_file.unwrap(), file!()); Ok(()) } #[test] fn poll_sender_send_item_panic_caller() -> Result<(), Box> { let panic_location_file = test_panic(|| { let (send, _) = channel::(3); let mut send = PollSender::new(send); let _ = send.send_item(42); }); // The panic location should be in this file assert_eq!(&panic_location_file.unwrap(), file!()); Ok(()) } #[test] fn local_pool_handle_new_panic_caller() -> Result<(), Box> { let panic_location_file = test_panic(|| { let _ = LocalPoolHandle::new(0); }); // The panic location should be in this file assert_eq!(&panic_location_file.unwrap(), file!()); Ok(()) } #[test] fn local_pool_handle_spawn_pinned_by_idx_panic_caller() -> Result<(), Box> { let panic_location_file = test_panic(|| { let rt = basic(); rt.block_on(async { let handle = LocalPoolHandle::new(2); handle.spawn_pinned_by_idx(|| async { "test" }, 3); }); }); // The panic location should be in this file assert_eq!(&panic_location_file.unwrap(), file!()); Ok(()) } #[test] fn delay_queue_insert_at_panic_caller() -> Result<(), Box> { let panic_location_file = test_panic(|| { let rt = basic(); rt.block_on(async { let mut queue = task::spawn(DelayQueue::with_capacity(3)); //let st = std::time::Instant::from(SystemTime::UNIX_EPOCH); let _k = queue.insert_at( "1", Instant::now() + Duration::from_millis(MAX_DURATION_MS + 1), ); }); }); // The panic location should be in this file assert_eq!(&panic_location_file.unwrap(), file!()); Ok(()) } #[test] fn delay_queue_insert_panic_caller() -> Result<(), Box> { let panic_location_file = test_panic(|| { let rt = basic(); rt.block_on(async { let mut queue = task::spawn(DelayQueue::with_capacity(3)); let _k = queue.insert("1", Duration::from_millis(MAX_DURATION_MS + 1)); }); }); // The panic location should be in this file assert_eq!(&panic_location_file.unwrap(), file!()); Ok(()) } #[test] fn delay_queue_remove_panic_caller() -> Result<(), Box> { let panic_location_file = test_panic(|| { let rt = basic(); rt.block_on(async { let mut queue = task::spawn(DelayQueue::with_capacity(3)); let key = queue.insert_at("1", Instant::now()); queue.remove(&key); queue.remove(&key); }); }); // The panic location should be in this file assert_eq!(&panic_location_file.unwrap(), file!()); Ok(()) } #[test] fn delay_queue_reset_at_panic_caller() -> Result<(), Box> { let panic_location_file = test_panic(|| { let rt = basic(); rt.block_on(async { let mut queue = task::spawn(DelayQueue::with_capacity(3)); let key = queue.insert_at("1", Instant::now()); queue.reset_at( &key, Instant::now() + Duration::from_millis(MAX_DURATION_MS + 1), ); }); }); // The panic location should be in this file assert_eq!(&panic_location_file.unwrap(), file!()); Ok(()) } #[test] fn delay_queue_reset_panic_caller() -> Result<(), Box> { let panic_location_file = test_panic(|| { let rt = basic(); rt.block_on(async { let mut queue = task::spawn(DelayQueue::with_capacity(3)); let key = queue.insert_at("1", Instant::now()); queue.reset(&key, Duration::from_millis(MAX_DURATION_MS + 1)); }); }); // The panic location should be in this file assert_eq!(&panic_location_file.unwrap(), file!()); Ok(()) } #[test] fn delay_queue_reserve_panic_caller() -> Result<(), Box> { let panic_location_file = test_panic(|| { let rt = basic(); rt.block_on(async { let mut queue = task::spawn(DelayQueue::::with_capacity(3)); queue.reserve((1 << 30) as usize); }); }); // The panic location should be in this file assert_eq!(&panic_location_file.unwrap(), file!()); Ok(()) } fn basic() -> Runtime { tokio::runtime::Builder::new_current_thread() .enable_all() .build() .unwrap() }