mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 13:33:42 +08:00
148 lines
7.6 KiB
Python
148 lines
7.6 KiB
Python
"""Actual shared Big/AA composition and camera dispatch; no native resources."""
|
|
from contextlib import ExitStack
|
|
from types import SimpleNamespace as NS
|
|
from unittest.mock import Mock, patch
|
|
|
|
import pyray as rl
|
|
import pytest
|
|
|
|
from openpilot.selfdrive.ui.onroad import augmented_road_view as large
|
|
from openpilot.selfdrive.ui.mici.onroad import augmented_road_view as compact
|
|
from openpilot.starpilot.system.starport.projection_layout import projection_appearance
|
|
from openpilot.starpilot.system.starport.projection_onroad import ProjectionOnroad
|
|
from openpilot.starpilot.ui import onroad
|
|
from openpilot.starpilot.ui.appearance_preferences import CameraViewChoice, OnroadAppearance
|
|
from openpilot.starpilot.ui.developer_preview import OnroadVisualPreview
|
|
from openpilot.starpilot.ui.onroad_customization import DEFAULT_WIDGET_ORDER, default_document
|
|
from openpilot.starpilot.ui.onroad_state import OnroadState, SpeedLimitObservation
|
|
from openpilot.starpilot.ui.presentation import Profile
|
|
|
|
|
|
@pytest.mark.parametrize('viewport', [None, (2880, 1080)])
|
|
@pytest.mark.parametrize('ordered', [False, True])
|
|
@pytest.mark.parametrize('editor', [False, True])
|
|
def test_big_and_projected_editor_share_one_graphic_cem_without_legacy_caption(viewport, ordered, editor):
|
|
document=default_document()
|
|
if ordered:
|
|
document['widgetOrder']={'large':list(DEFAULT_WIDGET_ORDER['large'])}
|
|
else:
|
|
document.pop('widgetOrder',None)
|
|
state=OnroadState(True, True, 20., 80., SpeedLimitObservation(), customization=document,
|
|
longitudinal_active=True, visual_preview=OnroadVisualPreview(cem_reason='STOP LIGHT',layout_sample=editor))
|
|
view=onroad.OnroadView.__new__(onroad.OnroadView)
|
|
view.fonts=Mock(profile=Profile.LARGE)
|
|
view.projection_viewport=viewport
|
|
view.camera_layer=view.background_layer=view.extra_overlays=None
|
|
view.pip_layer=view.map_layer=view.driver_monitor_layer=None
|
|
view.unified_speed=view.current_speed=view.steering_wheel=view.torque_bar=Mock()
|
|
view.cem_gauge=Mock()
|
|
view.alert=Mock()
|
|
view.navigation=view.navigation_favorites=Mock()
|
|
for name in ('_slc_actions','_clock','_driving_mode','_stopped_timer','_traffic_badge','_preview_badge'):
|
|
setattr(view,name,Mock())
|
|
view._corner_cache=Mock()
|
|
with ExitStack() as stack:
|
|
ordered_draw = stack.enter_context(patch.object(view,'_ordered_widgets',wraps=view._ordered_widgets))
|
|
for name in ('draw_rectangle_rec','draw_rectangle_gradient_v','draw_rectangle_lines_ex','draw_rectangle_rounded_lines_ex',
|
|
'rl_push_matrix','rl_translatef','rl_pop_matrix'):
|
|
stack.enter_context(patch.object(rl,name))
|
|
stack.enter_context(patch.object(onroad.clip,'begin_scissor_mode'))
|
|
stack.enter_context(patch.object(onroad.clip,'end_scissor_mode'))
|
|
for name in ('render_glow','render_corner_hint'):
|
|
stack.enter_context(patch.object(onroad,name))
|
|
border = stack.enter_context(patch.object(onroad,'render_large_half_borders'))
|
|
view._large(state)
|
|
assert ordered_draw.call_count == int(ordered)
|
|
border.assert_called_once()
|
|
view.cem_gauge.render.assert_called_once_with(state)
|
|
assert not any(str(call.args[0]).startswith(('CEM ','CCM ')) for call in view.fonts.draw.call_args_list)
|
|
|
|
|
|
def camera(module, current=None):
|
|
view=module.AugmentedRoadView.__new__(module.AugmentedRoadView)
|
|
view.close=Mock()
|
|
view._stream_type=module.NARROW_ROAD_CAM if current is None else current
|
|
view.available_streams=[module.NARROW_ROAD_CAM,module.WIDE_CAM,module.DRIVER_CAM]
|
|
view._switching=False
|
|
view._target_client=view._target_stream_type=None
|
|
view.switch_stream=Mock(side_effect=lambda target:setattr(view,'_stream_type',target))
|
|
return view
|
|
|
|
|
|
@pytest.mark.parametrize('module',[large,compact])
|
|
@pytest.mark.parametrize('speed',[0.,5.,7.,10.,30.])
|
|
def test_saved_standard_beats_auto_and_revokes_pending_wide(module,speed):
|
|
view=camera(module)
|
|
pending=view._target_client=Mock()
|
|
view._target_stream_type=module.WIDE_CAM
|
|
view._switching=True
|
|
sm={'selfdriveState':NS(experimentalMode=True),'carState':NS(vEgo=speed)}
|
|
assert view._switch_stream_if_needed(sm,CameraViewChoice.STANDARD)==module.NARROW_ROAD_CAM
|
|
assert not view._switching and view._target_client is None and view._target_stream_type is None
|
|
pending.recv.assert_not_called()
|
|
view.switch_stream.assert_not_called()
|
|
|
|
|
|
@pytest.mark.parametrize('module',[large,compact])
|
|
def test_reverse_is_temporary_and_none_cancels_pending(module):
|
|
view=camera(module)
|
|
sm={'selfdriveState':NS(experimentalMode=True),'carState':NS(vEgo=1.)}
|
|
choice=CameraViewChoice.STANDARD
|
|
assert view._switch_stream_if_needed(sm,choice,True)==module.DRIVER_CAM
|
|
assert view._switch_stream_if_needed(sm,choice,False)==module.NARROW_ROAD_CAM
|
|
assert choice is CameraViewChoice.STANDARD
|
|
view._switching=True
|
|
view._target_client=Mock()
|
|
view._target_stream_type=module.WIDE_CAM
|
|
assert view._switch_stream_if_needed(sm,CameraViewChoice.NONE,True) is None
|
|
assert not view._switching and view._target_client is None
|
|
|
|
|
|
@pytest.mark.parametrize('choice',list(CameraViewChoice))
|
|
def test_projection_keeps_shared_camera_choice_and_passes_it_to_actual_camera(choice):
|
|
appearance=OnroadAppearance(camera_view=choice,driver_camera_on_reverse=True)
|
|
projected=projection_appearance(appearance,{})
|
|
assert projected.camera_view is choice and projected.driver_camera_on_reverse
|
|
state=OnroadState(True,True,20.,80.,SpeedLimitObservation(),appearance=projected,reverse_driver_camera=True)
|
|
view=ProjectionOnroad.__new__(ProjectionOnroad)
|
|
view.camera=Mock(stream_type=large.NARROW_ROAD_CAM)
|
|
view._camera_layer(rl.Rectangle(0,0,2880,1080),state)
|
|
kwargs=view.camera.render_camera_model_layer.call_args.kwargs
|
|
assert kwargs['camera_view'] is choice and kwargs['reverse_driver'] is True
|
|
assert appearance.camera_view is choice
|
|
|
|
|
|
def test_large_driver_presentation_uses_plain_camera_transform_without_road_geometry():
|
|
view=camera(large,large.DRIVER_CAM)
|
|
with patch.object(large.CameraView,'_calc_frame_matrix',return_value='driver-matrix') as plain:
|
|
assert view._calc_frame_matrix(rl.Rectangle(0,0,100,80))=='driver-matrix'
|
|
plain.assert_called_once()
|
|
|
|
|
|
@pytest.mark.parametrize('choice,available', [(CameraViewChoice.DRIVER, True), (CameraViewChoice.DRIVER, False),
|
|
(CameraViewChoice.NONE, True)])
|
|
def test_large_camera_never_draws_road_geometry_over_driver_or_hidden_view(choice, available):
|
|
view=camera(large,large.DRIVER_CAM if available else large.NARROW_ROAD_CAM)
|
|
if not available:
|
|
view.available_streams.remove(large.DRIVER_CAM)
|
|
view._ensure_connection=Mock(return_value=True)
|
|
view._update_calibration=Mock()
|
|
view.model_renderer=Mock(road_style={})
|
|
rect=rl.Rectangle(0,0,100,80)
|
|
sm={'selfdriveState':NS(experimentalMode=True),'carState':NS(vEgo=1.)}
|
|
with patch.object(large,'ui_state',NS(started=True,sm=sm)), \
|
|
patch.object(large.gui_app,'measure_frame_phase',side_effect=lambda name,func,*args,**kwargs:func(*args,**kwargs)), \
|
|
patch.object(large.CameraView,'_render') as render_camera, patch.object(large.rl,'draw_rectangle_rec') as neutral:
|
|
view.render_camera_model_layer(rect,camera_view=choice)
|
|
view.model_renderer.render.assert_not_called()
|
|
if choice is CameraViewChoice.NONE or not available:
|
|
render_camera.assert_not_called()
|
|
neutral.assert_called_once_with(rect,large.rl.BLACK)
|
|
else:
|
|
render_camera.assert_called_once_with(view,rect,poll_camera=True)
|
|
# Ending the temporary driver presentation returns to the saved Standard
|
|
# and resumes calibrated road geometry on the actual native method.
|
|
view.render_camera_model_layer(rect,camera_view=CameraViewChoice.STANDARD)
|
|
assert view.stream_type==large.NARROW_ROAD_CAM
|
|
view.model_renderer.render.assert_called_once_with(rect)
|