Preserve AOL intent across native updates and classic cruise cancellation

This commit is contained in:
firestar5683
2026-10-21 02:03:14 -05:00
parent 708a68adeb
commit cb85a84e30
11 changed files with 744 additions and 19 deletions
@@ -72,12 +72,6 @@ def native_accepts(cp, model, param):
def independent_buttons_qualified(cp, *, marked_only=True):
if cp.carFingerprint == CAR.HYUNDAI_SONATA:
expected_word = 0xC04
elif cp.carFingerprint == CAR.HYUNDAI_SONATA_HYBRID:
expected_word = 0xC06
else:
return False
return bool(qualified(cp, marked_only=marked_only) and
return bool(cp.carFingerprint in CLASSIC_LONG_IDS and qualified(cp, marked_only=marked_only) and
cp.alternativeExperience in ((AOL_EXPERIENCE,) if marked_only else (0, AOL_EXPERIENCE)) and
aol_word(cp) == expected_word)
aol_word(cp) in (0xC04, 0xC05, 0xC06, 0xC44, 0xC45, 0xC46))
@@ -0,0 +1,499 @@
from opendbc.car.hyundai.classic_long_aol import CLASSIC_LONG_IDS
from opendbc.car.hyundai.values import HyundaiFlags
import ast
import inspect
import textwrap
import pytest
import importlib
import os
from types import SimpleNamespace
from unittest.mock import patch
from opendbc.can import CANPacker
from opendbc.car import structs
from opendbc.car.hyundai.interface import CarInterface
from opendbc.car.hyundai.values import CAR
from opendbc.car.hyundai.classic_long_aol import qualified as qualified_classic
from opendbc.safety.tests.libsafety import libsafety_py
from opendbc.safety.tests.test_hyundai import checksum
from openpilot.cereal import messaging
from openpilot.common.params import Params
from openpilot.common.prefix import OpenpilotPrefix
from openpilot.selfdrive.car import card as card_module
from openpilot.starpilot.aol.intent import AOL_TOGGLE, read_settings
from openpilot.starpilot.aol.runtime import current_native
from openpilot.starpilot.aol.tests.test_classic_long_profiles import params as classic_params
from openpilot.starpilot.aol.tests import test_ioniq_availability as caller_fixture
from openpilot.starpilot.aol.wire import PandaSlot, SafetyState, encode_safety
from openpilot.starpilot.car.hyundai.classic_long_intent import ClassicLongCardIntent
disable_ecu_module = importlib.import_module('opendbc.car.disable_ecu')
def classic_frames(ci, tick, *, main=0, lkas=0, button=0, engaged=False):
assert qualified_classic(ci.CP, marked_only=True)
packer = CANPacker('hyundai_can_generated')
gas_name = 'E_EMS11' if ci.CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV) else 'EMS16'
hz = {gas_name:100, 'ELECT_GEAR':20, 'WHL_SPD11':100, 'TCS13':100, 'MDPS12':50, 'CLU11':50,
'BCM_PO_11':50, 'LKAS11':100}
scheduled = {(packer.dbc.name_to_msg[name].address, 0): name for name in hz if name != 'LKAS11'}
for parser in ci.can_parsers.values():
for address in parser.addresses:
scheduled[(address, parser.bus)] = parser.dbc.addr_to_msg[address].name
drive_gear = next(code for code, label in ci.CS.shifter_values.items() if ci.CS.parse_gear_shifter(label) == structs.CarState.GearShifter.drive)
frames = []
for (address,bus),name in sorted(scheduled.items()):
parser = next((p for p in ci.can_parsers.values() if p.bus == bus), None)
declared = parser.message_states[address].frequency if parser is not None and address in parser.addresses else 0
frequency = hz.get(name, min(declared,100) if 0 < declared <= 100 else 100)
period = max(1,round(100/frequency))
if tick % period:
continue
counter = tick // period
values = {}
if name == 'CLU11':
values.update(CF_Clu_AliveCnt1=counter % 16, CF_Clu_CruiseSwState=button, CF_Clu_CruiseSwMain=main)
elif name == 'EMS16':
values.update(AliveCounter=counter % 4, CF_Ems_AclAct=0)
elif name == 'E_EMS11':
values['CR_Vcu_AccPedDep_Pos']=0
elif name == 'ELECT_GEAR':
values['Elect_Gear_Shifter']=drive_gear
elif name == 'WHL_SPD11':
values.update(WHL_SPD_AliveCounter_LSB=counter % 4, WHL_SPD_AliveCounter_MSB=(counter//4) % 4,
WHL_SPD_FL=72,WHL_SPD_FR=72,WHL_SPD_RL=72,WHL_SPD_RR=72)
elif name == 'TCS13':
values.update(AliveCounterTCS=counter % 8, ACCEnable=0, ACC_REQ=int(engaged), DriverOverride=0)
elif name == 'MDPS12':
values.update(CR_Mdps_StrColTq=0, CR_Mdps_OutTq=0)
elif name == 'BCM_PO_11':
values['LDA_BTN']=lkas
elif name == 'CGW1':
values['CF_Gway_DrvSeatBeltSw']=1
elif name == 'LVR12':
values['CF_Lvr_Gear']=drive_gear
elif name == 'TCU12':
values['CUR_GR'] = drive_gear
elif name == 'SAS11':
values['SAS_Angle']=1.
elif name == 'CLU15':
values.update(CF_Clu_VehicleSpeed=72, CF_Clu_Gear=drive_gear)
frame = packer.make_can_msg(name,bus,values)
if name in ('EMS16','WHL_SPD11','TCS13'):
address, data, bus = checksum(frame)
frame = (address, bytes(data), bus)
frames.append(frame)
assert all(address not in (0x420,0x421) for address,_,_ in frames), 'Disabled SCC source must not be fabricated'
assert all(address!=0x340 or bus==2 for address,_,bus in frames), 'Never echo outgoing LKAS on protected RX bus'
return frames
def _invalid_controls_emits_no_commands(received):
if 'sendcan' not in received:
return True
envelope = received['sendcan']
return envelope.which() == 'sendcan' and not envelope.valid and len(envelope.sendcan) == 0
def family_cases():
identities = []
for identity in sorted(CLASSIC_LONG_IDS):
if qualified_classic(classic_params(identity, lda=True)):
identities.append(identity)
assert len(identities) == 32
return identities
@pytest.mark.parametrize('identity', family_cases())
def test_actual_classic_family_cancel_and_manual_withdrawal(identity, monkeypatch):
withdrawal = 'toggle-off'
expect_retention = True
shared_long, incidental = True, 'cancel'
native = libsafety_py.libsafety
case = caller_fixture.IoniqAvailabilityTests(methodName='runTest')
card = None
with OpenpilotPrefix(), patch.dict(os.environ, {'SIMULATION': '0', 'AOL_REPLAY_RUNTIME': '1'}):
saved = Params()
saved.put_bool('OpenpilotEnabledToggle', True, block=True)
saved.put_bool('AlwaysOnLateral', True, block=True)
saved.put('LKASButtonControl', AOL_TOGGLE, block=True)
saved.put('MainCruiseButtonControl', 0, block=True)
now = [10_000_000_000]
for key in ('CancelButtonControl', 'LongCancelButtonControl', 'VeryLongCancelButtonControl'):
saved.put(key, 0, block=True)
try:
saved.put_bool('AlphaLongitudinalEnabled', True, block=True)
saved.put_bool('IsReleaseBranch', False, block=True)
cp = classic_params(identity, lda=True)
assert qualified_classic(cp) and not cp.pcmCruise and cp.openpilotLongitudinalControl
assert cp.lateralTuning.which() == 'torque'
expects_owner = identity == CAR.GENESIS_G90
assert CarInterface.startup_required(cp) == expects_owner
from opendbc.car import car_helpers, gen_empty_fingerprint
from opendbc.car.hyundai import ecu_startup
from opendbc.car.hyundai.tests.test_g90_continuity import ManualKeeper
diagnostic_requests = []
startup_owners = []
native.set_alternative_experience(0)
assert native.set_safety_hooks(structs.CarParams.SafetyModel.elm327, 1) == 0
native.init_tests()
startup_factory = CarInterface.startup_owner
def traced_factory(prepared_cp, callbacks, *, requested):
recv, send = callbacks
def diagnostic_send(frames):
for address, data, bus in frames:
assert native.safety_tx_hook(libsafety_py.make_CANPacket(address, bus, data))
if saved.get('CarParams') is None:
assert not native.get_controls_allowed() and native.aol_get_permission_mask() == 0
return send(frames)
owner = startup_factory(prepared_cp, (recv, diagnostic_send), requested=requested)
if owner is None:
assert not expects_owner
return None
assert expects_owner
owner._query = startup_query
owner.clock = lambda: now[0] / 1e9
owner.keeper_factory = ManualKeeper
startup_owners.append(owner)
return owner
def startup_query(sender, receiver, bus, addresses, requests, responses):
request = requests[0]
assert bus == 0 and addresses == [(0x7d0, None)]
assert request in (b'\x10\x03', b'\x28\x83\x01')
assert saved.get('CarParams') is None
diagnostic_requests.append(request)
def receive(timeout):
sender([(0x7d0, bytes([len(request)]) + request + bytes(7 - len(request)), bus)])
return {(0x7d0, None): b''} if request == b'\x10\x03' else {}
return SimpleNamespace(get_data=receive)
fp = gen_empty_fingerprint()
fp[0][0x391] = 8
identified = (identity, fp, '0' * 17, [], cp.fingerprintSource, True)
initial_can = messaging.new_message('can', 1, valid=True, logMonoTime=now[0])
initial_can.can[0].address, initial_can.can[0].src, initial_can.can[0].dat = 0x391, 0, bytes(8)
with patch.object(car_helpers, 'fingerprint', return_value=identified), \
patch.object(card_module.messaging, 'recv_one_retry', return_value=initial_can.as_reader()), \
patch.object(CarInterface, 'startup_owner', side_effect=traced_factory), \
patch.object(card_module.Car, 'startup_diagnostic_admission', return_value=True), \
patch.object(ecu_startup, 'IsoTpParallelQuery', side_effect=startup_query), \
patch('time.clock_gettime_ns', side_effect=lambda clock: now[0]), \
patch('time.monotonic_ns', side_effect=lambda: now[0]), patch('time.sleep'):
card = card_module.Car()
if expects_owner:
assert len(startup_owners) == 1 and card.vehicle_startup.owner is startup_owners[0]
assert card.vehicle_startup.owner.published and card.vehicle_startup.owner.prepared_for(card.CP)
else:
assert not startup_owners and card.vehicle_startup.owner is None
assert diagnostic_requests == ([b'\x10\x03', b'\x28\x83\x01'] if expects_owner else [])
assert qualified_classic(card.CP, marked_only=True)
expected_word = 0xC04
expected_word |= 2 if card.CP.flags & HyundaiFlags.HYBRID else (1 if card.CP.flags & HyundaiFlags.EV else 0)
expected_word |= 0x40 if card.CP.flags & HyundaiFlags.ALT_LIMITS else 0
assert card.CP.safetyConfigs[0].safetyParam == expected_word and card.CP.alternativeExperience == 32
assert isinstance(card.aol_card_intent, ClassicLongCardIntent)
if not expect_retention:
assert not card.aol_card_intent.independent_cruise_buttons, 'Published classic policy changed; reclassify baseline'
assert not card.ci_initialized and not saved.get_bool('ControlsReady')
card.slc_producer_session = 'authority-card'
card.CI.update([])
sd, controls, drive = case._authority_callers(False, cp=card.CP.as_reader().as_builder(), genuine_events=True,
initial_panda_config=(int(structs.CarParams.SafetyModel.elm327), 1, 0,
bool(native.get_controls_allowed()), bool(native.safety_test_toyota_rx_summary_invalid())))
sd.aol_settings = read_settings(saved)
assert sd.aol_settings.lkas_action == AOL_TOGGLE and sd.aol_settings.main_action == 0
assert sd.CP.to_dict() == card.CP.to_dict() == controls.CP.to_dict()
assert len(card.CP.safetyConfigs) == 1 and bool(card.CP.openpilotLongitudinalControl) == shared_long
config = card.CP.safetyConfigs[0]
model, word = int(config.safetyModel.raw), int(config.safetyParam)
configured = [False]
dispatch_tree = ast.parse(textwrap.dedent(inspect.getsource(card_module.Car.step))).body[0].body
assignments = [node for node in dispatch_tree if isinstance(node, ast.Assign) and
any(isinstance(t, ast.Name) and t.id == 'initialized' for t in node.targets)]
decisions = [node for node in dispatch_tree if isinstance(node, ast.If) and ast.unparse(node.test) == 'not self.CP.passive and initialized']
assert len(assignments) == len(decisions) == 1
dispatch = compile(ast.Module(body=assignments + decisions, type_ignores=[]), 'actual-Card-step-control-dispatch', 'exec')
queued, received, previous_sd = [], {}, {}
tick = 0
next_panda_tick = [1]
patch_scope = monkeypatch.context()
local = patch_scope.__enter__()
try:
def diagnostic_query(sender, receiver, bus, addresses, requests, responses):
assert bus == 0 and addresses == [(0x7d0, None)]
assert len(requests) == 1
request = requests[0]
assert request in (b'\x10\x03', b'\x28\x83\x01')
diagnostic_requests.append(request)
def receive(timeout):
sender([(0x7d0, bytes([len(request)]) + request + bytes(7-len(request)), bus)])
return {(0x7d0, None): b'\x50\x03'} if request == b'\x10\x03' else {}
return SimpleNamespace(get_data=receive)
original_receive, original_send = card.can_callbacks
def checked_diagnostic_send(frames):
for address, data, bus in frames:
assert native.safety_tx_hook(libsafety_py.make_CANPacket(address, bus, data))
return original_send(frames)
card.can_callbacks = (original_receive, checked_diagnostic_send)
local.setattr(disable_ecu_module, 'IsoTpParallelQuery', diagnostic_query)
local.setattr(card_module, 'REPLAY', False)
local.setattr(card_module.time, 'monotonic_ns', lambda: now[0])
local.setattr(card_module.time, 'clock_gettime_ns', lambda clock: now[0])
local.setattr(card_module.messaging, 'drain_sock_raw', lambda *args, **kwargs: list(queued))
local.setattr(card.sm, 'update', lambda *args: None)
create_message = messaging.new_message
def clocked_message(service=None, size=None, **kwargs):
kwargs.setdefault('logMonoTime', now[0])
return create_message(service, size, **kwargs)
local.setattr(messaging, 'new_message', clocked_message)
send = card.pm.send
def capture(service, event):
raw = event if isinstance(event, bytes) else event.to_bytes()
received[service] = messaging.log_from_bytes(raw).as_builder()
return send(service, raw)
local.setattr(card.pm, 'send', capture)
def step(*, main=0, lkas=0, button=0, engaged=False, withdraw_request=False):
nonlocal tick
tick += 1
now[0] = source_ns = 10_000_000_000 + tick * 10_000_000
prior_axis = previous_sd.get('aolAxisState')
prior_state = previous_sd.get('selfdriveState')
request = 0 if prior_axis is None else (
int(prior_axis.aolAxisState.desiredLateral) | int(prior_axis.aolAxisState.desiredLongitudinal) << 1)
if withdraw_request:
request = 0
heartbeat = bool(request or (prior_state is not None and prior_state.selfdriveState.enabled))
if saved.get_bool('ControlsReady') and not configured[0]:
native.set_alternative_experience(card.CP.alternativeExperience)
assert native.set_safety_hooks(model, word) == 0
native.init_tests()
native.set_alternative_experience(card.CP.alternativeExperience)
configured[0] = True
native.set_timer(source_ns // 1000)
if expects_owner:
card.vehicle_startup.owner.keeper.tick()
native.set_aol_test_heartbeat(heartbeat)
native.aol_set_host_request(request)
frames = classic_frames(card.CI, tick, main=main, lkas=lkas, button=button, engaged=engaged)
for address, data, bus in frames:
assert native.safety_rx_hook(libsafety_py.make_CANPacket(address, bus, data)), (hex(address), bus)
if tick % 100 == 0:
native.safety_tick()
actual_request = native.aol_get_request_mask()
mask = native.aol_get_permission_mask()
rx_invalid = bool(native.safety_test_toyota_rx_summary_invalid())
relay_fault = bool(native.get_relay_malfunction())
assert not relay_fault
batch = messaging.log_from_bytes(card_module.can_list_to_can_capnp(frames)).as_builder()
batch.logMonoTime = source_ns
queued[:] = [batch.to_bytes()]
device = messaging.new_message('deviceState', valid=True, logMonoTime=source_ns)
device.deviceState.started, device.deviceState.startedMonoTime = True, 9_000_000_000
pandas = messaging.new_message('pandaStates', 1, valid=True, logMonoTime=source_ns)
panda = pandas.pandaStates[0]
panda.safetyModel = config.safetyModel if configured[0] else structs.CarParams.SafetyModel.elm327
panda.safetyParam = word if configured[0] else 1
panda.alternativeExperience = card.CP.alternativeExperience if configured[0] else 0
panda.controlsAllowed = native.get_controls_allowed()
panda.safetyRxChecksInvalid = rx_invalid
panda.ignitionLine = True
actual_model = model if configured[0] else int(structs.CarParams.SafetyModel.elm327)
actual_word = word if configured[0] else 1
actual_experience = card.CP.alternativeExperience if configured[0] else 0
slot = PandaSlot(0, 'authority-panda', actual_model, actual_word, actual_experience,
native.get_controls_allowed(), safetyRxChecksInvalid=rx_invalid)
wire = messaging.new_message('aolSafetyWire', 0, valid=True, logMonoTime=source_ns)
wire.aolSafetyWire = encode_safety(SafetyState(1, configured[0], source_ns, source_ns + 30_000_000,
actual_model, actual_word, bool(mask & 1), bool(mask & 2), bool(actual_request & 1), bool(actual_request & 2),
'authority-panda', card.aol_axis_session(), source_ns, (slot,)))
messages = [device.as_reader(), wire.as_reader()]
if tick >= next_panda_tick[0]:
messages.append(pandas.as_reader())
next_panda_tick[0] = tick + 10
messages += [previous_sd[name] for name in ('selfdriveState', 'aolAxisState', 'onroadEvents') if name in previous_sd]
if 'onroadEvents' not in previous_sd:
messages.append(messaging.new_message('onroadEvents', 0, valid=True, logMonoTime=source_ns).as_reader())
card.sm.update_msgs(source_ns / 1e9, messages)
state, _ = card.state_update()
if state.canValid:
assert state.cruiseState.available
for service in ('carState', 'carOutput', 'aolIntentWire', 'starpilotCarState'):
received.pop(service, None)
card.state_publish(state, None)
state_envelope = received['carState']
output_envelope = received['carOutput']
assert state_envelope.logMonoTime == source_ns and state_envelope.valid == state.canValid
assert output_envelope.logMonoTime == source_ns
axis, host, command = drive(tick, intent_message=received['aolIntentWire'],
car_state_message=state_envelope, car_output_message=output_envelope,
companion=False, authority_message=received.get('starpilotCarState'),
allowed=bool(mask & 1), long_allowed=bool(mask & 2), native_request_lat=bool(actual_request & 1),
native_request_long=bool(actual_request & 2), panda_controls_allowed=native.get_controls_allowed(),
panda_fault=rx_invalid, relay_fault=relay_fault, panda_state_message=pandas, safety_message=wire)
if sd.conditional_car_state_valid:
assert sd.aol_car_state_log_ns == state_envelope.logMonoTime
assert controls.sm.logMonoTime['carState'] == state_envelope.logMonoTime
assert controls.sm.valid['carState'] == state_envelope.valid
assert controls.sm['carState'].to_dict() == state_envelope.carState.to_dict()
assert controls.sm.logMonoTime['carOutput'] == output_envelope.logMonoTime
assert controls.sm.valid['carOutput'] == output_envelope.valid
assert controls.sm['carOutput'].to_dict() == output_envelope.carOutput.to_dict()
assert bytes(controls.sm['aolSafetyWire']) == bytes(wire.aolSafetyWire)
for consumer in (unit for unit in (card, sd, controls) if 'pandaStates' in unit.sm.services):
observed = consumer.sm['pandaStates'][0]
assert int(observed.safetyModel.raw) in (int(structs.CarParams.SafetyModel.elm327), model)
if int(observed.safetyModel.raw) == int(structs.CarParams.SafetyModel.elm327):
assert observed.safetyParam == 1 and observed.alternativeExperience == 0
else:
assert configured[0] and observed.safetyParam == word and observed.alternativeExperience == card.CP.alternativeExperience
previous_sd.update(sd.pm.messages)
validated = current_native(sd.sm, sd.CP, now_ns=source_ns, axis_session_id=sd.aol_session_id)
if validated is not None:
assert configured[0] and not rx_invalid
assert validated.lateralAllowed == bool(mask & 1)
assert validated.requestedLateral == bool(actual_request & 1)
else:
assert not axis.lateralActive and not axis.longitudinalActive
assert not command.latActive and not command.longActive
if not configured[0]:
assert validated is None
now[0] += 1
card.sm.update_msgs(now[0] / 1e9, [controls.pm.messages['carControl']])
received.pop('sendcan', None)
exec(dispatch, dict(vars(card_module), self=card, CS=state))
steering = []
if state.canValid and card.ci_initialized and not configured[0] and 'sendcan' in received:
assert not command.enabled and not command.latActive and not command.longActive
assert command.actuators.torque == 0 and command.actuators.accel == 0
for frame in received['sendcan'].sendcan:
accepted = bool(native.safety_tx_hook(libsafety_py.make_CANPacket(frame.address, frame.src, frame.dat)))
assert accepted == (frame.address == 0x7d0 and len(frame.dat) == 8)
if state.canValid and card.ci_initialized and configured[0] and 'sendcan' in received and received['sendcan'].valid:
steering = [frame for frame in received['sendcan'].sendcan
if frame.address == 0x340 and frame.src == 0]
assert steering
for frame in received['sendcan'].sendcan:
assert native.safety_tx_hook(libsafety_py.make_CANPacket(frame.address, frame.src, frame.dat))
if 'sendcan' in received and not received['sendcan'].valid:
assert len(received['sendcan'].sendcan) == 0
assert not command.enabled and not command.latActive and not command.longActive
assert command.actuators.torque == 0 and command.actuators.accel == 0
return state, axis, host, command, steering
for _ in range(120):
step()
assert card.ci_initialized and configured[0] and saved.get_bool('ControlsReady'), 'ORDINARY_CLASSIC_NATIVE_BOOTSTRAP_STALLED'
if expects_owner:
assert card.vehicle_startup.owner.handed_off
assert not card.aol_card_intent.allowed_latch
assert native.safety_config_valid()
for _ in range(2):
step(button=2)
for _ in range(20):
state, axis, host, command, _ = step(engaged=True)
assert state.cruiseState.enabled and host.enabled and native.get_controls_allowed()
assert axis.lateralActive and command.latActive
if shared_long:
assert axis.longitudinalActive and command.longActive
assert card.aol_card_intent.allowed_latch, 'FULL_ENGAGEMENT_DID_NOT_KEEP_AOL'
assert card.aol_card_intent._last_latch_edge_ns > 0
edge = card.aol_card_intent._last_latch_edge_ns
assert diagnostic_requests == [b'\x10\x03', b'\x28\x83\x01']
assert card.ci_initialized and saved.get_bool('ControlsReady')
if not expect_retention:
for _ in range(2):
step(button=4)
for _ in range(5):
state, axis, host, command, _ = step()
assert not card.aol_card_intent.allowed_latch
assert not axis.lateralActive and not command.latActive
assert not axis.longitudinalActive and not command.longActive and not host.enabled
assert not native.get_controls_allowed() and native.aol_get_permission_mask() == 0
return
cancel_observed = False
for _cancel_index in range(2):
state, axis, host, command, steering = step(main=int(incidental == 'main'), button=4 if incidental == 'cancel' else 0)
assert card.aol_card_intent.allowed_latch, (
'FAMILY_CANCEL_INTENT_DROPPED', identity, word, native.get_controls_allowed(), native.aol_get_permission_mask())
assert native.aol_get_permission_mask() & 1, 'HKG_NATIVE_LATERAL_DROPPED'
assert axis.lateralActive and command.latActive, 'HKG_JOINED_LATERAL_DROPPED'
if any(event.type == structs.CarState.ButtonEvent.Type.cancel and event.pressed for event in state.buttonEvents):
cancel_observed = True
assert not axis.longitudinalActive and not command.longActive and not host.enabled
assert cancel_observed
nonzero = False
for _ in range(20):
state, axis, host, command, steering = step()
assert card.aol_card_intent.allowed_latch, (
'FAMILY_CANCEL_INTENT_DROPPED', identity, word, native.get_controls_allowed(), native.aol_get_permission_mask())
assert card.aol_card_intent._last_latch_edge_ns == edge, 'HKG_INCIDENTAL_EDGE_RENEWED'
assert native.aol_get_permission_mask() & 1, 'HKG_NATIVE_LATERAL_DROPPED'
assert axis.lateralActive and command.latActive, 'HKG_JOINED_LATERAL_DROPPED'
assert not state.cruiseState.enabled and not host.enabled and not native.get_controls_allowed()
assert not axis.longitudinalActive and not command.longActive
nonzero |= abs(command.actuators.torque) > 0
assert steering
if shared_long:
for _ in range(2):
state, axis, host, command, _ = step(main=1)
assert card.aol_card_intent.allowed_latch and axis.lateralActive and command.latActive
assert not host.enabled and not command.longActive and not native.get_controls_allowed()
for _ in range(20):
state, axis, host, command, _ = step()
assert card.aol_card_intent.allowed_latch and axis.lateralActive and command.latActive
assert card.aol_card_intent._last_latch_edge_ns == edge
assert not host.enabled and not axis.longitudinalActive and not command.longActive
assert not native.get_controls_allowed()
assert nonzero, 'Actual Controls did not produce nonzero correction for measured steering error'
invalid = controls.pm.messages['carControl'].as_builder()
invalid.valid = False
now[0] += 1
card.sm.update_msgs(now[0] / 1e9, [invalid.as_reader()])
received.pop('sendcan', None)
card.controls_update(state, card.sm['carControl'])
assert _invalid_controls_emits_no_commands(received), 'Invalid Controls packet produced valid or nonempty Card sendcan'
assert _invalid_controls_emits_no_commands({})
empty = messaging.new_message('sendcan', 0, valid=False)
assert _invalid_controls_emits_no_commands({'sendcan': empty.as_reader()})
for valid, frame_count in ((True, 0), (False, 1), (True, 1)):
rejected = messaging.new_message('sendcan', frame_count, valid=valid)
assert not _invalid_controls_emits_no_commands({'sendcan': rejected.as_reader()})
if withdrawal == 'toggle-off':
for _ in range(2):
step(lkas=1)
for _ in range(3):
state, axis, host, command, _ = step(withdraw_request=withdrawal == 'native-request-off')
assert not axis.lateralActive and not command.latActive
assert not axis.longitudinalActive and not command.longActive and not host.enabled
if withdrawal == 'toggle-off':
assert not card.aol_card_intent.allowed_latch
assert native.aol_get_permission_mask() == 0
finally:
patch_scope.__exit__(None, None, None)
finally:
native.init_tests()
native.set_alternative_experience(0)
native.set_safety_hooks(structs.CarParams.SafetyModel.noOutput, 0)
case.doCleanups()
if card is not None:
card.vehicle_startup.close()
@@ -12,7 +12,7 @@ class TestClassicCancelDebug:
class TestClassicCancelReleaseDenied:
@pytest.mark.parametrize('word', [0xC04, 0xC06])
@pytest.mark.parametrize('word', [0xC04, 0xC05, 0xC06, 0xC44, 0xC45, 0xC46])
def test_release_does_not_admit_classic_longitudinal_or_independent_lateral(self, word):
safety = libsafety_py.libsafety
safety.init_tests()
@@ -0,0 +1,33 @@
import pytest
from opendbc.car.hyundai.classic_long_aol import CLASSIC_LONG_IDS, aol_word, independent_buttons_qualified
from openpilot.starpilot.aol.intent import AolSettings
from openpilot.starpilot.aol.tests.test_classic_long_profiles import params
from openpilot.starpilot.car.hyundai.classic_long_intent import ClassicLongCardIntent
@pytest.mark.parametrize('identity', sorted(CLASSIC_LONG_IDS, key=str))
@pytest.mark.parametrize('lda', [False, True])
@pytest.mark.parametrize('release', [False, True])
def test_each_actual_factory_preserves_marker_lda_and_release_boundary(identity, lda, release):
cp = params(identity, lda=lda, release=release)
before = cp.to_dict()
assert independent_buttons_qualified(cp, marked_only=False) == (lda and not release)
assert not independent_buttons_qualified(cp)
assert cp.to_dict() == before
cp.safetyConfigs[0].safetyParam = aol_word(cp)
cp.alternativeExperience = 32
before = cp.to_dict()
assert independent_buttons_qualified(cp) == (lda and not release)
assert cp.to_dict() == before
@pytest.mark.parametrize('identity', sorted(CLASSIC_LONG_IDS, key=str))
@pytest.mark.parametrize('lkas,main', [(9, 0), (0, 0), (0, 9), (9, 9)])
def test_each_family_profile_keeps_exact_saved_mapping(identity, lkas, main):
cp = params(identity, lda=True)
before = cp.to_dict()
settings = AolSettings(True, 0., lkas, main, (0, 0, 0), (0, 0, 0))
owner = ClassicLongCardIntent(cp, settings)
assert owner.independent_cruise_buttons == (lkas == 9 and main == 0)
assert owner.settings == settings and cp.to_dict() == before
@@ -37,13 +37,13 @@ def test_stock_lkas_owned_aol_survives_unassigned_cruise_gesture(identity, alter
assert not owner.allowed_latch
def test_classic_long_keeps_published_policy_until_full_join_is_qualified():
def test_classic_long_qualified_profile_keeps_lkas_owned_cancel_intent():
from openpilot.starpilot.aol.tests.test_classic_long_profiles import params as classic_params
from opendbc.car.hyundai.classic_long_aol import qualified
cp = classic_params(lda=True)
assert qualified(cp)
owner = create_intent(cp, AolSettings(True, 0., AOL_TOGGLE, 0, (0, 0, 0), (0, 0, 0)))
assert not owner.independent_cruise_buttons
assert owner.independent_cruise_buttons
cs = structs.CarState(canValid=True, gearShifter='drive')
cs.cruiseState.available = True
owner.update(cs, now_ns=1)
@@ -54,7 +54,7 @@ def test_classic_long_keeps_published_policy_until_full_join_is_qualified():
owner.update(cs, now_ns=3)
cs.buttonEvents = [structs.CarState.ButtonEvent(type='cancel', pressed=True)]
owner.update(cs, now_ns=4)
assert not owner.allowed_latch
assert owner.allowed_latch
@pytest.mark.parametrize('identity', sorted(STOCK_EV_CARS, key=str))
@@ -52,7 +52,7 @@ def assert_fresh_ordinary_recovery(sd, axis, command, source):
class IoniqAvailabilityTests(unittest.TestCase):
def _authority_callers(self, full_op, *, cp=None, intent_companion=None, genuine_events=False):
def _authority_callers(self, full_op, *, cp=None, intent_companion=None, genuine_events=False, initial_panda_config=None):
self.enterContext(mock.patch('openpilot.selfdrive.selfdrived.selfdrived.REPLAY', False))
from opendbc.car.gm.tests.test_bolt_volt_configurations import ordinary_params
from opendbc.car.gm.values import CAR
@@ -154,6 +154,7 @@ class IoniqAvailabilityTests(unittest.TestCase):
# that unseen state, then feed only the preceding actual published command.
previous_control_message = [None]
due = {}
controls_panda_due = [base]
def feed_event_sources(stamp, companions=(), *, car_output_valid=True):
from openpilot.cereal.services import SERVICE_LIST
names = ['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'extrinsicsCalibration',
@@ -167,7 +168,7 @@ class IoniqAvailabilityTests(unittest.TestCase):
# Ignored authority wires remain fresh on every 100 Hz source tick.
period = round(1e9 / min(frequency, 100)) if frequency > 0 else 0
if (stamp < due.get(name, 0) and name not in ('aolIntentWire', 'aolSafetyWire', 'starpilotCarState') and
not (name == 'pandaStates' and name in supplied and
not (name == 'pandaStates' and initial_panda_config is None and name in supplied and
any(p.safetyRxChecksInvalid or p.faults for p in supplied[name].pandaStates))):
continue
due[name] = stamp + period
@@ -180,8 +181,13 @@ class IoniqAvailabilityTests(unittest.TestCase):
value.started, value.startedMonoTime, value.freeSpacePercent = True, base - 1_000_000_000, 50.
elif name == 'pandaStates':
for panda, config in zip(value, cp.safetyConfigs, strict=True):
panda.safetyModel, panda.safetyParam = config.safetyModel, config.safetyParam
panda.alternativeExperience, panda.controlsAllowed = cp.alternativeExperience, full_op
if initial_panda_config is None:
panda.safetyModel, panda.safetyParam = config.safetyModel, config.safetyParam
panda.alternativeExperience, panda.controlsAllowed = cp.alternativeExperience, full_op
else:
model, word, experience, controls_allowed, rx_invalid = initial_panda_config
panda.safetyModel, panda.safetyParam, panda.alternativeExperience = model, word, experience
panda.controlsAllowed, panda.safetyRxChecksInvalid = controls_allowed, rx_invalid
elif name == 'extrinsicsCalibration':
value.calStatus, value.rpyCalib = 'calibrated', [0., 0., 0.]
elif name == 'deviceMotion':
@@ -208,7 +214,7 @@ class IoniqAvailabilityTests(unittest.TestCase):
long_allowed=True, pause_long=False, state_stamp=None, intent_message=None, offset_ns=0,
native_request_lat=True, native_request_long=None, native_valid=True, panda_fault=False, panda_controls_allowed=None,
critical_event=None, calibrated=True, car_output_valid=True, relay_fault=False,
car_state_message=None, car_output_message=None, authority_message=None):
car_state_message=None, car_output_message=None, authority_message=None, panda_state_message=None, safety_message=None):
now = base + tick * 10_000_000 + offset_ns
clock.return_value = now
if seconds is not None:
@@ -248,6 +254,8 @@ class IoniqAvailabilityTests(unittest.TestCase):
panda.safetyRxChecksInvalid = panda_fault
if relay_fault:
panda.faults = ['relayMalfunction']
if panda_state_message is not None:
pandas = panda_state_message
frames.append(pandas.as_reader())
if car_output_message is not None:
frames.append(car_output_message.as_reader())
@@ -268,6 +276,8 @@ class IoniqAvailabilityTests(unittest.TestCase):
cp.alternativeExperience, panda_controls_allowed, panda_fault, False,
1 if relay_fault else 0)
for index, config in enumerate(cp.safetyConfigs)) if genuine_events else ()))
if safety_message is not None:
native = safety_message
if authority_message is not None:
frames.append(authority_message.as_reader())
elif companion:
@@ -304,6 +314,11 @@ class IoniqAvailabilityTests(unittest.TestCase):
if car_output_message is not None:
control_frames = [sample for sample in control_frames if sample.which() != 'carOutput']
control_frames.append(car_output_message.as_reader())
if panda_state_message is not None and 'pandaStates' in controls.sm.services:
control_frames = [sample for sample in control_frames if sample.which() != 'pandaStates']
if now >= controls_panda_due[0]:
control_frames.append(panda_state_message.as_reader())
controls_panda_due[0] = now + 100_000_000
replaced = ('carState', 'aolSafetyWire', 'modelV2', 'aolAxisState', 'selfdriveState')
controls.sm.update_msgs(now / 1e9, [message for message in control_frames if message.which() not in replaced] +
[control_state.as_reader(), native.as_reader(), model.as_reader(),
@@ -0,0 +1,164 @@
from dataclasses import replace
from types import SimpleNamespace
import pytest
from openpilot.cereal import messaging
from openpilot.starpilot.aol.runtime import current_native
from openpilot.starpilot.aol.transport_pause import _newer_combined_intent_native, healthy_transport
from openpilot.starpilot.aol.tests.test_panda_inventory import NOW, bound_state, configured_cp, sm_for
from openpilot.starpilot.aol.tests.test_runtime import car_state
@pytest.mark.parametrize('case', ['older', 'equal', 'newer', 'legacy', 'slot_controls', 'native_long', 'native_lat',
'request_long', 'request_lat', 'session', 'expired', 'future', 'lease', 'native_rx', 'native_heartbeat', 'native_fault',
'native_identity', 'native_experience', 'native_invalid', 'native_lost', 'panda_rx', 'panda_heartbeat', 'panda_fault',
'panda_identity', 'panda_experience', 'panda_invalid', 'panda_lost', 'panda_unseen', 'axis_lat', 'axis_long',
'axis_request_lat', 'axis_request_long', 'axis_ack', 'brake', 'gas', 'acc', 'can', 'timeout', 'steer', 'cruise'])
def test_newer_native_retains_only_complete_current_combined_intent(case):
cp = configured_cp()
native = bound_state(cp)
source = native.sourcePandaStatesMonoTime
timestamp = source - 50_000_000 if case not in ('equal', 'newer') else source + int(case == 'newer')
cs = car_state()
cs.cruiseState.enabled = True
axis = SimpleNamespace(sessionId=native.axisSessionId, nativeAcknowledged=True,
desiredLateral=True, lateralActive=True, desiredLongitudinal=True, longitudinalActive=True)
native_changes = {'native_long': {'longitudinalAllowed': False}, 'native_lat': {'lateralAllowed': False},
'request_long': {'requestedLongitudinal': False}, 'request_lat': {'requestedLateral': False},
'future': {'observedMonoTime': NOW + 1}, 'lease': {'validUntilMonoTime': NOW + 200_000_001}}
if case in native_changes:
native = replace(native, **native_changes[case])
slot_changes = {'slot_controls': {'controlsAllowed': False}, 'native_rx': {'safetyRxChecksInvalid': True},
'native_heartbeat': {'heartbeatLost': True}, 'native_fault': {'faults': 1},
'native_identity': {'safetyParam': native.safetyParam ^ 1}, 'native_experience': {'alternativeExperience': cp.alternativeExperience ^ 1}}
if case in slot_changes:
native = replace(native, pandaInventory=(replace(native.pandaInventory[0], **slot_changes[case]),))
if case == 'legacy':
native = replace(native, pandaInventory=(), sourcePandaStatesMonoTime=0)
timestamp = NOW - 50_000_000
sm = sm_for(cp, native, panda_stamp=timestamp)
update = messaging.new_message('pandaStates', 1, valid=True, logMonoTime=timestamp)
update.pandaStates[0] = sm['pandaStates'][0]
p = update.pandaStates[0]
p.controlsAllowed = False
panda_changes = {'panda_rx': ('safetyRxChecksInvalid', True), 'panda_heartbeat': ('heartbeatLost', True),
'panda_fault': ('faults', ['relayMalfunction']), 'panda_identity': ('safetyParam', 0),
'panda_experience': ('alternativeExperience', cp.alternativeExperience ^ 1)}
if case in panda_changes:
setattr(p, *panda_changes[case])
sm.update_msgs(NOW / 1e9, [update.as_reader()])
if case in ('panda_invalid', 'panda_lost', 'panda_unseen'):
getattr(sm, {'panda_invalid': 'valid', 'panda_lost': 'alive', 'panda_unseen': 'seen'}[case])['pandaStates'] = False
if case in ('native_invalid', 'native_lost'):
getattr(sm, 'valid' if case == 'native_invalid' else 'alive')['aolSafetyWire'] = False
axis_changes = {'session': ('sessionId', 'foreign'), 'axis_lat': ('lateralActive', False),
'axis_long': ('longitudinalActive', False), 'axis_request_lat': ('desiredLateral', False),
'axis_request_long': ('desiredLongitudinal', False), 'axis_ack': ('nativeAcknowledged', False)}
if case in axis_changes:
setattr(axis, *axis_changes[case])
cs_changes = {'brake': ('brakePressed', True), 'gas': ('gasPressed', True), 'acc': ('accFaulted', True),
'can': ('canValid', False), 'timeout': ('canTimeout', True), 'steer': ('steerFaultPermanent', True)}
if case in cs_changes:
setattr(cs, *cs_changes[case])
if case == 'cruise':
cs.cruiseState.enabled = False
now = NOW + 200_000_001 if case == 'expired' else NOW
assert healthy_transport(sm, cp, cs, axis.sessionId, now) == (case == 'request_long')
before = (native, tuple(sm.logMonoTime.items()), tuple(sm.recv_time.items()))
result = _newer_combined_intent_native(sm, cp, cs, axis, now)
assert (result is not None) == (case == 'older')
assert (native, tuple(sm.logMonoTime.items()), tuple(sm.recv_time.items())) == before
if result is not None:
assert result == current_native(sm, cp, now_ns=now, axis_session_id=axis.sessionId)
assert result.observedMonoTime == NOW and result.validUntilMonoTime == NOW + 200_000_000
@pytest.mark.parametrize('case', ['older', 'equal', 'newer', 'fault', 'native_identity', 'expired', 'source_lost'])
def test_actual_gm_sd_controls_and_card_keep_current_denials(case):
from unittest.mock import patch
from opendbc.car.gm.tests.test_bolt_volt_configurations import ordinary_params
from opendbc.car.gm.values import CAR
from openpilot.common.prefix import OpenpilotPrefix
from openpilot.selfdrive.car.card import Car
from openpilot.starpilot.aol.intent import AolProcessFaultContext, AolSettings
from openpilot.starpilot.aol.tests.test_ioniq_availability import IoniqAvailabilityTests
from openpilot.starpilot.aol.transport_pause import TransportPauseFeedback
from openpilot.starpilot.aol.vehicle import create_intent, policy_for
from openpilot.starpilot.aol.wire import IntentState, encode_intent, encode_safety
fixture = IoniqAvailabilityTests(methodName='runTest')
try:
with OpenpilotPrefix():
cp = ordinary_params(CAR.CHEVROLET_VOLT_ASCM, alpha=True, sascm=True, radar=True)
sd, controls, drive = fixture._authority_callers(True, cp=cp)
assert sd.CP.brand == 'gm' and sd.CP.openpilotLongitudinalControl
card = Car.__new__(Car)
card.CP, card.slc_producer_session = sd.CP, 'authority-card'
services = ['aolAxisState', 'aolSafetyWire', 'pandaStates', 'onroadEvents']
card.sm = messaging.SubMaster(services, ignore_avg_freq=services)
card.aol_process_fault_context = AolProcessFaultContext()
card.aol_transport_feedback = TransportPauseFeedback(card.CP)
settings = AolSettings(True, 0., 9, 0, (0, 0, 0), (0, 0, 0))
card.aol_card_intent = create_intent(card.CP, settings, policy_for(card.CP))
assert card.aol_card_intent is not None
cs = car_state()
cs.cruiseState.available = cs.cruiseState.enabled = True
card.aol_card_intent.update(cs, fault_active=False, now_ns=NOW - 1, standard_enabled=True)
assert card.aol_card_intent.allowed_latch
for tick in range(3):
now = NOW + tick * 10_000_000
state = bound_state(sd.CP, now=now)
pandas = messaging.new_message('pandaStates', len(cp.safetyConfigs), valid=True,
logMonoTime=state.sourcePandaStatesMonoTime)
for panda, config in zip(pandas.pandaStates, cp.safetyConfigs, strict=True):
panda.safetyModel, panda.safetyParam = config.safetyModel, config.safetyParam
panda.alternativeExperience, panda.controlsAllowed = cp.alternativeExperience, True
if tick == 2:
pandas.logMonoTime = state.sourcePandaStatesMonoTime + (1 if case == 'newer' else 0 if case == 'equal' else -50_000_000)
pandas.pandaStates[-1].controlsAllowed = False
if case == 'fault':
pandas.pandaStates[-1].faults = ['relayMalfunction']
if case == 'source_lost':
pandas.valid = False
if case == 'native_identity':
state = replace(state, pandaInventory=(replace(state.pandaInventory[0], safetyParam=0),))
if case == 'expired':
state = replace(state, observedMonoTime=now - 200_000_001, validUntilMonoTime=now - 1)
wire = messaging.new_message('aolSafetyWire', 0, valid=True, logMonoTime=state.observedMonoTime)
wire.aolSafetyWire = encode_safety(state)
vehicle = messaging.new_message('carState', valid=True, logMonoTime=now)
vehicle.carState = cs
intent = messaging.new_message('aolIntentWire', 0, valid=True, logMonoTime=now)
intent.aolIntentWire = encode_intent(IntentState('authority-card', tick + 1, now, now,
now + 200_000_000, True, False, False, True, True))
raw_companions = [intent.as_reader()]
authority = messaging.new_message('starpilotCarState', valid=True, logMonoTime=now)
authority.starpilotCarState.sourceCarStateMonoTime = now
authority.starpilotCarState.lateralAuthorityUnavailable = False
steering_companions = [authority.as_reader()]
optional_receive = messaging.recv_one_or_none
def receive(sock, raw_companions=raw_companions, steering_companions=steering_companions, optional_receive=optional_receive):
if sock is sd.aol_intent_companion.sock:
return raw_companions.pop(0) if raw_companions else None
if sock is sd.steering_companion.sock:
return steering_companions.pop(0) if steering_companions else None
return optional_receive(sock)
with patch.object(messaging, 'recv_one_or_none', side_effect=receive):
axis, _, command = drive(tick, panda_state_message=pandas, safety_message=wire,
car_state_message=vehicle, intent_message=intent, authority_message=authority)
assert not raw_companions and not steering_companions
if case in ('native_identity', 'expired') and tick == 2:
assert not axis.lateralActive and not axis.longitudinalActive and not command.latActive and not command.longActive
else:
assert axis.lateralActive and axis.longitudinalActive and command.latActive and command.longActive
frames = [pandas.as_reader(), wire.as_reader(), sd.pm.messages['aolAxisState'], sd.pm.messages['onroadEvents']]
card.sm.update_msgs(now / 1e9, frames)
fault = card.aol_disarming_fault(cs, int(card.sm.logMonoTime['onroadEvents']), now)
card.aol_card_intent.update(cs, fault_active=fault, now_ns=now, standard_enabled=True)
assert fault == (tick == 2 and case != 'older')
assert card.aol_card_intent.allowed_latch == (tick != 2 or case == 'older')
if tick == 2 and case == 'older':
assert not healthy_transport(card.sm, cp, cs, state.axisSessionId, now)
finally:
fixture.doCleanups()
@@ -42,7 +42,7 @@ def test_hybrid_added_scope_preserves_existing_profile_vetoes(defect):
elif defect == 'passive':
cp.passive = True
elif defect == 'fingerprint':
cp.carFingerprint = CAR.HYUNDAI_IONIQ
cp.carFingerprint = CAR.HYUNDAI_IONIQ_5
elif defect == 'identity_flags':
cp.flags = int(cp.flags) & ~int(HyundaiFlags.HYBRID)
before = cp.to_dict()
@@ -64,6 +64,21 @@ def _healthy_transport_native(sm, cp, cs, session, now_ns, native=None, *, requi
return None
def _newer_combined_intent_native(sm, cp, cs, axis, now_ns):
if (not axis.nativeAcknowledged or not axis.desiredLateral or not axis.lateralActive or
not axis.desiredLongitudinal or not axis.longitudinalActive or not cs.cruiseState.enabled or
cs.brakePressed or cs.gasPressed or cs.accFaulted):
return None
native = _healthy_transport_native(sm, cp, cs, str(axis.sessionId), now_ns, require_longitudinal=False)
if (native is None or not native.pandaInventory or not native.requestedLateral or not native.lateralAllowed or
not native.requestedLongitudinal or not native.longitudinalAllowed or
not 0 < int(sm.logMonoTime['pandaStates']) < native.sourcePandaStatesMonoTime or
any(not slot.controlsAllowed for slot, config in zip(native.pandaInventory, cp.safetyConfigs, strict=True)
if str(config.safetyModel) not in ('silent', 'noOutput'))):
return None
return native
def independent_lateral_health(sm, cp, cs, session, now_ns):
"""Structural health for retaining LAT intent; never grant either output axis."""
native = _healthy_transport_native(sm, cp, cs, session, now_ns, require_longitudinal=False)
@@ -1138,6 +1153,8 @@ class TransportPauseFeedback:
0 < axis.sourceCarStateMonoTime <= stamp and
stamp - axis.sourceCarStateMonoTime <= AXIS_MAX_AGE_NS)
native = _healthy_transport_native(sm, cp, cs, str(axis.sessionId), now_ns)
if native is None and latched and current and continuity and baseline:
native = _newer_combined_intent_native(sm, cp, cs, axis, now_ns)
lateral_native = (self._native_long_withdrawal_lateral_current(sm, cp, cs, axis, now_ns,
latched=latched, producer=intent_producer_session)
if native is None and current and continuity and baseline else None)
+1 -1
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "c8754462000a4920ab6c6a8eae9399d642aa4037780642999a646ba6be576591", "schema_version": 1}
{"protected_inventory_sha256": "e826736ec828dcc70a217d8f7a60c5aab6fe2613d23f7db75ba464ce7eb18873", "schema_version": 1}
+3
View File
@@ -33,6 +33,9 @@ DEBUG_SAFETY_CLASS_TARGETS = {
RELEASE_TARGETS = (
"openpilot/starpilot/aol/tests/test_native_inventory_control_cadence.py",
"openpilot/starpilot/aol/tests/test_classic_family_button_scope.py",
"openpilot/starpilot/aol/tests/test_hkg_independent_cancel_contract.py",
"opendbc_repo/opendbc/car/hyundai/tests/test_restore_session_settle.py",
"openpilot/starpilot/aol/tests/test_sonata_hybrid_cancel_scope.py",
"opendbc_repo/opendbc/car/hyundai/tests/test_sibling_continuity.py",