• 如何为Rust应用程序生成代码覆盖信息?
  • 发布于 1个月前
  • 111 热度
    0 评论
代码覆盖率是一个至关重要的度量标准,它可以很好地洞察软件质量。代码覆盖率通常以百分比表示,并提供有关软件的多少代码被一组单元测试或测试套件测试。把它想象成一个文本高亮工具,它显示代码的哪些部分经过测试,哪些部分可以考虑进行进一步测试,以提高代码的质量。

更高的代码覆盖率增加了对代码功能的信心,并降低了bug的风险。大多数代码覆盖工具都可以集成到CI管道中,这样代码覆盖就可以随着代码的增长而不断改进。在一些项目中,代码覆盖需求包含在合并策略中,以确保软件质量。由于代码覆盖工具还报告了测试套件未测试的代码区域,因此它有助于确定测试的重点区域。在某些安全至关重要的受监管领域,如汽车、航空航天和医疗保健,就更需要代码覆盖信息。

有多种方法可以用来评估测试软件的代码覆盖率,如下所述。
1,语句覆盖率:尽管更流行的术语是行覆盖,但在许多语言中,一行中可能有多条语句,因此纯粹主义者更喜欢使用语句覆盖。如果一条语句在运行测试套件时至少执行一次,则认为它已被覆盖。

2,区域覆盖率:它是已执行的代码区域的百分比,代码区域可以跨越由多个语句组成的多行,例如函数体。

3,分支覆盖率:分支覆盖率是软件测试中使用的一个度量,用于评估一组测试用例执行了程序控制流中的多少分支或决策点。在代码覆盖率分析的上下文中,“分支”通常指的是代码中的一个决策点,程序可以根据条件(例如,if语句或switch语句)采取不同的路径。

4,MC/DC覆盖率:修改条件/决策覆盖(Modified Condition/Decision coverage, MC/DC)是一种用于关键安全系统的严格的软件测试技术。它确保代码中的每个决策都在所有可能的结果下进行了测试,并且决策中的每个条件都可以独立地影响决策的结果。MC/DC的优势在于它的效率。它提供了高水平的故障检测,同时比完全条件覆盖所需的测试用例更少。

在这篇文章中,我们将讨论如何为Rust应用程序生成代码覆盖信息。我们还将讨论与支持代码覆盖相关的Rust编译器未来版本的当前状态和特性。Rust使用LLVM作为编译后端。相应地,Rust编译器利用了LLVM提供的一些检测覆盖特性。随着在LLVM中添加相应的功能,Rust中的支持代码覆盖的功能有望增长。我们将简要讨论Rust编译器对LLVM代码覆盖特性的依赖。

在Rust中启用代码覆盖
Rust中的代码覆盖率数据可以通过以下两种方法收集:
1,使用-Z profile命令标志
这使用了与gcc兼容的基于gcov的覆盖实现。简单来说,它可以帮助你收集基于DebugInfo的覆盖率数据。当你想要查看代码的哪些部分正在执行,哪些部分没有执行时,这是非常方便的,这就像有一个代码执行的路线图。

2. 使用-C instrument-coverage命令标志
此标志启用LLVM的本机覆盖检测,它以其效率和精度而闻名。它提供了高度精确的覆盖率数据,可以检查正在执行的代码的确切部分。这对于识别未被测试的代码区域非常有用。因此,当你希望使用高度精确的数据来提高测试的健壮性时,可以选择基于 -C instrument-coverage的覆盖工具。在本文中,我们将详细讨论-C instrument-coverage标志,它更广泛地用于基于源代码的代码覆盖。

下面的简单示例说明了这是如何工作的。
pub fn is_even(n: u32) -> bool {
    if n % 2 == 0 {
        true
    } else {
        false
    }
}
测试代码如下:
#[test]
pub fn test_even() {
    assert_eq!(is_even(2), true);
}
为了启用代码覆盖,我们在用Rust编译器编译程序时使用-C instrument-coverage标志。如果我们使用Cargo来构建程序,我们可以通过设置RUSTFLAGS环境变量启用这个标志,如下所示。
RUSTFLAGS="-Cinstrument-coverage" cargo test
这将产生所谓的“仪表化”二进制/可执行文件,其中注入了额外的代码来计算行、区域和分支。实际上发生的事情是编译器注入如下所示的计数器增量来跟踪计数:
pub fn is_even(n: u32) -> bool {
    is_even_counter += 1;

    if n % 2 == 0 {
        is_even_true_branch_counter += 1;
        true
    } else {
        is_even_false_branch_counter += 1;
        false
    }
}
因此,每当输入特定的功能/区域或分支时,相关的计数器就会增加。所有这些计数器都存储在程序内存中的特定位置。除此之外,一个额外的运行时也被注入到二进制文件中,它在程序的整个生命周期中初始化和管理这些计数器(称为LLVM分析器运行时)。
pub fn main() {
    __initialize_counters();

    test_even(); 

    //Other tests….
    __write_counter_data_to_file();
}
当程序退出时,来自这些计数器的原始数据被写入“profraw”文件,该文件可用于创建覆盖率报告。

Rust中的代码覆盖工具
下面是在Rust中流行的覆盖工具:
1,Tarpaulin:Tarpaulin是一个免费的Rust库,提供源代码行覆盖功能。它仍处于开发的早期阶段,但它已经被证明是测试Rust应用程序的一个好选择。
2,grcov:grcov是一个免费的库,它收集和聚合项目中所有Rust文件的代码覆盖率信息,在内部使用llvm-cov来创建报告。
3,cargo-llvm-cov:这是Rust代码覆盖的另一个流行工具。

使用cargo-tarpaulin
Cargo-tarpaulin是Rust生态系统中收集代码覆盖率数据的流行工具,它被设计成与cargo构建系统一起使用。下面是一个简单的例子,告诉你如何使用cargo-tarpaulin:
# 安装 cargo-tarpaulin
cargo install cargo-tarpaulin

# 运行 cargo-tarpaulin 收集覆盖率数据
cargo tarpaulin
结果如下:
INFO cargo_tarpaulin::statemachine::instrumented: For binary: target/debug/deps/code_covrage-4e2873bf50ec5596
INFO cargo_tarpaulin::statemachine::instrumented: Generated: target/tarpaulin/profraws/code_covrage-4e2873bf50ec5596_16540310165768346208_0-50218.profraw
INFO cargo_tarpaulin::statemachine::instrumented: Merging coverage reports
INFO cargo_tarpaulin::statemachine::instrumented: Mapping coverage data to source
INFO cargo_tarpaulin::report: Coverage Results:
|| Uncovered Lines:
|| src/main.rs: 5, 9
|| Tested/Total Lines:
|| src/main.rs: 3/5
|| 
60.00% coverage, 3/5 lines covered
在这个例子中,cargo-tarpaulin将在控制台中自动生成报告,对于更详细的报告,cargo-tarpaulin支持多种输出格式,如Json、Xml和Html。

使用grcov
Grcov是一个从测试套件中收集和聚合代码覆盖率数据的Rust工具。当你想要将代码覆盖率报告集成到持续集成管道中时,它特别有用。下面是如何在Rust项目中使用grcov的一个简单示例:
# 安装 grcov
cargo install grcov

# 运行测试
RUSTFLAGS="-Cinstrument-coverage" cargo test

# 生成覆盖率报告
grcov . -s . --binary-path ./target/debug/ -t html --ignore tests/  -o ./target/debug/coverage/

# 在浏览器中打开HTML文件查看报告
open target/debug/coverage/index.html
下图显示了一个示例覆盖率报告。我们可以找到项目中每个Rust模块的覆盖信息,如行覆盖、函数覆盖和分支覆盖。

使用cargo-llvm-cov
Cargo-llvm-cov是一个Rust工具,它利用LLVM基于源代码的代码覆盖率来生成详细的覆盖率报告。下面是一个简单的例子,说明如何在Rust项目中使用cargo-llvm-cov。
# 安装 cargo-llvm-cov
cargo install cargo-llvm-cov

# 运行有覆盖率的测试
cargo llvm-cov
这将在控制台中生成详细的覆盖率报告,结果如下:
Filename                        Regions    Missed Regions     Cover   Functions  Missed Functions  Executed       Lines      Missed Lines     Cover    Branches   Missed Branches     Cover
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
../code-covrage/src/main.rs           8                 2    75.00%           4                 1    75.00%          10                 2    80.00%           0                 0         -
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL                                 8                 2    75.00%           4                 1    75.00%          10                 2    80.00%           0                 0         -
总结
代码覆盖率为评估和改进源代码的质量提供了非常有用的信息。Rust已经为诸如行覆盖、函数覆盖等特性提供了很好的支持。虽然LLVM支持分支覆盖和MC/DC覆盖,但仍有一些功能有待添加。

用户评论