mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-05 06:45:09 +00:00
fix(display): Vegas resumes after live priority, and six smaller runtime fixes (#644)
- Vegas: a live-priority pause was only lifted from inside run_frame(), which returns before that check while paused, so the ticker never came back until a restart. run_iteration() now resumes it (the controller only calls it when nothing preempts Vegas); start()/stop() clear the pause state. Iteration length is timed with the monotonic clock. - Dim schedule: a per-day disabled day now updates the minute-gate cache, so brightness no longer flips back to dim within each minute. - On-demand: a second request no longer overwrites the rotation resume index with the first request's mode. - Render pipeline: reset() drops the prepared group and deferred queue, and a prefetch in flight across a reset discards its result. - Sync: stop() removes the status file (and the controller's cleanup now calls it), standalone removes a stale one at startup, and writes use a unique mkstemp temp file. - render_gate.swap_releases_gil() delegates to frame_timing. - Stale docstrings/comments corrected. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -55,3 +55,81 @@ def test_an_iteration_does_not_poll_every_plugin_for_its_mode(caplog):
|
||||
|
||||
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."""
|
||||
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}}})
|
||||
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._start_time = None
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user