Files
StarPilot/openpilot/starpilot/flm/tests/test_live_bootstrap_readiness.py
T
2026-10-21 23:28:39 -05:00

169 lines
9.5 KiB
Python

"""Reporting preserves original selection/caps; synthetic availability only."""
from dataclasses import replace
from types import SimpleNamespace
from unittest.mock import patch
import pytest
from openpilot.starpilot.flm import live_tracking_bootstrap_cohorts as cohorts
from openpilot.starpilot.flm.live_tracking_bootstrap_producer import response_snapshot, _epoch_checkpoints
from openpilot.starpilot.flm.controller_replay import Snapshot
from openpilot.starpilot.flm.tests.test_live_tracking_bootstrap_cohorts import source_data
def test_missing_raw_capture_still_declines_and_now_reports_its_actual_counts():
binding = source_data(seconds=1)[0]
collector = SimpleNamespace(adapter=SimpleNamespace(envelope=None), original_source_capture=None, ready=())
assert response_snapshot(collector, binding, 1) is None
assert collector._live_bootstrap_readiness == {
'reason': 'raw_prerequisites_unavailable', 'observedMonoTime': 1,
'rawPoses': 0, 'readyPairs': 0, 'captureEpoch': None, 'captureStartNs': None, 'captureCutoffNs': None}
@pytest.mark.parametrize('bad_until,expected_none', [(12, True), (0, False)])
def test_diagnostics_do_not_change_original_cohort_decision(bad_until, expected_none):
binding, q, frames, poses, events, controls, checkpoints = source_data(bad_until=bad_until, seconds=41)
arguments = {'binding': binding, 'epoch': 1, 'epoch_start_ns': q, 'cutoff_ns': q+100_000_000_000,
'now_ns': q+40_000_000_000, 'frames': frames, 'poses': poses, 'emissions': events,
'controls': controls, 'checkpoints': checkpoints}
original = cohorts.select_first_cohort(**arguments)
diagnostics = {}
reported = cohorts.select_first_cohort(**arguments, diagnostics=diagnostics)
assert reported == original
assert (reported is None) is expected_none
assert diagnostics['requiredCohortNs'] == 38_800_000_000
assert diagnostics['examinedAnchors'] > 0
assert diagnostics['reason'] == ('fixed_cohort_frame_health_or_contiguity_unavailable' if expected_none else
'source_cohort_selected_not_child_qualified')
def test_original_checkpoint_count_bound_still_raises_and_reports_literal_reason():
diagnostics = {}
checkpoints = tuple((index+1, Snapshot('reporting-only', ())) for index in range(129))
with pytest.raises(ValueError, match='genuine bounded checkpoint metadata'):
cohorts.source_quality_keys((), (), (), checkpoints, (), diagnostics=diagnostics)
assert diagnostics['reason'] == 'checkpoint_count_exceeds_128_bound'
def test_original_pose_support_decline_is_reported_without_response_inspection():
binding, q, frames, poses, events, controls, _ = source_data(bad_until=0, seconds=41)
diagnostics = {}
assert cohorts.quality_for_start(q, frames=frames, poses=poses[::3], emissions=events,
controls=controls, binding=binding, now_ns=q+40_000_000_000, diagnostics=diagnostics) is None
assert diagnostics['reason'] == 'fixed_slot_pose_count_unavailable'
def test_throttle_keeps_current_counts_and_labels_previous_selection_historical():
binding = source_data(seconds=1)[0]
previous = {'reason': 'fixed_cohort_clock_incomplete', 'observedMonoTime': 1,
'rawPoses': 2, 'readyPairs': 1}
capture = SimpleNamespace(rows=(1, 2, 3), epoch=7, epoch_start_ns=1, epoch_cutoff_ns=100)
collector = SimpleNamespace(adapter=SimpleNamespace(envelope=object()), original_source_capture=capture,
ready=(1, 2), _live_bootstrap_selection_epoch=7, _live_bootstrap_selection_next_ns=100,
_live_bootstrap_last_selection=previous)
with patch('openpilot.starpilot.flm.source_plant_runtime._current_fit_pair_reason', return_value=None):
assert response_snapshot(collector, binding, 50) is None
report = collector._live_bootstrap_readiness
assert report['observedMonoTime'] == 50
assert report['rawPoses'] == 3
assert report['readyPairs'] == 2
assert report['captureEpoch'] == 7
assert report['reason'] == 'selection_throttled'
assert report['throttled'] is True
assert report['previousSelection'] == previous
assert report['previousSelection'] is not previous
def test_unready_clock_avoids_archive_serialization_but_ready_and_bad_metadata_keep_full_checks():
binding, q, frames, poses, events, controls, checkpoints = source_data(bad_until=0, seconds=41)
def make(elapsed, checkpoint_override=None):
now = q+int(elapsed*1e9)
capture = SimpleNamespace(rows=tuple(row for row in poses if row.completed_ns <= now),
epoch=1, epoch_start_ns=poses[0].mono_ns, epoch_cutoff_ns=q+100_000_000_000)
replay = SimpleNamespace(checkpoints=checkpoint_override if checkpoint_override is not None else
tuple(row for row in checkpoints if row[0] <= now), records=frames,
emissions=SimpleNamespace(events={row.apply_ns: row for row in events}))
adapter = SimpleNamespace(envelope=object(), replay=replay,
original_context_receipts=SimpleNamespace(controls={row.control_ns: row for row in controls}))
return SimpleNamespace(adapter=adapter, original_source_capture=capture, ready=(1,)), now
# Unready is measured from original source clocks, never fit error/response.
with patch('openpilot.starpilot.flm.source_plant_runtime._current_fit_pair_reason', return_value=None):
with patch.object(cohorts, 'source_quality_keys', side_effect=AssertionError('full quality reached')) as full:
for elapsed in (1., 20., 38.79):
collector, now = make(elapsed)
assert response_snapshot(collector, binding, now) is None
assert collector._live_bootstrap_readiness['reason'] == 'fixed_cohort_clock_incomplete'
assert full.call_count == 0
collector, now = make(39.)
with pytest.raises(AssertionError, match='full quality reached'):
response_snapshot(collector, binding, now)
assert full.call_count == 1
# A bounded-clock preflight must not hide the original bad metadata guard.
collector, now = make(1., ((q, Snapshot('bad-bound', ())),)+tuple((poses[0].mono_ns+i*1_000_000, Snapshot('bad-bound', ())) for i in range(1, 129)))
with pytest.raises(ValueError, match='genuine bounded checkpoint metadata'):
response_snapshot(collector, binding, now)
def test_old_history_bound_failure_becomes_exact_original_epoch_selection():
binding, q, frames, poses, events, controls, epoch = source_data(seconds=61)
cutoff = q+100_000_000_000
# Old recorder history is structurally genuine metadata but irrelevant to this epoch.
old = tuple((stamp, Snapshot('synthetic-availability-only', ())) for stamp in range(1, 130))
assert old[-1][0] < q
retained = old+epoch
with pytest.raises(ValueError, match='genuine bounded checkpoint metadata'):
cohorts.source_quality_keys(frames, poses, events, retained, controls)
scoped = _epoch_checkpoints(retained, poses, cutoff)
assert scoped == epoch
assert cohorts.source_quality_keys(frames, poses, events, scoped, controls) == (
cohorts.source_quality_keys(frames, poses, events, epoch, controls))
arguments = {'binding': binding, 'epoch': 1, 'epoch_start_ns': q, 'cutoff_ns': cutoff,
'now_ns': q+60_000_000_000, 'frames': frames, 'poses': poses, 'emissions': events, 'controls': controls}
expected = cohorts.select_first_cohort(checkpoints=epoch, **arguments)
actual = cohorts.select_first_cohort(checkpoints=scoped, **arguments)
assert actual == expected
assert actual is not None
assert actual['start_ns'] == q+12_000_000_000
assert actual['origin_checkpoint_ns'] == q
assert actual['pose_counts'] == (80,)*8
# Every candidate anchor is retained, including later original valid alternatives.
assert tuple(stamp for stamp, _ in scoped) == tuple(stamp for stamp, _ in epoch)
changed_response = tuple(replace(row, yaw_rate=999., roll_rad=-1.) for row in poses)
assert _epoch_checkpoints(retained, changed_response, cutoff) == scoped
def test_129_checkpoints_inside_epoch_still_fail_original_cap():
_, q, frames, poses, events, controls, _ = source_data(seconds=1)
snapshot = Snapshot('synthetic-availability-only', ())
checkpoints = ((q, snapshot),)+tuple((poses[0].mono_ns+i*1_000_000, snapshot) for i in range(1, 129))
scoped = _epoch_checkpoints(checkpoints, poses, q+100_000_000_000)
assert scoped == checkpoints
assert len(scoped) == 129
with pytest.raises(ValueError, match='genuine bounded checkpoint metadata'):
cohorts.source_quality_keys(frames, poses, events, scoped, controls)
def test_scope_retains_preceding_anchor_and_discards_only_outside_epoch():
_, q, _, poses, _, _, checkpoints = source_data(seconds=1)
cutoff = q+100_000_000_000
retained = checkpoints+((cutoff, checkpoints[0][1]), (cutoff+1, checkpoints[0][1]))
assert _epoch_checkpoints(retained, poses, cutoff) == checkpoints
assert _epoch_checkpoints(((1, checkpoints[0][1]),)+retained, poses, cutoff) == checkpoints
@pytest.mark.parametrize('bad', ['order', 'type'])
def test_bad_metadata_is_rejected_even_outside_epoch(bad):
_, q, _, poses, _, _, checkpoints = source_data(seconds=1)
outside = ((2, checkpoints[0][1]), (1, checkpoints[0][1])) if bad == 'order' else ((1, object()),)
with pytest.raises(ValueError):
_epoch_checkpoints(outside+checkpoints, poses, q+100_000_000_000)
def test_missing_preceding_anchor_cannot_select_any_cohort():
binding, q, frames, poses, events, controls, checkpoints = source_data(seconds=41)
scoped = _epoch_checkpoints(checkpoints[1:], poses, q+100_000_000_000)
assert scoped == ()
assert cohorts.select_first_cohort(checkpoints=scoped, frames=frames, poses=poses, emissions=events, controls=controls,
binding=binding, epoch=1, epoch_start_ns=q, cutoff_ns=q+100_000_000_000, now_ns=q+40_000_000_000) is None