[AZ-594] Implement core-three harness stubs (fdr_reader, frame_source_replay, imu_replay)

Replaces the NotImplementedError stubs AZ-406 reserved on three runner-
side helpers; these were stranded from any tracker ticket since
AZ-407/408 never came back to fill them. Concrete bodies:

* fdr_reader.iter_records: JSONL parser + wire-envelope validator;
  recursive *.jsonl walk; projects {schema_version, ts, producer_id,
  kind, payload} to runner-side FdrRecord with record_type/monotonic_ms
  renames; yields oldest-first.
* frame_source_replay.replay_video: OpenCV VideoCapture decode + JPEG
  re-encode; auto-detects file vs directory; injectable sleep_fn for
  unit-test pacing.
* imu_replay.ImuReplayer.replay: csv.DictReader parse; degrees->radians
  attitude conversion; tolerates scientific notation; same sleep_fn
  injection pattern.

Adds 34 unit tests (14 + 10 + 10). Full e2e unit suite: 558 passed (+31).
Existing scenario _harness_helpers_implemented probes still return False
because they also depend on sitl_observer / fc_proxy_runtime stubs that
remain pending; scenario probe cleanup is out of AZ-594 scope.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-17 08:42:12 +03:00
parent 2d6d44af5d
commit 1d260f7e41
10 changed files with 1196 additions and 76 deletions
@@ -0,0 +1,91 @@
# Harness stubs — core three (fdr_reader, frame_source_replay, imu_replay)
**Task**: AZ-594_harness_stubs_core_three
**Name**: Implement the three foundational e2e/runner/helpers/ stubs that AZ-406 reserved
**Description**: Replace `NotImplementedError` stubs in `fdr_reader.iter_records`, `frame_source_replay.replay_video`, and `imu_replay.ImuReplayer.replay` with concrete bodies. Together they unblock the skip-gated scenarios from batches 71-73 once `sitl_observer` lands.
**Complexity**: 4 points
**Dependencies**: AZ-406, AZ-407, AZ-408
**Component**: Blackbox Tests / Test Infrastructure (epic AZ-262)
**Tracker**: AZ-594
**Epic**: AZ-262 (E-BBT)
## Problem
AZ-406 committed to the public surface of the runner-side helpers and
reserved each method body as `NotImplementedError`. AZ-407/408 filled
the fixture-builder side; the runtime replay/read surfaces were
deferred without a dedicated ticket. The skip-gated scenarios in
batches 71-73 (`test_ft_p_07/08/10/11`, `test_ft_n_01..04`) reference
these stubs in their `_harness_helpers_implemented` probe and skip
cleanly until the bodies land.
## Outcome
- `fdr_reader.iter_records` reads `*.jsonl` files under an archive
root and yields `FdrRecord` envelopes ordered by `monotonic_ms`.
- `frame_source_replay.replay_video` decodes `.mp4` files or
directories of `.jpg`/`.png` frames via OpenCV and emits to the
injected `FrameSink` at the requested cadence.
- `imu_replay.ImuReplayer.replay` parses `data_imu.csv` and drives a
`FcInboundEmitter` at the configured rate.
- Each body has ≥5 unit tests (happy path + ≥2 error paths + ≥1
boundary case).
## Scope
### Included
- The three method bodies + their unit tests.
- Layout-test entries (already present for the helper files).
### Excluded
- `sitl_observer.get_observer` + `read_*` surfaces — separate ticket.
- `fc_proxy` runtime driver — separate ticket.
- Removing the skip-gates in existing scenarios — happens once
`sitl_observer` lands.
## Acceptance Criteria
**AC-1**: `fdr_reader.iter_records` opens every `*.jsonl` file under
the archive root, parses each line as JSON, and yields `FdrRecord`
envelopes sorted by `monotonic_ms`. Raises `FileNotFoundError` on
missing root.
**AC-2**: `frame_source_replay.replay_video` accepts an `.mp4` file
OR a directory of `.jpg`/`.png` frames; decodes via OpenCV; emits to
the sink at the cadence (encoded FPS if `realtime`, configured FPS
otherwise). Returns the count emitted. Raises `FileNotFoundError` on
missing input.
**AC-3**: `imu_replay.ImuReplayer.replay` parses the CSV at
`csv_path`, constructs `ImuSample`s with accel/gyro/attitude/baro,
and calls `emitter.emit(sample)` for each row. Returns the count
emitted. Tolerates scientific-notation floats.
**AC-4**: Each new body has ≥5 unit tests in
`e2e/_unit_tests/helpers/` covering the happy path + 2 error paths +
1 boundary case.
**AC-5**: Full e2e unit-test suite passes (regression gate).
## System Under Test Boundary
None — these are runner-side helpers that synthesize the SUT's
inputs and parse the SUT's FDR output. They do NOT import any
`src/gps_denied_onboard` symbol.
## Constraints
- Use `opencv-python-headless` (already pinned in
`e2e/runner/requirements.txt`).
- `imu_replay.replay` must not block on wall-clock at the requested
rate when called in tests; a `time.sleep`-based pacing path is
acceptable for production use but the test must inject a fake
clock / cadence=0 to keep unit tests fast.
- `fdr_reader.iter_records` uses stdlib `json` (orjson is fine but
not required at this stage — keep dependencies minimal).
## Document Dependencies
- `_docs/02_document/tests/blackbox-tests.md` § Test infrastructure
- `_docs/02_tasks/done/AZ-406_test_infrastructure.md`
- `_docs/02_tasks/done/AZ-407_fixture_builders_static.md`