Files
gps-denied-onboard/tests/unit/c1_vio/test_az335_warm_start.py
T
Oleksandr Bezdieniezhnykh 06f655d8fb [AZ-335] C1 warm-start hint persistence + F8 reboot recovery wiring
Adds JsonSidecarWarmStartHintStore (atomic JSON + SHA-256 sidecar via
AZ-280) inside c1_vio, plus the cross-strategy WarmStartWiredStrategy
wrapper + prime_warm_start_from_disk / prime_warm_start_from_fc hooks
at runtime_root. AC-7 post-reset covariance inflation and AC-8 "no
fake confidence" baseline floor are enforced at the wiring layer so
no strategy module needed edits. Adds three c1_vio config fields
(warm_start_store_dir, warm_start_save_period_frames,
post_reset_covariance_inflation_factor) and registers the new FDR
kind vio.warm_start. 34 unit tests cover all 10 ACs + 3 NFRs.

Verdict PASS_WITH_WARNINGS — see
_docs/03_implementation/reviews/batch_56_review.md for the four
non-blocking documentation findings (F1 cold-start log kind shorthand,
F2 strategy-frame pose semantics, F3 dev-hardware perf smoke, F4
runtime_root importing c1-internal _facade_spine for shared FDR
conventions).

Closes AZ-335; depends on AZ-528 (batch 55).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 03:30:46 +03:00

933 lines
32 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""AZ-335 — C1 warm-start hint persistence + F8 reboot recovery wiring tests.
Covers all 10 acceptance criteria from
``_docs/02_tasks/todo/AZ-335_c1_warm_start_recovery.md`` plus three
non-functional requirements (perf-save, perf-load, no-crash). Tests
target both the c1-internal :class:`JsonSidecarWarmStartHintStore`
and the runtime-root :class:`WarmStartWiredStrategy` + prime hooks.
The wiring tests construct a deliberately minimal scriptable
:class:`_FakeVioStrategy` (kept local — the c1_vio strategy backends
already exercise the strategy-internal Protocol shape exhaustively;
this file's job is to verify the **wiring** layer behaves correctly
when wrapped around any strategy). The store tests use the real
:class:`Sha256Sidecar` (atomicwrites) on tmp_path — no fakes here
because the AC-1/AC-2/AC-10 contracts ARE about the on-disk
behaviour itself.
"""
from __future__ import annotations
import logging
import time
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Literal
import gtsam
import numpy as np
import pytest
from gps_denied_onboard._types.nav import (
FeatureQuality,
ImuBias,
ImuWindow,
NavCameraFrame,
VioHealth,
VioOutput,
VioState,
WarmStartPose,
)
from gps_denied_onboard._types.calibration import CameraCalibration
from gps_denied_onboard.components.c1_vio.warm_start_store import (
HINT_FILENAME,
HINT_SCHEMA_VERSION,
JsonSidecarWarmStartHintStore,
LoadedWarmStartHint,
WarmStartFcSource,
WarmStartHintStore,
)
from gps_denied_onboard.fdr_client.fakes import FakeFdrSink
from gps_denied_onboard.helpers.sha256_sidecar import SIDECAR_SUFFIX
from gps_denied_onboard.runtime_root.warm_start_wiring import (
WARM_START_PRODUCER_ID,
WarmStartWiredStrategy,
prime_warm_start_from_disk,
prime_warm_start_from_fc,
)
_DEFAULT_CALIBRATION_ID = "adti26"
# ---------------------------------------------------------------------------
# Shared builders.
def _make_pose(yaw_deg: float = 0.0, x: float = 1.0, y: float = 2.0, z: float = 3.0) -> gtsam.Pose3:
"""A concrete SE(3) pose with a deterministic, non-identity rotation."""
yaw = np.deg2rad(yaw_deg)
R = np.array(
[
[np.cos(yaw), -np.sin(yaw), 0.0],
[np.sin(yaw), np.cos(yaw), 0.0],
[0.0, 0.0, 1.0],
],
dtype=np.float64,
)
T = np.eye(4, dtype=np.float64)
T[:3, :3] = R
T[:3, 3] = [x, y, z]
return gtsam.Pose3(T)
def _make_hint(
*,
yaw_deg: float = 5.0,
velocity: tuple[float, float, float] = (1.0, 2.0, 3.0),
accel_bias: tuple[float, float, float] = (0.01, -0.02, 0.03),
gyro_bias: tuple[float, float, float] = (0.001, 0.002, -0.003),
captured_at_ns: int = 1_700_000_000_000,
) -> WarmStartPose:
return WarmStartPose(
body_T_world=_make_pose(yaw_deg=yaw_deg),
velocity_b=velocity,
bias=ImuBias(accel_bias=accel_bias, gyro_bias=gyro_bias),
captured_at_ns=captured_at_ns,
)
def _make_calibration() -> CameraCalibration:
return CameraCalibration(
camera_id="cam0",
intrinsics_3x3=np.eye(3, dtype=np.float64),
distortion=np.zeros(4, dtype=np.float64),
body_to_camera_se3=_make_pose(),
acquisition_method="checker_board",
)
def _make_imu_window() -> ImuWindow:
return ImuWindow(samples=tuple(), ts_start_ns=0, ts_end_ns=0)
def _make_frame(frame_id: int = 1) -> NavCameraFrame:
return NavCameraFrame(
frame_id=frame_id,
timestamp=datetime(2026, 5, 14, 0, 0, 0, tzinfo=timezone.utc),
image=np.zeros((10, 10), dtype=np.uint8),
camera_calibration_id="cam0",
)
# ---------------------------------------------------------------------------
# Local scriptable VioStrategy fake — wiring tests only.
@dataclass
class _ResetCall:
"""One captured ``reset_to_warm_start`` invocation on the fake strategy."""
hint: WarmStartPose
class _FakeVioStrategy:
"""Scriptable minimal :class:`VioStrategy` for AZ-335 wiring tests.
Returns a deterministic per-call :class:`VioOutput` whose
``pose_covariance_6x6`` is the value most recently set via
:meth:`set_emit_covariance` (default ``np.eye(6) * 0.01``).
Each :meth:`reset_to_warm_start` invocation is captured in
:attr:`reset_calls` so wiring tests can assert single-call,
correct-hint, no-call semantics.
"""
def __init__(self, *, label: Literal["okvis2", "vins_mono", "klt_ransac"] = "klt_ransac") -> None:
self._label = label
self._next_cov = np.eye(6, dtype=np.float64) * 0.01
self._next_bias = ImuBias(accel_bias=(0.0, 0.0, 0.0), gyro_bias=(0.0, 0.0, 0.0))
self._frame_counter = 0
self.reset_calls: list[_ResetCall] = []
self._raise_on_reset: Exception | None = None
def set_emit_covariance(self, cov: np.ndarray) -> None:
self._next_cov = np.asarray(cov, dtype=np.float64)
def set_emit_bias(self, bias: ImuBias) -> None:
self._next_bias = bias
def script_reset_failure(self, exc: Exception) -> None:
self._raise_on_reset = exc
def process_frame(
self,
frame: NavCameraFrame,
imu: ImuWindow,
calibration: CameraCalibration,
) -> VioOutput:
self._frame_counter += 1
return VioOutput(
frame_id=f"frame-{self._frame_counter}",
relative_pose_T=_make_pose(),
pose_covariance_6x6=self._next_cov.copy(),
imu_bias=self._next_bias,
feature_quality=FeatureQuality(
tracked=80, new=2, lost=1, mean_parallax=5.0, mre_px=0.8
),
emitted_at_ns=1_700_000_000_000 + self._frame_counter,
)
def reset_to_warm_start(self, hint: WarmStartPose) -> None:
if self._raise_on_reset is not None:
raise self._raise_on_reset
self.reset_calls.append(_ResetCall(hint=hint))
def health_snapshot(self) -> VioHealth:
return VioHealth(state=VioState.TRACKING, consecutive_lost=0, bias_norm=0.0)
def current_strategy_label(self) -> Literal["okvis2", "vins_mono", "klt_ransac"]:
return self._label
class _FakeFcSource:
"""Scriptable :class:`WarmStartFcSource` for F2 takeoff tests."""
def __init__(
self,
*,
hint: WarmStartPose | None = None,
raise_with: Exception | None = None,
calibration_id: str = _DEFAULT_CALIBRATION_ID,
) -> None:
self._hint = hint
self._raise_with = raise_with
self._calibration_id = calibration_id
self.fetch_call_count = 0
def fetch_warm_start_pose(self) -> WarmStartPose | None:
self.fetch_call_count += 1
if self._raise_with is not None:
raise self._raise_with
return self._hint
def calibration_id(self) -> str:
return self._calibration_id
def _make_wired(
inner: _FakeVioStrategy,
store: WarmStartHintStore,
*,
warm_start_max_frames: int = 5,
inflation_factor: float = 2.0,
save_period: int = 5,
) -> WarmStartWiredStrategy:
return WarmStartWiredStrategy(
inner=inner,
store=store,
warm_start_max_frames=warm_start_max_frames,
post_reset_covariance_inflation_factor=inflation_factor,
warm_start_save_period_frames=save_period,
)
def _drive_frames(wired: WarmStartWiredStrategy, n: int) -> list[VioOutput]:
return [
wired.process_frame(_make_frame(i), _make_imu_window(), _make_calibration())
for i in range(1, n + 1)
]
# ===========================================================================
# Store tests — AC-1, AC-2, AC-9, AC-10, NFR-perf-save, NFR-perf-load,
# Risk-2 calibration-mismatch.
class TestStoreAc1RoundTrip:
def test_save_then_load_returns_deep_equal_hint(self, tmp_path: Path) -> None:
# Arrange
store = JsonSidecarWarmStartHintStore(tmp_path, calibration_id=_DEFAULT_CALIBRATION_ID)
hint = _make_hint()
# Act
store.save(hint, pre_reboot_covariance_norm=0.123)
loaded = store.load()
# Assert
assert loaded is not None
assert isinstance(loaded, LoadedWarmStartHint)
assert loaded.calibration_id == _DEFAULT_CALIBRATION_ID
assert loaded.pre_reboot_covariance_norm == pytest.approx(0.123)
np.testing.assert_array_almost_equal(
loaded.pose.body_T_world.matrix(), hint.body_T_world.matrix()
)
assert loaded.pose.velocity_b == hint.velocity_b
assert loaded.pose.bias == hint.bias
assert loaded.pose.captured_at_ns == hint.captured_at_ns
def test_save_creates_payload_and_sidecar_files(self, tmp_path: Path) -> None:
# Arrange
store = JsonSidecarWarmStartHintStore(tmp_path, calibration_id=_DEFAULT_CALIBRATION_ID)
hint = _make_hint()
# Act
store.save(hint, pre_reboot_covariance_norm=0.5)
# Assert
assert (tmp_path / HINT_FILENAME).exists()
assert (tmp_path / (HINT_FILENAME + SIDECAR_SUFFIX)).exists()
assert store.payload_path == tmp_path / HINT_FILENAME
def test_save_creates_missing_parent_directory(self, tmp_path: Path) -> None:
# Arrange
nested = tmp_path / "nested" / "dirs" / "warm_start"
store = JsonSidecarWarmStartHintStore(nested, calibration_id=_DEFAULT_CALIBRATION_ID)
hint = _make_hint()
# Act
store.save(hint, pre_reboot_covariance_norm=0.0)
# Assert
assert (nested / HINT_FILENAME).exists()
class TestStoreAc2Corrupted:
def _seed_valid_then_flip_one_byte(
self, tmp_path: Path
) -> tuple[JsonSidecarWarmStartHintStore, Path]:
store = JsonSidecarWarmStartHintStore(tmp_path, calibration_id=_DEFAULT_CALIBRATION_ID)
store.save(_make_hint(), pre_reboot_covariance_norm=0.1)
payload_path = tmp_path / HINT_FILENAME
original = payload_path.read_bytes()
# Flip one byte mid-payload to trigger sha256 mismatch but keep
# the file structurally present and the sidecar untouched.
idx = len(original) // 2
corrupted = original[:idx] + bytes([(original[idx] + 1) % 256]) + original[idx + 1 :]
payload_path.write_bytes(corrupted)
return store, payload_path
def test_corrupted_payload_returns_none(self, tmp_path: Path, caplog: Any) -> None:
# Arrange
store, _ = self._seed_valid_then_flip_one_byte(tmp_path)
# Act
with caplog.at_level(logging.WARNING):
loaded = store.load()
# Assert
assert loaded is None
warn_records = [
r for r in caplog.records if getattr(r, "kind", "") == "c1.warm_start.corrupted"
]
assert len(warn_records) == 1
assert warn_records[0].levelname == "WARNING"
def test_corrupted_file_is_not_silently_deleted(self, tmp_path: Path) -> None:
# Arrange + Act
store, payload_path = self._seed_valid_then_flip_one_byte(tmp_path)
_ = store.load()
# Assert
assert payload_path.exists(), "AC-2: operator may want to forensically inspect"
def test_structurally_invalid_json_returns_none_with_warn(
self, tmp_path: Path, caplog: Any
) -> None:
# Arrange — write a payload with the WRONG schema version and rebuild the sidecar
# so sha256 verifies but envelope deserialisation rejects.
from gps_denied_onboard.helpers.sha256_sidecar import Sha256Sidecar
bad_payload = b'{"version": 999, "calibration_id": "x", "pose": {}}'
Sha256Sidecar.write_atomic_and_sidecar(tmp_path / HINT_FILENAME, bad_payload)
store = JsonSidecarWarmStartHintStore(tmp_path, calibration_id="x")
# Act
with caplog.at_level(logging.WARNING):
loaded = store.load()
# Assert
assert loaded is None
kinds = [getattr(r, "kind", "") for r in caplog.records]
assert "c1.warm_start.corrupted" in kinds
class TestStoreAc3CalibrationMismatch:
def test_calibration_mismatch_returns_none_with_specific_warn(
self, tmp_path: Path, caplog: Any
) -> None:
# Arrange
producer = JsonSidecarWarmStartHintStore(tmp_path, calibration_id="OLD_CAL")
producer.save(_make_hint(), pre_reboot_covariance_norm=0.1)
consumer = JsonSidecarWarmStartHintStore(tmp_path, calibration_id="NEW_CAL")
# Act
with caplog.at_level(logging.WARNING):
loaded = consumer.load()
# Assert
assert loaded is None
warn_records = [
r
for r in caplog.records
if getattr(r, "kind", "") == "c1.warm_start.calibration_mismatch"
]
assert len(warn_records) == 1
class TestStoreAc9Clear:
def test_clear_removes_payload_and_sidecar(self, tmp_path: Path) -> None:
# Arrange
store = JsonSidecarWarmStartHintStore(tmp_path, calibration_id=_DEFAULT_CALIBRATION_ID)
store.save(_make_hint(), pre_reboot_covariance_norm=0.1)
# Act
store.clear()
# Assert
assert not (tmp_path / HINT_FILENAME).exists()
assert not (tmp_path / (HINT_FILENAME + SIDECAR_SUFFIX)).exists()
assert store.load() is None
def test_clear_emits_info_log(self, tmp_path: Path, caplog: Any) -> None:
# Arrange
store = JsonSidecarWarmStartHintStore(tmp_path, calibration_id=_DEFAULT_CALIBRATION_ID)
# Act
with caplog.at_level(logging.INFO):
store.clear()
# Assert
info_records = [
r for r in caplog.records if getattr(r, "kind", "") == "c1.warm_start.cleared"
]
assert len(info_records) == 1
assert info_records[0].levelname == "INFO"
def test_clear_is_idempotent(self, tmp_path: Path) -> None:
# Arrange
store = JsonSidecarWarmStartHintStore(tmp_path, calibration_id=_DEFAULT_CALIBRATION_ID)
# Act + Assert — first clear with no files MUST NOT raise
store.clear()
store.clear()
class TestStoreAc10Atomicity:
def test_kill_mid_save_leaves_prior_hint_loadable(self, tmp_path: Path) -> None:
"""Simulate a crash mid-save by writing a temp file but never renaming.
``Sha256Sidecar.write_atomic_and_sidecar`` uses
``atomicwrites.atomic_write`` (temp-file + ``os.replace``), so
a mid-write crash never leaves a partial `c1_warm_start.json`.
We model the "process killed mid-save" scenario by leaving a
stray temp file alongside an already-committed prior hint;
:meth:`load` must still return the prior valid hint.
"""
# Arrange — first save commits a known hint
store = JsonSidecarWarmStartHintStore(tmp_path, calibration_id=_DEFAULT_CALIBRATION_ID)
prior = _make_hint(yaw_deg=0.0)
store.save(prior, pre_reboot_covariance_norm=0.1)
# Simulate a half-written temp file from a "killed" second save.
# atomicwrites uses a temp file with a `.<name>.<rand>` prefix.
stray = tmp_path / f".{HINT_FILENAME}.partial-write-stray"
stray.write_bytes(b"this-is-half-written-junk")
# Act
loaded = store.load()
# Assert — the prior valid hint loads despite the stray temp file.
assert loaded is not None
np.testing.assert_array_almost_equal(
loaded.pose.body_T_world.matrix(), prior.body_T_world.matrix()
)
# The stray file was NOT consumed as the hint.
assert stray.exists()
class TestStoreLifecycle:
def test_load_returns_none_when_no_file(self, tmp_path: Path) -> None:
# Arrange
store = JsonSidecarWarmStartHintStore(tmp_path, calibration_id=_DEFAULT_CALIBRATION_ID)
# Act + Assert
assert store.load() is None
def test_default_impl_satisfies_protocol(self, tmp_path: Path) -> None:
# Arrange
store = JsonSidecarWarmStartHintStore(tmp_path, calibration_id=_DEFAULT_CALIBRATION_ID)
# Assert — runtime_checkable Protocol conformance
assert isinstance(store, WarmStartHintStore)
class TestStoreNfrPerf:
def test_nfr_perf_save_p99_under_50ms(self, tmp_path: Path) -> None:
# Arrange
store = JsonSidecarWarmStartHintStore(tmp_path, calibration_id=_DEFAULT_CALIBRATION_ID)
hint = _make_hint()
n = 200 # bounded — full perf bench lives in C1-PT-01 Tier-2
# Act
timings_ms = []
for _ in range(n):
t0 = time.perf_counter()
store.save(hint, pre_reboot_covariance_norm=0.1)
timings_ms.append((time.perf_counter() - t0) * 1000.0)
# Assert — p99 under 50ms; this is a smoke-budget on dev hardware,
# the production budget is on Tier-2 NVMe per the task NFR.
p99 = float(np.percentile(timings_ms, 99))
assert p99 < 50.0, f"save p99 = {p99:.2f}ms exceeds 50ms NFR budget"
def test_nfr_perf_load_p99_under_20ms(self, tmp_path: Path) -> None:
# Arrange
store = JsonSidecarWarmStartHintStore(tmp_path, calibration_id=_DEFAULT_CALIBRATION_ID)
store.save(_make_hint(), pre_reboot_covariance_norm=0.1)
n = 200
# Act
timings_ms = []
for _ in range(n):
t0 = time.perf_counter()
loaded = store.load()
timings_ms.append((time.perf_counter() - t0) * 1000.0)
assert loaded is not None # sanity
# Assert
p99 = float(np.percentile(timings_ms, 99))
assert p99 < 20.0, f"load p99 = {p99:.2f}ms exceeds 20ms NFR budget"
# ===========================================================================
# Wiring tests — AC-3 .. AC-8, NFR-no-crash.
@pytest.fixture
def fdr_sink() -> FakeFdrSink:
return FakeFdrSink(producer_id=WARM_START_PRODUCER_ID)
@pytest.fixture
def fake_strategy() -> _FakeVioStrategy:
return _FakeVioStrategy()
@pytest.fixture
def store(tmp_path: Path) -> JsonSidecarWarmStartHintStore:
return JsonSidecarWarmStartHintStore(tmp_path, calibration_id=_DEFAULT_CALIBRATION_ID)
class TestWiringAc3ColdStart:
def test_cold_start_does_not_invoke_reset(
self,
fake_strategy: _FakeVioStrategy,
store: JsonSidecarWarmStartHintStore,
fdr_sink: FakeFdrSink,
caplog: Any,
) -> None:
# Arrange
wired = _make_wired(fake_strategy, store)
# Act
with caplog.at_level(logging.INFO):
applied = prime_warm_start_from_disk(wired, store, fdr_client=fdr_sink)
# Assert
assert applied is False
assert fake_strategy.reset_calls == []
info_records = [
r
for r in caplog.records
if getattr(r, "kind", "") == "c1.warm_start.cold_start_no_hint"
]
assert len(info_records) == 1
cold_records = [r for r in fdr_sink.records if r.kind == "vio.warm_start"]
assert len(cold_records) == 1
assert cold_records[0].payload["source"] == "cold_start_no_hint"
assert cold_records[0].payload["bias_norm"] is None
class TestWiringAc4F8Reboot:
def test_f8_reboot_loads_hint_calls_reset_emits_fdr(
self,
fake_strategy: _FakeVioStrategy,
store: JsonSidecarWarmStartHintStore,
fdr_sink: FakeFdrSink,
caplog: Any,
) -> None:
# Arrange — seed a hint on disk
prior_hint = _make_hint(yaw_deg=10.0)
store.save(prior_hint, pre_reboot_covariance_norm=0.0625)
wired = _make_wired(fake_strategy, store)
# Act
with caplog.at_level(logging.INFO):
applied = prime_warm_start_from_disk(wired, store, fdr_client=fdr_sink)
# Assert
assert applied is True
assert len(fake_strategy.reset_calls) == 1
np.testing.assert_array_almost_equal(
fake_strategy.reset_calls[0].hint.body_T_world.matrix(),
prior_hint.body_T_world.matrix(),
)
# AC-8 baseline floor installed
assert wired.baseline_floor == pytest.approx(0.0625)
info_records = [
r
for r in caplog.records
if getattr(r, "kind", "") == "c1.warm_start.f8_reboot_disk"
]
assert len(info_records) == 1
fdr_records = [r for r in fdr_sink.records if r.kind == "vio.warm_start"]
assert len(fdr_records) == 1
assert fdr_records[0].payload["source"] == "f8_reboot_disk"
assert fdr_records[0].payload["pre_reboot_covariance_norm"] == pytest.approx(0.0625)
assert fdr_records[0].payload["bias_norm"] is not None
assert fdr_records[0].payload["staleness_ns"] is not None
class TestWiringAc5F2Takeoff:
def test_f2_takeoff_fetches_fc_calls_reset_persists(
self,
fake_strategy: _FakeVioStrategy,
store: JsonSidecarWarmStartHintStore,
fdr_sink: FakeFdrSink,
caplog: Any,
) -> None:
# Arrange
fc_hint = _make_hint(yaw_deg=20.0)
source = _FakeFcSource(hint=fc_hint)
wired = _make_wired(fake_strategy, store)
# Act
with caplog.at_level(logging.INFO):
applied = prime_warm_start_from_fc(wired, source, store, fdr_client=fdr_sink)
# Assert
assert applied is True
assert source.fetch_call_count == 1
assert len(fake_strategy.reset_calls) == 1
np.testing.assert_array_almost_equal(
fake_strategy.reset_calls[0].hint.body_T_world.matrix(),
fc_hint.body_T_world.matrix(),
)
# F2 path persists the hint so a subsequent F8 reboot can recover it.
loaded = store.load()
assert loaded is not None
np.testing.assert_array_almost_equal(
loaded.pose.body_T_world.matrix(), fc_hint.body_T_world.matrix()
)
# AC-8 floor is NOT installed on the F2 path (no pre-reboot baseline).
assert wired.baseline_floor == pytest.approx(0.0)
info_records = [
r
for r in caplog.records
if getattr(r, "kind", "") == "c1.warm_start.f2_takeoff_fc"
]
assert len(info_records) == 1
fdr_records = [r for r in fdr_sink.records if r.kind == "vio.warm_start"]
assert len(fdr_records) == 1
assert fdr_records[0].payload["source"] == "f2_takeoff_fc"
class TestWiringAc6PerFrameSave:
def test_per_frame_save_respects_period(
self,
fake_strategy: _FakeVioStrategy,
store: JsonSidecarWarmStartHintStore,
tmp_path: Path,
) -> None:
# Arrange — period = 5; 12 frames → save fires at frames 5 and 10 only
wired = _make_wired(fake_strategy, store, save_period=5)
# Act
outputs = _drive_frames(wired, 12)
# Assert
assert len(outputs) == 12
# The on-disk hint should reflect frame 10's emit, not frame 12's.
loaded = store.load()
assert loaded is not None
assert loaded.pose.captured_at_ns == outputs[9].emitted_at_ns
def test_save_period_one_saves_every_frame(
self,
fake_strategy: _FakeVioStrategy,
store: JsonSidecarWarmStartHintStore,
) -> None:
# Arrange
wired = _make_wired(fake_strategy, store, save_period=1)
# Act
outputs = _drive_frames(wired, 3)
# Assert — last save reflects the most recent frame
loaded = store.load()
assert loaded is not None
assert loaded.pose.captured_at_ns == outputs[-1].emitted_at_ns
class TestWiringAc7PostResetInflation:
def test_first_n_frames_inflated_then_unmodified(
self,
fake_strategy: _FakeVioStrategy,
store: JsonSidecarWarmStartHintStore,
) -> None:
# Arrange — strategy emits cov of Frobenius norm 1.0, factor=2.0,
# window=5 frames. Save period large enough that no save fires
# in the inflation window for cleaner assertion.
emit_cov = np.eye(6, dtype=np.float64) * (1.0 / np.sqrt(6)) # ||·||_F = 1.0
fake_strategy.set_emit_covariance(emit_cov)
wired = _make_wired(
fake_strategy,
store,
warm_start_max_frames=5,
inflation_factor=2.0,
save_period=100,
)
wired.reset_to_warm_start(_make_hint())
# Act — drive 6 frames; the first 5 inflated, the 6th unmodified.
outputs = _drive_frames(wired, 6)
# Assert
for i in range(5):
norm = float(np.linalg.norm(outputs[i].pose_covariance_6x6, ord="fro"))
assert norm == pytest.approx(2.0, abs=1e-9), (
f"Frame {i + 1}: expected inflated norm 2.0, got {norm}"
)
norm6 = float(np.linalg.norm(outputs[5].pose_covariance_6x6, ord="fro"))
assert norm6 == pytest.approx(1.0, abs=1e-9)
def test_no_inflation_when_no_reset_was_called(
self,
fake_strategy: _FakeVioStrategy,
store: JsonSidecarWarmStartHintStore,
) -> None:
# Arrange — the wrapper without any reset call should pass through.
emit_cov = np.eye(6, dtype=np.float64) * (1.0 / np.sqrt(6))
fake_strategy.set_emit_covariance(emit_cov)
wired = _make_wired(
fake_strategy, store, save_period=100, warm_start_max_frames=5, inflation_factor=2.0
)
# Act
out = wired.process_frame(_make_frame(), _make_imu_window(), _make_calibration())
# Assert
norm = float(np.linalg.norm(out.pose_covariance_6x6, ord="fro"))
assert norm == pytest.approx(1.0, abs=1e-9)
class TestWiringAc8CovarianceFloor:
def test_post_reboot_floor_enforced_above_inflation_alone(
self,
fake_strategy: _FakeVioStrategy,
store: JsonSidecarWarmStartHintStore,
fdr_sink: FakeFdrSink,
) -> None:
# Arrange — pre-reboot baseline X = 5.0; strategy emits norm 1.0
# so 2× inflation alone is only 2.0, well below X. Floor must
# bump every output up to ≥ 5.0.
baseline_x = 5.0
store.save(_make_hint(), pre_reboot_covariance_norm=baseline_x)
emit_cov = np.eye(6, dtype=np.float64) * (1.0 / np.sqrt(6))
fake_strategy.set_emit_covariance(emit_cov)
wired = _make_wired(
fake_strategy,
store,
warm_start_max_frames=5,
inflation_factor=2.0,
save_period=100,
)
# Act — F8 prime installs the floor, then 5 frames flow through
applied = prime_warm_start_from_disk(wired, store, fdr_client=fdr_sink)
assert applied is True
outputs = _drive_frames(wired, 5)
# Assert — every post-reset frame's emitted norm ≥ X
for i, out in enumerate(outputs):
norm = float(np.linalg.norm(out.pose_covariance_6x6, ord="fro"))
assert norm >= baseline_x - 1e-9, (
f"AC-8 floor breached on frame {i + 1}: norm {norm} < baseline {baseline_x}"
)
def test_post_reboot_floor_does_not_lower_when_inflation_alone_already_above(
self,
fake_strategy: _FakeVioStrategy,
store: JsonSidecarWarmStartHintStore,
fdr_sink: FakeFdrSink,
) -> None:
# Arrange — baseline X = 0.5; strategy emits norm 1.0; inflation 2.0
# alone gives 2.0 which already exceeds X. Floor must NOT scale down.
baseline_x = 0.5
store.save(_make_hint(), pre_reboot_covariance_norm=baseline_x)
emit_cov = np.eye(6, dtype=np.float64) * (1.0 / np.sqrt(6))
fake_strategy.set_emit_covariance(emit_cov)
wired = _make_wired(fake_strategy, store, save_period=100)
# Act
applied = prime_warm_start_from_disk(wired, store, fdr_client=fdr_sink)
assert applied is True
outputs = _drive_frames(wired, 1)
# Assert — norm is the inflated value (2.0), NOT the baseline (0.5)
norm = float(np.linalg.norm(outputs[0].pose_covariance_6x6, ord="fro"))
assert norm == pytest.approx(2.0, abs=1e-9)
class TestWiringNfrNoCrash:
def test_fc_source_raising_does_not_crash(
self,
fake_strategy: _FakeVioStrategy,
store: JsonSidecarWarmStartHintStore,
fdr_sink: FakeFdrSink,
caplog: Any,
) -> None:
# Arrange
source = _FakeFcSource(raise_with=RuntimeError("FC link down"))
wired = _make_wired(fake_strategy, store)
# Act
with caplog.at_level(logging.WARNING):
applied = prime_warm_start_from_fc(wired, source, store, fdr_client=fdr_sink)
# Assert — degrades to cold-start; process keeps running
assert applied is False
assert fake_strategy.reset_calls == []
warn_records = [
r
for r in caplog.records
if getattr(r, "kind", "") == "c1.warm_start.f2_takeoff_fc_unavailable"
]
assert len(warn_records) == 1
def test_fc_source_returning_none_does_not_crash(
self,
fake_strategy: _FakeVioStrategy,
store: JsonSidecarWarmStartHintStore,
fdr_sink: FakeFdrSink,
caplog: Any,
) -> None:
# Arrange
source = _FakeFcSource(hint=None)
wired = _make_wired(fake_strategy, store)
# Act
with caplog.at_level(logging.WARNING):
applied = prime_warm_start_from_fc(wired, source, store, fdr_client=fdr_sink)
# Assert
assert applied is False
assert fake_strategy.reset_calls == []
def test_per_frame_save_failure_does_not_crash(
self,
fake_strategy: _FakeVioStrategy,
caplog: Any,
) -> None:
# Arrange — a store whose save always raises
class _BoomStore:
def save(self, hint: WarmStartPose, *, pre_reboot_covariance_norm: float) -> None:
raise OSError("disk full")
def load(self) -> LoadedWarmStartHint | None:
return None
def clear(self) -> None:
return None
wired = _make_wired(fake_strategy, _BoomStore(), save_period=1) # type: ignore[arg-type]
# Act
with caplog.at_level(logging.ERROR):
out = wired.process_frame(_make_frame(), _make_imu_window(), _make_calibration())
# Assert — frame still emitted, error logged, no exception escapes
assert out is not None
err_records = [
r for r in caplog.records if getattr(r, "kind", "") == "c1.warm_start.save_failed"
]
assert len(err_records) == 1
def test_inner_strategy_reset_failure_does_not_crash_prime(
self,
fake_strategy: _FakeVioStrategy,
store: JsonSidecarWarmStartHintStore,
fdr_sink: FakeFdrSink,
caplog: Any,
) -> None:
# Arrange
store.save(_make_hint(), pre_reboot_covariance_norm=0.1)
fake_strategy.script_reset_failure(RuntimeError("native bridge boom"))
wired = _make_wired(fake_strategy, store)
# Act
with caplog.at_level(logging.ERROR):
applied = prime_warm_start_from_disk(wired, store, fdr_client=fdr_sink)
# Assert
assert applied is False
err_records = [
r for r in caplog.records if getattr(r, "kind", "") == "c1.warm_start.reset_failed"
]
assert len(err_records) == 1
class TestWiringForwarders:
def test_health_snapshot_forwards_to_inner(
self, fake_strategy: _FakeVioStrategy, store: JsonSidecarWarmStartHintStore
) -> None:
# Arrange
wired = _make_wired(fake_strategy, store)
# Assert
assert wired.health_snapshot().state == VioState.TRACKING
def test_current_strategy_label_forwards_to_inner(
self, fake_strategy: _FakeVioStrategy, store: JsonSidecarWarmStartHintStore
) -> None:
# Arrange
wired = _make_wired(fake_strategy, store)
# Assert
assert wired.current_strategy_label() == "klt_ransac"
def test_wrapper_constructor_rejects_inflation_factor_le_one(
self, fake_strategy: _FakeVioStrategy, store: JsonSidecarWarmStartHintStore
) -> None:
# Arrange + Act + Assert
with pytest.raises(ValueError):
WarmStartWiredStrategy(
inner=fake_strategy,
store=store,
warm_start_max_frames=5,
post_reset_covariance_inflation_factor=1.0,
warm_start_save_period_frames=5,
)
# ===========================================================================
# Hint-schema sanity guard.
class TestHintSchemaConstants:
def test_hint_schema_version_is_v1(self) -> None:
# Assert
assert HINT_SCHEMA_VERSION == 1
def test_hint_filename_is_canonical(self) -> None:
# Assert
assert HINT_FILENAME == "c1_warm_start.json"
def test_warm_start_fc_source_is_runtime_checkable(self) -> None:
# Arrange — local fake conforms to the runtime_checkable Protocol
source = _FakeFcSource(hint=_make_hint())
# Assert
assert isinstance(source, WarmStartFcSource)