#覆盖率 #lcov #数据源 #grcov

bin+lib rust-covfix

根据源代码修复 Rust 覆盖率数据

6 个版本

0.2.1 2020 年 5 月 18 日
0.2.0 2020 年 4 月 20 日
0.1.1 2020 年 1 月 13 日
0.0.2 2019 年 12 月 24 日

#1295 in 命令行工具

MIT 许可协议

60KB
1.5K SLoC

rust-covfix

Build Status Build status codecov Version docs GitHub Release Date License: MIT PRs Welcome

Rustc 已知报告某些行的覆盖率不正确(https://stackoverflow.com/questions/32521800/why-does-kcov-calculate-incorrect-code-coverage-statistics-for-rust-programs)。rust-covfix 将从由 grcov 生成的文件中读取覆盖率,修复它,然后输出正确的覆盖率。

尽管目前只支持 lcov 格式,但在未来的版本中还将支持其他格式。

功能

  • 兼容最新的稳定/测试/夜间 Rust 编译器
  • Windows/OSX/Linux 均受支持
  • 轻量级(依赖项少)
  • 快速且安全(用 Rust 语言实现)
  • rust-covfix 还提供了 Rust API(文档
  • 显示覆盖率差异摘要。

可选功能

可选功能可以通过 cargo 的 --features 选项使用。您可以指定以下功能

$ cargo install --no-default-features --features "cli lcov"
功能名称 描述 默认值?
cli 命令行界面。此功能是构建 rust-covfix 可执行文件所必需的。
lcov 使 LcovParser 可用
noinline 避免在函数上添加 #cfg[inline(never)] 属性。(已弃用)
backtrace 每次错误发生时都转储回溯信息。

安装

GitHub 发布页面 下载最新版本。

您也可以通过 cargo 命令进行安装。

$ cargo install rust-covfix

如何从 Rust 程序生成正确的代码覆盖率?

1. 避免内联函数(可选)

当前版本的 rustc(1.42)会自动内联只在一个地方调用的函数。这种行为会导致测试的覆盖率不正确。

为了避免这种情况,您必须手动为它们的函数添加 #[inline(never)] 属性。我建议在您的包中定义一个新的功能标志 coverage。在 Cargo.toml 中,追加以下行。

[features]
coverage = []

然后,将如下属性添加到仅从一个位置调用的函数中:#[cfg(feature = "coverage", inline(never))]

#[cfg(feature = "coverage", inline(never))]
fn foo() {
  // ...
}

这样做将避免在启用 coverage 功能标志时内联函数。

2. 使用 -Zprofile 选项编译您的 crate

为了使用 rustc 生成代码覆盖率,您必须指定 -Zprofile 选项。此选项目前(1.42)尚不稳定,仅从夜间工具链中可用。

此外,还需要一些其他标志才能生成 正确 的覆盖率。以下是我的推荐。

$ export CARGO_INCREMENTAL=0
$ export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -C panic=abort"

然后,编译您的 crate 并运行测试。

$ cargo test --features coverage

3. 聚合代码覆盖率数据

现在,代码覆盖率数据位于 target/debug/deps 目录中。下一步是聚合它们,并转换格式,以便 rust-covfix 可以读取覆盖率数据。

强烈推荐使用 grcov 来聚合它们。此项目由 mozilla 团队开发,并支持最新的 Rust 工具链。

安装最新的 grcov 并从您的项目根目录运行以下命令。

zip -0 ccov.zip `find . \( -name "YOUR_PROJECT_NAME*.gc*" -o -name "test-*.gc*" \) -print`
./grcov ccov.zip -s . -t lcov --llvm --branch --ignore-not-existing --ignore "/*" --ignore "tests/*" -o lcov.info

其中 YOUR_PROJECT_NAME 是您在 Cargo.toml 中指定的 crate 名称。

4. 使用 rust-covfix 修复覆盖率数据

现在 rust-covfix 可以从 lcov.info 读取覆盖率。

$ rust-covfix -o lcov_correct.info lcov.info

此命令将写入一个 正确 的覆盖率到 lcov_correct.info。您可以将它们上传到 codecov.io,或者使用 genhtml 生成 HTML 摘要。

在 Travis CI 上使用 rust-covfix

以下是一个示例脚本,用于在 Travis CI 环境中使用 rust-covfix

#!/bin/bash
# ci/script.sh

set -ex

if [ "$TRAVIS_RUST_VERSION" = "nightly" ] && [ -z "$TRAVIS_TAG" ]; then
  # Setup grcov
  wget https://github.com/mozilla/grcov/releases/download/v0.5.7/grcov-linux-x86_64.tar.bz2
  tar xvf grcov-linux-x86_64.tar.bz2

  # Setup environmental variables
  export CARGO_INCREMENTAL=0
  export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -C panic=abort"
fi

# Compile and run tests
cargo test --all-features

if [ "$TRAVIS_RUST_VERSION" = "nightly" ] && [ -z "$TRAVIS_TAG" ]; then
  # collect coverages
  zip -0 ccov.zip `find . \( -name "rust_covfix*.gc*" -o -name "test-*.gc*" \) -print`
  ./grcov ccov.zip -s . -t lcov --llvm --branch --ignore-not-existing --ignore "/*" --ignore "tests/*" -o lcov.info

  # fix coverage using rust-covfix
  rust-covfix lcov.info -o lcov.info

  # upload coverage to codecov
  bash <(curl -s https://codecov.io/bash) -f lcov.info
fi

然后,从 travis.yml 调用此脚本。

language: rust

matrix:
  include:
    - os: linux
      rust: nightly

script:
  - bash ci/script.sh

为什么这个项目被开发成一个独立包?

有以下三个原因。

  1. A grcov 协作者了解到 rustc 报告了不正确的覆盖率(https://github.com/mozilla/grcov/issues/249#issuecomment-465154051)。我正在尝试比较原始 Rust 代码和转换后的 LLVM IR,但仍然不完全理解为什么 Rustc 报告了不正确的覆盖率。`rust-covfix`最初是作为临时解决方案开发的,直到我了解 rustc 在何时何地报告不正确的覆盖率。问题尚未完全解决。

  2. grcov 只是一个覆盖率收集工具,而不是覆盖率生成器。它只聚合原始覆盖率数据,并转换格式。它不读取源代码,也不操纵任何覆盖率数据。此外,尽管 grcov 针对多种编程语言,如 C/C++、Nim 和 Rust,但 rust-covfix 仅支持 Rust。

  3. rust-covfix 不仅针对 grcov,还针对未来的 cargo-kcovTarpaulin。实际上,Tarpaulin 现在可以生成 lcov.info。这意味着 rust-covfix 已经能够修复由 Tarpaulin 生成的覆盖率数据。我正在 feature/cobertura 分支 上工作以支持 cargo-kcov。最终目标是支持这三个工具。

如何检测不正确的行覆盖率

rust_covfix 通过一些规则修复覆盖率信息。您可以通过传递 --rules 选项来指定用于修复覆盖率的规则。

规则

close

忽略 else 块的闭合括号和行。

if a > 0 {
    b = a
} else {  // <-- marked as "not executable"
    b = -a
};  // <-- marked as "not executable"

test

名为 testtests 的模块块,该模块具有 cfg(test) 属性将被忽略。

具有 #[test] 属性的所有函数也将被忽略。

#[cfg(test)]
mod tests {  // <-- removed from coverage
    fn util() { ... }  // <-- removed from coverage

    #[test]
    fn test_hoge() { ... }  // <-- removed from coverage
}

loop

修复 Rust 内部循环分支传递不正确的错误。

for i in 0..10 {  // <-- fix branch coverage information
    println!("{}", i);
}

derive

具有 derive(...) 属性的结构将被忽略。

#[derive(Clone, Debug)]  // <-- removed from coverage
struct Point {  // <-- removed from coverage
  x: f64,  // <-- removed from coverage
  y: f64  // <-- removed from coverage
}  // <-- removed from coverage

comment

根据注释标记忽略覆盖率。

fn main() {
    let a = 1 + 2; // cov:ignore-line

    // cov:begin-ignore-branch
    println!("Hello");
    println!("world!");
    // cov:end-ignore-branch

    // cov: begin-ignore-line
    if a > 2 {
        println!("a is large!");
    } else if a == 0 {
        println!("a is small!");
    }
    // cov: end-ignore-line

    // cov:begin-ignore
    println!("a = {}", a);
    // cov:end-ignore

    println!("finish."); // cov:ignore-branch

    return (); // cov:ignore
}

路线图

  • 支持 cobertura.xml 文件。(进行中)
  • 添加上传正确覆盖率到 coveralls 的选项。
  • 使用使用 syn 包生成的语法树。
  • 性能改进

作者

👤 Kogia-sima

🤝 贡献

欢迎贡献、问题和功能请求!

请随意查看 问题页面

显示您的支持

如果此项目对您有帮助,请给它一个 ⭐️!

📝 许可证

版权所有 © 2019 Kogia-sima

本项目采用 MIT 许可。


此 README 由 readme-md-generator 用 ❤️ 生成。

依赖

~4–6MB
~112K SLoC