Skip to content

Commit 30409ef

Browse files
committed
Stabilize preview rendering GUI test
Refactors `test_preview_renders_frames` to remove real camera timing dependencies by monkeypatching controller lifecycle methods and emitting synthetic `MultiFrameData` via `QTimer`. The test now explicitly verifies preview start/stop state changes, frame assignment, and that the displayed pixmap actually updates (via cache key), making the functional GUI test faster and less flaky.
1 parent 4807ab8 commit 30409ef

1 file changed

Lines changed: 67 additions & 14 deletions

File tree

‎tests/gui/test_main.py‎

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import numpy as np
12
import pytest
2-
from PySide6.QtCore import Qt
3+
from PySide6.QtCore import Qt, QTimer
34
from PySide6.QtGui import QImage
45

56
from dlclivegui.services.dlc_processor import DLCLiveProcessor, Engine
7+
from dlclivegui.services.multi_camera_controller import MultiFrameData
68

79

810
def pixmap_bytes(label) -> bytes:
@@ -16,30 +18,81 @@ def pixmap_bytes(label) -> bytes:
1618

1719
@pytest.mark.gui
1820
@pytest.mark.functional
19-
def test_preview_renders_frames(qtbot, window, multi_camera_controller):
20-
"""
21-
Validate that:
22-
- Preview starts (`preview_button` clicked)
23-
- Camera controller emits all_started
24-
- GUI receives and renders frames to video_label.pixmap()
25-
- Preview stops cleanly
26-
"""
27-
21+
def test_preview_renders_frames(
22+
qtbot,
23+
window,
24+
multi_camera_controller,
25+
monkeypatch,
26+
):
27+
"""Verify preview controls and frame rendering without camera timing."""
2828
w = window
2929
ctrl = multi_camera_controller
30+
running = False
31+
32+
camera_id = "fake:index:0"
33+
frame = np.full((120, 160, 3), 127, dtype=np.uint8)
34+
frame_data = MultiFrameData(
35+
frames={camera_id: frame},
36+
timestamps={camera_id: 123.0},
37+
source_camera_id=camera_id,
38+
display_ids={camera_id: "Test camera"},
39+
)
3040

31-
with qtbot.waitSignal(ctrl.all_started, timeout=4000):
41+
initial_pixmap = w.video_label.pixmap()
42+
initial_cache_key = initial_pixmap.cacheKey() if initial_pixmap is not None else None
43+
44+
def fake_is_running():
45+
return running
46+
47+
def fake_get_active_count():
48+
return 1 if running else 0
49+
50+
def fake_start(_camera_settings):
51+
nonlocal running
52+
running = True
53+
54+
def finish_start():
55+
ctrl.all_started.emit()
56+
ctrl.frame_ready.emit(frame_data)
57+
ctrl.display_ready.emit(frame_data)
58+
59+
QTimer.singleShot(0, finish_start)
60+
61+
def fake_stop(*, wait=True):
62+
nonlocal running
63+
running = False
64+
QTimer.singleShot(0, ctrl.all_stopped.emit)
65+
66+
monkeypatch.setattr(ctrl, "is_running", fake_is_running)
67+
monkeypatch.setattr(
68+
ctrl,
69+
"get_active_count",
70+
fake_get_active_count,
71+
)
72+
monkeypatch.setattr(ctrl, "start", fake_start)
73+
monkeypatch.setattr(ctrl, "stop", fake_stop)
74+
75+
with qtbot.waitSignal(ctrl.all_started, timeout=1000):
3276
qtbot.mouseClick(w.preview_button, Qt.LeftButton)
3377

3478
qtbot.waitUntil(
35-
lambda: w.video_label.pixmap() is not None and not w.video_label.pixmap().isNull(),
36-
timeout=6000,
79+
lambda: (
80+
w._current_frame is not None
81+
and w.video_label.pixmap() is not None
82+
and not w.video_label.pixmap().isNull()
83+
and w.video_label.pixmap().cacheKey() != initial_cache_key
84+
),
85+
timeout=1000,
3786
)
3887

39-
with qtbot.waitSignal(ctrl.all_stopped, timeout=4000):
88+
assert w._current_frame is not None
89+
assert w.stop_preview_button.isEnabled()
90+
91+
with qtbot.waitSignal(ctrl.all_stopped, timeout=1000):
4092
qtbot.mouseClick(w.stop_preview_button, Qt.LeftButton)
4193

4294
assert not ctrl.is_running()
95+
assert w._current_frame is None
4396

4497

4598
@pytest.mark.gui

0 commit comments

Comments
 (0)