From caec996831c97d0387d29d9535f3ab4ab962051a Mon Sep 17 00:00:00 2001 From: Moshe Atlow Date: Tue, 19 Mar 2024 23:55:25 +0200 Subject: [PATCH] test_runner: emit diagnostics when watch mode drains PR-URL: https://github.com/nodejs/node/pull/52130 Fixes: https://github.com/nodejs/node/issues/51253 Reviewed-By: Chemi Atlow Reviewed-By: Colin Ihrig --- lib/internal/test_runner/harness.js | 25 +++++++++++++++---------- lib/internal/test_runner/runner.js | 8 +++++++- lib/internal/test_runner/test.js | 7 ++++++- test/parallel/test-runner-run.mjs | 9 ++++++++- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/lib/internal/test_runner/harness.js b/lib/internal/test_runner/harness.js index 08c29be9ffaf92..c663b33d874dea 100644 --- a/lib/internal/test_runner/harness.js +++ b/lib/internal/test_runner/harness.js @@ -182,20 +182,25 @@ function setup(root) { root.harness = { __proto__: null, bootstrapComplete: false, + watching: false, coverage: FunctionPrototypeBind(collectCoverage, null, root, coverage), - counters: { - __proto__: null, - all: 0, - failed: 0, - passed: 0, - cancelled: 0, - skipped: 0, - todo: 0, - topLevel: 0, - suites: 0, + resetCounters() { + root.harness.counters = { + __proto__: null, + all: 0, + failed: 0, + passed: 0, + cancelled: 0, + skipped: 0, + todo: 0, + topLevel: 0, + suites: 0, + }; }, + counters: null, shouldColorizeTestFiles: false, }; + root.harness.resetCounters(); root.startTime = hrtime(); return root; } diff --git a/lib/internal/test_runner/runner.js b/lib/internal/test_runner/runner.js index d07d49296c21ea..12fca09c700d23 100644 --- a/lib/internal/test_runner/runner.js +++ b/lib/internal/test_runner/runner.js @@ -35,6 +35,7 @@ const { createInterface } = require('readline'); const { deserializeError } = require('internal/error_serdes'); const { Buffer } = require('buffer'); const { FilesWatcher } = require('internal/watch_mode/files_watcher'); +const { queueMicrotask } = require('internal/process/task_queues'); const console = require('internal/console/global'); const { codes: { @@ -418,6 +419,7 @@ function runTestFile(path, filesWatcher, opts) { filesWatcher.runningSubtests.delete(path); if (filesWatcher.runningSubtests.size === 0) { opts.root.reporter[kEmitMessage]('test:watch:drained'); + queueMicrotask(() => opts.root.postRun()); } } @@ -445,6 +447,7 @@ function watchFiles(testFiles, opts) { const runningSubtests = new SafeMap(); const watcher = new FilesWatcher({ __proto__: null, debounce: 200, mode: 'filter', signal: opts.signal }); const filesWatcher = { __proto__: null, watcher, runningProcesses, runningSubtests }; + opts.root.harness.watching = true; watcher.on('changed', ({ owners }) => { watcher.unfilterFilesOwnedBy(owners); @@ -471,7 +474,10 @@ function watchFiles(testFiles, opts) { kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation; opts.signal.addEventListener( 'abort', - () => opts.root.postRun(), + () => { + opts.root.harness.watching = false; + opts.root.postRun(); + }, { __proto__: null, once: true, [kResistStopPropagation]: true }, ); } diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js index ae5bdc714a344f..c98a14541650fd 100644 --- a/lib/internal/test_runner/test.js +++ b/lib/internal/test_runner/test.js @@ -785,7 +785,12 @@ class Test extends AsyncResource { reporter.coverage(nesting, loc, coverage); } - reporter.end(); + if (harness.watching) { + this.reported = false; + harness.resetCounters(); + } else { + reporter.end(); + } } } diff --git a/test/parallel/test-runner-run.mjs b/test/parallel/test-runner-run.mjs index 77e0b21439327c..30633b7e066a00 100644 --- a/test/parallel/test-runner-run.mjs +++ b/test/parallel/test-runner-run.mjs @@ -203,9 +203,16 @@ describe('require(\'node:test\').run', { concurrency: true }, () => { signal: controller.signal, }) .compose(async function* (source) { + let waitForCancel = 2; for await (const chunk of source) { - if (chunk.type === 'test:pass') { + if (chunk.type === 'test:watch:drained' || + (chunk.type === 'test:diagnostic' && chunk.data.message.startsWith('duration_ms'))) { + waitForCancel--; + } + if (waitForCancel === 0) { controller.abort(); + } + if (chunk.type === 'test:pass') { yield chunk.data.name; } }