refactor(lab): объединить RAV004 в единый Rerun replay
This commit is contained in:
@@ -22,11 +22,14 @@ import k1link.web.vegetation_shadow_lab_api as vegetation_api_module
|
||||
from k1link.laboratory import LaboratoryEvidenceRegistry
|
||||
from k1link.laboratory.canonical_rerun_overlay import (
|
||||
CanonicalLabOverlayArtifact,
|
||||
CanonicalLabReplayArtifact,
|
||||
_artifact_is_regular,
|
||||
_encoded_semantic_png,
|
||||
_localized_semantic_label,
|
||||
_optimize_overlay,
|
||||
_semantic_palette,
|
||||
_video_reference_timestamps,
|
||||
canonical_lab_replay,
|
||||
)
|
||||
from k1link.laboratory.evidence_report import verify_laboratory_evidence_result
|
||||
from k1link.laboratory.vegetation_policy_review import seal_vegetation_policy_review
|
||||
@@ -51,6 +54,17 @@ def test_semantic_component_boxes_keep_distinct_objects_separate() -> None:
|
||||
(18, 4, 24, 12, 48),
|
||||
(3, 2, 8, 10, 40),
|
||||
]
|
||||
_, labels = canonical_overlay_module.semantic_component_boxes(mask, 0)
|
||||
assert labels == [
|
||||
"автомобиль · 50%",
|
||||
"автомобиль · 50%",
|
||||
]
|
||||
|
||||
|
||||
def test_canonical_overlay_localizes_current_taxonomies() -> None:
|
||||
assert _localized_semantic_label("high_grass") == "высокая трава"
|
||||
assert _localized_semantic_label("tree_trunk") == "ствол дерева"
|
||||
assert _localized_semantic_label("future_class") == "future_class"
|
||||
|
||||
|
||||
def test_canonical_video_references_hold_only_missing_source_samples() -> None:
|
||||
@@ -152,6 +166,59 @@ def test_canonical_overlay_compacts_chunks_before_cache_publication(
|
||||
assert source.read_bytes() == b"RRF2-optimized"
|
||||
|
||||
|
||||
def test_canonical_replay_merges_base_and_overlay_once(
|
||||
tmp_path: Path,
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
base = tmp_path / "base.rrd"
|
||||
base.write_bytes(b"RRF2-base")
|
||||
overlay_path = tmp_path / "overlay.rrd"
|
||||
overlay_path.write_bytes(b"RRF2-overlay")
|
||||
overlay = CanonicalLabOverlayArtifact(
|
||||
path=overlay_path,
|
||||
byte_length=overlay_path.stat().st_size,
|
||||
sha256=_sha256(overlay_path),
|
||||
)
|
||||
calls = 0
|
||||
|
||||
def optimize(command: list[str], **options: object) -> SimpleNamespace:
|
||||
nonlocal calls
|
||||
calls += 1
|
||||
assert command[13:15] == [str(base), str(overlay_path)]
|
||||
assert command[15] == "-o"
|
||||
Path(command[16]).write_bytes(b"RRF2-merged")
|
||||
assert options == {"check": False, "capture_output": True, "timeout": 180}
|
||||
return SimpleNamespace(returncode=0, stderr=b"")
|
||||
|
||||
monkeypatch.setattr(canonical_overlay_module.subprocess, "run", optimize)
|
||||
monkeypatch.setattr(
|
||||
canonical_overlay_module,
|
||||
"canonical_recording_id",
|
||||
lambda _path: "recording-001",
|
||||
)
|
||||
result_id = f"lab-v1-vegetation-shadow-{'e' * 64}"
|
||||
first = canonical_lab_replay(
|
||||
base,
|
||||
base_generation_sha256=_sha256(base),
|
||||
overlay=overlay,
|
||||
result_id=result_id,
|
||||
recording_id="recording-001",
|
||||
cache_root=tmp_path / "cache",
|
||||
)
|
||||
second = canonical_lab_replay(
|
||||
base,
|
||||
base_generation_sha256=_sha256(base),
|
||||
overlay=overlay,
|
||||
result_id=result_id,
|
||||
recording_id="recording-001",
|
||||
cache_root=tmp_path / "cache",
|
||||
)
|
||||
|
||||
assert first == second
|
||||
assert first.path.read_bytes() == b"RRF2-merged"
|
||||
assert calls == 1
|
||||
|
||||
|
||||
def test_canonical_overlay_get_is_generation_bound_and_range_streamable(
|
||||
tmp_path: Path,
|
||||
monkeypatch,
|
||||
@@ -163,6 +230,8 @@ def test_canonical_overlay_get_is_generation_bound_and_range_streamable(
|
||||
base.write_bytes(b"RRF2-base")
|
||||
overlay = tmp_path / "overlay.rrd"
|
||||
overlay.write_bytes(b"RRF2-overlay")
|
||||
replay = tmp_path / "replay.rrd"
|
||||
replay.write_bytes(b"RRF2-replay")
|
||||
generation = "b" * 64
|
||||
recording_id = "recording-001"
|
||||
artifact = CanonicalLabOverlayArtifact(
|
||||
@@ -170,6 +239,11 @@ def test_canonical_overlay_get_is_generation_bound_and_range_streamable(
|
||||
byte_length=overlay.stat().st_size,
|
||||
sha256=_sha256(overlay),
|
||||
)
|
||||
replay_artifact = CanonicalLabReplayArtifact(
|
||||
path=replay,
|
||||
byte_length=replay.stat().st_size,
|
||||
sha256=_sha256(replay),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
vegetation_api_module,
|
||||
"_resolve_candidate",
|
||||
@@ -195,6 +269,11 @@ def test_canonical_overlay_get_is_generation_bound_and_range_streamable(
|
||||
"canonical_lab_overlay",
|
||||
lambda *_args, **_kwargs: artifact,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
vegetation_api_module,
|
||||
"canonical_lab_replay",
|
||||
lambda *_args, **_kwargs: replay_artifact,
|
||||
)
|
||||
ffmpeg = tmp_path / "ffmpeg"
|
||||
ffmpeg.write_bytes(b"fixture")
|
||||
ffmpeg.chmod(0o700)
|
||||
@@ -262,6 +341,33 @@ def test_canonical_overlay_get_is_generation_bound_and_range_streamable(
|
||||
)
|
||||
assert stale_overlay.status_code == 412
|
||||
|
||||
replay_endpoint = (
|
||||
f"/api/v1/laboratory/vegetation-shadow/{result_id}/canonical-replay.rrd"
|
||||
)
|
||||
replay_descriptor = client.head(
|
||||
replay_endpoint,
|
||||
params={"base_generation": generation},
|
||||
)
|
||||
assert replay_descriptor.status_code == 200
|
||||
assert replay_descriptor.headers["content-length"] == str(replay_artifact.byte_length)
|
||||
assert replay_descriptor.headers["etag"] == f'"{replay_artifact.sha256}"'
|
||||
assert replay_descriptor.headers["x-rerun-format"] == "RRF2"
|
||||
|
||||
replay_response = client.get(
|
||||
replay_endpoint,
|
||||
params={"generation": replay_artifact.sha256},
|
||||
headers={"Range": "bytes=0-3"},
|
||||
)
|
||||
assert replay_response.status_code == 206
|
||||
assert replay_response.content == b"RRF2"
|
||||
assert replay_response.headers["etag"] == f'"{replay_artifact.sha256}"'
|
||||
|
||||
stale_replay = client.get(
|
||||
replay_endpoint,
|
||||
params={"generation": "f" * 64},
|
||||
)
|
||||
assert stale_replay.status_code == 412
|
||||
|
||||
|
||||
def test_route_playback_chunk_descriptor_seals_only_requested_binary_window() -> None:
|
||||
points = np.arange(18, dtype="<f4").reshape(6, 3)
|
||||
|
||||
Reference in New Issue
Block a user