mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-10-18 18:41:41 -07:00
forgetest: the arm-notice watch ends with its notice and the run
_clear_when_taken takes the operator's arm notice down the moment the
controller takes the press (the button lights for the arm wait and
goes out at the press). Its watch thread polled the button LEDs every
50 ms with the arm's timeouts as its only exits: 30 minutes for the
button to light, 90 minutes in all. Nothing in it read the run's end,
the abort event, or whether the notice it guarded still stood, so a
test that ended before the press (a FAIL on the way to the arm) left a
thread reading up to six sysfs files twenty times a second behind it.
The watch now lives only as long as the notice it guards and the run:
a notice cleared or replaced, a run that finished, or an abort ends
it. `standing()` reads both under the run's lock on every poll, and
the poll waits on the run's abort event instead of sleeping, so an
abort ends the wait at once. The cadence stays 50 ms: the notice still
drops within 50 ms of the press. The watch commands nothing; no press,
judge, or energy path changes.
Proof: forgetest-ci "Unit tests" (`python -B -m unittest discover -s
tests -v` in forgetest), on Ubuntu 24.04 with python 3.12:
test_fixture.py's test_the_arm_notice_comes_down_when_the_press_is_taken
gains the leg that a run finished before the press ends the watch
within 2 s and leaves the finished run's notice as it was; the file's
21 tests pass; test_setup_burns.py, whose burns reach the watch
through press_now, passes its 19 tests and leaves no
forgetest-arm-notice thread behind (10 before this change); and
test_server.py, test_operator.py and test_queue.py pass. Negative control on a scratch
copy: the new leg against the unchanged runner fails with the
forgetest-arm-notice thread still alive after the run finished.
pyflakes reports no undefined name.
Acceptance: every catalog test's fingerprint moves: runner.py is a
shared judge, in every implementation hash (43 of 43 move). Against
the campaign this series already needs, that adds no run: 850498f
moved all 43 once. tests/ are in none.
This commit is contained in:
@@ -495,26 +495,37 @@ class Context:
|
||||
controller lights the button white for its arm wait and puts it out
|
||||
at the press. Without this the notice stood until the test's next
|
||||
step, and an operator who still saw it pressed again, which pauses
|
||||
the job. A notice replaced meanwhile is left alone."""
|
||||
with self.run._lock:
|
||||
nid = (self.run.notice or {}).get("id")
|
||||
the job. A notice replaced meanwhile is left alone. The watch lives
|
||||
only as long as the notice it guards and the run: a notice cleared
|
||||
or replaced, a run that finished (passed, failed, or errored before
|
||||
the press) or an abort ends it, so no thread is left polling the
|
||||
LEDs behind a test."""
|
||||
run = self.run
|
||||
with run._lock:
|
||||
nid = (run.notice or {}).get("id")
|
||||
if nid is None:
|
||||
return
|
||||
|
||||
def standing():
|
||||
with run._lock:
|
||||
return run.finished is None and (run.notice or {}).get("id") == nid
|
||||
|
||||
def watch():
|
||||
t0 = time.time()
|
||||
while hw.button_lit() is not True:
|
||||
if time.time() - t0 > lit_timeout:
|
||||
if not standing() or time.time() - t0 > lit_timeout:
|
||||
return
|
||||
if run.aborted.wait(0.05):
|
||||
return
|
||||
time.sleep(0.05)
|
||||
while hw.button_lit() is not False:
|
||||
if time.time() - t0 > lit_timeout + ARM_PRESS_WAIT_S:
|
||||
if not standing() or time.time() - t0 > lit_timeout + ARM_PRESS_WAIT_S:
|
||||
return
|
||||
time.sleep(0.05)
|
||||
with self.run._lock:
|
||||
same = (self.run.notice or {}).get("id") == nid
|
||||
if run.aborted.wait(0.05):
|
||||
return
|
||||
with run._lock:
|
||||
same = (run.notice or {}).get("id") == nid
|
||||
if same:
|
||||
self.run.notice = None
|
||||
run.notice = None
|
||||
if same:
|
||||
self.log("ARM: the button went out at the press; the notice is down")
|
||||
|
||||
|
||||
@@ -576,6 +576,24 @@ class RoutingTests(unittest.TestCase):
|
||||
lit["v"] = False
|
||||
time.sleep(0.3)
|
||||
self.assertEqual((run.notice or {}).get("text"), "Something else.")
|
||||
# The watch ends with the run: a test that ends before the press
|
||||
# (a FAIL on the way to the arm, say) leaves no thread polling the
|
||||
# LEDs for up to the arm's whole timeout.
|
||||
def watching():
|
||||
return [t for t in threading.enumerate() if t.name == "forgetest-arm-notice"]
|
||||
deadline = time.time() + 2
|
||||
while watching() and time.time() < deadline:
|
||||
time.sleep(0.02) # the earlier watches are gone
|
||||
self.assertFalse(watching())
|
||||
lit["v"] = False
|
||||
self.assertFalse(ctx.arm_press())
|
||||
self.assertTrue(watching()) # up while the arm is still dark
|
||||
run.finished = {"result": "FAIL", "message": "", "duration_s": 0}
|
||||
deadline = time.time() + 2
|
||||
while watching() and time.time() < deadline:
|
||||
time.sleep(0.02)
|
||||
self.assertFalse(watching())
|
||||
self.assertIsNotNone(run.notice) # the finished run's notice is left as it was
|
||||
finally:
|
||||
runner_mod.hw.button_lit = saved
|
||||
|
||||
|
||||
Reference in New Issue
Block a user