Skip to content

Commit

Permalink
test_runner: emit test:watch:drained event
Browse files Browse the repository at this point in the history
PR-URL: #48259
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
  • Loading branch information
MoLow authored and nodejs-github-bot committed Jun 2, 2023
1 parent ca71ae6 commit 5d685e4
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 12 deletions.
4 changes: 4 additions & 0 deletions doc/api/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,10 @@ This event is only emitted if `--test` flag is passed.
Emitted when a running test writes to `stdout`.
This event is only emitted if `--test` flag is passed.

### Event: `'test:watch:drained'`

Emitted when no more tests are queued for execution in watch mode.

## Class: `TestContext`

<!-- YAML
Expand Down
35 changes: 23 additions & 12 deletions lib/internal/test_runner/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,15 +335,13 @@ class FileTest extends Test {
}
}

const runningProcesses = new SafeMap();
const runningSubtests = new SafeMap();

function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
const watchMode = filesWatcher != null;
const subtest = root.createSubtest(FileTest, path, async (t) => {
const args = getRunArgs({ path, inspectPort, testNamePatterns });
const stdio = ['pipe', 'pipe', 'pipe'];
const env = { ...process.env, NODE_TEST_CONTEXT: 'child-v8' };
if (filesWatcher) {
if (watchMode) {
stdio.push('ipc');
env.WATCH_REPORT_DEPENDENCIES = '1';
}
Expand All @@ -352,11 +350,13 @@ function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
}

const child = spawn(process.execPath, args, { signal: t.signal, encoding: 'utf8', env, stdio });
runningProcesses.set(path, child);
if (watchMode) {
filesWatcher.runningProcesses.set(path, child);
filesWatcher.watcher.watchChildProcessModules(child, path);
}

let err;

filesWatcher?.watchChildProcessModules(child, path);

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

runningProcesses.delete(path);
runningSubtests.delete(path);
if (watchMode) {
filesWatcher.runningProcesses.delete(path);
filesWatcher.runningSubtests.delete(path);
if (filesWatcher.runningSubtests.size === 0) {
root.reporter[kEmitMessage]('test:watch:drained');
}
}

if (code !== 0 || signal !== null) {
if (!err) {
const failureType = subtest.failedSubtests ? kSubtestsFailed : kTestCodeFailure;
Expand All @@ -410,9 +416,13 @@ function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
}

function watchFiles(testFiles, root, inspectPort, signal, testNamePatterns) {
const filesWatcher = new FilesWatcher({ throttle: 500, mode: 'filter', signal });
filesWatcher.on('changed', ({ owners }) => {
filesWatcher.unfilterFilesOwnedBy(owners);
const runningProcesses = new SafeMap();
const runningSubtests = new SafeMap();
const watcher = new FilesWatcher({ throttle: 500, mode: 'filter', signal });
const filesWatcher = { __proto__: null, watcher, runningProcesses, runningSubtests };

watcher.on('changed', ({ owners }) => {
watcher.unfilterFilesOwnedBy(owners);
PromisePrototypeThen(SafePromiseAllReturnVoid(testFiles, async (file) => {
if (!owners.has(file)) {
return;
Expand All @@ -433,6 +443,7 @@ function watchFiles(testFiles, root, inspectPort, signal, testNamePatterns) {
}));
});
signal?.addEventListener('abort', () => root.postRun(), { __proto__: null, once: true });

return filesWatcher;
}

Expand Down Expand Up @@ -482,7 +493,7 @@ function run(options) {
root.harness.bootstrapComplete = true;
return SafePromiseAllSettledReturnVoid(testFiles, (path) => {
const subtest = runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns);
runningSubtests.set(path, subtest);
filesWatcher?.runningSubtests.set(path, subtest);
return subtest;
});
};
Expand Down
10 changes: 10 additions & 0 deletions test/parallel/test-runner-run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,14 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {
.toArray();
assert.deepStrictEqual(result, ['this should pass']);
});

it('should emit "test:watch:drained" event on watch mode', async () => {
const controller = new AbortController();
await run({ files: [join(testFixtures, 'test/random.cjs')], watch: true, signal: controller.signal })
.on('data', function({ type }) {
if (type === 'test:watch:drained') {
controller.abort();
}
});
});
});

0 comments on commit 5d685e4

Please sign in to comment.