use std::sync::Once;
use std::sync::atomic::{AtomicU64, Ordering};
static PANICS: AtomicU64 = AtomicU64::new(0);
static INSTALL: Once = Once::new();
#[derive(Debug, Clone, Copy)]
pub struct PanicCounter {
_installed: (),
}
impl PanicCounter {
#[must_use]
pub fn install() -> Self {
INSTALL.call_once(|| {
let previous = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
PANICS.fetch_add(1, Ordering::Relaxed);
previous(info);
}));
});
Self { _installed: () }
}
#[must_use]
pub fn total(self) -> u64 {
PANICS.load(Ordering::Relaxed)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_panic_is_counted_whether_or_not_it_is_caught() {
let counter = PanicCounter::install();
let before = counter.total();
let caught = std::panic::catch_unwind(|| panic!("counted, then caught"));
let joined = std::thread::spawn(|| panic!("counted on another thread")).join();
assert!(
caught.is_err() && joined.is_err(),
"precondition: both panicked"
);
assert!(
counter.total() >= before + 2,
"{} panics counted after two, from {before}",
counter.total()
);
}
}