Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I silently catch panics in QuickCheck tests?

In the tests of my overflower_support crate, I have found that I get a lot of spurious reports of panics which are already handled using std::panic::catch_unwind(_). This is a bit unfortunate, as it obscures the real errors that may happen. The messages look like:

thread 'safe' panicked at 'arithmetic overflow', src/lib.rs:56

To quell those distracting messages, I introduced the dont_panic(..) function, which hijacks the panic handler, calls a closure and resets the panic handler when done, returning the closures result. It looks like this:

fn dont_panic<F, A, R>(args: A, f: F) -> R
    where F: Fn(A) -> R
{
    let p = panic::take_hook();
    panic::set_hook(Box::new(|_| ()));
    let result = f(args);
    panic::set_hook(p);
    result
}

However, using this function within the function to check somewhat surprisingly not only quells the desired messages, but also quickcheck's error output, which is obviously valuable to me. This occurs even when limiting tests to one thread.

#[test]
fn test_some_panic() {
    fn check(x: usize) -> bool {
        let expected = if x < 256 { Some(x) } else { None };
        let actual = dont_panic(|| panic::catch_unwind(|| { assert!(x < 256); x }).ok());
        expected == actual
    }
    quickcheck(check as fn(usize) -> bool);
}

How can I hide the caught panics from my code while keeping QuickCheck's panics visible?

like image 739
llogiq Avatar asked Jul 21 '16 21:07

llogiq


People also ask

Can you catch a panic rust?

A panic in Rust is not always implemented via unwinding, but can be implemented by aborting the process as well. This function only catches unwinding panics, not those that abort the process. Also note that unwinding into Rust code with a foreign exception (e.g. an exception thrown from C++ code) is undefined behavior.

What is a panic in Rust?

The panic! macro signals that your program is in a state it can't handle and lets you tell the process to stop instead of trying to proceed with invalid or incorrect values. The Result enum uses Rust's type system to indicate that operations might fail in a way that your code could recover from.


2 Answers

The default panic handler is printing panic information unconditionally on stderr.

You want to register your own handler.

like image 84
dpc.pw Avatar answered Nov 15 '22 10:11

dpc.pw


I've met the same problem and a few others, and I ended up writing a crate to solve them:

panic-control

With it, your example might be solved by running in a "quiet" thread (assuming you weren't interested in using catch_unwind specifically):

use panic_control::spawn_quiet;

#[test]
fn test_some_panic() {
    fn check(x: usize) -> bool {
        let expected = if x < 256 { Some(x) } else { None };
        let h = spawn_quiet(|| { assert!(x < 256); x });
        let actual = h.join().ok();
        expected == actual
    }
    quickcheck(check as fn(usize) -> bool);
}
like image 41
mzabaluev Avatar answered Nov 15 '22 08:11

mzabaluev