From 4d203961dab91f038da8e9404620a372a0fb1b26 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Fri, 23 Oct 2026 08:29:20 -0500 Subject: [PATCH] Align learner protocol and lifecycle fixtures --- .../flm/tests/test_combined_event_policy.py | 1 + .../tests/test_declared_restore_production.py | 21 +++++++++++--- .../flm/tests/test_event_boundaries.py | 10 +++++-- .../flm/tests/test_event_fd_boundary.py | 1 + .../starpilot/flm/tests/test_fit_worker_gc.py | 7 ++++- .../test_live_tracking_response_policy.py | 29 +++++++++++++------ .../flm/tests/test_physical_target_gravity.py | 2 +- prebuilt.json | 2 +- 8 files changed, 55 insertions(+), 18 deletions(-) diff --git a/openpilot/starpilot/flm/tests/test_combined_event_policy.py b/openpilot/starpilot/flm/tests/test_combined_event_policy.py index a1c7dcc6e9..35c420daa9 100644 --- a/openpilot/starpilot/flm/tests/test_combined_event_policy.py +++ b/openpilot/starpilot/flm/tests/test_combined_event_policy.py @@ -72,6 +72,7 @@ def test_shadow_secants_are_scoped_and_final_rollout_has_no_sticky_mode(): def test_forged_identification_token_rejects_before_controller_execution(): value = object.__new__(EventControllerReplay) + value.engine = SimpleNamespace() # Explicit empty delegate; no native/controller admission. value._EventControllerReplay__identification_token = object() value.activation_plan = SimpleNamespace(identification_policy=NATIVE_SHADOW_IDENTIFICATION) with pytest.raises(ValueError, match='scope/policy'): diff --git a/openpilot/starpilot/flm/tests/test_declared_restore_production.py b/openpilot/starpilot/flm/tests/test_declared_restore_production.py index a2066b6c5c..a56a388a18 100644 --- a/openpilot/starpilot/flm/tests/test_declared_restore_production.py +++ b/openpilot/starpilot/flm/tests/test_declared_restore_production.py @@ -5,6 +5,8 @@ from opendbc.car.honda.values import CAR as HONDA from opendbc.car.hyundai.values import CAR as HKG from opendbc.car.toyota.values import CAR as TOYOTA from openpilot.starpilot.flm.learner_factory import from_evidence +from openpilot.starpilot.flm.feedback_replay import PROTOCOL as FEEDBACK_PROTOCOL +from openpilot.starpilot.flm.source_plant_fit import PROTOCOL as LEGACY_SOURCE_PROTOCOL from openpilot.starpilot.flm.clipping_quality import ZERO_REFERENCE_QUALITY, QUALIFIED_EVENT_CLIPPING_QUALITY from openpilot.starpilot.flm.runtime_adapter_registry import baseline_values from openpilot.starpilot.flm.steerlearnerd import production_adapter_for_evidence @@ -14,15 +16,17 @@ from openpilot.starpilot.flm.tests.test_registered_controls_global import contro @pytest.mark.parametrize('platform,mode', ((TOYOTA.TOYOTA_RAV4_TSS2, 'standard'), (HKG.HYUNDAI_IONIQ_6, 'starpilot'), (HONDA.HONDA_CIVIC, None))) -def test_actual_registered_producer_explicit_restore_version_and_legacy_default(platform, mode): +def test_actual_registered_producer_explicit_restore_version_and_legacy_default(platform, mode, protocol=None, restore_version=8): cp = interfaces[platform].get_non_essential_params(platform) with controls_for(cp, mode, True) as controls: evidence, source, _ = capture(controls) before = controls.CP.as_builder().to_bytes(), baseline_values(controls.LaC) - live = production_adapter_for_evidence(controls.CP, evidence, ('event', 'center'), source=source) + live = production_adapter_for_evidence(controls.CP, evidence, ('event', 'center'), source=source, + source_plant_protocol=protocol) old = from_evidence(controls.CP, evidence, ('event', 'center'), source=source) - assert live is not None and live.replay_version == 3 and live.restore_version == 6 + assert live is not None and live.replay_version == 3 and live.restore_version == restore_version assert live.clipping_quality == ZERO_REFERENCE_QUALITY + assert live.source_plant_protocol == (FEEDBACK_PROTOCOL if protocol is None else protocol) for version in (4, 5): legacy = from_evidence(controls.CP, evidence, ('event', 'center'), source=source, event_replay_version=3, event_clipping_quality=QUALIFIED_EVENT_CLIPPING_QUALITY, event_restore_version=version) @@ -30,5 +34,14 @@ def test_actual_registered_producer_explicit_restore_version_and_legacy_default( assert old is not None and old.replay_version == 2 and old.restore_version == 2 assert from_evidence(controls.CP, evidence, ('event', 'center'), source=source, event_restore_version=4) is None live.select_objective(('event', 'deadband.high')) - assert live.restore_version == 6 + assert live.restore_version == restore_version assert (controls.CP.as_builder().to_bytes(), baseline_values(controls.LaC)) == before + + +@pytest.mark.parametrize('platform,mode', ((TOYOTA.TOYOTA_RAV4_TSS2, 'standard'), + (HKG.HYUNDAI_IONIQ_6, 'starpilot'), (HONDA.HONDA_CIVIC, None))) +def test_actual_registered_producer_explicit_legacy_source_restore6(platform, mode): + # Exercise the same real application, refusal and unchanged-controller checks + # for the independently selected legacy source protocol as for default8. + test_actual_registered_producer_explicit_restore_version_and_legacy_default( + platform, mode, protocol=LEGACY_SOURCE_PROTOCOL, restore_version=6) diff --git a/openpilot/starpilot/flm/tests/test_event_boundaries.py b/openpilot/starpilot/flm/tests/test_event_boundaries.py index 5e6d79b780..5d5cea6c6e 100644 --- a/openpilot/starpilot/flm/tests/test_event_boundaries.py +++ b/openpilot/starpilot/flm/tests/test_event_boundaries.py @@ -25,15 +25,21 @@ def test_interrupted_neutral_timer_does_not_split_one_large_maneuver(): def test_tracking_attempt_count_uses_same_contiguous_neutral_recovery(): - objective = SimpleNamespace(group='ff.left', names=('ff_gain_left',), anchor_digest='c' * 64, context_key='d' * 64) + objective = SimpleNamespace(group='ff.left', names=('ff_gain_left',), anchor_digest='c' * 64, context_key='d' * 64, + parameters=(SimpleNamespace(excitation_floor=.01),)) model = SimpleNamespace(heldout_rmse=.001) for neutral, expected in ((11, 0), (51, 1)): engine = trajectory(neutral) - samples = tuple({'control_ns': frame.control_ns, + samples = tuple({'control_ns': frame.control_ns, 'apply_ns': frame.control_ns + 20_000_000, 'control': {'stage_values': (('setpoint', frame.desired_physical_accel),)}, 'physical': {'lateral_accel': -.2}} for frame in engine.records[::5]) + # Declared unit basis only: each sampled endpoint has a visible derivative. + # This shell does not claim native qualification or current authority. + engine.projected = {row['apply_ns']: (0., (1.,)) for row in samples} blocks = ({'block_id': 'whole-physical-turn', 'samples': samples},) episodes = tracking_episodes(engine, blocks, model, objective) assert len(episodes) == expected if episodes: assert episodes[0]['block_ids'] == ('whole-physical-turn',) + engine.projected.clear() + assert tracking_episodes(engine, blocks, model, objective) == () # No encoded basis, no counted episode. diff --git a/openpilot/starpilot/flm/tests/test_event_fd_boundary.py b/openpilot/starpilot/flm/tests/test_event_fd_boundary.py index 8a34d06dc1..21dafe766c 100644 --- a/openpilot/starpilot/flm/tests/test_event_fd_boundary.py +++ b/openpilot/starpilot/flm/tests/test_event_fd_boundary.py @@ -9,6 +9,7 @@ from openpilot.starpilot.flm.event_controller_replay import EventControllerRepla def engine(): value = object.__new__(EventControllerReplay) + value.engine = SimpleNamespace() # Explicit empty delegate for this policy-only shell. value.restore_envelope = None value.objective = SimpleNamespace(parameters=(SimpleNamespace(low=0., high=.25),)) value.reference = (0.,) diff --git a/openpilot/starpilot/flm/tests/test_fit_worker_gc.py b/openpilot/starpilot/flm/tests/test_fit_worker_gc.py index cc7b8793d0..f95173fa0d 100644 --- a/openpilot/starpilot/flm/tests/test_fit_worker_gc.py +++ b/openpilot/starpilot/flm/tests/test_fit_worker_gc.py @@ -9,7 +9,7 @@ from typing import NoReturn, cast import pytest -from openpilot.starpilot.flm import fit_worker +from openpilot.starpilot.flm import fit_worker, native_primitives @pytest.fixture @@ -18,8 +18,13 @@ def worker_io(monkeypatch): incoming, outgoing = io.BytesIO(b'{"test":true}'), io.BytesIO() monkeypatch.setattr(fit_worker, 'sys', SimpleNamespace(platform='darwin', stdin=SimpleNamespace(buffer=incoming), stdout=SimpleNamespace(buffer=outgoing))) + # Both worker modules use the same explicitly simulated macOS context. + # Linux native-worker resource guards remain real in their own tests. + monkeypatch.setattr(native_primitives, 'sys', fit_worker.sys) monkeypatch.setattr(fit_worker.os, 'nice', lambda _: None) monkeypatch.setattr(fit_worker.os, 'sched_setscheduler', lambda *args: None, raising=False) + monkeypatch.setattr(fit_worker.os, 'SCHED_OTHER', 0, raising=False) + monkeypatch.setattr(fit_worker.os, 'sched_param', lambda priority: SimpleNamespace(sched_priority=priority), raising=False) limits = [] monkeypatch.setattr(fit_worker.resource, 'getrlimit', lambda kind: (-1, -1)) monkeypatch.setattr(fit_worker.resource, 'setrlimit', lambda *args: limits.append(args)) diff --git a/openpilot/starpilot/flm/tests/test_live_tracking_response_policy.py b/openpilot/starpilot/flm/tests/test_live_tracking_response_policy.py index 891db2d14f..16b398944a 100644 --- a/openpilot/starpilot/flm/tests/test_live_tracking_response_policy.py +++ b/openpilot/starpilot/flm/tests/test_live_tracking_response_policy.py @@ -101,15 +101,20 @@ def test_parent_discards_unqualified_successful_child_report(monkeypatch): def test_daemon_explicitly_constructs_production_response_process(): + # Current live source tracking owns no numerical plant-bootstrap process. import openpilot.starpilot.flm.steerlearnerd as daemon tree = ast.parse(Path(daemon.__file__).read_text()) calls = [node for node in ast.walk(tree) if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == 'LiveTrackingActor'] assert len(calls) == 1 keywords = {item.arg: item.value for item in calls[0].keywords} - assert isinstance(keywords['bootstrap_process'], ast.Call) - assert isinstance(keywords['bootstrap_process'].func, ast.Name) - assert keywords['bootstrap_process'].func.id == 'ProductionBootstrapProcess' + assert not calls[0].args + assert set(keywords) == {'probe_process', 'source_tracking'} + assert isinstance(keywords['source_tracking'], ast.Constant) + assert keywords['source_tracking'].value is True + from openpilot.starpilot.flm.live_tracking_actor import LiveTrackingActor + with pytest.raises(ValueError, match='source tracking has no plant bootstrap process'): + LiveTrackingActor(bootstrap_process=object(), source_tracking=True) assert isinstance(keywords['probe_process'], ast.Call) assert isinstance(keywords['probe_process'].func, ast.Name) assert keywords['probe_process'].func.id == 'ProbeProcess' @@ -131,10 +136,16 @@ def test_real_pending_child_survives_capture_rollover_but_not_changed_physical_b owner = RetainedResponseRequalifier(old, basis(old), new_drive(old)) control, output, _pose = synthetic_pair(owner, 0) binding = owner._binding - source = SimpleNamespace(cp_sha256=binding.cp_sha256, source_epoch=binding.source_epoch, - reset=binding.reset_generation, calibration_generation=binding.calibration_generation, - persistent_source_epoch=binding.persistent_source_epoch) - collector = SimpleNamespace(ready=[(control, output)], original_source_capture=SimpleNamespace(epoch=0), live_tracking=None) + from openpilot.starpilot.flm.runtime_adapter_registry import SourceState + from openpilot.starpilot.flm.torque_emission_replay import EmissionHistory + def source_at(now): + return SourceState(True, False, binding.reset_generation, binding.source_epoch, now, now+500_000_000, + binding.cp_sha256, (), binding.calibration_generation, binding.persistent_source_epoch) + source = source_at(99_000_000_000) + # Empty original inventory is truthful for this child-lifecycle-only fixture. + replay = SimpleNamespace(raw_mode=False, emissions=EmissionHistory(None)) + collector = SimpleNamespace(ready=[(control, output)], adapter=SimpleNamespace(replay=replay), + original_source_capture=SimpleNamespace(epoch=0), live_tracking=None) process = ProductionBootstrapProcess() actor = LiveTrackingActor(bootstrap_process=process) seed = actor.binding_for(collector, source) @@ -150,12 +161,12 @@ def test_real_pending_child_survives_capture_rollover_but_not_changed_physical_b assert actor.tick(collector, source, now_ns=99_000_000_000, current_native=True) is None assert process.child is child and child.poll() is None and not process.reaping collector.original_source_capture.epoch = 1 - assert actor.tick(collector, source, now_ns=101_000_000_000, current_native=True) is None + assert actor.tick(collector, source_at(101_000_000_000), now_ns=101_000_000_000, current_native=True) is None assert actor.seed == seed and process.token == token assert process.child is child and child.poll() is None and not process.reaping assert actor.attempted_epoch == 0 # No duplicate child at capture rotation. collector.ready = [(replace(control, vehicle_model_anchor=(16., 100000., 110000.)), output)] - assert actor.tick(collector, source, now_ns=102_000_000_000, current_native=True) is None + assert actor.tick(collector, source_at(102_000_000_000), now_ns=102_000_000_000, current_native=True) is None assert process.reaping or process.child is None assert actor.reason == 'actual_response_source_unavailable' finally: diff --git a/openpilot/starpilot/flm/tests/test_physical_target_gravity.py b/openpilot/starpilot/flm/tests/test_physical_target_gravity.py index 2732ae4c26..38af4d33c7 100644 --- a/openpilot/starpilot/flm/tests/test_physical_target_gravity.py +++ b/openpilot/starpilot/flm/tests/test_physical_target_gravity.py @@ -114,7 +114,7 @@ def test_physical_tracking_error_uses_force_goal_while_geometry_keeps_original_s rows.append(SimpleNamespace(control_ns=stamp,desired_kinematic_accel=goal,desired_physical_accel=force, inputs=(('vEgo',20.),),healthy=True)) if index%5==0: - points.append({'control_ns':stamp,'control':{'stage_values':(('setpoint',goal),)}, + points.append({'control_ns':stamp,'apply_ns':stamp+20_000_000,'control':{'stage_values':(('setpoint',goal),)}, 'physical':{'lateral_accel':force,'roll_rad':-.2}}) engine=SimpleNamespace(records=rows,replay_windows=((first,rows[-1].control_ns),), _frame_context=lambda frame:True,identity='a'*64,baseline_revision='b'*64) diff --git a/prebuilt.json b/prebuilt.json index 43a63dd14b..c52240ba43 100644 --- a/prebuilt.json +++ b/prebuilt.json @@ -1 +1 @@ -{"protected_inventory_sha256": "43b3191cf68f5e0cd10581d20c3ee38210c130f6e0b847453ed69477ed8edea0", "schema_version": 1} +{"protected_inventory_sha256": "cad571a1c0a2273b2af705736b5ed8e30def48befd996b0802ccffd13af667fa", "schema_version": 1}