fix(test): keep local Vitest checks serialized

This commit is contained in:
Vincent Koc
2026-04-25 03:05:28 -07:00
parent 5e0cca5e24
commit 814409a3b3
8 changed files with 144 additions and 72 deletions

View File

@@ -1657,7 +1657,7 @@
"@types/markdown-it": "^14.1.2",
"@types/node": "25.6.0",
"@types/ws": "^8.18.1",
"@typescript/native-preview": "7.0.0-dev.20260423.1",
"@typescript/native-preview": "7.0.0-dev.20260421.2",
"@vitest/coverage-v8": "^4.1.5",
"jscpd": "4.0.9",
"jsdom": "^29.0.2",

84
pnpm-lock.yaml generated
View File

@@ -176,8 +176,8 @@ importers:
specifier: ^8.18.1
version: 8.18.1
'@typescript/native-preview':
specifier: 7.0.0-dev.20260423.1
version: 7.0.0-dev.20260423.1
specifier: 7.0.0-dev.20260421.2
version: 7.0.0-dev.20260421.2
'@vitest/coverage-v8':
specifier: ^4.1.5
version: 4.1.5(@vitest/browser@4.1.5)(vitest@4.1.5)
@@ -204,7 +204,7 @@ importers:
version: 0.21.1(signal-polyfill@0.2.2)
tsdown:
specifier: 0.21.9
version: 0.21.9(@typescript/native-preview@7.0.0-dev.20260423.1)(typescript@6.0.3)
version: 0.21.9(@typescript/native-preview@7.0.0-dev.20260421.2)(typescript@6.0.3)
tsx:
specifier: ^4.21.0
version: 4.21.0
@@ -4081,51 +4081,43 @@ packages:
'@types/yauzl@2.10.3':
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
'@typescript/native-preview-darwin-arm64@7.0.0-dev.20260423.1':
resolution: {integrity: sha512-wbLr6o5fROaCYt6cOpFhbe92FJAOdhAHwm/s8I/IyN5HbL1ULgel/wHaZiR+ws+27rgruNUiCENzTUg9vSz2bA==}
engines: {node: '>=16.20.0'}
'@typescript/native-preview-darwin-arm64@7.0.0-dev.20260421.2':
resolution: {integrity: sha512-fHv1r3ZmVo6zxuAIFmuX3w9QxbcauoG0SsWhmDwm6VmRubLlOJIcmTtlmV3JAb9oOnq8LuzZljzT7Q39fSMQDw==}
cpu: [arm64]
os: [darwin]
'@typescript/native-preview-darwin-x64@7.0.0-dev.20260423.1':
resolution: {integrity: sha512-13MpNT+4MgkgrfiW2u03rnER5aB3yz9fA0bWEYh6IH3rIqA2AR3Dntp3QXW4sQrZf0SriXqHe2R7X3HCT5xmqA==}
engines: {node: '>=16.20.0'}
'@typescript/native-preview-darwin-x64@7.0.0-dev.20260421.2':
resolution: {integrity: sha512-KWTR6xbW9t+JS7D5DQIzo75pqVXVWUxF9PMv/+S6xsnOjCVd6g0ixHcFpFMJMKSUQpGPr8Z5f7b8ks6LHW01jg==}
cpu: [x64]
os: [darwin]
'@typescript/native-preview-linux-arm64@7.0.0-dev.20260423.1':
resolution: {integrity: sha512-ICIkJDTqmn0R4Vs811+Ht2RYTk1OCrAhHCu0JthmhR216T1Tqgi5DWRoCprp3RL1qU6fLnxxrIpEbNlNN7XFYA==}
engines: {node: '>=16.20.0'}
'@typescript/native-preview-linux-arm64@7.0.0-dev.20260421.2':
resolution: {integrity: sha512-VLMEuml3BhUb+jaL0TXQ4xvVODxJF+RhkI+tBWvlynsJI4khTXEiwWh+wPOJrsfBRYFRMXEu28Odl/HXkYze8w==}
cpu: [arm64]
os: [linux]
'@typescript/native-preview-linux-arm@7.0.0-dev.20260423.1':
resolution: {integrity: sha512-CxUA15qbPQRvz2nanBpiv1h4tgXTCJJwqOtgKMSdIuPkow8dyYW3ba5oLoH/jZhS4792XislX659hlFrfiU6CQ==}
engines: {node: '>=16.20.0'}
'@typescript/native-preview-linux-arm@7.0.0-dev.20260421.2':
resolution: {integrity: sha512-BWLQO3nemLDSV5PoE5GPHe1dU9Dth77Kv8/cle9Ujcp4LhPo0KincdPqFH/qKeU/xvW25mgFueflZ1nc4rKuww==}
cpu: [arm]
os: [linux]
'@typescript/native-preview-linux-x64@7.0.0-dev.20260423.1':
resolution: {integrity: sha512-cWLFS4R8dOU1YuUJ/2VLeGMVIjgI3GGb/f9rRY5MbWHq5l3NNZh8y1QwAOrTh3+g3q6+znArfxVnD2hZHUz8Mw==}
engines: {node: '>=16.20.0'}
'@typescript/native-preview-linux-x64@7.0.0-dev.20260421.2':
resolution: {integrity: sha512-qUrJWTB5/wv4wnRG0TRXElAxc2kykNiRNyEIEqBbLmzDlrcvAW7RRy8MXoY1ZyTiKGMu14itZ3x9oW6+blFpRw==}
cpu: [x64]
os: [linux]
'@typescript/native-preview-win32-arm64@7.0.0-dev.20260423.1':
resolution: {integrity: sha512-OWaGUI4+dHqYZv+k6sITx9Y27FNy3XzNFk4OrOiYtBkIO/xrb9TPMP4A5XI4n5zwRLIv3xne9g039xgRbaeyoQ==}
engines: {node: '>=16.20.0'}
'@typescript/native-preview-win32-arm64@7.0.0-dev.20260421.2':
resolution: {integrity: sha512-Rc6NsWlZmCs5YUKVzKgwoBOoRUGsPzct4BDMRX0csD1devLBBc4AbUXWKsJRbpwIAnqMO1ld4sNHEb+wXgfNHQ==}
cpu: [arm64]
os: [win32]
'@typescript/native-preview-win32-x64@7.0.0-dev.20260423.1':
resolution: {integrity: sha512-5MQjO/qdLwXpjW7Dy/1lNv7Vtpvo6bhCkbjan4PoRN5/eeyqEqDWxdf8AGE4btLmHqyIjEHRuYf7kp2tlAr6lQ==}
engines: {node: '>=16.20.0'}
'@typescript/native-preview-win32-x64@7.0.0-dev.20260421.2':
resolution: {integrity: sha512-GQv1+dya1t6EqF2Cpsb+xoozovdX10JUSf6Kl/8xNkTapzmlHd+uMr+8ku3jIASTxoRGn0Mklgjj3MDKrOTuLg==}
cpu: [x64]
os: [win32]
'@typescript/native-preview@7.0.0-dev.20260423.1':
resolution: {integrity: sha512-9WD7TJJlGvt9PQqJI/+44dVP4oqGQFIkYrpXt7nlQ0WgNIErN52x/XhxmJ4nWft06qejgPiUbPo4aYRNOmIHXg==}
engines: {node: '>=16.20.0'}
'@typescript/native-preview@7.0.0-dev.20260421.2':
resolution: {integrity: sha512-CmajHI25HpVWE9R1XFoxr+cphJPxoYD3eFioQtAvXYkMFKnLdICMS9pXre9Pybizb75ejRxjKD5/CVG055rEIg==}
hasBin: true
'@typespec/ts-http-runtime@0.3.5':
@@ -10777,36 +10769,36 @@ snapshots:
'@types/node': 25.6.0
optional: true
'@typescript/native-preview-darwin-arm64@7.0.0-dev.20260423.1':
'@typescript/native-preview-darwin-arm64@7.0.0-dev.20260421.2':
optional: true
'@typescript/native-preview-darwin-x64@7.0.0-dev.20260423.1':
'@typescript/native-preview-darwin-x64@7.0.0-dev.20260421.2':
optional: true
'@typescript/native-preview-linux-arm64@7.0.0-dev.20260423.1':
'@typescript/native-preview-linux-arm64@7.0.0-dev.20260421.2':
optional: true
'@typescript/native-preview-linux-arm@7.0.0-dev.20260423.1':
'@typescript/native-preview-linux-arm@7.0.0-dev.20260421.2':
optional: true
'@typescript/native-preview-linux-x64@7.0.0-dev.20260423.1':
'@typescript/native-preview-linux-x64@7.0.0-dev.20260421.2':
optional: true
'@typescript/native-preview-win32-arm64@7.0.0-dev.20260423.1':
'@typescript/native-preview-win32-arm64@7.0.0-dev.20260421.2':
optional: true
'@typescript/native-preview-win32-x64@7.0.0-dev.20260423.1':
'@typescript/native-preview-win32-x64@7.0.0-dev.20260421.2':
optional: true
'@typescript/native-preview@7.0.0-dev.20260423.1':
'@typescript/native-preview@7.0.0-dev.20260421.2':
optionalDependencies:
'@typescript/native-preview-darwin-arm64': 7.0.0-dev.20260423.1
'@typescript/native-preview-darwin-x64': 7.0.0-dev.20260423.1
'@typescript/native-preview-linux-arm': 7.0.0-dev.20260423.1
'@typescript/native-preview-linux-arm64': 7.0.0-dev.20260423.1
'@typescript/native-preview-linux-x64': 7.0.0-dev.20260423.1
'@typescript/native-preview-win32-arm64': 7.0.0-dev.20260423.1
'@typescript/native-preview-win32-x64': 7.0.0-dev.20260423.1
'@typescript/native-preview-darwin-arm64': 7.0.0-dev.20260421.2
'@typescript/native-preview-darwin-x64': 7.0.0-dev.20260421.2
'@typescript/native-preview-linux-arm': 7.0.0-dev.20260421.2
'@typescript/native-preview-linux-arm64': 7.0.0-dev.20260421.2
'@typescript/native-preview-linux-x64': 7.0.0-dev.20260421.2
'@typescript/native-preview-win32-arm64': 7.0.0-dev.20260421.2
'@typescript/native-preview-win32-x64': 7.0.0-dev.20260421.2
'@typespec/ts-http-runtime@0.3.5':
dependencies:
@@ -13850,7 +13842,7 @@ snapshots:
glob: 7.2.3
optional: true
rolldown-plugin-dts@0.23.2(@typescript/native-preview@7.0.0-dev.20260423.1)(rolldown@1.0.0-rc.16)(typescript@6.0.3):
rolldown-plugin-dts@0.23.2(@typescript/native-preview@7.0.0-dev.20260421.2)(rolldown@1.0.0-rc.16)(typescript@6.0.3):
dependencies:
'@babel/generator': 8.0.0-rc.3
'@babel/helper-validator-identifier': 8.0.0-rc.3
@@ -13864,7 +13856,7 @@ snapshots:
picomatch: 4.0.4
rolldown: 1.0.0-rc.16
optionalDependencies:
'@typescript/native-preview': 7.0.0-dev.20260423.1
'@typescript/native-preview': 7.0.0-dev.20260421.2
typescript: 6.0.3
transitivePeerDependencies:
- oxc-resolver
@@ -14349,7 +14341,7 @@ snapshots:
ts-algebra@2.0.0: {}
tsdown@0.21.9(@typescript/native-preview@7.0.0-dev.20260423.1)(typescript@6.0.3):
tsdown@0.21.9(@typescript/native-preview@7.0.0-dev.20260421.2)(typescript@6.0.3):
dependencies:
ansis: 4.2.0
cac: 7.0.0
@@ -14360,7 +14352,7 @@ snapshots:
obug: 2.1.1
picomatch: 4.0.4
rolldown: 1.0.0-rc.16
rolldown-plugin-dts: 0.23.2(@typescript/native-preview@7.0.0-dev.20260423.1)(rolldown@1.0.0-rc.16)(typescript@6.0.3)
rolldown-plugin-dts: 0.23.2(@typescript/native-preview@7.0.0-dev.20260421.2)(rolldown@1.0.0-rc.16)(typescript@6.0.3)
semver: 7.7.4
tinyexec: 1.1.1
tinyglobby: 0.2.16

View File

@@ -16,6 +16,9 @@ export const DEFAULT_LOCAL_FULL_SUITE_VITEST_WORKERS: number;
export const LARGE_LOCAL_FULL_SUITE_VITEST_WORKERS: number;
export function isCiLikeEnv(env?: Record<string, string | undefined>): boolean;
export function resolveLocalVitestEnv(
env?: Record<string, string | undefined>,
): Record<string, string | undefined>;
export function detectVitestHostInfo(): Required<VitestHostInfo>;
export function resolveLocalVitestMaxWorkers(
env?: Record<string, string | undefined>,

View File

@@ -24,6 +24,18 @@ export function isCiLikeEnv(env = process.env) {
return env.CI === "true" || env.GITHUB_ACTIONS === "true";
}
export function resolveLocalVitestEnv(env = process.env) {
const normalizedLocalCheck = env.OPENCLAW_LOCAL_CHECK?.trim().toLowerCase();
if (isCiLikeEnv(env) || (normalizedLocalCheck !== "0" && normalizedLocalCheck !== "false")) {
return env;
}
return {
...env,
OPENCLAW_LOCAL_CHECK: "1",
};
}
export function detectVitestHostInfo() {
return {
cpuCount:

View File

@@ -1,6 +1,7 @@
import { spawn } from "node:child_process";
import { createRequire } from "node:module";
import path from "node:path";
import { resolveLocalVitestEnv } from "./lib/vitest-local-scheduling.mjs";
import { spawnPnpmRunner } from "./pnpm-runner.mjs";
import {
forwardSignalToVitestProcessGroup,
@@ -47,15 +48,16 @@ export function resolveVitestSpawnParams(env = process.env, platform = process.p
}
export function resolveVitestSpawnEnv(env = process.env) {
if (!shouldApplyNativeWorkerBudget(env)) {
return env;
const nextEnv = resolveLocalVitestEnv(env);
if (!shouldApplyNativeWorkerBudget(nextEnv)) {
return nextEnv;
}
const nativeWorkerCount = String(resolveNativeWorkerCount(env));
const nativeWorkerCount = String(resolveNativeWorkerCount(nextEnv));
return {
...env,
RAYON_NUM_THREADS: env.RAYON_NUM_THREADS?.trim() || nativeWorkerCount,
TOKIO_WORKER_THREADS: env.TOKIO_WORKER_THREADS?.trim() || nativeWorkerCount,
...nextEnv,
RAYON_NUM_THREADS: nextEnv.RAYON_NUM_THREADS?.trim() || nativeWorkerCount,
TOKIO_WORKER_THREADS: nextEnv.TOKIO_WORKER_THREADS?.trim() || nativeWorkerCount,
};
}

View File

@@ -2,7 +2,11 @@ import fs from "node:fs";
import path from "node:path";
import { performance } from "node:perf_hooks";
import { acquireLocalHeavyCheckLockSync } from "./lib/local-heavy-check-runtime.mjs";
import { isCiLikeEnv, resolveLocalFullSuiteProfile } from "./lib/vitest-local-scheduling.mjs";
import {
isCiLikeEnv,
resolveLocalFullSuiteProfile,
resolveLocalVitestEnv,
} from "./lib/vitest-local-scheduling.mjs";
import {
resolveVitestCliEntry,
resolveVitestNodeArgs,
@@ -341,6 +345,7 @@ async function runVitestSpecsParallel(specs, concurrency) {
async function main() {
const args = process.argv.slice(2);
const baseEnv = resolveLocalVitestEnv(process.env);
const { targetArgs } = parseTestProjectsArgs(args, process.cwd());
const changedTargetArgs =
targetArgs.length === 0 ? resolveChangedTargetArgs(args, process.cwd()) : null;
@@ -349,7 +354,7 @@ async function main() {
? buildFullSuiteVitestRunPlans(args, process.cwd()).map((plan) => ({
config: plan.config,
continueOnFailure: true,
env: process.env,
env: baseEnv,
includeFilePath: null,
includePatterns: null,
pnpmArgs: [
@@ -365,12 +370,12 @@ async function main() {
watchMode: plan.watchMode,
}))
: createVitestRunSpecs(args, {
baseEnv: process.env,
baseEnv,
cwd: process.cwd(),
});
const runSpecs = applyDefaultMultiSpecVitestCachePaths(
applyDefaultVitestNoOutputTimeout(rawRunSpecs, { env: process.env }),
{ cwd: process.cwd(), env: process.env },
applyDefaultVitestNoOutputTimeout(rawRunSpecs, { env: baseEnv }),
{ cwd: process.cwd(), env: baseEnv },
);
if (runSpecs.length === 0) {
@@ -378,10 +383,10 @@ async function main() {
return;
}
releaseLock = shouldAcquireLocalHeavyCheckLock(runSpecs, process.env)
releaseLock = shouldAcquireLocalHeavyCheckLock(runSpecs, baseEnv)
? acquireLocalHeavyCheckLockSync({
cwd: process.cwd(),
env: process.env,
env: baseEnv,
toolName: "test",
})
: () => {};
@@ -391,28 +396,28 @@ async function main() {
changedTargetArgs === null &&
!runSpecs.some((spec) => spec.watchMode);
const isExplicitParallelMultiConfigRun =
Boolean(process.env.OPENCLAW_TEST_PROJECTS_PARALLEL) &&
Boolean(baseEnv.OPENCLAW_TEST_PROJECTS_PARALLEL) &&
runSpecs.length > 1 &&
!runSpecs.some((spec) => spec.watchMode);
const isParallelShardRun =
isFullSuiteRun || isFullExtensionsProjectRun(runSpecs) || isExplicitParallelMultiConfigRun;
if (isParallelShardRun) {
const concurrency = resolveParallelFullSuiteConcurrency(runSpecs.length, process.env);
const concurrency = resolveParallelFullSuiteConcurrency(runSpecs.length, baseEnv);
if (concurrency > 1) {
const localFullSuiteProfile = resolveLocalFullSuiteProfile(process.env);
const shardTimings = readShardTimings(process.cwd(), process.env);
const localFullSuiteProfile = resolveLocalFullSuiteProfile(baseEnv);
const shardTimings = readShardTimings(process.cwd(), baseEnv);
const parallelSpecs = applyDefaultParallelVitestWorkerBudget(
applyParallelVitestCachePaths(orderFullSuiteSpecsForParallelRun(runSpecs, shardTimings), {
cwd: process.cwd(),
env: process.env,
env: baseEnv,
}),
process.env,
baseEnv,
);
if (
!isCiLikeEnv(process.env) &&
!process.env.OPENCLAW_TEST_PROJECTS_PARALLEL &&
!process.env.OPENCLAW_VITEST_MAX_WORKERS &&
!process.env.OPENCLAW_TEST_WORKERS &&
!isCiLikeEnv(baseEnv) &&
!baseEnv.OPENCLAW_TEST_PROJECTS_PARALLEL &&
!baseEnv.OPENCLAW_VITEST_MAX_WORKERS &&
!baseEnv.OPENCLAW_TEST_WORKERS &&
localFullSuiteProfile.shardParallelism === 10 &&
localFullSuiteProfile.vitestMaxWorkers === 2
) {
@@ -425,7 +430,7 @@ async function main() {
parallelSpecs,
concurrency,
);
writeShardTimings(timings, process.cwd(), process.env);
writeShardTimings(timings, process.cwd(), baseEnv);
console.error(
`[test] completed ${parallelSpecs.length} Vitest shards; Vitest summaries above are per-shard, not aggregate totals.`,
);
@@ -455,7 +460,7 @@ async function main() {
timings.push(result.timing);
}
}
writeShardTimings(timings, process.cwd(), process.env);
writeShardTimings(timings, process.cwd(), baseEnv);
releaseLockOnce();
if (exitCode !== 0) {

View File

@@ -58,6 +58,38 @@ describe("scripts/run-vitest", () => {
});
});
it("reenables local check policy for local Vitest children", () => {
expect(
resolveVitestSpawnParams(
{
OPENCLAW_LOCAL_CHECK: "0",
PATH: "/usr/bin",
},
"darwin",
).env,
).toMatchObject({
OPENCLAW_LOCAL_CHECK: "1",
PATH: "/usr/bin",
});
});
it("preserves explicit local-check disablement in CI", () => {
expect(
resolveVitestSpawnParams(
{
CI: "true",
OPENCLAW_LOCAL_CHECK: "0",
PATH: "/usr/bin",
},
"linux",
).env,
).toMatchObject({
CI: "true",
OPENCLAW_LOCAL_CHECK: "0",
PATH: "/usr/bin",
});
});
it("caps native Rust worker pools for serial Vitest runs", () => {
expect(
resolveVitestSpawnParams(

View File

@@ -1,11 +1,37 @@
import { describe, expect, it } from "vitest";
import {
resolveLocalVitestEnv,
resolveLocalFullSuiteProfile,
resolveLocalVitestScheduling,
shouldUseLargeLocalFullSuiteProfile,
} from "../../scripts/lib/vitest-local-scheduling.mjs";
describe("vitest local full-suite profile", () => {
it("forces local Vitest runs back onto local-check policy", () => {
expect(resolveLocalVitestEnv({ OPENCLAW_LOCAL_CHECK: "0", PATH: "/usr/bin" })).toEqual({
OPENCLAW_LOCAL_CHECK: "1",
PATH: "/usr/bin",
});
expect(resolveLocalVitestEnv({ OPENCLAW_LOCAL_CHECK: "false", PATH: "/usr/bin" })).toEqual({
OPENCLAW_LOCAL_CHECK: "1",
PATH: "/usr/bin",
});
});
it("keeps local-check disablement for CI Vitest runs", () => {
expect(
resolveLocalVitestEnv({
CI: "true",
OPENCLAW_LOCAL_CHECK: "0",
PATH: "/usr/bin",
}),
).toEqual({
CI: "true",
OPENCLAW_LOCAL_CHECK: "0",
PATH: "/usr/bin",
});
});
it("selects the large local profile on roomy hosts that are not throttled", () => {
const env = {};
const hostInfo = {