[AZ-595] Batch 75: sitl_observer FDR-replay + scenario probe cleanup

Implement all 11 `sitl_observer` public surfaces as an offline
FDR-replay strategy (reads JSON fixtures under `${E2E_SITL_REPLAY_DIR}`
instead of live pymavlink/yamspy). Replace 12 per-scenario
`_harness_helpers_implemented` probes with one shared session-scoped
`sitl_replay_ready` fixture in `e2e/tests/conftest.py`.

Net: -636 LoC of duplicated scenario gating, +17 LoC shared fixture,
+38 new unit tests (596 total, up from 558). Includes K=3 cumulative
review for batches 73-75 (PASS).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-17 09:00:55 +03:00
parent 1d260f7e41
commit 43fdef1aac
23 changed files with 1485 additions and 639 deletions
+17
View File
@@ -40,3 +40,20 @@ _bootstrap_runner_path()
# regardless of which conftest it discovers first. Star imports here are
# the documented pytest pattern for conftest layering.
from runner.conftest import * # noqa: F401,F403,E402 — pytest conftest re-export
import pytest # noqa: E402
from runner.helpers import sitl_observer # noqa: E402
@pytest.fixture(scope="session")
def sitl_replay_ready() -> bool:
"""True iff the FDR-replay fixture directory is configured + present.
AZ-595 replaces the per-scenario `_harness_helpers_implemented` probes
that passed `/tmp/non-existent` to each helper and inspected the
exception type. Scenarios should now consult this fixture and skip
cleanly when the SITL replay fixtures haven't been prepared (the
typical case during local unit runs that only exercise the helpers).
"""
return sitl_observer.replay_dir_available()
@@ -22,40 +22,6 @@ from fixtures.injectors.outlier import OutlierInjectionReport
from runner.helpers import outlier_tolerance_evaluator as ote
@pytest.fixture(scope="module")
def _harness_helpers_implemented() -> bool:
from runner.helpers import fdr_reader, imu_replay
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_video(Path("/tmp/non-existent.mp4"))
except NotImplementedError:
return False
try:
list(fdr_reader.iter_records(Path("/tmp/non-existent")))
except NotImplementedError:
return False
try:
imu_replay.ImuReplayer(emitter=_NullImuEmitter()).replay(Path("/tmp/non-existent.csv")) # type: ignore[arg-type]
except NotImplementedError:
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
class _NullImuEmitter:
def emit(self, sample: object) -> None:
return None
@pytest.mark.parametrize(
"outlier_injection_derkachi",
[{"density": "medium", "seed": 0}],
@@ -69,14 +35,13 @@ def test_ft_n_01_outlier_tolerance(
evidence_dir, # type: ignore[no-untyped-def]
run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
_harness_helpers_implemented: bool,
sitl_replay_ready: bool,
) -> None:
if not _harness_helpers_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-N-01 full replay requires runner.helpers.{frame_source_replay,"
"fdr_reader,imu_replay} — currently AZ-441 / AZ-407 leftovers. "
"AC-1/AC-2/AC-3 helper logic covered by "
"e2e/_unit_tests/helpers/test_outlier_tolerance_evaluator.py."
"FT-N-01 full replay requires `E2E_SITL_REPLAY_DIR` to point at a "
"prepared SITL replay fixture (AZ-595). AC-1/AC-2/AC-3 helper logic "
"covered by e2e/_unit_tests/helpers/test_outlier_tolerance_evaluator.py."
)
from runner.helpers import fdr_reader
@@ -35,40 +35,6 @@ DERKACHI_IMU_CSV = DERKACHI_DIR / "data_imu.csv"
DERKACHI_MP4 = DERKACHI_DIR / "flight_derkachi.mp4"
@pytest.fixture(scope="module")
def _harness_helpers_implemented() -> bool:
from runner.helpers import fdr_reader, imu_replay
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_video(Path("/tmp/non-existent.mp4"))
except NotImplementedError:
return False
try:
list(fdr_reader.iter_records(Path("/tmp/non-existent")))
except NotImplementedError:
return False
try:
imu_replay.ImuReplayer(emitter=_NullImuEmitter()).replay(Path("/tmp/non-existent.csv")) # type: ignore[arg-type]
except NotImplementedError:
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
class _NullImuEmitter:
def emit(self, sample: object) -> None:
return None
@pytest.mark.traces_to("AC-3.2,AC-1,AC-2,AC-3,AC-7")
def test_ft_n_02_sharp_turn_failure(
fc_adapter: str,
@@ -76,14 +42,13 @@ def test_ft_n_02_sharp_turn_failure(
evidence_dir, # type: ignore[no-untyped-def]
run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
_harness_helpers_implemented: bool,
sitl_replay_ready: bool,
) -> None:
if not _harness_helpers_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-N-02 full replay requires runner.helpers.{frame_source_replay,"
"imu_replay,fdr_reader} — currently AZ-441 / AZ-407 leftovers. "
"AC-2/AC-3 helper logic covered by "
"e2e/_unit_tests/helpers/test_sharp_turn_detector.py."
"FT-N-02 full replay requires `E2E_SITL_REPLAY_DIR` to point at a "
"prepared SITL replay fixture (AZ-595). AC-2/AC-3 helper logic "
"covered by e2e/_unit_tests/helpers/test_sharp_turn_detector.py."
)
from runner.helpers import fdr_reader
@@ -31,39 +31,6 @@ DERKACHI_DIR = (
DERKACHI_MP4 = DERKACHI_DIR / "flight_derkachi.mp4"
@pytest.fixture(scope="module")
def _harness_helpers_implemented() -> bool:
from runner.helpers import fdr_reader, mavproxy_tlog_reader, sitl_observer
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_video(Path("/tmp/non-existent.mp4"))
except NotImplementedError:
return False
try:
list(fdr_reader.iter_records(Path("/tmp/non-existent")))
except NotImplementedError:
return False
try:
list(mavproxy_tlog_reader.iter_messages(Path("/tmp/non-existent.tlog")))
except NotImplementedError:
return False
try:
sitl_observer.read_ekf_divergence_events() # type: ignore[attr-defined]
except (AttributeError, NotImplementedError):
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
@pytest.mark.traces_to("AC-3.4,AC-1,AC-2,AC-3,AC-4,AC-5")
def test_ft_n_03_outage_reloc(
fc_adapter: str,
@@ -71,13 +38,12 @@ def test_ft_n_03_outage_reloc(
evidence_dir, # type: ignore[no-untyped-def]
run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
_harness_helpers_implemented: bool,
sitl_replay_ready: bool,
) -> None:
if not _harness_helpers_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-N-03 full replay requires runner.helpers.{frame_source_replay,"
"fdr_reader,mavproxy_tlog_reader,sitl_observer} — currently "
"AZ-441 / AZ-407 / AZ-416 leftovers. AC-1..AC-4 evaluator logic "
"FT-N-03 full replay requires `E2E_SITL_REPLAY_DIR` to point at a "
"prepared SITL replay fixture (AZ-595). AC-1..AC-4 evaluator logic "
"covered by e2e/_unit_tests/helpers/test_outage_request_evaluator.py."
)
@@ -26,40 +26,6 @@ from runner.helpers import blackout_spoof_evaluator as bse
_WINDOW_LADDER_S = (5.0, 15.0, 35.0)
@pytest.fixture(scope="module")
def _harness_helpers_implemented() -> bool:
from runner.helpers import fdr_reader, mavproxy_tlog_reader, sitl_observer
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_video(Path("/tmp/non-existent.mp4"))
except NotImplementedError:
return False
try:
list(fdr_reader.iter_records(Path("/tmp/non-existent")))
except NotImplementedError:
return False
try:
list(mavproxy_tlog_reader.iter_messages(Path("/tmp/non-existent.tlog")))
except NotImplementedError:
return False
try:
sitl_observer.read_gps_health_samples() # type: ignore[attr-defined]
sitl_observer.read_consistency_check_events() # type: ignore[attr-defined]
except (AttributeError, NotImplementedError):
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
@pytest.mark.parametrize(
"blackout_spoof_derkachi",
[{"window_seconds": s, "seed": 0} for s in _WINDOW_LADDER_S],
@@ -76,14 +42,14 @@ def test_ft_n_04_blackout_spoof(
evidence_dir, # type: ignore[no-untyped-def]
run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
_harness_helpers_implemented: bool,
sitl_replay_ready: bool,
) -> None:
if not _harness_helpers_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-N-04 full replay requires runner.helpers.{frame_source_replay,"
"fdr_reader,mavproxy_tlog_reader,sitl_observer,fc_proxy} — currently "
"AZ-441 / AZ-407 / AZ-416 leftovers. AC-1..AC-8 evaluator logic "
"covered by e2e/_unit_tests/helpers/test_blackout_spoof_evaluator.py."
"FT-N-04 full replay requires `E2E_SITL_REPLAY_DIR` to point at a "
"prepared SITL replay fixture (AZ-595) AND a runtime fc_proxy "
"driver. AC-1..AC-8 evaluator logic covered by "
"e2e/_unit_tests/helpers/test_blackout_spoof_evaluator.py."
)
from runner.helpers import fdr_reader, mavproxy_tlog_reader, sitl_observer
@@ -25,9 +25,10 @@ What this file does NOT own:
* The SITL message receipt → ``runner.helpers.sitl_observer`` (stub;
owned by AZ-416/AZ-417) — skip-gated.
When both upstream helpers land, this file's runtime path activates
automatically — the skip is keyed off the ``NotImplementedError`` from
the helper imports, not off a hard-coded marker.
When ``E2E_SITL_REPLAY_DIR`` is set and points at a prepared SITL
replay fixture, this file's runtime path activates automatically; until
then the scenario skips via the shared `sitl_replay_ready` fixture
(AZ-595).
"""
from __future__ import annotations
@@ -43,36 +44,6 @@ GT_CSV = Path(__file__).resolve().parents[3] / "_docs" / "00_problem" / "input_d
STILL_IMAGES_DIR = GT_CSV.parent
@pytest.fixture(scope="module")
def _harness_helpers_implemented() -> bool:
"""True iff the upstream replay + SITL-observation helpers are real.
Same auto-detect pattern as FT-P-02 / FT-P-03 — the gate flips when
the helpers stop raising NotImplementedError, so no marker churn.
"""
from runner.helpers import frame_source_replay, sitl_observer
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_image_directory(Path("/tmp/non-existent"))
except NotImplementedError:
return False
try:
sitl_observer.get_observer(fc_adapter="ardupilot", host="sitl-ardupilot")
except NotImplementedError:
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
def _ft_p_01_image_paths() -> list[Path]:
"""The 60 AD0000NN.jpg images, sorted lexicographically (AD000001..AD000060)."""
return sorted(STILL_IMAGES_DIR.glob("AD??????.jpg"))
@@ -85,7 +56,7 @@ def test_ft_p_01_still_image_accuracy(
evidence_dir, # type: ignore[no-untyped-def]
run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
_harness_helpers_implemented: bool,
sitl_replay_ready: bool,
) -> None:
"""Full FT-P-01 scenario (AC-1.1, AC-1.2).
@@ -95,11 +66,11 @@ def test_ft_p_01_still_image_accuracy(
AC-4: per-image timeout → ``error_m=∞``; aggregate continues.
AC-5: parametrized across ``(fc_adapter, vio_strategy)`` (4 variants).
"""
if not _harness_helpers_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-P-01 still-image push requires runner.helpers.{frame_source_replay,"
"sitl_observer} — currently AZ-441 + AZ-416/AZ-417 leftovers. "
"Pure-logic ACs covered by e2e/_unit_tests/helpers/test_accuracy_evaluator.py."
"FT-P-01 still-image push requires `E2E_SITL_REPLAY_DIR` to point "
"at a prepared SITL replay fixture (AZ-595). Pure-logic ACs "
"covered by e2e/_unit_tests/helpers/test_accuracy_evaluator.py."
)
from runner.helpers import frame_source_replay, sitl_observer
@@ -23,16 +23,16 @@ What this file does NOT own:
(still a stub; AZ-408 was about the synthetic-injection injectors,
not the video replayer); the scenario is marked
``@pytest.mark.deferred_ac(reason=...)`` until that helper lands.
* The FDR-archive iteration → ``runner.helpers.fdr_reader`` (owned by
AZ-441); same skip gate.
* The FDR-archive iteration → ``runner.helpers.fdr_reader`` (AZ-594,
landed in batch 74) — the scenario still depends on a prepared SITL
replay fixture (AZ-595) that produces the per-run FDR archive.
* The MAVLink ``GLOBAL_POSITION_INT`` GT replay → handled by the
``imu_replay`` helper which currently raises NotImplementedError
(owned by AZ-407 in spec, but the helper file was not touched by
the AZ-407 batch).
``imu_replay`` helper (AZ-594, landed in batch 74).
When all three upstream helpers land, this file's runtime path activates
automatically — the skip is keyed off the ``NotImplementedError`` from
the helper imports, not off a hard-coded marker.
When ``E2E_SITL_REPLAY_DIR`` is set and points at a prepared SITL
replay fixture, this file's runtime path activates automatically; until
then the scenario skips via the shared `sitl_replay_ready` fixture
(AZ-595).
"""
from __future__ import annotations
@@ -44,53 +44,6 @@ import pytest
from runner.helpers import anchor_pair_detector as apd
@pytest.fixture(scope="module")
def _harness_helpers_implemented() -> bool:
"""True iff every upstream helper FT-P-02 needs has a real impl.
Used to gate the full-replay scenarios. Helper-level NotImplementedError
is the signal — we don't hard-code a "deferred until task X" marker
because then a developer who lands the helper would have to also
remember to flip the marker. The auto-detect pattern is also what
other downstream scenarios will reuse.
"""
from runner.helpers import fdr_reader, frame_source_replay, imu_replay
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
# The cheapest sentinel for each helper:
# - FrameSourceReplayer.replay_video raises NotImplementedError
# - fdr_reader.iter_records raises NotImplementedError
# - ImuReplayer.replay raises NotImplementedError
# We check by inspecting __doc__ / source rather than calling, so
# the gate stays cheap.
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_video(Path("/tmp/non-existent.mp4"))
except NotImplementedError:
return False
try:
list(fdr_reader.iter_records(Path("/tmp/non-existent")))
except NotImplementedError:
return False
try:
imu_replay.ImuReplayer(emitter=_NullImuEmitter()).replay(Path("/tmp/non-existent.csv")) # type: ignore[arg-type]
except NotImplementedError:
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
class _NullImuEmitter:
def emit(self, sample: object) -> None:
return None
@pytest.mark.traces_to("AC-1.3,AC-1,AC-2,AC-3,AC-4,AC-5")
def test_ft_p_02_derkachi_drift(
fc_adapter: str,
@@ -98,7 +51,7 @@ def test_ft_p_02_derkachi_drift(
evidence_dir, # type: ignore[no-untyped-def]
run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
_harness_helpers_implemented: bool,
sitl_replay_ready: bool,
) -> None:
"""Full FT-P-02 scenario (AC-1.3). See module docstring.
@@ -110,11 +63,11 @@ def test_ft_p_02_derkachi_drift(
AC-4: bin medians monotonic with age — covered by check_monotonic().
AC-5: parametrized across (fc_adapter, vio_strategy).
"""
if not _harness_helpers_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-P-02 full replay requires runner.helpers.{frame_source_replay,"
"fdr_reader,imu_replay} — currently AZ-441 / AZ-407 leftovers. "
"Pure-logic ACs covered by e2e/_unit_tests/helpers/test_anchor_pair_detector.py."
"FT-P-02 full replay requires `E2E_SITL_REPLAY_DIR` to point at a "
"prepared SITL replay fixture (AZ-595). Pure-logic ACs covered by "
"e2e/_unit_tests/helpers/test_anchor_pair_detector.py."
)
# Once the helpers land, the body below activates. We keep it
@@ -35,53 +35,20 @@ import pytest
from runner.helpers import estimate_schema
@pytest.fixture(scope="module")
def _harness_helpers_implemented() -> bool:
"""Same gate as FT-P-02: are frame replay + SITL observer + sidechannel
decoders all real? If not, skip the docker-bound runtime path.
"""
from runner.helpers import frame_source_replay, mavproxy_tlog_reader, sitl_observer
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_image_directory(Path("/tmp/non-existent"))
except NotImplementedError:
return False
try:
sitl_observer.get_observer("ardupilot", "test-host")
except NotImplementedError:
return False
try:
list(mavproxy_tlog_reader.iter_messages(Path("/tmp/non-existent.tlog")))
except NotImplementedError:
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
@pytest.mark.traces_to("AC-1.4,AC-4.3")
def test_schema_and_source_label(
fc_adapter: str,
vio_strategy: str,
evidence_dir, # type: ignore[no-untyped-def]
nfr_recorder, # type: ignore[no-untyped-def]
_harness_helpers_implemented: bool,
sitl_replay_ready: bool,
) -> None:
"""FT-P-03: schema completeness (AC-1) + source-label set containment (AC-2)."""
if not _harness_helpers_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-P-03 single-image push requires runner.helpers.{frame_source_replay,"
"sitl_observer,mavproxy_tlog_reader} — currently pending AZ-407 / "
"AZ-416/417 leftovers. Pure-logic ACs covered by "
"e2e/_unit_tests/helpers/test_estimate_schema.py."
"FT-P-03 single-image push requires `E2E_SITL_REPLAY_DIR` to point "
"at a prepared SITL replay fixture (AZ-595). Pure-logic ACs "
"covered by e2e/_unit_tests/helpers/test_estimate_schema.py."
)
record, source_label = _push_single_image_and_observe(fc_adapter, vio_strategy)
@@ -109,13 +76,14 @@ def test_wgs84_coordinate_range(
vio_strategy: str,
evidence_dir, # type: ignore[no-untyped-def]
nfr_recorder, # type: ignore[no-untyped-def]
_harness_helpers_implemented: bool,
sitl_replay_ready: bool,
) -> None:
"""FT-P-14: decoded lat/lon inside WGS84 bounds (AC-3)."""
if not _harness_helpers_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-P-14 single-image push requires the same upstream helpers as FT-P-03. "
"Pure-logic AC covered by e2e/_unit_tests/helpers/test_estimate_schema.py."
"FT-P-14 single-image push requires `E2E_SITL_REPLAY_DIR` to point "
"at a prepared SITL replay fixture (AZ-595). Pure-logic AC covered "
"by e2e/_unit_tests/helpers/test_estimate_schema.py."
)
record, _label = _push_single_image_and_observe(fc_adapter, vio_strategy)
@@ -141,7 +109,7 @@ def _push_single_image_and_observe(fc_adapter: str, vio_strategy: str): # type:
"""Push AD000001.jpg through the SUT and return (outbound_record, source_label).
Stub until runner.helpers.{frame_source_replay,sitl_observer,mavproxy_tlog_reader}
land; the scenario test's skip gate (``_harness_helpers_implemented``)
land; the scenario test's `sitl_replay_ready` skip gate (AZ-595)
keeps this from executing prematurely.
"""
raise NotImplementedError(
@@ -29,9 +29,10 @@ What this file does NOT own:
* The FDR-archive iteration → ``runner.helpers.fdr_reader`` (stub;
AZ-441) — skip-gated.
When all three upstream helpers land, this file's runtime path activates
automatically — the skip is keyed off the ``NotImplementedError`` from
the helper imports, not off a hard-coded marker.
When ``E2E_SITL_REPLAY_DIR`` is set and points at a prepared SITL
replay fixture, this file's runtime path activates automatically; until
then the scenario skips via the shared `sitl_replay_ready` fixture
(AZ-595).
"""
from __future__ import annotations
@@ -53,41 +54,6 @@ DERKACHI_IMU_CSV = DERKACHI_DIR / "data_imu.csv"
DERKACHI_MP4 = DERKACHI_DIR / "flight_derkachi.mp4"
@pytest.fixture(scope="module")
def _harness_helpers_implemented() -> bool:
"""True iff every upstream helper FT-P-04 needs has a real impl."""
from runner.helpers import fdr_reader, frame_source_replay, imu_replay
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_video(Path("/tmp/non-existent.mp4"))
except NotImplementedError:
return False
try:
list(fdr_reader.iter_records(Path("/tmp/non-existent")))
except NotImplementedError:
return False
try:
imu_replay.ImuReplayer(emitter=_NullImuEmitter()).replay(Path("/tmp/non-existent.csv")) # type: ignore[arg-type]
except NotImplementedError:
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
class _NullImuEmitter:
def emit(self, sample: object) -> None:
return None
@pytest.mark.traces_to("AC-2.1a,AC-1,AC-2,AC-3,AC-4")
def test_ft_p_04_derkachi_f2f_registration(
fc_adapter: str,
@@ -95,7 +61,7 @@ def test_ft_p_04_derkachi_f2f_registration(
evidence_dir, # type: ignore[no-untyped-def]
run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
_harness_helpers_implemented: bool,
sitl_replay_ready: bool,
) -> None:
"""Full FT-P-04 scenario.
@@ -105,11 +71,11 @@ def test_ft_p_04_derkachi_f2f_registration(
AC-3: sharp-turn frames excluded from the denominator.
AC-4: parametrized across ``(fc_adapter, vio_strategy)``.
"""
if not _harness_helpers_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-P-04 full replay requires runner.helpers.{frame_source_replay,"
"imu_replay,fdr_reader} — currently AZ-441 / AZ-407 leftovers. "
"Pure-logic ACs covered by e2e/_unit_tests/helpers/test_registration_classifier.py."
"FT-P-04 full replay requires `E2E_SITL_REPLAY_DIR` to point at a "
"prepared SITL replay fixture (AZ-595). Pure-logic ACs covered by "
"e2e/_unit_tests/helpers/test_registration_classifier.py."
)
from runner.helpers import fdr_reader, imu_replay
+5 -35
View File
@@ -43,36 +43,6 @@ GT_CSV = Path(__file__).resolve().parents[3] / "_docs" / "00_problem" / "input_d
STILL_IMAGES_DIR = GT_CSV.parent
@pytest.fixture(scope="module")
def _harness_helpers_implemented() -> bool:
"""True iff replay + SITL observation + FDR helpers are all real."""
from runner.helpers import fdr_reader, frame_source_replay, sitl_observer
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_image_directory(Path("/tmp/non-existent"))
except NotImplementedError:
return False
try:
sitl_observer.get_observer(fc_adapter="ardupilot", host="sitl-ardupilot")
except NotImplementedError:
return False
try:
list(fdr_reader.iter_records(Path("/tmp/non-existent")))
except NotImplementedError:
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
@pytest.mark.traces_to("AC-2.1b,AC-1,AC-2,AC-3,AC-5")
def test_ft_p_05_sat_anchor(
fc_adapter: str,
@@ -80,7 +50,7 @@ def test_ft_p_05_sat_anchor(
evidence_dir, # type: ignore[no-untyped-def]
run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
_harness_helpers_implemented: bool,
sitl_replay_ready: bool,
) -> None:
"""Full FT-P-05 scenario.
@@ -89,11 +59,11 @@ def test_ft_p_05_sat_anchor(
AC-3: ≥80 % within 50 m AND ≥50 % within 20 m (same image set as FT-P-01).
AC-5: parametrized across ``(fc_adapter, vio_strategy)``.
"""
if not _harness_helpers_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-P-05 still-image push requires runner.helpers.{frame_source_replay,"
"sitl_observer,fdr_reader} — currently AZ-441 + AZ-416/AZ-417 leftovers. "
"Pure-logic ACs covered by e2e/_unit_tests/helpers/test_mre_evaluator.py."
"FT-P-05 still-image push requires `E2E_SITL_REPLAY_DIR` to point "
"at a prepared SITL replay fixture (AZ-595). Pure-logic ACs "
"covered by e2e/_unit_tests/helpers/test_mre_evaluator.py."
)
from runner.helpers import fdr_reader, frame_source_replay, sitl_observer
@@ -46,41 +46,6 @@ DERKACHI_IMU_CSV = DERKACHI_DIR / "data_imu.csv"
DERKACHI_MP4 = DERKACHI_DIR / "flight_derkachi.mp4"
@pytest.fixture(scope="module")
def _harness_helpers_implemented() -> bool:
"""True iff replay + IMU + FDR helpers are real."""
from runner.helpers import fdr_reader, imu_replay
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_video(Path("/tmp/non-existent.mp4"))
except NotImplementedError:
return False
try:
list(fdr_reader.iter_records(Path("/tmp/non-existent")))
except NotImplementedError:
return False
try:
imu_replay.ImuReplayer(emitter=_NullImuEmitter()).replay(Path("/tmp/non-existent.csv")) # type: ignore[arg-type]
except NotImplementedError:
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
class _NullImuEmitter:
def emit(self, sample: object) -> None:
return None
@pytest.mark.traces_to("AC-3.2,AC-1,AC-4,AC-5,AC-6,AC-7")
def test_ft_p_07_sharp_turn_recovery(
fc_adapter: str,
@@ -88,14 +53,13 @@ def test_ft_p_07_sharp_turn_recovery(
evidence_dir, # type: ignore[no-untyped-def]
run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
_harness_helpers_implemented: bool,
sitl_replay_ready: bool,
) -> None:
if not _harness_helpers_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-P-07 full replay requires runner.helpers.{frame_source_replay,"
"imu_replay,fdr_reader} — currently AZ-441 / AZ-407 leftovers. "
"AC-1/AC-4/AC-5/AC-6 helper logic covered by "
"e2e/_unit_tests/helpers/test_sharp_turn_detector.py."
"FT-P-07 full replay requires `E2E_SITL_REPLAY_DIR` to point at a "
"prepared SITL replay fixture (AZ-595). AC-1/AC-4/AC-5/AC-6 helper "
"logic covered by e2e/_unit_tests/helpers/test_sharp_turn_detector.py."
)
from runner.helpers import fdr_reader
@@ -38,32 +38,6 @@ import pytest
from runner.helpers import multi_segment_evaluator as mse
@pytest.fixture(scope="module")
def _harness_helpers_implemented() -> bool:
"""True iff replay + FDR helpers are real."""
from runner.helpers import fdr_reader, frame_source_replay
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_image_directory(Path("/tmp/non-existent"))
except NotImplementedError:
return False
try:
list(fdr_reader.iter_records(Path("/tmp/non-existent")))
except NotImplementedError:
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
@pytest.mark.traces_to("AC-3.3,AC-1,AC-2,AC-3,AC-4,AC-5")
def test_ft_p_08_multi_segment_reloc(
fc_adapter: str,
@@ -72,7 +46,7 @@ def test_ft_p_08_multi_segment_reloc(
run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
multi_segment_derkachi, # type: ignore[no-untyped-def] # AZ-408 pytest fixture
_harness_helpers_implemented: bool,
sitl_replay_ready: bool,
) -> None:
"""Full FT-P-08 scenario.
@@ -82,11 +56,11 @@ def test_ft_p_08_multi_segment_reloc(
AC-4: trajectory continuity ≤100 m at each recovery.
AC-5: parameterised across ``(fc_adapter, vio_strategy)``.
"""
if not _harness_helpers_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-P-08 multi-segment replay requires runner.helpers.{frame_source_replay,"
"fdr_reader} — currently AZ-441 leftover. Pure-logic ACs covered by "
"e2e/_unit_tests/helpers/test_multi_segment_evaluator.py."
"FT-P-08 multi-segment replay requires `E2E_SITL_REPLAY_DIR` to "
"point at a prepared SITL replay fixture (AZ-595). Pure-logic ACs "
"covered by e2e/_unit_tests/helpers/test_multi_segment_evaluator.py."
)
from runner.helpers import fdr_reader
+5 -36
View File
@@ -55,36 +55,6 @@ MAVLINK_PASSKEY_FIXTURE = (
REPLAY_WINDOW_S = 60
@pytest.fixture(scope="module")
def _ap_harness_implemented() -> bool:
"""True iff frame_source_replay + sitl_observer AP-side leg are real."""
from runner.helpers import sitl_observer
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_video(Path("/tmp/non-existent.mp4"))
except NotImplementedError:
return False
try:
sitl_observer.capture_ap_tlog(host="ardupilot-sitl", duration_s=0.01)
except (NotImplementedError, AttributeError):
return False
try:
sitl_observer.read_ap_parameter(host="ardupilot-sitl", name="EK3_SRC1_POSXY")
except (NotImplementedError, AttributeError):
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
@pytest.mark.traces_to("AC-4.3,AC-1,AC-2,AC-3,AC-4,AC-5,D-C8-9")
def test_ft_p_09_ap_signing(
vio_strategy: str,
@@ -92,7 +62,7 @@ def test_ft_p_09_ap_signing(
run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
request, # type: ignore[no-untyped-def]
_ap_harness_implemented: bool,
sitl_replay_ready: bool,
) -> None:
"""Full FT-P-09-AP scenario; parameterized per vio_strategy."""
fc_adapter = request.getfixturevalue("fc_adapter")
@@ -105,12 +75,11 @@ def test_ft_p_09_ap_signing(
"AZ-407 / AZ-408 owns the on-disk fixture."
)
if not _ap_harness_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-P-09-AP full scenario requires runner.helpers.{frame_source_replay,"
"sitl_observer.capture_ap_tlog,sitl_observer.read_ap_parameter} — "
"currently AZ-441 / AZ-407 leftovers. Pure-logic AC-1..AC-4 covered by "
"e2e/_unit_tests/helpers/test_ap_contract_evaluator.py."
"FT-P-09-AP full scenario requires `E2E_SITL_REPLAY_DIR` to point "
"at a prepared SITL replay fixture (AZ-595). Pure-logic AC-1..AC-4 "
"covered by e2e/_unit_tests/helpers/test_ap_contract_evaluator.py."
)
from runner.helpers import sitl_observer
+5 -32
View File
@@ -45,32 +45,6 @@ REPLAY_WINDOW_S = 60
TCP_HANDSHAKE_BUDGET_S = 5
@pytest.fixture(scope="module")
def _inav_harness_implemented() -> bool:
"""True iff frame_source_replay + sitl_observer iNav leg are real."""
from runner.helpers import sitl_observer
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_video(Path("/tmp/non-existent.mp4"))
except NotImplementedError:
return False
try:
sitl_observer.observe_inav_tcp_handshake(host="inav-sitl", port=5760, timeout_s=0.01)
except (NotImplementedError, AttributeError):
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
@pytest.mark.traces_to("AC-4.3,AC-1,AC-2,AC-3,AC-4")
def test_ft_p_09_inav(
vio_strategy: str,
@@ -78,7 +52,7 @@ def test_ft_p_09_inav(
run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
request, # type: ignore[no-untyped-def]
_inav_harness_implemented: bool,
sitl_replay_ready: bool,
) -> None:
"""Full FT-P-09-iNav scenario; parameterized per vio_strategy.
@@ -90,12 +64,11 @@ def test_ft_p_09_inav(
if fc_adapter != "inav":
pytest.skip("FT-P-09-iNav is iNav-only; ardupilot variant is FT-P-09-AP (AZ-416)")
if not _inav_harness_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-P-09-iNav full scenario requires runner.helpers.{frame_source_replay,"
"sitl_observer.observe_inav_tcp_handshake} — currently AZ-441 / AZ-407 leftovers. "
"Pure-logic AC-2/AC-3 covered by "
"e2e/_unit_tests/helpers/test_msp_frame_observer.py."
"FT-P-09-iNav full scenario requires `E2E_SITL_REPLAY_DIR` to "
"point at a prepared SITL replay fixture (AZ-595). Pure-logic "
"AC-2/AC-3 covered by e2e/_unit_tests/helpers/test_msp_frame_observer.py."
)
from runner.helpers import sitl_observer
@@ -49,41 +49,6 @@ DERKACHI_IMU_CSV = DERKACHI_DIR / "data_imu.csv"
DERKACHI_MP4 = DERKACHI_DIR / "flight_derkachi.mp4"
@pytest.fixture(scope="module")
def _harness_helpers_implemented() -> bool:
"""True iff replay + IMU + FDR helpers are real."""
from runner.helpers import fdr_reader, frame_source_replay, imu_replay
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_video(Path("/tmp/non-existent.mp4"))
except NotImplementedError:
return False
try:
list(fdr_reader.iter_records(Path("/tmp/non-existent")))
except NotImplementedError:
return False
try:
imu_replay.ImuReplayer(emitter=_NullImuEmitter()).replay(Path("/tmp/non-existent.csv")) # type: ignore[arg-type]
except NotImplementedError:
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
class _NullImuEmitter:
def emit(self, sample: object) -> None:
return None
def _load_derkachi_gt_track() -> list[se.GtPose]:
"""Read GLOBAL_POSITION_INT poses from data_imu.csv.
@@ -115,7 +80,7 @@ def test_ft_p_10_smoothing_lookback(
evidence_dir, # type: ignore[no-untyped-def]
run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
_harness_helpers_implemented: bool,
sitl_replay_ready: bool,
) -> None:
"""Full FT-P-10 scenario.
@@ -125,11 +90,11 @@ def test_ft_p_10_smoothing_lookback(
AC-3: mean_improvement_m ≥ 5 m.
AC-4: parameterised across ``(fc_adapter, vio_strategy)``.
"""
if not _harness_helpers_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-P-10 full replay requires runner.helpers.{frame_source_replay,"
"imu_replay,fdr_reader} — currently AZ-441 / AZ-407 leftovers. "
"Pure-logic ACs covered by e2e/_unit_tests/helpers/test_smoothing_evaluator.py."
"FT-P-10 full replay requires `E2E_SITL_REPLAY_DIR` to point at a "
"prepared SITL replay fixture (AZ-595). Pure-logic ACs covered by "
"e2e/_unit_tests/helpers/test_smoothing_evaluator.py."
)
from runner.helpers import fdr_reader, imu_replay
@@ -51,45 +51,6 @@ COLD_BOOT_FIXTURE = (
OPERATOR_ORIGIN = cse.LatLonAlt(lat_deg=50.0, lon_deg=36.2, alt_m=200.0)
@pytest.fixture(scope="module")
def _cold_start_harness_implemented() -> bool:
"""True iff frame_source_replay + sitl_observer + fdr_reader are real.
Cold start adds two specific SITL-observer surfaces beyond the
common replay path: ``prepare_sitl_cold_boot`` (parameter-load
path) and ``prepare_sitl_no_gps`` (``SIM_GPS_DISABLE = 1``).
"""
from runner.helpers import fdr_reader, sitl_observer
from runner.helpers.frame_source_replay import FrameSourceReplayer
try:
replayer = FrameSourceReplayer(sink=_NullSink()) # type: ignore[arg-type]
try:
replayer.replay_video(Path("/tmp/non-existent.mp4"))
except NotImplementedError:
return False
try:
list(fdr_reader.iter_records(Path("/tmp/non-existent")))
except NotImplementedError:
return False
try:
sitl_observer.prepare_sitl_cold_boot(host="ardupilot-sitl", fixture_path=COLD_BOOT_FIXTURE)
except (NotImplementedError, AttributeError):
return False
try:
sitl_observer.prepare_sitl_no_gps(host="ardupilot-sitl")
except (NotImplementedError, AttributeError):
return False
return True
except Exception:
return False
class _NullSink:
def write_frame(self, jpeg_bytes: bytes, timestamp_ms: int) -> None:
return None
@pytest.fixture
def _cold_run_id(run_id: str) -> str:
"""Return a fresh run_id — Cold-start REQUIRES an empty fdr-output volume.
@@ -116,16 +77,14 @@ def test_ft_p_11_cold_start_origin_variants(
_cold_run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
tmp_path: Path,
_cold_start_harness_implemented: bool,
sitl_replay_ready: bool,
) -> None:
"""FT-P-11 AC-1 / AC-2 / AC-4 across the three origin_source variants."""
if not _cold_start_harness_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-P-11 full scenario requires runner.helpers.{frame_source_replay,"
"fdr_reader,sitl_observer.prepare_sitl_cold_boot,"
"sitl_observer.prepare_sitl_no_gps} — currently AZ-441 / AZ-407 "
"leftovers. Pure-logic AC-1/2/3/4 covered by "
"e2e/_unit_tests/helpers/test_cold_start_evaluator.py."
"FT-P-11 full scenario requires `E2E_SITL_REPLAY_DIR` to point at a "
"prepared SITL replay fixture (AZ-595). Pure-logic AC-1/2/3/4 "
"covered by e2e/_unit_tests/helpers/test_cold_start_evaluator.py."
)
from runner.helpers import fdr_reader, sitl_observer
@@ -241,15 +200,14 @@ def test_ft_p_11_cold_start_no_origin_aborts(
_cold_run_id: str,
nfr_recorder, # type: ignore[no-untyped-def]
tmp_path: Path,
_cold_start_harness_implemented: bool,
sitl_replay_ready: bool,
) -> None:
"""AC-3: Manifest empty + SITL no GPS → SUT MUST refuse takeoff."""
if not _cold_start_harness_implemented:
if not sitl_replay_ready:
pytest.skip(
"FT-P-11 AC-3 full scenario requires runner.helpers.{frame_source_replay,"
"fdr_reader,sitl_observer.prepare_sitl_no_gps} — currently AZ-441 / "
"AZ-407 leftovers. Pure-logic AC-3 covered by "
"e2e/_unit_tests/helpers/test_cold_start_evaluator.py."
"FT-P-11 AC-3 full scenario requires `E2E_SITL_REPLAY_DIR` to point "
"at a prepared SITL replay fixture (AZ-595). Pure-logic AC-3 "
"covered by e2e/_unit_tests/helpers/test_cold_start_evaluator.py."
)
from runner.helpers import fdr_reader, sitl_observer