Files
forgefirm/forgetest/forgetest/__main__.py
T
ScottW514 c0f53a865f forgetest: the release acceptance tool and the bench diagnostics page
A stdlib-only daemon on the dev image (HTTP :8090) that runs the acceptance
catalog against the machine from a self-contained page, keeps the append-only
result log under /data/forgetest, and exports the release artifact the gate
reads. Tests declare kind (auto / operator / live), hardware (api / takeover),
coverage globs, prerequisites, and core membership; a test's domain
fingerprint is the hash of the manifest files its globs select plus the
platform and its own implementation, so a PASS stays valid exactly while
nothing it covers changed. Campaign rules: a FAIL ends the campaign, the core
(image health, kernel latch and drills, one live emission witness) is never
inherited, invalidate-all forces a full campaign, no SKIP. Live tests need the
operator acknowledgment and the physical arm press through the controller;
takeover tests stop forgectrl for the duration with a crash-recoverable
marker; the tool never touches the laser latch.

Catalog v1: 24 tests ported from the proven bench drills with their recorded
pass criteria (image, kernel K1-K3 and fire A/B/U, forgectrl API and logs,
motion incl. dead-man, cooling, live laser, camera, update, cloud). The bench
tab lists every scripts/bench tool and runs the board-side ones as
subprocesses (takeover tools wrapped). 44 host unit tests, including the gate
verification fixtures. Installed only by forgefirm-image-dev, with the bench
scripts under /usr/share/forgetest/bench.
2026-08-15 15:57:11 -04:00

70 lines
2.2 KiB
Python

"""forgetest daemon entry point.
python3 -m forgetest [--port 8090] [--host 0.0.0.0] [--manifest PATH]
Environment: FORGETEST_DATA (state directory, default /data/forgetest),
FORGETEST_MANIFEST, FORGETEST_BENCH_DIR, FORGETEST_MARKER, and the hw.py
overrides.
"""
import argparse
import os
import signal
import sys
import threading
from . import VERSION
from . import bench as _bench
from . import catalog as _catalog
from . import manifest as _manifest
from . import server as _server
from .log import Log, data_dir
from .runner import Runner
def main(argv=None):
ap = argparse.ArgumentParser(prog="forgetest")
ap.add_argument("--port", type=int, default=int(os.environ.get("FORGETEST_PORT") or 8090))
ap.add_argument("--host", default=os.environ.get("FORGETEST_HOST") or "0.0.0.0")
ap.add_argument("--manifest", default=None)
ap.add_argument("--version", action="version", version="forgetest %s" % VERSION)
args = ap.parse_args(argv)
try:
manifest = _manifest.Manifest.load(args.manifest)
except (OSError, ValueError) as e:
print("forgetest: cannot load the image manifest: %s" % e, file=sys.stderr)
return 2
registry = _catalog.load_suite()
os.makedirs(data_dir(), exist_ok=True)
log = Log()
bench = _bench.Bench()
runner = Runner(log, manifest, registry, bench)
token = _server.load_token()
app = _server.App(runner, token)
srv = _server.make_server(app, args.host, args.port)
stop = threading.Event()
def on_signal(*_):
stop.set()
signal.signal(signal.SIGTERM, on_signal)
signal.signal(signal.SIGINT, on_signal)
print("forgetest %s: %d tests, image %s (%s), listening on %s:%d"
% (VERSION, len(registry), manifest.version, (manifest.content_sha or "")[:12],
args.host, args.port), file=sys.stderr, flush=True)
for m in runner.messages:
print("forgetest: %s" % m, file=sys.stderr, flush=True)
th = threading.Thread(target=srv.serve_forever, name="forgetest-http", daemon=True)
th.start()
try:
while not stop.is_set():
stop.wait(1.0)
finally:
srv.shutdown()
srv.server_close()
return 0
if __name__ == "__main__":
sys.exit(main())