Skip to content

Commit 1718e2f

Browse files
committed
fix(updater): fail-fast Spoolman health check, no double start, hold overlay until fresh status
1 parent 94606fb commit 1718e2f

2 files changed

Lines changed: 35 additions & 10 deletions

File tree

‎BlocksScreen/lib/panels/widgets/MainWindow/updatePage.py‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ def handle_status_ready(self, json_str: str) -> None:
340340
_log.debug("status_ready: emitting call_load_panel(False)")
341341
self.call_load_panel.emit(False, "", False)
342342
self._post_update_status_pending = False
343+
self._overlay_shown = False
343344
else:
344345
_log.debug("status_ready: skipping loadscreen dismiss (busy=True)")
345346
self.build_cards()
@@ -370,10 +371,18 @@ def handle_busy_changed(self, busy: bool) -> None:
370371
# Keep the overlay up: SIGTERM is imminent, MainWindow would flash.
371372
QtCore.QTimer.singleShot(15000, self._dismiss_after_restart_grace)
372373
elif self._overlay_shown:
373-
self._overlay_shown = False
374-
self.call_load_panel.emit(False, "", False)
374+
# Hold the overlay until fresh status lands, else stale cards flash.
375+
self._post_update_status_pending = True
376+
QtCore.QTimer.singleShot(10000, self._dismiss_stale_overlay)
375377
self._request_status_debounced()
376378

379+
def _dismiss_stale_overlay(self) -> None:
380+
"""Drop the overlay if the post-update status never arrived."""
381+
if self._overlay_shown and not self._busy:
382+
self._overlay_shown = False
383+
self._post_update_status_pending = False
384+
self.call_load_panel.emit(False, "", False)
385+
377386
def _dismiss_after_restart_grace(self) -> None:
378387
"""Drop the overlay if the expected UI restart never came."""
379388
if self._restart_pending and not self._busy:

‎tests/widgets/test_update_page_unit.py‎

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111
def page(qapp):
1212
"""UpdatePage instance with all heavy UI deps mocked."""
1313
patches = [
14-
patch("BlocksScreen.lib.panels.widgets.MainWindow.updatePage.LoadingOverlayWidget"),
15-
patch("BlocksScreen.lib.panels.widgets.MainWindow.updatePage.BlocksCustomButton"),
14+
patch(
15+
"BlocksScreen.lib.panels.widgets.MainWindow.updatePage.LoadingOverlayWidget"
16+
),
17+
patch(
18+
"BlocksScreen.lib.panels.widgets.MainWindow.updatePage.BlocksCustomButton"
19+
),
1620
patch("BlocksScreen.lib.panels.widgets.MainWindow.updatePage.IconButton"),
1721
]
1822
for p in patches:
@@ -307,12 +311,22 @@ def test_false_does_not_emit_call_load_panel_when_no_overlay(self, page, qtbot):
307311
with qtbot.assertNotEmitted(page.call_load_panel, wait=200):
308312
page.handle_busy_changed(False)
309313

310-
def test_false_emits_call_load_panel_when_overlay_shown(self, page, qtbot):
314+
def test_false_holds_overlay_until_status_ready(self, page, qtbot):
311315
page.show_loading = MagicMock()
312316
page._overlay_shown = True
313-
with qtbot.waitSignal(page.call_load_panel, timeout=200) as blocker:
317+
with qtbot.assertNotEmitted(page.call_load_panel, wait=200):
314318
page.handle_busy_changed(False)
315-
assert blocker.args == [False, "",False]
319+
assert page._overlay_shown is True
320+
with qtbot.waitSignal(page.call_load_panel, timeout=200) as blocker:
321+
page.handle_status_ready(_make_payload())
322+
assert blocker.args == [False, "", False]
323+
assert page._overlay_shown is False
324+
325+
def test_stale_overlay_fallback_dismisses(self, page, qtbot):
326+
page._overlay_shown = True
327+
page._busy = False
328+
with qtbot.waitSignal(page.call_load_panel, timeout=200):
329+
page._dismiss_stale_overlay()
316330
assert page._overlay_shown is False
317331

318332
def test_true_starts_elapsed_timer(self, page):
@@ -413,13 +427,13 @@ class TestHandleStepComplete:
413427
def test_emits_call_load_panel_with_step_message(self, page, qtbot):
414428
with qtbot.waitSignal(page.call_load_panel, timeout=200) as blocker:
415429
page.handle_step_complete("klipper", 1, 4)
416-
assert blocker.args == [True, "klipper: fetching",False]
430+
assert blocker.args == [True, "klipper: fetching", False]
417431
page._progress_label.setText.assert_called_with("Step 1/4")
418432

419433
def test_unknown_steps_falls_back_to_working(self, page, qtbot):
420434
with qtbot.waitSignal(page.call_load_panel, timeout=200) as blocker:
421435
page.handle_step_complete("moonraker", 99, 4)
422-
assert blocker.args == [True, "moonraker: working",False]
436+
assert blocker.args == [True, "moonraker: working", False]
423437
page._progress_label.setText.assert_called_with("Step 99/4")
424438

425439

@@ -498,7 +512,9 @@ def test_bad_payload_keeps_statuses_and_toasts(self, page):
498512

499513
class TestConfirmPopupCleanup:
500514
def test_second_confirm_deletes_previous_popup(self, page):
501-
with patch("BlocksScreen.lib.panels.widgets.MainWindow.updatePage.BasePopup") as popup_cls:
515+
with patch(
516+
"BlocksScreen.lib.panels.widgets.MainWindow.updatePage.BasePopup"
517+
) as popup_cls:
502518
first = MagicMock()
503519
second = MagicMock()
504520
popup_cls.side_effect = [first, second]

0 commit comments

Comments
 (0)