1#![warn(rust_2018_idioms)]
2#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support panic recovery
3
4use parking_lot::{const_mutex, Mutex};
5use std::error::Error;
6use std::panic;
7use std::sync::Arc;
8use tokio::runtime::Runtime;
9use tokio::sync::mpsc::channel;
10use tokio::time::{Duration, Instant};
11use tokio_test::task;
12use tokio_util::io::SyncIoBridge;
13use tokio_util::sync::PollSender;
14use tokio_util::task::LocalPoolHandle;
15use tokio_util::time::DelayQueue;
16
17// Taken from tokio-util::time::wheel, if that changes then
18const MAX_DURATION_MS: u64 = (1 << (36)) - 1;
19
20fn test_panic<Func: FnOnce() + panic::UnwindSafe>(func: Func) -> Option<String> {
21 static PANIC_MUTEX: Mutex<()> = const_mutex(());
22
23 {
24 let _guard = PANIC_MUTEX.lock();
25 let panic_file: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
26
27 let prev_hook = panic::take_hook();
28 {
29 let panic_file = panic_file.clone();
30 panic::set_hook(Box::new(move |panic_info| {
31 let panic_location = panic_info.location().unwrap();
32 panic_file
33 .lock()
34 .clone_from(&Some(panic_location.file().to_string()));
35 }));
36 }
37
38 let result = panic::catch_unwind(func);
39 // Return to the previously set panic hook (maybe default) so that we get nice error
40 // messages in the tests.
41 panic::set_hook(prev_hook);
42
43 if result.is_err() {
44 panic_file.lock().clone()
45 } else {
46 None
47 }
48 }
49}
50
51#[test]
52fn sync_bridge_new_panic_caller() -> Result<(), Box<dyn Error>> {
53 let panic_location_file = test_panic(|| {
54 let _ = SyncIoBridge::new(tokio::io::empty());
55 });
56
57 // The panic location should be in this file
58 assert_eq!(&panic_location_file.unwrap(), file!());
59
60 Ok(())
61}
62
63#[test]
64fn poll_sender_send_item_panic_caller() -> Result<(), Box<dyn Error>> {
65 let panic_location_file = test_panic(|| {
66 let (send, _) = channel::<u32>(3);
67 let mut send = PollSender::new(send);
68
69 let _ = send.send_item(42);
70 });
71
72 // The panic location should be in this file
73 assert_eq!(&panic_location_file.unwrap(), file!());
74
75 Ok(())
76}
77
78#[test]
79
80fn local_pool_handle_new_panic_caller() -> Result<(), Box<dyn Error>> {
81 let panic_location_file = test_panic(|| {
82 let _ = LocalPoolHandle::new(0);
83 });
84
85 // The panic location should be in this file
86 assert_eq!(&panic_location_file.unwrap(), file!());
87
88 Ok(())
89}
90
91#[test]
92
93fn local_pool_handle_spawn_pinned_by_idx_panic_caller() -> Result<(), Box<dyn Error>> {
94 let panic_location_file = test_panic(|| {
95 let rt = basic();
96
97 rt.block_on(async {
98 let handle = LocalPoolHandle::new(2);
99 handle.spawn_pinned_by_idx(|| async { "test" }, 3);
100 });
101 });
102
103 // The panic location should be in this file
104 assert_eq!(&panic_location_file.unwrap(), file!());
105
106 Ok(())
107}
108#[test]
109fn delay_queue_insert_at_panic_caller() -> Result<(), Box<dyn Error>> {
110 let panic_location_file = test_panic(|| {
111 let rt = basic();
112 rt.block_on(async {
113 let mut queue = task::spawn(DelayQueue::with_capacity(3));
114
115 //let st = std::time::Instant::from(SystemTime::UNIX_EPOCH);
116 let _k = queue.insert_at(
117 "1",
118 Instant::now() + Duration::from_millis(MAX_DURATION_MS + 1),
119 );
120 });
121 });
122
123 // The panic location should be in this file
124 assert_eq!(&panic_location_file.unwrap(), file!());
125
126 Ok(())
127}
128
129#[test]
130fn delay_queue_insert_panic_caller() -> Result<(), Box<dyn Error>> {
131 let panic_location_file = test_panic(|| {
132 let rt = basic();
133 rt.block_on(async {
134 let mut queue = task::spawn(DelayQueue::with_capacity(3));
135
136 let _k = queue.insert("1", Duration::from_millis(MAX_DURATION_MS + 1));
137 });
138 });
139
140 // The panic location should be in this file
141 assert_eq!(&panic_location_file.unwrap(), file!());
142
143 Ok(())
144}
145
146#[test]
147fn delay_queue_remove_panic_caller() -> Result<(), Box<dyn Error>> {
148 let panic_location_file = test_panic(|| {
149 let rt = basic();
150 rt.block_on(async {
151 let mut queue = task::spawn(DelayQueue::with_capacity(3));
152
153 let key = queue.insert_at("1", Instant::now());
154 queue.remove(&key);
155 queue.remove(&key);
156 });
157 });
158
159 // The panic location should be in this file
160 assert_eq!(&panic_location_file.unwrap(), file!());
161
162 Ok(())
163}
164
165#[test]
166fn delay_queue_reset_at_panic_caller() -> Result<(), Box<dyn Error>> {
167 let panic_location_file = test_panic(|| {
168 let rt = basic();
169 rt.block_on(async {
170 let mut queue = task::spawn(DelayQueue::with_capacity(3));
171
172 let key = queue.insert_at("1", Instant::now());
173 queue.reset_at(
174 &key,
175 Instant::now() + Duration::from_millis(MAX_DURATION_MS + 1),
176 );
177 });
178 });
179
180 // The panic location should be in this file
181 assert_eq!(&panic_location_file.unwrap(), file!());
182
183 Ok(())
184}
185
186#[test]
187fn delay_queue_reset_panic_caller() -> Result<(), Box<dyn Error>> {
188 let panic_location_file = test_panic(|| {
189 let rt = basic();
190 rt.block_on(async {
191 let mut queue = task::spawn(DelayQueue::with_capacity(3));
192
193 let key = queue.insert_at("1", Instant::now());
194 queue.reset(&key, Duration::from_millis(MAX_DURATION_MS + 1));
195 });
196 });
197
198 // The panic location should be in this file
199 assert_eq!(&panic_location_file.unwrap(), file!());
200
201 Ok(())
202}
203
204#[test]
205fn delay_queue_reserve_panic_caller() -> Result<(), Box<dyn Error>> {
206 let panic_location_file = test_panic(|| {
207 let rt = basic();
208 rt.block_on(async {
209 let mut queue = task::spawn(DelayQueue::<u32>::with_capacity(3));
210
211 queue.reserve((1 << 30) as usize);
212 });
213 });
214
215 // The panic location should be in this file
216 assert_eq!(&panic_location_file.unwrap(), file!());
217
218 Ok(())
219}
220
221fn basic() -> Runtime {
222 tokio::runtime::Builder::new_current_thread()
223 .enable_all()
224 .build()
225 .unwrap()
226}
227