cargo-affected 0.1.0

Run only the tests affected by git changes, using LLVM coverage.
cargo-affected-0.1.0 is not a library.

cargo-affected

Like pytest-testmon for Rust. Maps each test to the source-line ranges it touches via LLVM coverage, then reruns only the tests whose ranges overlap git diff hunks.

Status: extremely early. Mac & linux only at this stage. The author is starting to use this in their own repos; others probably shouldn't yet. The schema can change without migration, behavior may break, and there is no support promise. CI should still run the full test suite.

Installation

Not on crates.io yet. Install from source:

git clone https://github.com/max-sixty/cargo-affected
cd cargo-affected
cargo install --path .
rustup component add llvm-tools

Quick start

# Build with coverage instrumentation and record what each test touches.
cargo affected collect

# After editing:
cargo affected run        # run only tests overlapping the diff
cargo affected status     # dry-run: show what would run
cargo affected clean      # wipe the coverage cache

run diffs the working tree against the git sha that was HEAD when collect last ran. Recollect periodically — every committed change since the last collect adds to the diff and broadens selection.

How it works

collect:

  1. cargo nextest list enumerates every test.
  2. cargo nextest run runs them with -C instrument-coverage and a per-test LLVM_PROFILE_FILE.
  3. For each test, llvm-profdata merges its profraw and llvm-cov export lists every hit function with its source-line regions.
  4. Per (test, file, function), the min/max line span is stored in target/affected/coverage.db (SQLite), keyed by a fingerprint of Cargo.lock, all workspace Cargo.tomls, rustc -vV, RUSTFLAGS, and CARGO_BUILD_TARGET. The git HEAD sha is recorded alongside.

run:

  1. git diff -U0 <collect_sha> produces OLD-side line ranges for changed files — same coordinate system as storage.
  2. For each changed file, the DB returns stored function ranges overlapping any hunk; the union of matching tests is run via cargo nextest run.
  3. If a hunk overlaps no stored range (struct fields, #[derive], const, use, mod), a per-file backstop selects every test that touched the file. Crate roots (lib.rs / main.rs / tests/*.rs) are stored with a sentinel range covering the whole file, so any edit there reselects every test that covered the crate root.

Accuracy model

cargo affected run is an approximation — it trades correctness for speed. CI should still run the full suite.

False positives (tests selected that didn't need to run)

  • Function-level granularity. A hit function's full line span is treated as one range, so an edit anywhere inside it reruns every test that touched the function — even if the edited line is unreachable from those tests.
  • Structural-edit backstop. Hunks outside any LLVM region (struct fields, derives, consts, use, mod) reselect every test that touched the file.
  • Crate roots. Any edit to lib.rs / main.rs / tests/*.rs reruns every test that covered that crate root.
  • Comment- and whitespace-only edits. Selection diffs lines, not semantics.

False negatives (tests skipped that should have run)

  • Non-Rust sources. include_str! / include_bytes! targets, SQL files, migrations, assets, and templates aren't seen by llvm-cov.
  • Build-time inputs not in the fingerprint. The fingerprint covers Cargo.lock, workspace Cargo.tomls, rustc -vV, RUSTFLAGS, and CARGO_BUILD_TARGET. Changes to build.rs, rust-toolchain.toml, or .cargo/config.toml don't currently invalidate the cache.
  • Proc-macro crate source. A proc-macro's own source files compile into a host dylib, not the test binary, so editing a proc-macro crate won't reselect its downstream tests.
  • External state. Tests that read env vars, filesystem state, or the network can change outcome without any source file changing.

When in doubt, cargo affected collect to refresh coverage, or skip cargo-affected and run the full suite.

License

Dual-licensed under MIT or Apache-2.0 at your option.