mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 05:23:41 +08:00
487dfc4bf4
Merge bounded control/output FIFO heads using original publisher headers. Preserve original age, native, source, overflow and malformed-input checks; retain producer order after joins. Validated with actual pinned cloud dependencies and fresh-head regression tests.
90 lines
4.1 KiB
Python
90 lines
4.1 KiB
Python
"""Existing status publications must name settings blocks accurately."""
|
|
from openpilot.starpilot.flm.tests._drain_fixture import separate_drain_fixture
|
|
from dataclasses import replace
|
|
from types import SimpleNamespace
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
|
|
from openpilot.starpilot.flm import steerlearnerd
|
|
from openpilot.starpilot.flm.runtime_adapter_registry import SourceState
|
|
|
|
|
|
NOW = 1_000_000_000
|
|
|
|
|
|
def publication(source, evidence=None, adapter=None, sources=None):
|
|
sources = [source] if sources is None else sources
|
|
sm = mock.Mock(updated={'deviceMotion': False}, logMonoTime={'deviceMotion': 0})
|
|
ticks = iter(NOW + index * 250_000_000 for index in range(len(sources)))
|
|
current_time = [NOW]
|
|
|
|
def update(timeout=None):
|
|
if timeout == 0:
|
|
return
|
|
current_time[0] = next(ticks)
|
|
|
|
sm.update.side_effect = update
|
|
pm, fitter = mock.Mock(), mock.Mock()
|
|
control_socket, applied_socket = object(), object()
|
|
factory = mock.Mock(return_value=adapter)
|
|
source_reader = mock.Mock(side_effect=sources)
|
|
clock = mock.Mock(side_effect=lambda: current_time[0])
|
|
|
|
def drain(socket, callback, receive, sink, now):
|
|
if socket is control_socket and evidence is not None:
|
|
event = SimpleNamespace(which=lambda: 'starpilotLateralState',
|
|
starpilotLateralState=SimpleNamespace(learningEvidence=SimpleNamespace(version=1)))
|
|
callback(event, now, now)
|
|
|
|
with mock.patch.object(steerlearnerd, 'drain_pair', separate_drain_fixture(drain)), mock.patch.object(steerlearnerd, 'read_control', return_value=evidence):
|
|
with pytest.raises(StopIteration):
|
|
steerlearnerd.run(SimpleNamespace(), sm, control_socket, applied_socket, pm, factory, mock.Mock(),
|
|
source_reader, lambda item: (('feedforward', 'low', 'steady'),),
|
|
lambda item: ('feedforward', 'low', 'steady'), mock.Mock(), mock.Mock(), fitter,
|
|
mock.Mock(), clock=clock)
|
|
fitter.close.assert_called_once_with('learner_parent_exit')
|
|
factory_calls = factory.call_count
|
|
message = pm.send.call_args.args[1]
|
|
assert message.valid
|
|
assert str(message.autoSteeringLearning.learning.status) == 'unavailable'
|
|
assert message.autoSteeringLearning.learning.sourceReceipt.manual == sources[-1].manual
|
|
return str(message.autoSteeringLearning.learning.reason), factory_calls
|
|
|
|
|
|
def source():
|
|
return SourceState(True, False, '0', 'a' * 64, NOW, NOW + 500_000_000, 'b' * 64, (0., 0., 0.))
|
|
|
|
|
|
@pytest.mark.parametrize('change,reason', (({'enabled': False}, 'learning_disabled'),
|
|
({'manual': True}, 'manual_profile_override'),
|
|
({'observed_ns': NOW - 600_000_000, 'expires_ns': NOW - 100_000_000},
|
|
'settings_receipt_expired')))
|
|
def test_settings_block_is_reported_without_claiming_an_unsupported_controller(change, reason):
|
|
actual, factory_calls = publication(replace(source(), **change))
|
|
assert actual == reason
|
|
assert factory_calls == 0
|
|
|
|
|
|
def test_no_controller_evidence_is_distinct_from_adapter_rejection():
|
|
reason, factory_calls = publication(source())
|
|
assert reason == 'no_current_controller_evidence'
|
|
assert factory_calls == 0
|
|
|
|
|
|
def test_cleared_manual_block_does_not_leave_a_stale_status_reason():
|
|
resumed = replace(source(), source_epoch='c' * 64)
|
|
reason, factory_calls = publication(resumed, sources=[replace(source(), manual=True), resumed, resumed])
|
|
assert reason == 'no_current_controller_evidence'
|
|
assert factory_calls == 0
|
|
|
|
|
|
def test_actual_adapter_unavailable_is_reserved_for_a_real_factory_refusal():
|
|
evidence = SimpleNamespace(source_epoch='a' * 64, source_receipt_ns=NOW, control_ns=NOW,
|
|
identity='c' * 64, generation=1, domain='normalized_torque', stage_revision='stage',
|
|
response_revision='response', controller_mode='starpilot', policy='ioniq6',
|
|
baseline_revision='baseline', controller_revision='controller', turn_assist=False)
|
|
reason, factory_calls = publication(source(), evidence)
|
|
assert reason == 'actual_adapter_unavailable'
|
|
assert factory_calls == 1
|