Skip to content

Commit 3f259b7

Browse files
MoLowtargos
authored andcommitted
test_runner: emit test:watch:drained event
PR-URL: #48259 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
1 parent c9f8e8c commit 3f259b7

File tree

3 files changed

+37
-12
lines changed

3 files changed

+37
-12
lines changed

doc/api/test.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1538,6 +1538,10 @@ This event is only emitted if `--test` flag is passed.
15381538
Emitted when a running test writes to `stdout`.
15391539
This event is only emitted if `--test` flag is passed.
15401540

1541+
### Event: `'test:watch:drained'`
1542+
1543+
Emitted when no more tests are queued for execution in watch mode.
1544+
15411545
## Class: `TestContext`
15421546

15431547
<!-- YAML

lib/internal/test_runner/runner.js

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -335,15 +335,13 @@ class FileTest extends Test {
335335
}
336336
}
337337

338-
const runningProcesses = new SafeMap();
339-
const runningSubtests = new SafeMap();
340-
341338
function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
339+
const watchMode = filesWatcher != null;
342340
const subtest = root.createSubtest(FileTest, path, async (t) => {
343341
const args = getRunArgs({ path, inspectPort, testNamePatterns });
344342
const stdio = ['pipe', 'pipe', 'pipe'];
345343
const env = { ...process.env, NODE_TEST_CONTEXT: 'child-v8' };
346-
if (filesWatcher) {
344+
if (watchMode) {
347345
stdio.push('ipc');
348346
env.WATCH_REPORT_DEPENDENCIES = '1';
349347
}
@@ -352,11 +350,13 @@ function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
352350
}
353351

354352
const child = spawn(process.execPath, args, { signal: t.signal, encoding: 'utf8', env, stdio });
355-
runningProcesses.set(path, child);
353+
if (watchMode) {
354+
filesWatcher.runningProcesses.set(path, child);
355+
filesWatcher.watcher.watchChildProcessModules(child, path);
356+
}
356357

357358
let err;
358359

359-
filesWatcher?.watchChildProcessModules(child, path);
360360

361361
child.on('error', (error) => {
362362
err = error;
@@ -388,8 +388,14 @@ function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
388388
finished(child.stdout, { signal: t.signal }),
389389
]);
390390

391-
runningProcesses.delete(path);
392-
runningSubtests.delete(path);
391+
if (watchMode) {
392+
filesWatcher.runningProcesses.delete(path);
393+
filesWatcher.runningSubtests.delete(path);
394+
if (filesWatcher.runningSubtests.size === 0) {
395+
root.reporter[kEmitMessage]('test:watch:drained');
396+
}
397+
}
398+
393399
if (code !== 0 || signal !== null) {
394400
if (!err) {
395401
const failureType = subtest.failedSubtests ? kSubtestsFailed : kTestCodeFailure;
@@ -410,9 +416,13 @@ function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
410416
}
411417

412418
function watchFiles(testFiles, root, inspectPort, signal, testNamePatterns) {
413-
const filesWatcher = new FilesWatcher({ throttle: 500, mode: 'filter', signal });
414-
filesWatcher.on('changed', ({ owners }) => {
415-
filesWatcher.unfilterFilesOwnedBy(owners);
419+
const runningProcesses = new SafeMap();
420+
const runningSubtests = new SafeMap();
421+
const watcher = new FilesWatcher({ throttle: 500, mode: 'filter', signal });
422+
const filesWatcher = { __proto__: null, watcher, runningProcesses, runningSubtests };
423+
424+
watcher.on('changed', ({ owners }) => {
425+
watcher.unfilterFilesOwnedBy(owners);
416426
PromisePrototypeThen(SafePromiseAllReturnVoid(testFiles, async (file) => {
417427
if (!owners.has(file)) {
418428
return;
@@ -433,6 +443,7 @@ function watchFiles(testFiles, root, inspectPort, signal, testNamePatterns) {
433443
}));
434444
});
435445
signal?.addEventListener('abort', () => root.postRun(), { __proto__: null, once: true });
446+
436447
return filesWatcher;
437448
}
438449

@@ -482,7 +493,7 @@ function run(options) {
482493
root.harness.bootstrapComplete = true;
483494
return SafePromiseAllSettledReturnVoid(testFiles, (path) => {
484495
const subtest = runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns);
485-
runningSubtests.set(path, subtest);
496+
filesWatcher?.runningSubtests.set(path, subtest);
486497
return subtest;
487498
});
488499
};

test/parallel/test-runner-run.mjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,14 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {
132132
.toArray();
133133
assert.deepStrictEqual(result, ['this should pass']);
134134
});
135+
136+
it('should emit "test:watch:drained" event on watch mode', async () => {
137+
const controller = new AbortController();
138+
await run({ files: [join(testFixtures, 'test/random.cjs')], watch: true, signal: controller.signal })
139+
.on('data', function({ type }) {
140+
if (type === 'test:watch:drained') {
141+
controller.abort();
142+
}
143+
});
144+
});
135145
});

0 commit comments

Comments
 (0)