diff --git a/src/k1link/compute/lidar_replay.py b/src/k1link/compute/lidar_replay.py index 394c0a7..f78299c 100644 --- a/src/k1link/compute/lidar_replay.py +++ b/src/k1link/compute/lidar_replay.py @@ -398,12 +398,16 @@ def build_lidar_replay_pack_v2( pose_coverage_threshold_ms=pose_coverage_threshold_ms, ) _write_json(staging / LIDAR_QUALITY_REPORT_NAME, quality) - equivalence = _equivalence_report( - pack_id, - logical_sha256, - source, - arrays, - ) + persisted_arrays = _MaterializedNpz(arrays_path) + try: + equivalence = _equivalence_report_from_arrays( + pack_id, + logical_sha256, + arrays, + persisted_arrays, + ) + finally: + persisted_arrays.close() _write_json(staging / LIDAR_EQUIVALENCE_REPORT_NAME, equivalence) artifacts = [ _artifact_descriptor("lidar-arrays", arrays_path, "application/x-npz"), @@ -816,7 +820,22 @@ def _equivalence_report( capture_path: Path, replay_arrays: Any, ) -> dict[str, object]: - source_arrays = _capture_arrays(capture_path) + return _equivalence_report_from_arrays( + pack_id, + logical_sha256, + _capture_arrays(capture_path), + replay_arrays, + ) + + +def _equivalence_report_from_arrays( + pack_id: str, + logical_sha256: str, + source_arrays: Any, + replay_arrays: Any, +) -> dict[str, object]: + """Compare one source-derived array set with its persisted replay image.""" + comparisons: dict[str, dict[str, object]] = {} mismatch_count = 0 for name in sorted(_ARRAY_DTYPES): diff --git a/tests/test_lidar_replay.py b/tests/test_lidar_replay.py index 845f64b..dfe7ac0 100644 --- a/tests/test_lidar_replay.py +++ b/tests/test_lidar_replay.py @@ -10,6 +10,7 @@ import pytest from fastapi import APIRouter from fastapi.routing import APIRoute +import k1link.compute.lidar_replay as lidar_replay_module from k1link.compute import ( K1_LIDAR_PACK_V2_PROFILE, K1LocalSurfaceV1, @@ -253,6 +254,35 @@ def test_lidar_replay_v2_retains_fields_and_passes_equivalence(tmp_path: Path) - assert build_lidar_replay_pack_v2(capture, tmp_path / "packs") == output +def test_lidar_replay_build_decodes_source_once( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + capture = _capture(tmp_path) + original_capture_arrays = lidar_replay_module._capture_arrays + calls: list[Path] = [] + + def counting_capture_arrays(path: Path) -> dict[str, Any]: + calls.append(path) + return original_capture_arrays(path) + + monkeypatch.setattr( + lidar_replay_module, + "_capture_arrays", + counting_capture_arrays, + ) + + output = build_lidar_replay_pack_v2(capture, tmp_path / "packs") + pack = LidarReplayPackV2(output) + try: + assert pack.equivalence["status"] == "passed" + assert pack.equivalence["array_mismatches"] == 0 + finally: + pack.close() + + assert calls == [capture.resolve(strict=True)] + + def test_lidar_replay_v2_unblocks_detector_input_but_not_mapping() -> None: assessments = { item.stage: item for item in assess_lidar_profile(K1_LIDAR_PACK_V2_PROFILE)