mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 01:29:56 +00:00
fix: run concrete changed test groups concurrently (#162701)
Allow concrete changed-test selections to use the existing bounded concurrent scheduler while preserving serial and exclusive-suite safeguards. Fixes #162615.
This commit is contained in:
parent
d8a110b1a1
commit
afca8f9fce
2 changed files with 22 additions and 8 deletions
|
|
@ -393,25 +393,27 @@ export async function runTestProjects(
|
|||
|
||||
const { parseCLI } = await import("vitest/node");
|
||||
let exactTargetRun = false;
|
||||
const selectedTargets = changedTargetArgs ?? targetArgs;
|
||||
if (
|
||||
targetArgs.length &&
|
||||
selectedTargets.length &&
|
||||
!runSpecs.some((spec) => spec.watchMode) &&
|
||||
!hasNonRunVitestSubcommand(forwardedArgs)
|
||||
) {
|
||||
// Native parsing stays in the execution owner. Original filters distinguish
|
||||
// explicit files from broad selections that also lower to literal include files.
|
||||
// Changed selection already resolved its targets. Broad/config selections
|
||||
// keep their existing policy even when lowered to literal include files.
|
||||
const execution = parseVitestExecutionArgs(["run", ...forwardedArgs], parseCLI);
|
||||
const filters = changedTargetArgs ?? execution?.filter ?? [];
|
||||
if (
|
||||
execution &&
|
||||
!execution.options.watch &&
|
||||
execution.options.run !== false &&
|
||||
execution.filter.length > 0 &&
|
||||
execution.filter.every(
|
||||
filters.length > 0 &&
|
||||
filters.every(
|
||||
(file) => isTestFileTarget(file) && /[/\\]/u.test(file) && !/[*?[\]{}]|[@+!]\(/u.test(file),
|
||||
)
|
||||
) {
|
||||
exactTargetRun = true;
|
||||
if (!Object.hasOwn(execution.options, "passWithNoTests")) {
|
||||
if (targetArgs.length && !Object.hasOwn(execution.options, "passWithNoTests")) {
|
||||
for (const spec of runSpecs) {
|
||||
const separator = spec.pnpmArgs.indexOf("--");
|
||||
spec.pnpmArgs.splice(
|
||||
|
|
|
|||
|
|
@ -654,6 +654,8 @@ describe("automatic exact-target admission", () => {
|
|||
|
||||
it.each([
|
||||
{ name: "CI=1", expected: 2 },
|
||||
{ name: "changed targets", changed: true, expected: 2 },
|
||||
{ name: "changed targets with serial override", changed: true, serial: "1", expected: 1 },
|
||||
{ name: "CI=true", ci: "true", expected: 2 },
|
||||
{ name: "unresolved config outputs", args: [], expected: 1 },
|
||||
{ name: "console reporter without coverage override", args: ["--reporter=dot"], expected: 1 },
|
||||
|
|
@ -682,7 +684,7 @@ describe("automatic exact-target admission", () => {
|
|||
{ name: "caller cache leaf", callerLeaf: true, expected: 1 },
|
||||
])(
|
||||
"preserves resolved specs and policy for $name",
|
||||
async ({ ci, cpus, gib, parallel, serial, portable, callerLeaf, args, expected }) => {
|
||||
async ({ ci, cpus, gib, parallel, serial, portable, callerLeaf, args, changed, expected }) => {
|
||||
vi.stubEnv("CI", ci ?? "1");
|
||||
vi.stubEnv("OPENCLAW_TEST_PROJECTS_PARALLEL", parallel ?? "");
|
||||
vi.stubEnv("OPENCLAW_TEST_PROJECTS_SERIAL", serial ?? "");
|
||||
|
|
@ -697,6 +699,10 @@ describe("automatic exact-target admission", () => {
|
|||
vi.mocked(os.availableParallelism).mockReturnValue(cpus ?? 8);
|
||||
vi.mocked(os.totalmem).mockReturnValue((gib ?? 24) * 1024 ** 3);
|
||||
const planner = await import("../../scripts/test-projects.test-support.mts");
|
||||
if (changed) {
|
||||
const lanes = await import("../../scripts/changed-lanes.mts");
|
||||
vi.spyOn(lanes, "listChangedPathsFromGit").mockReturnValue(files);
|
||||
}
|
||||
const produced = vi.spyOn(planner, "createVitestRunSpecs");
|
||||
const { runTestProjects } = await import("../../scripts/test-projects-run.mts");
|
||||
const selections: Array<{ config: string; include: string[] | null; workers: string }> = [];
|
||||
|
|
@ -720,11 +726,17 @@ describe("automatic exact-target admission", () => {
|
|||
getForwardedSignal: () => undefined,
|
||||
};
|
||||
});
|
||||
await runTestProjects(async () => {}, [...files, ...(args ?? outputArgs)]);
|
||||
await runTestProjects(async () => {}, [
|
||||
...(changed ? ["--changed", "origin/main"] : files),
|
||||
...(args ?? outputArgs),
|
||||
]);
|
||||
const specs = produced.mock.results[0]!.value as ReturnType<
|
||||
typeof planner.createVitestRunSpecs
|
||||
>;
|
||||
expect(specs.length).toBeGreaterThanOrEqual(3);
|
||||
if (changed) {
|
||||
expect(specs.some((spec) => spec.pnpmArgs.includes("--passWithNoTests=false"))).toBe(false);
|
||||
}
|
||||
expect(peak).toBe(expected);
|
||||
const actual = selections.map(({ config, include }) => ({ config, include }));
|
||||
const planned = specs.map((spec) => ({ config: spec.config, include: spec.includePatterns }));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue