mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-21 20:41:13 +00:00
f49d803252
Add `runner/helpers/replay_mode.py` (NullFrameSink, NullFcInboundEmitter, default_frame_period_ms, load_replay_json, resolve_replay_subdir, imu_replay_noop) and rewire all 13 scenarios off their local `_resolve_*` / `_drive_*` / `_push_*` NotImplementedError stubs. Closes the offline FDR-replay execution path. `grep raise NotImplementedError` under `e2e/tests/` now returns zero matches. +17 unit tests (626 total, up from 608). Unit-test behaviour unchanged (scenarios still skip via b75 sitl_replay_ready gate when E2E_SITL_REPLAY_DIR is unset). Co-authored-by: Cursor <cursoragent@cursor.com>
185 lines
4.3 KiB
Python
185 lines
4.3 KiB
Python
"""Unit tests for `e2e/runner/helpers/replay_mode.py` (AZ-597)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from e2e.runner.helpers import replay_mode as rm
|
|
|
|
|
|
@pytest.fixture
|
|
def replay_dir(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
|
|
monkeypatch.setenv("E2E_SITL_REPLAY_DIR", str(tmp_path))
|
|
return tmp_path
|
|
|
|
|
|
@pytest.fixture
|
|
def unset_replay_dir(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.delenv("E2E_SITL_REPLAY_DIR", raising=False)
|
|
|
|
|
|
# NullFrameSink
|
|
|
|
|
|
def test_null_frame_sink_counts_writes():
|
|
# Arrange
|
|
sink = rm.NullFrameSink()
|
|
|
|
# Act
|
|
sink.write_frame(b"jpeg-bytes-1", timestamp_ms=100)
|
|
sink.write_frame(b"jpeg-bytes-2", timestamp_ms=200)
|
|
sink.write_frame(b"jpeg-bytes-3", timestamp_ms=300)
|
|
|
|
# Assert
|
|
assert sink.frames_written == 3
|
|
|
|
|
|
def test_null_frame_sink_starts_at_zero():
|
|
# Assert
|
|
assert rm.NullFrameSink().frames_written == 0
|
|
|
|
|
|
# NullFcInboundEmitter
|
|
|
|
|
|
def test_null_emitter_counts_emits():
|
|
# Arrange
|
|
emitter = rm.NullFcInboundEmitter()
|
|
|
|
# Act
|
|
emitter.emit(object())
|
|
emitter.emit(object())
|
|
|
|
# Assert
|
|
assert emitter.samples_emitted == 2
|
|
|
|
|
|
def test_null_emitter_starts_at_zero():
|
|
# Assert
|
|
assert rm.NullFcInboundEmitter().samples_emitted == 0
|
|
|
|
|
|
# default_frame_period_ms
|
|
|
|
|
|
def test_default_frame_period_ms_is_30_fps():
|
|
# Assert
|
|
assert rm.default_frame_period_ms() == 33
|
|
assert rm.DEFAULT_FRAME_PERIOD_MS == 33
|
|
|
|
|
|
# imu_replay_noop
|
|
|
|
|
|
def test_imu_replay_noop_returns_none(tmp_path: Path):
|
|
# Assert
|
|
assert rm.imu_replay_noop(tmp_path / "any.csv") is None
|
|
|
|
|
|
def test_imu_replay_noop_does_not_touch_disk(tmp_path: Path):
|
|
# Arrange
|
|
csv = tmp_path / "does-not-exist.csv"
|
|
|
|
# Act
|
|
rm.imu_replay_noop(csv)
|
|
|
|
# Assert
|
|
assert not csv.exists()
|
|
|
|
|
|
# load_replay_json
|
|
|
|
|
|
def test_load_replay_json_raises_when_env_unset(unset_replay_dir):
|
|
# Assert
|
|
with pytest.raises(FileNotFoundError, match="E2E_SITL_REPLAY_DIR.*not set"):
|
|
rm.load_replay_json("any.json")
|
|
|
|
|
|
def test_load_replay_json_raises_when_env_empty(monkeypatch: pytest.MonkeyPatch):
|
|
# Arrange
|
|
monkeypatch.setenv("E2E_SITL_REPLAY_DIR", " ")
|
|
|
|
# Assert
|
|
with pytest.raises(FileNotFoundError, match="not set or empty"):
|
|
rm.load_replay_json("any.json")
|
|
|
|
|
|
def test_load_replay_json_raises_when_file_missing(replay_dir: Path):
|
|
# Assert
|
|
with pytest.raises(FileNotFoundError, match="replay fixture 'gone.json' not found"):
|
|
rm.load_replay_json("gone.json")
|
|
|
|
|
|
def test_load_replay_json_raises_on_malformed_json(replay_dir: Path):
|
|
# Arrange
|
|
(replay_dir / "bad.json").write_text("{not valid")
|
|
|
|
# Assert
|
|
with pytest.raises(ValueError, match="malformed replay fixture JSON"):
|
|
rm.load_replay_json("bad.json")
|
|
|
|
|
|
def test_load_replay_json_round_trips_dict(replay_dir: Path):
|
|
# Arrange
|
|
payload = {"key": "value", "nested": {"a": 1}}
|
|
(replay_dir / "ok.json").write_text(json.dumps(payload))
|
|
|
|
# Act
|
|
result = rm.load_replay_json("ok.json")
|
|
|
|
# Assert
|
|
assert result == payload
|
|
|
|
|
|
def test_load_replay_json_round_trips_list(replay_dir: Path):
|
|
# Arrange
|
|
payload = [{"frame_idx": i} for i in range(3)]
|
|
(replay_dir / "list.json").write_text(json.dumps(payload))
|
|
|
|
# Act
|
|
result = rm.load_replay_json("list.json")
|
|
|
|
# Assert
|
|
assert result == payload
|
|
|
|
|
|
# resolve_replay_subdir
|
|
|
|
|
|
def test_resolve_replay_subdir_raises_when_env_unset(unset_replay_dir):
|
|
# Assert
|
|
with pytest.raises(FileNotFoundError, match="E2E_SITL_REPLAY_DIR.*not set"):
|
|
rm.resolve_replay_subdir("frames")
|
|
|
|
|
|
def test_resolve_replay_subdir_raises_when_subdir_missing(replay_dir: Path):
|
|
# Assert
|
|
with pytest.raises(FileNotFoundError, match="replay fixture subdir 'frames' not found"):
|
|
rm.resolve_replay_subdir("frames")
|
|
|
|
|
|
def test_resolve_replay_subdir_returns_path_when_exists(replay_dir: Path):
|
|
# Arrange
|
|
target = replay_dir / "frames"
|
|
target.mkdir()
|
|
|
|
# Act
|
|
result = rm.resolve_replay_subdir("frames")
|
|
|
|
# Assert
|
|
assert result == target
|
|
assert result.is_dir()
|
|
|
|
|
|
def test_resolve_replay_subdir_rejects_file_at_path(replay_dir: Path):
|
|
# Arrange
|
|
(replay_dir / "actually-a-file").write_text("oops")
|
|
|
|
# Assert
|
|
with pytest.raises(FileNotFoundError, match="subdir 'actually-a-file' not found"):
|
|
rm.resolve_replay_subdir("actually-a-file")
|