mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 13:33:42 +08:00
169 lines
9.5 KiB
Python
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
|