Align source fixtures and cover composite MAIN freshness

This commit is contained in:
firestar5683
2026-10-23 22:43:56 -05:00
parent 15dc2cb26c
commit 4f4cb4e7d9
11 changed files with 87 additions and 26 deletions
@@ -16,9 +16,9 @@ from openpilot.starpilot.car.toyota.prius_preferences import PriusFilterPreferen
def make_ci(identity, params, *, standstill=False):
rack = structs.CarParams.CarFw.new_message(ecu=structs.CarParams.Ecu.eps,
fwVersion=b'8965B47070' + bytes(6))
fwVersion=b'8965B47070' + bytes(6), brand='toyota', address=0x7A1)
cp = CarInterface.get_params(identity, {0: {0x2FF: 4}, 1: {}, 2: {}}, [rack], False, False, False)
assert cp.safetyConfigs[0].safetyParam == 4169
assert cp.safetyConfigs[0].safetyParam == 0xD049
ci = CarInterface(cp)
ci.CC.prius_filter_input = PriusFilterPreference(cp, params)
state = structs.CarState(canValid=True, canTimeout=False, standstill=standstill,
@@ -12,12 +12,12 @@ def params(car, fp=None, alpha=False):
class TestRetrofit(unittest.TestCase):
def test_prius_smart_dsu_requires_exact_rack_and_source(self):
from opendbc.car.structs import CarParams
rack = CarParams.CarFw.new_message(ecu=CarParams.Ecu.eps, fwVersion=b'8965B47070\x00\x00\x00\x00\x00\x00')
rack = CarParams.CarFw.new_message(ecu=CarParams.Ecu.eps, fwVersion=b'8965B47070\x00\x00\x00\x00\x00\x00', brand='toyota', address=0x7A1)
from opendbc.car.toyota.prius_longitudinal import enabled
from opendbc.car.toyota.prius_longitudinal import stopping_decel_rate
for alpha in (False, True):
cp = CarInterface.get_params(CAR.TOYOTA_PRIUS_RETROFIT, {0: {0x2FF: 4}, 1: {}, 2: {}}, [rack], alpha, False, False)
self.assertEqual(cp.safetyConfigs[0].safetyParam, 4169)
self.assertEqual(cp.safetyConfigs[0].safetyParam, 0xD049)
self.assertTrue(enabled(cp))
self.assertFalse(cp.dashcamOnly)
self.assertTrue(cp.openpilotLongitudinalControl)
@@ -29,14 +29,14 @@ class TestRetrofit(unittest.TestCase):
automatic = CarInterface.get_params(CAR.TOYOTA_PRIUS, {0: {0x2FF: 4}, 1: {}, 2: {}}, [rack], False, False, False)
self.assertTrue(enabled(automatic))
self.assertEqual(automatic.carFingerprint, CAR.TOYOTA_PRIUS)
self.assertEqual(automatic.safetyConfigs[0].safetyParam, 4169)
self.assertEqual(automatic.safetyConfigs[0].safetyParam, 0xD049)
self.assertFalse(automatic.flags & ToyotaFlags.TSS2)
self.assertAlmostEqual(automatic.steerActuatorDelay, .14)
from opendbc.car.toyota.prius_longitudinal import configured, prepare_stock
self.assertTrue(prepare_stock(automatic))
self.assertTrue(configured(automatic))
self.assertFalse(enabled(automatic))
self.assertEqual(automatic.safetyConfigs[0].safetyParam, 4681)
self.assertEqual(automatic.safetyConfigs[0].safetyParam, 0xD249)
self.assertFalse(automatic.openpilotLongitudinalControl)
self.assertTrue(automatic.pcmCruise)
self.assertTrue(prepare_stock(automatic))
@@ -66,7 +66,7 @@ class TestRetrofit(unittest.TestCase):
for field, value in (('passive', True), ('notCar', True), ('dashcamOnly', True),
('pcmCruise', False), ('openpilotLongitudinalControl', True),
('secOcRequired', True), ('flags', int(ToyotaFlags.GAS_INTERCEPTOR)),
('carFingerprint', CAR.TOYOTA_COROLLA), ('alternativeExperience', 160)):
('carFingerprint', CAR.TOYOTA_COROLLA), ('alternativeExperience', 96)):
rejected = cp.as_reader().as_builder()
setattr(rejected, field, value)
self.assertFalse(qualified(rejected), field)
@@ -75,7 +75,7 @@ class TestRetrofit(unittest.TestCase):
rejected.safetyConfigs[0].safetyParam = word
self.assertFalse(qualified(rejected), word)
self.assertFalse(native_accepts_cp(rejected, int(structs.CarParams.SafetyModel.toyota), word), word)
for ae in (0, 1, 33, 128, 160, 256, 288):
for ae in (0, 1, 33, 128, 96, 256, 544):
rejected = cp.as_reader().as_builder()
rejected.alternativeExperience = ae
self.assertFalse(qualified(rejected, marked_only=True), ae)
@@ -89,7 +89,7 @@ class TestRetrofit(unittest.TestCase):
rejected = cp.as_reader().as_builder()
rejected.safetyConfigs[0].safetyModel = structs.CarParams.SafetyModel.noOutput
self.assertFalse(qualified(rejected))
for fp in ({0: {0x2FF: 8}, 1: {}, 2: {}}, {0: {}, 1: {}, 2: {0x343: 8}}):
for fp in ({0: {0x2FF: 7}, 1: {}, 2: {}}, {0: {}, 1: {}, 2: {0x343: 8}}):
takeover = params(CAR.TOYOTA_MATRIX_RETROFIT, fp, alpha)
self.assertTrue(takeover.dashcamOnly)
self.assertFalse(qualified(takeover))
@@ -114,7 +114,7 @@ class TestRetrofit(unittest.TestCase):
def test_observed_takeover_requires_paired_port(self):
for car in (CAR.TOYOTA_MATRIX_RETROFIT, CAR.TOYOTA_PRIUS_RETROFIT):
for fp in ({0: {0x2FF: 8}, 1: {}, 2: {}},
for fp in ({0: {0x2FF: 7}, 1: {}, 2: {}},
{0: {}, 1: {}, 2: {0x343: 8}}):
cp = params(car, fp, True)
self.assertTrue(cp.dashcamOnly)
@@ -133,7 +133,7 @@ class TestRetrofit(unittest.TestCase):
def test_retrofit_rack_is_distinct_from_standard_prius(self):
from opendbc.car.structs import CarParams
fw = CarParams.CarFw.new_message(ecu=CarParams.Ecu.eps, fwVersion=b'8965B47070\x00\x00\x00\x00\x00\x00')
fw = CarParams.CarFw.new_message(ecu=CarParams.Ecu.eps, fwVersion=b'8965B47070\x00\x00\x00\x00\x00\x00', brand='toyota', address=0x7A1)
fp = {0: {}, 1: {}, 2: {}}
cp = CarInterface.get_params(CAR.TOYOTA_PRIUS_RETROFIT, fp, [fw], False, False, False)
self.assertFalse(cp.dashcamOnly)
@@ -170,7 +170,7 @@ class TestRetrofit(unittest.TestCase):
from opendbc.car.toyota.values import DBC
rack = structs.CarParams.CarFw.new_message(ecu=structs.CarParams.Ecu.eps,
fwVersion=b'8965B47070\x00\x00\x00\x00\x00\x00')
fwVersion=b'8965B47070\x00\x00\x00\x00\x00\x00', brand='toyota', address=0x7A1)
cases = ((CAR.TOYOTA_PRIUS, False, 'ACC_CONTROL', 'DISTANCE', 0),
(CAR.TOYOTA_PRIUS_RETROFIT, False, 'ACC_CONTROL', 'DISTANCE', 0),
(CAR.TOYOTA_PRIUS, True, 'SDSU', 'FD_BUTTON', 0),
@@ -179,7 +179,7 @@ class TestRetrofit(unittest.TestCase):
(CAR.TOYOTA_COROLLA_TSS2, False, 'ACC_CONTROL', 'DISTANCE', 2))
for car, filtered, message, signal, bus in cases:
with self.subTest(car=car, filtered=filtered):
cp = CarInterface.get_params(car, {0: {0x2FF: 4} if filtered else {}, 1: {}, 2: {}},
cp = CarInterface.get_params(car, {0: {0x2FF: 8} if filtered else {}, 1: {}, 2: {}},
[rack] if filtered else [], False, False, False)
cs = CarState(cp)
parsers = cs.get_can_parsers(cp)
@@ -72,10 +72,10 @@ class TestZssCaller(unittest.TestCase):
fwVersion=b'8965B47070' + bytes(6))
cp = CarInterface.get_params(CAR.TOYOTA_PRIUS, {0: {0x23: 8, 0x2FF: 8}, 1: {}, 2: {}}, [rack], False, False, False)
self.assertTrue(configured(cp))
self.assertEqual(cp.safetyConfigs[0].safetyParam, 4169)
self.assertEqual(cp.safetyConfigs[0].safetyParam, 0x8049)
sibling = cp.as_reader().as_builder()
sibling.flags |= 1 << 17
self.assertFalse(configured(sibling))
self.assertTrue(prepare_stock(cp))
self.assertEqual(cp.safetyConfigs[0].safetyParam, 4681)
self.assertEqual(cp.safetyConfigs[0].safetyParam, 0x8249)
self.assertTrue(cp.flags & ToyotaFlags.ZSS)
@@ -55,7 +55,7 @@ class TestPairedPedal(unittest.TestCase):
def test_factory_current_words_and_exact_negatives(self):
self.assertTrue(pedal.configured(self.cp))
self.assertEqual(self.cp.safetyConfigs[0].safetyParam, 0x2049)
for fp in ({0: {0x201: 7}, 1: {}, 2: {}}, {0: {}, 1: {0x201: 6}, 2: {}}, {0: {0x201: 6, 0x2FF: 8}, 1: {}, 2: {}}):
for fp in ({0: {0x201: 7}, 1: {}, 2: {}}, {0: {}, 1: {0x201: 6}, 2: {}}, {0: {0x201: 5, 0x2FF: 8}, 1: {}, 2: {}}):
cp = CarInterface.get_params(self.candidate, fp, firmware(self.candidate), False, False, False)
self.assertFalse(pedal.configured(cp))
self.assertTrue(pedal.prepare_stock(self.cp))
@@ -107,9 +107,9 @@ class TestPairedPedal(unittest.TestCase):
self.safety.set_timer(1_300_001)
self.assertFalse(self.tx(0.1))
self.assertTrue(self.tx(0))
for word in (0x2149, 0x2849, 0x3049, 0xA049):
for word in (0x2149, 0x2849, 0x3048, 0xA048):
self.init(word)
self.assertFalse(self.tx(0))
self.assertFalse(self.tx(0), hex(word))
self.assertFalse(self.tx(0.1))
def test_native_command_pair_bounds_checksum_bus_and_neutral(self):
@@ -44,3 +44,59 @@ def test_parent_catalog_and_warm_switch_are_exact(word, parent):
finally:
safety.set_alternative_experience(0)
safety.set_safety_hooks(structs.CarParams.SafetyModel.noOutput, 0)
@pytest.mark.parametrize('word', (0xF149, 0xF349, 0xF04D, 0xF24D))
@pytest.mark.parametrize('fault', ('main_off', 'main_stale'))
def test_composite_alternate_main_keeps_its_independent_freshness(word, fault):
from opendbc.can import CANPacker
from opendbc.car import Bus
from opendbc.car.toyota import pedal
from opendbc.car.toyota.values import CAR, DBC
safety = libsafety_py.libsafety
safety.init_tests()
safety.set_alternative_experience(32)
assert safety.set_safety_hooks(structs.CarParams.SafetyModel.toyota, word) == 0
safety.set_aol_test_heartbeat(True)
identity = CAR.LEXUS_GS_F if word in (0xF149, 0xF349) else CAR.LEXUS_IS
packer = CANPacker(DBC[identity][Bus.pt])
now = 1_000_000
counter = 0
def tick(*, main=True, omit_main=False):
nonlocal now, counter
now += 10_000
safety.set_timer(now)
frames = helpers.physical(packer, cruise=True)
frames.append(packer.make_can_msg('PCS_HUD', 0, {}))
frames.append(packer.make_can_msg('PCM_CRUISE_ALT', 0, {'UI_SET_SPEED': 90}))
if not omit_main:
frames.append(packer.make_can_msg('DSU_CRUISE', 0, {'MAIN_ON': main, 'SET_SPEED': 90}))
address, data, bus = packer.make_can_msg('GAS_SENSOR', 0, {
'INTERCEPTOR_GAS': 0, 'INTERCEPTOR_GAS2': 0, 'COUNTER_PEDAL': counter, 'STATE': 0})
counter = (counter + 1) & 15
data = bytearray(data)
data[-1] = pedal.checksum(data)
frames.append((address, bytes(data), bus))
for address, data, bus in frames:
safety.safety_rx_hook(libsafety_py.make_CANPacket(address, bus, data))
safety.safety_tick()
safety.aol_set_host_request(3)
try:
assert safety.current_safety_config.rx_checks_len == 8
for _ in range(30):
tick()
expected = 1 if word in (0xF349, 0xF24D) else 3
assert safety.safety_config_valid()
assert safety.aol_get_permission_mask() == expected
for _ in range(40):
tick(main=False, omit_main=fault == 'main_stale')
assert safety.aol_get_permission_mask() == 0
for _ in range(30):
tick()
assert safety.aol_get_permission_mask() == expected
finally:
safety.set_alternative_experience(0)
safety.set_safety_hooks(structs.CarParams.SafetyModel.noOutput, 0)
@@ -123,18 +123,21 @@ def test_vehicle_fault_rearm_cannot_be_bypassed(factory, field):
def test_gm_resolved_fault_accepts_new_successful_engagement_only():
def test_gm_resolved_fault_rearms_only_after_healthy_main_observation():
owner, cs = GmAolCardIntent(SETTINGS), state()
observe(owner, cs, 1, enabled=False, active=False)
owner.update(cs, now_ns=2, fault_active=True)
assert owner._main_cycle_required and not owner.allowed_latch
owner.update(cs, now_ns=3, fault_active=False)
assert owner._main_cycle_required and not owner.allowed_latch
assert not owner._main_cycle_required and owner.allowed_latch
observe(owner, cs, 3, active=False)
assert owner._main_cycle_required and not owner.allowed_latch
assert not owner._main_cycle_required and owner.allowed_latch
observe(owner, cs, 4)
assert owner.allowed_latch and not owner._main_cycle_required
assert not owner.pause_lateral
cs.cruiseState.available = False
owner.update(cs, now_ns=5, fault_active=False)
assert not owner.allowed_latch
def test_physical_owners_cannot_be_forged_by_host_engagement():
+1 -1
View File
@@ -866,7 +866,7 @@ class TestGmAol(unittest.TestCase):
requested = []
for tick in range(18):
now = 1_000_000_000 + tick * 10_000_000
out, sources = feed_car(ci, packer, now, counter=tick % 4, active=False, brake=True, regen=True)
out, sources = feed_car(ci, packer, now, counter=tick % 4, active=False, brake=True, regen=True, eps_status=1)
sources = [source for source in sources if source[0] not in (0x1C4, 0xBE, 0xF1)]
sources.extend([packer.make_can_msg('AcceleratorPedal2', 0, {'CruiseState': 0}),
packer.make_can_msg('ECMAcceleratorPos', 0, {'BrakePedalPos': 12}),
@@ -16,7 +16,7 @@ def test_complete_sdsu_cp_tunes_retained_for_advertisement(candidate, stock):
fp[0][0x2FF] = 4
new = CarInterface.get_params(candidate, fp, fw, False, False, False)
assert legacy_advert_filter.enabled(new) and advert_filter.enabled(new)
assert not toyota_auto_hold_supported(new)
assert toyota_auto_hold_supported(new) == toyota_auto_hold_supported(old)
if stock:
assert rav4h_filter.prepare_stock(old) and advert_filter.prepare_stock(new)
assert legacy_advert_filter.configured(new)
@@ -137,7 +137,7 @@ def test_toyota_default_gap_callback_saves_personality_once(native_session, monk
runtime, session, ui, _, _ = native_session
rack = structs.CarParams.CarFw.new_message(ecu=structs.CarParams.Ecu.eps,
fwVersion=b'8965B47070\x00\x00\x00\x00\x00\x00')
ui.CP = CarInterface.get_params(getattr(CAR, vehicle), {0: {0x2FF: 4} if filtered else {}, 1: {}, 2: {}},
ui.CP = CarInterface.get_params(getattr(CAR, vehicle), {0: {0x2FF: 8} if filtered else {}, 1: {}, 2: {}},
[rack] if filtered else [], False, False, False)
ui.has_longitudinal_control = ui.CP.openpilotLongitudinalControl
ui.personality = 1
@@ -218,6 +218,7 @@ def test_actual_main_source_reader_reuses_lifecycle_for_cache_and_persistence():
'__getitem__': lambda self, key: cal,
'all_checks': lambda self, keys: True,
})()
sm.seen = {'extrinsicsCalibration': True}
sm.valid = {'extrinsicsCalibration': True}
sm.logMonoTime = {'extrinsicsCalibration': BASE}
sm.recv_time = {'extrinsicsCalibration': BASE / 1e9}
@@ -250,7 +251,8 @@ def test_actual_main_source_reader_reuses_lifecycle_for_cache_and_persistence():
assert persistence.current_sources('fresh-identity', fresh.source_epoch)[0] == 'fresh-identity'
for tick in range(100):
gap = BASE + (tick + 1) * 1_000_000_000
assert not source_reader(gap).enabled
retained = source_reader(gap)
assert retained.enabled and retained.source_epoch == fresh.source_epoch
sm.logMonoTime['extrinsicsCalibration'] = gap + 1
sm.recv_time['extrinsicsCalibration'] = (gap + 1) / 1e9
resumed = source_reader(gap + 1)
+1 -1
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "91232f8d24f79ff480b1bf29dfc58b1ec124af6001e6793a0326068b19192c18", "schema_version": 1}
{"protected_inventory_sha256": "ffb6b1825429c5d33a8adecb7f88aa090fb9f21d84c94b9cac3e37267e6bd4f4", "schema_version": 1}