1 个不稳定版本

0.0.0 2023年3月24日

#53#覆盖率

MIT/Apache

10KB
117

实验性/工作进度中

目前 lib.rs 中只包含一个 PoC

使用故障注入检查测试覆盖率

Fawlty 可以在检测点自动注入故障,并通过重启测试并使用先前运行的状态来遍历测试的所有潜在错误路径。它可以用来提供有关特定错误是否在应用程序中得到充分处理的信息。Fawlty 只在调试构建(debug_assertions)中启用。在发布构建中,它发出未检测代码。

示例

使用 fawlty 检测的代码只使用 fawlty! 宏,该宏有 2 种形式

语句 fawlty!();

在调用栈上创建一个检查点。至少任何调用 fawlty 检测函数的函数都应该在开始时调用此函数一次。

表达式 fawlty![good, bad..]

具有与返回类型相同类型的表达式列表。这是可以将故障注入程序的语句。第一个必须是 good 情况,当 fawlty 禁用时也使用此情况。可以有任意数量的 bad 表达式,模拟各种故障。这些可以是任意复杂的(使用括号)。故障注入将按顺序尝试这些,最后按顺序反向。

use fawlty::*;

fn main() {
    // enabling fawlty manually (FIXME: test driver)
    fawlty_enable();

    // any caller should put a checkpoint on the stack
    fawlty!();

    fn should_be_true() -> bool {
        // lets inject a false here
        fawlty![true, false]
    }

    // First call injects false (reverse order)
    assert_eq!(should_be_true(), false);
    // Any further call will return true
    assert_eq!(should_be_true(), true);
    assert_eq!(should_be_true(), true);

    fn zero_or_none() -> Option<u32> {
        fawlty![Some(0), None]
    }

    fn match_expr() -> &'static str {
        // injection can be done at any expression
        match fawlty![zero_or_none(), Some(1), None] {
            Some(0) => "zero",
            None => "none",
            _ => "oops",
        }
    }

    assert_eq!(match_expr(), "none");
    assert_eq!(match_expr(), "oops");
    assert_eq!(match_expr(), "none");
    assert_eq!(match_expr(), "zero");
}

算法细节

Fawlty 通过将调用者、线程名、文件、行和列一起散列来跟踪导致检测表达式的过程路径。因此,可以识别出所有到达检测点的独特方式。然后,键值状态存储跟踪迄今为止看到的所有路径的状态。

当测试启用 fawlty 运行时,它将正常进行,直到遇到检测表达式。然后,将咨询状态存储,将给定列表中的最后一个(bad)表达式注入新的检测路径。如果该路径之前已看到,则选择下一个(反向)表达式进行注入,直到最终到达第一个 good 表达式。任何 bad 注入都将禁用此次运行的进一步故障注入。因此,fawlty(在当前时间)将为每次运行注入一个单个故障,后续运行将遍历所有可能的单个故障代码路径。将来,这可能将修改为处理多个故障,这可能需要更高的(指数级)成本来运行测试。

目前,fawlty 使用需要检测的阴影堆栈跟踪调用路径。这是选择它的原因是因为它是便携式安全-rust,适用于所有平台,并且非常高效。未来的版本可能会(可选地)通过 BacktraceFrames 跟踪调用路径,当在 rust 中稳定时。

并发性

已损坏! 目前,将无序地在所有线程中注入故障,这会导致非确定性执行。以后的版本将以更有序的方式执行,每个线程只注入一个错误,遍历所有线程。

依赖项

~1–6.5MB
~21K SLoC