From 4a00192ab6a84fb76391f844a8b2e1978b543d1e Mon Sep 17 00:00:00 2001 From: Luca Toniolo <10792599+grandixximo@users.noreply.github.com> Date: Sat, 19 Sep 2026 22:06:38 +1000 Subject: [PATCH] tests: close the idle-detection hole in the G28.2 sequencing test The rejected G28.2 P99 puts task through its ERROR path, which reports DONE/IDLE with the resynch still on the interp list. The next MDI sent in that window is parked in the MDI queue, and a queued MDI also reads as DONE/IDLE, so wait_idle() returned before the move ran and the test read X while it was still 0. One slow task cycle on a loaded CI runner is enough to open the window. wait_idle() now also requires queued_mdi_commands == 0, and the recovery check waits for X to reach 2 instead of for idle. Fixes #4569 --- tests/interp/g28.2/sequencing/test-ui.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/tests/interp/g28.2/sequencing/test-ui.py b/tests/interp/g28.2/sequencing/test-ui.py index 67c05322a67..87c68638802 100755 --- a/tests/interp/g28.2/sequencing/test-ui.py +++ b/tests/interp/g28.2/sequencing/test-ui.py @@ -33,7 +33,21 @@ def wait_idle(timeout=5.0): t0 = time.time() while time.time() - t0 < timeout: poll() - if s.exec_state == linuxcnc.EXEC_DONE and s.interp_state == linuxcnc.INTERP_IDLE: + # A queued MDI command shows as DONE/IDLE until task takes it off + # the queue, so it has to be counted too. + if (s.exec_state == linuxcnc.EXEC_DONE + and s.interp_state == linuxcnc.INTERP_IDLE + and s.queued_mdi_commands == 0): + return True + time.sleep(0.01) + return False + + +def wait_x(expected, timeout=5.0): + t0 = time.time() + while time.time() - t0 < timeout: + poll() + if near(s.position[0], expected): return True time.sleep(0.01) return False @@ -93,11 +107,10 @@ def near(a, b, tol=0.001): if s.task_mode != mode_before: fail("task_mode after invalid Pn is {}, expected {} (MDI)".format(s.task_mode, mode_before)) +# The rejected G28.2 leaves a resynch on the interp list; task parks the +# next MDI behind it, so wait for the move itself rather than for idle. c.mdi("G0 X2") -if not wait_idle(): - fail("recovery MDI command after invalid Pn did not settle") -poll() -if not near(s.position[0], 2.0): +if not wait_x(2.0): fail("MDI command after an invalid Pn was rejected -- task_mode stuck (X stayed at {})".format(s.position[0])) print("PASS: an invalid Pn does not leave task_mode stuck")