Files
forgefirm/forgetest
ScottW514 0f116c8b7f forgetest: a resume is proven by leaving the hold, and status polling no longer eats [MSG:] lines
Bench (image 20260817124714): motion.button-hold-resume FAILED on a machine
that did exactly the right thing. The operator paused about 7 s into an 8 s
move, so the resume had a fraction of a second of travel left; the job was
already Idle by the next poll and the test - which insisted on catching the
Run state - called it "the second press did not resume the job". Its own
evidence contradicted the verdict: the kernel counters read 2133 = 40.000 mm,
the whole move, and the position check two lines below would have passed.

Catching the state a command moves INTO is a race whenever the remaining work
is short. What proves the press was acted on is the job LEAVING the hold, so
that is what the test waits for now (wait_left_state), with the state it left
into required to be Run or Idle - not Alarm or Door - and the existing "landed
on its target" check still doing the real work. laser.pause-resume-lid-cancel
had the same shape and gets the same treatment.

Second defect, visible in the same record as "message seen: False": the driver
DOES report "button pressed - job paused", but Grbl.status_report() began by
discarding the read buffer and then kept only what followed the report, so
every asynchronous [MSG:] line that landed during a poll was thrown away. It
now consumes status reports only - stale ones included, which is what that
discard was for - and leaves everything else for drain(). Tests that assert on
what the controller said open their window with an explicit drain() before the
prompt, so the text they judge is the text from the action.
2026-08-17 09:21:14 -04:00
..

forgetest - the ForgeFIRM release acceptance tool

The daemon behind http://<machine>:8090/ on the dev image: runs the acceptance catalog against the machine, keeps the append-only result log, decides which results still apply to the image that is running, exports the release artifact scripts/release.sh gates on, and serves the bench diagnostics page. The contract - catalog, campaigns, fingerprints, inheritance, the gate, the coverage rule - is docs/ACCEPTANCE.md.

Run the host tests

cd forgetest
python3 -m unittest discover -s tests -v

Run the daemon on a workstation (against a mock or a manifest file)

FORGETEST_DATA=/tmp/ft FORGETEST_MANIFEST=../tree-manifest.json \
FORGECTRL_URL=http://<machine>:8080 python3 -m forgetest --port 8090

scripts/manifest-from-tree.py produces tree-manifest.json from the recipe pins; the coverage lint is python3 -m forgetest.coverage --manifest ....

Environment

Variable Default Purpose
FORGETEST_DATA /data/forgetest results.jsonl, bench.jsonl, token, export/
FORGETEST_MANIFEST /etc/forgefirm-manifest.json the image manifest
FORGETEST_PORT, FORGETEST_HOST 8090, 0.0.0.0 listener
FORGETEST_BENCH_DIR /usr/share/forgetest/bench the installed bench scripts
FORGETEST_BENCH_DATA <FORGETEST_DATA>/bench passed to bench tools: where they keep their data files (with GF_HOST=127.0.0.1 and the panel token in GF_TOKEN)
FORGETEST_MARKER /run/forgetest.active takeover marker
FORGECTRL_URL, FORGECTRL_TOKEN_FILE http://127.0.0.1:8080, /data/forgefirm/panel.token forgectrl client
GF_SYSFS_ROOT /sys/glowforge/ kernel module sysfs
GRBL_HOST, GRBL_PORT 127.0.0.1, 23 Grbl TCP

Adding a test

Register it in the subsystem module under forgetest/suite/ with @test(...): id subsystem.name, kind, hardware, covers, requires, always, steps. The body gets a Context (log, check, fail, prompt, confirm, instruct, sleep, evidence, forgectrl, sysfs, grbl, takeover). Return normally for PASS, raise runner.Failed for FAIL. Then run the unit tests and the coverage lint.