1use std::{
80 collections::HashSet,
81 panic,
82 sync::{Mutex, OnceLock},
83 thread,
84 time::Duration,
85};
86
87mod macros;
88
89fn ignore_threads() -> &'static Mutex<HashSet<String>> {
90 static INSTANCE: OnceLock<Mutex<HashSet<String>>> = OnceLock::new();
91 INSTANCE.get_or_init(|| {
92 let panic_hook = panic::take_hook();
94 panic::set_hook(Box::new(move |panic_info| {
96 let ignore_threads = ignore_threads().lock().expect("lock ignore threads");
97 if let Some(thread_name) = thread::current().name() {
98 if !ignore_threads.contains(thread_name) {
99 panic_hook(panic_info);
101 }
102 } else {
103 panic_hook(panic_info);
105 }
106 }));
107 Mutex::new(HashSet::new())
108 })
109}
110
111struct IgnoreGuard;
112
113impl IgnoreGuard {
114 fn new() -> IgnoreGuard {
115 if let Some(thread_name) = thread::current().name() {
116 ignore_threads()
117 .lock()
118 .expect("lock ignore threads")
119 .insert(thread_name.to_string());
120 }
121 IgnoreGuard
122 }
123}
124
125impl Drop for IgnoreGuard {
126 fn drop(&mut self) {
127 if let Some(thread_name) = thread::current().name() {
128 ignore_threads()
129 .lock()
130 .expect("lock ignore threads")
131 .remove(thread_name);
132 }
133 }
134}
135
136pub fn that<A, R>(repetitions: usize, delay: Duration, assert: A) -> R
159where
160 A: Fn() -> R,
161{
162 let ignore_guard = IgnoreGuard::new();
164
165 for _ in 0..(repetitions - 1) {
166 let result = panic::catch_unwind(panic::AssertUnwindSafe(&assert));
168 if let Ok(value) = result {
170 return value;
171 }
172 thread::sleep(delay);
174 }
175
176 drop(ignore_guard);
178
179 assert()
181}
182
183#[cfg(feature = "async")]
184pub async fn that_async<A, F, R>(repetitions: usize, delay: Duration, assert: A) -> R
186where
187 A: Fn() -> F,
188 F: std::future::Future<Output = R>,
189{
190 use futures::future::FutureExt;
191
192 let ignore_guard = IgnoreGuard::new();
194
195 for _ in 0..(repetitions - 1) {
196 let result = panic::AssertUnwindSafe(assert()).catch_unwind().await;
198 if let Ok(value) = result {
200 return value;
201 }
202 tokio::time::sleep(delay).await;
204 }
205
206 drop(ignore_guard);
208
209 assert().await
211}
212
213pub fn with_catch<A, C, R>(
235 repetitions: usize,
236 delay: Duration,
237 repetitions_catch: usize,
238 catch: C,
239 assert: A,
240) -> R
241where
242 A: Fn() -> R,
243 C: FnOnce(),
244{
245 let ignore_guard = IgnoreGuard::new();
246
247 for _ in 0..repetitions_catch {
248 let result = panic::catch_unwind(panic::AssertUnwindSafe(&assert));
250 if let Ok(value) = result {
252 return value;
253 }
254 thread::sleep(delay);
256 }
257
258 let thread_name = thread::current()
259 .name()
260 .unwrap_or("<unnamed thread>")
261 .to_string();
262 println!("{}: executing repeated-assert catch block", thread_name);
263 catch();
264
265 for _ in repetitions_catch..(repetitions - 1) {
266 let result = panic::catch_unwind(panic::AssertUnwindSafe(&assert));
268 if let Ok(value) = result {
270 return value;
271 }
272 thread::sleep(delay);
274 }
275
276 drop(ignore_guard);
278
279 assert()
281}
282
283#[cfg(feature = "async")]
284pub async fn with_catch_async<A, F, C, G, R>(
286 repetitions: usize,
287 delay: Duration,
288 repetitions_catch: usize,
289 catch: C,
290 assert: A,
291) -> R
292where
293 A: Fn() -> F,
294 F: std::future::Future<Output = R>,
295 C: FnOnce() -> G,
296 G: std::future::Future<Output = ()>,
297{
298 use futures::future::FutureExt;
299
300 let ignore_guard = IgnoreGuard::new();
301
302 for _ in 0..repetitions_catch {
303 let result = panic::AssertUnwindSafe(assert()).catch_unwind().await;
305 if let Ok(value) = result {
307 return value;
308 }
309 tokio::time::sleep(delay).await;
311 }
312
313 let thread_name = thread::current()
314 .name()
315 .unwrap_or("<unnamed thread>")
316 .to_string();
317 println!("{}: executing repeated-assert catch block", thread_name);
318 catch().await;
319
320 for _ in repetitions_catch..(repetitions - 1) {
321 let result = panic::AssertUnwindSafe(assert()).catch_unwind().await;
323 if let Ok(value) = result {
325 return value;
326 }
327 tokio::time::sleep(delay).await;
329 }
330
331 drop(ignore_guard);
333
334 assert().await
336}
337
338#[cfg(test)]
339mod tests {
340 use crate as repeated_assert;
341 use std::sync::{Arc, Mutex};
342 use std::thread;
343 use std::time::Duration;
344
345 static STEP_MS: u64 = 100;
346
347 fn spawn_thread(x: Arc<Mutex<i32>>) {
348 thread::spawn(move || loop {
349 thread::sleep(Duration::from_millis(10 * STEP_MS));
350 if let Ok(mut x) = x.lock() {
351 *x += 1;
352 }
353 });
354 }
355
356 #[test]
379 fn single_success() {
380 let x = Arc::new(Mutex::new(0));
381
382 spawn_thread(x.clone());
383
384 repeated_assert::that(5, Duration::from_millis(5 * STEP_MS), || {
385 assert!(*x.lock().unwrap() > 0);
386 });
387 }
388
389 #[cfg(feature = "async")]
390 #[tokio::test]
391 async fn single_success_async() {
392 let x = Arc::new(Mutex::new(0));
393
394 spawn_thread(x.clone());
395
396 repeated_assert::that_async(5, Duration::from_millis(5 * STEP_MS), || async {
397 assert!(*x.lock().unwrap() > 0);
398 })
399 .await;
400 }
401
402 #[test]
403 #[should_panic(expected = "assertion failed: *x.lock().unwrap() > 0")]
404 fn single_failure() {
405 let x = Arc::new(Mutex::new(0));
406
407 spawn_thread(x.clone());
408
409 repeated_assert::that(3, Duration::from_millis(STEP_MS), || {
410 assert!(*x.lock().unwrap() > 0);
411 });
412 }
413
414 #[cfg(feature = "async")]
415 #[tokio::test]
416 #[should_panic(expected = "assertion failed: *x.lock().unwrap() > 0")]
417 async fn single_failure_async() {
418 let x = Arc::new(Mutex::new(0));
419
420 spawn_thread(x.clone());
421
422 repeated_assert::that_async(3, Duration::from_millis(STEP_MS), || async {
423 assert!(*x.lock().unwrap() > 0);
424 })
425 .await;
426 }
427
428 #[test]
429 fn multiple_success() {
430 let x = Arc::new(Mutex::new(0));
431 let a = 11;
432 let b = 11;
433
434 spawn_thread(x.clone());
435
436 repeated_assert::that(5, Duration::from_millis(5 * STEP_MS), || {
437 assert!(*x.lock().unwrap() > 0);
438 assert_eq!(a, b);
439 });
440 }
441
442 #[cfg(feature = "async")]
443 #[tokio::test]
444 async fn multiple_success_async() {
445 let x = Arc::new(Mutex::new(0));
446 let a = 11;
447 let b = 11;
448
449 spawn_thread(x.clone());
450
451 repeated_assert::that_async(5, Duration::from_millis(5 * STEP_MS), || async {
452 assert!(*x.lock().unwrap() > 0);
453 assert_eq!(a, b);
454 })
455 .await;
456 }
457
458 #[test]
459 #[should_panic(expected = "assertion failed: *x.lock().unwrap() > 0")]
460 fn multiple_failure_1() {
461 let x = Arc::new(Mutex::new(0));
462 let a = 11;
463 let b = 11;
464
465 spawn_thread(x.clone());
466
467 repeated_assert::that(3, Duration::from_millis(STEP_MS), || {
468 assert!(*x.lock().unwrap() > 0);
469 assert_eq!(a, b);
470 });
471 }
472
473 #[cfg(feature = "async")]
474 #[tokio::test]
475 #[should_panic(expected = "assertion failed: *x.lock().unwrap() > 0")]
476 async fn multiple_failure_1_async() {
477 let x = Arc::new(Mutex::new(0));
478 let a = 11;
479 let b = 11;
480
481 spawn_thread(x.clone());
482
483 repeated_assert::that_async(3, Duration::from_millis(STEP_MS), || async {
484 assert!(*x.lock().unwrap() > 0);
485 assert_eq!(a, b);
486 })
487 .await;
488 }
489
490 #[test]
491 #[should_panic(expected = "assertion `left == right` failed")]
492 fn multiple_failure_2() {
493 let x = Arc::new(Mutex::new(0));
494 let a = 11;
495 let b = 12;
496
497 spawn_thread(x.clone());
498
499 repeated_assert::that(5, Duration::from_millis(5 * STEP_MS), || {
500 assert!(*x.lock().unwrap() > 0);
501 assert_eq!(a, b);
502 });
503 }
504
505 #[cfg(feature = "async")]
506 #[tokio::test]
507 #[should_panic(expected = "assertion `left == right` failed")]
508 async fn multiple_failure_2_async() {
509 let x = Arc::new(Mutex::new(0));
510 let a = 11;
511 let b = 12;
512
513 spawn_thread(x.clone());
514
515 repeated_assert::that_async(5, Duration::from_millis(5 * STEP_MS), || async {
516 assert!(*x.lock().unwrap() > 0);
517 assert_eq!(a, b);
518 })
519 .await;
520 }
521
522 #[test]
523 fn catch() {
524 let x = Arc::new(Mutex::new(-1_000));
525
526 spawn_thread(x.clone());
527
528 repeated_assert::with_catch(
529 10,
530 Duration::from_millis(5 * STEP_MS),
531 5,
532 || {
533 *x.lock().unwrap() = 0;
534 },
535 || {
536 assert!(*x.lock().unwrap() > 0);
537 },
538 );
539 }
540
541 #[cfg(feature = "async")]
542 #[tokio::test]
543 async fn catch_async() {
544 let x = Arc::new(Mutex::new(-1_000));
545
546 spawn_thread(x.clone());
547
548 repeated_assert::with_catch_async(
549 10,
550 Duration::from_millis(5 * STEP_MS),
551 5,
552 || async {
553 *x.lock().unwrap() = 0;
554 },
555 || async {
556 assert!(*x.lock().unwrap() > 0);
557 },
558 )
559 .await;
560 }
561}