Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test_runner: bootstrap reporters before running tests #46737

Merged
merged 1 commit into from
Feb 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/api/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,9 @@ added:
**Default:** `false`.
* `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 @@ -22,8 +22,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 = kGenericUserError;
});
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 @@ -161,29 +161,35 @@ function setup(root) {
}

let globalRoot;
let reportersSetup;
function getGlobalRoot() {
if (!globalRoot) {
globalRoot = createTestTree();
globalRoot.reporter.once('test:fail', () => {
process.exitCode = kGenericUserError;
});
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 @@ -299,7 +302,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 @@ -348,14 +354,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 @@ -366,13 +375,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