From e39d5b9ef80e01b372fc14cf0ac14abb15e3218b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 22 Mar 2026 12:24:47 -0700 Subject: [PATCH] perf: default unit vitest lanes to threads --- scripts/test-parallel.mjs | 38 +- test/fixtures/test-parallel.behavior.json | 418 +++++++++++----------- 2 files changed, 236 insertions(+), 220 deletions(-) diff --git a/scripts/test-parallel.mjs b/scripts/test-parallel.mjs index f8b8dd0bc23..98aafcf1b0f 100644 --- a/scripts/test-parallel.mjs +++ b/scripts/test-parallel.mjs @@ -85,14 +85,13 @@ const testProfile = const isMacMiniProfile = testProfile === "macmini"; // Vitest executes Node tests through Vite's SSR/module-runner pipeline, so the // shared unit lane still retains transformed ESM/module state even when the -// tests themselves are not "server rendering" a website. vmForks can win in -// ideal transform-heavy cases, but for this repo we measured higher aggregate -// CPU load and fatal heap OOMs on memory-constrained dev machines and CI when -// unit-fast stayed on vmForks. Keep forks as the default unless that evidence -// is re-run and replaced: -// PR: https://github.com/openclaw/openclaw/pull/51145 -// OOM evidence: https://github.com/openclaw/openclaw/pull/51145#issuecomment-4099663958 -// Preserve OPENCLAW_TEST_VM_FORKS=1 as the explicit override/debug escape hatch. +// tests themselves are not "server rendering" a website. We previously kept +// forks as the default after vmFork OOM regressions on constrained hosts. On +// 2026-03-22, a direct full-unit threads run finished 1109/1110 green; the sole +// correctness exception stayed on the manifest fork lane, so the wrapper now +// defaults unit runs to threads while preserving explicit fork/vmFork escapes. +// Preserve OPENCLAW_TEST_UNIT_DEFAULT_POOL=forks or OPENCLAW_TEST_VM_FORKS=1 as +// explicit debug escape hatches. const supportsVmForks = Number.isFinite(nodeMajor) ? nodeMajor <= 24 : true; const useVmForks = process.env.OPENCLAW_TEST_VM_FORKS === "1" && supportsVmForks; const forceIsolation = @@ -103,6 +102,15 @@ const disableIsolation = process.env.OPENCLAW_TEST_NO_ISOLATE !== "false"; const includeGatewaySuite = process.env.OPENCLAW_TEST_INCLUDE_GATEWAY === "1"; const includeExtensionsSuite = process.env.OPENCLAW_TEST_INCLUDE_EXTENSIONS === "1"; +const parsePoolOverride = (value, fallback) => { + if (value === "threads" || value === "forks") { + return value; + } + if (value === "vmForks") { + return supportsVmForks ? value : "forks"; + } + return fallback; +}; // Even on low-memory hosts, keep the isolated lane split so files like // git-commit.test.ts still get the worker/process isolation they require. const shouldSplitUnitRuns = testProfile !== "serial"; @@ -263,8 +271,16 @@ const allKnownTestFiles = [ ...walkTestFiles(path.join("ui", "src", "ui")), ]), ]; +const defaultUnitPool = parsePoolOverride(process.env.OPENCLAW_TEST_UNIT_DEFAULT_POOL, "threads"); +const isTargetedIsolatedUnitFile = (fileFilter) => + unitBehaviorIsolatedFiles.includes(fileFilter) || + unitSingletonBatchFiles.includes(fileFilter) || + unitMemorySingletonFiles.includes(fileFilter); const inferTarget = (fileFilter) => { - const isolated = unitBehaviorIsolatedFiles.includes(fileFilter); + const isolated = isTargetedIsolatedUnitFile(fileFilter); + if (isUnitConfigTestFile(fileFilter)) { + return { owner: "unit", isolated }; + } if (fileFilter.endsWith(".live.test.ts")) { return { owner: "live", isolated }; } @@ -465,7 +481,7 @@ const unitFastEntries = unitFastBuckets.flatMap((files, index) => { "run", "--config", "vitest.unit.config.ts", - `--pool=${useVmForks ? "vmForks" : "forks"}`, + `--pool=${defaultUnitPool}`, ...(disableIsolation ? ["--isolate=false"] : []), ], })); @@ -638,7 +654,7 @@ const createTargetedEntry = (owner, isolated, filters) => { "run", "--config", "vitest.unit.config.ts", - `--pool=${forceForks ? "forks" : useVmForks ? "vmForks" : "forks"}`, + `--pool=${forceForks ? "forks" : defaultUnitPool}`, ...(disableIsolation ? ["--isolate=false"] : []), ...filters, ], diff --git a/test/fixtures/test-parallel.behavior.json b/test/fixtures/test-parallel.behavior.json index c2914c8c719..3e07c5c7597 100644 --- a/test/fixtures/test-parallel.behavior.json +++ b/test/fixtures/test-parallel.behavior.json @@ -12,214 +12,6 @@ { "file": "src/agents/pi-extensions/compaction-safeguard.test.ts", "reason": "Measured ~27% faster under threads than forks on base config after removing process.chdir() from the test." - }, - { - "file": "test/git-hooks-pre-commit.test.ts", - "reason": "Measured ~12% faster under threads than forks on base config while keeping the file green." - }, - { - "file": "test/web-search-provider-boundary.test.ts", - "reason": "Measured ~13% faster under threads than forks on base config while keeping the boundary checks green." - }, - { - "file": "test/scripts/test-runner-manifest.test.ts", - "reason": "Measured ~10% faster under threads than forks on base config while keeping the manifest routing checks green." - }, - { - "file": "test/architecture-smells.test.ts", - "reason": "Measured ~14% faster under threads than forks on base config while keeping the architecture guardrails green." - }, - { - "file": "test/scripts/test-extension.test.ts", - "reason": "Measured ~10% faster under threads than forks on base config while keeping the extension script checks green." - }, - { - "file": "test/vitest-unit-config.test.ts", - "reason": "Measured ~13% faster under threads than forks on base config while keeping the Vitest config checks green." - }, - { - "file": "test/release-check.test.ts", - "reason": "Measured ~11% faster under threads than forks on base config while keeping the release checks green." - }, - { - "file": "src/cli/secrets-cli.test.ts", - "reason": "Measured ~11% faster under threads than forks on base config while keeping secrets CLI coverage green." - }, - { - "file": "src/cli/plugin-registry.test.ts", - "reason": "Measured ~11% faster under threads than forks on base config while keeping plugin registry CLI coverage green." - }, - { - "file": "src/cli/route.test.ts", - "reason": "Measured ~12% faster under threads than forks on base config while keeping route CLI coverage green." - }, - { - "file": "src/cli/exec-approvals-cli.test.ts", - "reason": "Measured ~15% faster under threads than forks on base config while keeping approvals CLI coverage green." - }, - { - "file": "src/cli/browser-cli-manage.timeout-option.test.ts", - "reason": "Measured ~14% faster under threads than forks on base config while keeping browser manage timeout coverage green." - }, - { - "file": "src/cli/browser-cli-manage.test.ts", - "reason": "Measured ~14% faster under threads than forks on base config while keeping browser manage coverage green." - }, - { - "file": "src/cli/deps.test.ts", - "reason": "Measured ~81% faster under threads than forks on base config while keeping dependency checks green." - }, - { - "file": "src/cli/nodes-cli.coverage.test.ts", - "reason": "Measured ~12% faster under threads than forks on base config while keeping nodes CLI coverage green." - }, - { - "file": "src/cli/daemon-cli/restart-health.test.ts", - "reason": "Measured ~35% faster under threads than forks on base config while keeping daemon restart health coverage green." - }, - { - "file": "src/cli/program/config-guard.test.ts", - "reason": "Measured ~14% faster under threads than forks on base config while keeping program config guard coverage green." - }, - { - "file": "src/cli/update-cli/restart-helper.test.ts", - "reason": "Measured ~12% faster under threads than forks on base config while keeping update restart helper coverage green." - }, - { - "file": "src/cli/security-cli.test.ts", - "reason": "Measured ~31% faster under threads than forks on base config while keeping security CLI coverage green." - }, - { - "file": "src/cli/channel-options.test.ts", - "reason": "Measured ~13% faster under threads than forks on base config while keeping channel options coverage green." - }, - { - "file": "src/cli/log-level-option.test.ts", - "reason": "Measured ~11% faster under threads than forks on base config while keeping log level option coverage green." - }, - { - "file": "src/cli/plugin-install-plan.test.ts", - "reason": "Measured ~14% faster under threads than forks on base config while keeping plugin install planning coverage green." - }, - { - "file": "src/cli/memory-cli.test.ts", - "reason": "Measured ~50% faster under threads than forks on base config while keeping memory CLI coverage green." - }, - { - "file": "src/cli/program.nodes-test-helpers.test.ts", - "reason": "Measured ~22% faster under threads than forks on base config while keeping nodes test helper coverage green." - }, - { - "file": "src/cli/skills-cli.test.ts", - "reason": "Measured ~27% faster under threads than forks on base config while keeping skills CLI coverage green." - }, - { - "file": "src/cli/run-main.exit.test.ts", - "reason": "Measured ~12% faster under threads than forks on base config while keeping run-main exit coverage green." - }, - { - "file": "src/cli/completion-fish.test.ts", - "reason": "Measured ~41% faster under threads than forks on base config while keeping fish completion coverage green." - }, - { - "file": "src/cli/config-set-mode.test.ts", - "reason": "Measured ~11% faster under threads than forks on base config while keeping config set mode coverage green." - }, - { - "file": "src/cli/program/build-program.version-alias.test.ts", - "reason": "Measured ~13% faster under threads than forks on base config while keeping version alias coverage green." - }, - { - "file": "src/cli/program/register.agent.test.ts", - "reason": "Measured ~11% faster under threads than forks on base config while keeping register agent coverage green." - }, - { - "file": "test/scripts/test-parallel.test.ts", - "reason": "Measured ~44% faster under threads than forks on base config while keeping the parallel wrapper checks green." - }, - { - "file": "test/scripts/test-find-thread-candidates.test.ts", - "reason": "Measured ~19% faster under threads than forks on base config while keeping the thread candidate helper checks green." - }, - { - "file": "test/openclaw-npm-release-check.test.ts", - "reason": "Measured ~19% faster under threads than forks on base config while keeping the npm release checks green." - }, - { - "file": "src/cli/pairing-cli.test.ts", - "reason": "Measured ~12% faster under threads than forks on base config while keeping pairing CLI coverage green." - }, - { - "file": "src/cli/daemon-cli.coverage.test.ts", - "reason": "Measured ~11% faster under threads than forks on base config while keeping daemon CLI coverage green." - }, - { - "file": "src/cli/config-set-input.test.ts", - "reason": "Measured ~43% faster under threads than forks on base config while keeping config set input coverage green." - }, - { - "file": "src/cli/cron-cli.test.ts", - "reason": "Measured ~18% faster under threads than forks on base config while keeping cron CLI coverage green." - }, - { - "file": "src/cli/plugins-config.test.ts", - "reason": "Measured ~15% faster under threads than forks on base config while keeping plugin config coverage green." - }, - { - "file": "src/cli/program/register.configure.test.ts", - "reason": "Measured ~12% faster under threads than forks on base config while keeping register configure coverage green." - }, - { - "file": "src/cli/program/command-tree.test.ts", - "reason": "Measured ~10% faster under threads than forks on base config while keeping command tree coverage green." - }, - { - "file": "src/cli/program/register.subclis.test.ts", - "reason": "Measured ~11% faster under threads than forks on base config while keeping sub-CLI registration coverage green." - }, - { - "file": "test/vitest-unit-paths.test.ts", - "reason": "Measured ~11% faster under threads than forks on base config while keeping unit path checks green." - }, - { - "file": "test/scripts/check-channel-agnostic-boundaries.test.ts", - "reason": "Measured ~29% faster under threads than forks on base config while keeping channel-agnostic boundary checks green." - }, - { - "file": "test/plugin-npm-release.test.ts", - "reason": "Measured ~10% faster under threads than forks on base config while keeping npm release checks green." - }, - { - "file": "src/cli/daemon-cli-compat.test.ts", - "reason": "Measured ~12% faster under threads than forks on base config while keeping daemon CLI compatibility coverage green." - }, - { - "file": "src/cli/program/command-registry.test.ts", - "reason": "Measured ~10% faster under threads than forks on base config while keeping command registry coverage green." - }, - { - "file": "src/cli/program.force.test.ts", - "reason": "Measured ~14% faster under threads than forks on base config while keeping force option coverage green." - }, - { - "file": "src/cli/cli-utils.test.ts", - "reason": "Measured ~15% faster under threads than forks on base config while keeping CLI utility coverage green." - }, - { - "file": "src/cli/devices-cli.test.ts", - "reason": "Measured ~21% faster under threads than forks on base config while keeping devices CLI coverage green." - }, - { - "file": "src/cli/message-secret-scope.test.ts", - "reason": "Measured ~16% faster under threads than forks on base config while keeping message secret scope coverage green." - }, - { - "file": "src/cli/banner.test.ts", - "reason": "Measured ~30% faster under threads than forks on base config while keeping banner rendering coverage green." - }, - { - "file": "src/cli/gateway-cli.coverage.test.ts", - "reason": "Measured ~18% faster under threads than forks on base config while keeping gateway CLI coverage green." } ] }, @@ -257,7 +49,7 @@ }, { "file": "src/secrets/runtime.integration.test.ts", - "reason": "Secrets runtime activation/write-through integration coverage is CPU-heavy and safer outside the shared unit-fast lane." + "reason": "Secrets runtime activation/write-through integration coverage fails under a direct full-unit threads run and is safer on the fork singleton lane." }, { "file": "src/memory/index.test.ts", @@ -529,6 +321,214 @@ } ], "threadSingleton": [ + { + "file": "test/git-hooks-pre-commit.test.ts", + "reason": "Measured ~12% faster under threads than forks on base config while keeping the file green." + }, + { + "file": "test/web-search-provider-boundary.test.ts", + "reason": "Measured ~13% faster under threads than forks on base config while keeping the boundary checks green." + }, + { + "file": "test/scripts/test-runner-manifest.test.ts", + "reason": "Measured ~10% faster under threads than forks on base config while keeping the manifest routing checks green." + }, + { + "file": "test/architecture-smells.test.ts", + "reason": "Measured ~14% faster under threads than forks on base config while keeping the architecture guardrails green." + }, + { + "file": "test/scripts/test-extension.test.ts", + "reason": "Measured ~10% faster under threads than forks on base config while keeping the extension script checks green." + }, + { + "file": "test/vitest-unit-config.test.ts", + "reason": "Measured ~13% faster under threads than forks on base config while keeping the Vitest config checks green." + }, + { + "file": "test/release-check.test.ts", + "reason": "Measured ~11% faster under threads than forks on base config while keeping the release checks green." + }, + { + "file": "src/cli/secrets-cli.test.ts", + "reason": "Measured ~11% faster under threads than forks on base config while keeping secrets CLI coverage green." + }, + { + "file": "src/cli/plugin-registry.test.ts", + "reason": "Measured ~11% faster under threads than forks on base config while keeping plugin registry CLI coverage green." + }, + { + "file": "src/cli/route.test.ts", + "reason": "Measured ~12% faster under threads than forks on base config while keeping route CLI coverage green." + }, + { + "file": "src/cli/exec-approvals-cli.test.ts", + "reason": "Measured ~15% faster under threads than forks on base config while keeping approvals CLI coverage green." + }, + { + "file": "src/cli/browser-cli-manage.timeout-option.test.ts", + "reason": "Measured ~14% faster under threads than forks on base config while keeping browser manage timeout coverage green." + }, + { + "file": "src/cli/browser-cli-manage.test.ts", + "reason": "Measured ~14% faster under threads than forks on base config while keeping browser manage coverage green." + }, + { + "file": "src/cli/deps.test.ts", + "reason": "Measured ~81% faster under threads than forks on base config while keeping dependency checks green." + }, + { + "file": "src/cli/nodes-cli.coverage.test.ts", + "reason": "Measured ~12% faster under threads than forks on base config while keeping nodes CLI coverage green." + }, + { + "file": "src/cli/daemon-cli/restart-health.test.ts", + "reason": "Measured ~35% faster under threads than forks on base config while keeping daemon restart health coverage green." + }, + { + "file": "src/cli/program/config-guard.test.ts", + "reason": "Measured ~14% faster under threads than forks on base config while keeping program config guard coverage green." + }, + { + "file": "src/cli/update-cli/restart-helper.test.ts", + "reason": "Measured ~12% faster under threads than forks on base config while keeping update restart helper coverage green." + }, + { + "file": "src/cli/security-cli.test.ts", + "reason": "Measured ~31% faster under threads than forks on base config while keeping security CLI coverage green." + }, + { + "file": "src/cli/channel-options.test.ts", + "reason": "Measured ~13% faster under threads than forks on base config while keeping channel options coverage green." + }, + { + "file": "src/cli/log-level-option.test.ts", + "reason": "Measured ~11% faster under threads than forks on base config while keeping log level option coverage green." + }, + { + "file": "src/cli/plugin-install-plan.test.ts", + "reason": "Measured ~14% faster under threads than forks on base config while keeping plugin install planning coverage green." + }, + { + "file": "src/cli/memory-cli.test.ts", + "reason": "Measured ~50% faster under threads than forks on base config while keeping memory CLI coverage green." + }, + { + "file": "src/cli/program.nodes-test-helpers.test.ts", + "reason": "Measured ~22% faster under threads than forks on base config while keeping nodes test helper coverage green." + }, + { + "file": "src/cli/skills-cli.test.ts", + "reason": "Measured ~27% faster under threads than forks on base config while keeping skills CLI coverage green." + }, + { + "file": "src/cli/run-main.exit.test.ts", + "reason": "Measured ~12% faster under threads than forks on base config while keeping run-main exit coverage green." + }, + { + "file": "src/cli/completion-fish.test.ts", + "reason": "Measured ~41% faster under threads than forks on base config while keeping fish completion coverage green." + }, + { + "file": "src/cli/config-set-mode.test.ts", + "reason": "Measured ~11% faster under threads than forks on base config while keeping config set mode coverage green." + }, + { + "file": "src/cli/program/build-program.version-alias.test.ts", + "reason": "Measured ~13% faster under threads than forks on base config while keeping version alias coverage green." + }, + { + "file": "src/cli/program/register.agent.test.ts", + "reason": "Measured ~11% faster under threads than forks on base config while keeping register agent coverage green." + }, + { + "file": "test/scripts/test-parallel.test.ts", + "reason": "Measured ~44% faster under threads than forks on base config while keeping the parallel wrapper checks green." + }, + { + "file": "test/scripts/test-find-thread-candidates.test.ts", + "reason": "Measured ~19% faster under threads than forks on base config while keeping the thread candidate helper checks green." + }, + { + "file": "test/openclaw-npm-release-check.test.ts", + "reason": "Measured ~19% faster under threads than forks on base config while keeping the npm release checks green." + }, + { + "file": "src/cli/pairing-cli.test.ts", + "reason": "Measured ~12% faster under threads than forks on base config while keeping pairing CLI coverage green." + }, + { + "file": "src/cli/daemon-cli.coverage.test.ts", + "reason": "Measured ~11% faster under threads than forks on base config while keeping daemon CLI coverage green." + }, + { + "file": "src/cli/config-set-input.test.ts", + "reason": "Measured ~43% faster under threads than forks on base config while keeping config set input coverage green." + }, + { + "file": "src/cli/cron-cli.test.ts", + "reason": "Measured ~18% faster under threads than forks on base config while keeping cron CLI coverage green." + }, + { + "file": "src/cli/plugins-config.test.ts", + "reason": "Measured ~15% faster under threads than forks on base config while keeping plugin config coverage green." + }, + { + "file": "src/cli/program/register.configure.test.ts", + "reason": "Measured ~12% faster under threads than forks on base config while keeping register configure coverage green." + }, + { + "file": "src/cli/program/command-tree.test.ts", + "reason": "Measured ~10% faster under threads than forks on base config while keeping command tree coverage green." + }, + { + "file": "src/cli/program/register.subclis.test.ts", + "reason": "Measured ~11% faster under threads than forks on base config while keeping sub-CLI registration coverage green." + }, + { + "file": "test/vitest-unit-paths.test.ts", + "reason": "Measured ~11% faster under threads than forks on base config while keeping unit path checks green." + }, + { + "file": "test/scripts/check-channel-agnostic-boundaries.test.ts", + "reason": "Measured ~29% faster under threads than forks on base config while keeping channel-agnostic boundary checks green." + }, + { + "file": "test/plugin-npm-release.test.ts", + "reason": "Measured ~10% faster under threads than forks on base config while keeping npm release checks green." + }, + { + "file": "src/cli/daemon-cli-compat.test.ts", + "reason": "Measured ~12% faster under threads than forks on base config while keeping daemon CLI compatibility coverage green." + }, + { + "file": "src/cli/program/command-registry.test.ts", + "reason": "Measured ~10% faster under threads than forks on base config while keeping command registry coverage green." + }, + { + "file": "src/cli/program.force.test.ts", + "reason": "Measured ~14% faster under threads than forks on base config while keeping force option coverage green." + }, + { + "file": "src/cli/cli-utils.test.ts", + "reason": "Measured ~15% faster under threads than forks on base config while keeping CLI utility coverage green." + }, + { + "file": "src/cli/devices-cli.test.ts", + "reason": "Measured ~21% faster under threads than forks on base config while keeping devices CLI coverage green." + }, + { + "file": "src/cli/message-secret-scope.test.ts", + "reason": "Measured ~16% faster under threads than forks on base config while keeping message secret scope coverage green." + }, + { + "file": "src/cli/banner.test.ts", + "reason": "Measured ~30% faster under threads than forks on base config while keeping banner rendering coverage green." + }, + { + "file": "src/cli/gateway-cli.coverage.test.ts", + "reason": "Measured ~18% faster under threads than forks on base config while keeping gateway CLI coverage green." + }, { "file": "src/channels/plugins/actions/actions.test.ts", "reason": "Terminates cleanly under threads, but not process forks on this host."