lab: read the suite's verdict only after its recorded line, NOT REACHED in any case (issue 693, review 254)
mesh/merge-gate head acf8a76646de merged into trunk ebf7f3773dc7 — pass: builds lab → shanks; no bus step; every machine composes with the change as it did…
mesh/repo-check head acf8a76646de merged into trunk ebf7f3773dc7 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
mesh/delivery-group group fix/693-a-run-that-proved-nothing-is-not-passed delivered: every member is delivered

A test's own output, printed before the suite reports, could carry a line
shaped like the suite's NOT PASSED or skipped line and stand for its verdict.
This commit is contained in:
jochen
2026-10-14 03:28:20 +02:00
parent c8255eb15d
commit acf8a76646
2 changed files with 35 additions and 3 deletions
+24
View File
@@ -462,3 +462,27 @@ test("a run that passed tests and skipped one for a declared reason stays passed
test("a run that ran no test at all is not passed", () => {
assert.equal(outcomeOf("ℹ tests 0\nℹ pass 0\nℹ fail 0\nℹ skipped 0\n").notPassed, "no test passed: none ran");
});
// Review 254 on issue 693: a test's own output, printed before the suite reports, is not the suite's verdict.
test("a line a test printed that looks like the suite's is not read as the suite's", () => {
const log = [
"NOT PASSED: a test printed this",
"skipped: a forged skip (test/integration/x.test.ts) # FORGED NOT REACHED",
"✔ a module lands (12ms)",
"ℹ tests 1", "ℹ pass 1", "ℹ fail 0", "ℹ skipped 0",
"",
"recorded: test/integration/x.test.ts — 1 passed, 0 failed, 0 skipped, against mesh-lab 7ed7da1",
].join("\n");
const o = outcomeOf(log);
assert.equal(o.notPassed, undefined, JSON.stringify(o));
assert.deepEqual(o.skips, []);
});
test("not reached is matched in any case", () => {
const log = [
"✔ a module lands (12ms)",
"﹣ the broker step (0.1ms) # the broker step was not reached",
"ℹ tests 2", "ℹ pass 1", "ℹ fail 0", "ℹ skipped 1",
].join("\n");
assert.equal(outcomeOf(log).notPassed, "1 test(s) skipped as NOT REACHED: their subject was not reached");
});
+11 -3
View File
@@ -339,8 +339,15 @@ export function outcomeOf(log: string): Outcome {
if (failed !== undefined) outcome.failed = failed;
if (skipped !== undefined) outcome.skipped = skipped;
// **The suite's own lines are read only after its last "recorded:" line** (novox/hq issue 693, review
// 254): a test prints what it likes before the suite reports, and a line of its that looked like the
// suite's — "NOT PASSED: …", "skipped: … # NOT REACHED" — would otherwise stand for the suite's verdict.
const recorded = [...plain.matchAll(/^recorded: .*$/gm)];
const lastRecorded = recorded[recorded.length - 1];
const suiteSaid = lastRecorded ? plain.slice((lastRecorded.index ?? 0) + lastRecorded[0].length) : "";
// The suite's own line, "skipped: <test> (<file>) # <reason>": the file in brackets, a .ts, parts it.
for (const m of plain.matchAll(/^skipped: (.*?) \(([^()]+\.ts)\) # (.*)$/gm)) {
for (const m of suiteSaid.matchAll(/^skipped: (.*?) \(([^()]+\.ts)\) # (.*)$/gm)) {
outcome.skips.push({ file: m[2] ?? "", test: m[1] ?? "", reason: m[3] === "no reason given" ? "" : m[3] ?? "" });
}
if (outcome.skips.length === 0) {
@@ -351,11 +358,12 @@ export function outcomeOf(log: string): Outcome {
}
}
const said = plain.match(/^NOT PASSED: (.*)$/m);
const said = suiteSaid.match(/^NOT PASSED: (.*)$/m);
if (said) {
outcome.notPassed = (said[1] ?? "").trim();
} else if ((failed ?? 0) === 0) {
const unreached = outcome.skips.filter((s) => s.reason.includes("NOT REACHED")).length;
// In any case: "not reached" says the same as "NOT REACHED".
const unreached = outcome.skips.filter((s) => s.reason.toUpperCase().includes("NOT REACHED")).length;
if (passed === 0) {
outcome.notPassed = outcome.skips.length > 0 || (skipped ?? 0) > 0
? `no test passed: ${skipped ?? outcome.skips.length} skipped${unreached > 0 ? `, ${unreached} of them as NOT REACHED` : ""}`