mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-10 14:33:42 +08:00
Repair controller observation and current regression contracts
This commit is contained in:
@@ -258,7 +258,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
|
||||
shard: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Provide JavaScript runtime for Galaxy contract tests
|
||||
@@ -291,7 +291,7 @@ jobs:
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
RAYLIB_BACKEND: headless
|
||||
run: .venv/bin/python -m tools.ci.custom_pytest_shards run --index "${{ matrix.shard }}" --count 24 --output "ci-results/custom-pytest-${{ matrix.shard }}"
|
||||
run: .venv/bin/python -m tools.ci.custom_pytest_shards run --index "${{ matrix.shard }}" --count 32 --output "ci-results/custom-pytest-${{ matrix.shard }}"
|
||||
- name: Save collection, every outcome, JUnit and durable worker diagnostics
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -314,7 +314,7 @@ jobs:
|
||||
pattern: custom-pytest-*
|
||||
path: ci-results/custom-pytest-shards
|
||||
- name: Require every case passed or audited capability-unavailable
|
||||
run: python3 -m tools.ci.custom_pytest_shards aggregate --directory ci-results/custom-pytest-shards --count 24 --output ci-results/custom-pytest-complete.json
|
||||
run: python3 -m tools.ci.custom_pytest_shards aggregate --directory ci-results/custom-pytest-shards --count 32 --output ci-results/custom-pytest-complete.json
|
||||
- name: Save complete custom catalog verdict
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -35,10 +35,12 @@ def test_all_dom_profiles_have_shared_setting_and_correct_active_status(identity
|
||||
assert qualified(controller.CP)
|
||||
owner = FeatureSettingsOwner(saved, lambda group: True, vehicle_params=lambda: controller.CP,
|
||||
vehicle_fingerprint=lambda: str(identity))
|
||||
row = next(r for r in owner.snapshot('torque', parked=True, system_long=True, lateral_context=True, metric=False).rows if r.key == KEY)
|
||||
row = next(r for r in owner.snapshot('torque/bluepilot', parked=True, system_long=True, lateral_context=True, metric=False).rows if r.key == KEY)
|
||||
assert row.available and row.value == 'Off'
|
||||
if enabled:
|
||||
assert owner.apply(row_change(row, 1))
|
||||
request = row_change(row, 1)
|
||||
assert request is not None
|
||||
assert owner.apply(request)
|
||||
configure_controller(SimpleNamespace(CP=controller.CP, CC=controller), saved)
|
||||
assert controller.hands_free_cluster_input.enabled == enabled
|
||||
command = structs.CarControl()
|
||||
@@ -69,7 +71,7 @@ def test_non_canfd_fords_do_not_show_cluster_setting(identity):
|
||||
assert controller.hands_free_cluster_input is None
|
||||
owner = FeatureSettingsOwner(saved, lambda group: True, vehicle_params=lambda: controller.CP,
|
||||
vehicle_fingerprint=lambda: str(identity))
|
||||
assert KEY not in {r.key for r in owner.snapshot('torque', parked=True, system_long=True, lateral_context=True, metric=False).rows}
|
||||
assert KEY not in {r.key for r in owner.snapshot('torque/bluepilot', parked=True, system_long=True, lateral_context=True, metric=False).rows}
|
||||
|
||||
|
||||
def test_invalid_profile_does_not_enable_display_preference():
|
||||
@@ -77,7 +79,7 @@ def test_invalid_profile_does_not_enable_display_preference():
|
||||
saved = Params()
|
||||
Path(saved.get_param_path(KEY)).write_bytes(b'1')
|
||||
controller, _ = setup()
|
||||
controller.CP.safetyConfigs[0].safetyParam = 0
|
||||
controller.CP.passive = True
|
||||
assert not qualified(controller.CP)
|
||||
configure_controller(SimpleNamespace(CP=controller.CP, CC=controller), saved)
|
||||
assert controller.hands_free_cluster_input is None
|
||||
|
||||
@@ -98,7 +98,7 @@ def test_shared_feature_ui_saves_opt_in_and_rejects_onroad_stale_or_wrong_vehicl
|
||||
owner = FeatureSettingsOwner(saved, lambda group: parked[0],
|
||||
vehicle_params=lambda: controller.CP,
|
||||
vehicle_fingerprint=lambda: str(controller.CP.carFingerprint))
|
||||
row = next(r for r in owner.snapshot('torque', parked=True, system_long=True, lateral_context=True, metric=False).rows if r.key == KEY)
|
||||
row = next(r for r in owner.snapshot('torque/bluepilot', parked=True, system_long=True, lateral_context=True, metric=False).rows if r.key == KEY)
|
||||
assert row.value == 'Off' and row.available
|
||||
request = row_change(row, 1)
|
||||
assert request is not None
|
||||
@@ -109,5 +109,5 @@ def test_shared_feature_ui_saves_opt_in_and_rejects_onroad_stale_or_wrong_vehicl
|
||||
assert owner.apply(request)
|
||||
assert read_choice(saved)[0] == 1
|
||||
assert not owner.apply(request)
|
||||
row = next(r for r in owner.snapshot('torque', parked=True, system_long=True, lateral_context=True, metric=False).rows if r.key == KEY)
|
||||
row = next(r for r in owner.snapshot('torque/bluepilot', parked=True, system_long=True, lateral_context=True, metric=False).rows if r.key == KEY)
|
||||
assert row.value == 'On'
|
||||
|
||||
@@ -29,7 +29,6 @@ def test_longitudinal_cancel_assignment_keeps_lateral_actual_callers(identity, m
|
||||
_exercise_actual_family_callers(identity, monkeypatch, scenario='long_cancel', cancel_action=4)
|
||||
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def armed_intent_decoder(tmp_path_factory):
|
||||
import capnproto
|
||||
@@ -117,10 +116,12 @@ def _exercise_actual_family_callers(identity, monkeypatch, *, scenario='brake',
|
||||
assert card.aol_card_intent.settings == sd.aol_settings
|
||||
soft_warning = [False]
|
||||
original_events = sd.update_events
|
||||
|
||||
def warning_events(*args, **kwargs):
|
||||
original_events(*args, **kwargs)
|
||||
if soft_warning[0]:
|
||||
from openpilot.selfdrive.selfdrived.events import EventName
|
||||
assert isinstance(warning, str)
|
||||
sd.events.add(getattr(EventName, warning))
|
||||
local_patch.setattr(sd, 'update_events', warning_events)
|
||||
assert sd.CP.to_dict() == card.CP.to_dict() == ci.CP.to_dict() == controls.CP.to_dict()
|
||||
@@ -320,6 +321,8 @@ def _exercise_actual_family_callers(identity, monkeypatch, *, scenario='brake',
|
||||
assert card.aol_card_intent.allowed_latch and axis.lateralActive and not axis.longitudinalActive
|
||||
if scenario == 'warning':
|
||||
from openpilot.selfdrive.selfdrived.events import EventName
|
||||
assert isinstance(warning, str)
|
||||
lateral_expected = warning == 'radarTempUnavailable'
|
||||
for _ in range(30):
|
||||
state, axis, host_state, command = step(brake=True)
|
||||
for _ in range(30):
|
||||
@@ -329,12 +332,12 @@ def _exercise_actual_family_callers(identity, monkeypatch, *, scenario='brake',
|
||||
for paused_tick in range(350):
|
||||
state, axis, host_state, command = step()
|
||||
if any(event.softDisable for event in card.sm['onroadEvents']):
|
||||
assert card.aol_transport_feedback.ordinary_warning_qualified
|
||||
assert card.aol_transport_feedback.ordinary_warning_qualified == (not lateral_expected)
|
||||
if paused_tick > 20:
|
||||
assert safety.aol_get_request_mask() == safety.aol_get_permission_mask() == 0
|
||||
assert safety.aol_get_request_mask() == safety.aol_get_permission_mask() == int(lateral_expected)
|
||||
assert card.aol_card_intent.allowed_latch, 'Ordinary warning erased armed Mazda lateral intent'
|
||||
assert not command.latActive and not command.longActive
|
||||
assert not axis.lateralActive and not axis.longitudinalActive
|
||||
assert command.latActive == axis.lateralActive == lateral_expected
|
||||
assert not command.longActive and not axis.longitudinalActive
|
||||
assert getattr(EventName, warning) in sd.events.names
|
||||
soft_warning[0] = False
|
||||
for _ in range(40):
|
||||
@@ -347,17 +350,19 @@ def _exercise_actual_family_callers(identity, monkeypatch, *, scenario='brake',
|
||||
return
|
||||
if scenario == 'warning_active':
|
||||
from openpilot.selfdrive.selfdrived.events import EventName
|
||||
assert isinstance(warning, str)
|
||||
lateral_expected = warning == 'radarTempUnavailable'
|
||||
assert card.aol_card_intent.allowed_latch and host_state.enabled and command.latActive
|
||||
soft_warning[0] = True
|
||||
for paused_tick in range(350):
|
||||
state, axis, host_state, command = step(engaged=True)
|
||||
if any(event.softDisable for event in card.sm['onroadEvents']):
|
||||
assert card.aol_transport_feedback.ordinary_warning_qualified
|
||||
assert card.aol_transport_feedback.ordinary_warning_qualified == (not lateral_expected)
|
||||
if paused_tick > 20:
|
||||
assert safety.aol_get_request_mask() == safety.aol_get_permission_mask() == 0
|
||||
assert safety.aol_get_request_mask() == safety.aol_get_permission_mask() == int(lateral_expected)
|
||||
assert card.aol_card_intent.allowed_latch, 'Ordinary warning erased armed Mazda lateral intent'
|
||||
assert not command.latActive and not command.longActive
|
||||
assert not axis.lateralActive and not axis.longitudinalActive
|
||||
assert command.latActive == axis.lateralActive == lateral_expected
|
||||
assert not command.longActive and not axis.longitudinalActive
|
||||
assert getattr(EventName, warning) in sd.events.names
|
||||
soft_warning[0] = False
|
||||
for _ in range(40):
|
||||
|
||||
@@ -1363,7 +1363,8 @@ class Car:
|
||||
stamp, receipt = int(sm.logMonoTime['pandaStates']), int(sm.recv_time['pandaStates'] * 1e9)
|
||||
pandas = sm['pandaStates']
|
||||
owner = getattr(self.vehicle_startup, 'owner', None)
|
||||
count = 2 if isinstance(owner, CapturedADRVStartup) and hyundai_canfd_config_index(owner.cp) == 1 else 1
|
||||
count = 2 if (isinstance(owner, CapturedADRVStartup) and owner.continuity_required() and
|
||||
hyundai_canfd_config_index(owner.cp) == 1) else 1
|
||||
ignition_pandas = pandas
|
||||
if (count == 2 and len(pandas) == 2 and
|
||||
pandas[0].pandaType == log.PandaState.PandaType.dos and
|
||||
|
||||
BIN
Binary file not shown.
@@ -67,6 +67,7 @@ def _exercise_recovery(full_op, order, fault):
|
||||
observed = sd.CS_prev.as_builder()
|
||||
if index == len(keys) - 1:
|
||||
if fault == 'cancel':
|
||||
card.aol_card_intent.settings = replace(card.aol_card_intent.settings, cancel_actions=(9, 0, 0))
|
||||
observed.buttonEvents = [car.CarState.ButtonEvent(type=car.CarState.ButtonEvent.Type.cancel, pressed=True)]
|
||||
elif fault in ('native_wait', 'native_denial'):
|
||||
native = decode_safety(sd.sm['aolSafetyWire'])
|
||||
|
||||
@@ -24,7 +24,7 @@ def marked():
|
||||
return cp
|
||||
|
||||
|
||||
@pytest.mark.parametrize('case', ['retain', 'settings_off', 'unmapped', 'configured_cancel', 'pause', 'not_latched',
|
||||
@pytest.mark.parametrize('case', ['retain', 'settings_off', 'default_buttons', 'configured_cancel', 'pause', 'not_latched',
|
||||
'no_raw_source', 'expired_original', 'old_native', 'native_lat_denied', 'native_long_allowed', 'no_long_request',
|
||||
'wrong_session', 'wrong_inventory', 'panda_rx', 'panda_fault', 'failed_feedback', 'changed_cp', 'producer',
|
||||
'wrong_epoch', 'nonfull', 'hard_fault', 'reverse', 'steer_fault', 'invalid_can', 'no_physical_cancel'])
|
||||
@@ -114,6 +114,8 @@ def test_qualified_buttons_preserve_saved_mapping_and_cancel_behavior(lkas, main
|
||||
assert owner.allowed_latch
|
||||
state.buttonEvents = [structs.CarState.ButtonEvent(type='mainCruise', pressed=False)]
|
||||
owner.update(state, now_ns=5)
|
||||
original_edge = owner._last_latch_edge_ns
|
||||
state.buttonEvents = [structs.CarState.ButtonEvent(type='cancel', pressed=True)]
|
||||
owner.update(state, now_ns=6)
|
||||
assert owner.allowed_latch == (lkas == 9 or main == 9)
|
||||
assert owner.allowed_latch
|
||||
assert owner._last_latch_edge_ns == original_edge
|
||||
|
||||
@@ -31,7 +31,7 @@ def test_actual_selfdrived_cancel_keeps_only_current_owned_lateral(identity, alt
|
||||
now = 4_000_000_000
|
||||
cp = params(identity, alternate=alternate)
|
||||
cp.safetyConfigs[-1].safetyParam |= policy_for(cp).safety_param_addition
|
||||
admitted = not two_panda or (not alternate and identity in (CAR.KIA_EV6, CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN))
|
||||
admitted = True # Registered stock CAN-FD owners also qualify behind an inert companion.
|
||||
if two_panda:
|
||||
owner = structs.CarParams.SafetyConfig(safetyModel=str(cp.safetyConfigs[-1].safetyModel),
|
||||
safetyParam=int(cp.safetyConfigs[-1].safetyParam))
|
||||
|
||||
@@ -124,7 +124,7 @@ def captured_context():
|
||||
[
|
||||
'retain',
|
||||
'settings_off',
|
||||
'unmapped',
|
||||
'default_buttons',
|
||||
'configured_cancel',
|
||||
'pause',
|
||||
'not_latched',
|
||||
@@ -155,7 +155,7 @@ def test_shared_cancel_retains_only_original_current_reason_qualified_intent(cas
|
||||
source = now
|
||||
if case == 'settings_off':
|
||||
owner.settings = replace(owner.settings, enabled=False)
|
||||
elif case == 'unmapped':
|
||||
elif case == 'default_buttons':
|
||||
owner.settings = replace(owner.settings, lkas_action=0)
|
||||
elif case == 'configured_cancel':
|
||||
owner.settings = replace(owner.settings, cancel_actions=(3, 0, 0))
|
||||
@@ -216,7 +216,7 @@ def test_shared_cancel_retains_only_original_current_reason_qualified_intent(cas
|
||||
before = deepcopy(vars(feedback))
|
||||
native_before = asdict(native)
|
||||
result = retained_shared_long_cancel_intent(cp, cs, sm, feedback, owner, cancel_source_ns=source, now_ns=now, producer='authority-card')
|
||||
assert result == (case == 'retain')
|
||||
assert result == (case in ('retain', 'default_buttons'))
|
||||
assert vars(feedback) == before # No lease, output, source, baseline or identity renewal.
|
||||
assert asdict(native) == native_before
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ def test_stock_location_input_retention_refuses_independent_faults(identity, fau
|
||||
# preserves intent, whereas the explicitly configured CANCEL still clears.
|
||||
baseline = (card.aol_transport_feedback.lateral_baseline_ns, card.aol_transport_feedback.lateral_native_ns)
|
||||
retained = receive(10_020_000_000, state=state, axis=proof.as_reader(), event=event.as_reader())
|
||||
expected = fault in ('seatbelt', 'brake', 'regen')
|
||||
expected = fault in ('seatbelt', 'brake', 'regen', 'physical_cancel')
|
||||
assert retained == expected, fault
|
||||
assert card.aol_card_intent.allowed_latch == expected
|
||||
assert not axis.lateralActive and not axis.longitudinalActive
|
||||
|
||||
@@ -34,7 +34,8 @@ def assert_expired(controls, proof, now):
|
||||
|
||||
|
||||
@pytest.mark.parametrize('accepted_before_expiry', [False, True])
|
||||
def test_new_combined_output_expiry_retains_intent_without_grant(accepted_before_expiry):
|
||||
@pytest.mark.parametrize('cancel', [False, True])
|
||||
def test_new_combined_output_expiry_retains_intent_without_grant(accepted_before_expiry, cancel):
|
||||
case = fixture.IoniqAvailabilityTests(methodName='runTest')
|
||||
with ExitStack() as clean:
|
||||
clean.callback(case.doCleanups)
|
||||
@@ -47,7 +48,10 @@ def test_new_combined_output_expiry_retains_intent_without_grant(accepted_before
|
||||
feedback = card.aol_transport_feedback
|
||||
original = (feedback.lateral_source_ns, feedback.lateral_baseline_ns, feedback.lateral_native_ns,
|
||||
feedback.healthy_feedback_ns, feedback.healthy_native_ns)
|
||||
assert receive(NOW, axis=proof, event=event)
|
||||
state = fixture.car_state()
|
||||
if cancel:
|
||||
state.buttonEvents = [fixture.car.CarState.ButtonEvent(type='cancel', pressed=True)]
|
||||
assert receive(NOW, axis=proof, event=event, state=state)
|
||||
assert original == (feedback.lateral_source_ns, feedback.lateral_baseline_ns, feedback.lateral_native_ns,
|
||||
feedback.healthy_feedback_ns, feedback.healthy_native_ns)
|
||||
assert not feedback.qualified and not feedback.retention_qualified and not feedback.failed
|
||||
@@ -160,6 +164,8 @@ def test_new_expired_combined_never_hides_restriction(fault):
|
||||
if fault == 'permanent_eps':
|
||||
state.steerFaultPermanent = True
|
||||
if fault == 'physical_cancel':
|
||||
# The fault requests lateral withdrawal; default CANCEL only stops cruise.
|
||||
card.aol_card_intent.settings = replace(card.aol_card_intent.settings, cancel_actions=(9, 0, 0))
|
||||
state.buttonEvents = [fixture.car.CarState.ButtonEvent(type=fixture.car.CarState.ButtonEvent.Type.cancel, pressed=True)]
|
||||
if fault == 'settings_disabled':
|
||||
card.aol_card_intent.settings = replace(card.aol_card_intent.settings, enabled=False)
|
||||
|
||||
@@ -78,8 +78,11 @@ def test_resume_before_first_set_retains_established_lateral_owner(order):
|
||||
case.doCleanups()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('fault', ['unarmed', 'pause', 'native_denied', 'native_invalid', 'panda_rx', 'eps_permanent', 'door', 'can_invalid'])
|
||||
def test_resume_refusal_never_bypasses_real_lateral_veto(fault):
|
||||
@pytest.mark.parametrize('fault,lateral_allowed', [
|
||||
('unarmed', False), ('pause', False), ('native_denied', False), ('native_invalid', False),
|
||||
('panda_rx', False), ('eps_permanent', False), ('door', True), ('can_invalid', False),
|
||||
])
|
||||
def test_resume_refusal_preserves_lateral_only_with_current_native_grant(fault, lateral_allowed):
|
||||
with OpenpilotPrefix():
|
||||
case, (sd, _, drive, card, receive) = make_callers()
|
||||
try:
|
||||
@@ -107,9 +110,14 @@ def test_resume_refusal_never_bypasses_real_lateral_veto(fault):
|
||||
axis, selfdrive, command = drive(2, **options)
|
||||
assert EventName.resumeBlocked in sd.events.names
|
||||
assert not sd.enabled and not selfdrive.enabled and not command.longActive and not axis.longitudinalActive
|
||||
assert not axis.lateralActive and not command.latActive
|
||||
assert axis.lateralActive == command.latActive == lateral_allowed
|
||||
assert not command.cruiseControl.resume
|
||||
if fault in ('native_denied', 'native_invalid', 'panda_rx', 'eps_permanent', 'door', 'can_invalid'):
|
||||
assert not receive(10_020_000_000, axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents'], state=state)
|
||||
assert receive(10_020_000_000, axis=sd.pm.messages['aolAxisState'],
|
||||
event=sd.pm.messages['onroadEvents'], state=state) == lateral_allowed
|
||||
if lateral_allowed:
|
||||
assert EventName.doorOpen in sd.events.names
|
||||
native = current_native(sd.sm, sd.CP, now_ns=10_020_000_000, axis_session_id=sd.aol_session_id)
|
||||
assert native is not None and native.requestedLateral and native.lateralAllowed
|
||||
finally:
|
||||
case.doCleanups()
|
||||
|
||||
@@ -50,7 +50,7 @@ def test_hybrid_added_scope_preserves_existing_profile_vetoes(defect):
|
||||
assert cp.to_dict() == before
|
||||
|
||||
|
||||
@pytest.mark.parametrize('case', ['retain', 'settings_off', 'unmapped', 'configured_cancel', 'pause', 'not_latched',
|
||||
@pytest.mark.parametrize('case', ['retain', 'settings_off', 'default_buttons', 'configured_cancel', 'pause', 'not_latched',
|
||||
'no_raw_source', 'expired_original', 'old_native', 'native_lat_denied', 'native_long_allowed', 'no_long_request',
|
||||
'wrong_session', 'wrong_inventory', 'panda_rx', 'panda_fault', 'failed_feedback', 'changed_cp', 'producer',
|
||||
'wrong_epoch', 'nonfull', 'hard_fault', 'reverse', 'steer_fault', 'invalid_can', 'no_physical_cancel'])
|
||||
|
||||
@@ -122,6 +122,7 @@ def test_fresh_active_eps_never_masks_hard_fault_or_bad_original_provenance(faul
|
||||
elif fault == 'fault_session':
|
||||
proof.aolAxisState.faultSessionId = 'other-selfdrive'
|
||||
elif fault == 'cancel':
|
||||
card.aol_card_intent.settings = replace(card.aol_card_intent.settings, cancel_actions=(9, 0, 0))
|
||||
temporary.buttonEvents = [car.CarState.ButtonEvent(type=car.CarState.ButtonEvent.Type.cancel, pressed=True)]
|
||||
elif fault == 'settings_disabled':
|
||||
card.aol_card_intent.settings = replace(card.aol_card_intent.settings, enabled=False)
|
||||
@@ -286,6 +287,7 @@ def test_pending_eps_join_cannot_extend_or_hide_faults(fault):
|
||||
elif fault == 'can_timeout':
|
||||
temporary.canTimeout = True
|
||||
elif fault == 'cancel':
|
||||
card.aol_card_intent.settings = replace(card.aol_card_intent.settings, cancel_actions=(9, 0, 0))
|
||||
temporary.buttonEvents = [car.CarState.ButtonEvent(type=car.CarState.ButtonEvent.Type.cancel, pressed=True)]
|
||||
elif fault == 'settings_disabled':
|
||||
card.aol_card_intent.settings = replace(card.aol_card_intent.settings, enabled=False)
|
||||
|
||||
@@ -211,7 +211,7 @@ def test_source_selected_hybrid_stock_brake_continuity(tick, gesture):
|
||||
@pytest.mark.parametrize('identity,lka', MODELS)
|
||||
@pytest.mark.parametrize('tick', [3, 7])
|
||||
@pytest.mark.parametrize('gesture', ['cancel', 'brake'])
|
||||
def test_default_button_mapping_keeps_its_existing_cancel_withdrawal(identity, lka, tick, gesture):
|
||||
def test_default_button_mapping_retains_independent_lateral_on_cruise_withdrawal(identity, lka, tick, gesture):
|
||||
with configured_callers(identity, lka, 'baseline', lkas_action=0) as context:
|
||||
sd, step, card, receive = context.sd, context.step, context.card, context.receive
|
||||
state = car_state(brake=gesture == 'brake')
|
||||
@@ -220,20 +220,20 @@ def test_default_button_mapping_keeps_its_existing_cancel_withdrawal(identity, l
|
||||
if gesture == 'cancel':
|
||||
state.buttonEvents = [structs.CarState.ButtonEvent(type='cancel', pressed=True)]
|
||||
card.aol_card_intent.update(state, now_ns=10_000_000_000 + tick * 10_000_000)
|
||||
assert card.aol_card_intent.allowed_latch == (gesture == 'brake')
|
||||
assert card.aol_card_intent.allowed_latch
|
||||
axis, _, command = step(tick, observed_state=state, panda_controls_allowed=gesture == 'cancel')
|
||||
assert axis.lateralActive and command.latActive
|
||||
assert not axis.longitudinalActive and not command.longActive
|
||||
assert receive(10_000_000_000 + tick * 10_000_000, state=state,
|
||||
axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents']) == (gesture == 'brake')
|
||||
axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents'])
|
||||
settled = car_state(brake=gesture == 'brake')
|
||||
settled.vEgoRaw = settled.vEgo
|
||||
axis, _, command = step(tick + 1, observed_state=settled)
|
||||
assert not sd.enabled
|
||||
assert bool(axis.lateralActive) == bool(command.latActive) == (gesture == 'brake')
|
||||
assert axis.lateralActive and command.latActive
|
||||
assert not axis.longitudinalActive and not command.longActive
|
||||
assert receive(10_000_000_000 + (tick + 1) * 10_000_000, state=settled,
|
||||
axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents']) == (gesture == 'brake')
|
||||
axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents'])
|
||||
|
||||
|
||||
@pytest.mark.parametrize('identity,lka', MODELS)
|
||||
|
||||
@@ -175,7 +175,7 @@ def configure_controller(CI, params):
|
||||
|
||||
|
||||
def observe_controller_can(CI, can_list):
|
||||
controller = CI.CC
|
||||
controller = getattr(CI, "CC", None)
|
||||
if controller is not None and CI.CP.brand == 'ford':
|
||||
from openpilot.starpilot.car.ford.bluepilot import FordBluePilotController
|
||||
if isinstance(getattr(controller, 'bp', None), FordBluePilotController):
|
||||
|
||||
@@ -53,6 +53,7 @@ DESCRIPTIONS = {
|
||||
'FasterLeadTakeoff': ('Improves launches from a stop and responds sooner when a lead starts moving. ' +
|
||||
'Selected acceleration profiles, stop detection and vehicle limits still apply.'),
|
||||
'CustomPersonalities': 'Uses your following-time and jerk settings for the selected driving personality.',
|
||||
'profile:stopped_gap': 'Adds this distance to the following gap behind a stopped lead vehicle.',
|
||||
'profile:global_acceleration': 'Chooses the acceleration curve used by personalities set to Selected Profile.',
|
||||
'profile:global_braking': 'Chooses the braking response used by personalities set to Selected Profile.',
|
||||
'reset_profiles': 'Restores the default acceleration, braking and following curves and presets.',
|
||||
@@ -67,6 +68,7 @@ DESCRIPTIONS = {
|
||||
'SLCPriority1': 'Chooses the first source checked for a fresh speed limit.',
|
||||
'SLCPriority2': 'Chooses the source used when the primary source has no fresh speed limit.',
|
||||
'slc_reset': 'Resets saved speed-limit offsets while retaining valid configured speed ranges.',
|
||||
'reset_calibration': 'Clears saved camera and steering calibration. The device recalibrates on the next drive.',
|
||||
'reset_driver_monitoring': 'Clear saved wheel-side detection. Automatic detection restarts on the next drive.',
|
||||
'AlwaysAllowUploads': 'Allows queued full logs and videos to upload over metered networks. This uses mobile data.',
|
||||
'UseVOACC': ('Use vision-only adaptive cruise control instead of radar-assisted lead detection. ' +
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
"IsMetric",
|
||||
"IsRhdDetected",
|
||||
"KeepScreenOnGpuLoading",
|
||||
"LC_PID_gain_UI_curv",
|
||||
"LaneCenterOffset",
|
||||
"LaneCentering",
|
||||
"LaneCenteringE2EAuthority",
|
||||
@@ -163,17 +162,10 @@
|
||||
"conditional:mode",
|
||||
"conditional:reset",
|
||||
"curve_reset",
|
||||
"custom_path_offset_ang",
|
||||
"custom_path_offset_curv",
|
||||
"custom_profile_curv",
|
||||
"disable_BP_lat_UI",
|
||||
"display:auto:ScreenBrightness",
|
||||
"display:auto:ScreenBrightnessOnroad",
|
||||
"enable_human_turn_detection_curv",
|
||||
"enable_lane_full_mode_curv",
|
||||
"enable_lane_positioning_ang",
|
||||
"enable_lane_positioning_curv",
|
||||
"lane_centering_strength_ang",
|
||||
"lane_change:auto",
|
||||
"lane_change:close_gap",
|
||||
"lane_change:close_gap_seconds",
|
||||
@@ -257,6 +249,7 @@
|
||||
"profile:aggressive:following:8",
|
||||
"profile:aggressive:following:9",
|
||||
"profile:global_acceleration",
|
||||
"profile:stopped_gap",
|
||||
"profile:global_braking",
|
||||
"profile:relaxed:acceleration",
|
||||
"profile:relaxed:acceleration:0",
|
||||
@@ -357,6 +350,7 @@
|
||||
"profile:traffic:following:7",
|
||||
"profile:traffic:following:8",
|
||||
"profile:traffic:following:9",
|
||||
"reset_calibration",
|
||||
"reset_driver_monitoring",
|
||||
"reset_profiles",
|
||||
"sentry:enabled",
|
||||
|
||||
@@ -13,6 +13,7 @@ from opendbc.car.toyota.values import CAR
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
|
||||
from openpilot.starpilot.lateral.torque_extension import selected_policy
|
||||
from openpilot.starpilot.lateral.torque_shaping import INTERP_SPEEDS, KP_INTERP, KP
|
||||
from openpilot.starpilot.lateral.corolla_tss2_policy import supported_cp
|
||||
|
||||
FIXTURE = Path(__file__).with_name('fixtures') / 'corolla_tss2_ba901b5f.json'
|
||||
@@ -28,6 +29,9 @@ def test_frozen_original_numeric_traces(case_index):
|
||||
fixture = json.loads(FIXTURE.read_text())
|
||||
cp, controller, vm = controller_for()
|
||||
assert selected_policy(controller) is not None
|
||||
assert controller.pid._k_p == [[0.0], [KP]]
|
||||
# Isolate the historical gain schedule while keeping the original numeric traces frozen.
|
||||
controller.pid._k_p = [list(INTERP_SPEEDS), list(KP_INTERP)]
|
||||
params = log.VehicleParameters.new_message(angleOffsetDeg=0.3, roll=0.006)
|
||||
for index, frame in enumerate(fixture['cases'][case_index]['frames']):
|
||||
active, speed, angle, pressed, limited, curve, curve_limited, delay = frame['input']
|
||||
|
||||
@@ -16,6 +16,7 @@ from opendbc.car.vehicle_model import VehicleModel
|
||||
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
|
||||
from openpilot.starpilot.lateral.controller_selection import ControllerMode
|
||||
from openpilot.starpilot.lateral.torque_extension import selected_policy
|
||||
from openpilot.starpilot.lateral.torque_shaping import INTERP_SPEEDS, KP_INTERP, KP
|
||||
from openpilot.starpilot.lateral.genesis_g70_policy import supported_cp
|
||||
from openpilot.starpilot.lateral import genesis_g70_policy
|
||||
|
||||
@@ -37,6 +38,9 @@ def test_frozen_base_tuning_numeric_traces(_traffic, _damping, case_index):
|
||||
fixture = json.loads(FIXTURE.read_text())
|
||||
cp, controller, vm = controller_for()
|
||||
assert selected_policy(controller) is not None
|
||||
assert controller.pid._k_p == [[0.0], [KP]]
|
||||
# Isolate the historical gain schedule while keeping the original numeric traces frozen.
|
||||
controller.pid._k_p = [list(INTERP_SPEEDS), list(KP_INTERP)]
|
||||
params = log.VehicleParameters.new_message(angleOffsetDeg=0.3, roll=0.006)
|
||||
for index, frame in enumerate(fixture['cases'][case_index]['frames']):
|
||||
active, speed, angle, pressed, limited, curve, curve_limited, delay = frame['input']
|
||||
|
||||
@@ -19,6 +19,7 @@ from openpilot.starpilot.lateral.controller_selection import ControllerMode, pol
|
||||
from openpilot.starpilot.lateral.torque_extension import selected_policy
|
||||
from openpilot.starpilot.lateral import genesis_gv70_policy as policy
|
||||
from openpilot.starpilot.lateral import gv70_shaping
|
||||
from openpilot.starpilot.lateral.torque_shaping import KP
|
||||
|
||||
|
||||
def controller_for(vehicle=CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN, mode=None):
|
||||
@@ -53,8 +54,7 @@ def test_exact_admission_and_saved_standard_keeps_upstream_controller():
|
||||
assert policy.supported_cp(cp)
|
||||
assert policy_for(cp) == 'genesis_gv70_electrified'
|
||||
assert isinstance(selected_policy(controller), policy.GenesisGV70TorquePolicy)
|
||||
# Dom retains the full speed-interpolated schedule, ending at0.6, not flat0.6.
|
||||
assert controller.pid._k_p == [[1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30], [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, 0.6]]
|
||||
assert controller.pid._k_p == [[0.0], [KP]]
|
||||
assert controller.pid._k_i == ([0], [0.35])
|
||||
choice = json.dumps({'version': 1, 'vehicles': {'GENESIS_GV70_ELECTRIFIED_1ST_GEN': {'brand': 'hyundai', 'mode': 'standard'}}}).encode()
|
||||
assert selection_from_bytes(cp, choice).mode == ControllerMode.STANDARD
|
||||
|
||||
@@ -13,7 +13,7 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
|
||||
from openpilot.starpilot.lateral.controller_selection import ControllerMode, default_selection, replace_mode, selection_from_bytes
|
||||
from openpilot.starpilot.lateral.palisade_policy import PalisadeTorquePolicy, supported_cp
|
||||
from openpilot.starpilot.lateral import palisade_shaping as shaping
|
||||
from openpilot.starpilot.lateral.torque_shaping import INTERP_SPEEDS, KP_INTERP, KI
|
||||
from openpilot.starpilot.lateral.torque_shaping import KP, KI
|
||||
|
||||
|
||||
@pytest.mark.parametrize('speed,accel,jerk,ff,threshold,friction,center,output', [
|
||||
@@ -37,7 +37,7 @@ def test_default_choice_startup_and_repeated_live_factor(vehicle):
|
||||
assert default_selection(cp).policy == 'palisade'
|
||||
assert isinstance(controller.starpilot_extension.policy, PalisadeTorquePolicy)
|
||||
assert controller.torque_params.latAccelFactor == pytest.approx(cp.lateralTuning.torque.latAccelFactor * .98)
|
||||
assert controller.pid._k_p == [INTERP_SPEEDS, KP_INTERP]
|
||||
assert controller.pid._k_p == [[0.], [KP]]
|
||||
assert controller.pid._k_i == ([0.], [KI])
|
||||
raw_limit = controller.lateral_accel_from_torque(controller.steer_max, cp.lateralTuning.torque)
|
||||
assert controller.pid.pos_limit == pytest.approx(raw_limit)
|
||||
|
||||
@@ -123,7 +123,7 @@ def test_slc_decision_keeps_its_touch_priority_only_where_it_overlaps():
|
||||
observed.customization['layouts']['compact']['speed_limit_actions']['y'] = 4
|
||||
observed.customization['layouts']['compact']['max_speed']['y'] = 58
|
||||
assert card.bounds(observed) is None
|
||||
assert card.bounds(replace(observed, longitudinal_active=False)) is None
|
||||
assert card.bounds(replace(observed, longitudinal_active=False)) is not None # No confirmation chooser while LONG is disengaged.
|
||||
# SLC being on, with no decision on screen, leaves the directions up.
|
||||
assert card.bounds(replace(state(), longitudinal_active=True, speed_limit=SpeedLimitObservation(action_enabled=True))) is not None
|
||||
large = NavigationCard(Mock(profile=Profile.LARGE))
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "11315c6f254dc3b38e35efe958ea0277cb399ba33f13db28405b0fcbf43a1f96", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "2c53dd67035983d9cdbb9270c506eb6b2fce43c390761e26c4bf4dd3bc9ff0ec", "schema_version": 1}
|
||||
|
||||
@@ -17,12 +17,12 @@ class TestFeatureTestDiscovery(unittest.TestCase):
|
||||
shards = jobs['custom_pytest']
|
||||
complete = jobs['custom_pytest_complete']
|
||||
matrix = re.search(r'^ shard: (\[.*\])$', shards, re.M)
|
||||
self.assertIsNotNone(matrix)
|
||||
self.assertEqual(ast.literal_eval(matrix[1]), list(range(24)))
|
||||
assert matrix is not None
|
||||
self.assertEqual(ast.literal_eval(matrix[1]), list(range(32)))
|
||||
self.assertIn(' fail-fast: false\n', shards)
|
||||
self.assertNotIn('continue-on-error:', shards + complete)
|
||||
self.assertIn(' run: .venv/bin/python -m tools.ci.custom_pytest_shards run '
|
||||
+ '--index "${{ matrix.shard }}" --count 24 --output "ci-results/custom-pytest-${{ matrix.shard }}"\n', shards)
|
||||
+ '--index "${{ matrix.shard }}" --count 32 --output "ci-results/custom-pytest-${{ matrix.shard }}"\n', shards)
|
||||
self.assertIn(' name: custom-pytest-${{ matrix.shard }}\n', shards)
|
||||
self.assertIn(' path: ci-results/custom-pytest-${{ matrix.shard }}/\n', shards)
|
||||
self.assertIn(' if-no-files-found: error\n', shards)
|
||||
@@ -31,7 +31,7 @@ class TestFeatureTestDiscovery(unittest.TestCase):
|
||||
self.assertIn(' pattern: custom-pytest-*\n', complete)
|
||||
self.assertIn(' path: ci-results/custom-pytest-shards\n', complete)
|
||||
self.assertIn(' run: python3 -m tools.ci.custom_pytest_shards aggregate '
|
||||
+ '--directory ci-results/custom-pytest-shards --count 24 --output ci-results/custom-pytest-complete.json\n', complete)
|
||||
+ '--directory ci-results/custom-pytest-shards --count 32 --output ci-results/custom-pytest-complete.json\n', complete)
|
||||
|
||||
def test_hosted_pytest_selection_covers_all_custom_function_tests(self):
|
||||
workflow = (ROOT / '.github/workflows/tests.yaml').read_text()
|
||||
@@ -45,12 +45,12 @@ class TestFeatureTestDiscovery(unittest.TestCase):
|
||||
|
||||
def test_missing_duplicate_or_mismatched_shards_are_rejected(self):
|
||||
workflow = (ROOT / '.github/workflows/tests.yaml').read_text()
|
||||
matrix = 'shard: [' + ', '.join(map(str, range(24))) + ']'
|
||||
for replacement in (list(range(23)), list(range(23)) + [22], list(range(25))):
|
||||
matrix = 'shard: [' + ', '.join(map(str, range(32))) + ']'
|
||||
for replacement in (list(range(31)), list(range(31)) + [30], list(range(33))):
|
||||
with self.subTest(matrix=replacement), self.assertRaises(AssertionError):
|
||||
self.assert_hosted_pytest_contract(workflow.replace(matrix, 'shard: ' + str(replacement)))
|
||||
with self.assertRaises(AssertionError):
|
||||
self.assert_hosted_pytest_contract(workflow.replace('--count 24', '--count 23'))
|
||||
self.assert_hosted_pytest_contract(workflow.replace('--count 32', '--count 31'))
|
||||
|
||||
def test_completion_and_full_runner_cannot_be_bypassed(self):
|
||||
workflow = (ROOT / '.github/workflows/tests.yaml').read_text()
|
||||
@@ -60,7 +60,7 @@ class TestFeatureTestDiscovery(unittest.TestCase):
|
||||
(' if: always()\n', ' if: success()\n'),
|
||||
('pattern: custom-pytest-*', 'pattern: custom-pytest-0'),
|
||||
('if-no-files-found: error', 'if-no-files-found: ignore'),
|
||||
('--count 24 --output', '--count 24 -k selected --output'),
|
||||
('--count 32 --output', '--count 32 -k selected --output'),
|
||||
(' needs: custom_pytest', ' continue-on-error: true\n needs: custom_pytest'),
|
||||
):
|
||||
with self.subTest(change=old), self.assertRaises(AssertionError):
|
||||
|
||||
Reference in New Issue
Block a user