Files
StarPilot/openpilot/starpilot/aol/tests/test_host_source_lag_axes.py
T
firestar5683 80fbb4f6db Feature registration
Register vehicle controls, planner modes, UI actions and Galaxy services after their providers exist. Wire the shared runtime entrypoints and cover their combined behavior.

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
Co-authored-by: Daniel Koepping <elkoled@gmail.com>
Co-authored-by: Danny <dannyirwin31@yahoo.com>
Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com>
Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com>
Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com>
Co-authored-by: Kacper Rączy <gfw.kra@gmail.com>
Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu>
Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com>
Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com>
Co-authored-by: stef <19478336+stefpi@users.noreply.github.com>
Co-authored-by: whoisdomi <dcantu83@gmail.com>

Co-authored-by: Intelli <contact@intelli.software>
Co-authored-by: haraschax <6804392+haraschax@users.noreply.github.com>
Co-authored-by: prabhaavp <prabhaav@msu.edu>
2026-10-14 20:02:39 -05:00

104 lines
6.5 KiB
Python

"""Host source expiry preserves intent without expired commands."""
from contextlib import ExitStack
import time
from unittest import mock
import pytest
from openpilot.cereal import log
from openpilot.common.prefix import OpenpilotPrefix
from openpilot.starpilot.aol.tests import test_ioniq_availability as fixture
from openpilot.starpilot.aol.tests.test_input_deadline_continuity import BASE
from openpilot.starpilot.aol.tests.test_ordinary_source_availability import settings
@pytest.mark.parametrize('prior_lag', [False, True])
def test_actual_aol_only_host_lag_source_gap_preserves_only_armed_intent(prior_lag):
case = fixture.IoniqAvailabilityTests(methodName='runTest')
with ExitStack() as cleanup:
cleanup.callback(case.doCleanups)
cleanup.enter_context(OpenpilotPrefix())
sd, controls, drive, card, receive = case._ioniq_availability_callers(full_op=False)
old, state, command = drive(2, state_stamp=BASE, offset_ns=4_497_535, **settings('lateral_only', BASE, 3))
assert old.lateralActive and command.latActive and not old.longitudinalActive and not command.longActive
assert not state.enabled and not state.active and card.aol_card_intent.allowed_latch
assert receive(BASE + 4_497_535, axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents'])
deadline = old.validUntilMonoTime
with mock.patch('openpilot.selfdrive.controls.controlsd.time.monotonic_ns', return_value=deadline + 1):
expired, _ = controls.state_control()
assert not expired.latActive and not expired.longActive
source, sequence = BASE, 3
with mock.patch.object(sd.rk.avg_dt, 'get_average', return_value=0.02):
if prior_lag:
source, sequence = BASE + 10_000_000, 4
axis, state, command = drive(3, state_stamp=source, **settings('lateral_only', source, sequence))
assert log.OnroadEvent.EventName.selfdrivedLagging in sd.events.names
assert not axis.lateralActive and not command.latActive and not sd.aol_authority_lost
assert not state.enabled and not state.active and not axis.longitudinalActive and not command.longActive
assert receive(source, axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents'])
tick = 7 if prior_lag else 5
now = BASE + (tick - 2) * 10_000_000 + 5_399_000
axis, state, command = drive(tick, timeout=True, state_stamp=source, offset_ns=5_399_000,
**settings('lateral_only', source, sequence))
assert log.OnroadEvent.EventName.selfdrivedLagging in sd.events.names
assert not axis.lateralActive and not axis.longitudinalActive and not command.latActive and not command.longActive
assert old.validUntilMonoTime == deadline < now
assert not state.enabled and not state.active
# An ordinary host fault must not fabricate Panda-authority loss or
# revoke established armed intent; native/config/physical inputs are healthy.
assert log.OnroadEvent.EventName.controlsMismatch not in sd.events.names
assert not sd.aol_authority_lost
assert receive(now, axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents'])
assert card.aol_card_intent.allowed_latch
fresh = BASE + (tick - 1) * 10_000_000
axis, state, command = drive(tick + 1, state_stamp=fresh, **settings('lateral_only', fresh, sequence + 1))
assert not state.enabled and not state.active and not axis.longitudinalActive and not command.longActive
assert axis.lateralActive and command.latActive
assert not sd.events.contains(fixture.ET.ENABLE)
assert receive(fresh, axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents'])
@pytest.mark.parametrize('mode', ['lateral_only', 'long_only', 'combined', 'combined_unarmed'])
def test_first_real_lag_fresh_decision_expiring_during_publication_keeps_normal_state_and_intent(mode):
case = fixture.IoniqAvailabilityTests(methodName='runTest')
with ExitStack() as cleanup:
cleanup.callback(case.doCleanups)
cleanup.enter_context(OpenpilotPrefix())
full_op = mode != 'lateral_only'
sd, controls, drive, card, receive = case._ioniq_availability_callers(full_op=full_op)
old, state, _ = drive(2, state_stamp=BASE, offset_ns=4_497_535, **settings(mode, BASE, 3))
assert state.enabled == full_op and old.longitudinalActive == full_op
if mode in ('lateral_only', 'combined'):
assert receive(BASE + 4_497_535, axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents'])
fresh = BASE + 40_000_000
clock = [fresh + 29_999_000]
original_publish = sd.publish_axis_proof
entered = []
def publish(cs, **kwargs):
publication_ns = time.monotonic_ns()
entered.append((publication_ns, sd.aol_car_state_log_ns, tuple(sd.events.names),
sd.state_machine.state, sd.state_machine.soft_disable_timer))
assert publication_ns == fresh + 29_999_000 and sd.aol_car_state_log_ns == fresh
assert log.OnroadEvent.EventName.selfdrivedLagging in sd.events.names
assert log.OnroadEvent.EventName.commIssue not in sd.events.names
assert not sd.aol_authority_lost and sd.aol_published_fault_reason == 'none'
clock[0] = fresh + 30_001_000
return original_publish(cs, **kwargs)
with mock.patch.object(sd.rk.avg_dt, 'get_average', return_value=0.02), \
mock.patch('openpilot.selfdrive.selfdrived.selfdrived.time.monotonic_ns', side_effect=lambda: clock[0]), \
mock.patch.object(sd, 'publish_axis_proof', side_effect=publish):
axis, state, command = drive(6, state_stamp=fresh, offset_ns=29_999_000, **settings(mode, fresh, 4))
assert len(entered) == 1
assert not axis.lateralActive and not axis.longitudinalActive and not command.latActive and not command.longActive
assert old.validUntilMonoTime < axis.observedMonoTime
assert state.enabled == full_op and state.active == full_op
if full_op:
assert state.state == log.SelfdriveState.OpenpilotState.softDisabling and sd.state_machine.soft_disable_timer == 300
assert log.OnroadEvent.EventName.controlsMismatch not in sd.events.names
assert not sd.aol_authority_lost
if mode in ('lateral_only', 'combined'):
assert receive(fresh + 30_001_000, axis=sd.pm.messages['aolAxisState'], event=sd.pm.messages['onroadEvents'])
assert card.aol_card_intent.allowed_latch
with mock.patch('openpilot.selfdrive.controls.controlsd.time.monotonic_ns', return_value=old.validUntilMonoTime + 1):
expired, _ = controls.state_control()
assert not expired.latActive and not expired.longActive