Skip to content

Commit

Permalink
test_runner: bootstrap reporters before running tests
Browse files Browse the repository at this point in the history
PR-URL: nodejs#46737
Fixes: nodejs#46747
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
  • Loading branch information
MoLow committed Feb 25, 2023
1 parent 3983267 commit 5025bb2
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 12 deletions.
3 changes: 3 additions & 0 deletions doc/api/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,9 @@ added: v18.9.0
**Default:** `true`.
* `files`: {Array} An array containing the list of files to run.
**Default** matching files from [test runner execution model][].
* `setup` {Function} A function that accepts the `TestsStream` instance
and can be used to setup listeners before any tests are run.
**Default:** `undefined`.
* `signal` {AbortSignal} Allows aborting an in-progress test execution.
* `timeout` {number} A number of milliseconds the test execution will
fail after.
Expand Down
5 changes: 2 additions & 3 deletions lib/internal/main/test_runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ if (isUsingInspector()) {
inspectPort = process.debugPort;
}

const testsStream = run({ concurrency, inspectPort, watch: getOptionValue('--watch') });
testsStream.once('test:fail', () => {
run({ concurrency, inspectPort, watch: getOptionValue('--watch'), setup: setupTestReporters })
.once('test:fail', () => {
process.exitCode = 1;
});
setupTestReporters(testsStream);
12 changes: 9 additions & 3 deletions lib/internal/test_runner/harness.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,29 +159,35 @@ function setup(root) {
}

let globalRoot;
let reportersSetup;
function getGlobalRoot() {
if (!globalRoot) {
globalRoot = createTestTree();
globalRoot.reporter.once('test:fail', () => {
process.exitCode = 1;
});
setupTestReporters(globalRoot.reporter);
reportersSetup = setupTestReporters(globalRoot.reporter);
}
return globalRoot;
}

async function startSubtest(subtest) {
await reportersSetup;
await subtest.start();
}

function test(name, options, fn) {
const parent = testResources.get(executionAsyncId()) || getGlobalRoot();
const subtest = parent.createSubtest(Test, name, options, fn);
return subtest.start();
return startSubtest(subtest);
}

function runInParentContext(Factory) {
function run(name, options, fn, overrides) {
const parent = testResources.get(executionAsyncId()) || getGlobalRoot();
const subtest = parent.createSubtest(Factory, name, options, fn, overrides);
if (parent === getGlobalRoot()) {
subtest.start();
startSubtest(subtest);
}
}

Expand Down
21 changes: 15 additions & 6 deletions lib/internal/test_runner/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ const {
ObjectAssign,
ObjectKeys,
PromisePrototypeThen,
SafePromiseAll,
SafePromiseAllReturnVoid,
SafePromiseAllSettledReturnVoid,
PromiseResolve,
SafeMap,
SafeSet,
StringPrototypeIndexOf,
Expand All @@ -24,6 +26,7 @@ const {

const { spawn } = require('child_process');
const { readdirSync, statSync } = require('fs');
const { finished } = require('internal/streams/end-of-stream');
// TODO(aduh95): switch to internal/readline/interface when backporting to Node.js 16.x is no longer a concern.
const { createInterface } = require('readline');
const { FilesWatcher } = require('internal/watch_mode/files_watcher');
Expand All @@ -33,7 +36,7 @@ const {
ERR_TEST_FAILURE,
},
} = require('internal/errors');
const { validateArray, validateBoolean } = require('internal/validators');
const { validateArray, validateBoolean, validateFunction } = require('internal/validators');
const { getInspectPort, isUsingInspector, isInspectorMessage } = require('internal/util/inspector');
const { kEmptyObject } = require('internal/util');
const { createTestTree } = require('internal/test_runner/harness');
Expand Down Expand Up @@ -298,7 +301,10 @@ function runTestFile(path, root, inspectPort, filesWatcher) {
subtest.addToReport(ast);
});

const { 0: code, 1: signal } = await once(child, 'exit', { signal: t.signal });
const { 0: { 0: code, 1: signal } } = await SafePromiseAll([
once(child, 'exit', { signal: t.signal }),
finished(parser, { signal: t.signal }),
]);

runningProcesses.delete(path);
runningSubtests.delete(path);
Expand Down Expand Up @@ -347,14 +353,17 @@ function run(options) {
if (options === null || typeof options !== 'object') {
options = kEmptyObject;
}
const { concurrency, timeout, signal, files, inspectPort, watch } = options;
const { concurrency, timeout, signal, files, inspectPort, watch, setup } = options;

if (files != null) {
validateArray(files, 'options.files');
}
if (watch != null) {
validateBoolean(watch, 'options.watch');
}
if (setup != null) {
validateFunction(setup, 'options.setup');
}

const root = createTestTree({ concurrency, timeout, signal });
const testFiles = files ?? createTestFileList();
Expand All @@ -365,13 +374,13 @@ function run(options) {
filesWatcher = watchFiles(testFiles, root, inspectPort);
postRun = undefined;
}

PromisePrototypeThen(SafePromiseAllSettledReturnVoid(testFiles, (path) => {
const runFiles = () => SafePromiseAllSettledReturnVoid(testFiles, (path) => {
const subtest = runTestFile(path, root, inspectPort, filesWatcher);
runningSubtests.set(path, subtest);
return subtest;
}), postRun);
});

PromisePrototypeThen(PromisePrototypeThen(PromiseResolve(setup?.(root.reporter)), runFiles), postRun);

return root.reporter;
}
Expand Down

0 comments on commit 5025bb2

Please sign in to comment.