cranpose 0.1.145

Cranpose runtime and UI facade
Documentation
use std::panic::PanicHookInfo;

pub(crate) fn chained_panic_hook(
    own_hook: impl Fn(&PanicHookInfo<'_>) + Sync + Send + 'static,
    previous_hook: Box<dyn Fn(&PanicHookInfo<'_>) + Sync + Send>,
) -> Box<dyn Fn(&PanicHookInfo<'_>) + Sync + Send> {
    Box::new(move |info| {
        own_hook(info);
        previous_hook(info);
    })
}

#[cfg(test)]
mod tests {
    use std::sync::{
        Arc, Mutex,
        atomic::{AtomicBool, Ordering},
    };

    use super::*;

    static HOOK_LOCK: Mutex<()> = Mutex::new(());

    #[test]
    fn chained_hook_runs_both_the_framework_hook_and_the_previously_installed_one() {
        let _guard = HOOK_LOCK
            .lock()
            .unwrap_or_else(|poison| poison.into_inner());
        let saved_hook = std::panic::take_hook();

        let marker_fired = Arc::new(AtomicBool::new(false));
        let marker_for_previous = Arc::clone(&marker_fired);
        std::panic::set_hook(Box::new(move |_| {
            marker_for_previous.store(true, Ordering::SeqCst)
        }));
        let previous_hook = std::panic::take_hook();

        let own_fired = Arc::new(AtomicBool::new(false));
        let own_for_hook = Arc::clone(&own_fired);
        std::panic::set_hook(chained_panic_hook(
            move |_| own_for_hook.store(true, Ordering::SeqCst),
            previous_hook,
        ));

        let panicked = std::panic::catch_unwind(|| panic!("chained_panic_hook test panic"));

        std::panic::set_hook(saved_hook);

        assert!(
            panicked.is_err(),
            "the panic should have unwound and been caught"
        );
        assert!(
            own_fired.load(Ordering::SeqCst),
            "framework's own hook did not run"
        );
        assert!(
            marker_fired.load(Ordering::SeqCst),
            "previously installed hook was replaced instead of chained onto"
        );
    }
}