Skip to content

Report coverage for binaries a rust_test runs as subprocesses - #4287

Draft
robot-head wants to merge 1 commit into
bazelbuild:mainfrom
robot-head:coverage-subprocess-objects
Draft

robot-head wants to merge 1 commit into
bazelbuild:mainfrom
robot-head:coverage-subprocess-objects

Conversation

@robot-head

@robot-head robot-head commented Sep 26, 2026 •

Copy link
Copy Markdown

A rust_test can list a rust_binary in data and spawn it through CARGO_BIN_EXE_<name>. This is the usual way to smoke-test a CLI. Under bazel coverage, lines that only that subprocess reaches are reported as not covered.

Cause

The binary is instrumented, and because it inherits LLVM_PROFILE_FILE it writes its .profraw into the test's COVERAGE_DIR. collect_rust_coverage merges that file with the test's own. It then runs llvm-cov export with only the test binary, and llvm-cov reads counters only for the objects it is given. So the subprocess's counters are merged and then discarded.

Change

  • rust.bzl: rust_test lists each instrumented bin crate in data in RUST_COVERAGE_OBJECTS. The paths are resolved the same way as RUST_LLVM_COV: exec paths with experimental_use_coverage_metadata_files, runfiles paths without it.
  • collect_rust_coverage.rs: passes each of those binaries to llvm-cov export as -object.
  • rustc.bzl: data joins deps and crate in dependency_attributes, as it already is for cc_*, sh_* and py_* rules. This puts the binary's sources in the instrumented file set. It also puts the binary itself in the coverage metadata that --experimental_split_coverage_postprocessing stages, which the collector needs in order to open it.

RUST_COVERAGE_OBJECTS is only passed between the rule and the collector; it adds no attribute or setting.

Test

//test/coverage_subprocess has a binary whose lines only a subprocess reaches. Presubmit's coverage validation now requires greeter.rs to have lines hit.

greeter.rs
main LH:0 LF:0
this PR, split postprocessing (repo default) LH:7 LF:7
this PR, --noexperimental_split_coverage_postprocessing --//rust/settings:experimental_use_coverage_metadata_files=false LH:7 LF:7

bazel coverage --instrumentation_filter=^// --instrument_test_targets //test/... on linux-aarch64 (excluding the two multi-channel packages): 506 pass, 40 skipped.

This came up in a downstream Cargo workspace that builds through rules_rs. There, it restored coverage for CLI source files exercised only by CARGO_BIN_EXE smoke tests (e.g. one file went from 461/608 to 583/608 lines).

This change was written with AI assistance (Claude Code), per the AI tools policy in CONTRIBUTING.md. It stays a draft until it has been reviewed by hand.

A `rust_test` can list a `rust_binary` in `data` and spawn it through
`CARGO_BIN_EXE_<name>`. Under `bazel coverage` that binary is
instrumented, inherits `LLVM_PROFILE_FILE`, and writes its `.profraw`
into the test's `COVERAGE_DIR`, where the collector merges it with the
test's own. But `llvm-cov export` is given only the test binary, and it
reads counters only for the objects it is given, so the subprocess's
counters were merged and then discarded. Lines that only the subprocess
reaches were reported as though no test ran them.

- `rust_test` names each instrumented bin crate in `data` in
  `RUST_COVERAGE_OBJECTS`, and the collector passes each one to
  `llvm-cov export` as `-object`.
- `data` joins `deps` and `crate` as a coverage dependency attribute, as
  it already is for `cc_*`, `sh_*` and `py_*` rules. That puts the
  binary's sources in the instrumented file set, and puts the binary in
  the coverage metadata that split coverage postprocessing stages.

`//test/coverage_subprocess` is a binary reached only through a
subprocess. Presubmit's coverage validation now requires its lines to
be hit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@google-cla

google-cla Bot commented Sep 26, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants