mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-07 04:53:47 +08:00
159 lines
7.8 KiB
Python
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()
|