test: restore thread-first vitest defaults

This commit is contained in:
Peter Steinberger 2026-04-05 11:29:01 +01:00
parent dbac5fa258
commit 41d08a6feb
No known key found for this signature in database
12 changed files with 57 additions and 45 deletions

View File

@ -28,7 +28,7 @@ describe("resolveLocalVitestMaxWorkers", () => {
"threads",
idleVitestStats,
),
).toBe(2);
).toBe(6);
});
it("lets OPENCLAW_VITEST_MAX_WORKERS override the inferred cap", () => {
@ -77,7 +77,7 @@ describe("resolveLocalVitestMaxWorkers", () => {
"threads",
idleVitestStats,
),
).toBe(1);
).toBe(2);
});
it("lets roomy hosts use more local parallelism", () => {
@ -92,7 +92,7 @@ describe("resolveLocalVitestMaxWorkers", () => {
"threads",
idleVitestStats,
),
).toBe(3);
).toBe(8);
});
it("backs off further when the host is already busy", () => {
@ -122,7 +122,7 @@ describe("resolveLocalVitestMaxWorkers", () => {
"threads",
idleVitestStats,
),
).toBe(3);
).toBe(12);
});
});
@ -188,7 +188,7 @@ describe("resolveLocalVitestScheduling", () => {
idleVitestStats,
),
).toEqual({
maxWorkers: 3,
maxWorkers: 8,
fileParallelism: true,
throttledBySystem: false,
});

View File

@ -12,7 +12,7 @@ describe("loadBoundaryIncludePatternsFromEnv", () => {
});
describe("boundary vitest config", () => {
it("keeps boundary suites on the shared runner with shared test bootstrap", () => {
it("keeps boundary suites on the non-isolated runner with shared test bootstrap", () => {
const config = createBoundaryVitestConfig({});
expect(config.test?.isolate).toBe(false);

View File

@ -13,14 +13,14 @@ describe("projects vitest config", () => {
expect(baseConfig.test?.projects).toEqual([...rootVitestProjects]);
});
it("keeps the heavy root projects on fork workers only where explicitly required", () => {
expect(createGatewayVitestConfig().test.pool).toBe("forks");
expect(createAgentsVitestConfig().test.pool).toBe("forks");
expect(createCommandsVitestConfig().test.pool).toBe("forks");
it("keeps root projects on the shared thread-first pool by default", () => {
expect(createGatewayVitestConfig().test.pool).toBe("threads");
expect(createAgentsVitestConfig().test.pool).toBe("threads");
expect(createCommandsVitestConfig().test.pool).toBe("threads");
expect(createContractsVitestConfig().test.pool).toBe("threads");
});
it("keeps the contracts lane on the shared non-isolated runner", () => {
it("keeps the contracts lane on the non-isolated runner by default", () => {
const config = createContractsVitestConfig();
expect(config.test.isolate).toBe(false);
expect(config.test.runner).toBe("./test/non-isolated-runner.ts");
@ -36,13 +36,13 @@ describe("projects vitest config", () => {
expect(config.test.deps?.optimizer?.web?.enabled).toBe(true);
});
it("keeps the unit lane on the shared non-isolated runner", () => {
it("keeps the unit lane on the non-isolated runner by default", () => {
const config = createUnitVitestConfig();
expect(config.test.isolate).toBe(false);
expect(config.test.runner).toBe("./test/non-isolated-runner.ts");
});
it("keeps the bundled lane on the shared non-isolated runner", () => {
it("keeps the bundled lane on thread workers with the non-isolated runner", () => {
expect(bundledConfig.test?.pool).toBe("threads");
expect(bundledConfig.test?.isolate).toBe(false);
expect(bundledConfig.test?.runner).toBe("./test/non-isolated-runner.ts");

View File

@ -51,7 +51,7 @@ import { BUNDLED_PLUGIN_TEST_GLOB, bundledPluginFile } from "./helpers/bundled-p
const EXTENSIONS_CHANNEL_GLOB = ["extensions", "channel", "**"].join("/");
describe("resolveVitestIsolation", () => {
it("defaults shared scoped configs to non-isolated workers", () => {
it("defaults shared scoped configs to the non-isolated runner", () => {
expect(resolveVitestIsolation({})).toBe(false);
});
@ -63,7 +63,7 @@ describe("resolveVitestIsolation", () => {
});
describe("createScopedVitestConfig", () => {
it("applies non-isolated mode by default", () => {
it("applies the non-isolated runner by default", () => {
const config = createScopedVitestConfig(["src/example.test.ts"], { env: {} });
expect(config.test?.isolate).toBe(false);
expect(config.test?.runner).toBe("./test/non-isolated-runner.ts");
@ -160,7 +160,7 @@ describe("scoped vitest configs", () => {
const defaultUtilsConfig = createUtilsVitestConfig({});
const defaultWizardConfig = createWizardVitestConfig({});
it("keeps most scoped lanes on thread workers with the non-isolated runner", () => {
it("keeps scoped lanes on threads with the shared non-isolated runner", () => {
for (const config of [
defaultChannelsConfig,
defaultAcpConfig,
@ -176,19 +176,18 @@ describe("scoped vitest configs", () => {
expect(config.test?.isolate).toBe(false);
expect(config.test?.runner).toBe("./test/non-isolated-runner.ts");
}
});
it("keeps gateway, commands, and agents on fork workers", () => {
for (const config of [defaultGatewayConfig, defaultCommandsConfig, defaultAgentsConfig]) {
expect(config.test?.pool).toBe("forks");
expect(config.test?.pool).toBe("threads");
expect(config.test?.isolate).toBe(false);
expect(config.test?.runner).toBe("./test/non-isolated-runner.ts");
}
});
it("defaults channel tests to non-isolated thread mode", () => {
it("defaults channel tests to threads with the non-isolated runner", () => {
expect(defaultChannelsConfig.test?.isolate).toBe(false);
expect(defaultChannelsConfig.test?.pool).toBe("threads");
expect(defaultChannelsConfig.test?.runner).toBe("./test/non-isolated-runner.ts");
});
it("keeps the core channel lane limited to non-extension roots", () => {
@ -222,9 +221,10 @@ describe("scoped vitest configs", () => {
}
});
it("defaults extension tests to non-isolated thread mode", () => {
it("defaults extension tests to threads with the non-isolated runner", () => {
expect(defaultExtensionsConfig.test?.isolate).toBe(false);
expect(defaultExtensionsConfig.test?.pool).toBe("threads");
expect(defaultExtensionsConfig.test?.runner).toBe("./test/non-isolated-runner.ts");
});
it("normalizes extension channel include patterns relative to the scoped dir", () => {

View File

@ -3,20 +3,20 @@ import uiConfig from "../ui/vitest.config.ts";
import uiNodeConfig from "../ui/vitest.node.config.ts";
describe("ui package vitest config", () => {
it("keeps the standalone ui package on forks with isolation enabled", () => {
expect(uiConfig.test?.pool).toBe("forks");
it("keeps the standalone ui package on thread workers with isolation enabled", () => {
expect(uiConfig.test?.pool).toBe("threads");
expect(uiConfig.test?.isolate).toBe(true);
expect(uiConfig.test?.projects).toHaveLength(3);
for (const project of uiConfig.test?.projects ?? []) {
expect(project.test?.pool).toBe("forks");
expect(project.test?.pool).toBe("threads");
expect(project.test?.isolate).toBe(true);
expect(project.test?.runner).toBeUndefined();
}
});
it("keeps the standalone ui node config on forks with isolation enabled", () => {
expect(uiNodeConfig.test?.pool).toBe("forks");
it("keeps the standalone ui node config on thread workers with isolation enabled", () => {
expect(uiNodeConfig.test?.pool).toBe("threads");
expect(uiNodeConfig.test?.isolate).toBe(true);
expect(uiNodeConfig.test?.runner).toBeUndefined();
});

View File

@ -68,7 +68,7 @@ describe("loadExtraExcludePatternsFromEnv", () => {
});
describe("unit vitest config", () => {
it("defaults unit tests to non-isolated mode", () => {
it("defaults unit tests to the non-isolated runner", () => {
const unitConfig = createUnitVitestConfig({});
expect(unitConfig.test?.isolate).toBe(false);
expect(unitConfig.test?.runner).toBe("./test/non-isolated-runner.ts");

View File

@ -1,5 +1,6 @@
import { defineProject } from "vitest/config";
import { loadPatternListFromEnv, narrowIncludePatternsForCli } from "./vitest.pattern-file.ts";
import { resolveVitestIsolation } from "./vitest.scoped-config.ts";
import { sharedVitestConfig } from "./vitest.shared.config.ts";
import { boundaryTestFiles } from "./vitest.unit-paths.mjs";
@ -14,13 +15,14 @@ export function createBoundaryVitestConfig(
argv: string[] = process.argv,
) {
const cliIncludePatterns = narrowIncludePatternsForCli(boundaryTestFiles, argv);
const isolate = resolveVitestIsolation(env);
return defineProject({
...sharedVitestConfig,
test: {
...sharedVitestConfig.test,
name: "boundary",
isolate: false,
runner: "./test/non-isolated-runner.ts",
isolate,
...(isolate ? { runner: undefined } : { runner: "./test/non-isolated-runner.ts" }),
include: loadBoundaryIncludePatternsFromEnv(env) ?? cliIncludePatterns ?? boundaryTestFiles,
...(cliIncludePatterns !== null ? { passWithNoTests: true } : {}),
// Boundary workers still need the shared isolated HOME/bootstrap. Only

View File

@ -62,6 +62,7 @@ export default defineConfig({
...sharedVitestConfig,
test: {
...sharedVitestConfig.test,
runner: "./test/non-isolated-runner.ts",
projects: [...rootVitestProjects],
},
});

View File

@ -9,6 +9,8 @@ export function createContractsVitestConfig() {
...base,
test: {
...baseTest,
isolate: false,
runner: "./test/non-isolated-runner.ts",
setupFiles: baseTest.setupFiles ?? [],
include: [
"src/channels/plugins/contracts/**/*.test.ts",

View File

@ -69,6 +69,7 @@ export function createScopedVitestConfig(
]),
];
const useNonIsolatedRunner = options?.useNonIsolatedRunner ?? !isolate;
const runner = useNonIsolatedRunner ? "./test/non-isolated-runner.ts" : undefined;
return defineConfig({
...base,
@ -78,7 +79,7 @@ export function createScopedVitestConfig(
...(options?.name ? { name: options.name } : {}),
...(options?.environment ? { environment: options.environment } : {}),
isolate,
...(useNonIsolatedRunner ? { runner: "./test/non-isolated-runner.ts" } : {}),
...(runner ? { runner } : { runner: undefined }),
setupFiles,
...(scopedDir ? { dir: scopedDir } : {}),
include: relativizeScopedPatterns(cliInclude ?? include, scopedDir),

View File

@ -86,36 +86,42 @@ export function resolveLocalVitestScheduling(
const loadAverage1m = Math.max(0, system.loadAverage1m ?? 0);
const totalMemoryGb = (system.totalMemoryBytes ?? 0) / 1024 ** 3;
// Keep smaller hosts conservative, but let large local boxes actually use
// their cores. Thread workers scale much better than the old fork-first cap.
let inferred =
cpuCount <= 4 ? 1 : cpuCount <= 8 ? 2 : cpuCount <= 12 ? 3 : cpuCount <= 16 ? 4 : 6;
cpuCount <= 2
? 1
: cpuCount <= 4
? 2
: cpuCount <= 8
? 4
: Math.max(1, Math.floor(cpuCount * 0.75));
if (totalMemoryGb <= 16) {
inferred = Math.min(inferred, 2);
} else if (totalMemoryGb <= 32) {
inferred = Math.min(inferred, 3);
} else if (totalMemoryGb <= 64) {
inferred = Math.min(inferred, 4);
} else if (totalMemoryGb <= 128) {
inferred = Math.min(inferred, 5);
} else {
} else if (totalMemoryGb <= 64) {
inferred = Math.min(inferred, 6);
} else if (totalMemoryGb <= 128) {
inferred = Math.min(inferred, 8);
} else if (totalMemoryGb <= 256) {
inferred = Math.min(inferred, 12);
} else {
inferred = Math.min(inferred, 16);
}
const loadRatio = loadAverage1m > 0 ? loadAverage1m / cpuCount : 0;
if (loadRatio >= 1) {
inferred = Math.max(1, Math.floor(inferred / 2));
} else if (loadRatio >= 0.75) {
inferred = Math.max(1, inferred - 2);
} else if (loadRatio >= 0.5) {
inferred = Math.max(1, inferred - 1);
}
if (pool === "threads") {
inferred = Math.min(inferred, 4);
if (cpuCount >= 8) {
inferred = Math.max(1, inferred - 1);
}
if (loadRatio >= 0.5) {
inferred = Math.max(1, inferred - 1);
}
if (pool === "forks") {
inferred = Math.min(inferred, 8);
}
inferred = clamp(inferred, 1, 16);

View File

@ -40,7 +40,7 @@ export function createUnitVitestConfigWithOptions(
...sharedTest,
name: options.name ?? "unit",
isolate,
...(isolate ? {} : { runner: "./test/non-isolated-runner.ts" }),
...(isolate ? { runner: undefined } : { runner: "./test/non-isolated-runner.ts" }),
setupFiles: [
...new Set([...(sharedTest.setupFiles ?? []), "test/setup-openclaw-runtime.ts"]),
],