mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 13:33:42 +08:00
109 lines
4.9 KiB
Python
109 lines
4.9 KiB
Python
"""C4 native toggle behavior, including audio saves during Params contention."""
|
|
|
|
import fcntl
|
|
from pathlib import Path
|
|
from types import SimpleNamespace as NS
|
|
import tempfile
|
|
import threading
|
|
import time
|
|
import unittest
|
|
from unittest.mock import Mock, call, patch
|
|
|
|
import pyray as rl
|
|
|
|
from openpilot.common.params import Params
|
|
from openpilot.selfdrive.ui.layouts.settings import common
|
|
from openpilot.selfdrive.ui.mici.layouts.settings import toggles
|
|
from openpilot.selfdrive.ui.mici.widgets import button
|
|
from openpilot.system.ui.lib.application import gui_app, MousePos
|
|
|
|
|
|
class TestC4SafeMode(unittest.TestCase):
|
|
def test_safe_mode_clears_experimental_choice_and_relaxes_personality(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
params = Params(directory)
|
|
params.put_bool("SafeMode", True, block=True)
|
|
params.put_bool("ExperimentalMode", True, block=True)
|
|
params.put("LongitudinalPersonality", 0, block=True)
|
|
panel = toggles.TogglesLayoutMici.__new__(toggles.TogglesLayoutMici)
|
|
panel._safe_mode_btn = Mock()
|
|
panel._experimental_btn = Mock()
|
|
panel._personality_toggle = Mock()
|
|
panel._metric_toggle = Mock()
|
|
panel._initial_cruise_speed = Mock(spec=toggles.InitialCruiseSpeedButton)
|
|
self.enterContext(patch.object(panel, "_slc_offsets", NS(_raw=lambda _key: b"0"), create=True))
|
|
toggles_to_refresh = (("SafeMode", panel._safe_mode_btn), ("ExperimentalMode", panel._experimental_btn))
|
|
self.enterContext(patch.object(panel, "_refresh_toggles", toggles_to_refresh, create=True))
|
|
fake_ui = NS(params=params, CP=None, update_params=Mock())
|
|
with patch.object(toggles, "ui_state", fake_ui):
|
|
panel._update_toggles()
|
|
panel._initial_cruise_speed.refresh.assert_called_once_with()
|
|
self.assertFalse(params.get_bool("ExperimentalMode"))
|
|
self.assertEqual(params.get("LongitudinalPersonality"), 2)
|
|
panel._experimental_btn.set_enabled.assert_called_once_with(False)
|
|
panel._personality_toggle.set_enabled.assert_called_once_with(False)
|
|
panel._personality_toggle.set_value.assert_called_once_with("relaxed")
|
|
|
|
|
|
class TestC4AudioToggle(unittest.TestCase):
|
|
def setUp(self):
|
|
directory = tempfile.TemporaryDirectory()
|
|
self.addCleanup(directory.cleanup)
|
|
self.directory = directory.name
|
|
self.params = Params(directory.name)
|
|
self.params.put_bool("RecordAudio", False, block=True)
|
|
self.ui = NS(params=self.params, CP=None, engaged=False, started=False,
|
|
add_engaged_transition_callback=Mock(), update_params=Mock())
|
|
self.enterContext(patch.object(toggles, "ui_state", self.ui))
|
|
self.enterContext(patch.object(common, "ui_state", self.ui))
|
|
self.enterContext(patch.object(button, "Params", return_value=self.params))
|
|
self.enterContext(patch.object(gui_app, "font", return_value=rl.Font()))
|
|
self.enterContext(patch.object(gui_app, "texture", return_value=NS(width=64, height=64)))
|
|
self.panel = toggles.TogglesLayoutMici()
|
|
self.audio = next(item for key, item in self.panel._refresh_toggles if key == "RecordAudio")
|
|
|
|
def test_release_queues_audio_before_restart_without_blocking_writes(self):
|
|
with patch.object(self.params, "put_bool") as save:
|
|
self.audio._handle_mouse_release(MousePos(0, 0))
|
|
self.audio._handle_mouse_release(MousePos(0, 0))
|
|
self.assertEqual(save.call_args_list, [call("RecordAudio", True), call("OnroadCycleRequested", True),
|
|
call("RecordAudio", False), call("OnroadCycleRequested", True)])
|
|
|
|
def test_release_returns_while_params_lock_is_held_then_persists_in_order(self):
|
|
finished = threading.Event()
|
|
errors = []
|
|
|
|
def release():
|
|
try:
|
|
self.audio._handle_mouse_release(MousePos(0, 0))
|
|
except Exception as error:
|
|
errors.append(error)
|
|
finally:
|
|
finished.set()
|
|
|
|
with (Path(self.directory) / ".lock").open("a") as lock:
|
|
fcntl.flock(lock, fcntl.LOCK_EX)
|
|
worker = threading.Thread(target=release, daemon=True)
|
|
worker.start()
|
|
try:
|
|
self.assertTrue(finished.wait(1), "Audio touch handler waited for the Params filesystem lock")
|
|
self.assertFalse(self.params.get_bool("RecordAudio"))
|
|
self.assertFalse(self.params.get_bool("OnroadCycleRequested"))
|
|
finally:
|
|
fcntl.flock(lock, fcntl.LOCK_UN)
|
|
worker.join(timeout=2)
|
|
self.assertEqual(errors, [])
|
|
deadline = time.monotonic() + 2
|
|
while not self.params.get_bool("OnroadCycleRequested") and time.monotonic() < deadline:
|
|
time.sleep(0.01)
|
|
self.assertTrue(self.params.get_bool("OnroadCycleRequested"))
|
|
self.assertTrue(self.params.get_bool("RecordAudio"))
|
|
|
|
def test_audio_refresh_and_engaged_guard_remain_effective(self):
|
|
self.params.put_bool("RecordAudio", True, block=True)
|
|
self.panel._update_toggles()
|
|
self.assertTrue(self.audio._checked)
|
|
self.assertTrue(self.audio.enabled)
|
|
self.ui.engaged = True
|
|
self.assertFalse(self.audio.enabled)
|