如何在QuickCheck测试中默默地捕捉恐慌?

时间:2016-07-21 21:21:03

标签: testing rust panic

在我的overflower_support箱子的测试中,我发现我收到很多关于已经使用std::panic::catch_unwind(_)处理的恐慌的虚假报告。这有点不幸,因为它掩盖了可能发生的真实错误。消息如下:

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

为了平息那些分散注意力的消息,我引入了dont_panic(..)函数,它劫持了恐慌处理程序,调用了一个闭包并在完成时重置了恐慌处理程序,返回了闭包结果。它看起来像这样:

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
}

然而,在功能中使用此功能有点令人惊讶地不仅可以消除所需的消息,还可以快速检查错误输出,这对我来说显然很有价值。即使将测试限制在一个线程中也会发生这种情况。

#[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);
}

如何在保持QuickCheck的恐慌可见的同时隐藏我的代码中的抓捕恐慌?

3 个答案:

答案 0 :(得分:2)

默认的恐慌处理程序无条件地在stderr上打印恐慌信息。

您想要register your own handler

答案 1 :(得分:2)

我遇到了同样的问题和其他一些问题,最后我写了一个箱子来解决它们:

panic-control

有了它,您的示例可能会通过在“安静”的线程中运行来解决(假设您对专门使用catch_unwind不感兴趣):

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);
}

答案 2 :(得分:1)

我的方法存在两个问题:

  1. 测试并行运行(并且快速检查似乎增加了一些并行性 它本身,因为-j 1似乎无法平息恐慌信息。)
  2. 邮件被写入(或被set_hook(_)压制)no 如果有catch_unwind(_),则无关紧要。
  3. 然而,dpc.pw的想法是基于恐慌处理程序中的文件进行区分 发现。之前我改变了我的方法来调用install_handler()函数 调用quickcheck(_),我在这里完整地重现:

    use std::panic;
    use std::sync::{Once, ONCE_INIT};
    
    static HANDLER : Once = ONCE_INIT;
    
    fn install_handler() {
        HANDLER.call_once(|| {
            let p = panic::take_hook();
            panic::set_hook(Box::new(move|info| {
                if info.location().map_or(false, |l| l.file() != "src/lib.rs" &&
                        !l.file().ends_with("/num/mod.rs")) {
                    p(info);
                }
            }));
        })
    }
    

    如果恐慌来自src/lib.rs,那将会平息恐慌信息 是我的overflower_support代码)或/num/mod.rs的某个地方(因为 Rust libcore代码也可能会出现恐慌。

    请注意,您可以省略Once,但这会将处理程序多次添加 时间并且在加剧时大大增加堆栈轨迹的大小 测试表现。