mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 05:23:41 +08:00
a82935f326
Integrate onroad views, configurable widgets, local settings, steering-profile contracts, Bluetooth and Android Auto with their device and runtime services. Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> Co-authored-by: Daniel Koepping <elkoled@gmail.com> Co-authored-by: Danny <dannyirwin31@yahoo.com> Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com> Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com> Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com> Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu> Co-authored-by: Shane Smiskol <shane@smiskol.com> Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com> Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Co-authored-by: stef <19478336+stefpi@users.noreply.github.com> Co-authored-by: whoisdomi <dcantu83@gmail.com> Co-authored-by: Intelli <contact@intelli.software> Co-authored-by: pharmacomaniac <blittle65@gmail.com> Co-authored-by: prabhaavp <prabhaav@msu.edu>
268 lines
13 KiB
Python
268 lines
13 KiB
Python
"""Native Device policy with saved display choices and no hardware calls."""
|
|
|
|
from types import SimpleNamespace as NS
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import Mock, patch
|
|
|
|
from openpilot.common.params import Params
|
|
from openpilot.selfdrive.ui import ui_state as module
|
|
from openpilot.system.ui.lib.application import MouseEvent, MousePos
|
|
from openpilot.starpilot.ui.display_preferences import SCREEN_OFF, read_preferences
|
|
|
|
|
|
class DisplayDeviceTests(unittest.TestCase):
|
|
def setUp(self):
|
|
temporary = tempfile.TemporaryDirectory()
|
|
self.addCleanup(temporary.cleanup)
|
|
self.params = Params(temporary.name)
|
|
self.state = NS(params=self.params, started=False, ignition=False, light_sensor=-1,
|
|
projection_read_only=False, sm=NS(valid={}, alive={}))
|
|
self.patches = [patch.object(module, "ui_state", self.state),
|
|
patch.object(module.gui_app, "big_ui", return_value=True),
|
|
patch.object(module.HARDWARE, "set_display_power"),
|
|
patch.object(module.HARDWARE, "set_screen_brightness")]
|
|
for guard in self.patches:
|
|
guard.start()
|
|
self.addCleanup(guard.stop)
|
|
filter_guard = patch.object(module.FirstOrderFilter, "update", autospec=True,
|
|
side_effect=lambda _filter, value: value)
|
|
self.filter_update = filter_guard.start()
|
|
self.addCleanup(filter_guard.stop)
|
|
self.device = module.Device()
|
|
|
|
def test_absent_master_matches_existing_auto_and_timeout_policy(self):
|
|
self.device._refresh_display_preferences()
|
|
self.assertFalse(self.device._display_preferences.enabled)
|
|
self.assertEqual(self.device.interactive_timeout, 30)
|
|
self.device._update_brightness()
|
|
self.assertEqual(self.device._brightness_target, module.BACKLIGHT_OFFROAD)
|
|
self.state.started = self.state.ignition = True
|
|
self.state.light_sensor = 100
|
|
self.assertEqual(self.device.interactive_timeout, 10)
|
|
self.device._update_brightness()
|
|
self.assertEqual(self.device._brightness_target, 100)
|
|
self.device._awake = False
|
|
self.device._update_brightness()
|
|
self.assertEqual(self.device._brightness_target, 0)
|
|
|
|
def test_manual_target_uses_existing_filter_and_wake_zero(self):
|
|
self.params.put_bool("StarPilotDisplayPreferencesEnabled", True, block=True)
|
|
self.params.put("ScreenBrightness", 40, block=True)
|
|
self.params.put("ScreenBrightnessOnroad", 75, block=True)
|
|
self.params.put("ScreenTimeout", 45, block=True)
|
|
self.params.put("ScreenTimeoutOnroad", 20, block=True)
|
|
self.device._refresh_display_preferences()
|
|
self.assertEqual(self.device.interactive_timeout, 45)
|
|
self.device._update_brightness()
|
|
self.filter_update.assert_called_with(self.device._brightness_filter, 40)
|
|
self.state.started = self.state.ignition = True
|
|
self.assertEqual(self.device.interactive_timeout, 20)
|
|
self.device._update_brightness()
|
|
self.filter_update.assert_called_with(self.device._brightness_filter, 75)
|
|
self.device._awake = False
|
|
self.device._update_brightness()
|
|
self.assertEqual(self.device._brightness_target, 0)
|
|
self.device.set_override_interactive_timeout(7)
|
|
self.assertEqual(self.device.interactive_timeout, 7)
|
|
|
|
def test_bounded_reads_invalidation_and_ignition_transition(self):
|
|
with patch.object(module, "read_preferences", wraps=read_preferences) as reads, \
|
|
patch.object(module.time, "monotonic", side_effect=[10.0, 10.2, 11.1, 11.2]):
|
|
self.device._refresh_display_preferences()
|
|
self.device._refresh_display_preferences()
|
|
self.assertEqual(reads.call_count, 1)
|
|
self.device._refresh_display_preferences()
|
|
self.assertEqual(reads.call_count, 2)
|
|
self.device.invalidate_display_preferences()
|
|
self.device._refresh_display_preferences()
|
|
self.assertEqual(reads.call_count, 3)
|
|
self.params.put_bool("StarPilotDisplayPreferencesEnabled", True, block=True)
|
|
self.device.invalidate_display_preferences()
|
|
self.device._refresh_display_preferences()
|
|
self.device._interaction_time = 1
|
|
with patch.object(self.device, "_reset_interactive_timeout", wraps=self.device._reset_interactive_timeout) as reset, \
|
|
patch.object(module.time, "monotonic", return_value=100.):
|
|
self.state.ignition = True
|
|
self.device._update_wakefulness()
|
|
reset.assert_called_once()
|
|
self.assertEqual(self.device._interaction_time, 130.)
|
|
self.assertTrue(self.device.awake)
|
|
|
|
def test_forced_offroad_uses_parked_timeout_with_live_ignition(self):
|
|
for saved in (False, True):
|
|
with self.subTest(saved=saved):
|
|
self.params.put_bool("StarPilotDisplayPreferencesEnabled", saved, block=True)
|
|
self.params.put("ScreenTimeout", 5, block=True)
|
|
self.params.put("ScreenTimeoutOnroad", 20, block=True)
|
|
self.state.started = self.state.ignition = True
|
|
device = module.Device()
|
|
callback = Mock()
|
|
device.add_interactive_timeout_callback(callback)
|
|
with patch.object(device, "_start_brightness_thread"), \
|
|
patch.object(module, "PC", False), \
|
|
patch.object(module.gui_app, "_mouse_events", []), \
|
|
patch.object(module.gui_app, "set_should_render"), \
|
|
patch.object(module.time, "monotonic", return_value=100.) as clock:
|
|
device.update()
|
|
self.assertTrue(device.awake)
|
|
self.state.started = False # hardwared force-offroad, physical ignition remains ON.
|
|
clock.return_value = 101.
|
|
device.update()
|
|
self.assertTrue(self.state.ignition)
|
|
self.assertEqual(device.interactive_timeout, 5 if saved else 30)
|
|
self.assertEqual(device._interaction_time, 106. if saved else 131.)
|
|
self.assertTrue(device.awake)
|
|
clock.return_value = 140.
|
|
device.update()
|
|
self.assertFalse(device.awake)
|
|
module.HARDWARE.set_display_power.assert_called_with(False)
|
|
callback.assert_called_once()
|
|
clock.return_value = 140.1
|
|
device.update()
|
|
self.assertFalse(device.awake)
|
|
self.assertEqual(device._brightness_target, 0)
|
|
callback.assert_called_once()
|
|
module.gui_app.mouse_events.append(MouseEvent(MousePos(0., 0.), 0, True, False, True, 141.))
|
|
clock.return_value = 141.
|
|
device.update()
|
|
self.assertTrue(device.awake)
|
|
module.HARDWARE.set_display_power.assert_called_with(True)
|
|
module.gui_app.mouse_events.clear()
|
|
self.state.started = True
|
|
clock.return_value = 142.
|
|
device.update()
|
|
self.assertTrue(device.awake)
|
|
self.assertEqual(device.interactive_timeout, 20 if saved else 10)
|
|
clock.return_value = 200.
|
|
device.update()
|
|
self.assertTrue(device.awake) # Effective onroad remains awake after its UI timeout.
|
|
|
|
def test_invalid_saved_values_fail_to_existing_auto_policy(self):
|
|
self.params.put_bool("StarPilotDisplayPreferencesEnabled", True, block=True)
|
|
self.params.put("ScreenBrightnessOnroad", 0, block=True)
|
|
saved = read_preferences(self.params, large=True)
|
|
self.assertTrue(saved.enabled)
|
|
self.assertEqual(saved.driving_brightness, 101)
|
|
self.params.put("ScreenTimeoutOnroad", 999, block=True)
|
|
self.assertEqual(read_preferences(self.params, large=True).driving_timeout, 10)
|
|
|
|
def test_screen_off_timeout_single_tap_wake_and_critical_alerts(self):
|
|
for large in (True, False):
|
|
with self.subTest(large=large), patch.object(module.gui_app, "big_ui", return_value=large):
|
|
self.state.sm = NS(valid={}, alive={})
|
|
self.device = module.Device()
|
|
self._check_screen_off_timeout_single_tap_wake_and_critical_alerts(large)
|
|
|
|
def _check_screen_off_timeout_single_tap_wake_and_critical_alerts(self, large):
|
|
self.params.put_bool(SCREEN_OFF, True, block=True)
|
|
self.state.started = self.state.ignition = True
|
|
with patch.object(self.device, "_start_brightness_thread"), \
|
|
patch.object(module.gui_app, "_mouse_events", []), \
|
|
patch.object(module.gui_app, "set_should_render") as render, \
|
|
patch.object(module.time, "monotonic", return_value=100.) as clock:
|
|
self.device.update()
|
|
self.assertTrue(self.device.awake)
|
|
self.assertEqual(self.device.interactive_timeout, 10 if large else 5)
|
|
clock.return_value = 106.
|
|
self.device.update()
|
|
self.assertEqual(self.device.awake, large)
|
|
clock.return_value = 111.
|
|
self.device.update()
|
|
self.assertFalse(self.device.awake)
|
|
self.assertEqual(self.device._brightness_target, 0)
|
|
render.assert_called_with(False)
|
|
module.HARDWARE.set_display_power.assert_called_with(False)
|
|
gui_events = module.gui_app.mouse_events
|
|
gui_events.append(MouseEvent(MousePos(0., 0.), 0, True, False, True, 112.))
|
|
clock.return_value = 112.
|
|
self.device.update()
|
|
self.assertTrue(self.device.awake)
|
|
self.assertGreater(self.device._brightness_target, 0)
|
|
self.assertEqual(gui_events, []) # Wake press cannot activate an underlying button.
|
|
render.assert_called_with(True)
|
|
gui_events.append(MouseEvent(MousePos(0., 0.), 0, False, True, False, 112.1))
|
|
clock.return_value = 112.1
|
|
self.device.update()
|
|
self.assertEqual(gui_events, []) # Wake release is consumed too.
|
|
clock.return_value = 123.
|
|
self.device.update()
|
|
self.assertFalse(self.device.awake)
|
|
alert = NS(alertStatus=module.log.SelfdriveState.AlertStatus.critical)
|
|
class Messages(dict):
|
|
valid = {"selfdriveState": True}
|
|
alive = {"selfdriveState": True}
|
|
self.state.sm = Messages(selfdriveState=alert)
|
|
self.device.update()
|
|
self.assertTrue(self.device.awake)
|
|
alert.alertStatus = module.log.SelfdriveState.AlertStatus.normal
|
|
clock.return_value = 124.
|
|
self.device.update()
|
|
self.assertTrue(self.device.awake)
|
|
clock.return_value = 134.
|
|
self.device.update()
|
|
self.assertFalse(self.device.awake)
|
|
self.assertTrue(self.device.toggle_screen_off()) # Existing controller shortcut also wakes automatic sleep.
|
|
self.assertTrue(self.device.awake)
|
|
clock.return_value = 155.
|
|
self.device.update()
|
|
self.assertFalse(self.device.awake)
|
|
self.state.started = False
|
|
self.device.update()
|
|
self.assertTrue(self.device.awake)
|
|
|
|
def test_screen_off_setting_changes_wake_and_use_saved_driving_timeout(self):
|
|
self.params.put_bool("StarPilotDisplayPreferencesEnabled", True, block=True)
|
|
self.params.put("ScreenTimeoutOnroad", 20, block=True)
|
|
self.params.put_bool(SCREEN_OFF, True, block=True)
|
|
self.state.started = self.state.ignition = True
|
|
with patch.object(self.device, "_start_brightness_thread"), \
|
|
patch.object(module.gui_app, "_mouse_events", []), \
|
|
patch.object(module.gui_app, "set_should_render"), \
|
|
patch.object(module.time, "monotonic", return_value=100.) as clock:
|
|
self.device.update()
|
|
clock.return_value = 111.
|
|
self.device.update()
|
|
self.assertTrue(self.device.awake)
|
|
clock.return_value = 121.
|
|
self.device.update()
|
|
self.assertFalse(self.device.awake)
|
|
self.params.put_bool(SCREEN_OFF, False, block=True)
|
|
self.device.invalidate_display_preferences()
|
|
self.device.update()
|
|
self.assertTrue(self.device.awake)
|
|
clock.return_value = 200.
|
|
self.device.update()
|
|
self.assertTrue(self.device.awake)
|
|
|
|
def test_started_transition_refreshes_saved_profile_without_worker_restart(self):
|
|
self.params.put_bool("StarPilotDisplayPreferencesEnabled", True, block=True)
|
|
self.params.put("ScreenBrightnessOnroad", 80, block=True)
|
|
with patch.object(self.device, "_start_brightness_thread") as start, \
|
|
patch.object(module, "read_preferences", wraps=read_preferences) as reads:
|
|
self.device.update()
|
|
self.device.update()
|
|
self.assertEqual(reads.call_count, 1)
|
|
self.state.started = self.state.ignition = True
|
|
self.device.update()
|
|
self.assertEqual(reads.call_count, 2)
|
|
self.assertEqual(self.filter_update.call_args.args[-1], 80)
|
|
self.assertEqual(start.call_count, 3)
|
|
|
|
def test_parked_timeout_callback_and_ignition_wake(self):
|
|
self.params.put_bool("StarPilotDisplayPreferencesEnabled", True, block=True)
|
|
self.params.put("ScreenTimeout", 5, block=True)
|
|
self.device._refresh_display_preferences()
|
|
callback = Mock()
|
|
self.device.add_interactive_timeout_callback(callback)
|
|
self.device._interaction_time = 1
|
|
with patch.object(module, "PC", False), patch.object(module.gui_app, "set_should_render") as render:
|
|
self.device._update_wakefulness()
|
|
callback.assert_called_once()
|
|
self.assertFalse(self.device.awake)
|
|
render.assert_called_with(False)
|
|
self.state.ignition = True
|
|
self.device._update_wakefulness()
|
|
self.assertTrue(self.device.awake)
|
|
render.assert_called_with(True)
|