mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-10-16 01:29:55 -07:00
cfa6557847b276424fe366ea58ca1e22ccdcb207
24
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
520d62ac05 |
forgetest tests: the confirmed trims of the tool's own unit tests
The cases that tested a mock, a timer, a wording or a table, or that
replayed a judgment a host test already holds, leave forgetest/tests.
What catches a false PASS, keeps the bench safe, or guards the gate
stays.
- test_baseline: the fake sysfs tree is a mixin with no test_ methods,
so ExtensionFreeTests and BaselineModeTests no longer re-run
BaselineTests' 25 cases (133 runs, now 45). CoolPublishTests, the
lamp-fade cases and TransientNotLeftoverTests go; PositionDeadbandTests
keeps a reading past the band and an unreadable reading, both never
excused. Both latch-relock cases stay.
- test_fixture: MdnsTests, the three timing cases, the probe's log
wording, and nine prompt-routing variants go. A live test never runs
unattended, the arm-press opt-in, the short-press edge, the presence
press handing over the arm press, and the arm notice dropping at the
press stay (35 cases, now 21).
- test_cloud_suite: three false-PASS guards stay: the latch open at a
dark print's button, a lid stop that is not edge-driven, the kernel
refusing the resume. The guards for the latch unlocking in the hold
and a paused print resuming left with their catalog tests in
|
||
|
|
6a434bbf77 |
forgetest: nineteen tests a host test already proves leave the catalog
The catalog is for what needs the machine or an operator. Each test below asserted software rules that a host test in CI already proves, read assertion by assertion next to that host test before it left; the record of that reading is kept with the audit. Safety rules (each approved for deletion by the owner on that reading): - laser.disarm-in-hold: laser_lifecycle_test test_resume_after_grace. - laser.arm-wait-lid: laser_arm_test E-G, laser_lifecycle_test test_lid_open_in_wait. - laser.port-dark: ctlport_test test_port_dark (port jogs, a long jog and its cancel, four injected words, the panel ops: no FIRE tick under an open window with M3 modal). - motion.respawn-gate: super_test A and D. - motion.interlock-cancel-home: the lifecycle loop cases, laser_arm_test G. - motion.lid-policy-hold: laser_lifecycle_test test_lid_policy_hold. - cloud.lid-during-button-wait, cloud.dark-print, cloud.verdict-refuse, cloud.paused-lid-cancel: test_machine_lid_button.py. - cooling.fan-duty-readback: cool_fanwrite_test. - cooling.intake-one-fan: cool_intake_test A-E. - exthost.hold-pause-tier: holdkeep_test, run_test.py, cool_flow_test P, holds_test. Other rules: - homing.cloud-offsets: manual_home_test [own-offsets], test_gfhome_motion; the real session stays in cloud.mode-switch. - exthost.core-range: version_test, install_test. - exthost.operator-destinations: run_test.py, netrules_test, install_test, extpkg_test. - exthost.lifecycle: run_test.py's ext.shutdown read before the stop. - exthost.mcode: the null-sink mcode_test, forgectrl mcode_test, sdk_test, install_test. - motion.soft-limits: z_envelope_test's homed cases, which now assert no step before the alarm, and envelope_test. - cooling_intake.py, extcore.py, extdest.py, extlife.py and extmcode.py go whole; homeoff.py keeps only the camera-home helpers that cloud.mode-switch, setup.check-envelope and their meta-tests use. - The helpers only the removed tests used go with them. - exthost.armed-freeze still runs the cloud suite's dark-print helper, whose latch checks still run; its description says so. It now requires cooling.floor-and-warm-up, the dark print's own prerequisite, in place of the removed cloud.dark-print. - HOST_TESTED: forgectrl src/holds.* (holds_test) and src/mcode.* (mcode_test), whose only catalog home left with these tests. - The meta-tests that replayed the removed tests go: test_cooling_intake_suite.py, the cloud replay cases and their fixture. The one that catches a false PASS stays and runs the dark print as armed-freeze does: a latch open at the button fails. - Text that named a removed test (exthost.motion-job's description and comment, test_baseline's docstring) no longer does. Proof: the coverage lint on a manifest from the recipe pins reports 0 uncovered paths and 0 host-tested row problems over 111 tests (its one failure is setup.check-lens's src/lensfind.* entry, which the forgectrl pin bump clears); pyflakes adds no finding; the gate self-check refuses an empty artifact cleanly; the forgetest host suite passes under Linux (547 tests); the catalog loads with 111 tests after armed-freeze's description edit. Acceptance: the catalog hash moves, so the next image opens a new campaign. Every remaining test in laser.py, motion.py, cloud.py, cooling.py, exthost.py, homeoff.py and bedsize.py moves its fingerprint (their modules' shared text changed); exthost.armed-freeze moves by its requires. |
||
|
|
4c5dde2549 |
forgetest: the burns, the lens and the intake checks follow forgectrl's review fixes
- baseline: the lid lamp fades to a new level (the kernel's smooth trigger), so the hand-back reads it until it holds the wanted level twice in a row, for up to 3 s, before it is a leftover. motion.deadman failed its hand-back reading 232 against 236 0.05 s after the supervisor re-applied 236 to a respawned controller. - setup.check-lens and setup.focus: grblHAL reads the lens window and the edge only at its start, and their restore came after the wizard had restarted the controller on the test's values. Each test now waits for the step to end, puts the settings back, and restarts the controller (stop, start, running and verified with a new pid) on every exit path; cloud mode restarts nothing. - setup.focus and setup.floor: the served program ends with G0 Z<edge>, G0 X0 Y0, G92.1, M2; setup.floor requires setup.focus and checks the scrap's thickness against the lens reach; the burns cover lenshome. - setup.check-intakes: the two assertions that could not fail are gone; an intake-count prompt fails the test unanswered (the bench reference's intakes both turn). The precheck's refusal names no setting. - setup.check-airflow: the machine's count rules (intake 2 turning writes 2; silent on a one-fan machine, the count is not written). Shared text: setup_dark.py's precheck string (all setup_dark tests, setup_burns, setup.check-envelope), setup_burns.py's imports, covers, docstring and preview check, cooling_intake.py's docstring; every test they move covers paths forgectrl changed in the same work. Proof: the forgetest host suite passes (548), with new host tests for the lamp's fade, the restarts on pass, fail and abort, the floor's Focus refusal and reach, the tail without G92.1, the intakes prompt, and the airflow count rules; pyflakes is clean on the changed modules. Acceptance: these are the tests. |
||
|
|
8dc7c0bc56 |
forgetest: the stale boot reference judged at a held uptime, on both sides of the limit
test_stale_preconfig_reference_is_retaken_on_a_fresh_boot read the host's
uptime after boot_reference() had read it to decide, and expected the
decision its own reading implied. On a CI runner the uptime crossed
BOOT_MAX_AGE_S (600 s) between the two readings: boot_reference retook
the reference and the test expected the stale one (forgetest-ci on
|
||
|
|
1a306d5d8d |
forgetest: a campaign's machine is extension-free
An extension package is software the image does not carry, and a result taken beside one is not a result about the image. Two places hold the line. The baseline (_ext_side, in every pre and post pass): a package under the tests' own prefix (org.forgetest.) and an owner key named forgetest-*.pub are what a test made and left behind; they are removed (forgeext remove, the key's file), recorded as restored, and the host stops the service on its next turn. A process that still runs under a pool account after that belongs to the operator's own packages: it is recorded as unrestorable and never touched. An installed package that does not run is nobody's leftover. hw.pool_pids() reads each process's Uid line; hw.ext_packages() lists the package directory. image.health (5b): the extension host is one process (/usr/bin/forgeext run), its start link sorts after forgectrl's and its kill link before it, no package is installed, and nothing runs under a pool account. Proven. test_baseline.py, ExtensionFreeTests, over a stand-in forgeext and a fake package tree: an installed package that does not run leaves nothing; a test's package and key are removed and the operator's package and key stay; a removal that fails says so in the host's own words; running extensions are reported and left alone. The unit suite passes (451 tests, 0 undefined names). The link order image.health asks for is the one the built root filesystems of image 20260921014201 have: S90forgectrl before S91forgeext, K09forgeext before K90forgectrl. Acceptance. image.health gains forgeext's init/** in its covers map; it runs first in every campaign and is the on-image proof of 5b. The baseline is harness, outside the suite and outside every fingerprint. |
||
|
|
888b47abe8 |
forgetest: the hand-back never moves the head across a lost counter frame
Seen on the bench reference, twice in one session: at the end of a passing run the hand-back jogged the head 30 mm into the back-left stop blocks, from a head that had not moved. The baseline compares the kernel's step counters at the end of a run with the start and jogs the head back by the difference. The GRBL controller zeroes those counters at every start (the lens's startup reference) and at every home (home_completed()), and rewrites its anchor, /run/grblhal.homed, each time. Across either event the difference between two counter readings is not a distance the head traveled. It stayed hidden because the counters normally read zero between tests. homing.manual broke that: a manual home zeroes the counters 30 mm out from where the test began, so after the test's own correct return they read -6400, and the next test that restarts the controller (setup.check-flow-verify, then motion.release) ended at 0, "expected -6400", and was "returned" by 30 mm. An operator who jogs the head from a Grbl client and then starts any takeover test from the page would have met the same thing, by whatever distance they had jogged. The baseline's capture() now records the counters' frame, the anchor's inode and mtime. If the frame changed during the run and the test did not vouch for the new one, the hand-back logs that the two readings share no frame, and moves nothing. ctx.counters_rezeroed() is how a test vouches: it now sets rezero_declared beside the position it expects. The start_reads argument it briefly took is gone, since it made the baseline accept counters that nothing after it could live with. homing.manual restarts the controller once more after returning the head, so it ends with the counters at zero where it began, and declares that. events.stream waits for its three places. A stream an earlier test closed keeps its place until the daemon's next write to it (its keep-alive), as documented, so run straight after forgectrl.lease the third stream drew 503. The test now opens the three once they can be opened, and says so in its log. Proven. test_baseline gains test_a_lost_counter_frame_never_moves_the_head: counters at -6400, a new anchor, counters at 0: no jog, no leftover, and the log says why; the same counters with the frame intact are still a displaced head; a declared re-zero is held to the position it declared. With the frame check unable to see the change (the first cut of the test reused an inode inside one clock tick) the case fails with ['position'], which is the old behavior. The unit suite passes. On the bench reference, arranged so a failure would move the head away from the stop: the head jogged to +60 mm (counters 12800), motion.release run, PASS, "the controller re-zeroed its counters during the run ... the head is not moved", and nothing moved. forgectrl.lease then events.stream: two logged waits, PASS. homing.manual then setup.check-flow-verify, the sequence that drove the head into the stop: both PASS with a clean hand-back. |
||
|
|
f306c9983f |
forgetest: the hand-back reads an engine hold again past the engine's next tick
The cooling engine publishes its state once a tick (1 Hz). A /cool/status
read inside the tick after a run ended still shows the run: while a
diagnostic owns the hardware every tick publishes phase "diag" with the
hold set, and a fail tier's hold stands until the tick that ends its
session. The baseline took one read, and by its rule an arm or a hold is
the run's doing, so a test that finished inside that second failed its
hand-back on a hold the engine's next tick cleared.
Seen on the bench reference twice. cooling.aa-offset-calibrate in campaign
c-20260919215024-c402: the diagnostic reported done at 22:07:14 with its
offset measured (15.7 counts, spread 0.7), and the hand-back at 22:07:15
read "cool=diag/armed=False/hold=True ... -> waited" and failed the run;
the test had passed on five images before, the last one earlier the same
day. cooling.fail-tier-stop in c-20260919202934-3d3a, the same way on the
crash fault's hold (
|
||
|
|
cd4c176a87 |
Finish the x32 xy_microsteps default in the baseline test and the stream harness
The x32 default landed in forgetest/baseline.py and the driver, but two callers still judged the machine at x8 and both failed on the host. forgetest/tests/test_baseline.py: setUp seeded the fake machine from the x8 FIXED_SYSFS literals while enforce() compares against fixed_sysfs() of the resolved mode, so x_mode, y_mode, step_freq and ramp_rate read as deviations on a clean machine - 23 failures across BaselineTests and TransientNotLeftoverTests. It seeds from fixed_sysfs() now, and the tick expectations come from it (DEFAULT_TICK) rather than a typed 28160. The xy_mode_of and ref_xy_mode unset/invalid cases expect 32, with an explicit "8" case added that had no coverage. Two reference_preconfig dumps taken on an x8 machine carry xy_microsteps = 8, because the markers are read at the reference's own mode. wait_configured wrote the static CONFIGURED_MARKERS where the function watches configured_markers() of the mode in force, and the held-controller jog typed 221 steps for "4.144 mm", which is 1.036 mm at x32; both derive from the mode now. 69 tests, all pass. scripts/bench/laser_stream_test.py: STEPS_PER_MM was the x8 53.333, so the X-peak check failed at 2133 steps against an expected 533. The whole harness now derives from XY_MICROSTEPS_BASE/DEFAULT the way glowforge.h and baseline.py do, which uncovered five more x8-only expectations behind the first: the machine tick, the fire-gap limit (it grows as sqrt(k), not k - a finer mode shortens the accel interval by sqrt(k) while speeding the tick by k), the rung split in fire_spans, the density period and minimum burst (laser_pulse_ticks is in x8 ticks and the stream scales it, so the config keeps the x8 numbers and the measured lengths scale), and the decel/hold budgets. Run against the null-sink build: all stream emission rules hold. xy_mode_test.py's docstring still described the no-key case as x8 while its own assertions had moved to x32. No behavior change and no acceptance-catalog consequence: these are test expectations and a bench harness, not image component sources. The coverage lint is unchanged at 0 uncovered paths. |
||
|
|
0eb764bf75 | Added SPDX | ||
|
|
2f2a4160af |
Stream and motion robustness: harness rules, catalog tests, the hand-back's counter scale
The GRBL driver's stream engine and its motion envelope change (grblHAL-glowforge: the shipper writes outside the lock, a clamp inside an armed window faults, the X/Y soft limits follow the home, the machine's settings are pinned, the homing keys are clamped). This commit carries the host rules and the catalog tests that hold them; the driver commit follows, because its CI fetches these harnesses unpinned. scripts/bench/laser_stream_test.py: - Rule 28: a 300 ms producer stall while armed faults the stream with ALARM:17, the kernel sees no step burst (at most the planned steps per 100 ticks), the stream ends dark, the latch sideband ends on the lock. The same stall unarmed is a warning: the move completes with every step, the clamp visible as the burst the kernel counts. - Rule 29: a 300 ms stall of the sink's write leaves the producer on pace: no clamp, every step, lit through, dark at the end. - The stand-in engine takes GFSINK_STALL_MS and GFSINK_WRITE_STALL_MS, null-sink only. scripts/bench/z_envelope_test.py: - Rule 10: homed (a gfcloud home), a program move past X max, Y max or the near edge alarms with ALARM:2 before any motion, a jog past the bed is refused with error 15, a move inside the bed runs, and a $20 write keeps the limits. The core repeats the last error for the line after a refused jog until an empty line clears it, so the rule sends one. forgetest/forgetest/suite/motion.py: - motion.soft-limits (kind auto, no emission): homes through the cloud suite's gfhome homing when the machine is not homed, then the three refusals (ALARM:2, the kernel counters still), the refused jog, the inside move, and the return to the corner read at rest. - motion.deadman phase 2b: a 100 ms SIGSTOP mid-move, inside the kernel's queue: no underrun, the controller's log warns of the clamped late events, the move completes with every step (read at rest), the latch stays locked. The armed clamp is proven on the host (rule 28). forgetest/forgetest/suite/cloud.py: - gfhome_homing drains the driver's answer to $H once the session ends: it sits behind the status reports and passed for the reply to the caller's next command (a setting read as None). forgetest/forgetest/baseline.py: - The hand-back reads the position counters at the kernel's own microstep mode (cnc/x_mode, read before the sysfs restore puts the settings' mode back). At the x8 constant, an x32 machine's 30 mm read as 120 mm, beyond the return bound, and the displaced head was left in place. The dead band scales the same way. tests/test_baseline.py holds both. Proof. Host: stream rules 1 to 29 and z_envelope rules 1 to 10 against the null-sink driver, the forgetest unit tests. Bench reference: motion.soft-limits passed (X 495 and Y 279 refused with ALARM:2 and 0.000 mm of kernel motion, X-1 the same, the jog error:15, the inside move ran, back at the corner 0.0/0.0 mm); motion.deadman passed with the new phase (92 late events clamped, max behind 92.3 ms, no underrun, 30.0 mm counted, latch locked) and the hand-back jogged the head back under the x32 scale. |
||
|
|
b94c990bbc |
Leave the lens to the cloud client in cloud mode
cloud.verdict-hold printed to completion and then failed its hand-back on head/z_mode=0. That job's header carried ZSmd 0, gfhardware's set_mode_from_puls turns that into z_mode 0, and z_mode 0 is full step (lenshome.c): the cloud client had set the lens the way the pulse file it was playing asked. Nothing was left behind - the machine did what the job said. The baseline already skips nine attributes in cloud mode, for the reason written above the list: the cloud client sets its own values for them from every pulse header, and forcing the GRBL values under it would be the baseline configuring another controller's machine. head/z_current and head/z_mode are the same thing and were not on the list. They are now, in cloud mode only; in GRBL mode the pair is still checked against 1 and 1, which is the half of this an exemption could quietly break, so a test holds it. Host-proven: 89 baseline unit tests. |
||
|
|
8d8ca2a8c6 |
Judge a hand-back on what a run left, not on the machine still working
A leftover is dirt a run left behind. Four of the checks were reporting
something else: the machine part-way through work of its own, which it
finishes on its own a few seconds later. Each one failed a run that had
done nothing wrong.
laser.arm-wait-lid found it. The test passed every check it makes and
failed on cool=smoke/armed=False/hold=False. The smoke clear is the
engine's post-job work - run duty for smoke_s after an armed session,
timed, ending by itself (cool.c, Cool_Smoke) - and the machine was idle
fifteen seconds later without being asked. Armed and hold were both
false: nothing had been left.
The four, and what each was catching:
cool a phase alone (the smoke clear, a cooldown). Armed or
holding stays a leftover: the run left a job alive and the
engine is keeping the fans up for it.
controller the supervisor's own start. A takeover ends by starting
forgectrl again, and the respawn runs the liveness probe
and the lens reference before it reports running and
verified. The run had put it back.
state the ring draining to the end of a job. An underrun stays a
leftover and is still acknowledged with cnc/stop: that one
is the run's.
leds read_led read brightness, write_led writes target, and the
smooth trigger fades brightness toward target. An LED the
machine had already released still read lit mid-fade, and
the restore called itself done before the fade had moved.
Judged on target now: a run that left the button lit left
a target standing.
The waiting, the restoring and the reporting of anything a run actually
left are unchanged. What changed is which readings count as dirt.
Host-proven: 87 baseline unit tests, two of them new - a fading button
LED is not a leftover, a lit one is.
|
||
|
|
d3fe1d90b9 |
Hand the machine back, do not describe what is wrong with it
The hand-back is the promise that a run leaves the machine where it found it. Two of its checks reported instead of restoring, and the machine sat in the state the run left it in. The cooling engine: a run that ends without ending its job leaves one alive behind it - the engine armed and holding for a job that is never coming back, the fans at run duty. The check waited two minutes for that to resolve itself, which it cannot, and wrote "failed: still run/armed=True/hold=True". On the bench reference the fans then ran for an hour. The pulse ring: bytes the last job never played sit there, and the next run replays them before its own. The check refused the return jog and wrote "clear the ring (controller restart) before moving" - the instruction, to a log, instead of the action. Both end the same way: stop the controller and let the supervisor bring it back. The job goes, the arm and the hold go with it, and the ring is empty on the way in. stand_down() does that and proves it settled; the cooling check calls it when the engine will not idle on its own, and the return jog calls it when the ring has residue, refusing only if bytes survive a restart. A leftover still reports what it found - the record of what the run did is the point - but it reports having fixed it. Host-proven: 62 baseline unit tests, including one that the hand-back calls the stand-down for ring residue rather than describing it. |
||
|
|
b2c42f0cc3 |
Do not fail a hand-back on the step the counters round to
The post-run pass compared the kernel position counters exactly, so a test that put the head back within a hundredth of a millimeter failed whenever that distance did not round to the same step. motion.lid-cancel-home found it on the bench reference. The test cancels a job with the lid twice and the controller returns the head to the job start each time, landing 0.038 and 0.037 mm out - the same figures as the run before it, which passed. This time the two returns left four steps on X, 0.075 mm at 53.333 steps per mm, and the hand-back called it a leftover. Twenty-three runs of that test, all passing, on returns of the same accuracy: whether the residue rounds to zero is chance, not a property of the machine or of the test. A difference inside POSITION_DEADBAND_MM (0.1 mm, five steps at x8) on X and Y is now the quantization rather than a leftover. The head is still put back, so nothing accumulates over a campaign - only the failure goes. Z stays exact: the return never moves the lens, so a Z difference is still a leftover and still unrestorable. Host-proven: four new unit tests on the boundary (four steps and a moved Z on either side of it, and an unreadable reading), and the forgetest suite. |
||
|
|
8fc5250d6d |
XY microstep modes: the baseline, the catalog test and the bench tools
The baseline derives x/y_mode, step_freq, ramp_rate and the configured markers from the xy_microsteps setting; ramp_rate joins the GRBL controller's set (the driver writes it; the cloud client runs at the module's). motion.microstep-modes cycles 8, 16 and 32: the save restarts the idle controller, the kernel reads the mode with its tick and ramp, $100/$101 are the mode's and a typed $100 is overwritten, a 40 mm jog at top speed returns to Idle with the kernel counters over the mode agreeing with the commanded travel and the accelerometer seeing the head move; the setting is put back as found. Bench tools: xy_mode_test.py (the null-sink harness the grblHAL CI runs), raster_dry.py (a top-speed raster per mode), xy_pattern_accel.py (the operator's pattern from home with the machine silent and the head accelerometer listening), arc_tolerance_sweep.py (a $12 ladder on the 9 in circle: the chord rate the protocol loop feeds, about 300 a second, is the ceiling, not the core), and the xymode and xycircle live drills. BRINGUP carries the present state and the facts; CAMPAIGN-LOG the dated record, including the planner-blocks spin (a $398 of 255 or more loops forever at start, a core bug) and its recovery. |
||
|
|
97287aa6a9 |
commissioning: the layer, the acceptance tests, the harness rule, the docs, and the bench drills
meta-forgefirm: the forgefirm-users init replays the account at boot; sshd refuses root and empty passwords and runs only while the panel turns it on; the release image keeps an empty root password for the console; the console banner; avahi announces forgefirm.local; https in libmicrohttpd and ulfius; the panel on 80 and 443; the license bundle on the rootfs; release.sh checks the root policy on the built rootfs. forgetest: the commission suites (commission, commission_dark, commission_sheet: 23 cases); the runner turns cloud mode on with the typed phrase for a test that declares it; the baseline's motor_lock is 0; the log-export test checks the bundle for the camera key; the record helpers write bytes as given and join the daemon's paths as POSIX. The stream harness gains rule 24: a hold verdict is held again after a resume. Bench drills: lens_travel.py and lens_stop_accel.py. Docs: BRINGUP carries the present state; CAMPAIGN-LOG carries the dated record. |
||
|
|
00ded74189 |
forgetest: the fixture spaces its presses; an unattended refusal is an error; the baseline resets out of a hold
The first campaign with the bench actuator wired failed motion.button-hold-resume on the tool, not the machine: the second press was asked while the first 200 ms pulse was still on, the fixture answered 409, the runner handed the step to an operator who was not in the room, and the post pass could not jog a controller left in Hold. - fixture.py: a press waits for the last pulse to end (the fixture's pulse_ms) plus a 300 ms release, so the controller sees the edge; a 409 for a pulse in progress is waited out against button_pulsing and retried once. - runner.py: in an unattended run a fixture refusal ends the test at once as ERROR naming the refusal; the operator fallback stays for attended runs. - baseline.py: a controller in Hold or Door gets a soft reset before the return jog, position kept. - tests: the fake fixture refuses a press while one is in progress and reports button_pulsing; FakeGrbl records ^X and can land a reset in a chosen state; five new tests. - docs: ACCEPTANCE.md fixture rules, fixture/README.md tool's side. No catalog consequence: tool-side change, no covers map moves. Bench: campaign c-20260824174545-0bdc 25/25 with every action by the fixture; the hold reset proven by a dry drill. |
||
|
|
969bac6013 |
forgetest: the service's hunt paid only where it is the subject
A cloud client the tool starts for anything but homing comes up under the /run/gfcloud-nohunt marker: the real client back after the emulator, a mode the runner switches to or hands back, a controller it restarts. The service keeps the head position it has. cloud.mode-switch and cloud.service-protocol keep their hunts, and so does the one real print: enter_cloud reuses a running session only when that client has hunted the machine itself (session_hunted: never the emulator's, never a no-hunt start), otherwise it restarts the client with the hunt, since a print placed on a head position the service only believes can run the gantry into a rail. The markers are one start, taken down by the client that read them first thing; the tool's own removal stays for a start that never happened. Catalog unchanged; the cloud tests' shared code moved, so their implementation hashes move with it. |
||
|
|
86ce0419e5 |
forgetest: cloud tests stay in cloud mode; the page can ignore prerequisites
The cloud job tests (lid-abort, lid-during-button-wait, hunt-lid-open,
pause-resume) run in cloud mode and leave the machine there: enter_cloud
reuses a live session (pid-scoped from the client's own websocket state
lines) and switches once from GRBL mode, declaring the change to the
baseline; nothing switches back. Each test judges the log from its own
window, the print by its own "print [id]: finished" line, and waits the
service's follow-up moves out (wait_quiet) before it ends. hunt-lid-open
restarts the cloud client through the supervisor's stop/start lever for
a fresh connect. The former switch-back is what failed the last bench run
of hunt-lid-open (409 machine is not idle: the service was still
re-finding the head after the lid closed).
The baseline is mode-aware: in cloud mode the client owns the GRBL
controller's init values, the lid lamp, and the position counters; the
mode itself is preserved unless the run declared the change
(Context.mode_changed); controller_mode is never handed back as a bare
setting (a bare write left the persisted mode out of step with the live
one). The undeclared-change restore now uses the captured state, which
the post pass never saw before.
The acceptance page gets an "Ignore prerequisites" switch (remembered by
the browser): POST /start {ignore_requires} starts a test whose requires
are unmet, and the run records the unmet prerequisites in its evidence
and log; they stay required for the release. The cloud tests' requires
no longer chain through cloud.mode-switch.
Proof: tests/test_cloud_suite.py replays the four tests on the bench's
own gfcloud excerpts (fixtures/) and the run loop's emitted pause/resume
lines under the real runner Context against a fake forgectrl; baseline
mode tests and the server override test; the whole suite (98) and the
coverage lint pass. Catalog consequence: cloud.* fingerprints move with
the module; the catalog hash moves with the requires.
|
||
|
|
9878c8d3b9 |
forgetest: kernel counters prove the return, and ring residue is a leftover that blocks the jog
The lid-cancel tests (motion.lid-cancel-home, laser.lid-cancel-mid-fire) now check that the KERNEL position counters returned to the job start, not only grbl's MPos - grbl's drift read 0.000 while the head had not moved. The baseline reports unplayed bytes queued in the kernel ring (cnc/position total minus processed) as a leftover and refuses its return jog while any exist: a run started on top of them replays them first, which is what put the head into the rail. BRINGUP item 16 records the bench finding and the stream-engine fix. |
||
|
|
b68d790b71 |
baseline: take the fresh-boot reference after the controller applied its config
/mode reports controller=running at the spawn, so the reference dump raced grblHAL's init writes and captured the supervisor's motion-probe values (motor_lock 0, step_freq 10000, y_mode at the module default) instead of the resting state. boot_reference() now waits for the controller's markers (step_freq/motor_lock/y_mode at their fixed values, bounded 20 s, GRBL mode only) plus a 1 s settle before dumping, retakes a saved reference that shows the pre-config state while the boot is still fresh, and marks it otherwise. Tests cover the wait, its timeout, the non-GRBL no-op, the pre-config recognition and the retake. |
||
|
|
d0291ec7cb |
forgetest: the lid lamp idles at forgectrl's lid_lamp_idle; the liveness test masks and restarts
The lid lamp now has a resting policy in forgectrl (lid_lamp_idle, default 236, asserted at start and at every spawn), so the baseline expects it there instead of preserving whatever level a boot left, and forgectrl.settings-bounds proves it: resting at the setting, 256 / -1 / 'bright' refused, a new level applied to the lamp at once, the cleared key back to the default. Clearing a key goes through the query-string form (an empty JSON value reads as no setting). motion.liveness-probe adds the regression the bench needed: with every axis masked (cnc/motor_lock=15, as a bench tool may leave it) forgectrl is restarted and its fresh probe must read MOTION OK on the first try - the probe unmasks the axes itself - and the controller comes up with the mask cleared. Bench 2026-08-16: MOTION OK at p2p 2047/1341 against the 800 threshold, no ladder. The baseline's settle no longer counts 'probe verified, spawn pending' as settled (the post pass ran between the probe's own writes and the controller's init writes and mis-flagged motor_lock/step_freq): it waits for the controller to be running, with a bounded allowance for a respawn backoff. |
||
|
|
a80d7aabfb |
forgetest: cloud tests prove the session from the client's log and hand the head back
cloud.mode-switch took the optional connect-time firmware probe file as the evidence of a live session and failed on a bench where that check is off; the evidence is now gfcloud's own authenticate/ws-connect lines in the unified log after the switch, the probe recorded when present. Cloud mode's connect clears the kernel position counters at the head's start and its hunt homes the head to the corner: the test tells the runner the counters were re-zeroed (Context.counters_rezeroed) and jogs the head back by the counter-measured displacement, and hands the lid lamp back at the level it found. cloud.gfhome-homing documents that it leaves the machine homed at the corner. Baseline: a displaced head is jogged back along its own path by the kernel-measured X/Y delta through the GRBL controller (bounded 100 mm, waits out a controller respawn backoff); Z is never touched. |
||
|
|
4aaedc8080 |
forgetest: every run starts from, and leaves, the fresh-boot idle state
A baseline pass brackets every test and bench tool: before the run the machine is verified against the fresh-boot idle state and anything off it is restored; after the run - pass, fail, or abort - it is restored again. Fixed items are the resting values the boot establishes (module defaults, forgectrl's start-up writes, the GRBL controller's init writes) and forgectrl's idle picture (controller running with motion verified, no diagnostic, camera and cooling engines idle); preserved items (lid lamp level, position counters, settings map, controller mode) are captured before and handed back after. Deviations are leftovers: in the run pane, in the result's evidence, and on the page - attributed to the previous run when found before, to the run itself when found after. forgetest takes a fresh-boot reference once per boot (within ten minutes of boot, after the supervisor settles) as the session's resting lid-lamp level and the check on the fixed values; the values were confirmed against a fresh boot of the dev image on the bench (step_freq rests at 28160, the controller's default tick, not the probe's 10000). Takeover runs capture the controller-owned kernel attributes on entry and write them back before forgectrl restarts: the bench found the kernel tests leaving motor_lock=15 behind, which masked the supervisor's liveness probe - no motion by construction, a false driver-wedge verdict, the rail-off ladder, and finally motion-fault. The takeover wrapper also waits for the supervisor to settle on both sides (moved into baseline). Catalog consequence: none beyond the runner; the tests' own drills are unchanged. |