Skip to content

test_runner: fix --test-rerun-failures swallowing failures on retry#63431

Open
atlowChemi wants to merge 2 commits into
nodejs:mainfrom
atlowChemi:fix-test-runner-rerun-swallow
Open

test_runner: fix --test-rerun-failures swallowing failures on retry#63431
atlowChemi wants to merge 2 commits into
nodejs:mainfrom
atlowChemi:fix-test-runner-rerun-swallow

Conversation

@atlowChemi
Copy link
Copy Markdown
Member

Summary

The state-file disambiguator could mis-align between the runner and the reporter, letting a real failure-on-retry be reported as a pass. Three independent bugs interacted:

  1. Runner off-by-one (lib/internal/test_runner/test.js) — the counter was stored against the suffixed identifier instead of the base key, so it never advanced past 1 and every 3rd+ same-loc registration collided on :(1).
  2. Reporter off-by-one (lib/internal/test_runner/reporter/rerun.js) — same off-by-one when writing the state file.
  3. Reporter ignored test:fail — only test:pass bumped the counter, so any failing test at a shared source location desynchronised the writer and runner counters. On retry, the surviving failing sibling inherited a slot that, in the previous attempt, belonged to a different (passing) sibling. The runner matched by that slot, replaced this.fn with the synthetic noop replay, and reported the failure as a pass.

The documented key format at doc/api/test.md:137-140 (file:line:column[:(N)]) is unchanged — the implementation now honours it correctly.

Fix

  • Track the base identifier separately in the runner and bump the counter against the base key.
  • Same in the reporter.
  • Bump the reporter counter on test:fail in addition to test:pass (still records to the state file only on test:pass).

Regression test

The new fixture test/fixtures/test-runner/rerun-shared-helper-swallows-failure.mjs exercises two sibling groups that share a source location via a factory function:

  • A 4-sibling group (D pass, E pass, F fail, G pass) — placed first so the passing sibling at global position 2 ends up at base:(1). With the runner off-by-one reintroduced, the buggy counter aliases F's id onto E's recorded slot and swallows F's failure. This is the only shape that catches bug 1 in isolation.
  • The original 3-sibling repro from the issue (A pass, B fail, C pass) — exercises the writer-side bugs via state-file slot assertions.

Each of the three bugs was verified by reintroducing it in isolation and confirming the new test fails for the right reason.

Fixes: #63424

Three independent bugs interacted to let a real failure-on-retry be
reported as a pass:

1. The runner's disambiguator stored the counter against the suffixed
   identifier (after mutation) instead of the base key, so the counter
   never advanced past 1 and every 3rd+ same-loc registration collided
   on :(1).
2. The reporter had the same off-by-one when writing the state file.
3. The reporter only bumped its counter on `test:pass`, so any failing
   test at a shared source location desynchronised the writer and
   runner counters - on retry, the surviving failing sibling would
   inherit a slot that in the previous attempt belonged to a different
   (passing) sibling. Node matched by that slot, replaced `this.fn`
   with a synthetic noop replay, and reported the failure as a pass.

Track the base identifier separately in the runner, bump the counter
against the base key in both the runner and the reporter, and bump the
reporter's counter on `test:fail` in addition to `test:pass`.

Fixes: nodejs#63424
Signed-off-by: atlowChemi <chemi@atlow.co.il>
@nodejs-github-bot
Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/test_runner

@nodejs-github-bot nodejs-github-bot added needs-ci PRs that need a full CI run. test_runner Issues and PRs related to the test runner subsystem. labels May 19, 2026
@atlowChemi
Copy link
Copy Markdown
Member Author

@MoLow I think this is safer than #63428 as it fixes the bug without changing the JSONs shape (ie. I believe your PR is a breaking change, while this one is a bug fix)

@codecov
Copy link
Copy Markdown

codecov Bot commented May 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.00%. Comparing base (4ee7567) to head (b974628).
⚠️ Report is 9 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #63431      +/-   ##
==========================================
- Coverage   90.05%   90.00%   -0.06%     
==========================================
  Files         714      712       -2     
  Lines      225876   224852    -1024     
  Branches    42737    42576     -161     
==========================================
- Hits       203408   202372    -1036     
- Misses      14244    14338      +94     
+ Partials     8224     8142      -82     
Files with missing lines Coverage Δ
lib/internal/test_runner/reporter/rerun.js 92.50% <100.00%> (+0.29%) ⬆️
lib/internal/test_runner/test.js 97.03% <100.00%> (+<0.01%) ⬆️

... and 129 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@atlowChemi atlowChemi added request-ci Add this label to start a Jenkins CI on a PR. author ready PRs that have at least one approval, no pending requests for changes, and a CI started. and removed request-ci Add this label to start a Jenkins CI on a PR. labels May 19, 2026
@nodejs-github-bot
Copy link
Copy Markdown
Collaborator

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

Labels

author ready PRs that have at least one approval, no pending requests for changes, and a CI started. needs-ci PRs that need a full CI run. test_runner Issues and PRs related to the test runner subsystem.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test runner's --test-rerun-failures could swallow actual failures on retries

4 participants