[AZ-600] Batch 80: refactor sitl_replay_builder to strategy pattern

Replace per-scenario fixture builders with a parameterized strategy
framework so future Derkachi-based scenarios compose existing pieces
instead of duplicating ~200 lines of orchestration per scenario.

New e2e/fixtures/sitl_replay_builder/builder.py:
- VideoSource ABC + StillImagesSource, Mp4PassthroughSource
- TlogSource ABC + SyntheticStationaryTlog, ImuCsvTlog
- FdrProjection ABC + RawFdrPassthrough, OutboundMessagesProjection
- FixtureBuilderConfig + build_fixtures(cfg) orchestrator
- Consolidated MAVLink pack_raw_imu / pack_attitude helpers
- Consolidated run_gps_denied_replay + write_observer_fixture

build_p01_fixtures.py: 423 -> 107 lines (75% reduction).
build_p02_fixtures.py: 292 -> 98 lines (66% reduction).
_common.py: deleted (folded into builder.py).

Tests reorganized:
- test_sitl_replay_builder_builder.py (new, 33 strategy-level tests)
- test_sitl_replay_builder.py (slimmed, 6 FT-P-01 integration)
- test_sitl_replay_builder_p02.py (slimmed, 7 FT-P-02 integration)

README documents the strategy framework + a worked example for
adding FT-P-04 in ~30 lines (no new strategy code required).

Regression gate: 700 passing (was 686; +14 from finer-grained
coverage of new strategy classes and the build_fixtures orchestrator).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-17 14:19:08 +03:00
parent 4e0717e543
commit 7fb3cb3f34
13 changed files with 2050 additions and 1272 deletions
@@ -0,0 +1,100 @@
# Refactor sitl_replay_builder to strategy pattern
**Task**: AZ-600_refactor_sitl_replay_builder_to_strategy_pattern
**Complexity**: 3 points
**Dependencies**: AZ-598 (b78 FT-P-01 builder), AZ-599 (b79 FT-P-02 builder)
**Component**: Blackbox Tests / Test Infrastructure (epic AZ-262)
**Tracker**: AZ-600
## Problem
b78 and b79 introduced two per-scenario builder modules
(`build_p01_fixtures.py`, `build_p02_fixtures.py`) that share `_common.py`
(gps-denied-replay subprocess wrapper + observer fixture writer) but still
duplicate ~45 lines of MAVLink packers, pymavlink writer factory boilerplate,
and argparse boilerplate. More importantly, the structure forces every future
scenario to copy-paste the orchestration loop. With 6+ Derkachi-based
scenarios still to land (FT-P-04, FT-P-05, FT-P-07, FT-P-08, FT-P-10,
FT-P-11), the duplication compounds.
## Strategy
Introduce three strategy ABCs in a new module
`e2e/fixtures/sitl_replay_builder/builder.py`:
1. **VideoSource** — produces the MP4 the replay CLI consumes
- `StillImagesSource(image_paths, fps)` — was b78 `encode_stills_to_mp4`
- `Mp4PassthroughSource(mp4_path)` — was b79 path resolution
2. **TlogSource** — produces the tlog the replay CLI consumes
- `SyntheticStationaryTlog(duration_s, hz)` — was b78 `generate_stationary_tlog`
- `ImuCsvTlog(csv_path, column_schema)` — was b79 `convert_imu_csv_to_tlog`
3. **FdrProjection** — translates the FDR JSONL into the scenario fixture shape
- `RawFdrPassthrough()` — b79 / FT-P-02 / future Derkachi scenarios
- `OutboundMessagesProjection(fdr_kind, image_ids)` — was b78
`parse_fdr_for_outbound_estimates` + `write_outbound_messages_fixture`
A single `build_fixtures(cfg: FixtureBuilderConfig)` orchestrator composes
the three strategies plus `run_gps_denied_replay` + `write_observer_fixture`.
`build_p01_fixtures.py` and `build_p02_fixtures.py` shrink to thin
scenario-config factories + argparse CLI wrappers (~60 lines each).
The MAVLink RAW_IMU + ATTITUDE packers consolidate into one parameterized
helper (zero-motion is just one config of real-motion).
## Files Touched
* `e2e/fixtures/sitl_replay_builder/builder.py` (new) — strategy ABCs +
concrete impls + orchestrator + `FixtureBuilderConfig` dataclass +
parameterized MAVLink packers.
* `e2e/fixtures/sitl_replay_builder/build_p01_fixtures.py` (edit, shrink) —
scenario config factory + CLI; ~60 lines.
* `e2e/fixtures/sitl_replay_builder/build_p02_fixtures.py` (edit, shrink) —
scenario config factory + CLI; ~60 lines.
* `e2e/fixtures/sitl_replay_builder/README.md` (edit) — replace per-builder
sections with strategy reference + worked example for adding a new scenario.
* `e2e/_unit_tests/fixtures/test_sitl_replay_builder_builder.py` (new) —
strategy class tests (one suite per strategy ABC + orchestrator).
* `e2e/_unit_tests/fixtures/test_sitl_replay_builder_p01.py` (edit) —
scenario-level integration tests for the FT-P-01 builder; drops tests
that moved to the strategy suite.
* `e2e/_unit_tests/fixtures/test_sitl_replay_builder_p02.py` (edit) —
same for FT-P-02.
* `e2e/_unit_tests/test_directory_layout.py` (edit) — register new module.
## Acceptance Criteria
**AC-1**: `builder.py` exports `VideoSource`, `TlogSource`, `FdrProjection`
ABCs plus the four concrete impls listed above, with `build_fixtures(cfg)`
orchestrator and `FixtureBuilderConfig` dataclass.
**AC-2**: `build_p01_fixtures.py` is ≤80 lines and only contains:
image-glob helper, scenario config factory, CLI wrapper.
**AC-3**: `build_p02_fixtures.py` is ≤80 lines and only contains:
Derkachi-input resolve helper, scenario config factory, CLI wrapper.
**AC-4**: The MAVLink RAW_IMU + ATTITUDE packer is a single helper
parameterized by IMU values + yaw; zero-motion is the
`(0, 0, -9810, 0, 0, 0)` config; real-motion is per-row CSV values.
**AC-5**: Unit tests reorganize as described in *Files Touched*. Same
coverage, same AC verification, no functional regression.
**AC-6**: Full `e2e/_unit_tests` suite passes at ≥ 686 tests (regression
gate). All b78 + b79 acceptance behaviors (AC-1..AC-5 of AZ-598;
AC-1..AC-5 of AZ-599) remain validated.
**AC-7**: A worked example in `README.md` shows how to add a new scenario
(e.g., FT-P-04) by writing only a ~30-line scenario config — no new
strategy code required.
## Out of Scope
* Adding new scenarios (FT-P-04..FT-P-11 stay in todo/; this refactor only
enables them).
* Changing the `gps-denied-replay` CLI contract.
* Changing the `observer_<fc_kind>_<host>.json` payload schema.
* Live capture pipeline changes.