diff --git a/openpilot/starpilot/tests/test_state_migration.py b/openpilot/starpilot/tests/test_state_migration.py index ac3e618123..63af415531 100644 --- a/openpilot/starpilot/tests/test_state_migration.py +++ b/openpilot/starpilot/tests/test_state_migration.py @@ -98,17 +98,21 @@ class TestStateMigration(unittest.TestCase): marker_bytes = marker.read_bytes() before = dict(known, **retired) prepare_manager_start(self.params, self.storage, auto_migrate=True) - snapshot, = (self.storage / 'snapshots').iterdir() - self.assertEqual(load_snapshot(snapshot), before) + snapshots = tuple((self.storage / 'snapshots').iterdir()) + self.assertEqual(len(snapshots), 2) + after_retired = {key: raw for key, raw in before.items() if key != 'ShareUsageStats'} + self.assertCountEqual([load_snapshot(snapshot) for snapshot in snapshots], [before, after_retired]) self.assertEqual({entry.name: entry.read_bytes() for entry in self.namespace.iterdir()}, known) self.assertEqual({key: ((self.namespace / key).stat().st_ino, (self.namespace / key).stat().st_mtime_ns) for key in known}, identities) self.assertEqual(marker.read_bytes(), marker_bytes) - receipt = json.loads((snapshot / 'migration.json').read_bytes()) - self.assertEqual(set(receipt['actions']), set(retired)) - self.assertEqual(receipt['status'], 'migrated') + for snapshot in snapshots: + receipt = json.loads((snapshot / 'migration.json').read_bytes()) + expected_actions = {'ShareUsageStats'} if 'ShareUsageStats' in load_snapshot(snapshot) else {'RetiredUnknownSetting'} + self.assertEqual(set(receipt['actions']), expected_actions) + self.assertEqual(receipt['status'], 'migrated') prepare_manager_start(self.params, self.storage, dry_run=True) prepare_manager_start(self.params, self.storage, auto_migrate=True) - self.assertEqual(list((self.storage / 'snapshots').iterdir()), [snapshot]) + self.assertEqual(set((self.storage / 'snapshots').iterdir()), set(snapshots)) def test_initialized_unknown_retirement_fails_before_deletion(self): prepare_manager_start(self.params, self.storage) @@ -126,7 +130,7 @@ class TestStateMigration(unittest.TestCase): def test_initialized_unknown_keys_do_not_admit_invalid_known_state(self): prepare_manager_start(self.params, self.storage) - self.raw('ShareUsageStats', b'1') + self.raw('UnportedFeature', b'1') for key, raw in (('AlwaysOnLateral', b'true'), ('CalibrationParams', b'invalid retained calibration')): with self.subTest(key=key): self.raw(key, raw) @@ -136,6 +140,33 @@ class TestStateMigration(unittest.TestCase): self.assertEqual({entry.name: entry.read_bytes() for entry in self.namespace.iterdir()}, before) (self.namespace / key).unlink() + def test_retired_key_archival_does_not_admit_invalid_known_state(self): + prepare_manager_start(self.params, self.storage) + for key, raw in (('AlwaysOnLateral', b'true'), ('CalibrationParams', b'invalid retained calibration')): + with self.subTest(key=key): + self.raw('ShareUsageStats', b'1') + self.raw(key, raw) + before = {entry.name: entry.read_bytes() for entry in self.namespace.iterdir()} + before_tree = self.tree_bytes() + with self.assertRaises(MigrationRequired): + prepare_manager_start(self.params, self.storage, dry_run=True, auto_migrate=True) + self.assertEqual(self.tree_bytes(), before_tree) + snapshots_before = set((self.storage / 'snapshots').iterdir()) if (self.storage / 'snapshots').exists() else set() + identity = ((self.namespace / key).stat().st_ino, (self.namespace / key).stat().st_mtime_ns) + with self.assertRaises(MigrationRequired): + prepare_manager_start(self.params, self.storage, auto_migrate=True) + retained = {name: value for name, value in before.items() if name != 'ShareUsageStats'} + self.assertEqual({entry.name: entry.read_bytes() for entry in self.namespace.iterdir()}, retained) + self.assertEqual(((self.namespace / key).stat().st_ino, (self.namespace / key).stat().st_mtime_ns), identity) + snapshots = set((self.storage / 'snapshots').iterdir()) - snapshots_before + self.assertEqual(len(snapshots), 2) + self.assertCountEqual([load_snapshot(snapshot) for snapshot in snapshots], [before, retained]) + archived = next(snapshot for snapshot in snapshots if 'ShareUsageStats' in load_snapshot(snapshot)) + receipt = json.loads((archived / 'migration.json').read_bytes()) + self.assertEqual(set(receipt['actions']), {'ShareUsageStats'}) + self.assertEqual(receipt['status'], 'migrated') + (self.namespace / key).unlink() + def raw(self, key, value): (self.namespace / key).write_bytes(value) diff --git a/openpilot/starpilot/ui/tests/test_appearance_settings.py b/openpilot/starpilot/ui/tests/test_appearance_settings.py index 143f21c88d..69578a0d36 100644 --- a/openpilot/starpilot/ui/tests/test_appearance_settings.py +++ b/openpilot/starpilot/ui/tests/test_appearance_settings.py @@ -524,6 +524,7 @@ class AppearanceSettingsTests(unittest.TestCase): view.background_layer = None view.projection_viewport = None object.__setattr__(view, "fonts", type("Fonts", (), {"profile": profile})()) + view.navigation_favorites = onroad.NavigationFavorites(view.fonts) view.camera_layer = None view.extra_overlays = None view.alert = Mock() diff --git a/openpilot/starpilot/ui/tests/test_conditional_perception_render.py b/openpilot/starpilot/ui/tests/test_conditional_perception_render.py index 0b6838a633..1f8a769d94 100644 --- a/openpilot/starpilot/ui/tests/test_conditional_perception_render.py +++ b/openpilot/starpilot/ui/tests/test_conditional_perception_render.py @@ -35,6 +35,7 @@ def test_large_aol_light_uses_same_perception_without_control_authority(): view = OnroadView.__new__(OnroadView) view.fonts = Mock(profile=Profile.LARGE) view.fonts.measure.return_value = NS(width=100, height=30) + view.navigation_favorites = onroad.NavigationFavorites(view.fonts) view.camera_layer = view.background_layer = view.extra_overlays = None view.projection_viewport = None view._corner_cache = onroad.CornerHintCache() diff --git a/openpilot/starpilot/ui/tests/test_developer_preview_render.py b/openpilot/starpilot/ui/tests/test_developer_preview_render.py index 549a294fe4..a148fe0026 100644 --- a/openpilot/starpilot/ui/tests/test_developer_preview_render.py +++ b/openpilot/starpilot/ui/tests/test_developer_preview_render.py @@ -10,6 +10,7 @@ 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_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 @@ -74,6 +75,7 @@ class DeveloperPreviewRenderTests(unittest.TestCase): 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 @@ -111,6 +113,7 @@ class DeveloperPreviewRenderTests(unittest.TestCase): fonts = Mock() fonts.profile = Profile.LARGE view.fonts = fonts + view.navigation_favorites = NavigationFavorites(fonts) view.camera_layer = None view.background_layer = None view.pip_layer = None