mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 05:23:41 +08:00
441 lines
22 KiB
Python
441 lines
22 KiB
Python
from unittest.mock import Mock, patch
|
|
|
|
import pyray as rl
|
|
import pytest
|
|
from types import SimpleNamespace
|
|
|
|
from openpilot.selfdrive.ui.mici.onroad.augmented_road_view import AugmentedRoadView
|
|
from openpilot.selfdrive.ui.onroad import model_source
|
|
from openpilot.selfdrive.ui.ui_state import ChestnutState, UIState
|
|
|
|
|
|
@pytest.mark.parametrize('raw, expected', [(True, True), (False, False), (b'1', True), (b'0', False),
|
|
('1', True), ('0', False), (None, None), (b'invalid', None)])
|
|
def test_native_chestnut_status_accepts_typed_params(raw, expected):
|
|
params = Mock()
|
|
params.get.return_value = raw
|
|
params.get_bool.return_value = False
|
|
state = Mock(spec=UIState, params=params, started=True, chestnut_compiled=True, usb_connected=False)
|
|
with patch('openpilot.selfdrive.ui.ui_state.get_cache', return_value=None), \
|
|
patch('openpilot.selfdrive.ui.ui_state.cable_connected', return_value=False):
|
|
UIState.update_params(state)
|
|
assert state.chestnut_active is expected
|
|
|
|
|
|
@pytest.mark.parametrize('indicator', [False, None])
|
|
def test_custom_model_source_layer_uses_native_status_animation(indicator):
|
|
view = Mock(spec=AugmentedRoadView, _hud_renderer=Mock(), _long_indicator=Mock())
|
|
rect = rl.Rectangle(0, 0, 476, 240)
|
|
AugmentedRoadView.render_model_source_layer(view, rect, show_long_indicator=indicator)
|
|
view._hud_renderer._draw_model_source.assert_called_once_with(rect)
|
|
if indicator is None:
|
|
view._long_indicator.set_should_draw.assert_not_called()
|
|
view._long_indicator.render.assert_not_called()
|
|
else:
|
|
view._long_indicator.set_should_draw.assert_called_once_with(False)
|
|
view._long_indicator.render.assert_called_once_with(rect)
|
|
|
|
|
|
@pytest.mark.parametrize('compiled', [False, True])
|
|
@pytest.mark.parametrize('prior', [ChestnutState.UNCOMPILED, ChestnutState.FAILED, ChestnutState.LOADING])
|
|
def test_catalog_gpu_output_does_not_require_retired_bundled_artifact(compiled, prior):
|
|
sm = Mock()
|
|
sm.__getitem__ = Mock(side_effect=lambda service: SimpleNamespace(chestnutPresent=True) if service == 'deviceState' else SimpleNamespace(big=True))
|
|
sm.recv_frame = {'modelV2': 12}
|
|
sm.logMonoTime = {'modelV2': 1_000_000_000}
|
|
sm.valid = {'modelV2': True}
|
|
sm.alive = {'modelV2': True}
|
|
state = Mock(spec=UIState, sm=sm, started=True, started_frame=10, chestnut_present=True, chestnut_compiled=compiled,
|
|
chestnut_loading=False, chestnut_active=True, chestnut_state=prior)
|
|
with patch('openpilot.selfdrive.ui.ui_state.time.monotonic_ns', return_value=1_100_000_000):
|
|
UIState._update_chestnut_state(state)
|
|
assert state.chestnut_state == ChestnutState.ACTIVE
|
|
|
|
|
|
@pytest.mark.parametrize('big, valid, alive, age, active', [(False, True, True, 100_000_000, False),
|
|
(True, False, True, 100_000_000, True),
|
|
(True, True, False, 100_000_000, True),
|
|
(True, True, True, 300_000_000, True)])
|
|
def test_catalog_gpu_fallback_or_stale_output_stays_failed(big, valid, alive, age, active):
|
|
sm = Mock()
|
|
sm.__getitem__ = Mock(side_effect=lambda service: SimpleNamespace(chestnutPresent=True) if service == 'deviceState' else SimpleNamespace(big=big))
|
|
sm.recv_frame = {'modelV2': 12}
|
|
sm.logMonoTime = {'modelV2': 1_000_000_000}
|
|
sm.valid = {'modelV2': valid}
|
|
sm.alive = {'modelV2': alive}
|
|
state = Mock(spec=UIState, sm=sm, started=True, started_frame=10, chestnut_present=True, chestnut_compiled=False,
|
|
chestnut_loading=False, chestnut_active=active, chestnut_output_seen=True, chestnut_state=ChestnutState.ACTIVE)
|
|
with patch('openpilot.selfdrive.ui.ui_state.time.monotonic_ns', return_value=1_000_000_000 + age):
|
|
UIState._update_chestnut_state(state)
|
|
assert state.chestnut_state == ChestnutState.FAILED
|
|
|
|
|
|
@pytest.mark.parametrize('detected,installed,checking,expected', [
|
|
(True, True, False, ChestnutState.READY),
|
|
(True, False, True, ChestnutState.LOADING),
|
|
(True, False, False, ChestnutState.UNCOMPILED),
|
|
(False, True, False, ChestnutState.DISCONNECTED),
|
|
])
|
|
def test_offroad_status_uses_selected_download(detected, installed, checking, expected):
|
|
sm = Mock()
|
|
sm.__getitem__ = Mock(return_value=SimpleNamespace(chestnutPresent=detected))
|
|
state = Mock(spec=UIState, sm=sm, started=False, chestnut_alert=False, chestnut_compiled=installed, chestnut_checking=checking)
|
|
UIState._update_chestnut_state(state)
|
|
assert state.chestnut_state == expected
|
|
|
|
|
|
def test_offroad_selection_rechecks_without_a_ui_restart():
|
|
params = Mock()
|
|
params.get.return_value = None
|
|
params.get_bool.return_value = False
|
|
artifacts = Mock()
|
|
artifacts.selected_gpu_status.side_effect = [(False, True), (True, False), (False, False)]
|
|
state = Mock(spec=UIState, params=params, started=False, _gpu_artifacts=artifacts, _gpu_artifacts_at=None,
|
|
usb_connected=False)
|
|
with patch('openpilot.selfdrive.ui.ui_state.get_cache', return_value=None), \
|
|
patch('openpilot.selfdrive.ui.ui_state.cable_connected', return_value=False), \
|
|
patch('openpilot.selfdrive.ui.ui_state.time.monotonic', side_effect=[1., 1.2, 2., 3., 4.]):
|
|
UIState.update_params(state)
|
|
assert state.chestnut_checking and not state.chestnut_compiled
|
|
UIState.update_params(state)
|
|
artifacts.selected_gpu_status.assert_called_once()
|
|
UIState.update_params(state)
|
|
assert state.chestnut_compiled and not state.chestnut_checking
|
|
UIState.update_params(state)
|
|
assert not state.chestnut_compiled and not state.chestnut_checking
|
|
state.started = True
|
|
UIState.update_params(state)
|
|
assert artifacts.selected_gpu_status.call_count == 3
|
|
|
|
|
|
@pytest.fixture(params=[1, 3])
|
|
def source(request, monkeypatch):
|
|
monkeypatch.setattr(model_source.gui_app, 'texture', lambda path, width, height: SimpleNamespace(path=path, width=width, height=height))
|
|
ui = SimpleNamespace(started_frame=10, chestnut_present=True, chestnut_output_seen=False, chestnut_state=ChestnutState.LOADING,
|
|
chestnut_loading=False, chestnut_active=False, replay_clock=None)
|
|
ui.sm = Mock(recv_frame={'carState': 12, 'modelV2': 12})
|
|
ui.sm.__getitem__ = Mock(return_value=SimpleNamespace(enabled=False))
|
|
clock, draw = Mock(return_value=10.), Mock()
|
|
monkeypatch.setattr(model_source, 'ui_state', ui)
|
|
monkeypatch.setattr(rl, 'get_time', clock)
|
|
monkeypatch.setattr(rl, 'draw_texture_ex', draw)
|
|
return SimpleNamespace(widget=model_source.ModelSource(scale=request.param), ui=ui, clock=clock, draw=draw, scale=request.param)
|
|
|
|
|
|
@pytest.mark.parametrize('state,small_engaged,texture', [
|
|
(ChestnutState.LOADING, False, '_loading'),
|
|
(ChestnutState.LOADING, True, '_loading'),
|
|
(ChestnutState.ACTIVE, False, '_active'),
|
|
(ChestnutState.UNCOMPILED, False, '_failed'),
|
|
(ChestnutState.FAILED, False, '_failed'),
|
|
(ChestnutState.FAILED, True, '_fallback'),
|
|
(ChestnutState.DISCONNECTED, True, '_fallback'),
|
|
(ChestnutState.ACTIVE, True, '_active'),
|
|
])
|
|
def test_shared_gpu_icons_and_alignment(source, state, small_engaged, texture):
|
|
source.ui.chestnut_state = state
|
|
source.widget._small_model_engaged = small_engaged
|
|
rect = rl.Rectangle(30, 30, 2070, 1020)
|
|
source.widget._render(rect)
|
|
icon, pos = source.draw.call_args.args[:2]
|
|
assert icon is getattr(source.widget, texture)
|
|
assert pos.x + icon.width == rect.x + rect.width
|
|
assert pos.y + icon.height / 2 == rect.y + rect.height / 2
|
|
assert icon.width == (75 if texture == '_failed' else 60) * source.scale
|
|
assert icon.height == (52 if texture == '_fallback' else 44) * source.scale
|
|
|
|
|
|
@pytest.mark.parametrize('state', [ChestnutState.DISCONNECTED, ChestnutState.READY])
|
|
def test_shared_gpu_icon_hidden_without_a_status(source, state):
|
|
source.ui.chestnut_state = state
|
|
source.widget._render(rl.Rectangle(0, 0, 476, 240))
|
|
source.draw.assert_not_called()
|
|
|
|
|
|
def test_shared_gpu_loading_pulses_without_disappearing(source):
|
|
import math
|
|
|
|
source.widget._alpha_filter.x = 1.
|
|
source.clock.return_value = 0.
|
|
source.widget._render(rl.Rectangle(0, 0, 476, 240))
|
|
low = source.draw.call_args.args[-1].a
|
|
source.clock.return_value = math.pi / 6
|
|
source.widget._render(rl.Rectangle(0, 0, 476, 240))
|
|
high = source.draw.call_args.args[-1].a
|
|
assert 0 < low < high == 255
|
|
|
|
|
|
@pytest.mark.parametrize('state', [ChestnutState.LOADING, ChestnutState.ACTIVE, ChestnutState.FAILED])
|
|
def test_shared_gpu_loading_persists_and_other_states_fade(source, state):
|
|
source.ui.chestnut_state = state
|
|
rect = rl.Rectangle(0, 0, 476, 240)
|
|
source.widget._render(rect)
|
|
source.clock.return_value = 13.
|
|
for _ in range(120):
|
|
source.widget._render(rect)
|
|
source.draw.reset_mock()
|
|
source.widget._render(rect)
|
|
assert source.draw.called == (state == ChestnutState.LOADING)
|
|
source.ui.chestnut_state = ChestnutState.ACTIVE if state == ChestnutState.FAILED else ChestnutState.FAILED
|
|
source.widget._render(rect)
|
|
assert source.draw.call_args.args[0] is (source.widget._active if state == ChestnutState.FAILED else source.widget._failed)
|
|
|
|
|
|
@pytest.mark.parametrize('recv_frame,loading,active,expected', [(9, False, False, False), (12, True, False, False),
|
|
(12, False, True, False), (12, False, False, True)])
|
|
def test_shared_gpu_marks_small_model_on_engagement(source, recv_frame, loading, active, expected):
|
|
source.ui.sm.recv_frame['modelV2'] = recv_frame
|
|
source.ui.sm.__getitem__.return_value.enabled = True
|
|
source.ui.chestnut_loading, source.ui.chestnut_active = loading, active
|
|
source.widget._update_state()
|
|
assert source.widget._small_model_engaged is expected
|
|
|
|
|
|
@pytest.mark.parametrize('present,detected', [(False, False), (True, True), (True, False)])
|
|
@pytest.mark.parametrize('active', [False, None])
|
|
def test_gpu_fallback_on_engagement_requires_chestnut_for_this_drive(source, present, detected, active):
|
|
ui = source.ui
|
|
ui.started, ui.replay_clock = True, None
|
|
ui.chestnut_present, ui.chestnut_output_seen, ui.chestnut_active = present, present, active
|
|
ui.sm.__getitem__.return_value = SimpleNamespace(chestnutPresent=detected, enabled=False, big=False)
|
|
ui.sm.logMonoTime = {'modelV2': 1_000_000_000}
|
|
ui.sm.valid = ui.sm.alive = {'modelV2': True}
|
|
with patch('openpilot.selfdrive.ui.ui_state.time.monotonic_ns', return_value=1_100_000_000):
|
|
UIState._update_chestnut_state(ui)
|
|
assert ui.chestnut_state == (ChestnutState.FAILED if present else ChestnutState.DISCONNECTED)
|
|
assert ui.chestnut_present is present
|
|
for now, engaged in ((10., False), (14., True), (18., False), (22., True)):
|
|
source.clock.return_value = now
|
|
ui.sm.__getitem__.return_value.enabled = engaged
|
|
source.draw.reset_mock()
|
|
source.widget._update_state()
|
|
source.widget._render(rl.Rectangle(30, 30, 2100, 1020))
|
|
if not present:
|
|
source.draw.assert_not_called()
|
|
elif engaged:
|
|
assert source.draw.call_args.args[0] is source.widget._fallback
|
|
|
|
|
|
def test_gpu_fallback_clears_before_next_drive_without_chestnut(source):
|
|
source.ui.chestnut_state = ChestnutState.FAILED
|
|
source.ui.chestnut_output_seen = True
|
|
source.ui.sm.__getitem__.return_value.enabled = True
|
|
rect = rl.Rectangle(30, 30, 2100, 1020)
|
|
source.widget._update_state()
|
|
source.widget._render(rect)
|
|
assert source.draw.call_args.args[0] is source.widget._fallback
|
|
source.ui.chestnut_present = False
|
|
source.ui.chestnut_output_seen = False
|
|
source.ui.chestnut_state = ChestnutState.DISCONNECTED
|
|
source.ui.started_frame = 20
|
|
source.ui.sm.recv_frame = {'carState': 22, 'modelV2': 22}
|
|
for now, engaged in ((20., False), (24., True)):
|
|
source.clock.return_value = now
|
|
source.ui.sm.__getitem__.return_value.enabled = engaged
|
|
source.draw.reset_mock()
|
|
source.widget._update_state()
|
|
source.widget._render(rect)
|
|
source.draw.assert_not_called()
|
|
source.ui.chestnut_present = True
|
|
source.ui.chestnut_state = ChestnutState.FAILED
|
|
source.ui.started_frame = 30
|
|
source.ui.sm.recv_frame = {'carState': 32, 'modelV2': 32}
|
|
for now, engaged, icon in ((30., False, '_failed'), (34., True, '_fallback')):
|
|
source.clock.return_value = now
|
|
source.ui.sm.__getitem__.return_value.enabled = engaged
|
|
source.draw.reset_mock()
|
|
source.widget._update_state()
|
|
source.widget._render(rect)
|
|
assert source.draw.call_args.args[0] is getattr(source.widget, icon)
|
|
|
|
|
|
@pytest.mark.parametrize('replay', [False, True])
|
|
@pytest.mark.parametrize('initial', [ChestnutState.LOADING, ChestnutState.ACTIVE])
|
|
def test_gpu_disconnect_warning_survives_lost_presence_during_same_drive(source, replay, initial):
|
|
ui = source.ui
|
|
ui.started, ui.replay_clock = True, object() if replay else None
|
|
ui.replay_sample = SimpleNamespace(valid=True, now_ns=1_100_000_000)
|
|
ui.chestnut_output_seen = False
|
|
ui.chestnut_loading = initial == ChestnutState.LOADING
|
|
ui.sm.__getitem__.return_value = SimpleNamespace(chestnutPresent=True, enabled=False, big=initial == ChestnutState.ACTIVE)
|
|
ui.sm.logMonoTime = {'modelV2': 1_000_000_000}
|
|
ui.sm.valid = {'modelV2': initial == ChestnutState.ACTIVE}
|
|
ui.sm.alive = {'modelV2': True}
|
|
rect = rl.Rectangle(30, 30, 2100, 1020)
|
|
with patch('openpilot.selfdrive.ui.ui_state.time.monotonic_ns', return_value=1_100_000_000):
|
|
UIState._update_chestnut_state(ui)
|
|
assert ui.chestnut_state == initial
|
|
source.widget._update_state()
|
|
source.widget._render(rect)
|
|
ui.sm.__getitem__.return_value = SimpleNamespace(chestnutPresent=False, enabled=True, big=False)
|
|
ui.sm.valid['modelV2'] = True
|
|
ui.chestnut_loading, ui.chestnut_active = False, False
|
|
UIState._update_chestnut_state(ui)
|
|
assert ui.chestnut_state == (ChestnutState.DISCONNECTED if replay else ChestnutState.FAILED)
|
|
source.draw.reset_mock()
|
|
source.widget._update_state()
|
|
source.widget._render(rect)
|
|
assert source.draw.call_args.args[0] is source.widget._fallback
|
|
|
|
|
|
@pytest.mark.parametrize('had_gpu', [False, True])
|
|
def test_replay_gpu_fallback_after_returning_to_onroad(source, had_gpu):
|
|
ui = source.ui
|
|
ui.started, ui.replay_clock = True, object()
|
|
ui.chestnut_present = had_gpu
|
|
ui.replay_sample = SimpleNamespace(valid=True, now_ns=1_100_000_000)
|
|
ui.sm.logMonoTime = {'modelV2': 1_000_000_000}
|
|
ui.sm.valid = ui.sm.alive = {'modelV2': True}
|
|
ui.sm.__getitem__.return_value = SimpleNamespace(chestnutPresent=had_gpu, enabled=False, big=had_gpu)
|
|
UIState._update_chestnut_state(ui)
|
|
assert ui.chestnut_state == (ChestnutState.ACTIVE if had_gpu else ChestnutState.DISCONNECTED)
|
|
assert ui.chestnut_output_seen is had_gpu
|
|
ui.sm.__getitem__.return_value = SimpleNamespace(chestnutPresent=False, enabled=True, big=False)
|
|
UIState._update_chestnut_state(ui)
|
|
assert ui.chestnut_state == ChestnutState.DISCONNECTED
|
|
assert ui.chestnut_output_seen is had_gpu
|
|
source.widget._update_state()
|
|
source.widget._render(rl.Rectangle(30, 30, 2100, 1020))
|
|
if had_gpu:
|
|
source.draw.assert_called_once()
|
|
assert source.draw.call_args.args[0] is source.widget._fallback
|
|
else:
|
|
source.draw.assert_not_called()
|
|
|
|
|
|
@pytest.mark.parametrize('active', [False, None])
|
|
def test_gpu_output_without_chestnut_does_not_show_live_fallback(source, active):
|
|
ui = source.ui
|
|
ui.started, ui.chestnut_present, ui.chestnut_active = True, False, active
|
|
ui.sm.logMonoTime = {'modelV2': 1_000_000_000}
|
|
ui.sm.valid = ui.sm.alive = {'modelV2': True}
|
|
ui.sm.__getitem__.return_value = SimpleNamespace(chestnutPresent=False, enabled=True, big=True)
|
|
with patch('openpilot.selfdrive.ui.ui_state.time.monotonic_ns', return_value=1_100_000_000):
|
|
UIState._update_chestnut_state(ui)
|
|
assert ui.chestnut_state == ChestnutState.DISCONNECTED and ui.chestnut_output_seen
|
|
source.widget._update_state()
|
|
source.widget._render(rl.Rectangle(30, 30, 2100, 1020))
|
|
source.draw.assert_not_called()
|
|
|
|
|
|
@pytest.mark.parametrize('width', [1860, 2160])
|
|
def test_large_gpu_layer_uses_centered_right_inset(width):
|
|
from openpilot.selfdrive.ui.onroad.augmented_road_view import AugmentedRoadView as LargeRoadView
|
|
from openpilot.selfdrive.ui.onroad.hud_renderer import HudRenderer
|
|
|
|
hud = HudRenderer.__new__(HudRenderer)
|
|
hud._model_source = Mock()
|
|
view = Mock(spec=LargeRoadView, _hud_renderer=hud)
|
|
LargeRoadView.render_model_source_layer(view, rl.Rectangle(30, 30, width - 60, 1020))
|
|
rect = hud._model_source.render.call_args.args[0]
|
|
assert (rect.x + rect.width, rect.y + rect.height / 2) == (width - 60, 540)
|
|
|
|
|
|
def test_compact_gpu_layer_keeps_original_position():
|
|
from openpilot.selfdrive.ui.mici.onroad.hud_renderer import HudRenderer
|
|
|
|
hud = HudRenderer.__new__(HudRenderer)
|
|
hud._model_source = Mock()
|
|
hud._draw_model_source(rl.Rectangle(20, 8, 476, 240))
|
|
rect = hud._model_source.render.call_args.args[0]
|
|
assert (rect.x + rect.width, rect.y + rect.height / 2) == (486, 209)
|
|
|
|
|
|
def test_startup_invalid_big_frame_remains_loading_until_first_valid_output():
|
|
sm = Mock()
|
|
sm.__getitem__ = Mock(side_effect=lambda service: SimpleNamespace(chestnutPresent=True) if service == 'deviceState' else SimpleNamespace(big=True))
|
|
sm.recv_frame = {'modelV2': 12}
|
|
sm.logMonoTime = {'modelV2': 1_000_000_000}
|
|
sm.valid = {'modelV2': False}
|
|
sm.alive = {'modelV2': True}
|
|
state = Mock(spec=UIState, sm=sm, started=True, started_frame=10, chestnut_present=True, chestnut_compiled=True,
|
|
chestnut_loading=False, chestnut_active=True, chestnut_output_seen=False, chestnut_state=ChestnutState.LOADING)
|
|
with patch('openpilot.selfdrive.ui.ui_state.time.monotonic_ns', return_value=1_100_000_000):
|
|
UIState._update_chestnut_state(state)
|
|
assert state.chestnut_state == ChestnutState.LOADING
|
|
assert not state.chestnut_output_seen
|
|
sm.valid['modelV2'] = True
|
|
state.chestnut_active = False
|
|
UIState._update_chestnut_state(state)
|
|
assert state.chestnut_state == ChestnutState.ACTIVE
|
|
assert state.chestnut_output_seen
|
|
sm.valid['modelV2'] = False
|
|
UIState._update_chestnut_state(state)
|
|
assert state.chestnut_state == ChestnutState.FAILED
|
|
|
|
|
|
def test_initial_valid_small_output_is_failed_despite_loaded_big_marker():
|
|
sm = Mock()
|
|
sm.__getitem__ = Mock(side_effect=lambda service: SimpleNamespace(chestnutPresent=True) if service == 'deviceState' else SimpleNamespace(big=False))
|
|
sm.recv_frame = {'modelV2': 12}
|
|
sm.logMonoTime = {'modelV2': 1_000_000_000}
|
|
sm.valid = {'modelV2': True}
|
|
sm.alive = {'modelV2': True}
|
|
state = Mock(spec=UIState, sm=sm, started=True, started_frame=10, chestnut_present=True, chestnut_compiled=True,
|
|
chestnut_loading=True, chestnut_active=True, chestnut_output_seen=False, chestnut_state=ChestnutState.LOADING)
|
|
with patch('openpilot.selfdrive.ui.ui_state.time.monotonic_ns', return_value=1_100_000_000):
|
|
UIState._update_chestnut_state(state)
|
|
assert state.chestnut_state == ChestnutState.FAILED
|
|
|
|
|
|
@pytest.mark.parametrize('phase,started,expected', [
|
|
('connecting', False, ChestnutState.LOADING),
|
|
('connected', False, ChestnutState.LOADING),
|
|
('ready', False, ChestnutState.READY),
|
|
('active', False, ChestnutState.READY),
|
|
('unavailable', False, ChestnutState.FAILED),
|
|
('disconnected', False, ChestnutState.DISCONNECTED),
|
|
('connecting', True, ChestnutState.LOADING),
|
|
('ready', True, ChestnutState.LOADING),
|
|
('active', True, ChestnutState.ACTIVE),
|
|
('unavailable', True, ChestnutState.FAILED),
|
|
('disconnected', True, ChestnutState.DISCONNECTED),
|
|
])
|
|
def test_jetlink_uses_existing_gpu_feedback(phase, started, expected):
|
|
from openpilot.starpilot.models.jetlink_status import JetlinkStatus
|
|
sm = Mock()
|
|
sm.__getitem__ = Mock(side_effect=lambda service: SimpleNamespace(chestnutPresent=False) if service == 'deviceState' else SimpleNamespace(big=True))
|
|
sm.recv_frame = {'modelV2': 12}
|
|
sm.logMonoTime = {'modelV2': 1_000_000_000}
|
|
sm.valid = {'modelV2': True}
|
|
sm.alive = {'modelV2': True}
|
|
feed = Mock()
|
|
feed.snapshot.return_value = JetlinkStatus('Computer', phase, port='connected')
|
|
state = Mock(spec=UIState, sm=sm, started=started, started_frame=10, jetlink_status_feed=feed,
|
|
chestnut_output_seen=False, chestnut_present=False)
|
|
with patch('openpilot.selfdrive.ui.ui_state.time.monotonic_ns', return_value=1_100_000_000):
|
|
UIState._update_chestnut_state(state)
|
|
assert state.chestnut_state == expected
|
|
|
|
|
|
@pytest.mark.parametrize('big,valid,alive,age', [(False, True, True, 100_000_000),
|
|
(True, False, True, 100_000_000),
|
|
(True, True, False, 100_000_000),
|
|
(True, True, True, 300_000_000)])
|
|
def test_jetlink_cannot_keep_green_without_fresh_big_output(big, valid, alive, age):
|
|
from openpilot.starpilot.models.jetlink_status import JetlinkStatus
|
|
sm = Mock()
|
|
sm.__getitem__ = Mock(side_effect=lambda service: SimpleNamespace(chestnutPresent=False) if service == 'deviceState' else SimpleNamespace(big=big))
|
|
sm.recv_frame = {'modelV2': 12}
|
|
sm.logMonoTime = {'modelV2': 1_000_000_000}
|
|
sm.valid = {'modelV2': valid}
|
|
sm.alive = {'modelV2': alive}
|
|
feed = Mock()
|
|
feed.snapshot.return_value = JetlinkStatus('Computer', 'active', port='connected')
|
|
state = Mock(spec=UIState, sm=sm, started=True, started_frame=10, jetlink_status_feed=feed,
|
|
chestnut_output_seen=True, chestnut_present=True)
|
|
with patch('openpilot.selfdrive.ui.ui_state.time.monotonic_ns', return_value=1_000_000_000 + age):
|
|
UIState._update_chestnut_state(state)
|
|
assert state.chestnut_state == ChestnutState.FAILED
|
|
|
|
|
|
def test_physical_chestnut_keeps_its_own_status_with_jetlink_feed():
|
|
sm = Mock()
|
|
sm.__getitem__ = Mock(return_value=SimpleNamespace(chestnutPresent=True))
|
|
feed = Mock()
|
|
state = Mock(spec=UIState, sm=sm, started=False, jetlink_status_feed=feed, chestnut_alert=False,
|
|
chestnut_checking=False, chestnut_compiled=True)
|
|
UIState._update_chestnut_state(state)
|
|
feed.snapshot.assert_not_called()
|
|
assert state.chestnut_state == ChestnutState.READY
|