mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 09:39:25 +00:00
* perf(ci): run qualified unit tests with native Bun * test(ci): keep a V8-only cache warm fixture * fix(ci): include shared timeout in PR wrapper inventory * perf(test): skip Bun bytecode cache during test teardown * test: handle worker fixture retirement notifications Handle rejected observer returns without delaying retirement, matching the worker pool contract, and avoid shadowing the lane fixture input helper. * perf(ci): qualify and tune Bun test workers Complete SQLite admission before Bun Vitest threads start and retain each worker generation's canonical cleanup decision in lifecycle fixtures. Keep proxy-agent fixture values out of Bun's native fetch cache and compare large socket payloads with full byte equality. Renew the 292-file native qualification from paired 2,518-case proof. Apply the existing UI JIT and allocator settings to ordinary unit-fast Bun/Vitest selections and their cache warmer, preserving Node and native Bun settings and dual-runtime release coverage. Proof: 208 routing/warming cases; 61 lifecycle cases on each runtime; 66 ordered proxy/fetch cases plus the canonical 11-case proxy owner on each runtime; independent P0-P2 review. The changed-file gate was interrupted by the local disk reserve guard after core typing and 14 core-test type shards passed. Tuned full-cohort performance remains to be measured; the completed untuned comparison was 4.60% slower than Node. * perf(test): expand native Bun coverage and reduce artifact IO * perf(test): yield between artifact verification batches * test: qualify native Bun cases after runtime integration * test: refresh native Bun qualification after main integration
120 lines
4.2 KiB
TypeScript
120 lines
4.2 KiB
TypeScript
// Runs grouped batches through the repository's installed Vitest entrypoint.
|
|
import path from "node:path";
|
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
import { assertTestHomeSelection, type TestHomeSelection } from "../../test/test-home-policy.mts";
|
|
import { installVitestProcessGroupCleanup } from "../vitest-process-group.mts";
|
|
import { resolveVitestCliEntry } from "./vitest-build-prerequisites.mts";
|
|
import { resolveExplicitVitestMode } from "./vitest-cli-mode.mts";
|
|
import { resolveVitestHomeSelection } from "./vitest-home-selection.mts";
|
|
import { resolveVitestNodeArgs } from "./vitest-process-env.mts";
|
|
import { exitVitestBySignal, spawnOwnedVitestProcess } from "./vitest-process.mts";
|
|
import type { VitestReportOutcome } from "./vitest-report-owner.mts";
|
|
import { resolveVitestTestCommand } from "./vitest-test-runtime.mts";
|
|
import { createVitestWorkerRun } from "./vitest-worker-run.mts";
|
|
|
|
export type VitestBatchRunParams = {
|
|
args: string[];
|
|
config: string;
|
|
env?: NodeJS.ProcessEnv;
|
|
// Owner-generated report configs retain their validated original selection.
|
|
homeMode?: TestHomeSelection;
|
|
targets: string[];
|
|
onComplete?: (outcome: VitestReportOutcome) => void;
|
|
};
|
|
|
|
const scriptFile = fileURLToPath(import.meta.url);
|
|
const scriptDir = path.dirname(scriptFile);
|
|
const repoRoot = path.resolve(scriptDir, "../..");
|
|
|
|
/**
|
|
* Runs one Vitest batch and forwards process-group cleanup signals.
|
|
*/
|
|
export async function runVitestBatch(params: VitestBatchRunParams): Promise<number> {
|
|
const env = params.env ?? process.env;
|
|
const homeMode =
|
|
params.homeMode ??
|
|
resolveVitestHomeSelection(["--config", params.config, ...params.args, ...params.targets], {
|
|
cwd: repoRoot,
|
|
env,
|
|
});
|
|
assertTestHomeSelection(env, homeMode);
|
|
const testCommand = resolveVitestTestCommand(
|
|
[
|
|
...resolveVitestNodeArgs(env),
|
|
resolveVitestCliEntry({ env }),
|
|
"run",
|
|
"--config",
|
|
params.config,
|
|
...params.args,
|
|
...params.targets,
|
|
],
|
|
env,
|
|
);
|
|
const workers =
|
|
resolveExplicitVitestMode(["run", ...params.args]) === "watch"
|
|
? undefined
|
|
: createVitestWorkerRun(env);
|
|
if (workers) {
|
|
const cliIndex = testCommand.args.findIndex((arg) => path.basename(arg) === "vitest.mjs");
|
|
testCommand.args.splice(
|
|
cliIndex,
|
|
0,
|
|
path.join(repoRoot, "scripts/lib/vitest-worker-bootstrap.mts"),
|
|
workers.descriptor.directory,
|
|
);
|
|
}
|
|
let interrupted: NodeJS.Signals | undefined;
|
|
const onSignal = (signal: NodeJS.Signals) => {
|
|
interrupted ??= signal;
|
|
};
|
|
// Artifact verification can outlive the child's signal handlers.
|
|
process.on("SIGINT", onSignal);
|
|
process.on("SIGTERM", onSignal);
|
|
let outcome: VitestReportOutcome;
|
|
try {
|
|
// Match project runs: native workers borrow this invocation's prepared source,
|
|
// rather than compiling the application independently inside every worker.
|
|
const { child, completion } = spawnOwnedVitestProcess({
|
|
homeMode,
|
|
...testCommand,
|
|
options: {
|
|
cwd: repoRoot,
|
|
env: testCommand.envOverrides ? { ...env, ...testCommand.envOverrides } : env,
|
|
stdio: workers ? ["inherit", "inherit", "inherit", "ipc"] : "inherit",
|
|
},
|
|
});
|
|
const cleanup = installVitestProcessGroupCleanup({
|
|
child,
|
|
forceSignal: "SIGKILL",
|
|
forceSignalDelayMs: 100,
|
|
});
|
|
try {
|
|
const { code, signal } = await (workers ? workers.borrow(child, completion) : completion);
|
|
interrupted ??= cleanup.getForwardedSignal() ?? signal ?? undefined;
|
|
outcome = { code: code ?? 1, signal: interrupted ?? null };
|
|
} finally {
|
|
cleanup.teardown();
|
|
}
|
|
} finally {
|
|
try {
|
|
await workers?.dispose();
|
|
} finally {
|
|
process.off("SIGINT", onSignal);
|
|
process.off("SIGTERM", onSignal);
|
|
if (interrupted && !params.onComplete) {
|
|
await exitVitestBySignal(interrupted);
|
|
}
|
|
}
|
|
}
|
|
outcome.signal = interrupted ?? outcome.signal;
|
|
params.onComplete?.(outcome);
|
|
return outcome.code;
|
|
}
|
|
|
|
/**
|
|
* Checks whether a module URL is the current direct script entrypoint.
|
|
*/
|
|
export function isDirectScriptRun(metaUrl: string): boolean {
|
|
const entryHref = process.argv[1] ? pathToFileURL(path.resolve(process.argv[1])).href : "";
|
|
return metaUrl === entryHref;
|
|
}
|