mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-30 01:06:11 +00:00
test: speed up isolated test lanes
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
resolveTestRunExitCode,
|
||||
} from "./test-parallel-utils.mjs";
|
||||
import {
|
||||
dedupeFilesPreserveOrder,
|
||||
loadUnitMemoryHotspotManifest,
|
||||
loadTestRunnerBehavior,
|
||||
loadUnitTimingManifest,
|
||||
@@ -81,18 +82,17 @@ const testProfile =
|
||||
? rawTestProfile
|
||||
: "normal";
|
||||
const isMacMiniProfile = testProfile === "macmini";
|
||||
// vmForks is a big win for transform/import heavy suites. Node 24 is stable again
|
||||
// for the default unit-fast lane after moving the known flaky files to fork-only
|
||||
// isolation, but Node 25+ still falls back to process forks until re-validated.
|
||||
// Keep it opt-out via OPENCLAW_TEST_VM_FORKS=0, and let users force-enable with =1.
|
||||
// vmForks is still useful for local transform-heavy runs, but do not flip CI
|
||||
// back to vmForks by default unless you have fresh green evidence on current
|
||||
// main. The retained Vite/Vitest module graph is the OOM pattern we keep
|
||||
// tripping over under CI-style pressure. Keep it opt-out via
|
||||
// OPENCLAW_TEST_VM_FORKS=0, and let users force-enable with =1 for comparison.
|
||||
const supportsVmForks = Number.isFinite(nodeMajor) ? nodeMajor <= 24 : true;
|
||||
const preferVmForksByDefault =
|
||||
!isCI && !isWindows && supportsVmForks && !lowMemLocalHost && testProfile !== "low";
|
||||
const useVmForks =
|
||||
process.env.OPENCLAW_TEST_VM_FORKS === "1" ||
|
||||
(process.env.OPENCLAW_TEST_VM_FORKS !== "0" &&
|
||||
!isWindows &&
|
||||
supportsVmForks &&
|
||||
!lowMemLocalHost &&
|
||||
(isCI || testProfile !== "low"));
|
||||
(process.env.OPENCLAW_TEST_VM_FORKS !== "0" && preferVmForksByDefault);
|
||||
const disableIsolation = process.env.OPENCLAW_TEST_NO_ISOLATE === "1";
|
||||
const includeGatewaySuite = process.env.OPENCLAW_TEST_INCLUDE_GATEWAY === "1";
|
||||
const includeExtensionsSuite = process.env.OPENCLAW_TEST_INCLUDE_EXTENSIONS === "1";
|
||||
@@ -345,15 +345,46 @@ const { memoryHeavyFiles: memoryHeavyUnitFiles, timedHeavyFiles: timedHeavyUnitF
|
||||
memoryHeavyFiles: [],
|
||||
timedHeavyFiles: [],
|
||||
};
|
||||
const unitSingletonBatchFiles = dedupeFilesPreserveOrder(
|
||||
unitSingletonIsolatedFiles,
|
||||
new Set(unitBehaviorIsolatedFiles),
|
||||
);
|
||||
const unitMemorySingletonFiles = dedupeFilesPreserveOrder(
|
||||
memoryHeavyUnitFiles,
|
||||
new Set([...unitBehaviorOverrideSet, ...unitSingletonBatchFiles]),
|
||||
);
|
||||
const unitSchedulingOverrideSet = new Set([...unitBehaviorOverrideSet, ...memoryHeavyUnitFiles]);
|
||||
const unitFastExcludedFiles = [
|
||||
...new Set([...unitSchedulingOverrideSet, ...timedHeavyUnitFiles, ...channelSingletonFiles]),
|
||||
];
|
||||
const unitAutoSingletonFiles = [
|
||||
...new Set([...unitSingletonIsolatedFiles, ...memoryHeavyUnitFiles]),
|
||||
];
|
||||
const defaultSingletonBatchLaneCount =
|
||||
testProfile === "serial"
|
||||
? 0
|
||||
: unitSingletonBatchFiles.length === 0
|
||||
? 0
|
||||
: isCI
|
||||
? Math.ceil(unitSingletonBatchFiles.length / 6)
|
||||
: highMemLocalHost
|
||||
? Math.ceil(unitSingletonBatchFiles.length / 8)
|
||||
: lowMemLocalHost
|
||||
? Math.ceil(unitSingletonBatchFiles.length / 12)
|
||||
: Math.ceil(unitSingletonBatchFiles.length / 10);
|
||||
const singletonBatchLaneCount =
|
||||
unitSingletonBatchFiles.length === 0
|
||||
? 0
|
||||
: Math.min(
|
||||
unitSingletonBatchFiles.length,
|
||||
Math.max(
|
||||
1,
|
||||
parseEnvNumber("OPENCLAW_TEST_SINGLETON_ISOLATED_LANES", defaultSingletonBatchLaneCount),
|
||||
),
|
||||
);
|
||||
const estimateUnitDurationMs = (file) =>
|
||||
unitTimingManifest.files[file]?.durationMs ?? unitTimingManifest.defaultDurationMs;
|
||||
const unitSingletonBuckets =
|
||||
singletonBatchLaneCount > 0
|
||||
? packFilesByDuration(unitSingletonBatchFiles, singletonBatchLaneCount, estimateUnitDurationMs)
|
||||
: [];
|
||||
const unitFastExcludedFileSet = new Set(unitFastExcludedFiles);
|
||||
const unitFastCandidateFiles = allKnownUnitFiles.filter(
|
||||
(file) => !unitFastExcludedFileSet.has(file),
|
||||
@@ -400,6 +431,11 @@ const unitHeavyEntries = heavyUnitBuckets.map((files, index) => ({
|
||||
name: `unit-heavy-${String(index + 1)}`,
|
||||
args: ["vitest", "run", "--config", "vitest.unit.config.ts", "--pool=forks", ...files],
|
||||
}));
|
||||
const unitSingletonEntries = unitSingletonBuckets.map((files, index) => ({
|
||||
name:
|
||||
unitSingletonBuckets.length === 1 ? "unit-singleton" : `unit-singleton-${String(index + 1)}`,
|
||||
args: ["vitest", "run", "--config", "vitest.unit.config.ts", "--pool=forks", ...files],
|
||||
}));
|
||||
const baseRuns = [
|
||||
...(shouldSplitUnitRuns
|
||||
? [
|
||||
@@ -420,7 +456,8 @@ const baseRuns = [
|
||||
]
|
||||
: []),
|
||||
...unitHeavyEntries,
|
||||
...unitAutoSingletonFiles.map((file) => ({
|
||||
...unitSingletonEntries,
|
||||
...unitMemorySingletonFiles.map((file) => ({
|
||||
name: `${path.basename(file, ".test.ts")}-isolated`,
|
||||
args: [
|
||||
"vitest",
|
||||
@@ -756,6 +793,9 @@ const maxWorkersForRun = (name) => {
|
||||
if (resolvedOverride) {
|
||||
return resolvedOverride;
|
||||
}
|
||||
if (name === "unit-singleton" || name.startsWith("unit-singleton-")) {
|
||||
return 1;
|
||||
}
|
||||
if (isCI && !isMacOS) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -231,3 +231,18 @@ export function packFilesByDuration(files, bucketCount, estimateDurationMs) {
|
||||
|
||||
return buckets.map((bucket) => bucket.files).filter((bucket) => bucket.length > 0);
|
||||
}
|
||||
|
||||
export function dedupeFilesPreserveOrder(files, exclude = new Set()) {
|
||||
const result = [];
|
||||
const seen = new Set();
|
||||
|
||||
for (const file of files) {
|
||||
if (exclude.has(file) || seen.has(file)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(file);
|
||||
result.push(file);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user