From 0c7cf097977527bb24da944bb47f34785b7cd27e Mon Sep 17 00:00:00 2001 From: ChuckBuilds Date: Fri, 10 Jul 2026 13:09:49 -0400 Subject: [PATCH] fix(plugin-harness): add no-op process_deferred_updates to test double The safety harness's VisualTestDisplayManager (base of BoundsCheckingDisplayManager) doesn't implement process_deferred_updates(), which 5 first-party ledmatrix-plugins call unconditionally between set_scrolling_state() and their scroll-position update: news, odds-ticker, ledmatrix-leaderboard, stock-news, and ledmatrix-stocks. Any of them fails the harness with AttributeError the moment it's touched (surfaced when ledmatrix-plugins#177 had to add a local hasattr guard in ledmatrix-stocks just to pass CI). Add the method as a no-op, mirroring the existing "no-op for testing" pattern already used for set_scrolling_state, so these plugins render under the harness without every touching PR needing its own guard. --- src/plugin_system/testing/visual_display_manager.py | 12 ++++++++++++ test/plugins/test_visual_rendering.py | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/plugin_system/testing/visual_display_manager.py b/src/plugin_system/testing/visual_display_manager.py index 73d64f3cf..7f84a94e1 100644 --- a/src/plugin_system/testing/visual_display_manager.py +++ b/src/plugin_system/testing/visual_display_manager.py @@ -454,6 +454,18 @@ def is_currently_scrolling(self) -> bool: """Check if display is currently scrolling.""" return self._scrolling_state['is_scrolling'] + def process_deferred_updates(self): + """Process any deferred updates (no-op for testing). + + Several ticker-style plugins (news, odds-ticker, leaderboard, + stock-news, stocks) call this unconditionally between + set_scrolling_state() and their scroll-position update, mirroring the + real display_manager's deferred-update queue. This double has no such + queue, so there is nothing to process — the no-op just lets those + plugins render under the harness instead of raising AttributeError. + """ + pass + # ------------------------------------------------------------------ # Utility methods # ------------------------------------------------------------------ diff --git a/test/plugins/test_visual_rendering.py b/test/plugins/test_visual_rendering.py index 3bfffa577..0bb77d0ce 100644 --- a/test/plugins/test_visual_rendering.py +++ b/test/plugins/test_visual_rendering.py @@ -172,6 +172,16 @@ def test_scrolling_state(self): vdm.set_scrolling_state(False) assert vdm.is_currently_scrolling() is False + def test_process_deferred_updates_is_noop(self): + # Ticker-style plugins (news, odds-ticker, leaderboard, stock-news, + # stocks) call this unconditionally alongside set_scrolling_state(); + # it must exist and be harmless so those plugins render under the + # harness instead of raising AttributeError. + vdm = VisualTestDisplayManager(width=128, height=32) + vdm.set_scrolling_state(True) + vdm.process_deferred_updates() # should not raise + assert vdm.is_currently_scrolling() is True + def test_format_date_with_ordinal(self): from datetime import datetime vdm = VisualTestDisplayManager(width=128, height=32)