Files
StarPilot/openpilot/starpilot/sentry_mode/tests/test_actions.py
T
firestar5683 a82935f326 User interfaces and Android Auto
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>
2026-10-14 20:02:39 -05:00

126 lines
5.8 KiB
Python

"""Exact-source saved motion edits use disposable native Params only."""
import fcntl
import os
from pathlib import Path
import tempfile
import unittest
from unittest import mock
from openpilot.common.params import Params
from openpilot.starpilot.sentry_mode.actions import commit
from openpilot.starpilot.sentry_mode.preferences import KEY, Preferences, decode, encode
from openpilot.starpilot.ui.feature_settings_state import FeatureSettingsRequest
from openpilot.starpilot.ui.sentry_owner import ENABLED, RESET, SENSITIVITY, WARNING, SentryOwner
class SentryActionsTest(unittest.TestCase):
def setUp(self):
temporary = tempfile.TemporaryDirectory()
self.addCleanup(temporary.cleanup)
self.params = Params(temporary.name)
self.path = Path(self.params.get_param_path(KEY))
def test_absent_defaults_and_confirmed_edit(self):
owner = SentryOwner(self.params, lambda: True)
Path(self.params.get_param_path("SentryModeEnabled")).write_bytes(b"1")
state = owner.snapshot()
self.assertFalse(self.path.exists())
self.assertIn("Off: Motion monitoring is disabled", state.subtitle)
self.assertEqual([row.value for row in state.rows], ["Off", "0.040", "1.0"])
self.assertFalse(owner.apply(FeatureSettingsRequest(ENABLED, None, "On")))
self.assertFalse(self.path.exists())
self.assertTrue(owner.apply(FeatureSettingsRequest(ENABLED, None, "On", confirmation=True)))
self.assertTrue(owner.last_write.committed and owner.last_write.verified)
self.assertIn(b'"enabled":true', self.path.read_bytes())
def test_live_status_does_not_change_edit_sources_and_parked_loss_hides_armed(self):
self.path.write_bytes(encode(Preferences(True)))
status = mock.Mock()
status.runtime_snapshot.return_value = ("Arming · 82s", "Fresh evidence")
owner = SentryOwner(self.params, lambda: True, status=status)
before = owner.snapshot()
saved = status.runtime_snapshot.call_args.args[0]
self.assertTrue(saved.valid and saved.preferences.enabled)
self.assertEqual(saved.raw, self.path.read_bytes())
status.runtime_snapshot.return_value = ("Arming · 81s", "Fresh evidence")
after = owner.snapshot()
self.assertEqual(before.rows, after.rows)
self.assertNotEqual(before.subtitle, after.subtitle)
status.runtime_snapshot.return_value = ("Monitoring motion", "Fresh evidence")
denied = SentryOwner(self.params, lambda: False, status=status).snapshot()
self.assertIn("Not armed", denied.subtitle)
self.assertNotIn("Monitoring motion", denied.subtitle)
self.assertFalse(any(row.available for row in denied.rows))
def test_invalid_source_requires_explicit_reset_and_exact_bytes(self):
owner = SentryOwner(self.params, lambda: True)
self.path.write_bytes(b'{"version":2}')
state = owner.snapshot()
self.assertEqual([row.key for row in state.rows], ["", RESET])
self.assertFalse(owner.apply(FeatureSettingsRequest(RESET, b'{"version":2}', "confirm")))
self.path.write_bytes(b'{"version":3}')
self.assertFalse(owner.apply(FeatureSettingsRequest(RESET, b'{"version":2}', "confirm", confirmation=True)))
self.assertEqual(self.path.read_bytes(), b'{"version":3}')
self.assertTrue(owner.apply(FeatureSettingsRequest(RESET, b'{"version":3}', "confirm", confirmation=True)))
self.assertEqual(self.path.read_bytes(), encode(Preferences()))
def test_unreadable_symlink_oversize_and_lock_contention_preserve_source(self):
owner = SentryOwner(self.params, lambda: True)
self.path.write_bytes(b'X' * 513)
self.assertFalse(any(row.available for row in owner.snapshot().rows))
self.assertFalse(owner.apply(FeatureSettingsRequest(RESET, b"", "confirm", confirmation=True)))
self.assertEqual(self.path.read_bytes(), b'X' * 513)
self.path.unlink()
target = self.path.parent / 'elsewhere'
target.write_bytes(b'{"version":2}')
self.path.symlink_to(target)
self.assertFalse(any(row.available for row in owner.snapshot().rows))
self.path.unlink()
lock = os.open(self.path.parent.parent / '.lock', os.O_CREAT | os.O_RDONLY, 0o775)
try:
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
self.assertFalse(commit(self.params, encode(Preferences(True)), None, lambda: True).committed)
finally:
os.close(lock)
self.assertFalse(self.path.exists())
def test_numeric_limits_and_final_parked_recheck(self):
owner = SentryOwner(self.params, lambda: True)
self.assertTrue(owner.apply(FeatureSettingsRequest(SENSITIVITY, None, "0.005", confirmation=True)))
first = self.path.read_bytes()
self.assertEqual(decode(first).settings.sensitivity, 0.005)
self.assertFalse(owner.apply(FeatureSettingsRequest(WARNING, first, "10.1", confirmation=True)))
self.assertEqual(self.path.read_bytes(), first)
self.assertTrue(owner.apply(FeatureSettingsRequest(WARNING, first, "10.0", confirmation=True)))
self.assertEqual(decode(self.path.read_bytes()).settings.warning_time_seconds, 10.0)
self.path.unlink()
calls = 0
def parked():
nonlocal calls
calls += 1
return calls < 3
self.assertFalse(commit(self.params, encode(Preferences(True)), None, parked).committed)
self.assertFalse(self.path.exists())
def test_bad_raw_and_post_replace_uncertainty(self):
self.assertFalse(commit(self.params, b'{"version":9}', None, lambda: True).committed)
raw = encode(Preferences(True))
real_fsync = os.fsync
calls = 0
def uncertain(fd):
nonlocal calls
calls += 1
if calls == 2:
raise OSError('directory fsync unavailable')
return real_fsync(fd)
with mock.patch('openpilot.starpilot.saved_document.os.fsync', side_effect=uncertain):
outcome = commit(self.params, raw, None, lambda: True)
self.assertTrue(outcome.committed)
self.assertFalse(outcome.verified)
self.assertEqual(self.path.read_bytes(), raw)
if __name__ == '__main__':
unittest.main()