Files
scriptc/tests/harness/ci-test-plan.test.ts
Chris Tate cc75597869 Shorten the CI critical path (#605)
* Shorten the CI critical path

* Bound Effect runner memory and avoid repeated type checks
2026-10-01 22:18:40 -05:00

74 lines
4.0 KiB
TypeScript

import { expect, test } from "vitest";
import { ciTestPlan, corpusFiles, effectFiles, fileExclusions } from "../../scripts/ci-test-plan.mjs";
import { runCiTestPlan } from "../../scripts/ci-test.mjs";
test.for([1, 3, 5, 10])("CI retains every suite with %i shards without mixing partition axes", (count) => {
for (const flavor of ["plain", "san"]) {
const plans = Array.from({ length: count }, (_, index) => ciTestPlan({ shard: `${index + 1}/${count}`, flavor }));
const sideFiles = plans.flatMap((plan) => plan.side.filter((task) => task.args.length === 2).map((task) => task.args[1]));
for (const file of fileExclusions.filter((file) => !corpusFiles.includes(file) && !effectFiles.includes(file) && !file.includes("native-driver") && !file.includes("coverage") && !file.includes("native-toolchain"))) {
expect(sideFiles.filter((selected) => selected === file), file).toHaveLength(1);
}
for (const plan of plans) {
expect(plan.corpus.args).toEqual(["test", ...corpusFiles]);
expect(plan.side[0]!.args).toContain(`--shard=${plan.corpus.env.SCRIPTC_TEST_SHARD}`);
for (const file of fileExclusions) expect(plan.side[0]!.args).toContain(`--exclude=${file}`);
const cache = plan.side.find((task) => task.name === "native-cache correctness");
expect(Boolean(cache)).toBe(flavor === "plain");
if (cache) {
expect(cache.env.SCRIPTC_CACHE_TEST_SHARD).toBe(plan.corpus.env.SCRIPTC_TEST_SHARD);
expect(cache.env.SCRIPTC_TEST_STABLE_TOOLCHAIN).toBe("0");
}
expect(plan.side.some((task) => task.name === "coverage sweep")).toBe(flavor === "plain");
}
}
});
test.for([1, 2, 4, 8])("CI overlaps reserved lanes within a budget of %i workers", async (workers) => {
const plan = ciTestPlan({ shard: "1/10", flavor: "plain", workers });
const events: string[] = [];
let active = 0;
let peak = 0;
let finishCorpus!: () => void;
const corpusWait = new Promise<void>((resolve) => { finishCorpus = resolve; });
await runCiTestPlan(plan, async (task) => {
const cost = Number(task.env.SCRIPTC_TEST_WORKERS);
active += cost;
peak = Math.max(peak, active);
events.push(`start:${task.name}`);
if (task === plan.corpus && workers > 1) await corpusWait;
else await Promise.resolve();
events.push(`end:${task.name}`);
active -= cost;
if (task === plan.side.at(-1)) finishCorpus();
});
expect(peak).toBeLessThanOrEqual(workers);
expect(events.filter((event) => event.startsWith("start:")).sort()).toEqual([plan.corpus, ...plan.side].map((task) => `start:${task.name}`).sort());
for (let index = 1; index < plan.side.length; index++) {
expect(events.indexOf(`start:${plan.side[index]!.name}`)).toBeGreaterThan(events.indexOf(`end:${plan.side[index - 1]!.name}`));
}
if (workers === 1) expect(events.indexOf(`start:${plan.side[0]!.name}`)).toBeGreaterThan(events.indexOf(`end:${plan.corpus.name}`));
else expect(events.indexOf(`start:${plan.side[0]!.name}`)).toBeLessThan(events.indexOf(`end:${plan.corpus.name}`));
});
test("CI reports failures after running every scheduled suite", async () => {
const plan = ciTestPlan({ shard: "1/10", flavor: "plain" });
const seen: string[] = [];
let failure: AggregateError | undefined;
try {
await runCiTestPlan(plan, async (task) => {
seen.push(task.name);
if (task === plan.corpus || task === plan.side[0]) throw new Error(task.name);
});
} catch (error) { failure = error as AggregateError; }
expect(failure).toBeInstanceOf(AggregateError);
expect(failure!.errors).toHaveLength(2);
expect(seen.sort()).toEqual([plan.corpus, ...plan.side].map((task) => task.name).sort());
});
test("CI rejects malformed scheduling inputs", () => {
for (const shard of [undefined, "", "0/10", "11/10", "2/x"]) expect(() => ciTestPlan({ shard, flavor: "plain" })).toThrow();
for (const workers of [0, -1, 1.5, NaN, Infinity]) expect(() => ciTestPlan({ shard: "1/10", flavor: "plain", workers })).toThrow();
expect(() => ciTestPlan({ shard: "1/10", flavor: "unknown" })).toThrow();
});