A suite run in which no test passed exits non-zero and names its skips (issue 693)
mesh/merge-gate head 8635560993aa merged into trunk f9be71b9ad44 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 8635560993aa merged into trunk f9be71b9ad44 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript)…
mesh/delivery-group group fix/693-a-run-that-proved-nothing-is-not-passed ready: every member ready, and composed together they pass
mesh/delivery superseded: a newer head of the same pull request

The runner exits 0 when every test skipped, so a bed that stopped before its
subject and skipped its only test as NOT REACHED was recorded as passed, "0
passed, 0 failed". The suite now collects each skip and its reason through a
reporter of its own, prints NOT PASSED and a skipped line per skip after its
recorded line, keeps them in the receipt, and exits 1 when no test passed or a
test was not reached. A skip for a declared reason beside passes stays passed.
This commit is contained in:
jochen
2026-10-14 03:14:05 +02:00
parent f9be71b9ad
commit 8635560993
5 changed files with 345 additions and 21 deletions
+19 -3
View File
@@ -24,6 +24,7 @@ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { homedir } from "node:os";
import { dirname, join } from "node:path";
import type { Outcome, Skip } from "./outcomes.ts";
import { repositories } from "./repos.ts";
/** What a run was taken against, per repository. */
@@ -38,6 +39,11 @@ export interface Receipt {
against: Against;
/** The test files this run was pointed at. See {@link endToEnd}. */
ran: string[];
/** How many tests skipped, and each with its reason (novox/hq issue 693). Absent on older receipts. */
skipped?: number;
skips?: Skip[];
/** Why the run did not pass although nothing failed — no test passed, or one was not reached. */
notPassed?: string;
}
/**
@@ -104,6 +110,7 @@ export function record(
ran: string[],
env = process.env,
builtAgainst?: Against,
outcome?: Outcome,
): Receipt {
const receipt: Receipt = {
at: new Date().toISOString(),
@@ -115,6 +122,8 @@ export function record(
// never in the binaries. A verdict is only worth something attributed to one exact state.
against: builtAgainst ?? whatWasTested(env),
ran,
...(outcome ? { skipped: outcome.skipped, skips: outcome.skips } : {}),
...(outcome?.notPassed ? { notPassed: outcome.notPassed } : {}),
};
const path = receiptPath();
mkdirSync(dirname(path), { recursive: true });
@@ -166,9 +175,14 @@ export function judge(
const days = (now.getTime() - Date.parse(receipt.at)) / 86_400_000;
const lines: string[] = [];
// A run that proved nothing did not pass, though nothing failed (novox/hq issue 693): no test passed,
// or one skipped as not reached. A receipt from before the field is judged by its count alone.
const notPassed = receipt.failed > 0 ? "" : receipt.notPassed ?? (receipt.passed === 0 ? "no test passed" : "");
const outcome = receipt.failed > 0
? `last ran ${ago(days)} and ${receipt.failed} test(s) failed`
: `last passed ${ago(days)}, ${receipt.passed} test(s)`;
: notPassed
? `last ran ${ago(days)} and did not pass: ${notPassed}`
: `last passed ${ago(days)}, ${receipt.passed} test(s)`;
lines.push(`the end-to-end suite ${outcome}`);
let moved = false;
@@ -192,7 +206,9 @@ export function judge(
lines.push(` That run did not include ${endToEnd}, so it raised no machines.`);
}
if (receipt.failed > 0) {
for (const s of receipt.skips ?? []) lines.push(` skipped: ${s.test} — ${s.reason || "no reason given"}`);
if (receipt.failed > 0 || notPassed) {
lines.push(" Nothing has been proven end to end since.");
} else if (moved) {
lines.push(" What it proved was proven about code that has since changed.");
@@ -201,7 +217,7 @@ export function judge(
}
return {
current: endToEndRan && receipt.failed === 0 && !moved && days <= staleAfterDays,
current: endToEndRan && receipt.failed === 0 && !notPassed && !moved && days <= staleAfterDays,
lines,
};
}