-
-
Notifications
You must be signed in to change notification settings - Fork 27
fix(vegas): restore live plugin-update refresh dropped by sync refactor #395
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+196
−1
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| """ | ||
| Regression tests for DisplayController._tick_plugin_updates_for_vegas(). | ||
|
|
||
| PR #299 added logic to detect which plugins actually got fresh data on a | ||
| scheduled-update tick and notify Vegas mode via | ||
| vegas_coordinator.mark_plugin_updated(), so a live score change reaches the | ||
| scroll within seconds instead of waiting for a full cycle. PR #330's | ||
| multi-display sync refactor deleted this method (folding the callback back | ||
| to the plain _tick_plugin_updates(), which reports nothing), silently | ||
| orphaning VegasModeCoordinator.mark_plugin_updated() -- it has had zero | ||
| callers since. | ||
| """ | ||
|
|
||
| from unittest.mock import MagicMock | ||
|
|
||
| from src.display_controller import DisplayController | ||
|
|
||
|
|
||
| def _make_controller(plugin_last_update, vegas_coordinator=None): | ||
| dc = object.__new__(DisplayController) | ||
| dc.plugin_manager = MagicMock() | ||
| dc.plugin_manager.plugin_last_update = dict(plugin_last_update) | ||
| dc.vegas_coordinator = vegas_coordinator | ||
| return dc | ||
|
|
||
|
|
||
| class TestTickPluginUpdatesForVegas: | ||
| def test_marks_only_plugins_whose_timestamp_advanced(self): | ||
| dc = _make_controller({"stock-news": 100.0, "odds-ticker": 100.0}) | ||
| vc = MagicMock() | ||
| dc.vegas_coordinator = vc | ||
|
|
||
| # Simulate run_scheduled_updates() advancing only stock-news. | ||
| def fake_tick(): | ||
| dc.plugin_manager.plugin_last_update["stock-news"] = 200.0 | ||
| dc._tick_plugin_updates = fake_tick | ||
|
|
||
| dc._tick_plugin_updates_for_vegas() | ||
|
|
||
| vc.mark_plugin_updated.assert_called_once_with("stock-news") | ||
|
|
||
| def test_no_advance_marks_nothing(self): | ||
| dc = _make_controller({"stock-news": 100.0}) | ||
| vc = MagicMock() | ||
| dc.vegas_coordinator = vc | ||
| dc._tick_plugin_updates = lambda: None | ||
|
|
||
| dc._tick_plugin_updates_for_vegas() | ||
|
|
||
| vc.mark_plugin_updated.assert_not_called() | ||
|
|
||
| def test_no_vegas_coordinator_does_not_raise(self): | ||
| dc = _make_controller({"stock-news": 100.0}, vegas_coordinator=None) | ||
|
|
||
| def fake_tick(): | ||
| dc.plugin_manager.plugin_last_update["stock-news"] = 200.0 | ||
| dc._tick_plugin_updates = fake_tick | ||
|
|
||
| dc._tick_plugin_updates_for_vegas() # must not raise | ||
|
|
||
| def test_mark_plugin_updated_exception_does_not_propagate(self): | ||
| """One plugin's mark_plugin_updated failing must not stop the tick | ||
| or crash the update loop it runs in.""" | ||
| dc = _make_controller({"a": 1.0, "b": 1.0}) | ||
| vc = MagicMock() | ||
| vc.mark_plugin_updated.side_effect = [RuntimeError("boom"), None] | ||
| dc.vegas_coordinator = vc | ||
|
|
||
| def fake_tick(): | ||
| dc.plugin_manager.plugin_last_update["a"] = 2.0 | ||
| dc.plugin_manager.plugin_last_update["b"] = 2.0 | ||
| dc._tick_plugin_updates = fake_tick | ||
|
|
||
| dc._tick_plugin_updates_for_vegas() # must not raise | ||
|
|
||
| assert vc.mark_plugin_updated.call_count == 2 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| """ | ||
| Regression tests for RenderPipeline.should_recompose()'s pending-updates check. | ||
|
|
||
| PR #299 added a check so a plugin's live score/status change (a "pending | ||
| update" in StreamManager) triggers a hot-swap within a few seconds instead | ||
| of waiting for a full scroll cycle to complete. PR #330 (multi-display sync) | ||
| refactored should_recompose() and dropped that check entirely -- not just | ||
| gated behind the new sync-mode deferral it added, but removed outright, so | ||
| even standalone (non-sync) installations silently lost live-refresh and fell | ||
| back to waiting for full cycle boundaries (which, depending on | ||
| min/max_cycle_duration, can be minutes). | ||
| """ | ||
|
|
||
| from unittest.mock import MagicMock | ||
|
|
||
| from src.vegas_mode.config import VegasModeConfig | ||
| from src.vegas_mode.render_pipeline import RenderPipeline | ||
|
|
||
|
|
||
| class FakeDisplayManager: | ||
| width = 64 | ||
| height = 32 | ||
|
|
||
|
|
||
| def _make_pipeline(sync_manager=None): | ||
| stream_manager = MagicMock() | ||
| stream_manager.get_buffer_status.return_value = {'staging_count': 0} | ||
| pipeline = RenderPipeline(VegasModeConfig(), FakeDisplayManager(), stream_manager) | ||
| pipeline.sync_manager = sync_manager | ||
| return pipeline, stream_manager | ||
|
|
||
|
|
||
| class TestShouldRecompose: | ||
| def test_cycle_complete_always_recomposes(self): | ||
| pipeline, stream_manager = _make_pipeline() | ||
| pipeline._cycle_complete = True | ||
| stream_manager.has_pending_updates_for_visible_segments.return_value = False | ||
| assert pipeline.should_recompose() is True | ||
|
|
||
| def test_no_pending_updates_no_staging_does_not_recompose(self): | ||
| pipeline, stream_manager = _make_pipeline() | ||
| stream_manager.has_pending_updates_for_visible_segments.return_value = False | ||
| assert pipeline.should_recompose() is False | ||
|
|
||
| def test_pending_updates_on_visible_segment_triggers_recompose(self): | ||
| """The actual regression: a live-updated plugin currently in view | ||
| must trigger a recompose instead of waiting for cycle end.""" | ||
| pipeline, stream_manager = _make_pipeline() | ||
| stream_manager.has_pending_updates_for_visible_segments.return_value = True | ||
| assert pipeline.should_recompose() is True | ||
|
|
||
| def test_staging_buffer_content_triggers_recompose(self): | ||
| pipeline, stream_manager = _make_pipeline() | ||
| stream_manager.get_buffer_status.return_value = {'staging_count': 1} | ||
| stream_manager.has_pending_updates_for_visible_segments.return_value = False | ||
| assert pipeline.should_recompose() is True | ||
|
|
||
| def test_sync_active_defers_pending_updates_to_cycle_boundary(self): | ||
| """Sync-mode deferral (PR #330's actual intent) must still hold: | ||
| pending updates alone must NOT trigger a mid-cycle hot-swap when a | ||
| follower display is attached, since that causes a visible | ||
| freeze+jump on the follower. This must keep working after | ||
| restoring the non-sync pending-updates check above.""" | ||
| pipeline, stream_manager = _make_pipeline(sync_manager=MagicMock()) | ||
| stream_manager.has_pending_updates_for_visible_segments.return_value = True | ||
| assert pipeline.should_recompose() is False | ||
|
|
||
| def test_sync_active_still_recomposes_on_cycle_complete(self): | ||
| pipeline, stream_manager = _make_pipeline(sync_manager=MagicMock()) | ||
| pipeline._cycle_complete = True | ||
| stream_manager.has_pending_updates_for_visible_segments.return_value = True | ||
| assert pipeline.should_recompose() is True |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.