import assert from "node:assert/strict";
import { execFileSync, spawnSync } from "node:child_process";
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import { fileURLToPath } from "node:url";
import {
chooseRust,
classify,
cratePackage,
dartDefine,
hunkRanges,
ITERATIONS,
listPlaywright,
MAX_PLAYWRIGHT_JOURNEYS,
nodeTestArgs,
parseDartReport,
parseJunit,
playwrightRepetitions,
playwrightShards,
runners,
rustFilterset,
rustGroups,
rustImporters,
rustIncludedTests,
rustIncludes,
rustModulePath,
rustModules,
rustTarget,
rustTargetRoot,
rustTests,
rustWorkspaces,
selectRust,
selectTests,
testBodies,
testCalls,
} from "./flake-check.mjs";
const read = (path) => readFileSync(new URL(path, import.meta.url), "utf8");
const root = fileURLToPath(new URL("../", import.meta.url));
const git = (args, env = {}) =>
execFileSync("git", ["-c", "core.hooksPath=/dev/null", ...args], {
cwd: root,
encoding: "utf8",
env: { ...process.env, ...env },
});
// `select` over a synthetic head tree: HEAD's tree with a comment appended to
// each given file, so only those files differ and no test body changes. The
// outputs are keyed per runner as the workflow reads them. The selector
// writes its summary and outputs to $GITHUB_STEP_SUMMARY and $GITHUB_OUTPUT
// when they are set (as under Actions, where this suite also runs) and to
// stdout otherwise, so the child gets one contract or the other by `github`,
// never whichever the parent happens to have.
function selectAppending(files, { github = false } = {}) {
const dir = mkdtempSync(join(tmpdir(), "flake-select-"));
try {
const env = { GIT_INDEX_FILE: join(dir, "index") };
git(["read-tree", "HEAD"], env);
for (const file of files) {
const blob = execFileSync("git", ["hash-object", "-w", "--stdin"], {
cwd: root,
encoding: "utf8",
input: `${git(["show", `HEAD:${file}`])}// flake-check probe\n`,
}).trim();
git(["update-index", "--cacheinfo", `100644,${blob},${file}`], env);
}
const tree = git(["write-tree"], env).trim();
const { GITHUB_STEP_SUMMARY, GITHUB_OUTPUT, ...inherited } = process.env;
const routed = {
GITHUB_STEP_SUMMARY: join(dir, "summary.md"),
GITHUB_OUTPUT: join(dir, "output.txt"),
};
const result = spawnSync(
process.execPath,
[join(root, "scripts/flake-check.mjs"), "select", "HEAD", tree],
{
cwd: root,
encoding: "utf8",
env: github ? { ...inherited, ...routed } : inherited,
},
);
assert.equal(result.status, 0, result.stderr);
const summary = github
? readFileSync(routed.GITHUB_STEP_SUMMARY, "utf8")
: result.stdout;
const output = github
? readFileSync(routed.GITHUB_OUTPUT, "utf8")
: result.stdout;
const outputs = Object.fromEntries(
[...output.matchAll(/^(\w+)=(.*)$/gm)].map(([, key, value]) => [
key,
value.split(" ").filter(Boolean),
]),
);
return { summary, outputs, stdout: result.stdout };
} finally {
rmSync(dir, { recursive: true, force: true });
}
}
// A hunk writing new-side lines start..end; by default it removed nothing.
const hunk = (start, end, removed = { start: start + 1, end: start }) => ({
start,
end,
removed,
});
// A -U0 diff of (old start, old count, new start, new count) hunks.
const diff = (...hunks) =>
hunks.map(([o, oc, n, nc]) => `@@ -${o},${oc} +${n},${nc} @@`).join("\n");
test("classification follows each runner's test file layout", () => {
for (const [file, runner] of [
["crates/buzz-core/src/filter.rs", "rust"],
["crates/buzz-test-client/tests/e2e_relay.rs", "rust"],
["desktop/src-tauri/src/lib.rs", "tauri"],
["desktop/src-tauri/tests/rodio_mixer_diagnostic.rs", "tauri"],
["desktop/src-tauri/crates/buzz-terminal/src/lib.rs", "tauri"],
["desktop/src-tauri/crates/buzz-terminal/tests/latency.rs", "tauri"],
["desktop/src/features/channels/lib/channelDescription.test.mjs", "node"],
["desktop/src/shared/ui/thing.jsdom-test.mjs", "node"],
["desktop/scripts/demo-build-config.test.mjs", "node"],
["desktop/tests/e2e/channels.spec.ts", "playwright"],
["mobile/test/features/invites/invite_join_provider_test.dart", "dart"],
])
assert.equal(classify(file), runner, file);
for (const file of [
"crates/buzz-core/Cargo.toml",
"crates/buzz-feature-flags/tests/fixtures/relay-feature-selection/src/main.rs",
"desktop/src-tauri/build.rs",
"desktop/src-tauri/tests/fixtures/x.rs",
"desktop/src-tauri/crates/buzz-terminal/Cargo.toml",
"desktop/src/features/channels/lib/channelDescription.ts",
"desktop/tests/e2e/helpers/animations.ts",
"desktop/tests/e2e/typing-latency.perf.ts",
"desktop/tests/e2e/fixtures/x.spec.ts",
"mobile/lib/main.dart",
"mobile/test/helpers.dart",
"scripts/flake-check.test.mjs",
"web/src/x.test.mjs",
// Selections are shell-split downstream; refuse unusual names.
"desktop/tests/e2e/odd name.spec.ts",
"crates/buzz-core/src/$(id).rs",
])
assert.equal(classify(file), undefined, file);
// Each Rust runner names the cargo workspace its lane repeats.
assert.deepEqual(Object.keys(rustWorkspaces), ["rust", "tauri"]);
assert.ok(runners.includes("tauri"));
});
test("-U0 hunks map to new-side ranges, with deletions attached to the preceding line and their old-side lines kept", () => {
const text = [
"diff --git a/x.spec.ts b/x.spec.ts",
"--- a/x.spec.ts",
"+++ b/x.spec.ts",
"@@ -1 +1 @@",
'-import { test } from "@playwright/test";',
'+import { expect, test } from "@playwright/test";',
"@@ -12,0 +13,3 @@ test('a', async () => {",
"+ const x = 1;",
"+ const y = 2;",
"+ expect(x).toBe(y - 1);",
"@@ -30,2 +33,0 @@ test('b', async () => {",
"- removed();",
"- alsoRemoved();",
"@@ -1,3 +0,0 @@",
"-gone",
"",
].join("\n");
assert.deepEqual(hunkRanges(text), [
{ start: 1, end: 1, removed: { start: 1, end: 1 } },
{ start: 13, end: 15, removed: { start: 12, end: 11 } },
{ start: 33, end: 33, removed: { start: 30, end: 31 } },
{ start: 0, end: 0, removed: { start: 1, end: 3 } },
]);
assert.deepEqual(hunkRanges(""), []);
});
test("Playwright test calls span the declaration; groups, hooks and configuration are outside", () => {
const spec = [
'import { expect, test } from "@playwright/test";',
"test.use({ viewport: { width: 800, height: 600 } });",
"const seed = 1;",
'test.describe("suite", () => {',
" test.beforeEach(async ({ page }) => {",
' await page.goto("/");',
" });",
' test("journey", async ({ page }) => {',
' test.skip(seed === 2, "not here");',
' await expect(page.getByText("Go")).toBeVisible();',
" });",
' for (const mode of ["mouse", "keyboard"]) {',
' test.fixme(mode + " later", async () => {',
" await Promise.resolve();",
" });",
" }",
"});",
'test.describe.configure({ mode: "serial" });',
'test.fail("known broken", async () => {',
" await Promise.resolve();",
"});",
"",
].join("\n");
const calls = testCalls(spec, "x.spec.ts");
assert.deepEqual(calls, [
{ start: 8, end: 11 },
{ start: 13, end: 15 },
{ start: 19, end: 21 },
]);
// Playwright lists a looped declaration once per iteration, at one line.
assert.deepEqual(testBodies(calls, [13, 13, 8]), [
{ start: 8, end: 11, line: 8 },
{ start: 13, end: 15, line: 13 },
]);
// A listed line outside every call (a hook) has no body.
assert.deepEqual(testBodies(calls, [5]), []);
});
const rustSource = [
"//! Module docs mentioning #[test] in prose.",
"use std::fmt;",
"",
"pub fn code_under_test(input: &str) -> String {",
" let brace = '{';",
' let raw = r"\\";',
' format!("{brace}{raw}{input}")',
"}",
"",
"#[cfg(test)]",
"mod tests {",
" use super::*;",
"",
" fn helper() -> &'static str {",
' "}"',
" }",
"",
" /// Docs for the first test.",
" #[test]",
" fn plain() {",
' assert_eq!(code_under_test("x"), "{\\\\x");',
" }",
"",
' #[tokio::test(flavor = "multi_thread")]',
' #[ignore = "requires Postgres }"]',
" async fn needs_infra() {",
" /* a block comment with a } brace",
" and #[test] inside */",
" helper();",
" }",
"",
" #[ignore]",
" #[test]",
" pub(crate) fn also_ignored() {}",
"",
" mod postgres_tests {",
" #[sqlx::test]",
" async fn in_nested_module(pool: Pool) -> Result<(), E> {",
" Ok(())",
" }",
" }",
"}",
"",
].join("\n");
test("Rust test functions span their attribute block to the closing brace, through strings, chars and comments, named by their inline module path", () => {
assert.deepEqual(rustTests(rustSource), [
{ name: "tests::plain", start: 19, end: 22, ignored: false },
{ name: "tests::needs_infra", start: 24, end: 30, ignored: true },
{ name: "tests::also_ignored", start: 32, end: 34, ignored: true },
{
name: "tests::postgres_tests::in_nested_module",
start: 37,
end: 40,
ignored: false,
},
]);
assert.deepEqual(rustTests("fn main() {}\n"), []);
});
test("same-named Rust tests in different modules are distinct, and a hunk selects only its own", () => {
const source = [
"mod alpha {",
" #[test]",
" fn same() {}",
"}",
"pub(crate) mod beta {",
" mod tests {",
" #[test]",
" fn same() { let _ = 1; }",
" }",
"}",
"",
].join("\n");
assert.deepEqual(
rustTests(source).map((test) => test.name),
["alpha::same", "beta::tests::same"],
);
assert.deepEqual(
selectRust(diff([8, 1, 8, 1]), source, { previous: source }),
{
lines: [7],
names: ["beta::tests::same"],
},
);
// The file's path predicts the rest of the name; cargo's layout rules.
for (const [file, modules] of [
["crates/buzz-relay/src/lib.rs", []],
["crates/buzz-relay/src/main.rs", []],
["crates/buzz-relay/src/api/mod.rs", ["api"]],
["crates/buzz-relay/src/handlers/auth.rs", ["handlers", "auth"]],
["crates/buzz-test-client/src/bin/mention.rs", []],
["crates/buzz-test-client/tests/e2e_relay.rs", []],
["crates/x/tests/suite/main.rs", []],
["crates/x/tests/suite/helpers.rs", ["helpers"]],
["desktop/src-tauri/src/app_state.rs", ["app_state"]],
["desktop/src-tauri/tests/rodio_mixer_diagnostic.rs", []],
[
"desktop/src-tauri/crates/buzz-terminal/src/grid/cell.rs",
["grid", "cell"],
],
])
assert.deepEqual(rustModulePath(file), modules, file);
// Listed candidates: the predicted name wins; without it every suffix
// match is kept rather than nothing.
const candidates = [
{ binary: "buzz-relay", name: "api::relay_members::tests::same" },
{ binary: "buzz-relay", name: "handlers::auth::tests::same" },
{ binary: "buzz-relay", name: "handlers::auth::tests::other" },
{
binary: "buzz-relay::bin/buzz-relay",
name: "handlers::auth::tests::same",
},
];
assert.deepEqual(
chooseRust(
[{ path: "tests::same", exact: "handlers::auth::tests::same" }],
candidates,
),
[candidates[1], candidates[3]],
);
assert.deepEqual(
chooseRust(
[
{ path: "tests::same", exact: "elsewhere::tests::same" },
{ path: "tests::other", exact: "handlers::auth::tests::other" },
],
candidates,
),
[candidates[0], candidates[1], candidates[3], candidates[2]],
);
});
test("Rust files build only the targets they can be part of, merged per package one whole flag each", () => {
for (const [file, flags] of [
["crates/buzz-relay/src/api/mod.rs", [["--lib"], ["--bins"]]],
["crates/buzz-admin/src/main.rs", [["--bins"]]],
["crates/buzz-test-client/tests/e2e_relay.rs", [["--test", "e2e_relay"]]],
["crates/buzz-agent/tests/common/mod.rs", []],
["desktop/src-tauri/src/lib.rs", [["--lib"], ["--bins"]]],
[
"desktop/src-tauri/tests/rodio_mixer_diagnostic.rs",
[["--test", "rodio_mixer_diagnostic"]],
],
[
"desktop/src-tauri/crates/buzz-terminal/tests/latency.rs",
[["--test", "latency"]],
],
])
assert.deepEqual(rustTarget(file), flags, file);
// Two integration tests keep two `--test` flags: `--test codec connection`
// would make nextest filter on a test named "connection" and repeat nothing.
const args = (...entries) =>
[...rustGroups(entries).groups.values()].map((group) => group.args);
assert.deepEqual(
args(
"crates/buzz-audio-client/tests/codec.rs::roundtrip",
"crates/buzz-audio-client/tests/connection.rs::connects",
),
[["-p", "buzz-audio-client", "--test", "codec", "--test", "connection"]],
);
assert.deepEqual(
args(
"crates/buzz-audio-client/src/wire.rs::tests::frame",
"crates/buzz-audio-client/src/jitter.rs::tests::order",
"crates/buzz-audio-client/tests/codec.rs::roundtrip",
),
[["-p", "buzz-audio-client", "--lib", "--bins", "--test", "codec"]],
);
// A custom-path file builds every target of its package; packages stay apart.
assert.deepEqual(
args(
"crates/buzz-agent/tests/regressions.rs::replay",
"crates/buzz-agent/tests/common/mod.rs::helper",
"crates/buzz-admin/src/main.rs::tests::parses",
),
[
["-p", "buzz-agent"],
["-p", "buzz-admin", "--bins"],
],
);
// The Tauri workspace's packages are addressed by its manifest, and never
// mixed into a root-workspace invocation.
const tauri = rustGroups([
"desktop/src-tauri/tests/rodio_mixer_diagnostic.rs::single_queue_serializes_sources",
"desktop/src-tauri/crates/buzz-terminal/tests/latency.rs::g3_renderer_acquire_stays_within_frame_budget",
]);
assert.equal(tauri.runner, "tauri");
assert.equal(rustWorkspaces[tauri.runner], "desktop/src-tauri/Cargo.toml");
assert.deepEqual(
[...tauri.groups.values()].map((group) => group.args),
[
["-p", "buzz-desktop", "--test", "rodio_mixer_diagnostic"],
["-p", "buzz-terminal", "--test", "latency"],
],
);
assert.equal(
rustGroups(["crates/buzz-core/src/filter.rs::tests::a"]).runner,
"rust",
);
assert.throws(
() =>
rustGroups([
"crates/buzz-core/src/filter.rs::tests::a",
"desktop/src-tauri/src/lib.rs::tests::b",
]),
/span workspaces/,
);
assert.throws(
() => rustGroups(["desktop/src/x.rs::tests::a"]),
/rust entry must be/,
);
// The root workspace's listing excludes its Postgres-backed tests by name
// and binary; nextest rejects `binary(/^postgres_/)` in the Tauri
// workspace, where no such binary exists, so that listing carries no
// exclusion.
assert.equal(
rustFilterset("rust", ["tests::a", "b", "tests::a"]),
"(test(/(^|::)tests::a$/) | test(/(^|::)b$/)) & !(test(/postgres_tests::/) | binary(/^postgres_/) | test(/(^|::)external_infra[^:]*::/))",
);
assert.equal(rustFilterset("tauri", ["a"]), "(test(/(^|::)a$/))");
assert.doesNotMatch(rustFilterset("tauri", ["a"]), /binary\(/);
});
test("a helper under tests/ selects the integration binaries that include it", () => {
const helper = "crates/x/tests/common/mod.rs";
const includes = (target, ...lines) =>
rustIncludes(target, `${lines.join("\n")}\n`, helper);
// `mod common;` in a crate root, in any form; not in a comment or string.
assert.ok(
includes("crates/x/tests/a.rs", "mod common;", "#[test] fn t() {}"),
);
assert.ok(includes("crates/x/tests/a.rs", "pub(crate) mod common ;"));
assert.ok(!includes("crates/x/tests/a.rs", "// uses mod common; elsewhere"));
assert.ok(!includes("crates/x/tests/a.rs", 'const S: &str = "mod common;";'));
assert.ok(!includes("crates/x/tests/a.rs", "mod commonplace;", "mod other;"));
// `#[path]` reaching the file itself or its directory's mod.rs.
assert.ok(
includes("crates/x/tests/a.rs", '#[path = "common/mod.rs"]', "mod shared;"),
);
assert.ok(
includes(
"crates/x/tests/a.rs",
'#[path="../tests/common/mod.rs"] mod shared;',
),
);
assert.ok(
!includes("crates/x/tests/a.rs", '#[path = "other/mod.rs"]', "mod shared;"),
);
assert.ok(
rustIncludes(
"crates/x/tests/a.rs",
'#[path = "common/mod.rs"] mod shared;\n',
"crates/x/tests/common/util.rs",
),
);
// A tests/
/main.rs target owns every file under its directory.
assert.ok(
rustIncludes(
"crates/x/tests/suite/main.rs",
"",
"crates/x/tests/suite/util.rs",
),
);
assert.ok(!rustIncludes("crates/x/tests/suite/main.rs", "", helper));
assert.ok(
rustIncludes(
"crates/x/tests/suite/main.rs",
'#[path = "../common/mod.rs"]\nmod common;\n',
helper,
),
);
// The real importers of buzz-agent's shared harness: the files declaring
// `mod common;`, not the suites that do not.
assert.deepEqual(rustImporters("crates/buzz-agent/tests/common/mod.rs"), {
importers: [
"crates/buzz-agent/tests/anthropic_continuation.rs",
"crates/buzz-agent/tests/fake_llm.rs",
"crates/buzz-agent/tests/permission_boundary.rs",
"crates/buzz-agent/tests/regressions.rs",
],
scanned: true,
});
for (const importer of rustImporters("crates/buzz-agent/tests/common/mod.rs")
.importers)
assert.match(read(`../${importer}`), /^mod common;$/m, importer);
assert.doesNotMatch(
read("../crates/buzz-agent/tests/golden_transcripts.rs"),
/^mod common;$/m,
);
// A binary's root is tests/.rs or tests//main.rs; anything else
// under tests/ is a module of one.
for (const file of ["crates/x/tests/a.rs", "crates/x/tests/suite/main.rs"])
assert.ok(rustTargetRoot(file), file);
for (const file of [
"crates/x/tests/common/mod.rs",
"crates/x/tests/suite/util.rs",
"crates/x/src/lib.rs",
"crates/x/src/main.rs",
])
assert.ok(!rustTargetRoot(file), file);
// File modules resolve by cargo's conventions against the real tree: a
// root's `mod common;` is tests/common/mod.rs, a non-root's `mod launch;`
// is /launch.rs, a `#[path]` names its file, and a declaration in a
// comment or one no file backs is left out.
assert.deepEqual(
rustModules(
"crates/buzz-agent/tests/fake_llm.rs",
"mod common;\n// mod missing;\nmod nowhere;\n",
),
[{ name: "common", file: "crates/buzz-agent/tests/common/mod.rs" }],
);
assert.deepEqual(rustModules("crates/buzz-acp/src/acp.rs", "mod launch;\n"), [
{ name: "launch", file: "crates/buzz-acp/src/acp/launch.rs" },
]);
const wrapper = "crates/buzz-acp/tests/pool_lifecycle_state.rs";
assert.deepEqual(rustModules(wrapper, read(`../${wrapper}`)), [
{ name: "pool_lifecycle", file: "crates/buzz-acp/src/pool_lifecycle.rs" },
]);
// The wrapper's tests are the included module's, under its module name:
// the names nextest lists for the pool_lifecycle_state binary.
const included = rustTests(read("../crates/buzz-acp/src/pool_lifecycle.rs"));
assert.ok(included.length > 0);
assert.deepEqual(
rustIncludedTests(wrapper, read(`../${wrapper}`)).map((test) => test.name),
included.map((test) => `pool_lifecycle::${test.name}`),
);
assert.deepEqual(
rustIncludedTests("crates/buzz-agent/tests/fake_llm.rs", "mod common;\n"),
[],
);
});
test("select, through its entry point, repeats a changed Tauri test and the binaries including a changed helper", () => {
// Reviewer probes on #8613: before, both produced an empty selection and
// every repeat lane skipped.
const tauri = selectAppending([
"desktop/src-tauri/tests/rodio_mixer_diagnostic.rs",
]);
assert.match(
tauri.summary,
/\| desktop\/src-tauri\/tests\/rodio_mixer_diagnostic\.rs \| tauri \| whole file \(shared code changed\): 3 test\(s\) \|/,
);
assert.deepEqual(tauri.outputs.rust, []);
assert.deepEqual(
tauri.outputs.tauri,
rustTests(read("../desktop/src-tauri/tests/rodio_mixer_diagnostic.rs")).map(
(test) =>
`desktop/src-tauri/tests/rodio_mixer_diagnostic.rs::${test.name}`,
),
);
assert.equal(tauri.outputs.tauri.length, 3);
// Under Actions, where the Desktop JS lane runs this suite, the selector
// routes the summary and outputs to $GITHUB_STEP_SUMMARY and
// $GITHUB_OUTPUT: the same selection arrives there, and nothing on stdout.
const routed = selectAppending(
["desktop/src-tauri/tests/rodio_mixer_diagnostic.rs"],
{ github: true },
);
assert.equal(routed.stdout, "");
assert.match(
routed.summary,
/\| desktop\/src-tauri\/tests\/rodio_mixer_diagnostic\.rs \| tauri \| whole file \(shared code changed\): 3 test\(s\) \|/,
);
assert.deepEqual(routed.outputs, tauri.outputs);
const helper = selectAppending(["crates/buzz-agent/tests/common/mod.rs"]);
assert.match(
helper.summary,
/\| crates\/buzz-agent\/tests\/common\/mod\.rs \| rust \| helper for: anthropic_continuation, fake_llm, permission_boundary, regressions: \d+ test\(s\) \|/,
);
const binaries = new Set(
helper.outputs.rust.map((entry) => entry.split("::")[0]),
);
assert.deepEqual([...binaries].sort(), [
"crates/buzz-agent/tests/anthropic_continuation.rs",
"crates/buzz-agent/tests/fake_llm.rs",
"crates/buzz-agent/tests/permission_boundary.rs",
"crates/buzz-agent/tests/regressions.rs",
]);
assert.ok(helper.outputs.rust.length > 0);
// Changed together, the helper and an importer select each test once.
const both = selectAppending([
"crates/buzz-agent/tests/common/mod.rs",
"crates/buzz-agent/tests/fake_llm.rs",
]);
assert.deepEqual(both.outputs.rust, [...new Set(both.outputs.rust)]);
assert.deepEqual(both.outputs.rust.length, helper.outputs.rust.length);
// A tests/.rs root with no test of its own is a binary, not a
// helper: buzz-acp's pool_lifecycle_state compiles src/pool_lifecycle.rs
// in by `#[path]`, and a change to it repeats that binary's tests, not its
// siblings' (before, the selection was git_bootstrap, run_task and
// stdio_contract, and pool_lifecycle_state was not repeated at all).
const wrapper = "crates/buzz-acp/tests/pool_lifecycle_state.rs";
assert.deepEqual(rustTests(read(`../${wrapper}`)), []);
const included = rustTests(read("../crates/buzz-acp/src/pool_lifecycle.rs"));
const wrapped = selectAppending([wrapper]);
assert.match(
wrapped.summary,
new RegExp(
`\\| crates/buzz-acp/tests/pool_lifecycle_state\\.rs \\| rust \\| whole file \\(shared code changed\\): ${included.length} test\\(s\\) \\|`,
),
);
assert.deepEqual(
wrapped.outputs.rust,
included.map((test) => `${wrapper}::pool_lifecycle::${test.name}`),
);
const { groups } = rustGroups(wrapped.outputs.rust);
assert.deepEqual(groups.get("buzz-acp").args, [
"-p",
"buzz-acp",
"--test",
"pool_lifecycle_state",
]);
assert.deepEqual(
groups.get("buzz-acp").tests.map(({ exact }) => exact),
included.map((test) => `pool_lifecycle::${test.name}`),
);
// A src/** file without test functions still selects nothing: the regular
// lanes run what depends on it.
const source = selectAppending(["crates/buzz-core/src/lib.rs"]);
assert.match(
source.summary,
/\| crates\/buzz-core\/src\/lib\.rs \| rust \| no test functions \|/,
);
assert.deepEqual(source.outputs.rust, []);
});
test("Rust selection: a hunk inside a test selects it, elsewhere selects every runnable test; ignored tests never run", () => {
const select = (text, options) =>
selectRust(text, rustSource, { previous: rustSource, ...options });
assert.deepEqual(select(diff([21, 1, 21, 1])), {
lines: [19],
names: ["tests::plain"],
});
// Inside an ignored test: owned, so not shared code, but nothing to run.
assert.deepEqual(select(diff([28, 0, 29, 1])), { lines: [24], names: [] });
// The code under test, a helper, or a deleted helper (attached to line 13)
// can change every test.
const whole = (reason) => ({
whole: true,
reason,
names: ["tests::plain", "tests::postgres_tests::in_nested_module"],
});
assert.deepEqual(select(diff([4, 0, 5, 1])), whole("shared code changed"));
assert.deepEqual(
select(diff([21, 1, 21, 1], [14, 0, 15, 1])),
whole("shared code changed"),
);
assert.deepEqual(select(diff([14, 3, 13, 0])), whole("shared code removed"));
assert.deepEqual(
select(diff([21, 1, 21, 1]), { added: true, previous: "" }),
whole("new file"),
);
// Tests of the file modules an integration-test root declares ride along
// with a whole selection, not with a hunk inside one of the root's own
// tests; a root with no test of its own selects only them, and one with no
// tests at all is not a test file.
const included = [
{ name: "m::a", ignored: false },
{ name: "m::b", ignored: true },
];
assert.deepEqual(select(diff([4, 0, 5, 1]), { included }), {
...whole("shared code changed"),
names: [...whole().names, "m::a"],
});
assert.deepEqual(select(diff([21, 1, 21, 1]), { included }), {
lines: [19],
names: ["tests::plain"],
});
assert.deepEqual(selectRust(diff([1, 1, 1, 1]), "mod m;\n", { included }), {
whole: true,
reason: "shared code changed",
names: ["m::a"],
});
assert.deepEqual(
selectRust(diff([0, 0, 1, 1]), "mod m;\n", { added: true, included }),
{ whole: true, reason: "new file", names: ["m::a"] },
);
assert.equal(selectRust(diff([1, 1, 1, 1]), "fn main() {}\n"), undefined);
assert.equal(cratePackage("crates/buzz-core/src/filter.rs"), "buzz-core");
});
test("hunks inside tests select those tests; shared code, new or unlisted files select the whole file", () => {
const bodies = [
{ start: 10, end: 22, line: 10 },
{ start: 25, end: 37, line: 25 },
{ start: 38, end: 52, line: 40 },
];
// The old file had the same tests, with a hook between the last two.
const previous = [
{ start: 10, end: 22 },
{ start: 25, end: 37 },
{ start: 41, end: 55 },
];
const select = (ranges, options) =>
selectTests(ranges, bodies, { previous, ...options });
// Edit inside one test, including its first and last lines.
assert.deepEqual(select([hunk(12, 14)]), { lines: [10] });
assert.deepEqual(select([hunk(10, 10)]), { lines: [10] });
assert.deepEqual(select([hunk(22, 22)]), { lines: [10] });
// A hunk across two adjacent bodies selects both, by their listed lines.
assert.deepEqual(select([hunk(36, 39)]), { lines: [25, 40] });
// A hunk that also covers the gap between tests, or lines after the last
// test, touches shared code.
for (const range of [hunk(22, 25), hunk(52, 53), hunk(400, 405)])
assert.deepEqual(select([range]), {
whole: true,
reason: "shared code changed",
});
// Several hunks accumulate distinct tests in file order.
assert.deepEqual(select([hunk(41, 41), hunk(11, 11), hunk(12, 12)]), {
lines: [10, 40],
});
// Replacing lines inside a test, or deleting some of them (attached to the
// preceding line), selects that test when the old lines were its own.
assert.deepEqual(select([hunk(30, 31, { start: 30, end: 32 })]), {
lines: [25],
});
assert.deepEqual(select([hunk(30, 30, { start: 31, end: 32 })]), {
lines: [25],
});
// Deleting the hook that followed the second test attaches to that test's
// closing line, but the removed lines were never a test's.
assert.deepEqual(select([hunk(37, 37, { start: 38, end: 40 })]), {
whole: true,
reason: "shared code removed",
});
// Deletion at the top of the file (line 0) and edits before the first test
// fall back to the whole file.
assert.deepEqual(select([hunk(0, 0, { start: 1, end: 1 })]), {
whole: true,
reason: "shared code removed",
});
assert.deepEqual(select([hunk(12, 12), hunk(3, 3)]), {
whole: true,
reason: "shared code changed",
});
assert.deepEqual(select([hunk(12, 12)], { added: true }), {
whole: true,
reason: "new file",
});
assert.deepEqual(selectTests([hunk(12, 12)], []), {
whole: true,
reason: "no tests listed",
});
// Without old-side calls, every removal is shared code.
assert.deepEqual(
selectTests([hunk(30, 30, { start: 31, end: 32 })], bodies),
{ whole: true, reason: "shared code removed" },
);
});
test("Playwright lists smoke journeys per spec once each; integration-only specs list none", () => {
const smoke = "desktop/tests/e2e/channel-sort.spec.ts";
const looped = "desktop/tests/e2e/claude-effort-picker.spec.ts";
const integration = "desktop/tests/e2e/agents.spec.ts";
// Alone, an integration spec makes Playwright exit 1 with "No tests found".
assert.deepEqual(listPlaywright([integration]), new Map([[integration, []]]));
const listed = listPlaywright([smoke, looped, integration]);
assert.deepEqual(listed.get(integration), []);
const lines = listed.get(smoke);
assert.ok(lines.length > 0, "a smoke spec must list its journeys");
assert.equal(
testBodies(testCalls(read(`../${smoke}`), smoke), lines).length,
lines.length,
"every listed journey must be owned by a test call",
);
// A looped declaration is listed once per iteration at the same line.
const loopedLines = listed.get(looped);
assert.deepEqual(loopedLines, [...new Set(loopedLines)]);
assert.ok(
/^\s*for \(const .+\) \{\n\s+test\(/m.test(read(`../${looped}`)),
"the looped spec must still declare a test inside a loop",
);
});
test("Playwright repetitions get one shard per five journeys, at most ten, and a selection past ~19 minutes per shard skips", () => {
assert.deepEqual(
[0, 1, 5, 6, 18, 40, 41, 50, 51, 200].map(playwrightShards),
[1, 1, 1, 2, 4, 8, 9, 10, 10, 10],
);
// 250 journey-runs per shard at ~4.5 s each, over ten shards.
assert.equal(MAX_PLAYWRIGHT_JOURNEYS, 250);
assert.equal(ITERATIONS, 10);
});
test("nextest's JUnit classname is the binary id its list reports", () => {
// Captured from cargo-nextest 0.9.136 on one buzz-core test: `cargo nextest
// list -p buzz-core --lib -E 'test(=kind::tests::no_duplicate_kind_values)'
// --message-format json` (trimmed to the matching testcase) and the
// target/nextest/flake/junit.xml of `cargo nextest run --profile flake` with
// the same arguments. repeatRust keys expectations as ` `
// from the list and reads statuses as ` ` from the report,
// so the two must agree.
const list = {
"rust-suites": {
"buzz-core": {
"package-name": "buzz-core",
"binary-id": "buzz-core",
"binary-name": "buzz_core",
kind: "lib",
status: "listed",
testcases: {
"kind::tests::no_duplicate_kind_values": {
kind: "test",
ignored: false,
"filter-match": { status: "matches" },
},
},
},
},
};
const junit = [
'',
'',
' ',
' ',
" ",
" ",
"",
"",
].join("\n");
const expected = Object.entries(list["rust-suites"]).flatMap(
([binary, suite]) =>
Object.keys(suite.testcases).map((name) => `${binary} ${name}`),
);
assert.deepEqual(
parseJunit(junit),
new Map(expected.map((test) => [test, "passed"])),
);
});
test("reports give per-test statuses per iteration", () => {
assert.deepEqual(
parseJunit(
[
'',
'',
'',
'',
'',
'assertion `left == right` failed',
"",
'',
"",
'',
'',
"",
"",
].join("\n"),
),
new Map([
["buzz-core filter::tests::a", "passed"],
["buzz-core filter::tests::b ", "failed"],
["buzz-core filter::tests::c", "skipped"],
["buzz-core::e2e d", "failed"],
]),
);
const event = (value) => JSON.stringify(value);
assert.deepEqual(
parseDartReport(
[
event({ type: "start", protocolVersion: "0.1.1" }),
event({
type: "testStart",
test: { id: 1, name: "loading /m/test/a_test.dart", url: null },
}),
event({
type: "testDone",
testID: 1,
result: "success",
hidden: true,
skipped: false,
}),
event({
type: "testStart",
test: {
id: 2,
name: "group renders",
url: "file:///m/test/a_test.dart",
},
}),
event({
type: "testStart",
test: {
id: 3,
name: "group fails",
url: "file:///m/test/a_test.dart",
},
}),
event({
type: "testStart",
test: { id: 4, name: "later", url: "file:///m/test/b_test.dart" },
}),
"not json",
event({
type: "testDone",
testID: 2,
result: "success",
hidden: false,
skipped: false,
}),
event({
type: "testDone",
testID: 3,
result: "error",
hidden: false,
skipped: false,
}),
event({
type: "testDone",
testID: 4,
result: "success",
hidden: false,
skipped: true,
}),
event({ type: "done", success: false }),
].join("\n"),
"/m/",
),
new Map([
["test/a_test.dart › group renders", "passed"],
["test/a_test.dart › group fails", "failed"],
["test/b_test.dart › later", "skipped"],
]),
);
// Playwright lists a --repeat-each journey once per repetition, in order.
const spec = (title, line, status) => ({
title,
file: "channels.spec.ts",
line,
tests: [{ status }],
});
assert.deepEqual(
playwrightRepetitions({
suites: [
{
specs: [spec("a", 10, "expected"), spec("a", 10, "unexpected")],
suites: [
{ specs: [spec("b", 30, "skipped"), spec("a", 10, "expected")] },
],
},
],
}),
new Map([
["channels.spec.ts:10 › a", ["passed", "failed", "passed"]],
["channels.spec.ts:30 › b", ["skipped"]],
]),
);
});
test("repeat invocations mirror the lanes they stand in for", () => {
// node:test flags come from desktop/package.json "test"; the Flutter define
// from the mobile test shards; nextest's flake profile must keep retries off.
const script = JSON.parse(read("../desktop/package.json")).scripts.test;
for (const file of ["x.test.mjs", "x.jsdom-test.mjs"])
assert.ok(script.includes(`node ${nodeTestArgs(file).join(" ")} "`), file);
assert.ok(
read("../.github/workflows/_ci-clients.yml").includes(
`scripts/mobile-test-shard.sh \${{ matrix.shard }} 3 ${dartDefine}`,
),
);
// nextest reads its config from the workspace root, so each Rust workspace
// carries the flake profile.
for (const config of [
"../.config/nextest.toml",
"../desktop/src-tauri/.config/nextest.toml",
])
assert.match(
read(config),
/^\[profile\.flake\]\n(?:#.*\n)*retries = 0\nfail-fast = false\n\n\[profile\.flake\.junit\]\npath = "junit\.xml"\n/m,
config,
);
const flake = read("../.github/workflows/_ci-flake.yml");
for (const runner of ["rust", "tauri", "node", "dart"])
assert.ok(
flake.includes(`flake-check.mjs ${runner} --iterations=10 $SELECTED`),
runner,
);
// The Tauri lane builds what the Desktop Tauri core lane does: the same
// workspace cache, system packages and sidecar stubs.
const desktop = read("../.github/workflows/_ci-desktop.yml");
const packages = (text) =>
/apt-get install -y --no-install-recommends \\\n((?: {12}\S+(?: \\)?\n)+)/.exec(
text,
)?.[1];
assert.ok(packages(desktop), "Desktop Tauri apt packages");
assert.equal(packages(flake), packages(desktop));
assert.match(
flake,
/flake-tauri:\n(?: {4}.*\n)*? {6}- uses: Swatinem\/rust-cache@\w+\n {8}with:\n {10}workspaces: desktop\/src-tauri\n {10}shared-key: desktop-core\n {10}save-if: false\n/,
);
assert.match(desktop, /- lane: core\n {12}cache: desktop-core\n/);
assert.ok(flake.includes("run: just _ensure-sidecar-stubs"));
assert.ok(read("../Justfile").includes("\n_ensure-sidecar-stubs:\n"));
assert.ok(flake.includes("--repeat-each=10 --retries=0"));
// The lanes are advisory: ci.yml calls the workflow on pull requests only,
// and no required gate depends on it.
const ci = read("../.github/workflows/ci.yml");
assert.match(
ci,
/^ {2}flake-check:\n(?: {4}.*\n)*? {4}if: github\.event_name == 'pull_request'\n(?: {4}.*\n)*? {4}uses: \.\/\.github\/workflows\/_ci-flake\.yml\n/m,
);
assert.doesNotMatch(ci, /needs: \[[^\]]*flake/);
});