GeezerVison is showing github.com — test_runner: preserve run duration when using test-rerun · nodejs/node@904bdf5
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/test_runner/reporter/rerun.js +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -62,6 +62,7 @@ function reportReruns(previousRuns, globalOptions) { | 62 | 62 | name: data.name, | 63 | 63 | children, | 64 | 64 | passed_on_attempt: data.details.passed_on_attempt ?? data.details.attempt, | | 65 | + duration_ms: data.details.duration_ms, | 65 | 66 | }; | 66 | 67 | } | 67 | 68 | } | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/test_runner/test.js +14-1Lines changed: 14 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -9,10 +9,12 @@ const { | 9 | 9 | ArrayPrototypeSplice, | 10 | 10 | ArrayPrototypeUnshift, | 11 | 11 | ArrayPrototypeUnshiftApply, | | 12 | + BigInt, | 12 | 13 | Error, | 13 | 14 | FunctionPrototype, | 14 | 15 | MathFloor, | 15 | 16 | MathMax, | | 17 | + MathRound, | 16 | 18 | Number, | 17 | 19 | NumberPrototypeToFixed, | 18 | 20 | ObjectKeys, | @@ -769,14 +771,25 @@ class Test extends AsyncResource { | 769 | 771 | const previousAttempt = this.root.harness.previousRuns[this.attempt - 1]?.[testIdentifier]; | 770 | 772 | if (previousAttempt != null) { | 771 | 773 | this.passedAttempt = previousAttempt.passed_on_attempt; | | 774 | + if (previousAttempt.duration_ms !== undefined) { | | 775 | + this.replayedDurationNs = BigInt(MathRound(previousAttempt.duration_ms * 1_000_000)); | | 776 | + } | 772 | 777 | this.fn = () => { | | 778 | + // Restore the original duration on the synthetic replay. Suites are | | 779 | + // skipped here because Suite.run() unconditionally reassigns | | 780 | + // startTime later; only non-suite tests benefit from setting it. | | 781 | + if (this.reportedType !== 'suite') { | | 782 | + this.startTime = hrtime(); | | 783 | + this.endTime = this.startTime + (this.replayedDurationNs ?? 0n); | | 784 | + } | 773 | 785 | for (let i = 0; i < (previousAttempt.children?.length ?? 0); i++) { | 774 | 786 | const child = previousAttempt.children[i]; | 775 | 787 | const t = this.createSubtest(Test, child.name, { __proto__: null }, noop, { | 776 | 788 | __proto__: null, | 777 | 789 | loc: [child.line, child.column, child.file], | 778 | 790 | }, noop); | 779 | | - t.endTime = t.startTime = hrtime(); | | 791 | + t.startTime = hrtime(); | | 792 | + t.endTime = t.startTime + (t.replayedDurationNs ?? 0n); | 780 | 793 | // For suites, Suite.run() starts the subtests via SafePromiseAll. | 781 | 794 | // Starting them here as well would run them twice, re-invoking the | 782 | 795 | // synthetic children-creator against a now-incremented disambiguator | |
Collapse file Copy file name to clipboard +13Lines changed: 13 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | +// A passing test with a measurable duration alongside a failing test. The | | 3 | +// failing test forces the rerun feature to retry on a second invocation, | | 4 | +// causing the passing test to be replayed via the synthetic noop stub. | | 5 | +const { test } = require('node:test'); | | 6 | + | | 7 | +test('passing slow test', async () => { | | 8 | + await new Promise((resolve) => setTimeout(resolve, 25)); | | 9 | +}); | | 10 | + | | 11 | +test('always failing', () => { | | 12 | + throw new Error('boom'); | | 13 | +}); |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-runner-test-rerun-failures.js +15Lines changed: 15 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -89,6 +89,7 @@ const getStateFile = async () => { | 89 | 89 | res.forEach((entry) => { | 90 | 90 | for (const item in entry) { | 91 | 91 | delete entry[item].children; | | 92 | + delete entry[item].duration_ms; | 92 | 93 | } | 93 | 94 | }); | 94 | 95 | return res; | @@ -152,6 +153,20 @@ test('test should pass on third rerun with `--test`', async () => { | 152 | 153 | assert.deepStrictEqual(await getStateFile(), expectedStateFile); | 153 | 154 | }); | 154 | 155 |
| | 156 | +test('rerun preserves the original duration on the replayed pass', async () => { | | 157 | + const durationFixture = fixtures.path('test-runner', 'rerun-duration.js'); | | 158 | + const args = ['--test-rerun-failures', stateFile, durationFixture]; | | 159 | + | | 160 | + await common.spawnPromisified(process.execPath, args); | | 161 | + await common.spawnPromisified(process.execPath, args); | | 162 | + | | 163 | + const raw = JSON.parse(await readFile(stateFile, 'utf8')); | | 164 | + const passKey = Object.keys(raw[0]).find((k) => raw[0][k].name === 'passing slow test'); | | 165 | + assert.ok(passKey, 'expected the passing test to be recorded on attempt 0'); | | 166 | + assert.ok(raw[0][passKey].duration_ms > 0, 'expected a measurable duration on attempt 0'); | | 167 | + assert.strictEqual(raw[1][passKey].duration_ms, raw[0][passKey].duration_ms); | | 168 | +}); | | 169 | + | 155 | 170 | test('using `run` api', async () => { | 156 | 171 | let stream = run({ files: [fixture], rerunFailuresFilePath: stateFile }); | 157 | 172 | stream.on('test:pass', common.mustCall(19)); | |
|