Files
StarPilot/openpilot/starpilot/ui/tests/test_developer_preview_render.py
T

159 lines
7.8 KiB
Python

"""Synthetic onroad visuals remain presentation-only across both device profiles."""
from dataclasses import replace
from pathlib import Path
from types import SimpleNamespace as NS
import unittest
from unittest.mock import Mock, patch
import pyray as rl
from openpilot.starpilot.ui.developer_preview import OnroadVisualPreview
from openpilot.starpilot.ui.onroad import CornerHintCache, OnroadView
from openpilot.starpilot.ui.onroad_cem_gauge import AetherGauge, IndicatorType
from openpilot.starpilot.ui.onroad_navigation_favorites import NavigationFavorites
from openpilot.starpilot.ui.onroad_compact_widgets import MiciSidebarWidgets
from openpilot.starpilot.ui.onroad_conditional import status as conditional_status
from openpilot.starpilot.ui.onroad_curve import controlling, glowing, render_glow, status_label
from openpilot.starpilot.ui.onroad_state import AlertSize, OnroadAlert, OnroadState, SpeedLimitObservation
from openpilot.starpilot.ui.presentation import Profile
def state(*, full_alert=False, cem=True, csc=True) -> OnroadState:
return OnroadState(engaged=False, camera_available=False, speed_mps=0.0, cruise_kph=None,
speed_limit=SpeedLimitObservation(),
alert=OnroadAlert(size=AlertSize.FULL if full_alert else AlertSize.NONE),
visual_preview=OnroadVisualPreview('CURVE' if cem else None, .0115 if csc else None))
class DeveloperPreviewRenderTests(unittest.TestCase):
def test_synthetic_labels_do_not_change_real_authority(self):
both = state()
self.assertFalse(controlling(both))
self.assertFalse(glowing(both))
self.assertEqual(status_label(both), 'CURVE PREVIEW')
label = conditional_status(both)
self.assertIsNotNone(label)
if label is None:
self.fail("the synthetic CEM state has no status")
self.assertEqual(label[:2], ('CEM', 'PREVIEW CURVE'))
self.assertIsNone(status_label(replace(both, visual_preview=None)))
self.assertIsNone(conditional_status(replace(both, visual_preview=None)))
self.assertIsNone(status_label(state(full_alert=True)))
self.assertIsNone(conditional_status(state(full_alert=True)))
def test_synthetic_curve_glow_obeys_full_alert_without_control_claim(self):
rect = rl.Rectangle(30, 30, 1800, 1020)
with patch.object(rl, 'draw_rectangle_gradient_v') as vertical, patch.object(rl, 'draw_rectangle_gradient_h') as horizontal:
render_glow(rect, state(), border_width=30, time_s=1.0)
self.assertEqual((vertical.call_count, horizontal.call_count), (2, 2))
vertical.reset_mock()
horizontal.reset_mock()
render_glow(rect, state(full_alert=True), border_width=30, time_s=1.0)
self.assertEqual((vertical.call_count, horizontal.call_count), (0, 0))
def test_compact_center_prefers_curve_then_cem_and_never_overlays_full_alert(self):
fonts = Mock()
fonts.measure.return_value = NS(width=30, height=20)
sidebar = MiciSidebarWidgets(fonts)
with patch.object(rl, 'draw_rectangle'), patch.object(sidebar, '_confidence_ball'), \
patch.object(sidebar, '_personality'), patch.object(sidebar, '_curve_icon') as icon, \
patch.object(sidebar, '_chill_icon'):
sidebar.render(rl.Rectangle(0, 0, 536, 240), state())
icon.assert_called_once()
icon.reset_mock()
sidebar.render(rl.Rectangle(0, 0, 536, 240), state(csc=False))
icon.assert_called_once()
icon.reset_mock()
sidebar.render(rl.Rectangle(0, 0, 536, 240), state(full_alert=True))
icon.assert_not_called()
def test_large_and_compact_keep_badge_even_for_full_alert(self):
for profile in (Profile.LARGE, Profile.COMPACT):
with self.subTest(profile=profile):
view = OnroadView.__new__(OnroadView)
fonts = Mock()
fonts.profile = profile
fonts.measure.return_value = NS(width=100, height=20)
view.fonts = fonts
view.navigation_favorites = NavigationFavorites(fonts)
view.camera_layer = None
view.background_layer = None
view.pip_layer = None
view.extra_overlays = None
view.alert = Mock()
view.navigation = Mock()
view.projection_viewport = None
view._corner_cache = CornerHintCache()
view.unified_speed = Mock()
view.current_speed = Mock()
view.steering_wheel = Mock()
view.compact_hud = Mock()
view.compact_sidebar = Mock()
view.torque_bar = Mock()
view._fade = Mock()
view._fade_path = Path('/unused')
with patch('openpilot.starpilot.ui.onroad.clip.begin_scissor_mode'), \
patch('openpilot.starpilot.ui.onroad.clip.end_scissor_mode'), \
patch('openpilot.starpilot.ui.onroad.rl.draw_rectangle_rec'), \
patch('openpilot.starpilot.ui.onroad.rl.draw_rectangle_gradient_v'), \
patch('openpilot.starpilot.ui.onroad.rl.draw_rectangle_lines_ex'), \
patch('openpilot.starpilot.ui.onroad.rl.draw_rectangle'), \
patch('openpilot.starpilot.ui.onroad.rl.draw_texture_ex'), \
patch('openpilot.starpilot.ui.onroad.rl.draw_rectangle_rounded'), \
patch('openpilot.starpilot.ui.onroad.rl.draw_rectangle_rounded_lines_ex'), \
patch('openpilot.starpilot.ui.onroad.render_corner_hint'), \
patch.object(view, '_prepare_compact_fade'), patch.object(view, '_slc_actions'):
view.render(state(full_alert=True))
drawn = [call.args[0] for call in fonts.draw.call_args_list]
self.assertTrue(any('SYNTHETIC REPLAY PREVIEW' in text for text in drawn))
self.assertFalse(any('CEM PREVIEW' in text or 'CURVE PREVIEW' in text for text in drawn))
def test_large_nonalert_draws_canonical_gauge_and_preview_labels_without_real_curve(self):
view = OnroadView.__new__(OnroadView)
fonts = Mock()
fonts.profile = Profile.LARGE
view.fonts = fonts
view.cem_gauge = AetherGauge(fonts)
view.navigation_favorites = NavigationFavorites(fonts)
view.camera_layer = None
view.background_layer = None
view.pip_layer = None
view.extra_overlays = None
view.alert = Mock()
view.navigation = Mock()
view.projection_viewport = None
view._corner_cache = CornerHintCache()
view.unified_speed = Mock()
view.current_speed = Mock()
view.steering_wheel = Mock()
view.torque_bar = Mock()
with patch('openpilot.starpilot.ui.onroad.clip.begin_scissor_mode'), \
patch('openpilot.starpilot.ui.onroad.clip.end_scissor_mode'), \
patch('openpilot.starpilot.ui.onroad.rl.draw_rectangle_rec'), \
patch('openpilot.starpilot.ui.onroad.rl.draw_rectangle_gradient_v'), \
patch('openpilot.starpilot.ui.onroad.rl.draw_rectangle_gradient_h'), \
patch('openpilot.starpilot.ui.onroad.rl.draw_rectangle_lines_ex'), \
patch('openpilot.starpilot.ui.onroad.rl.draw_rectangle_rounded'), \
patch('openpilot.starpilot.ui.onroad.rl.draw_rectangle_rounded_lines_ex'), \
patch('openpilot.starpilot.ui.onroad.render_corner_hint'), \
patch.object(view.cem_gauge, '_render_unified_road') as road, \
patch.object(view, '_slc_actions'):
view.render(state())
road.assert_called_once()
self.assertEqual(road.call_args.args[3].indicator_type, IndicatorType.ROAD_CURVE)
self.assertEqual(road.call_args.args[3].indicator_value, .0115)
road.reset_mock()
view.render(replace(state(), visual_preview=OnroadVisualPreview('STOP LIGHT', None)))
road.assert_called_once()
self.assertEqual(road.call_args.args[3].indicator_type, IndicatorType.STOP_LIGHT)
drawn = [call.args[0] for call in fonts.draw.call_args_list]
self.assertIn('CURVE PREVIEW', drawn)
self.assertFalse(any(text.startswith(('CEM PREVIEW', 'CCM PREVIEW')) for text in drawn))
self.assertIn('SYNTHETIC REPLAY PREVIEW', drawn)
self.assertIn('CEM + CSC', drawn)
if __name__ == '__main__':
unittest.main()