Skip to content

New-test detection ignores positional test-name filters in the run passthrough #39

Description

@max-sixty

Problem

New-test detection lists tests with cargo nextest list and compares against the coverage DB. The build args for that listing are extracted from the post--- passthrough by cargo_build_args (src/collect.rs), which is an allowlist of cargo build flags — it deliberately drops everything else, including positional test-name filters and -E/--filterset.

So when the user runs cargo affected run -- some_test_name:

  • cargo nextest list enumerates all tests (the positional filter is dropped).
  • cargo nextest run receives the positional filter and runs only matching tests.

A new test (absent from the DB) that does not match some_test_name is still flagged new, lands in the generated --config-file selection, and is handed to nextest run — which then skips it because it doesn't match the positional filter. If the selection collapses to only such tests, nextest run exits 4 (error: no tests to run). run_tests in src/run.rs does not pass --no-tests=warn, so that exit propagates.

This is the same failure class as the bugs fixed in #38, via a different trigger. It is pre-existing and was not introduced or worsened by #38 — flagged there as a known follow-up.

Proposed fix

Two coordinated changes:

  1. cargo_build_args → a run-only-flag denylist. Instead of allowlisting build flags, forward everything to nextest list except nextest run-only flags (--retries, --no-fail-fast, --no-tests, --max-fail, -j/--test-threads, --status-level, …). That carries positionals and -E through, so the listing reflects the run's full filter config.
  2. Key detection off filter-match.status instead of the ignored boolean. cargo nextest list --message-format json tags every testcase with filter-match: {status: matches|mismatch, …}. A test counts toward new/stranded/affected selection only when filter-match.status == "matches". This single predicate subsumes #[ignore]d tests, positional/-E filters, --run-ignored, and the project's own default-filter — unifying the three exclusion checks (new/stranded/affected) that fix: correct new-test detection for ignored tests and feature builds #38 left as separate listing.ignored filters.

Verified: cargo nextest list <positional-filter> still enumerates all testcases (each tagged with its filter-match status), so forwarding filters to list does not shrink Listing.tests and does not break collect --diff's prune.

Tradeoff

The denylist must stay complete against nextest's CLI. A future nextest run-only flag not in the denylist would be forwarded to cargo nextest list, which rejects unknown args and exits non-zero — cargo affected run -- --that-new-flag would break loudly until the denylist is updated. The current allowlist instead fails silently on a future cargo build flag (listing mismatch, no error). The denylist's loud failure is arguably better aligned with the repo's "fail loudly over silently degrading" principle, but it is a maintenance commitment worth noting.

Ref #38.

This was written by Claude Code on behalf of Maximilian Roos

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions