项目作者: taiki-e

项目描述 :
[EXPERIMENTAL] A wrapper for source based code coverage (-Zinstrument-coverage).
高级语言: Rust
项目地址: git://github.com/taiki-e/cargo-llvm-cov.git
创建时间: 2021-01-16T16:59:06Z
项目社区:https://github.com/taiki-e/cargo-llvm-cov

开源协议:Other

下载


cargo-llvm-cov

crates.io
license
github actions

Cargo subcommand to easily use LLVM source-based code coverage.

This is a wrapper around rustc -C instrument-coverage and provides:

  • Generate very precise coverage data. (line, region, and branch coverage. branch coverage is currently optional and requires nightly, see #8 for more)
  • Support cargo test, cargo run, and cargo nextest with command-line interface compatible with cargo.
  • Support for proc-macro, including coverage of UI tests.
  • Support for doc tests. (this is currently optional and requires nightly, see #2 for more)
  • Fast because it does not introduce extra layers between rustc, cargo, and llvm-tools.

Table of Contents:

Usage

Basic usage


Complete list of options (click to show)

(See docs directory for options of subcommands)


console $ cargo llvm-cov --help cargo-llvm-cov Cargo subcommand to easily use LLVM source-based code coverage (-C instrument-coverage). USAGE: cargo llvm-cov [SUBCOMMAND] [OPTIONS] [-- <args>...] ARGS: <args>... Arguments for the test binary OPTIONS: --json Export coverage data in "json" format If --output-path is not specified, the report will be printed to stdout. This internally calls `llvm-cov export -format=text`. See <https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-export> for more. --lcov Export coverage data in "lcov" format If --output-path is not specified, the report will be printed to stdout. This internally calls `llvm-cov export -format=lcov`. See <https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-export> for more. --cobertura Export coverage data in "cobertura" XML format If --output-path is not specified, the report will be printed to stdout. This internally calls `llvm-cov export -format=lcov` and then converts to cobertura.xml. See <https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-export> for more. --codecov Export coverage data in "Codecov Custom Coverage" format If --output-path is not specified, the report will be printed to stdout. This internally calls `llvm-cov export -format=json` and then converts to codecov.json. See <https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-export> for more. --text Generate coverage report in “text” format If --output-path or --output-dir is not specified, the report will be printed to stdout. This internally calls `llvm-cov show -format=text`. See <https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-show> for more. --html Generate coverage report in "html" format If --output-dir is not specified, the report will be generated in `target/llvm-cov/html` directory. This internally calls `llvm-cov show -format=html`. See <https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-show> for more. --open Generate coverage reports in "html" format and open them in a browser after the operation. See --html for more. --summary-only Export only summary information for each file in the coverage data This flag can only be used together with --json, --lcov, or --cobertura. --output-path <PATH> Specify a file to write coverage data into. This flag can only be used together with --json, --lcov, --cobertura, or --text. See --output-dir for --html and --open. --output-dir <DIRECTORY> Specify a directory to write coverage report into (default to `target/llvm-cov`). This flag can only be used together with --text, --html, or --open. See also --output-path. --failure-mode <any|all> Fail if `any` or `all` profiles cannot be merged (default to `any`) --ignore-filename-regex <PATTERN> Skip source code files with file paths that match the given regular expression --show-instantiations Show instantiations in report --no-cfg-coverage Unset cfg(coverage), which is enabled when code is built using cargo-llvm-cov --no-cfg-coverage-nightly Unset cfg(coverage_nightly), which is enabled when code is built using cargo-llvm-cov and nightly compiler --no-report Run tests, but don't generate coverage report --no-clean Build without cleaning any old build artifacts --fail-under-functions <MIN> Exit with a status of 1 if the total function coverage is less than MIN percent --fail-under-lines <MIN> Exit with a status of 1 if the total line coverage is less than MIN percent --fail-under-regions <MIN> Exit with a status of 1 if the total region coverage is less than MIN percent --fail-uncovered-lines <MAX> Exit with a status of 1 if the uncovered lines are greater than MAX --fail-uncovered-regions <MAX> Exit with a status of 1 if the uncovered regions are greater than MAX --fail-uncovered-functions <MAX> Exit with a status of 1 if the uncovered functions are greater than MAX --show-missing-lines Show lines with no coverage --include-build-script Include build script in coverage report --dep-coverage <NAME> Show coverage of the specified dependency instead of the crates in the current workspace. (unstable) --skip-functions Skip exporting per-function coverage data. This flag can only be used together with --json, --lcov, or --cobertura. --branch Enable branch coverage. (unstable) --mcdc Enable mcdc coverage. (unstable) --doctests Including doc tests (unstable) This flag is unstable. See <https://github.com/taiki-e/cargo-llvm-cov/issues/2> for more. --no-run Generate coverage report without running tests --no-fail-fast Run all tests regardless of failure --ignore-run-fail Run all tests regardless of failure and generate report If tests failed but report generation succeeded, exit with a status of 0. -q, --quiet Display one character per test instead of one line --lib Test only this package's library unit tests --bin <NAME> Test only the specified binary --bins Test all binaries --example <NAME> Test only the specified example --examples Test all examples --test <NAME> Test only the specified test target --tests Test all tests --bench <NAME> Test only the specified bench target --benches Test all benches --all-targets Test all targets --doc Test only this library's documentation (unstable) This flag is unstable because it automatically enables --doctests flag. See <https://github.com/taiki-e/cargo-llvm-cov/issues/2> for more. -p, --package <SPEC> Package to run tests for --workspace Test all packages in the workspace --all Alias for --workspace (deprecated) --exclude <SPEC> Exclude packages from both the test and report --exclude-from-test <SPEC> Exclude packages from the test (but not from the report) --exclude-from-report <SPEC> Exclude packages from the report (but not from the test) -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs -r, --release Build artifacts in release mode, with optimizations --profile <PROFILE-NAME> Build artifacts with the specified profile -F, --features <FEATURES> Space or comma separated list of features to activate --all-features Activate all available features --no-default-features Do not activate the `default` feature --target <TRIPLE> Build for the target triple When this option is used, coverage for proc-macro and build script will not be displayed because cargo does not pass RUSTFLAGS to them. --coverage-target-only Activate coverage reporting only for the target triple Activate coverage reporting only for the target triple specified via `--target`. This is important, if the project uses multiple targets via the cargo bindeps feature, and not all targets can use `instrument-coverage`, e.g. a microkernel, or an embedded binary. -v, --verbose Use verbose output Use -vv (-vvv) to propagate verbosity to cargo. --color <WHEN> Coloring: auto, always, never --remap-path-prefix Use --remap-path-prefix for workspace root Note that this does not fully compatible with doctest. --include-ffi Include coverage of C/C++ code linked to Rust library/binary Note that `CC`/`CXX`/`LLVM_COV`/`LLVM_PROFDATA` environment variables must be set to Clang/LLVM compatible with the LLVM version used in rustc. --keep-going Do not abort the build as soon as there is an error (unstable) --ignore-rust-version Ignore `rust-version` specification in packages --manifest-path <PATH> Path to Cargo.toml --frozen Require Cargo.lock and cache are up to date --locked Require Cargo.lock is up to date --offline Run without accessing the network -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Print help information -V, --version Print version information SUBCOMMANDS: test Run tests and generate coverage report This is equivalent to `cargo llvm-cov` without subcommand, except that test name filtering is supported. run Run a binary or example and generate coverage report report Generate coverage report show-env Output the environment set by cargo-llvm-cov to build Rust projects clean Remove artifacts that cargo-llvm-cov has generated in the past nextest Run tests with cargo nextest This internally calls `cargo nextest run`.


By default, run tests (via cargo test), and print the coverage summary to stdout.

  1. cargo llvm-cov

Currently, doc tests are disabled by default because nightly-only features are required to make coverage work for doc tests. see #2 for more.

To run cargo run instead of cargo test, use run subcommand.

  1. cargo llvm-cov run

With html report (the report will be generated to target/llvm-cov/html directory):

  1. cargo llvm-cov --html
  2. open target/llvm-cov/html/index.html

or

  1. cargo llvm-cov --open

With plain text report (if --output-path is not specified, the report will be printed to stdout):

  1. cargo llvm-cov --text | less -R

With json report (if --output-path is not specified, the report will be printed to stdout):

  1. cargo llvm-cov --json --output-path cov.json

With lcov report (if --output-path is not specified, the report will be printed to stdout):

  1. cargo llvm-cov --lcov --output-path lcov.info

You can get a coverage report in a different format based on the results of a previous run by using cargo llvm-cov report.

  1. cargo llvm-cov --html # run tests and generate html report
  2. cargo llvm-cov report --lcov # generate lcov report

cargo llvm-cov/cargo llvm-cov run/cargo llvm-cov nextest cleans some build artifacts by default to avoid false positives/false negatives due to old build artifacts.
This behavior is disabled when --no-clean, --no-report, or --no-run is passed, and old build artifacts are retained.
When using these flags, it is recommended to first run cargo llvm-cov clean --workspace to remove artifacts that may affect the coverage results.

  1. cargo llvm-cov clean --workspace # remove artifacts that may affect the coverage results
  2. cargo llvm-cov --no-clean

Merge coverages generated under different test conditions

You can merge the coverages generated under different test conditions by using --no-report and cargo llvm-cov report.

  1. cargo llvm-cov clean --workspace # remove artifacts that may affect the coverage results
  2. cargo llvm-cov --no-report --features a
  3. cargo llvm-cov --no-report --features b
  4. cargo llvm-cov report --lcov # generate report without tests

[!TIP]
To include coverage for doctests you also need to pass --doctests to cargo llvm-cov report.

Get coverage of C/C++ code linked to Rust library/binary

Set CC, CXX, LLVM_COV, and LLVM_PROFDATA environment variables to Clang/LLVM compatible with the LLVM version used in rustc, and run cargo-llvm-cov with --include-ffi flag.

  1. CC=<clang-path> \
  2. CXX=<clang++-path> \
  3. LLVM_COV=<llvm-cov-path> \
  4. LLVM_PROFDATA=<llvm-profdata-path> \
  5. cargo llvm-cov --lcov --include-ffi

Known compatible Rust (installed via rustup) and LLVM versions:

Rust 1.60-1.77 Rust 1.78-1.81 Rust 1.82-1.87
LLVM 14-17
LLVM 18
LLVM 19-20

Get coverage of external tests

cargo test, cargo run, and cargo nextest are available as builtin, but cargo-llvm-cov can also be used for arbitrary binaries built using cargo (including other cargo subcommands or external tests that use make, xtask, etc.)

  1. # Set the environment variables needed to get coverage.
  2. source <(cargo llvm-cov show-env --export-prefix)
  3. # Remove artifacts that may affect the coverage results.
  4. # This command should be called after show-env.
  5. cargo llvm-cov clean --workspace
  6. # Above two commands should be called before build binaries.
  7. cargo build # Build rust binaries.
  8. # Commands using binaries in target/debug/*, including `cargo test` and other cargo subcommands.
  9. # ...
  10. cargo llvm-cov report --lcov # Generate report without tests.

[!CAUTION]
cargo-llvm-cov subcommands other than report and clean may not work correctly in the context where environment variables are set by show-env; consider using normal cargo/cargo-nextest/etc. commands.

[!TIP]
To include coverage for doctests you also need to pass --doctests to both cargo llvm-cov show-env and cargo llvm-cov report.

[!TIP]
The same thing can be achieved in PowerShell 6+ by substituting the source command with:

  1. Invoke-Expression (cargo llvm-cov show-env --with-pwsh-env-prefix | Out-String)

Get coverage of AFL fuzzers

Cargo-llvm-cov can be used with AFL.rs similar to the way external tests are done, but with a few caveats.

  1. # Set environment variables and clean workspace
  2. source <(cargo llvm-cov show-env --export-prefix)
  3. cargo llvm-cov clean --workspace
  4. # Build the fuzz target
  5. cargo afl build
  6. # Run the fuzzer, the AFL_FUZZER_LOOPCOUNT is needed, because otherwise .profraw files aren't emitted
  7. # To get coverage of current corpus, minimize it and set it as input, then run the fuzzer until it processes the corpus
  8. AFL_FUZZER_LOOPCOUNT=20 cargo afl fuzz -c - -i in -o out target/debug/fuzz-target
  9. # Generate report
  10. # If you pass `--release` to `cargo afl build`, you also need to pass `--release` to `cargo llvm-cov report`
  11. cargo llvm-cov report --lcov

Exclude file from coverage

To exclude specific file patterns from the report, use the --ignore-filename-regex option.

  1. cargo llvm-cov --open --ignore-filename-regex build

By default, the following patterns and crates specified by --exclude or --exclude-from-report are excluded.

  1. {SEPARATOR}rustc{SEPARATOR}([0-9a-f]+|[0-9]+\.[0-9]+\.[0-9]+){SEPARATOR}
  2. ^{WORKSPACE_ROOT}({SEPARATOR}.*)?{SEPARATOR}(tests|examples|benches){SEPARATOR}
  3. ^{TARGET_DIR}($|{SEPARATOR})
  4. ^{CARGO_HOME}{SEPARATOR}(registry|git){SEPARATOR}
  5. ^{RUSTUP_HOME}{SEPARATOR}toolchains($|{SEPARATOR})

You can use --disable-default-ignore-filename-regex to disable default exclude setting.

Exclude code from coverage

To exclude the specific function or module from coverage, use the #[coverage(off)] attribute.

Since #[coverage(off)] is unstable, it is recommended to use it together with cfg(coverage) or cfg(coverage_nightly) set by cargo-llvm-cov.

  1. #![cfg_attr(coverage_nightly, feature(coverage_attribute))]
  2. // function
  3. #[cfg_attr(coverage_nightly, coverage(off))]
  4. fn exclude_fn_from_coverage() {
  5. // ...
  6. }
  7. // module
  8. #[cfg_attr(coverage_nightly, coverage(off))]
  9. mod exclude_mod_from_coverage {
  10. // ...
  11. }

cfgs are set under the following conditions:

  • cfg(coverage) is always set when using cargo-llvm-cov (unless --no-cfg-coverage flag passed)
  • cfg(coverage_nightly) is set when using cargo-llvm-cov with nightly toolchain (unless --no-cfg-coverage-nightly flag passed)

Rust 1.80+ warns the above cfgs as unexpected_cfgs. The recommended way to address this is to add a lints table to Cargo.toml.

  1. [lints.rust]
  2. unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage,coverage_nightly)'] }

If you want to ignore all #[test]-related code, you can use module-level #[coverage(off)] attribute:

  1. #![cfg_attr(coverage_nightly, feature(coverage_attribute))]
  2. #[cfg(test)]
  3. #[cfg_attr(coverage_nightly, coverage(off))]
  4. mod tests {
  5. // ...
  6. }

cargo-llvm-cov excludes code contained in the directory named tests from the report by default, so you can also use it instead of #[coverage(off)] attribute.

Continuous Integration

GitHub Actions and Codecov

Here is an example of GitHub Actions workflow that uploads coverage to Codecov.

  1. name: Coverage
  2. on: [pull_request, push]
  3. jobs:
  4. coverage:
  5. runs-on: ubuntu-latest
  6. env:
  7. CARGO_TERM_COLOR: always
  8. steps:
  9. - uses: actions/checkout@v4
  10. - name: Install Rust
  11. run: rustup update stable
  12. - name: Install cargo-llvm-cov
  13. uses: taiki-e/install-action@cargo-llvm-cov
  14. - name: Generate code coverage
  15. run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
  16. - name: Upload coverage to Codecov
  17. uses: codecov/codecov-action@v3
  18. with:
  19. token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
  20. files: lcov.info
  21. fail_ci_if_error: true

Currently, when using --lcov flag, only line coverage is available on Codecov.

By using --codecov flag instead of --lcov flag, you can use region coverage on Codecov:

  1. - name: Generate code coverage
  2. run: cargo llvm-cov --all-features --workspace --codecov --output-path codecov.json
  3. - name: Upload coverage to Codecov
  4. uses: codecov/codecov-action@v3
  5. with:
  6. token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
  7. files: codecov.json
  8. fail_ci_if_error: true

Note that the way Codecov shows region/branch coverage is not very good.

GitLab CI

First of all, when running the CI you need to make sure cargo-llvm-cov is available
in the execution script. Whether you add it to a custom image, or run cargo install as part
of your pipeline, it should be available and in PATH. Once done, it’s simple:

  1. unit_tests:
  2. artifacts:
  3. reports:
  4. junit: target/nextest/default/junit.xml
  5. coverage_report:
  6. coverage_format: cobertura
  7. path: target/llvm-cov-target/cobertura.xml
  8. # this uses region for coverage summary
  9. coverage: '/TOTAL\s+(\d+\s+)+(\d+\.\d+\%)/'
  10. script:
  11. - cargo llvm-cov nextest
  12. - cargo llvm-cov report --cobertura --output-path target/llvm-cov-target/cobertura.xml

[!CAUTION]
GitLab has certain limits for Cobertura reports
make sure you obey them.

[!NOTE]
Note that this example uses cargo-nextest to run the tests (which must
similarly be available), with the following .config/nextest.toml:

  1. [profile.default.junit]
  2. path = "junit.xml"

Display coverage in VS Code

You can display coverage in VS Code using Coverage Gutters.

Coverage Gutters supports lcov style coverage file and detects lcov.info files at the top level or in the coverage directory. Below is an example command to generate the coverage file.

  1. cargo llvm-cov --lcov --output-path lcov.info

You may need to click the “Watch” label in the bottom bar of VS Code to display coverage.

Environment variables

You can override these environment variables to change cargo-llvm-cov’s behavior on your system:

  • CARGO_LLVM_COV_TARGET_DIR — Location of where to place all generated artifacts, relative to the current working directory. Default to <cargo_target_dir>/llvm-cov-target.
  • CARGO_LLVM_COV_SETUP — Control behavior if llvm-tools-preview component is not installed. See #219 for more.
  • LLVM_COV — Override the path to llvm-cov. You may need to specify both this and LLVM_PROFDATA environment variables if you are using --include-ffi flag or if you are using a toolchain installed without via rustup. llvm-cov version must be compatible with the LLVM version used in rustc.
  • LLVM_PROFDATA — Override the path to llvm-profdata. See LLVM_COV environment variable for more.
  • LLVM_COV_FLAGS — A space-separated list of additional flags to pass to all llvm-cov invocations that cargo-llvm-cov performs. See LLVM documentation for available options.
  • LLVM_PROFDATA_FLAGS — A space-separated list of additional flags to pass to all llvm-profdata invocations that cargo-llvm-cov performs. See LLVM documentation for available options.
  • LLVM_PROFILE_FILE_NAME — Override the file name (the final component of the path) of the LLVM_PROFILE_FILE. See LLVM documentation for available syntax.

See also environment variables that Cargo reads. cargo-llvm-cov respects many of them.

Additional JSON information

If JSON is selected as output format (with the --json flag), then cargo-llvm-cov will add additional contextual information at the root of the llvm-cov data. This can be helpful for programs that rely on the output of cargo-llvm-cov.

  1. {
  2. // Other regular llvm-cov fields ...
  3. "cargo_llvm_cov": {
  4. "version": "0.0.0",
  5. "manifest_path": "/path/to/your/project/Cargo.toml"
  6. }
  7. }
  • version specifies the version of cargo-llvm-cov that was used. This allows other programs to verify a certain version of it was used and make assertions of its behavior.
  • manifest_path defines the absolute path to the Rust project’s Cargo.toml that cargo-llvm-cov was executed on. It can help to avoid repeating the same option on both programs.

For example, when forwarding the JSON output directly to another program:

  1. cargo-llvm-cov --json | some-program

Installation

From source

  1. cargo +stable install cargo-llvm-cov --locked

Currently, installing cargo-llvm-cov requires rustc 1.81+.

cargo-llvm-cov is usually runnable with Cargo versions older than the Rust version
required for installation (e.g., cargo +1.60 llvm-cov). Currently, to run
cargo-llvm-cov requires Cargo 1.60+.

From prebuilt binaries

You can download prebuilt binaries from the Release page.
Prebuilt binaries are available for macOS, Linux (gnu and musl), Windows (static executable), and FreeBSD.


Example of script to install from the Release page (click to show)

sh # Get host target host=$(rustc -vV | grep '^host:' | cut -d' ' -f2) # Download binary and install to $HOME/.cargo/bin curl --proto '=https' --tlsv1.2 -fsSL "https://github.com/taiki-e/cargo-llvm-cov/releases/latest/download/cargo-llvm-cov-$host.tar.gz" \ | tar xzf - -C "$HOME/.cargo/bin"

On GitHub Actions

You can use taiki-e/install-action to install prebuilt binaries on Linux, macOS, and Windows.
This makes the installation faster and may avoid the impact of problems caused by upstream changes.

  1. - uses: taiki-e/install-action@cargo-llvm-cov

When used with nextest:

  1. - uses: taiki-e/install-action@cargo-llvm-cov
  2. - uses: taiki-e/install-action@nextest

Via Homebrew

You can install cargo-llvm-cov from homebrew-core (x86_64/AArch64 macOS, x86_64 Linux):

  1. brew install cargo-llvm-cov

Alternatively, you can also install from the Homebrew tap maintained by us (x86_64/AArch64 macOS, x86_64/AArch64 Linux):

  1. brew install taiki-e/tap/cargo-llvm-cov

Via Scoop (Windows)

You can install cargo-llvm-cov from the Scoop bucket maintained by us:

  1. scoop bucket add taiki-e https://github.com/taiki-e/scoop-bucket
  2. scoop install cargo-llvm-cov

Via cargo-binstall

You can install cargo-llvm-cov using cargo-binstall:

  1. cargo binstall cargo-llvm-cov

Via pacman (Arch Linux)

You can install cargo-llvm-cov from the extra repository:

  1. pacman -S cargo-llvm-cov

Via ports (FreeBSD)

You can install cargo-llvm-cov from the official ports:

  1. pkg install cargo-llvm-cov

Via other package managers

Packaging status

Known limitations

See also the code-coverage-related issues reported in rust-lang/rust.

  • cargo-config2: Library to load and resolve Cargo configuration. cargo-llvm-cov uses this library.
  • cargo-hack: Cargo subcommand to provide various options useful for testing and continuous integration.
  • cargo-minimal-versions: Cargo subcommand for proper use of -Z minimal-versions.

License

Licensed under either of Apache License, Version 2.0 or
MIT license at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.