AZ-408 (3pt) — Replace AZ-406 injector scaffolds with concrete generators: - outlier.py: deterministic stride + far-away tile replacement; AC-2 ≥350m offset - blackout_spoof.py: paired video blackout + FC GPS spoof with ≤40ms alignment; AC-4 realistic fix_type/hdop; AC-NEW-8 200-500m inter-spoof deltas - multi_segment.py: ≥3 disjoint windows, ≥30s gaps, ≤25% coverage - fc_proxy.py: timed-splice runtime proxy with pre-activate RuntimeError guard - _common.py: derive_rng + tile-manifest reader + tmpfs helpers - injector_fixtures.py: pytest fixtures wired via runner conftest AZ-410 (3pt) — FT-P-02 cumulative drift between satellite anchors: - anchor_pair_detector.py: AC-1 detection, AC-2/3 pass-fraction, AC-4 monotonicity check, CSV evidence - test_ft_p_02_derkachi_drift.py: scenario gated on upstream helper NotImplementedError (frame_source_replay / fdr_reader / imu_replay) AZ-411 (2pt) — FT-P-03 + FT-P-14 schema + WGS84: - estimate_schema.py: AC-1 schema completeness, AC-2 source-label set containment, AC-3 WGS84 range + int32 1e-7 decode - test_ft_p_03_14_schema_wgs84.py: shared single-image-push scenario Tests: 248 unit tests pass (+91 vs batch 68). Reports: batch_69_report.md, batch_69_review.md (PASS), cumulative_review_batches_67-69_cycle1_report.md (PASS). Co-authored-by: Cursor <cursoragent@cursor.com>
Blackbox Test Harness (e2e/)
This directory is the public-boundary test harness for gps-denied-onboard. It is owned by the blackbox_tests cross-cutting entry in _docs/02_document/module-layout.md and implements task AZ-406 (Test Infrastructure Bootstrap) plus its downstream test-task siblings (AZ-407..AZ-446).
The harness runs in two execution tiers (environment.md § Two-tier execution profile):
- Tier-1 — workstation Docker.
cd e2e/docker && docker compose -f docker-compose.test.yml up --build --abort-on-container-exit e2e-runner - Tier-2 — Jetson Orin Nano Super hardware loop.
./e2e/jetson/run-tier2.sh --fc-adapter <ardupilot|inav> --vio-strategy <okvis2|klt_ransac>
Both tiers emit the same CSV report format (one row per test) per environment.md § Reporting.
Layout
e2e/
├── docker/ Tier-1 entrypoint (docker-compose.test.yml + Tier-2 bridge override + secrets mount)
├── jetson/ Tier-2 entrypoint (run-tier2.sh + systemd unit + tegrastats/jtop parsers)
├── runner/ e2e-runner image (Dockerfile, conftest, pytest plugins, helpers, requirements)
├── fixtures/ Fixture builders (tile-cache, age-injector, injectors/, mock-suite-sat, secrets, security)
├── tests/ Pytest target — `positive/`, `negative/`, `performance/`, `resilience/`, `security/`, `resource_limit/`
└── _unit_tests/ Out-of-container unit tests for the harness internals (run as part of the project test suite)
Public-Boundary Discipline (hard rule)
The e2e-runner image MUST NOT import any module from the SUT source tree (src/gps_denied_onboard/**). The only legal interaction surfaces are:
- MAVLink (ArduPilot SITL — UDP 14550)
- MSP2 (iNav SITL — TCP 5760)
- HTTP/JSON (mock-suite-sat-service — port 8080)
- Filesystem read of the FDR archive after a run (
fdr-outputvolume)
This rule is enforced by:
- The runner
Dockerfilebuilding from a base image that does NOT install the SUT package. - Layout discipline: no
import gps_denied_onboard.*in any file undere2e/. - Compose
e2e-net.internal: true— no external network egress (RESTRICT-SAT-1, NFT-SEC-02).
See _docs/02_document/tests/environment.md for the full per-service spec.
RUN_ID and report paths
Each invocation must set RUN_ID (defaults to local-${USER}-${EPOCH} in development; CI sets it from the workflow run id). Reports land at:
e2e-results/run-${RUN_ID}/report.csve2e-results/run-${RUN_ID}/evidence/(per-run.tlog, FDR archives, screenshots, profiler traces, tegrastats CSV, jtop CSV)
The e2e-results/ directory is gitignored.
How to add a new blackbox scenario
- Decompose the scenario into a task spec under
_docs/02_tasks/todo/. - Implement the test under the appropriate
e2e/tests/<category>/folder. - The conftest's session-scoped
(fc_adapter, vio_strategy)parameterization automatically applies — opt out with@pytest.mark.parametrizeoverrides. - Trace the scenario to the AC/RESTRICT IDs it exercises via the
traces_topytest marker — the CSV reporter emits this verbatim.
How to add a new fixture builder
Fixture builders live under e2e/fixtures/ and may be standalone Python modules (for runtime injectors) or Dockerized helpers (for tile-cache / mock-suite-sat). Each builder must:
- Be reproducible — given the same input, produce bit-identical output.
- Document its output volume / path in
_docs/02_document/tests/test-data.md. - Have a corresponding unit test under
e2e/_unit_tests/fixtures/.
Out-of-container unit tests
The harness's internal Python — CSV reporter, helpers, parsers, mock app, conftest skip rules — is unit-tested under e2e/_unit_tests/. These tests do NOT require Docker, SITL, or any external service and run as part of the project's main pytest invocation (testpaths extension in pyproject.toml).