From 1d4898496fcd58ba71096bf82bfb2b704159e33f Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 7 Nov 2026 22:49:03 +0000 Subject: [PATCH] acceptance: a failed check prints the serial log where it looked (#136) Run 197's riscv64 failure (#131) could only be read from the run's logs artifact, which Gitea's API does not list. qemu_session.py's check() now prints, on a FAIL, the serial lines after the first match of an anchor the check names, or the log's last lines; control characters as escapes, so a stray byte or terminal sequence in a line shows instead of only failing a match. The boot-disk checks anchor on 'Boot successful!'. A passing run prints as before (riscv64 here, logs/ committed). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2 --- .../runs/doe-riscv64-20261107-224732.csv | 0 .../riscv64/qemu-riscv64-20261107-224732.log | 3 ++ scripts/qemu_session.py | 32 ++++++++++++++++--- 3 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 experiments/bare_metal/runs/doe-riscv64-20261107-224732.csv create mode 100644 logs/20261107-224732/riscv64/qemu-riscv64-20261107-224732.log diff --git a/experiments/bare_metal/runs/doe-riscv64-20261107-224732.csv b/experiments/bare_metal/runs/doe-riscv64-20261107-224732.csv new file mode 100644 index 00000000..e69de29b diff --git a/logs/20261107-224732/riscv64/qemu-riscv64-20261107-224732.log b/logs/20261107-224732/riscv64/qemu-riscv64-20261107-224732.log new file mode 100644 index 00000000..659595e9 --- /dev/null +++ b/logs/20261107-224732/riscv64/qemu-riscv64-20261107-224732.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08448453d06f8ce28ede962fda83eaff8a0fe3a24535d856f62ce2fcd09b7f4a +size 7993 diff --git a/scripts/qemu_session.py b/scripts/qemu_session.py index ec667e96..31867d9d 100755 --- a/scripts/qemu_session.py +++ b/scripts/qemu_session.py @@ -213,9 +213,33 @@ def wait_for(pat, secs, after=0): return False -def check(name, cond): +def excerpt(near=None, after=20, tail=40): + """The serial log where a failed check looked (#136): the lines after the first match of + `near`, else the log's last lines -- control characters shown as escapes, so a terminal's + sequences cannot garble the job's output.""" + lines = text().splitlines() + start = None + if near is not None: + start = next((i for i, l in enumerate(lines) if re.search(near, l)), None) + if start is None: + start, what = max(0, len(lines) - tail), f"the log's last {tail} lines" + stop = len(lines) + else: + stop, what = min(len(lines), start + 1 + after), f"after /{near}/, line {start + 1}" + print(f" --- serial log, {what} ({os.path.relpath(log, ROOT)}):", flush=True) + for i in range(start, stop): + shown = "".join(c if c.isprintable() else repr(c)[1:-1] for c in lines[i]) + print(f" {i + 1:6} | {shown}", flush=True) + print(" ---", flush=True) + + +def check(name, cond, near=None): + """PASS or FAIL; a FAIL also prints the serial log where the check looked: after the first + line matching `near` (a regex), when it is given, else the log's end.""" global ok print(f"{'PASS' if cond else 'FAIL'}: {name}", flush=True) + if not cond: + excerpt(near) ok = ok and cond @@ -328,7 +352,7 @@ if m == "headless": elif m == "zuse": check("Boot disk: not a StarshipOS stick (an ISO or ESP boot)", - re.search(r"Boot disk: not a StarshipOS stick", text()) is not None) + re.search(r"Boot disk: not a StarshipOS stick", text()) is not None, near=r"Boot successful!") check("Zuse: identity confirmed", wait_for(r"Zuse: identity confirmed from attached thumbdrive", 120)) check("[zuse@Hera] ok>", wait_for(PROMPT, 60)) n = count(PROMPT) @@ -343,7 +367,7 @@ elif m in ("stick_boot", "two_sticks"): if m == "stick_boot": # the PC stick: Artemis is the rest of 2 GiB (well over 1 million user blocks), its blank # identity 500 MiB (about 380000) - check("Boot disk: a StarshipOS stick", re.search(r"Boot disk: a StarshipOS stick", text()) is not None) + check("Boot disk: a StarshipOS stick", re.search(r"Boot disk: a StarshipOS stick", text()) is not None, near=r"Boot successful!") check("System: StarForth v4", re.search(r"System: StarForth v4", text()) is not None) check("Artemis: the stick's partition attached", wait_for(r"Artemis: the stick's partition attached", 120)) @@ -370,7 +394,7 @@ elif m in ("stick_boot", "two_sticks"): elif m in ("stick_artemis", "stick_zuse"): if m == "stick_zuse": - check("Boot disk: a StarshipOS stick", re.search(r"Boot disk: a StarshipOS stick", text()) is not None) + check("Boot disk: a StarshipOS stick", re.search(r"Boot disk: a StarshipOS stick", text()) is not None, near=r"Boot successful!") check("Artemis: the stick's partition attached", wait_for(r"Artemis: the stick's partition attached", 120)) check("Zuse: identity confirmed", wait_for(r"Zuse: identity confirmed from attached thumbdrive", 120)) check("[zuse@Hera] ok>", wait_for(PROMPT, 60))