mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-27 09:21:35 +07:00
refactor: rename vitest behavior lanes
This commit is contained in:
@@ -13,9 +13,9 @@ title: "Tests"
|
||||
- `pnpm test:coverage`: Runs the unit suite with V8 coverage (via `vitest.unit.config.ts`). Global thresholds are 70% lines/branches/functions/statements. Coverage excludes integration-heavy entrypoints (CLI wiring, gateway/telegram bridges, webchat static server) to keep the target focused on unit-testable logic.
|
||||
- `pnpm test` on Node 22, 23, and 24 uses Vitest `vmForks` by default for local runs with enough memory. CI stays on `forks` unless explicitly overridden. Node 25+ falls back to `forks` until re-validated. You can force behavior with `OPENCLAW_TEST_VM_FORKS=0|1`.
|
||||
- `pnpm test`: runs the full wrapper. It keeps only a small behavioral override manifest in git, then uses a checked-in timing snapshot to peel the heaviest measured unit files into dedicated lanes.
|
||||
- Unit files default to `threads` in the wrapper; keep fork-only or singleton exceptions documented in `test/fixtures/test-parallel.behavior.json`.
|
||||
- Unit files default to `threads` in the wrapper; keep fork-only or `forkBatched` exceptions documented in `test/fixtures/test-parallel.behavior.json`.
|
||||
- `pnpm test:extensions` now defaults to `threads` via `vitest.extensions.config.ts`; the March 22, 2026 direct full-suite control run passed clean without extension-specific fork exceptions.
|
||||
- Files marked `singletonIsolated` no longer spawn one fresh Vitest process each by default. The wrapper batches them into dedicated `forks` lanes with `maxWorkers=1`, which preserves isolation from `unit-fast` while cutting process startup overhead. Tune lane count with `OPENCLAW_TEST_SINGLETON_ISOLATED_LANES=<n>`.
|
||||
- Files marked `forkBatched` stay on `forks`, but the wrapper batches them into low-concurrency `forks` lanes with `maxWorkers=1` instead of spawning one fresh Vitest process per file. Tune lane count with `OPENCLAW_TEST_UNIT_FORK_BATCH_LANES=<n>`; the old `OPENCLAW_TEST_SINGLETON_ISOLATED_LANES=<n>` alias still works.
|
||||
- `pnpm test:channels`: runs channel-heavy suites.
|
||||
- `pnpm test:extensions`: runs extension/plugin suites.
|
||||
- `pnpm test:perf:update-timings`: refreshes the checked-in slow-file timing snapshot used by `scripts/test-parallel.mjs`.
|
||||
|
||||
@@ -80,10 +80,14 @@ export function parseArgs(argv) {
|
||||
|
||||
export function getExistingThreadCandidateExclusions(behavior) {
|
||||
return new Set([
|
||||
...(behavior.base?.threadPinned ?? []).map((entry) => entry.file),
|
||||
...(behavior.base?.threadSingleton ?? []).map((entry) => entry.file),
|
||||
...(behavior.unit?.isolated ?? []).map((entry) => entry.file),
|
||||
...(behavior.unit?.forkBatched ?? []).map((entry) => entry.file),
|
||||
...(behavior.unit?.singletonIsolated ?? []).map((entry) => entry.file),
|
||||
...(behavior.unit?.threadPinned ?? []).map((entry) => entry.file),
|
||||
...(behavior.unit?.threadSingleton ?? []).map((entry) => entry.file),
|
||||
...(behavior.unit?.vmForkPinned ?? []).map((entry) => entry.file),
|
||||
...(behavior.unit?.vmForkSingleton ?? []).map((entry) => entry.file),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -49,19 +49,17 @@ const cleanupTempArtifacts = () => {
|
||||
tempArtifactDir = null;
|
||||
};
|
||||
const existingUnitConfigFiles = (entries) => existingFiles(entries).filter(isUnitConfigTestFile);
|
||||
const baseThreadSingletonFiles = existingFiles(behaviorManifest.base?.threadSingleton ?? []);
|
||||
const unitBehaviorIsolatedFiles = existingUnitConfigFiles(behaviorManifest.unit.isolated);
|
||||
const unitSingletonIsolatedFiles = existingUnitConfigFiles(behaviorManifest.unit.singletonIsolated);
|
||||
const unitThreadSingletonFiles = existingUnitConfigFiles(behaviorManifest.unit.threadSingleton);
|
||||
const unitVmForkSingletonFiles = existingUnitConfigFiles(behaviorManifest.unit.vmForkSingleton);
|
||||
const extensionSingletonIsolatedFiles = existingFiles(
|
||||
behaviorManifest.extensions.singletonIsolated,
|
||||
);
|
||||
const baseThreadPinnedFiles = existingFiles(behaviorManifest.base?.threadPinned ?? []);
|
||||
const unitForkIsolatedFiles = existingUnitConfigFiles(behaviorManifest.unit.isolated);
|
||||
const unitForkBatchedConfiguredFiles = existingUnitConfigFiles(behaviorManifest.unit.forkBatched);
|
||||
const unitThreadPinnedFiles = existingUnitConfigFiles(behaviorManifest.unit.threadPinned);
|
||||
const unitVmForkPinnedFiles = existingUnitConfigFiles(behaviorManifest.unit.vmForkPinned);
|
||||
const extensionIsolatedFiles = existingFiles(behaviorManifest.extensions.isolated);
|
||||
const unitBehaviorOverrideSet = new Set([
|
||||
...unitBehaviorIsolatedFiles,
|
||||
...unitSingletonIsolatedFiles,
|
||||
...unitThreadSingletonFiles,
|
||||
...unitVmForkSingletonFiles,
|
||||
...unitForkIsolatedFiles,
|
||||
...unitForkBatchedConfiguredFiles,
|
||||
...unitThreadPinnedFiles,
|
||||
...unitVmForkPinnedFiles,
|
||||
]);
|
||||
const channelSingletonFiles = [];
|
||||
|
||||
@@ -276,9 +274,9 @@ const allKnownTestFiles = [
|
||||
];
|
||||
const defaultUnitPool = parsePoolOverride(process.env.OPENCLAW_TEST_UNIT_DEFAULT_POOL, "threads");
|
||||
const isTargetedIsolatedUnitFile = (fileFilter) =>
|
||||
unitBehaviorIsolatedFiles.includes(fileFilter) ||
|
||||
unitSingletonBatchFiles.includes(fileFilter) ||
|
||||
unitMemorySingletonFiles.includes(fileFilter);
|
||||
unitForkIsolatedFiles.includes(fileFilter) ||
|
||||
unitForkBatchFiles.includes(fileFilter) ||
|
||||
unitMemoryIsolatedFiles.includes(fileFilter);
|
||||
const inferTarget = (fileFilter) => {
|
||||
const isolated = isTargetedIsolatedUnitFile(fileFilter);
|
||||
if (isUnitConfigTestFile(fileFilter)) {
|
||||
@@ -371,42 +369,40 @@ const { memoryHeavyFiles: memoryHeavyUnitFiles, timedHeavyFiles: timedHeavyUnitF
|
||||
memoryHeavyFiles: [],
|
||||
timedHeavyFiles: [],
|
||||
};
|
||||
const unitSingletonBatchFiles = dedupeFilesPreserveOrder(
|
||||
unitSingletonIsolatedFiles,
|
||||
new Set(unitBehaviorIsolatedFiles),
|
||||
const unitForkBatchFiles = dedupeFilesPreserveOrder(
|
||||
unitForkBatchedConfiguredFiles,
|
||||
new Set(unitForkIsolatedFiles),
|
||||
);
|
||||
const unitMemorySingletonFiles = dedupeFilesPreserveOrder(
|
||||
const unitMemoryIsolatedFiles = dedupeFilesPreserveOrder(
|
||||
memoryHeavyUnitFiles,
|
||||
new Set([...unitBehaviorOverrideSet, ...unitSingletonBatchFiles]),
|
||||
new Set([...unitBehaviorOverrideSet, ...unitForkBatchFiles]),
|
||||
);
|
||||
const unitSchedulingOverrideSet = new Set([...unitBehaviorOverrideSet, ...memoryHeavyUnitFiles]);
|
||||
const unitFastExcludedFiles = [
|
||||
...new Set([...unitSchedulingOverrideSet, ...timedHeavyUnitFiles, ...channelSingletonFiles]),
|
||||
];
|
||||
const defaultSingletonBatchLaneCount =
|
||||
const defaultForkBatchLaneCount =
|
||||
testProfile === "serial"
|
||||
? 0
|
||||
: unitSingletonBatchFiles.length === 0
|
||||
: unitForkBatchFiles.length === 0
|
||||
? 0
|
||||
: isCI
|
||||
? Math.ceil(unitSingletonBatchFiles.length / 6)
|
||||
? Math.ceil(unitForkBatchFiles.length / 6)
|
||||
: testProfile === "low" && highMemLocalHost
|
||||
? Math.ceil(unitSingletonBatchFiles.length / 8) + 1
|
||||
? Math.ceil(unitForkBatchFiles.length / 8) + 1
|
||||
: highMemLocalHost
|
||||
? Math.ceil(unitSingletonBatchFiles.length / 8)
|
||||
? Math.ceil(unitForkBatchFiles.length / 8)
|
||||
: lowMemLocalHost
|
||||
? Math.ceil(unitSingletonBatchFiles.length / 12)
|
||||
: Math.ceil(unitSingletonBatchFiles.length / 10);
|
||||
const singletonBatchLaneCount =
|
||||
unitSingletonBatchFiles.length === 0
|
||||
? Math.ceil(unitForkBatchFiles.length / 12)
|
||||
: Math.ceil(unitForkBatchFiles.length / 10);
|
||||
const configuredForkBatchLaneCount =
|
||||
parseEnvNumber("OPENCLAW_TEST_UNIT_FORK_BATCH_LANES", null) ??
|
||||
// Backward-compatible alias for the old manifest terminology.
|
||||
parseEnvNumber("OPENCLAW_TEST_SINGLETON_ISOLATED_LANES", defaultForkBatchLaneCount);
|
||||
const forkBatchLaneCount =
|
||||
unitForkBatchFiles.length === 0
|
||||
? 0
|
||||
: Math.min(
|
||||
unitSingletonBatchFiles.length,
|
||||
Math.max(
|
||||
1,
|
||||
parseEnvNumber("OPENCLAW_TEST_SINGLETON_ISOLATED_LANES", defaultSingletonBatchLaneCount),
|
||||
),
|
||||
);
|
||||
: Math.min(unitForkBatchFiles.length, Math.max(1, configuredForkBatchLaneCount));
|
||||
const estimateUnitDurationMs = (file) =>
|
||||
unitTimingManifest.files[file]?.durationMs ?? unitTimingManifest.defaultDurationMs;
|
||||
const splitFilesByDurationBudget = (files, targetDurationMs, estimateDurationMs) => {
|
||||
@@ -435,17 +431,17 @@ const splitFilesByDurationBudget = (files, targetDurationMs, estimateDurationMs)
|
||||
|
||||
return batches;
|
||||
};
|
||||
const unitSingletonBuckets =
|
||||
singletonBatchLaneCount > 0
|
||||
? packFilesByDuration(unitSingletonBatchFiles, singletonBatchLaneCount, estimateUnitDurationMs)
|
||||
const unitForkBatchBuckets =
|
||||
forkBatchLaneCount > 0
|
||||
? packFilesByDuration(unitForkBatchFiles, forkBatchLaneCount, estimateUnitDurationMs)
|
||||
: [];
|
||||
const unitFastExcludedFileSet = new Set(unitFastExcludedFiles);
|
||||
const unitFastCandidateFiles = allKnownUnitFiles.filter(
|
||||
(file) => !unitFastExcludedFileSet.has(file),
|
||||
);
|
||||
const extensionSingletonExcludedFileSet = new Set(extensionSingletonIsolatedFiles);
|
||||
const extensionIsolatedExcludedFileSet = new Set(extensionIsolatedFiles);
|
||||
const extensionSharedCandidateFiles = allKnownTestFiles.filter(
|
||||
(file) => file.startsWith("extensions/") && !extensionSingletonExcludedFileSet.has(file),
|
||||
(file) => file.startsWith("extensions/") && !extensionIsolatedExcludedFileSet.has(file),
|
||||
);
|
||||
const defaultUnitFastLaneCount = isCI && !isWindows ? 3 : 1;
|
||||
const unitFastLaneCount = Math.max(
|
||||
@@ -502,13 +498,13 @@ 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) => ({
|
||||
const unitForkBatchEntries = unitForkBatchBuckets.map((files, index) => ({
|
||||
name:
|
||||
unitSingletonBuckets.length === 1 ? "unit-singleton" : `unit-singleton-${String(index + 1)}`,
|
||||
unitForkBatchBuckets.length === 1 ? "unit-fork-batch" : `unit-fork-batch-${String(index + 1)}`,
|
||||
args: ["vitest", "run", "--config", "vitest.unit.config.ts", "--pool=forks", ...files],
|
||||
}));
|
||||
const unitThreadEntries =
|
||||
unitThreadSingletonFiles.length > 0
|
||||
unitThreadPinnedFiles.length > 0
|
||||
? [
|
||||
{
|
||||
name: "unit-threads",
|
||||
@@ -518,12 +514,16 @@ const unitThreadEntries =
|
||||
"--config",
|
||||
"vitest.unit.config.ts",
|
||||
"--pool=threads",
|
||||
...unitThreadSingletonFiles,
|
||||
...unitThreadPinnedFiles,
|
||||
],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
const extensionSingletonEntries = extensionSingletonIsolatedFiles.map((file) => ({
|
||||
const unitIsolatedEntries = unitForkIsolatedFiles.map((file) => ({
|
||||
name: `unit-${path.basename(file, ".test.ts")}-isolated`,
|
||||
args: ["vitest", "run", "--config", "vitest.unit.config.ts", "--pool=forks", file],
|
||||
}));
|
||||
const extensionIsolatedEntries = extensionIsolatedFiles.map((file) => ({
|
||||
name: `${path.basename(file, ".test.ts")}-extensions-isolated`,
|
||||
args: ["vitest", "run", "--config", "vitest.extensions.config.ts", "--pool=forks", file],
|
||||
}));
|
||||
@@ -531,25 +531,11 @@ const baseRuns = [
|
||||
...(shouldSplitUnitRuns
|
||||
? [
|
||||
...unitFastEntries,
|
||||
...(unitBehaviorIsolatedFiles.length > 0
|
||||
? [
|
||||
{
|
||||
name: "unit-isolated",
|
||||
args: [
|
||||
"vitest",
|
||||
"run",
|
||||
"--config",
|
||||
"vitest.unit.config.ts",
|
||||
"--pool=forks",
|
||||
...unitBehaviorIsolatedFiles,
|
||||
],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...unitIsolatedEntries,
|
||||
...unitHeavyEntries,
|
||||
...unitSingletonEntries,
|
||||
...unitMemorySingletonFiles.map((file) => ({
|
||||
name: `${path.basename(file, ".test.ts")}-isolated`,
|
||||
...unitForkBatchEntries,
|
||||
...unitMemoryIsolatedFiles.map((file) => ({
|
||||
name: `unit-${path.basename(file, ".test.ts")}-memory-isolated`,
|
||||
args: [
|
||||
"vitest",
|
||||
"run",
|
||||
@@ -560,7 +546,7 @@ const baseRuns = [
|
||||
],
|
||||
})),
|
||||
...unitThreadEntries,
|
||||
...unitVmForkSingletonFiles.map((file) => ({
|
||||
...unitVmForkPinnedFiles.map((file) => ({
|
||||
name: `${path.basename(file, ".test.ts")}-vmforks`,
|
||||
args: [
|
||||
"vitest",
|
||||
@@ -611,7 +597,7 @@ const baseRuns = [
|
||||
...(useVmForks ? ["--pool=vmForks"] : []),
|
||||
],
|
||||
},
|
||||
...extensionSingletonEntries,
|
||||
...extensionIsolatedEntries,
|
||||
]
|
||||
: []),
|
||||
...(includeGatewaySuite
|
||||
@@ -647,9 +633,9 @@ const resolveFilterMatches = (fileFilter) => {
|
||||
}
|
||||
return allKnownTestFiles.filter((file) => file.includes(normalizedFilter));
|
||||
};
|
||||
const isVmForkSingletonUnitFile = (fileFilter) => unitVmForkSingletonFiles.includes(fileFilter);
|
||||
const isThreadSingletonUnitFile = (fileFilter) => unitThreadSingletonFiles.includes(fileFilter);
|
||||
const isBaseThreadSingletonFile = (fileFilter) => baseThreadSingletonFiles.includes(fileFilter);
|
||||
const isVmForkPinnedUnitFile = (fileFilter) => unitVmForkPinnedFiles.includes(fileFilter);
|
||||
const isThreadPinnedUnitFile = (fileFilter) => unitThreadPinnedFiles.includes(fileFilter);
|
||||
const isBaseThreadPinnedFile = (fileFilter) => baseThreadPinnedFiles.includes(fileFilter);
|
||||
const createTargetedEntry = (owner, isolated, filters) => {
|
||||
const name = isolated ? `${owner}-isolated` : owner;
|
||||
const forceForks = isolated;
|
||||
@@ -749,6 +735,28 @@ const createTargetedEntry = (owner, isolated, filters) => {
|
||||
],
|
||||
};
|
||||
};
|
||||
const formatPerFileEntryName = (owner, file) => {
|
||||
const baseName = path
|
||||
.basename(file)
|
||||
.replace(/\.live\.test\.ts$/u, "")
|
||||
.replace(/\.e2e\.test\.ts$/u, "")
|
||||
.replace(/\.test\.ts$/u, "");
|
||||
return `${owner}-${baseName}`;
|
||||
};
|
||||
const createPerFileTargetedEntry = (file) => {
|
||||
const target = inferTarget(file);
|
||||
const owner = isThreadPinnedUnitFile(file)
|
||||
? "unit-threads"
|
||||
: isVmForkPinnedUnitFile(file)
|
||||
? "unit-vmforks"
|
||||
: isBaseThreadPinnedFile(file)
|
||||
? "base-threads"
|
||||
: target.owner;
|
||||
return {
|
||||
...createTargetedEntry(owner, target.isolated, [file]),
|
||||
name: `${formatPerFileEntryName(owner, file)}${target.isolated ? "-isolated" : ""}`,
|
||||
};
|
||||
};
|
||||
const targetedEntries = (() => {
|
||||
if (passthroughFileFilters.length === 0) {
|
||||
return [];
|
||||
@@ -758,11 +766,11 @@ const targetedEntries = (() => {
|
||||
if (matchedFiles.length === 0) {
|
||||
const normalizedFile = normalizeRepoPath(fileFilter);
|
||||
const target = inferTarget(normalizedFile);
|
||||
const owner = isThreadSingletonUnitFile(normalizedFile)
|
||||
const owner = isThreadPinnedUnitFile(normalizedFile)
|
||||
? "unit-threads"
|
||||
: isVmForkSingletonUnitFile(normalizedFile)
|
||||
: isVmForkPinnedUnitFile(normalizedFile)
|
||||
? "unit-vmforks"
|
||||
: isBaseThreadSingletonFile(normalizedFile)
|
||||
: isBaseThreadPinnedFile(normalizedFile)
|
||||
? "base-threads"
|
||||
: target.owner;
|
||||
const key = `${owner}:${target.isolated ? "isolated" : "default"}`;
|
||||
@@ -773,11 +781,11 @@ const targetedEntries = (() => {
|
||||
}
|
||||
for (const matchedFile of matchedFiles) {
|
||||
const target = inferTarget(matchedFile);
|
||||
const owner = isThreadSingletonUnitFile(matchedFile)
|
||||
const owner = isThreadPinnedUnitFile(matchedFile)
|
||||
? "unit-threads"
|
||||
: isVmForkSingletonUnitFile(matchedFile)
|
||||
: isVmForkPinnedUnitFile(matchedFile)
|
||||
? "unit-vmforks"
|
||||
: isBaseThreadSingletonFile(matchedFile)
|
||||
: isBaseThreadPinnedFile(matchedFile)
|
||||
? "base-threads"
|
||||
: target.owner;
|
||||
const key = `${owner}:${target.isolated ? "isolated" : "default"}`;
|
||||
@@ -789,8 +797,12 @@ const targetedEntries = (() => {
|
||||
}, new Map());
|
||||
return Array.from(groups, ([key, filters]) => {
|
||||
const [owner, mode] = key.split(":");
|
||||
return createTargetedEntry(owner, mode === "isolated", [...new Set(filters)]);
|
||||
});
|
||||
const uniqueFilters = [...new Set(filters)];
|
||||
if (mode === "isolated") {
|
||||
return uniqueFilters.map((file) => createPerFileTargetedEntry(file));
|
||||
}
|
||||
return [createTargetedEntry(owner, false, uniqueFilters)];
|
||||
}).flat();
|
||||
})();
|
||||
// Node 25 local runs still show cross-process worker shutdown contention even
|
||||
// after moving the known heavy files into singleton lanes.
|
||||
@@ -913,7 +925,7 @@ const maxWorkersForRun = (name) => {
|
||||
if (resolvedOverride) {
|
||||
return resolvedOverride;
|
||||
}
|
||||
if (name === "unit-singleton" || name.startsWith("unit-singleton-")) {
|
||||
if (name === "unit-fork-batch" || name.startsWith("unit-fork-batch-")) {
|
||||
return 1;
|
||||
}
|
||||
if (isCI && !isMacOS) {
|
||||
@@ -925,10 +937,10 @@ const maxWorkersForRun = (name) => {
|
||||
if (name.endsWith("-threads") || name.endsWith("-vmforks")) {
|
||||
return 1;
|
||||
}
|
||||
if (name.endsWith("-isolated") && name !== "unit-isolated") {
|
||||
if (name.endsWith("-isolated")) {
|
||||
return 1;
|
||||
}
|
||||
if (name === "unit-isolated" || name.startsWith("unit-heavy-")) {
|
||||
if (name.startsWith("unit-heavy-")) {
|
||||
return defaultWorkerBudget.unitIsolated;
|
||||
}
|
||||
if (name === "extensions") {
|
||||
|
||||
@@ -27,6 +27,22 @@ const normalizeManifestEntries = (entries) =>
|
||||
)
|
||||
.filter((entry) => entry.file.length > 0);
|
||||
|
||||
const mergeManifestEntries = (section, keys) => {
|
||||
const merged = [];
|
||||
const seenFiles = new Set();
|
||||
for (const key of keys) {
|
||||
const normalizedEntries = normalizeManifestEntries(section?.[key] ?? []);
|
||||
for (const entry of normalizedEntries) {
|
||||
if (seenFiles.has(entry.file)) {
|
||||
continue;
|
||||
}
|
||||
seenFiles.add(entry.file);
|
||||
merged.push(entry);
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
};
|
||||
|
||||
export function loadTestRunnerBehavior() {
|
||||
const raw = tryReadJsonFile(behaviorManifestPath, {});
|
||||
const unit = raw.unit ?? {};
|
||||
@@ -34,16 +50,16 @@ export function loadTestRunnerBehavior() {
|
||||
const extensions = raw.extensions ?? {};
|
||||
return {
|
||||
base: {
|
||||
threadSingleton: normalizeManifestEntries(base.threadSingleton ?? []),
|
||||
threadPinned: mergeManifestEntries(base, ["threadPinned", "threadSingleton"]),
|
||||
},
|
||||
unit: {
|
||||
isolated: normalizeManifestEntries(unit.isolated ?? []),
|
||||
singletonIsolated: normalizeManifestEntries(unit.singletonIsolated ?? []),
|
||||
threadSingleton: normalizeManifestEntries(unit.threadSingleton ?? []),
|
||||
vmForkSingleton: normalizeManifestEntries(unit.vmForkSingleton ?? []),
|
||||
isolated: mergeManifestEntries(unit, ["isolated"]),
|
||||
forkBatched: mergeManifestEntries(unit, ["forkBatched", "singletonIsolated"]),
|
||||
threadPinned: mergeManifestEntries(unit, ["threadPinned", "threadSingleton"]),
|
||||
vmForkPinned: mergeManifestEntries(unit, ["vmForkPinned", "vmForkSingleton"]),
|
||||
},
|
||||
extensions: {
|
||||
singletonIsolated: normalizeManifestEntries(extensions.singletonIsolated ?? []),
|
||||
isolated: mergeManifestEntries(extensions, ["isolated", "singletonIsolated"]),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
52
test/fixtures/test-parallel.behavior.json
vendored
52
test/fixtures/test-parallel.behavior.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"base": {
|
||||
"threadSingleton": [
|
||||
"threadPinned": [
|
||||
{
|
||||
"file": "src/commands/backup.test.ts",
|
||||
"reason": "Measured ~16% faster under threads than forks on base config after removing process.chdir() from the test."
|
||||
@@ -23,10 +23,10 @@
|
||||
},
|
||||
{
|
||||
"file": "src/secrets/runtime.integration.test.ts",
|
||||
"reason": "Passes alone but flakes in the batched singleton fork lane; keep it in a dedicated fork worker."
|
||||
"reason": "Passes alone but flakes in the shared fork lane; keep it in a dedicated fork worker."
|
||||
}
|
||||
],
|
||||
"singletonIsolated": [
|
||||
"forkBatched": [
|
||||
{
|
||||
"file": "src/plugins/provider-wizard.test.ts",
|
||||
"reason": "Retained ~318 MiB in an isolated memory trace and is safer outside the shared unit-fast lane."
|
||||
@@ -75,50 +75,10 @@
|
||||
"file": "src/channels/plugins/contracts/registry.contract.test.ts",
|
||||
"reason": "Plugin contract registry coverage retained a large cross-plugin graph in both failing unit-fast shards."
|
||||
},
|
||||
{
|
||||
"file": "src/acp/persistent-bindings.test.ts",
|
||||
"reason": "Persistent bindings coverage retained a large unit-fast heap spike on Linux CI and is safer outside the shared lane."
|
||||
},
|
||||
{
|
||||
"file": "src/channels/plugins/setup-wizard-helpers.test.ts",
|
||||
"reason": "Setup wizard helper coverage retained the largest shared unit-fast heap spike on Linux Node 24 CI."
|
||||
},
|
||||
{
|
||||
"file": "src/cli/config-cli.integration.test.ts",
|
||||
"reason": "Config CLI integration coverage retained a large shared unit-fast heap spike on Linux CI."
|
||||
},
|
||||
{
|
||||
"file": "src/config/plugin-auto-enable.test.ts",
|
||||
"reason": "Plugin auto-enable coverage retained a large shared unit-fast heap spike on Linux Node 22 CI."
|
||||
},
|
||||
{
|
||||
"file": "src/cron/service.runs-one-shot-main-job-disables-it.test.ts",
|
||||
"reason": "Passes alone on shared threads, but flakes in the full shared unit stream and stays safer on the singleton lane."
|
||||
},
|
||||
{
|
||||
"file": "src/cron/service.issue-regressions.test.ts",
|
||||
"reason": "Issue regression cron coverage retained the largest shared unit-fast heap spike in the March 20, 2026 Linux Node 22 and Node 24 OOM lanes."
|
||||
},
|
||||
{
|
||||
"file": "src/memory/qmd-manager.test.ts",
|
||||
"reason": "QMD manager coverage retained recurring shared unit-fast heap spikes across Linux CI lanes."
|
||||
},
|
||||
{
|
||||
"file": "src/secrets/apply.test.ts",
|
||||
"reason": "Secrets apply coverage retained a large shared unit-fast heap spike on Linux Node 22 CI."
|
||||
},
|
||||
{
|
||||
"file": "src/tui/tui-command-handlers.test.ts",
|
||||
"reason": "TUI command handler coverage retained a top shared unit-fast heap spike in the March 20, 2026 Linux Node 24 shard 2 OOM lane."
|
||||
},
|
||||
{
|
||||
"file": "src/node-host/invoke-system-run.test.ts",
|
||||
"reason": "Missing from unit timings and retained the largest shared unit-fast heap spike across the March 20, 2026 Linux Node 22 and Node 24 OOM lanes."
|
||||
},
|
||||
{
|
||||
"file": "src/media-understanding/apply.test.ts",
|
||||
"reason": "Missing from unit timings and retained a top shared unit-fast heap spike across the March 20, 2026 Linux Node 22 and Node 24 OOM lanes."
|
||||
},
|
||||
{
|
||||
"file": "src/tui/tui-event-handlers.test.ts",
|
||||
"reason": "Missing from unit timings and retained the largest shared unit-fast heap spike in the March 20, 2026 Linux Node 24 shard 1 OOM lane."
|
||||
@@ -132,7 +92,7 @@
|
||||
"reason": "Missing from unit timings and retained a top shared unit-fast heap spike in the March 20, 2026 Linux Node 24 shard 2 OOM lane."
|
||||
}
|
||||
],
|
||||
"threadSingleton": [
|
||||
"threadPinned": [
|
||||
{
|
||||
"file": "test/git-hooks-pre-commit.test.ts",
|
||||
"reason": "Measured ~12% faster under threads than forks on base config while keeping the file green."
|
||||
@@ -790,10 +750,10 @@
|
||||
"reason": "Terminates cleanly under threads, but not process forks on this host."
|
||||
}
|
||||
],
|
||||
"vmForkSingleton": []
|
||||
"vmForkPinned": []
|
||||
},
|
||||
"extensions": {
|
||||
"singletonIsolated": [
|
||||
"isolated": [
|
||||
{
|
||||
"file": "extensions/bluebubbles/src/setup-surface.test.ts",
|
||||
"reason": "Loads the setup-wizard/plugin-sdk graph and retained ~576 MiB in an isolated memory trace."
|
||||
|
||||
@@ -42,6 +42,30 @@ describe("scripts/test-find-thread-candidates parseArgs", () => {
|
||||
|
||||
describe("scripts/test-find-thread-candidates exclusions", () => {
|
||||
it("collects already-pinned files across behavior buckets", () => {
|
||||
expect(
|
||||
getExistingThreadCandidateExclusions({
|
||||
base: {
|
||||
threadPinned: [{ file: "src/base-a.test.ts" }],
|
||||
},
|
||||
unit: {
|
||||
isolated: [{ file: "src/a.test.ts" }],
|
||||
forkBatched: [{ file: "src/b.test.ts" }],
|
||||
threadPinned: [{ file: "src/c.test.ts" }],
|
||||
vmForkPinned: [{ file: "src/d.test.ts" }],
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
new Set([
|
||||
"src/base-a.test.ts",
|
||||
"src/a.test.ts",
|
||||
"src/b.test.ts",
|
||||
"src/c.test.ts",
|
||||
"src/d.test.ts",
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps backward-compatible aliases readable", () => {
|
||||
expect(
|
||||
getExistingThreadCandidateExclusions({
|
||||
base: {
|
||||
|
||||
Reference in New Issue
Block a user