mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-06 23:35:08 +00:00
Whole-tree audit. Every symbol was checked against core, the plugin monorepo and all eight third-party plugins in plugins.json first. - Deprecate (removal 3.10.0) plugin-facing methods nothing calls: LogoDownloader bulk download, ConfigManager backup/secret wrappers, APIHelper extras, BackgroundDataService poll API, PluginManager / PluginStateManager info readers, and a few CacheManager, FontManager, BaseOddsManager, DynamicTeamResolver methods and PluginTestCase. plugin_api_usage.py learns their receiver names; DEPRECATIONS doc regenerated. - Remove core-internal dead code: CacheMetrics, Vegas status/stats plumbing, sync "new cycle" message (followers ignore unknown types), unused operation types, test-only PluginCatalog readers, IPC to_dict and ping, _parse_form_value, CacheStrategyProtocol, ErrorAggregator callbacks, duplicate web response helpers. - Web UI: drop never-mounted json-file-manager.js, the example widget, utils/error_handler.js, four uncalled PluginAPI methods, and 29 escapeHtml shims (call window.LEDEscape directly). Public globals, BaseWidget and widget names unchanged. - Remove six one-off scripts (owner decision) and the unused markupsafe and pytest-mock pins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
139 lines
5.2 KiB
Python
139 lines
5.2 KiB
Python
"""run_iteration() does no per-iteration static-mode bookkeeping.
|
|
|
|
Every iteration used to rebuild a set of STATIC-mode plugins -- asking each
|
|
plugin for its display mode and logging the result at INFO -- that nothing
|
|
ever read. The static pause is driven by _check_static_plugin_trigger(), which
|
|
looks at the next segment, not at that set.
|
|
"""
|
|
|
|
import logging
|
|
import threading
|
|
from types import SimpleNamespace
|
|
from unittest.mock import MagicMock
|
|
|
|
from src.plugin_system.base_plugin import VegasDisplayMode
|
|
from src.vegas_mode.config import VegasModeConfig
|
|
from src.vegas_mode.coordinator import VegasModeCoordinator
|
|
|
|
|
|
def _coordinator(plugins):
|
|
coord = VegasModeCoordinator.__new__(VegasModeCoordinator)
|
|
coord.vegas_config = VegasModeConfig.from_config({'display': {'vegas_scroll': {
|
|
'enabled': True, 'max_cycle_duration': 0}}})
|
|
coord.render_pipeline = MagicMock()
|
|
# The loop paces itself from these (#628); a MagicMock can't be compared.
|
|
coord.render_pipeline.frame_interval = 0.0
|
|
coord.render_pipeline.target_fps = 90
|
|
coord.stream_manager = MagicMock()
|
|
coord.display_manager = MagicMock()
|
|
coord.plugin_manager = SimpleNamespace(plugins=plugins, get_plugin=plugins.get)
|
|
coord._state_lock = threading.Lock()
|
|
coord._is_active = True
|
|
coord._is_paused = False
|
|
coord._should_stop = False
|
|
coord._fps_last_health_log = 0.0
|
|
coord._fps_was_degraded = False
|
|
coord._live_priority_check = None
|
|
coord._live_priority_active = False
|
|
coord._interrupt_check = None
|
|
coord._interrupt_check_interval = 10
|
|
coord._update_callback = None
|
|
coord._update_tick_running = False
|
|
coord._check_static_plugin_trigger = lambda: None
|
|
coord.run_frame = lambda: True
|
|
return coord
|
|
|
|
|
|
def test_an_iteration_does_not_poll_every_plugin_for_its_mode(caplog):
|
|
static = MagicMock()
|
|
static.get_vegas_display_mode.return_value = VegasDisplayMode.STATIC
|
|
coord = _coordinator({'static-plugin': static})
|
|
|
|
with caplog.at_level(logging.INFO, logger='src.vegas_mode.coordinator'):
|
|
assert coord.run_iteration() is True
|
|
|
|
static.get_vegas_display_mode.assert_not_called()
|
|
assert not any('Static mode plugins' in r.getMessage() for r in caplog.records)
|
|
|
|
|
|
def _live_coordinator(live):
|
|
"""A coordinator running the real run_frame(), with a switchable live check.
|
|
|
|
live_in_ticker off: these pin the full-screen takeover, which is no longer
|
|
the default.
|
|
"""
|
|
coord = _coordinator({})
|
|
del coord.run_frame # the real one: it is what refuses frames while paused
|
|
coord.vegas_config = VegasModeConfig.from_config({'display': {'vegas_scroll': {
|
|
'enabled': True, 'max_cycle_duration': 0, 'continuous_scroll': True,
|
|
'live_in_ticker': False}}})
|
|
coord.render_pipeline.has_deferred.return_value = False
|
|
coord.render_pipeline.needs_extension.return_value = False
|
|
coord.render_pipeline.render_frame.return_value = True
|
|
coord._pending_config_update = False
|
|
coord._last_live_check = float('-inf')
|
|
coord._live_priority_check = lambda: live[0]
|
|
return coord
|
|
|
|
|
|
def test_vegas_resumes_after_a_live_priority_pause():
|
|
"""The pause for live content was only ever lifted from inside run_frame(),
|
|
which returns before reaching that check while paused -- so once live
|
|
content had interrupted the ticker, it never came back until a restart.
|
|
The display controller only calls run_iteration() when nothing preempts
|
|
Vegas, so an iteration starting while paused for live content resumes."""
|
|
live = ['nfl_live']
|
|
coord = _live_coordinator(live)
|
|
|
|
assert coord.run_iteration() is False
|
|
assert coord._is_paused and coord._live_priority_active
|
|
coord.render_pipeline.render_frame.assert_not_called()
|
|
|
|
live[0] = None
|
|
coord._last_live_check = float('-inf')
|
|
assert coord.run_iteration() is True
|
|
assert not coord._is_paused and not coord._live_priority_active
|
|
coord.render_pipeline.render_frame.assert_called()
|
|
|
|
|
|
def test_stop_clears_a_live_priority_pause():
|
|
coord = _live_coordinator(['nfl_live'])
|
|
coord._restore_switch_interval = lambda: None
|
|
coord._remove_render_gate = lambda: None
|
|
coord.run_iteration()
|
|
assert coord._is_paused
|
|
|
|
coord.stop()
|
|
assert not coord._is_paused and not coord._live_priority_active
|
|
|
|
|
|
def test_iteration_length_ignores_wall_clock_steps(monkeypatch):
|
|
"""No RTC: the wall clock jumps when NTP first syncs. A forward step used
|
|
to end the iteration on its first frame."""
|
|
from src.vegas_mode import coordinator as coordinator_module
|
|
coord = _coordinator({})
|
|
coord.vegas_config = VegasModeConfig.from_config({'display': {'vegas_scroll': {
|
|
'enabled': True, 'max_cycle_duration': 10}}})
|
|
frames = [0]
|
|
|
|
def frame():
|
|
frames[0] += 1
|
|
return True
|
|
coord.run_frame = frame
|
|
clock = [1000.0]
|
|
wall = [1000.0]
|
|
|
|
def monotonic():
|
|
clock[0] += 0.5
|
|
return clock[0]
|
|
|
|
def wall_time():
|
|
wall[0] += 3600.0 # NTP stepping the wall clock forward
|
|
return wall[0]
|
|
monkeypatch.setattr(coordinator_module.time, 'monotonic', monotonic)
|
|
monkeypatch.setattr(coordinator_module.time, 'time', wall_time)
|
|
monkeypatch.setattr(coordinator_module.time, 'sleep', lambda s: None)
|
|
|
|
assert coord.run_iteration() is True
|
|
assert frames[0] > 1
|