Repository: nodejs/node
Pull request: test_runner: fix suite rerun


Changed files:
filename: lib/internal/test_runner/test.js
patch: @@ -706,10 +706,12 @@ class Test extends AsyncResource {
         this.fn = () => {
           for (let i = 0; i < (previousAttempt.children?.length ?? 0); i++) {
             const child = previousAttempt.children[i];
-            this.createSubtest(Test, child.name, { __proto__: null }, noop, {
+            const t = this.createSubtest(Test, child.name, { __proto__: null }, noop, {
               __proto__: null,
               loc: [child.line, child.column, child.file],
-            }, noop).start();
+            }, noop);
+            t.endTime = t.startTime = hrtime();
+            t.start();
           }
         };
       }

filename: test/fixtures/test-runner/rerun.js
patch: @@ -1,4 +1,4 @@
-const { test } = require('node:test')
+const { test, describe } = require('node:test')
 
 test('should fail on first two attempts', ({ attempt }) => {
   if (attempt < 2) {
@@ -38,3 +38,11 @@ function nestedAmbiguousTest(expectedAttempts) {
 
 test('nested ambiguous (expectedAttempts=0)', nestedAmbiguousTest(0));
 test('nested ambiguous (expectedAttempts=1)', nestedAmbiguousTest(2));
+
+
+describe('describe rerun', { timeout: 1000, concurrency: 1000 }, () => {
+  test('passed on first attempt', async (t) => {
+    await t.test('nested', async () => {});
+  });
+  test('a');
+});

filename: test/parallel/test-runner-test-rerun-failures.js
patch: @@ -23,6 +23,10 @@ const expectedStateFile = [
     'test/fixtures/test-runner/rerun.js:39:1': { passed_on_attempt: 0, name: 'nested ambiguous (expectedAttempts=0)' },
     'test/fixtures/test-runner/rerun.js:30:16:(1)': { passed_on_attempt: 0, name: '2 levels deep' },
     'test/fixtures/test-runner/rerun.js:35:13:(1)': { passed_on_attempt: 0, name: 'ok' },
+    'test/fixtures/test-runner/rerun.js:45:13': { passed_on_attempt: 0, name: 'nested' },
+    'test/fixtures/test-runner/rerun.js:44:3': { passed_on_attempt: 0, name: 'passed on first attempt' },
+    'test/fixtures/test-runner/rerun.js:47:3': { passed_on_attempt: 0, name: 'a' },
+    'test/fixtures/test-runner/rerun.js:43:1': { passed_on_attempt: 0, name: 'describe rerun' },
   },
   {
     'test/fixtures/test-runner/rerun.js:9:1': { passed_on_attempt: 0, name: 'ok' },
@@ -34,6 +38,11 @@ const expectedStateFile = [
     'test/fixtures/test-runner/rerun.js:39:1': { passed_on_attempt: 0, name: 'nested ambiguous (expectedAttempts=0)' },
     'test/fixtures/test-runner/rerun.js:30:16:(1)': { passed_on_attempt: 0, name: '2 levels deep' },
     'test/fixtures/test-runner/rerun.js:35:13:(1)': { passed_on_attempt: 0, name: 'ok' },
+    'test/fixtures/test-runner/rerun.js:43:1': { passed_on_attempt: 0, name: 'describe rerun' },
+    'test/fixtures/test-runner/rerun.js:44:3': { passed_on_attempt: 0, name: 'passed on first attempt' },
+    'test/fixtures/test-runner/rerun.js:45:13': { passed_on_attempt: 0, name: 'nested' },
+    'test/fixtures/test-runner/rerun.js:45:13:(1)': { passed_on_attempt: 1, name: 'nested' },
+    'test/fixtures/test-runner/rerun.js:47:3': { passed_on_attempt: 0, name: 'a' },
   },
   {
     'test/fixtures/test-runner/rerun.js:3:1': { passed_on_attempt: 2, name: 'should fail on first two attempts' },
@@ -48,6 +57,11 @@ const expectedStateFile = [
     'test/fixtures/test-runner/rerun.js:30:16:(1)': { passed_on_attempt: 0, name: '2 levels deep' },
     'test/fixtures/test-runner/rerun.js:35:13:(1)': { passed_on_attempt: 0, name: 'ok' },
     'test/fixtures/test-runner/rerun.js:40:1': { passed_on_attempt: 2, name: 'nested ambiguous (expectedAttempts=1)' },
+    'test/fixtures/test-runner/rerun.js:43:1': { passed_on_attempt: 0, name: 'describe rerun' },
+    'test/fixtures/test-runner/rerun.js:44:3': { passed_on_attempt: 0, name: 'passed on first attempt' },
+    'test/fixtures/test-runner/rerun.js:45:13': { passed_on_attempt: 0, name: 'nested' },
+    'test/fixtures/test-runner/rerun.js:45:13:(1)': { passed_on_attempt: 1, name: 'nested' },
+    'test/fixtures/test-runner/rerun.js:47:3': { passed_on_attempt: 0, name: 'a' },
   },
 ];
 
@@ -67,23 +81,26 @@ test('test should pass on third rerun', async () => {
   let { code, stdout, signal } = await common.spawnPromisified(process.execPath, args);
   assert.strictEqual(code, 1);
   assert.strictEqual(signal, null);
-  assert.match(stdout, /pass 8/);
+  assert.match(stdout, /pass 11/);
   assert.match(stdout, /fail 4/);
+  assert.match(stdout, /suites 1/);
   assert.deepStrictEqual(await getStateFile(), expectedStateFile.slice(0, 1));
 
   ({ code, stdout, signal } = await common.spawnPromisified(process.execPath, args));
   assert.strictEqual(code, 1);
   assert.strictEqual(signal, null);
-  assert.match(stdout, /pass 9/);
+  assert.match(stdout, /pass 13/);
   assert.match(stdout, /fail 3/);
+  assert.match(stdout, /suites 1/);
   assert.deepStrictEqual(await getStateFile(), expectedStateFile.slice(0, 2));
 
 
   ({ code, stdout, signal } = await common.spawnPromisified(process.execPath, args));
   assert.strictEqual(code, 0);
   assert.strictEqual(signal, null);
-  assert.match(stdout, /pass 12/);
+  assert.match(stdout, /pass 18/);
   assert.match(stdout, /fail 0/);
+  assert.match(stdout, /suites 1/);
   assert.deepStrictEqual(await getStateFile(), expectedStateFile);
 });
 
@@ -93,29 +110,32 @@ test('test should pass on third rerun with `--test`', async () => {
   let { code, stdout, signal } = await common.spawnPromisified(process.execPath, args);
   assert.strictEqual(code, 1);
   assert.strictEqual(signal, null);
-  assert.match(stdout, /pass 8/);
+  assert.match(stdout, /pass 11/);
   assert.match(stdout, /fail 4/);
+  assert.match(stdout, /suites 1/);
   assert.deepStrictEqual(await getStateFile(), expectedStateFile.slice(0, 1));
 
   ({ code, stdout, signal } = await common.spawnPromisified(process.execPath, args));
   assert.strictEqual(code, 1);
   assert.strictEqual(signal, null);
-  assert.match(stdout, /pass 9/);
+  assert.match(stdout, /pass 13/);
   assert.match(stdout, /fail 3/);
+  assert.match(stdout, /suites 1/);
   assert.deepStrictEqual(await getStateFile(), expectedStateFile.slice(0, 2));
 
 
   ({ code, stdout, signal } = await common.spawnPromisified(process.execPath, args));
   assert.strictEqual(code, 0);
   assert.strictEqual(signal, null);
-  assert.match(stdout, /pass 12/);
+  assert.match(stdout, /pass 18/);
   assert.match(stdout, /fail 0/);
+  assert.match(stdout, /suites 1/);
   assert.deepStrictEqual(await getStateFile(), expectedStateFile);
 });
 
 test('using `run` api', async () => {
   let stream = run({ files: [fixture], rerunFailuresFilePath: stateFile });
-  stream.on('test:pass', common.mustCall(8));
+  stream.on('test:pass', common.mustCall(12));
   stream.on('test:fail', common.mustCall(4));
 
   // eslint-disable-next-line no-unused-vars
@@ -125,7 +145,7 @@ test('using `run` api', async () => {
 
 
   stream = run({ files: [fixture], rerunFailuresFilePath: stateFile });
-  stream.on('test:pass', common.mustCall(9));
+  stream.on('test:pass', common.mustCall(14));
   stream.on('test:fail', common.mustCall(3));
 
   // eslint-disable-next-line no-unused-vars
@@ -135,7 +155,7 @@ test('using `run` api', async () => {
 
 
   stream = run({ files: [fixture], rerunFailuresFilePath: stateFile });
-  stream.on('test:pass', common.mustCall(12));
+  stream.on('test:pass', common.mustCall(19));
   stream.on('test:fail', common.mustNotCall());
 
   // eslint-disable-next-line no-unused-vars