【问题标题】:How can I silently catch panics in QuickCheck tests?如何在 QuickCheck 测试中静默捕捉恐慌?
【发布时间】:2016-07-21 21:21:03
【问题描述】:

在我的overflower_support crate 的测试中,我发现我收到了很多关于已使用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
}

但是,在函数内使用这个函数来检查有些令人惊讶的是,不仅平息了想要的消息,而且quickcheck的错误输出,这对我来说显然很有价值。即使将测试限制为一个线程也会发生这种情况。

#[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 的恐慌可见的同时从我的代码中隐藏捕获的恐慌?

【问题讨论】:

  • 您能否提供一个可重现的代码示例?如果您将quickcheck::quickcheck 包装在dont_panic 中,那么您的处理程序正在压缩错误消息是有道理的,因为quickcheck 的合同会因失败而恐慌。也许您可能会从 QuickCheck.quicktest 中获得更多的收益,它会返回 Result 而不是恐慌。
  • 我将 dont_panic(..) 中的 panic::catch_unwind(_) 调用包装在 fn check((usize, usize)) -&gt; bool 函数中,以供 quickcheck::quickcheck 检查
  • 添加了一个几乎完整的示例(应该运行一次use std::panic;)。

标签: testing rust panic


【解决方案1】:

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

你想register your own handler

【讨论】:

  • 这就是我在dont_panic(_) 中所做的。然而,不幸的是,这似乎隐藏了恐慌信息,即使它应该被采取。
  • 哦。好吧,除非你有办法使用例如。一个线程本地标志,或者PanicInfo传递给你的恐慌处理程序以某种方式区分另一个,我认为没有其他方法。
【解决方案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);
}

【讨论】:

    【解决方案3】:

    我的方法有两个问题:

    1. 测试并行运行(并且 quickcheck 似乎增加了一些并行性 它自己的,因为-j 1 似乎无法平息恐慌消息)。
    2. 消息被写入(或以其他方式被set_hook(_) 抑制)否 不管有没有catch_unwind(_)

    然而,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,但这会添加多个处理程序 倍并显着增加堆栈跟踪的大小,同时加剧 测试性能。

    【讨论】:

      猜你喜欢
      • 2014-09-21
      • 1970-01-01
      • 1970-01-01
      • 2015-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-28
      • 1970-01-01
      相关资源
      最近更新 更多