Skip to content

Commit

Permalink
test_runner: accept testOnly in run
Browse files Browse the repository at this point in the history
  • Loading branch information
MoLow committed Sep 23, 2023
1 parent 55fde47 commit 2ea498f
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 17 deletions.
2 changes: 2 additions & 0 deletions doc/api/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,8 @@ changes:
number. If a nullish value is provided, each process gets its own port,
incremented from the primary's `process.debugPort`.
**Default:** `undefined`.
* `only`: {boolean} If truthy, the test context will only run tests that
have the `only` option set
* `setup` {Function} A function that accepts the `TestsStream` instance
and can be used to setup listeners before any tests are run.
**Default:** `undefined`.
Expand Down
41 changes: 24 additions & 17 deletions lib/internal/test_runner/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,17 @@ function filterExecArgv(arg, i, arr) {
!ArrayPrototypeSome(kFilterArgValues, (p) => arg === p || (i > 0 && arr[i - 1] === p) || StringPrototypeStartsWith(arg, `${p}=`));
}

function getRunArgs({ path, inspectPort, testNamePatterns }) {
function getRunArgs(path, { inspectPort, testNamePatterns, only }) {
const argv = ArrayPrototypeFilter(process.execArgv, filterExecArgv);
if (isUsingInspector()) {
ArrayPrototypePush(argv, `--inspect-port=${getInspectPort(inspectPort)}`);
}
if (testNamePatterns) {
if (testNamePatterns != null) {
ArrayPrototypeForEach(testNamePatterns, (pattern) => ArrayPrototypePush(argv, `--test-name-pattern=${pattern}`));
}
if (only === true) {
ArrayPrototypePush(argv, '--test-only');
}
ArrayPrototypePush(argv, path);

return argv;
Expand Down Expand Up @@ -301,17 +304,17 @@ class FileTest extends Test {
}
}

function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
function runTestFile(path, filesWatcher, opts) {
const watchMode = filesWatcher != null;
const subtest = root.createSubtest(FileTest, path, async (t) => {
const args = getRunArgs({ __proto__: null, path, inspectPort, testNamePatterns });
const subtest = opts.root.createSubtest(FileTest, path, async (t) => {
const args = getRunArgs(path, opts);
const stdio = ['pipe', 'pipe', 'pipe'];
const env = { __proto__: null, ...process.env, NODE_TEST_CONTEXT: 'child-v8' };
if (watchMode) {
stdio.push('ipc');
env.WATCH_REPORT_DEPENDENCIES = '1';
}
if (root.harness.shouldColorizeTestFiles) {
if (opts.root.harness.shouldColorizeTestFiles) {
env.FORCE_COLOR = '1';
}

Expand Down Expand Up @@ -358,7 +361,7 @@ function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
filesWatcher.runningProcesses.delete(path);
filesWatcher.runningSubtests.delete(path);
if (filesWatcher.runningSubtests.size === 0) {
root.reporter[kEmitMessage]('test:watch:drained');
opts.root.reporter[kEmitMessage]('test:watch:drained');
}
}

Expand All @@ -381,10 +384,10 @@ function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
return subtest.start();
}

function watchFiles(testFiles, root, inspectPort, signal, testNamePatterns) {
function watchFiles(testFiles, opts) {
const runningProcesses = new SafeMap();
const runningSubtests = new SafeMap();
const watcher = new FilesWatcher({ __proto__: null, debounce: 200, mode: 'filter', signal });
const watcher = new FilesWatcher({ __proto__: null, debounce: 200, mode: 'filter', signal: opts.signal });
const filesWatcher = { __proto__: null, watcher, runningProcesses, runningSubtests };

watcher.on('changed', ({ owners }) => {
Expand All @@ -400,19 +403,19 @@ function watchFiles(testFiles, root, inspectPort, signal, testNamePatterns) {
}
if (!runningSubtests.size) {
// Reset the topLevel counter
root.harness.counters.topLevel = 0;
opts.root.harness.counters.topLevel = 0;
}
await runningSubtests.get(file);
runningSubtests.set(file, runTestFile(file, root, inspectPort, filesWatcher, testNamePatterns));
runningSubtests.set(file, runTestFile(file, filesWatcher, opts));
}, undefined, (error) => {
triggerUncaughtException(error, true /* fromPromise */);
}));
});
if (signal) {
if (opts.signal) {
kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation;
signal.addEventListener(
opts.signal.addEventListener(
'abort',
() => root.postRun(),
() => opts.root.postRun(),
{ __proto__: null, once: true, [kResistStopPropagation]: true },
);
}
Expand All @@ -425,14 +428,17 @@ function run(options) {
options = kEmptyObject;
}
let { testNamePatterns, shard } = options;
const { concurrency, timeout, signal, files, inspectPort, watch, setup } = options;
const { concurrency, timeout, signal, files, inspectPort, watch, setup, only } = options;

if (files != null) {
validateArray(files, 'options.files');
}
if (watch != null) {
validateBoolean(watch, 'options.watch');
}
if (only != null) {
validateBoolean(only, 'options.only');
}
if (shard != null) {
validateObject(shard, 'options.shard');
// Avoid re-evaluating the shard object in case it's a getter
Expand Down Expand Up @@ -478,14 +484,15 @@ function run(options) {

let postRun = () => root.postRun();
let filesWatcher;
const opts = { __proto__: null, root, signal, inspectPort, testNamePatterns, only };
if (watch) {
filesWatcher = watchFiles(testFiles, root, inspectPort, signal, testNamePatterns);
filesWatcher = watchFiles(testFiles, opts);
postRun = undefined;
}
const runFiles = () => {
root.harness.bootstrapComplete = true;
return SafePromiseAllSettledReturnVoid(testFiles, (path) => {
const subtest = runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns);
const subtest = runTestFile(path, filesWatcher, opts);
filesWatcher?.runningSubtests.set(path, subtest);
return subtest;
});
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/test-runner/test_only.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';
const test = require('node:test');

test('this should be skipped');
test.only('this should be executed');
12 changes: 12 additions & 0 deletions test/parallel/test-runner-run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,18 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {
assert.strictEqual(result[5], 'ok 2 - this should be executed\n');
});

it('should pass only to children', async () => {
const result = await run({
files: [join(testFixtures, 'test_only.js')],
only: true
})
.compose(tap)
.toArray();

assert.strictEqual(result[2], 'ok 1 - this should be skipped # SKIP \'only\' option not set\n');
assert.strictEqual(result[5], 'ok 2 - this should be executed\n');
});

it('should emit "test:watch:drained" event on watch mode', async () => {
const controller = new AbortController();
await run({
Expand Down

0 comments on commit 2ea498f

Please sign in to comment.