qemu: json/stat/region channels, seeding, driver tests

- rootfs/sbin/init: the control bridge answers `@cat PATH` locally so the
  driver can read /tmp/warden-web-status.json out of the guest. That file
  has no trailing newline; the bridge adds one so the sentinel stays on
  its own line and the line-based reader never blocks.
- tests/qmp.py: wait_json/assert_json (dotted paths, eq/ne/contains/
  len_eq/len_ge/gt/lt), assert_stat off the FIFO's stats reply,
  capture_region, assert_region NAME [TOLERANCE] and assert_ocr. A
  tolerance other than the captured one, a reference box that does not
  fit the screendump, a missing reference or a missing tesseract is FATAL
  for that step and the run continues (flare-edge #147).
- tests/imgtools.py: P6 reader, crop, perceptual and structural hashes,
  compare, with a self-test.
- tests/test_qmp_drive.py: drive() with QMP and the control channel
  faked, pinning the per-step ok/fail/fatal contract.
- mkimage.sh SEED_DIR and ui-drive.sh --seed/--refs: settings fixtures
  staged into userdata before warden-ui starts, and a reference store
  handed to the driver.
- ci: the driver tests and the imgtools self-test run in qemu-tools.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013aHKWzT5EF86RFKRMtAv9n
This commit is contained in:
Noah
2026-09-07 23:26:01 -06:00
co-authored by Claude Fable 5.1
parent 8a57057053
commit 86a9544dcc
7 changed files with 1140 additions and 27 deletions
+19 -2
View File
@@ -15,8 +15,14 @@
# flare-edge build's rootfs.img/oem.img matched pair) into slot A instead of
# the busybox skeleton; slot B keeps the skeleton as a known-good fallback.
# Env:
# BUSYBOX path to a local busybox binary (skips the download; still verified)
# OUT output dir (default: qemu/out); image at $OUT/disk.img
# BUSYBOX path to a local busybox binary (skips the download; still verified)
# OUT output dir (default: qemu/out); image at $OUT/disk.img
# SEED_DIR a directory of pre-built userdata/warden files (one file per
# settings key, e.g. flare-edge tools/seed-fixtures.py's output)
# copied in VERBATIM, after --portal-url/--state. Lets a caller
# seed an arbitrary key set -- a flow spec's setup.seed can name
# anything settings.c reads -- without growing --state into a
# multi-value flag; last one written wins, same as --state.
set -euo pipefail
QEMU_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -85,6 +91,17 @@ for kv in ${STATE_KV[@]+"${STATE_KV[@]}"}; do
printf '%s\n' "${kv#*=}" > "$UDATA/warden/${kv%%=*}"
done
# SEED_DIR: a pre-built set of settings files (see the Env note above), copied
# in whole rather than re-parsed here -- seed-fixtures.py already wrote them
# in the exact format settings.c reads (filename = key, mode 0600 for a
# secret), so re-deriving that here would be a second place to keep in sync
# with settings.c. Applied after --state so a seeded file can override a
# same-named --state value; `cp -a` preserves the 0600 on a secret entry.
if [ -n "${SEED_DIR:-}" ]; then
[ -d "$SEED_DIR" ] || { echo "FATAL: SEED_DIR '$SEED_DIR' is not a directory" >&2; exit 1; }
cp -a "$SEED_DIR"/. "$UDATA/warden/"
fi
mkdir -p "$SCRATCH/empty"
# mkfs an ext4 partition image of exactly $2 bytes from staged dir $1.