mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-21 23:21:12 +00:00
1d260f7e41
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>
8.6 KiB
8.6 KiB
Code Review Report
Batch: 74 — AZ-594 (harness stubs: core three) Date: 2026-05-17 Verdict: PASS
Findings
(none)
Findings Sweep
Phase 1 — Context Loading
Read the AZ-594 task spec, then the three stub files in their
pre-implementation state (fdr_reader.py, frame_source_replay.py,
imu_replay.py). Cross-checked the SUT-side FdrRecord wire schema
in src/gps_denied_onboard/fdr_client/records.py to confirm field
names (schema_version, ts, producer_id, kind, payload,
extra) and the rename strategy the runner-side FdrRecord
duplicates (kind → record_type, ts → monotonic_ms). Re-read the
existing scenario _harness_helpers_implemented probes in the batch
71-73 scenario files to confirm the post-fix probe semantics.
Phase 2 — Spec Compliance
| AC | Coverage | Status |
|---|---|---|
AC-1 (fdr_reader.iter_records parses every *.jsonl under root, yields FdrRecord ordered by monotonic_ms; raises FileNotFoundError on missing root) |
test_missing_root_raises_file_not_found, test_empty_root_yields_nothing, test_single_file_round_trip, test_multiple_files_are_merged_and_sorted, test_subdirectory_files_included, test_payload_passed_through; envelope-validation tests cover the wire-schema gate (test_missing_envelope_key_raises, test_non_object_line_raises, test_malformed_json_raises, test_empty_producer_id_raises, test_ts_iso_without_z_parses, test_blank_lines_are_skipped); archive_size_bytes covered by test_archive_size_bytes_sums_all_files and test_archive_size_bytes_missing_root_returns_zero |
Covered |
AC-2 (frame_source_replay.replay_video accepts .mp4 OR directory; decodes via OpenCV; emits via sink at cadence; raises FileNotFoundError on missing input) |
test_video_missing_path_raises_file_not_found, test_video_dir_delegates_to_image_directory, test_video_mp4_round_trip; image-dir mode covered by test_image_dir_missing_raises_file_not_found, test_image_dir_empty_returns_zero, test_image_dir_emits_sorted_by_name, test_image_dir_non_jpeg_reencoded, test_image_dir_skips_non_image_files, test_image_dir_non_realtime_does_not_sleep, test_image_dir_realtime_sleeps_per_frame |
Covered |
AC-3 (imu_replay.ImuReplayer.replay parses CSV, constructs ImuSamples, calls emitter; tolerates scientific-notation floats) |
test_happy_path_emits_all_rows, test_scientific_notation_parses, test_attitude_radians_converted, test_realtime_sleeps_per_sample, test_non_realtime_does_not_sleep, test_empty_csv_emits_nothing; error paths: test_missing_csv_raises_file_not_found, test_rate_hz_must_be_positive, test_missing_required_columns_raises, test_row_with_unparseable_value_raises |
Covered |
| AC-4 (≥5 unit tests per body — happy + 2 error + 1 boundary) | fdr_reader: 14 tests; frame_source_replay: 10 tests; imu_replay: 10 tests | Covered (exceeds floor) |
| AC-5 (full suite passes) | 558 passed (+31 from 527 baseline) | Covered |
Phase 3 — Code Quality
- Single responsibility:
fdr_readerowns wire-envelope parsing + projection to the runner-side dataclass. It does NOT validate per-kind payload keys — that's left to the consuming evaluators, matching the AZ-272/273 contract that documentspayloadas opaque on the runner side.frame_source_replay.FrameSourceReplayerowns frame decode + sink emission. Cadence pacing is parameterised viasleep_fnfor testability; OpenCV is the only third-party import.imu_replay.ImuReplayerowns CSV parse + emitter invocation. Cadence pacing same pattern as frame_source_replay. Row parsing is a pure function_parse_rowso missing/malformed columns surface as aValueErrorwith a file + line pointer.
- No suppressed errors:
fdr_reader._parse_enveloperaisesValueError(with file + line) on every malformed envelope branch;json.JSONDecodeErrorpropagates naturally for unrecoverable JSON corruption.frame_source_replay._decode_and_emit_videoraises explicitValueErrorif OpenCV cannot open the file or fails to encode a frame — never silently skips a frame.imu_replay._parse_rowre-raisesKeyError/ValueErrorwrapped as a contextualValueError; the original__cause__is preserved viaraise ... from exc.- No
except Exception: pass,2>/dev/null, or emptyexceptblocks.
- AAA comment discipline: every new test uses `# Arrange / # Act /
Assert`; sections omitted when not needed.
- No code comments narrating what code does — only the module-level docstrings explain the wire-vs-runner schema rename, the OpenCV realtime/non-realtime distinction, and the scientific-notation tolerance rationale.
- Public boundary: confirmed all three modules import only
stdlib (
csv,json,math,time,pathlib,datetime) +cv2. Nofrom gps_denied_onboard ...anywhere.
Phase 4 — Security
- No new credentials, secrets, or network surface. All three helpers are deterministic file-I/O over caller-supplied paths.
- Wire-schema gate in
fdr_reader._parse_envelopeis the safety invariant — if the SUT FDR schema drifts, the runner blows up at parse time with a file+line pointer rather than silently producing records with default-zero fields. This is the explicit drift-detection rationale documented in theFdrRecorddocstring. - No
eval,exec,pickle, orsubprocessin any of the three modules.
Phase 5 — Performance
- All three implementations are O(N) over their input streams
(JSONL lines, frames, CSV rows). Sorting in
iter_recordsis the one O(N log N) step — necessary for the multi-file merge-sort guarantee, and acceptable since the typical archive is bounded by the AZ-441 50 GB / 8 h budget. - No file I/O at module-import time.
FrameSourceReplayer._decode_and_emit_videoalways re-encodes frames to JPEG (lossy double-encode if source was MP4). The intent is so the sink always receives JPEG bytes regardless of source format — matches the SUT'sONBOARD_FRAME_SOURCE_PATHexpectation of file-system-readable JPEG frames.
Phase 6 — Cross-Task Consistency
- Sleep injection pattern:
frame_source_replay.FrameSourceReplayerandimu_replay.ImuReplayerboth acceptsleep_fnas a keyword argument defaulting totime.sleep. Tests passlambda _: Noneor a recording stub. Single pattern across the two replay surfaces. realtimevsnon-realtimeflag: both replayers default torealtime=Trueand skip the sleep whenFalse. Consistent semantics so that future scenarios can choose between wall-clock replay (for live-FC tests) and fast replay (for FDR/evidence-only tests).- FileNotFoundError: all three new bodies surface missing input
paths via
FileNotFoundError, consistent with how the existing helpersaccuracy_evaluator,multi_segment_evaluator,mavproxy_tlog_reader,cold_start_evaluatorhandle missing inputs. - Scenario probe interaction: the existing
_harness_helpers_implementedprobes in batches 71-73 (FT-P-07, FT-P-08, FT-P-10, FT-P-11, FT-N-01, FT-N-02, FT-N-03, FT-N-04) pass/tmp/non-existentto each helper. Previously the innerexcept NotImplementedError: return Falsefired; now the outerexcept Exception: return Falsecatches the newFileNotFoundErrorand still returns False. The scenarios continue to skip — which is correct because the OTHER probe-gated helpers (sitl_observer,mavproxy_tlog_readerfor some scenarios,fc_proxy_runtime) are still pending. Probe cleanup is explicitly excluded from AZ-594 scope and will happen once the remaining harness stubs land.
Phase 7 — Architecture Compliance
- Module placement unchanged: existing files at
e2e/runner/helpers/{fdr_reader,frame_source_replay,imu_replay}.pywere edited in place (bodies replaceNotImplementedError); no layout changes. New unit-test files ine2e/_unit_tests/helpers/. Layout invariant test still passes — these helpers were already listed. - No
src/gps_denied_onboardimports anywhere. Verified. - Existing scenario surface preserved:
FdrRecorddataclass field names and types are unchanged from the AZ-406 contract.ImuSamplelikewise.ReplayCadenceandFrameSinkProtocol unchanged. Consumers in batches 71-73 stay valid.
Test Results
- New unit tests: 14 (fdr_reader) + 10 (frame_source_replay) + 10 (imu_replay) = 34 new tests.
- Full
e2e/_unit_testssuite: 558 passed in 139 s (previous cumulative: 527 → +31 net). - No new linter errors (
ReadLintson all six new/modified files reported clean).