mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-06 12:33:51 +08:00
145 lines
7.6 KiB
Python
145 lines
7.6 KiB
Python
"""Usage settings use owner values without raw private data or mutations."""
|
|
from dataclasses import replace
|
|
import json
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
from openpilot.common.params import Params
|
|
from openpilot.starpilot.analytics import settings
|
|
from openpilot.starpilot.ui.feature_settings_state import FeatureRow
|
|
from openpilot.starpilot.ui.presentation import Profile
|
|
|
|
|
|
class SecretTrap:
|
|
def __init__(self, params):
|
|
self.params = params
|
|
self.secret_reads = []
|
|
|
|
def __getattr__(self, name):
|
|
if name in ("put", "put_bool", "remove", "clear_all", "all_keys"):
|
|
raise AssertionError(f"Collector attempted {name}")
|
|
function = getattr(self.params, name)
|
|
|
|
def call(key, *args, **kwargs):
|
|
if key in ("AccessToken", "SecOCKey", "LastGPSPosition", "GalaxyPairing", "CarParams", "DongleId"):
|
|
self.secret_reads.append(key)
|
|
raise AssertionError("Private data read")
|
|
return function(key, *args, **kwargs)
|
|
return call
|
|
|
|
|
|
class TestScalarProjection(unittest.TestCase):
|
|
def row(self, **kwargs):
|
|
return FeatureRow("NewFeature", "New Feature", "On", choices=("Off", "On"), default_value="Off", **kwargs)
|
|
|
|
def test_new_rows_are_automatic_without_exporting_sources(self):
|
|
row = self.row(source=b"private-bytes", related_source=b"private-related", dependencies=(("AccessToken", b"private-token"),))
|
|
with patch.object(settings, "_owner_rows", return_value=iter((("data", row),))):
|
|
result = settings.collect_settings(object(), None)
|
|
self.assertTrue(result["setting_data_NewFeature"])
|
|
self.assertFalse(result["default_data_NewFeature"])
|
|
self.assertTrue(result["valid_data_NewFeature"])
|
|
self.assertNotIn("private", json.dumps(result))
|
|
|
|
def test_invalid_numbers_fixed_enums_and_commands(self):
|
|
number = FeatureRow("Gain", "Gain", ".75", step=.05, minimum=.5, maximum=1.5, default_value="1")
|
|
mode = FeatureRow("Mode", "Mode", "Auto", choices=("Auto", "Manual"), default_value="Manual")
|
|
rows = (("lane", number), ("lane", mode), ("lane", replace(number, key="BadGain", value="NaN")),
|
|
("lane", replace(number, key="TooLarge", value="2")),
|
|
("lane", replace(mode, key="FreeText", choices=(), default_value=None, value="SECRET")),
|
|
("sounds", replace(mode, key="SoundPack", choices=("SECRET",), value="SECRET")),
|
|
("lane", replace(self.row(), key="lane:reset")),
|
|
("lane", replace(self.row(), key="torque_prepare_firestar")))
|
|
result = settings._fields(rows)
|
|
self.assertEqual(result["setting_lane_Gain"], .75)
|
|
self.assertEqual(result["default_lane_Gain"], 1.0)
|
|
self.assertEqual(result["setting_lane_Mode"], "Auto")
|
|
self.assertFalse(result["valid_lane_BadGain"])
|
|
self.assertNotIn("setting_lane_BadGain", result)
|
|
self.assertFalse(result["valid_lane_TooLarge"])
|
|
self.assertNotIn("SECRET", json.dumps(result))
|
|
self.assertFalse(any("reset" in key or "prepare" in key for key in result))
|
|
|
|
def test_deterministic_bounds_and_field_id_collisions(self):
|
|
rows = [("page", replace(self.row(), key=f"Feature{i}")) for i in range(settings.MAX_SETTINGS + 10)]
|
|
first, second = settings._fields(rows), settings._fields(reversed(rows))
|
|
self.assertEqual(first, second)
|
|
self.assertTrue(first["settings_truncated"])
|
|
self.assertLessEqual(len(json.dumps(first).encode()), settings.MAX_BYTES)
|
|
collision = settings._fields((("page", replace(self.row(), key="a:b")), ("page", replace(self.row(), key="a_b"))))
|
|
self.assertFalse(collision["valid_page_a_b"])
|
|
self.assertNotIn("setting_page_a_b", collision)
|
|
|
|
|
|
class TestActualSettingsOwners(unittest.TestCase):
|
|
def setUp(self):
|
|
self.temp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.temp.cleanup)
|
|
self.params = Params(self.temp.name)
|
|
self.params.put_bool("IsOffroad", True, block=True)
|
|
|
|
def files(self):
|
|
return {str(path.relative_to(self.temp.name)): path.read_bytes()
|
|
for path in Path(self.temp.name).rglob("*") if path.is_file()}
|
|
|
|
def test_real_owners_default_values_and_profile_defaults(self):
|
|
self.params.put_bool("LaneCentering", True, block=True)
|
|
self.params.put_bool("StockConfidenceBallWidget", True, block=True)
|
|
trap = SecretTrap(self.params)
|
|
before = self.files()
|
|
large = settings.collect_settings(trap, None, profile=Profile.LARGE)
|
|
compact = settings.collect_settings(trap, None, profile=Profile.COMPACT)
|
|
self.assertNotIn("setting_data_ShareUsageStats", large)
|
|
self.assertNotIn("default_data_ShareUsageStats", large)
|
|
self.assertTrue(large["setting_lane_LaneCentering"])
|
|
self.assertTrue(large["default_lane_LaneCentering"])
|
|
self.assertFalse(compact["setting_appearance_StockConfidenceBallWidget"])
|
|
self.assertTrue(compact["default_appearance_StockConfidenceBallWidget"])
|
|
self.assertEqual(large["default_display_ScreenTimeoutOnroad"], 10.0)
|
|
self.assertEqual(compact["default_display_ScreenTimeoutOnroad"], 5.0)
|
|
self.assertEqual(large["setting_display_ScreenBrightness"], "Auto")
|
|
self.assertEqual(self.files(), before)
|
|
self.assertEqual(trap.secret_reads, [])
|
|
self.assertTrue(all(type(value) in (bool, float, str) for value in large.values()))
|
|
|
|
def test_malformed_saved_value_and_secret_files_remain_private(self):
|
|
Path(self.params.get_param_path("LaneCentering")).write_bytes(b"PRIVATE_MALFORMED")
|
|
for key in ("AccessToken", "LastGPSPosition", "SecOCKey"):
|
|
Path(self.params.get_param_path(key)).write_bytes(b"PRIVATE_SECRET")
|
|
before = self.files()
|
|
trap = SecretTrap(self.params)
|
|
result = settings.collect_settings(trap, None, profile=Profile.LARGE)
|
|
self.assertFalse(result["valid_lane_LaneCentering"])
|
|
self.assertNotIn("setting_lane_LaneCentering", result)
|
|
self.assertNotIn("PRIVATE", json.dumps(result))
|
|
self.assertEqual(self.files(), before)
|
|
self.assertEqual(trap.secret_reads, [])
|
|
|
|
def test_nested_routes_and_saved_monitor_choices_exclude_layouts(self):
|
|
from openpilot.starpilot.sentry_mode.preferences import KEY as SENTRY, Preferences as Sentry, encode as encode_sentry
|
|
from openpilot.starpilot.sentry_mode.policy import Settings
|
|
from openpilot.starpilot.spot_monitor.preferences import KEY as VASM, Preferences as Visual, encode as encode_visual
|
|
|
|
self.params.put_bool("PIPPreviewShowOnBlinker", True, block=True)
|
|
self.params.put(SENTRY, json.loads(encode_sentry(Sentry(True, Settings(.123, 2.5)))), block=True)
|
|
self.params.put(VASM, json.loads(encode_visual(Visual(confidence=.96, smooth_seconds=.3))), block=True)
|
|
before = self.files()
|
|
result = settings.collect_settings(SecretTrap(self.params), None, profile=Profile.COMPACT)
|
|
self.assertTrue(result["setting_pip_PIPPreviewShowOnBlinker"])
|
|
self.assertTrue(result["setting_sentry_sentry_enabled"])
|
|
self.assertEqual(result["setting_sentry_sentry_sensitivity"], .123)
|
|
self.assertEqual(result["setting_sentry_sentry_warning"], 2.5)
|
|
self.assertEqual(result["setting_vasm_vasm_confidence"], .96)
|
|
self.assertEqual(result["setting_vasm_vasm_smooth"], .3)
|
|
for personality in ("traffic", "aggressive", "standard", "relaxed"):
|
|
for category in ("acceleration", "braking", "following"):
|
|
key = f"setting_{personality}_{category}_profile_{personality}_{category}"
|
|
self.assertIn(key, result)
|
|
self.assertIsInstance(result[key], str)
|
|
self.assertTrue(any(key.startswith("setting_conditional_cem_") for key in result))
|
|
self.assertTrue(any(key.startswith("setting_conditional_ccm_") for key in result))
|
|
self.assertFalse(any("pip_mask" in key or "pip_format" in key or "annotation" in key for key in result))
|
|
self.assertEqual(self.files(), before)
|