Files
StarPilot/selfdrive/ui/tests/test_stopped_timer_widget.py
2026-08-02 22:53:46 -04:00

109 lines
3.4 KiB
Python

import importlib.util
import sys
from pathlib import Path
from types import ModuleType, SimpleNamespace
def _load_stopped_timer(monkeypatch):
rl = SimpleNamespace(
Color=lambda r, g, b, a=255: SimpleNamespace(r=r, g=g, b=b, a=a),
Rectangle=lambda x=0, y=0, width=0, height=0: SimpleNamespace(x=x, y=y, width=width, height=height),
Vector2=lambda x, y: SimpleNamespace(x=x, y=y),
WHITE=SimpleNamespace(r=255, g=255, b=255, a=255),
draw_text_ex=lambda *_args: None,
)
monkeypatch.setitem(sys.modules, "pyray", rl)
def module(name, **attributes):
result = ModuleType(name)
for key, value in attributes.items():
setattr(result, key, value)
monkeypatch.setitem(sys.modules, name, result)
class Widget:
def __init__(self):
pass
def set_enabled(self, _enabled):
pass
module("openpilot.system.ui.widgets", Widget=Widget)
module(
"openpilot.system.ui.lib.application",
FontWeight=SimpleNamespace(BOLD=1, NORMAL=2),
gui_app=SimpleNamespace(font=lambda *_args: None),
)
module(
"openpilot.system.ui.lib.text_measure",
measure_text_cached=lambda *_args: SimpleNamespace(x=100, y=20),
)
module(
"openpilot.selfdrive.ui.lib.starpilot_status",
ENGAGED_COLOR=rl.Color(22, 127, 64),
EXPERIMENTAL_COLOR=rl.Color(218, 111, 37),
TRAFFIC_COLOR=rl.Color(201, 34, 49),
)
module("openpilot.selfdrive.ui.ui_state", ui_state=SimpleNamespace())
module_path = Path(__file__).parents[1] / "onroad/starpilot/widgets/stopped_timer.py"
spec = importlib.util.spec_from_file_location("stopped_timer_under_test", module_path)
stopped_timer = importlib.util.module_from_spec(spec)
spec.loader.exec_module(stopped_timer)
return stopped_timer
def test_stopped_timer_visibility_waits_for_onroad_grace_period(monkeypatch):
stopped_timer = _load_stopped_timer(monkeypatch)
params = SimpleNamespace(get_bool=lambda key: key in {"QOLVisuals", "StoppedTimer"})
car_state = SimpleNamespace(standstill=True)
class SubMaster:
valid = {"carState": True}
recv_frame = {"carState": 1}
def __getitem__(self, _key):
return car_state
ui_state = SimpleNamespace(
started=True,
started_frame=1,
started_time=100.0,
ui_params=params,
sm=SubMaster(),
)
monkeypatch.setattr(stopped_timer, "ui_state", ui_state)
now = iter((100.0, 159.9, 160.0))
monkeypatch.setattr(stopped_timer.time, "monotonic", lambda: next(now))
widget = stopped_timer.StoppedTimerWidget()
assert not widget.is_visible
assert not widget.is_visible
assert widget.is_visible
def test_stopped_timer_uses_qt_text_contract(monkeypatch):
stopped_timer = _load_stopped_timer(monkeypatch)
assert stopped_timer.StoppedTimerWidget._format_duration_text(61) == ("1 minute", "1 second")
assert stopped_timer.StoppedTimerWidget._format_duration_text(121) == ("2 minutes", "1 second")
def test_stopped_timer_draws_qt_positions_and_opaque_seconds(monkeypatch):
stopped_timer = _load_stopped_timer(monkeypatch)
widget = stopped_timer.StoppedTimerWidget()
widget._duration = 61
draws = []
monkeypatch.setattr(stopped_timer.rl, "draw_text_ex", lambda *args: draws.append(args))
widget._render(stopped_timer.rl.Rectangle(0, 0, 2160, 1080))
assert draws[0][2].x == 1030
assert draws[0][2].y == 190
assert draws[0][3] == 176
assert draws[1][2].x == 1030
assert draws[1][2].y == 270
assert draws[1][3] == 66
assert draws[1][5].a == 255