Skip to content

Commit

Permalink
test_runner: introduce NODE_TEST_WORKER_ID for improved concurrent te…
Browse files Browse the repository at this point in the history
…st execution

Added a new environment variable, `NODE_TEST_WORKER_ID`, which ranges from 1 to N when `--experimental-test-isolation=process` is enabled and defaults to 1 when `--experimental-test-isolation=none` is used.
  • Loading branch information
cu8code committed Dec 5, 2024
1 parent 3fb2ea8 commit c722258
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 3 deletions.
13 changes: 13 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -2249,6 +2249,19 @@ Starts the Node.js command line test runner. This flag cannot be combined with
See the documentation on [running tests from the command line][]
for more details.

### Environment Variable

The following environment variable is available when running tests with the `--test` flag:

`NODE_TEST_WORKER_ID`:

* Parallel Test Runs: If tests are executed in parallel (using the `--test` flag
alone or with the `--experimental-test-isolation=process` flag), this variable is assigned
a unique number ranging from `1` to `N`, where `N` represents the total number of test files being processed.

* Isolated Test Runs: When tests are run in isolation (using the `--experimental-test-isolation=none` flag),
this variable is always set to `1`.

### `--test-concurrency`

<!-- YAML
Expand Down
9 changes: 6 additions & 3 deletions lib/internal/test_runner/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,14 +358,14 @@ class FileTest extends Test {
}
}

function runTestFile(path, filesWatcher, opts) {
function runTestFile(path, filesWatcher, opts, workerId = 1) {
const watchMode = filesWatcher != null;
const testPath = path === kIsolatedProcessName ? '' : path;
const testOpts = { __proto__: null, signal: opts.signal };
const subtest = opts.root.createSubtest(FileTest, testPath, testOpts, async (t) => {
const args = getRunArgs(path, opts);
const stdio = ['pipe', 'pipe', 'pipe'];
const env = { __proto__: null, ...process.env, NODE_TEST_CONTEXT: 'child-v8' };
const env = { __proto__: null, ...process.env, NODE_TEST_CONTEXT: 'child-v8', NODE_TEST_WORKER_ID: workerId };
if (watchMode) {
stdio.push('ipc');
env.WATCH_REPORT_DEPENDENCIES = '1';
Expand Down Expand Up @@ -724,8 +724,10 @@ function run(options = kEmptyObject) {
runFiles = () => {
root.harness.bootstrapPromise = null;
root.harness.buildPromise = null;
let workerId = 1;
return SafePromiseAllSettledReturnVoid(testFiles, (path) => {
const subtest = runTestFile(path, filesWatcher, opts);
const subtest = runTestFile(path, filesWatcher, opts, workerId);
workerId++;
filesWatcher?.runningSubtests.set(path, subtest);
return subtest;
});
Expand Down Expand Up @@ -766,6 +768,7 @@ function run(options = kEmptyObject) {

root.entryFile = resolve(testFile);
debug('loading test file:', fileURL.href);
process.env.NODE_TEST_WORKER_ID = 1;
try {
await cascadedLoader.import(fileURL, parent, { __proto__: null });
} catch (err) {
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/test-runner/NODE_TEST_WORKER_ID/1.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const test = require('node:test');

test('test1', t => {
console.log('NODE_TEST_WORKER_ID', process.env.NODE_TEST_WORKER_ID)
});
5 changes: 5 additions & 0 deletions test/fixtures/test-runner/NODE_TEST_WORKER_ID/2.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const test = require('node:test');

test('test2', t => {
console.log('NODE_TEST_WORKER_ID', process.env.NODE_TEST_WORKER_ID)
});
5 changes: 5 additions & 0 deletions test/fixtures/test-runner/NODE_TEST_WORKER_ID/3.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const test = require('node:test');

test('test3', t => {
console.log('NODE_TEST_WORKER_ID', process.env.NODE_TEST_WORKER_ID)
});
35 changes: 35 additions & 0 deletions test/parallel/test-runner-worker-id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict';

require('../common');
const assert = require('assert');
const { spawnSync } = require('child_process');
const { join } = require('path');
const fixtures = require('../common/fixtures');
const testFixtures = fixtures.path('test-runner');

{
const args = ['--test', '--experimental-test-isolation=process'];
const child = spawnSync(process.execPath, args, { cwd: join(testFixtures, 'NODE_TEST_WORKER_ID') });

assert.strictEqual(child.stderr.toString(), '');
const stdout = child.stdout.toString();

assert.match(stdout, /NODE_TEST_WORKER_ID 1/);
assert.match(stdout, /NODE_TEST_WORKER_ID 2/);
assert.match(stdout, /NODE_TEST_WORKER_ID 3/);

assert.strictEqual(child.status, 0);
}

{
const args = ['--test', '--experimental-test-isolation=none'];
const child = spawnSync(process.execPath, args, { cwd: join(testFixtures, 'NODE_TEST_WORKER_ID') });

assert.strictEqual(child.stderr.toString(), '');
const stdout = child.stdout.toString();
const regex = /NODE_TEST_WORKER_ID 1/g;
const result = stdout.match(regex);

assert.strictEqual(result.length, 3);
assert.strictEqual(child.status, 0);
}

0 comments on commit c722258

Please sign in to comment.