mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-06 20:43:46 +08:00
80fbb4f6db
Register vehicle controls, planner modes, UI actions and Galaxy services after their providers exist. Wire the shared runtime entrypoints and cover their combined behavior. 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: Kacper Rączy <gfw.kra@gmail.com> Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu> 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: haraschax <6804392+haraschax@users.noreply.github.com> Co-authored-by: prabhaavp <prabhaav@msu.edu>
143 lines
6.2 KiB
Python
143 lines
6.2 KiB
Python
import importlib.util
|
|
import json
|
|
from pathlib import Path
|
|
import tempfile
|
|
from types import SimpleNamespace
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
|
|
spec = importlib.util.spec_from_file_location('handoff_state_migration', Path(__file__).resolve().parents[1] / 'state_migration.py')
|
|
assert spec is not None
|
|
migration = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(migration)
|
|
|
|
|
|
class TestDomHandoff(unittest.TestCase):
|
|
def setUp(self):
|
|
temporary = tempfile.TemporaryDirectory()
|
|
self.addCleanup(temporary.cleanup)
|
|
self.root = Path(temporary.name)
|
|
self.namespace = self.root / 'params' / 'd'
|
|
self.namespace.mkdir(parents=True)
|
|
self.storage = self.root / 'recovery'
|
|
self.types = {'IsMetric': 'BOOL', 'CalibrationParams': 'BYTES', 'DongleId': 'STRING'}
|
|
self.params = SimpleNamespace(get_param_path=lambda: str(self.namespace), all_keys=lambda: self.types,
|
|
get_type=lambda key: SimpleNamespace(name=self.types[key]))
|
|
cache = SimpleNamespace(CACHE_KEYS={'CalibrationParams'}, inspect_cache=lambda key, raw: SimpleNamespace(status='valid'))
|
|
self.patch = patch.dict('sys.modules', {'openpilot.starpilot.schema_cache': cache})
|
|
self.patch.start()
|
|
self.addCleanup(self.patch.stop)
|
|
self.start()
|
|
self.profile = next((self.storage / 'profiles').iterdir())
|
|
self.handoff = self.namespace.parent / f'.starpilot-dom-handoff-{migration.digest(str(self.namespace).encode())}.json'
|
|
|
|
def start(self, **kwargs):
|
|
return migration.prepare_manager_start(self.params, self.storage, auto_migrate=True, **kwargs)
|
|
|
|
def write(self, **values):
|
|
for key, raw in values.items():
|
|
(self.namespace / key).write_bytes(raw)
|
|
|
|
def mark_dom(self, token='a' * 32, **changes):
|
|
record: dict = {'format': 'starpilot-dom-handoff', 'version': 1, 'namespace': str(self.namespace),
|
|
'target': str(self.namespace.resolve()), 'token': token}
|
|
record.update(changes)
|
|
migration._atomic_write(self.handoff, migration.canonical_json(record))
|
|
|
|
def test_handoff_archives_foreign_settings_and_keeps_identity(self):
|
|
self.write(IsMetric=b'1', CalibrationParams=b'Dom calibration', DomSetting=b'old', DongleId=b'identity')
|
|
original = migration._read_namespace(self.namespace)
|
|
self.mark_dom()
|
|
self.start()
|
|
self.assertEqual(migration._read_namespace(self.namespace), {'DongleId': b'identity'})
|
|
self.assertIn(original, [migration.load_snapshot(path) for path in (self.storage / 'snapshots').iterdir()])
|
|
self.assertEqual(json.loads(self.profile.read_bytes())['consumed_dom_token'], 'a' * 32)
|
|
self.assertTrue(self.handoff.exists())
|
|
|
|
def test_same_token_and_marker_removal_preserve_rebase_preferences(self):
|
|
self.mark_dom()
|
|
self.start()
|
|
self.write(IsMetric=b'1', CalibrationParams=b'rebase calibration')
|
|
self.start()
|
|
self.assertEqual((self.namespace / 'IsMetric').read_bytes(), b'1')
|
|
self.handoff.unlink()
|
|
self.start()
|
|
self.assertEqual((self.namespace / 'CalibrationParams').read_bytes(), b'rebase calibration')
|
|
|
|
def test_new_dom_token_resets_again(self):
|
|
self.mark_dom()
|
|
self.start()
|
|
self.write(IsMetric=b'1')
|
|
self.mark_dom('b' * 32)
|
|
self.start()
|
|
self.assertFalse((self.namespace / 'IsMetric').exists())
|
|
self.assertEqual(json.loads(self.profile.read_bytes())['consumed_dom_token'], 'b' * 32)
|
|
|
|
def test_malformed_wrong_target_and_bad_permissions_never_reset(self):
|
|
self.write(IsMetric=b'1')
|
|
for changes in ({'target': str(self.root)}, {'token': 'invalid'}, {'version': True}):
|
|
with self.subTest(changes=changes):
|
|
self.mark_dom(**changes)
|
|
with self.assertRaises(migration.MigrationRequired):
|
|
self.start()
|
|
self.assertEqual((self.namespace / 'IsMetric').read_bytes(), b'1')
|
|
self.mark_dom()
|
|
self.handoff.chmod(0o644)
|
|
with self.assertRaises(migration.MigrationRequired):
|
|
self.start()
|
|
self.assertEqual((self.namespace / 'IsMetric').read_bytes(), b'1')
|
|
|
|
def test_archive_failure_does_not_consume(self):
|
|
self.write(IsMetric=b'1')
|
|
before = self.profile.read_bytes()
|
|
self.mark_dom()
|
|
with patch.object(migration, 'load_snapshot', return_value={'wrong': b'value'}):
|
|
with self.assertRaises(migration.MigrationRequired):
|
|
self.start()
|
|
self.assertEqual(self.profile.read_bytes(), before)
|
|
self.assertEqual((self.namespace / 'IsMetric').read_bytes(), b'1')
|
|
self.start()
|
|
self.assertFalse((self.namespace / 'IsMetric').exists())
|
|
|
|
def test_partial_reset_failure_retries_same_token(self):
|
|
self.write(IsMetric=b'1', CalibrationParams=b'old', DomSetting=b'old')
|
|
original = migration._read_namespace(self.namespace)
|
|
before = self.profile.read_bytes()
|
|
self.mark_dom()
|
|
unlink = Path.unlink
|
|
def fail(path, *args, **kwargs):
|
|
if path == self.namespace.resolve() / 'IsMetric':
|
|
raise OSError('reset failed')
|
|
return unlink(path, *args, **kwargs)
|
|
with patch.object(Path, 'unlink', fail):
|
|
with self.assertRaises(migration.MigrationRequired):
|
|
self.start()
|
|
self.assertEqual(self.profile.read_bytes(), before)
|
|
self.assertIn(original, [migration.load_snapshot(path) for path in (self.storage / 'snapshots').iterdir()])
|
|
self.start()
|
|
self.assertEqual(migration._read_namespace(self.namespace), {})
|
|
|
|
def test_strict_and_dry_run_do_not_consume(self):
|
|
self.mark_dom()
|
|
before = self.profile.read_bytes()
|
|
with self.assertRaises(migration.MigrationRequired):
|
|
self.start(dry_run=True)
|
|
with self.assertRaises(migration.MigrationRequired):
|
|
migration.prepare_manager_start(self.params, self.storage)
|
|
self.assertEqual(self.profile.read_bytes(), before)
|
|
|
|
def test_namespace_markers_are_isolated(self):
|
|
other = self.namespace.parent / 'other'
|
|
other.mkdir()
|
|
self.mark_dom()
|
|
other_params = SimpleNamespace(get_param_path=lambda: str(other), all_keys=lambda: self.types,
|
|
get_type=self.params.get_type)
|
|
migration.prepare_manager_start(other_params, self.storage, auto_migrate=True)
|
|
self.assertNotIn('consumed_dom_token', json.loads((self.storage / 'profiles' /
|
|
f'{migration.digest(str(other).encode())}.json').read_bytes()))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|