diff options
| author | hathach <[email protected]> | 2026-08-27 15:26:43 +0700 |
|---|---|---|
| committer | hathach <[email protected]> | 2026-08-28 14:18:41 +0700 |
| commit | 20bb94fcf9ad7fca7fb685e53307f4d03b1340fd (patch) | |
| tree | 1407f23cfc4b667a8bee2f95068440aa5f41d881 /test | |
| parent | b190840e1d85f806575f58635827502d714febcf (diff) | |
test/hil, docs: move the containment history into the design doc
The modules were 21% comment, much of it review-cycle argument rather than
guidance -- _kill_kids stated 'descendant by construction, no argv check needed'
twice, eight lines apart. Deleting such comments outright makes maintenance
worse: the next reader simplifies the thing the comment was defending. So the
history moves to the 2026-07-30 fleet-wedge design doc, which gains a trim
addendum recording what was removed, what was deliberately kept, and the rule
that decided each -- the CI ceiling bounds how long a run burns, and does nothing
about state that outlives it.
One comment was not merely long but WRONG: the report wipe carried 'The unlink is
DEFERRED to inside the pool try/except below', which is the opposite of what the
code does -- it sits before Manager() with its own comment explaining why. That
is the failure mode this pass is about, so it is deleted rather than reworded.
Kept everywhere: citations that refute a plausible wrong reading. That
usb_lock_device_interruptible is why the readers are killable, that usblp_mutex
is driver-global, that rawmidi honours O_NONBLOCK where usblp does not.
Two follow-ups are retired with them: pr3803-hil-blindness-reporting.md (there
is no blindness to report any more) and pr3803-usbtest-recovery-reserve.md (the
reserve is derived now). Kept: pr3803-flasher-recover.md, which PR #3832
implements, plus pr3803-pci-rebind-stranding.md and pr3803-hil-iar-rerun-spec.md,
both independent of this work.
Diffstat (limited to 'test')
| -rw-r--r-- | test/hil/helper/hil_health.py | 6 | ||||
| -rw-r--r-- | test/hil/helper/hil_util.py | 6 | ||||
| -rwxr-xr-x | test/hil/hil_test.py | 30 |
3 files changed, 18 insertions, 24 deletions
diff --git a/test/hil/helper/hil_health.py b/test/hil/helper/hil_health.py index b9c05c236..d78d0f220 100644 --- a/test/hil/helper/hil_health.py +++ b/test/hil/helper/hil_health.py @@ -246,10 +246,8 @@ def _kill_kids(kids: dict, seen: set) -> int: if denied: _p(f'warning: could not kill {sorted(denied)}; they still hold whatever they ' f'had open (probe, usbfs node) into the next job', flush=True) - # SURVIVORS, not the signalled-child count: the caller needs to know the rig is dirty - # for the next job, and a count of what we successfully signalled cannot tell it that. - # (They are different units anyway -- a killpg is counted once per child sharing the - # group -- so the old return was never comparable to anything.) + # SURVIVORS, not the count we signalled: the caller needs to know the rig is dirty for + # the next job, and a killpg is counted once per child sharing the group anyway. return len(denied) diff --git a/test/hil/helper/hil_util.py b/test/hil/helper/hil_util.py index b2c233cf3..6f84c143d 100644 --- a/test/hil/helper/hil_util.py +++ b/test/hil/helper/hil_util.py @@ -343,9 +343,9 @@ def usb_scan(vid_pid=None, serial=None, vid=None, timeout=SYSFS_READ_GRACE) -> l """ out = [] for d in glob.glob('/sys/bus/usb/devices/*-*'): - # Interfaces are '<busport>:<cfg>.<ifnum>' (e.g. 2-4:1.0) -- they CONTAIN the - # colon, they do not end with it, so the original endswith() never fired and every - # scan opened idVendor/idProduct on all of them (measured: 31 of 44 matches). + # `in`, not endswith: an interface is '<busport>:<cfg>.<ifnum>' (2-4:1.0), which + # CONTAINS the colon rather than ending with it. Screening them out here is worth + # real time -- they were 31 of 44 matches on this rig. if ':' in os.path.basename(d): continue try: diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index ac89bdd95..b2b74b13c 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -239,9 +239,9 @@ USBTEST_BATTERY_BUDGET = hil_util.pos_int_env('HIL_USBTEST_BATTERY_BUDGET', 260) # as it goes to print its JSON, turning ~29 real per-case verdicts into "usbtest did not # run" and re-paying the whole battery on retry. # Worst case, from usbtest.py: --timeout 60 (the case) + 5s post-SIGKILL reap + -# dmesg_tail(), which is bounded by HELPER_TIMEOUT=30 and runs on BOTH the FAIL and HUNG -# timeout paths = 95s. 120 leaves a margin; 75 (my first estimate, taken before checking -# dmesg_tail) was 20s SHORT and would have killed the battery mid-print. +# dmesg_tail(), bounded by HELPER_TIMEOUT=30 and run on BOTH the FAIL and HUNG timeout +# paths = 95s. 120 leaves a margin. Re-derive it if any of those three moves -- dmesg_tail +# is the one easily missed, and without it the estimate lands 20s short. USBTEST_OVERSHOOT = 120 # Named, not a literal, so the unit tests can zero it: every test that drives # test_device_usbtest against a fake rig otherwise pays a real 3s (ten of them, 30s a run). @@ -1563,9 +1563,9 @@ def test_device_usbtest(board): f'usbfs node, so usbtest hang recovery is disabled for {board["name"]}; a ' f'HUNG case will leave it wedged for the rest of the run', flush=True) if recovery: - # ship the RECOVERY flasher as `flasher`: usbtest.py, recovery_steps and - # convoy_safe all read board['flasher'], so substituting here keeps the entire - # child side unaware that a second roster entry exists + # ship the RECOVERY flasher as `flasher`: usbtest.py and convoy_safe both read + # board['flasher'], so substituting here keeps the entire child side unaware that + # a second roster entry exists rb = json.dumps({'name': board['name'], 'flasher': _rec_flasher}) cmd += f' --recover-board {shlex.quote(rb)} --recover-fw {shlex.quote(_current_fw)}' # The reserve above USBTEST_BATTERY_BUDGET exists because the battery can overrun by @@ -2604,20 +2604,16 @@ def main() -> None: err_count = build_err + sum(e[1] for e in mret) _write_failed_spec(failed_fname, report_dir, mret) finally: - # Not `with Pool(...)`: its __exit__ joins the workers unbounded, hanging on + # Not `with Pool(...)`: its __exit__ joins the workers unbounded and hangs on # any worker in uninterruptible sleep. shutdown_pool bounds the same terminate() - # by a grace period, so the pool is NOT cleanly closed/joined when it returns - # False. Record the outcome but never exit here: the report below is the only - # record of a run that otherwise passed. + # and returns False when the pool is NOT cleanly closed. # - # Same ordering as the timeout path: what the workers spawned must be - # snapshotted and killed while its parent is alive, or terminate() reparents it - # out of reach. + # Sweep BEFORE shutdown: what the workers spawned must be snapshotted and + # killed while its parent is alive, or terminate() reparents it out of reach. # - # Both calls must stay guarded: a raise here skips accumulate_report(), so a run - # whose boards ALL passed publishes an empty report dir -- and both can raise - # for reasons unrelated to the results. pool_abandoned stays fail-CLOSED, so - # _abandon_exit still arms. + # Both calls stay guarded and neither exits: a raise here would skip + # accumulate_report and publish an empty report dir for a run whose boards all + # passed. pool_abandoned is fail-CLOSED, so _abandon_exit still arms. try: # Still worth running for the TIMEOUT path, where the workers are # genuinely stuck mid-task and their children are still reachable through |
