Skip to content

Commit e1746a9

Browse files
committed
test_runner: avoid reading process state directly in run()
Refs: #53867 The run() function is exposed as a public API via node:test module. Previously, it accessed process.argv, process.execArgv, process.cwd(), and process.env directly, which meant programmatic API users could not fully control the test runner behavior. This change: - Captures process.argv, process.cwd(), process.env, and process.execArgv in the CLI entry point (main/test_runner.js) and passes them explicitly to run() as options - Adds a processExecArgv option for V8 flag propagation - Replaces process.env fallback in runTestFile() with opts.env - Replaces process.argv usage in getRunArgs() with globPatterns - Replaces process.env.NODE_TEST_CONTEXT check with env option - Maintains backwards compatibility: when options are not provided, run() falls back to process state as before
1 parent 7547e79 commit e1746a9

2 files changed

Lines changed: 30 additions & 6 deletions

File tree

lib/internal/main/test_runner.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ if (isUsingInspector() && options.isolation === 'process') {
3030
options.inspectPort = process.debugPort;
3131
}
3232

33+
// Capture process state explicitly so run() does not need to access it.
3334
options.globPatterns = ArrayPrototypeSlice(process.argv, 1);
35+
options.cwd = process.cwd();
36+
options.env = process.env;
37+
options.processExecArgv = process.execArgv;
3438

3539
debug('test runner configuration:', options);
3640
run(options).on('test:summary', (data) => {

lib/internal/test_runner/runner.js

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ function getRunArgs(path, { forceExit,
167167
only,
168168
argv: suppliedArgs,
169169
execArgv,
170+
processExecArgv,
171+
globPatterns,
170172
rerunFailuresFilePath,
171173
root: { timeout },
172174
cwd }) {
@@ -183,7 +185,7 @@ function getRunArgs(path, { forceExit,
183185
*/
184186
const nodeOptionsSet = new SafeSet(processNodeOptions);
185187
const unknownProcessExecArgv = ArrayPrototypeFilter(
186-
process.execArgv,
188+
processExecArgv,
187189
(arg) => !nodeOptionsSet.has(arg),
188190
);
189191
ArrayPrototypePushApply(runArgs, unknownProcessExecArgv);
@@ -214,7 +216,9 @@ function getRunArgs(path, { forceExit,
214216

215217
if (path === kIsolatedProcessName) {
216218
ArrayPrototypePush(runArgs, '--test');
217-
ArrayPrototypePushApply(runArgs, ArrayPrototypeSlice(process.argv, 1));
219+
if (globPatterns != null) {
220+
ArrayPrototypePushApply(runArgs, globPatterns);
221+
}
218222
} else {
219223
ArrayPrototypePush(runArgs, path);
220224
}
@@ -421,7 +425,7 @@ function runTestFile(path, filesWatcher, opts) {
421425
const subtest = opts.root.createSubtest(FileTest, testPath, testOpts, async (t) => {
422426
const args = getRunArgs(path, opts);
423427
const stdio = ['pipe', 'pipe', 'pipe'];
424-
const env = { __proto__: null, NODE_TEST_CONTEXT: 'child-v8', ...(opts.env || process.env) };
428+
const env = { __proto__: null, NODE_TEST_CONTEXT: 'child-v8', ...opts.env };
425429

426430
// Acquire a worker ID from the pool for process isolation mode
427431
let workerId;
@@ -648,11 +652,26 @@ function run(options = kEmptyObject) {
648652
functionCoverage = 0,
649653
execArgv = [],
650654
argv = [],
651-
cwd = process.cwd(),
655+
cwd,
656+
processExecArgv,
652657
rerunFailuresFilePath,
653658
env,
654659
} = options;
655660

661+
// Default to process state when not explicitly provided, maintaining
662+
// backwards compatibility for public API users while allowing the
663+
// internal CLI entry point to pass these values explicitly.
664+
const userProvidedEnv = env !== undefined;
665+
if (cwd === undefined) {
666+
cwd = process.cwd();
667+
}
668+
if (env === undefined) {
669+
env = process.env;
670+
}
671+
if (processExecArgv === undefined) {
672+
processExecArgv = process.execArgv;
673+
}
674+
656675
if (files != null) {
657676
validateArray(files, 'options.files');
658677
}
@@ -759,7 +778,7 @@ function run(options = kEmptyObject) {
759778
validatePath(globalSetupPath, 'options.globalSetupPath');
760779
}
761780

762-
if (env != null) {
781+
if (userProvidedEnv) {
763782
validateObject(env);
764783

765784
if (isolation === 'none') {
@@ -830,13 +849,14 @@ function run(options = kEmptyObject) {
830849
isolation,
831850
argv,
832851
execArgv,
852+
processExecArgv,
833853
rerunFailuresFilePath,
834854
env,
835855
workerIdPool: isolation === 'process' ? workerIdPool : null,
836856
};
837857

838858
if (isolation === 'process') {
839-
if (process.env.NODE_TEST_CONTEXT !== undefined) {
859+
if (env.NODE_TEST_CONTEXT !== undefined) {
840860
process.emitWarning('node:test run() is being called recursively within a test file. skipping running files.');
841861
root.postRun();
842862
return root.reporter;

0 commit comments

Comments
 (0)