fix(coverage): handle nested coverage collection#3823
Conversation
We discovered that when a py_test invokes py_binary, coverage information collected by those binaries is lost and only the coverage of the test itself appears. It turns out that each binary writes to the same coverage file and only the last one is included in the report. This PR makes the lcov_path unique, ensuring the coverage is collected for every binary that might be launched in such a setup. I have setup https://github.com/gergondet-woven/repro-rules_python-coverage-issue to show the issue. In this repo, a py_binary is launched through a py_test and we would expect coverage information generated by the binary to surface in the coverage report but it doesn't with the current release: Before the patch: ``` # Generate coverage information bazel coverage --combined_report=lcov //... # Generate the report genhtml --ignore-errors category --branch-coverage --output ~/genhtml "$(bazel info output_path)/_coverage/_coverage_report.dat" # (snip) Overall coverage rate: lines......: 30.0% (3 of 10 lines) # Looking into the report, neither coverage for the library function # called by the binary nor the binary code itself is collected ``` After applying the patch: ``` Overall coverage rate: lines......: 100.0% (11 of 11 lines) ``` However, I am not sure how/if this can be properly tested within rules_python itself.
There was a problem hiding this comment.
Code Review
This pull request updates the coverage collection in stage2_bootstrap_template.py to append a unique identifier to the generated LCOV report filename (pylcov_{}.dat), which helps prevent potential file name collisions. There are no review comments, and I have no additional feedback to provide.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Thank you!
This needs to be verified but we could do:
- Have a
py_test. - Have a rule that transitions the target to set the instrumentation flags.
- Have a wrapper test rule that ensures that the coverage file is as expected.
I think we can merge this now and the tests can be added as a followup PR.
…trib#3832) Under `bazel coverage`, coverage.py raises `NoDataError` from `lcov_report()` when no instrumented Python code was executed, instead of writing an (empty) report. This error currently propagates out of the coverage bootstrap and fails an otherwise passing test. This is common and benign in a few setups: - a `py_binary` that only spawns another process and runs no Python itself - a test whose instrumented sources happen not to execute any Python **Before:** `bazel coverage //a:test` fails with `coverage.exceptions.NoDataError` even though `bazel test //a:test` passes. **After:** the `NoDataError` is caught and the empty report is skipped, so the test result is unchanged by whether coverage data happened to be collected. This mirrors the reporter's suggestion in bazel-contrib#2762 to "ignore the return code of `coverage lcov`", applied in-process. As noted on the original report and in bazel-contrib#3823, there is no in-repo harness for exercising real coverage collection, so this change does not add an automated test. Fixes bazel-contrib#2762 --------- Co-authored-by: Richard Levasseur <richardlev@gmail.com>
We discovered that when a py_test invokes py_binary, coverage information collected by those binaries is lost and only the coverage of the test itself appears.
It turns out that each binary writes to the same coverage file and only the last one is included in the report.
This PR makes the lcov_path unique, ensuring the coverage is collected for every binary that might be launched in such a setup.
I have setup https://github.com/gergondet-woven/repro-rules_python-coverage-issue to show the issue.
In this repo, a py_binary is launched through a py_test and we would expect coverage information generated by the binary to surface in the coverage report but it doesn't with the current release:
Before the patch:
After applying the patch:
However, I am not sure how/if this can be properly tested within rules_python itself.