Skip to content

test_runner: avoid reading process state directly in run()#62377

Open
murataslan1 wants to merge 2 commits intonodejs:mainfrom
murataslan1:fix/test-runner-process-state
Open

test_runner: avoid reading process state directly in run()#62377
murataslan1 wants to merge 2 commits intonodejs:mainfrom
murataslan1:fix/test-runner-process-state

Conversation

@murataslan1
Copy link

Refs: #53867

Summary

The run() function is exposed as a public API via node:test, but it accessed process.argv, process.execArgv, process.cwd(), and process.env directly. This meant programmatic API users could not fully control the test runner behavior.

PR #54705 added cwd as an option but other process state references remained (as noted by @cjihrig).

Changes

  • Capture process.argv, process.cwd(), process.env, and process.execArgv in the CLI entry point (lib/internal/main/test_runner.js) and pass them explicitly to run() as options
  • Add processExecArgv option for V8 flag propagation to child processes
  • Replace process.env fallback in runTestFile() with opts.env
  • Replace process.argv usage in getRunArgs() with globPatterns
  • Replace process.env.NODE_TEST_CONTEXT check with env option
  • Maintain backwards compatibility: when options are not provided, run() falls back to process state as before

Test plan

  • Existing test runner tests should pass without changes
  • Programmatic run() calls now respect explicitly provided env, cwd, processExecArgv options instead of reading from process globals

Refs: nodejs#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
Copilot AI review requested due to automatic review settings March 21, 2026 23:34
@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/test_runner

@nodejs-github-bot nodejs-github-bot added needs-ci PRs that need a full CI run. test_runner Issues and PRs related to the test runner subsystem. labels Mar 21, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Node.js test runner so the public node:test run() API can be controlled programmatically without implicitly reading from process globals (argv/cwd/env/execArgv), while keeping backwards-compatible fallbacks.

Changes:

  • Capture process.argv, process.cwd(), process.env, and process.execArgv in the CLI entry point and pass them into run() as options.
  • Add/propagate processExecArgv for forwarding V8 flags to child processes.
  • Replace remaining direct reads of process.argv/process.env in runner internals with option-based values.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
lib/internal/test_runner/runner.js Adds option plumbing/defaults for process state, uses passed-in argv/env/execArgv equivalents when spawning child test processes.
lib/internal/main/test_runner.js Captures process state at the CLI entry point and passes it down via run() options.
Comments suppressed due to low confidence (1)

lib/internal/test_runner/runner.js:787

  • run() now treats any defined env as user-provided, and rejects it when isolation === 'none'. Because the CLI entry point always passes options.env = process.env, node --test --test-isolation=none will now throw. Consider adjusting the "user provided" detection (e.g., distinguish internal captured env from an explicit API override) or relaxing the isolation='none' restriction for the default/inherited env case.
  if (userProvidedEnv) {
    validateObject(env);

    if (isolation === 'none') {
      throw new ERR_INVALID_ARG_VALUE('options.env', env, 'is not supported with isolation=\'none\'');
    }
  }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 170 to 172
processExecArgv,
globPatterns,
rerunFailuresFilePath,
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

globPatterns is forwarded into runArgs via ArrayPrototypePushApply() (so it must be an array of strings). Currently run() only ensures it's an array; consider validating its element types (e.g., string array) to avoid child-spawn argument errors caused by non-string entries.

Copilot uses AI. Check for mistakes.
// Capture process state explicitly so run() does not need to access it.
options.globPatterns = ArrayPrototypeSlice(process.argv, 1);
options.cwd = process.cwd();
options.env = process.env;
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

options.env is being set unconditionally, which makes run() think the caller explicitly provided an env option. With --test-isolation=none, run() currently rejects options.env, so this change will break node --test --test-isolation=none. Consider only setting/passing options.env in the CLI when isolation='process' (or update run() to treat this captured env as non-user-provided).

Suggested change
options.env = process.env;
if (options.isolation === 'process') {
options.env = process.env;
}

Copilot uses AI. Check for mistakes.
Comment on lines 654 to 657
argv = [],
cwd = process.cwd(),
cwd,
processExecArgv,
rerunFailuresFilePath,
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

processExecArgv is consumed by getRunArgs() as an array (it is filtered and iterated), but there is no validation for the options.processExecArgv type. If a public API caller passes a non-array value, this will fail at runtime in less obvious ways. Consider validating it (e.g., validateStringArray(processExecArgv, 'options.processExecArgv')) when it is explicitly provided.

Copilot uses AI. Check for mistakes.
options.globPatterns = ArrayPrototypeSlice(process.argv, 1);
options.cwd = process.cwd();
options.env = process.env;
options.processExecArgv = process.execArgv;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How's this differ from execArgv?

Copy link
Author

@murataslan1 murataslan1 Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you're right, dropped it. The idea was to avoid the direct process.execArgv read, but it doesn't really make sense as a user-facing option — it's just the parent's V8 flags that get forwarded to child processes. Reverted to reading it directly.

@avivkeller
Copy link
Member

Blocking as this needs documentation and tests

samueldreadfulwater3-hash

This comment was marked as off-topic.

- Remove processExecArgv option: process.execArgv is runtime state
  that should not be a public API option. Keep reading it directly
  in getRunArgs() as before.
- Fix env with isolation=none: only set options.env from CLI entry
  point when isolation is not 'none', preventing ERR_INVALID_ARG_VALUE
  when using --test-isolation=none.
- Add documentation for the env option in doc/api/test.md.
- Add tests for env option acceptance and env+isolation=none rejection.
@murataslan1
Copy link
Author

murataslan1 commented Mar 22, 2026

Pushed fixes based on the feedback:

  • Dropped processExecArgv entirely — doesn't belong in the public API, reverted to reading process.execArgv directly
  • Fixed the env + isolation=none issue — only passing env from CLI when isolation is process-based
  • Added docs for the env option and two tests (env acceptance + env rejection with isolation=none)

Let me know if anything else needs changing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-ci PRs that need a full CI run. test_runner Issues and PRs related to the test runner subsystem.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants