Files
StarPilot/openpilot/starpilot/tests/test_vehicle_selection.py
T
firestar5683 f71daaf60f Volvo
Add the Volvo C1 port with matching Python registration and exact native parameter admission.

Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com>
2026-10-14 20:02:39 -05:00

156 lines
7.6 KiB
Python

import json
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
from opendbc.car import gen_empty_fingerprint
from opendbc.car.hyundai.values import CAR
from opendbc.car.structs import car
from openpilot.common.params import Params
from openpilot.starpilot.vehicle_selection import (KEY, VehicleSelectionOwner, choices, read_selection, startup_candidate)
class TestVehicleSelection(unittest.TestCase):
def setUp(self):
self.directory = tempfile.TemporaryDirectory()
self.addCleanup(self.directory.cleanup)
self.params = Params(self.directory.name)
self.parked = True
self.owner = VehicleSelectionOwner(self.params, lambda: self.parked)
def test_absent_auto_and_registered_choices(self):
self.assertEqual(self.owner.snapshot().platform, None)
self.assertTrue(self.owner.snapshot().valid)
self.assertIsNone(startup_candidate(self.params))
self.assertIn(CAR.KIA_XCEED_PHEV, [choice.platform for choice in choices()])
def test_multiple_documented_trims_share_neutral_platform_choice(self):
from opendbc.car.hyundai.values import CAR as HYUNDAI
from opendbc.car.values import PLATFORMS
self.assertGreater(len(PLATFORMS[HYUNDAI.HYUNDAI_IONIQ_6].config.car_docs), 1)
selected = [choice for choice in choices() if choice.platform == HYUNDAI.HYUNDAI_IONIQ_6]
self.assertEqual(len(selected), 1)
self.assertEqual(selected[0].label, 'Hyundai Ioniq 6')
def test_volt_harness_choices_are_under_chevrolet(self):
from opendbc.car.gm.values import CAR as GM
expected = {
GM.CHEVROLET_VOLT: 'Chevrolet Volt (OBD-II Harness) 2017-18',
GM.CHEVROLET_VOLT_ASCM: 'Chevrolet Volt (ASCM Harness) 2017-18',
GM.CHEVROLET_VOLT_2019: 'Chevrolet Volt (SDGM Harness) 2019',
GM.CHEVROLET_VOLT_CAMERA: 'Chevrolet Volt (Camera Harness)',
GM.CHEVROLET_VOLT_CC: 'Chevrolet Volt (CC)',
}
catalog = {choice.platform: choice for choice in self.owner.choices()}
for platform, label in expected.items():
with self.subTest(platform=platform):
self.assertEqual(catalog[platform].make, 'Chevrolet')
self.assertEqual(catalog[platform].label, label)
original = self.owner.snapshot()
self.assertTrue(self.owner.choose(original.raw, str(platform)).verified)
self.assertEqual(startup_candidate(self.params), platform)
def test_k4_2026_documentation_keeps_one_manual_platform_and_factory(self):
from opendbc.car.hyundai.interface import CarInterface
from opendbc.car.values import PLATFORMS
labels = {doc.name for doc in PLATFORMS[CAR.KIA_K4_2025].config.car_docs}
self.assertEqual(labels, {'Kia K4 (without HDA II) 2025', 'Kia K4 (without HDA II) 2026',
'Kia K4 (with HDA II) 2025'})
selected = [choice for choice in self.owner.choices() if choice.platform == CAR.KIA_K4_2025]
self.assertEqual(len(selected), 1)
self.assertTrue(self.owner.choose(None, selected[0].platform).committed)
self.assertEqual(startup_candidate(self.params), CAR.KIA_K4_2025)
candidate = startup_candidate(self.params)
assert candidate is not None
cp = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], False, False, False)
self.assertEqual(cp.carFingerprint, CAR.KIA_K4_2025)
self.assertFalse(cp.openpilotLongitudinalControl)
def test_strict_document_and_corrupt_source_preserved_until_explicit_repair(self):
for bad in (b'{}', b'{"version":true,"platform":null}', b'{"version":1,"platform":"FAKE"}',
b'{"version":1,"platform":null,"platform":"KIA_XCEED_PHEV"}', b'\xff'):
with self.subTest(raw=bad):
Path(self.params.get_param_path(KEY)).write_bytes(bad)
snapshot = read_selection(self.params)
self.assertTrue(snapshot.readable)
self.assertFalse(snapshot.valid)
self.assertEqual(snapshot.raw, bad)
self.assertEqual(startup_candidate(self.params), 'MOCK')
self.assertEqual(Path(self.params.get_param_path(KEY)).read_bytes(), bad)
self.assertIsNone(startup_candidate(self.params, developer_fingerprint=True))
def test_parked_exact_source_selection_and_auto_restoration(self):
original = self.owner.snapshot()
saved = self.owner.choose(original.raw, str(CAR.KIA_XCEED_PHEV))
self.assertTrue(saved.committed and saved.verified, saved)
selected = self.owner.snapshot()
self.assertEqual(selected.platform, CAR.KIA_XCEED_PHEV)
self.assertEqual(startup_candidate(self.params), CAR.KIA_XCEED_PHEV)
self.assertEqual(json.loads(Path(self.params.get_param_path(KEY)).read_bytes()),
{'version': 1, 'platform': CAR.KIA_XCEED_PHEV})
self.assertFalse(self.owner.choose(original.raw, None).committed)
self.parked = False
self.assertFalse(self.owner.choose(selected.raw, None).committed)
self.assertEqual(self.owner.snapshot(), selected)
self.parked = True
reset = self.owner.choose(selected.raw, None)
self.assertTrue(reset.committed and reset.verified)
self.assertIsNone(self.owner.snapshot().platform)
self.assertIsNone(startup_candidate(self.params))
def test_authority_revocation_while_staging_prevents_commit(self):
from openpilot.starpilot import saved_document
original_sync = saved_document.os.fsync
def revoke(fd):
self.parked = False
return original_sync(fd)
with patch.object(saved_document.os, 'fsync', side_effect=revoke):
result = self.owner.choose(None, str(CAR.KIA_XCEED_PHEV))
self.assertFalse(result.committed)
self.assertIsNone(self.owner.snapshot().raw)
def test_unreadable_source_does_not_revert_to_auto_or_get_rewritten(self):
path = Path(self.params.get_param_path(KEY))
outside = path.parent / 'other-vehicle-choice'
raw = b'{"version":1,"platform":"KIA_CEED"}'
outside.write_bytes(raw)
path.symlink_to(outside)
observed = self.owner.snapshot()
self.assertFalse(observed.readable)
self.assertEqual(startup_candidate(self.params), 'MOCK')
self.assertFalse(self.owner.choose(observed.raw, None).committed)
self.assertEqual(outside.read_bytes(), raw)
def test_native_candidate_override_changes_identity_source_only(self):
from opendbc.car import car_helpers
detected = (CAR.KIA_CEED, gen_empty_fingerprint(), '00000000000000000', [],
car.CarParams.FingerprintSource.can, True)
cached = car.CarParams(carFingerprint=CAR.KIA_CEED)
with patch.object(car_helpers, 'fingerprint', return_value=detected) as identify:
ci = car_helpers.get_car(lambda wait_for_one=False: [], lambda _: None, lambda _: None,
False, False, cached_params=cached, forced_candidate=str(CAR.KIA_XCEED_PHEV))
self.assertIsNone(identify.call_args.args[-1])
self.assertEqual(ci.CP.carFingerprint, CAR.KIA_XCEED_PHEV)
self.assertEqual(ci.CP.fingerprintSource, car.CarParams.FingerprintSource.fixed)
self.assertFalse(ci.CP.openpilotLongitudinalControl)
with patch.object(car_helpers, 'fingerprint', return_value=detected) as identify:
automatic = car_helpers.get_car(lambda wait_for_one=False: [], lambda _: None, lambda _: None,
False, False, cached_params=cached, forced_candidate=None)
self.assertIs(identify.call_args.args[-1], cached)
self.assertEqual(automatic.CP.carFingerprint, CAR.KIA_CEED)
self.assertEqual(automatic.CP.fingerprintSource, car.CarParams.FingerprintSource.can)
with patch.object(car_helpers, 'fingerprint', return_value=detected), self.assertRaises(ValueError):
car_helpers.get_car(lambda wait_for_one=False: [], lambda _: None, lambda _: None,
False, False, forced_candidate='UNREGISTERED')
if __name__ == '__main__':
unittest.main()