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 命令行工具
60KB
1.5K SLoC
rust-covfix
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
为什么这个项目被开发成一个独立包?
有以下三个原因。
-
A grcov 协作者了解到 rustc 报告了不正确的覆盖率(https://github.com/mozilla/grcov/issues/249#issuecomment-465154051)。我正在尝试比较原始 Rust 代码和转换后的 LLVM IR,但仍然不完全理解为什么 Rustc 报告了不正确的覆盖率。`rust-covfix`最初是作为临时解决方案开发的,直到我了解 rustc 在何时何地报告不正确的覆盖率。问题尚未完全解决。
-
grcov
只是一个覆盖率收集工具,而不是覆盖率生成器。它只聚合原始覆盖率数据,并转换格式。它不读取源代码,也不操纵任何覆盖率数据。此外,尽管 grcov 针对多种编程语言,如 C/C++、Nim 和 Rust,但rust-covfix
仅支持 Rust。 -
rust-covfix
不仅针对 grcov,还针对未来的 cargo-kcov 和 Tarpaulin。实际上,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
名为 test
或 tests
的模块块,该模块具有 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
- Twitter: @Kogia_sima
- Github: @Kogia-sima
🤝 贡献
欢迎贡献、问题和功能请求!
请随意查看 问题页面。
显示您的支持
如果此项目对您有帮助,请给它一个 ⭐️!
📝 许可证
版权所有 © 2019 Kogia-sima。
本项目采用 MIT 许可。
此 README 由 readme-md-generator 用 ❤️ 生成。
依赖
~4–6MB
~112K SLoC