Skip to content

Add code coverage + HTML report machinery#19692

Open
woodruffw wants to merge 8 commits into
mainfrom
ww/cov
Open

Add code coverage + HTML report machinery#19692
woodruffw wants to merge 8 commits into
mainfrom
ww/cov

Conversation

@woodruffw

@woodruffw woodruffw commented Jun 5, 2026

Copy link
Copy Markdown
Member

Summary

This adds a new cargo dev coverage subcommand, which invokes nextest under our new coverage pesudo-profile. This profile in turn enables -C instrument-coverage.

cargo dev coverage also takes care of merging raw profiling samples into profdata and LCOV reports for us, which our new coverage-html-report.py script can turn into a readable (but very large) HTML report.

The basic flow for this is:

$ # any nextest flags will work
$ cargo dev coverage ...

That will output something like:

Merged profile: .../astral-sh/uv/target/coverage/profdata/b46cc0f3-7cd0-4309-9df2-10ab653e6dd8.profdata
LCOV profile: .../astral-sh/uv/target/coverage/lcov/b46cc0f3-7cd0-4309-9df2-10ab653e6dd8.lcov
Generate an HTML report with: uv run --script scripts/coverage-html-report.py b46cc0f3-7cd0-4309-9df2-10ab653e6dd8

And then you can run:

uv run --script scripts/coverage-html-report.py b46cc0f3-7cd0-4309-9df2-10ab653e6dd8

(or add --open to auto-open in the browser).

Under the hood, this only uses existing dependencies plus Rust's own LLVM toolchain, plus coverage.py for the HTML report generation.

TODOs:

  • Document this in CONTRIBUTING.md
  • Figure out if/how best to surface this in CI -- maybe we want to export combined LCOVs for each test job, similarly to what we do for JUnit test summaries?

Test Plan

Internal only.

@woodruffw woodruffw self-assigned this Jun 5, 2026
@woodruffw woodruffw added the internal A refactor or improvement that is not user-facing label Jun 5, 2026
@woodruffw

Copy link
Copy Markdown
Member Author

Example from running cargo dev coverage -p uv-audit:

image

@woodruffw woodruffw requested a review from Gankra June 5, 2026 19:12
@woodruffw woodruffw added the coverage Perform a dedicated coverage-instrumented test run label Jun 5, 2026
Comment thread .github/workflows/coverage.yml Outdated
Comment thread crates/uv-dev/src/coverage.rs Outdated
@codspeed-hq

codspeed-hq Bot commented Jun 5, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚡ 1 improved benchmark
✅ 12 untouched benchmarks

Performance Changes

Mode Benchmark BASE HEAD Efficiency
WallTime resolve_warm_airflow 437.7 ms 424.7 ms +3.07%

Tip

Curious why this is faster? Use the CodSpeed MCP and ask your agent.


Comparing ww/cov (05f8df5) with main (1ed52dc)

Open in CodSpeed

@woodruffw

Copy link
Copy Markdown
Member Author

Noting: the reason I'm currently gating this behind a label is because it makes tests in CI more than twice as slow.

Comment thread .github/workflows/test.yml Outdated
Comment thread .github/workflows/test.yml Outdated
@woodruffw woodruffw force-pushed the ww/cov branch 4 times, most recently from f310af7 to 226b0e5 Compare June 12, 2026 14:26
@woodruffw woodruffw marked this pull request as ready for review June 12, 2026 14:42
Comment thread .cargo/coverage.toml
Comment on lines +1 to +3
[build]
rustflags = ["-C", "instrument-coverage"]
target-dir = "target/coverage"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have a runtime cost?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enabling instrumentation? Yeah, it's significant, which is why it gets its own target here.

Comment thread .github/workflows/ci.yml

# Decisions
[[ ! $has_skip_label && ($any_code_changed || $on_main_branch) ]] && test_code=1
[[ $on_main_branch || $has_coverage_label ]] && collect_coverage=1

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we only collect coverage on pull requests with a label?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly because it's slow -- instrumentation makes our test runs roughly 50% slower, sadly.

Comment thread .github/workflows/coverage.yml Outdated
Comment thread .github/workflows/test.yml
Comment thread .github/workflows/test.yml Outdated
Comment thread .github/workflows/test.yml Outdated
Comment thread crates/uv-dev/src/main.rs
use uv_dev::{COVERAGE_RUSTC_WRAPPER_ENV, coverage_rustc_wrapper, run};
use uv_static::EnvVars;

fn main() -> ExitCode {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting: it feels like a pretty bad hack to reuse uv-dev as a rustc wrapper, but this needs to live somewhere. Maybe I can abstract it as uv-dev rustc ... or similar.

This adds a new `cargo dev coverage` subcommand, which invokes nextest
under our new coverage pesudo-profile. This profile in turn enables
`-C instrument-coverage`.

`cargo dev coverage` also takes care of merging raw profiling
samples into profdata and LCOV reports for us, which our
new `coverage-html-report.py` script can turn into a readable
(but very large) HTML report.

The basic flow for this is:

```
$ # any nextest flags will work
$ cargo dev coverage ...
```

That will output something like:

```
Merged profile: .../astral-sh/uv/target/coverage/profdata/b46cc0f3-7cd0-4309-9df2-10ab653e6dd8.profdata
LCOV profile: .../astral-sh/uv/target/coverage/lcov/b46cc0f3-7cd0-4309-9df2-10ab653e6dd8.lcov
Generate an HTML report with: uv run --script scripts/coverage-html-report.py b46cc0f3-7cd0-4309-9df2-10ab653e6dd8
```

And then you can run:

```
uv run --script scripts/coverage-html-report.py b46cc0f3-7cd0-4309-9df2-10ab653e6dd8
```

(or add `--open` to auto-open in the browser).

Under the hood, this only uses existing dependencies plus
Rust's own LLVM toolchain, plus coverage.py for the HTML
report generation.

Signed-off-by: William Woodruff <william@yossarian.net>

MVP coverage workflow

Signed-off-by: William Woodruff <william@yossarian.net>

MVP coverage workflow, take 2

Signed-off-by: William Woodruff <william@yossarian.net>

Use a pool of 16 raw profiles for pooling

Signed-off-by: William Woodruff <william@yossarian.net>

Avoid some Windows UNC pain

Signed-off-by: William Woodruff <william@yossarian.net>

Fix rustflags for coverage on Windows

Signed-off-by: William Woodruff <william@yossarian.net>

Remove coverage ID chum

Signed-off-by: William Woodruff <william@yossarian.net>

Avoid Windows nonsense

Signed-off-by: William Woodruff <william@yossarian.net>

Add coverage guide to CONTRIBUTING

Signed-off-by: William Woodruff <william@yossarian.net>

Be less strict about the ID

Signed-off-by: William Woodruff <william@yossarian.net>

Prettier

Signed-off-by: William Woodruff <william@yossarian.net>
Signed-off-by: William Woodruff <william@yossarian.net>
Signed-off-by: William Woodruff <william@yossarian.net>
Signed-off-by: William Woodruff <william@yossarian.net>
Signed-off-by: William Woodruff <william@yossarian.net>
Signed-off-by: William Woodruff <william@yossarian.net>
Signed-off-by: William Woodruff <william@yossarian.net>
Signed-off-by: William Woodruff <william@yossarian.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

coverage Perform a dedicated coverage-instrumented test run internal A refactor or improvement that is not user-facing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants