Harnesses: the null-sink cases other tests prove leave

The grblHAL harnesses keep what only a running controller shows; the
cases a host unit test in grblHAL-glowforge proves, and the log and
reply wording, leave.

- laser_lifecycle_test: test_arm_waits_for_the_fans goes. laser_arm_test
  case A4 (grblHAL-glowforge) proves the fan grace stretching the arm's
  acknowledgment budget, on the same arm path. The docstring names it.
- xy_mode_test: case_x32, case_invalid and the log checks go; the
  default, x16 and the low tick stay. xy_scale_test holds each mode's
  tick and the not-a-mode parse.
- planner_blocks_test: one restart, at $398 1000; the 400 pass and the
  fresh-store probe go.
- ctlport_test: test_poll_lf and test_crlf go (the CR LF poll stays); in
  test_sender_out the network turn-away goes (serial_test holds it), and
  the busy gating, the grant and the outlives-client part stay.
  test_port_dark is whole.
- mcode_test: the timeout case runs on the host build's 5 s
  GFMCODE_WAIT_S (grblHAL-glowforge's CI configures it; the image keeps
  30 s) and still shows the hold, at 5.1 s; 5 s outlasts the 3 s
  test_dark watches the wait; test_table refuses one form per
  parser branch. test_timeout_holds and test_dark stay.
- envelope_test: test_unset and test_bounds go (lens_home_test holds the
  clamp); test_armed and the open, apply and close cases stay.
- manual_home_test: the out-of-range offset block goes, a repeat of
  gfhome_clamp_home_mm (lens_home_test); the modal-M3 FIRE check and
  every release and energize case stay.
- cool_report_test: one non-hex secret and one CR LF injection stay;
  runner-malformed and the exact header-set checks go. A bad secret is
  never sent and a good one is.
- tray_test: test_offset keeps 20 mm; test_errors keeps a bad P and a
  bad axis; the frame shift, envelope, barrier, persistence, port op,
  unreferenced lens and camera home stay.

Proof: with grblHAL-glowforge's matching commit, the eleven harnesses
its CI runs pass under Linux against the null-sink build: nine on a
build with a 1 s M-code wait, and mcode_test and tray_test, the two
that wait at an M-code, on the 5 s build CI configures
(-DGFMCODE_WAIT_S=5.0). pyflakes reports nothing in the nine changed
files.

Push this before grblHAL-glowforge's commit.

Acceptance: none; host harnesses only.
This commit is contained in:
ScottW514
2026-10-13 21:20:31 -04:00
parent 520d62ac05
commit 8e1fe5090d
9 changed files with 66 additions and 216 deletions
+10 -39
View File
@@ -11,15 +11,15 @@ never takes them typed: $100/$101, the machine tick, and the kernel stop
ramp. This harness drives the native grblHAL_glowforge binary in
null-sink mode (no hardware, no root) over TCP, one process per config:
1. no key: x32, $100/$101 = 213.333, the 112640 Hz tick
2. xy_microsteps = 16: 106.667, the 56320 Hz tick; a typed $100 is
overwritten on the spot; $110 is left alone under a tick that
carries it
3. xy_microsteps = 32: 213.333, the 112640 Hz tick
4. a value that is not a mode: x32 with a warning in the log
5. GFSINK_RATE lowered under the mode's tick: $110/$111 are held at
1. no key: x32, $100/$101 = 213.333, and $110 stands
2. xy_microsteps = 16: 106.667; a typed $100 is overwritten on the
spot; $110 is left alone under a tick that carries it
3. GFSINK_RATE lowered under the mode's tick: $110/$111 are held at
the feed the tick carries, one step per tick per axis
Each mode's derivations, and a value that is not a mode, are the host
unit test's (xy_scale_test.c in grblHAL-glowforge).
The binary keeps its settings in EEPROM.DAT in the working directory, so
each run starts from defaults in a temporary directory and leaves
nothing behind.
@@ -142,10 +142,6 @@ def expect_scale(s, mode, spm):
"%s is %.3f at x%d, want %.3f" % (key, got, mode, spm))
def expect_log(log, needle, what):
check(needle in log, what, "the log does not say %r:\n%s" % (needle, log))
def case_default():
s = Session()
try:
@@ -154,8 +150,7 @@ def case_default():
check(near(rate, 12000.0), "$110=%.0f stands at x32" % rate,
"$110 is %.3f at x32, want 12000" % rate)
finally:
log = s.close()
expect_log(log, "x32 microsteps, 112640 Hz machine tick", "no key: x32, the 112640 Hz tick")
s.close()
def case_x16():
@@ -172,27 +167,7 @@ def case_x16():
check(near(rate, 12000.0), "$110=%.0f stands under a tick that carries it" % rate,
"$110 is %.3f under x16, want 12000" % rate)
finally:
log = s.close()
expect_log(log, "x16 microsteps, 56320 Hz machine tick", "x16: the 56320 Hz tick")
def case_x32():
s = Session("xy_microsteps = 32\n")
try:
expect_scale(s, 32, 213.333)
finally:
log = s.close()
expect_log(log, "x32 microsteps, 112640 Hz machine tick", "x32: the 112640 Hz tick")
def case_invalid():
s = Session("xy_microsteps = 24\n")
try:
expect_scale(s, 32, 213.333)
finally:
log = s.close()
expect_log(log, "xy_microsteps '24' is not 8, 16 or 32", "a value that is not a mode is refused with a warning")
expect_log(log, "x32 microsteps, 112640 Hz machine tick", "and the machine runs at x32")
s.close()
def case_low_tick():
@@ -211,9 +186,7 @@ def case_low_tick():
check(near(got, 562.5, 0.05), "a typed $110=6000 comes back held at %.3f" % got,
"a typed $110=6000 stuck at %.3f under a 1000 Hz tick" % got)
finally:
log = s.close()
expect_log(log, "1000 Hz machine tick", "GFSINK_RATE took the tick to 1000 Hz")
expect_log(log, "held at the ceiling", "the hold is logged")
s.close()
def main():
@@ -221,8 +194,6 @@ def main():
fail("no controller binary at %s" % BIN)
case_default()
case_x16()
case_x32()
case_invalid()
case_low_tick()
print("PASS xy_mode_test")