Files
NODEDC_MISSION_CORE/tests/test_e10_integrated_perception.py
T

756 lines
25 KiB
Python

from __future__ import annotations
import hashlib
import importlib.util
import json
import sys
import threading
import time
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from types import SimpleNamespace
from typing import cast
import numpy as np
import pytest
import k1link.compute.integrated_perception as integrated_module
from k1link.artifact_gateway import (
ArtifactGateway,
ArtifactManifest,
ArtifactMember,
ArtifactStoreUnavailable,
ResolvedArtifact,
)
from k1link.compute.integrated_perception import (
IntegratedPerceptionOverlayStore,
_CuboidPresentationState,
)
from k1link.compute.results import RecordedPerceptionOverlayError
def _write_result_descriptor(
results_root: Path,
*,
session_id: str,
created_at_utc: str,
) -> Path:
identity = {
"schema_version": integrated_module.IDENTITY_SCHEMA,
"job_id": f"recorded-camera-{'1' * 24}",
"session_id": session_id,
}
identity_sha256 = hashlib.sha256(
json.dumps(
identity,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode()
).hexdigest()
result_id = f"e10-integrated-perception-{identity_sha256}"
root = results_root / result_id
root.mkdir()
(root / "result.json").write_text(
json.dumps(
{
"schema_version": integrated_module.RESULT_SCHEMA,
"result_id": result_id,
"identity": identity,
"identity_sha256": identity_sha256,
"acceptance_state": "accepted",
"publication_scope": "recorded-integrated-realtime-qualification-only",
"created_at_utc": created_at_utc,
}
)
)
return root
def _worker_modules() -> tuple[object, object]:
root = Path(__file__).resolve().parents[1] / "experiments" / "perception"
worker = root / "worker"
sys.path.insert(0, str(root))
sys.path.insert(0, str(worker))
try:
fusion_spec = importlib.util.spec_from_file_location(
"e10_test_fusion", root / "e10_fusion_runtime.py"
)
assert fusion_spec is not None and fusion_spec.loader is not None
fusion = importlib.util.module_from_spec(fusion_spec)
sys.modules[fusion_spec.name] = fusion
fusion_spec.loader.exec_module(fusion)
runner_spec = importlib.util.spec_from_file_location(
"e10_test_runner", worker / "run_e10_integrated_perception.py"
)
assert runner_spec is not None and runner_spec.loader is not None
runner = importlib.util.module_from_spec(runner_spec)
sys.modules[runner_spec.name] = runner
runner_spec.loader.exec_module(runner)
return fusion, runner
finally:
sys.path.pop(0)
sys.path.pop(0)
def test_integrated_overlay_recovers_admission_from_sealed_cache_without_revalidation(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
jobs_root = tmp_path / "jobs"
results_root = tmp_path / "results"
lidar_packs_root = tmp_path / "lidar-packs"
cache_root = tmp_path / "cache"
for root in (jobs_root, results_root, lidar_packs_root):
root.mkdir()
result = _write_result_descriptor(
results_root,
session_id="session-1",
created_at_utc="2026-07-29T12:00:00Z",
)
payload = b"RRF2sealed-overlay"
recording_id = "recording-1"
cache = cache_root / "session-1" / result.name
cache.mkdir(parents=True)
output = cache / f"{recording_id}.rrd"
output.write_bytes(payload)
(cache / f"{recording_id}.rrd.cache.json").write_text(
json.dumps(
{
"schema_version": integrated_module.OVERLAY_CACHE_SCHEMA,
"renderer_version": integrated_module.OVERLAY_RENDERER_VERSION,
"result_id": result.name,
"recording_id": recording_id,
"byte_length": len(payload),
"sha256": hashlib.sha256(payload).hexdigest(),
}
)
)
def reject_revalidation(*_args: object, **_kwargs: object) -> object:
raise AssertionError("sealed presentation cache must not revalidate source artifacts")
monkeypatch.setattr(
integrated_module,
"validate_integrated_perception_result",
reject_revalidation,
)
store = IntegratedPerceptionOverlayStore(
jobs_root=jobs_root,
results_root=results_root,
lidar_packs_root=lidar_packs_root,
cache_root=cache_root,
ffmpeg_path=tmp_path / "ffmpeg",
)
artifact = store.materialize(
"session-1",
application_id="nodedc_mission_core_recorded",
recording_id=recording_id,
)
assert artifact is not None
assert artifact.path == output.resolve()
assert artifact.byte_length == len(payload)
assert artifact.sha256 == hashlib.sha256(payload).hexdigest()
admission = json.loads((cache_root / "session-1" / "admission.json").read_text())
assert admission["result_id"] == result.name
assert store.status("session-1", recording_id=recording_id) == {
"state": "ready",
"phase": "ready",
"elapsed_seconds": pytest.approx(0.0, abs=0.1),
"byte_length": len(payload),
}
restarted = IntegratedPerceptionOverlayStore(
jobs_root=jobs_root,
results_root=results_root,
lidar_packs_root=lidar_packs_root,
cache_root=cache_root,
ffmpeg_path=tmp_path / "ffmpeg",
)
restarted_artifact = restarted.materialize(
"session-1",
application_id="nodedc_mission_core_recorded",
recording_id=recording_id,
)
assert restarted_artifact == artifact
assert (
restarted.render(
"session-1",
application_id="nodedc_mission_core_recorded",
recording_id=recording_id,
)
== payload
)
def test_integrated_overlay_serializes_heavy_materialization_across_sessions(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
for name in ("jobs", "results", "lidar-packs"):
(tmp_path / name).mkdir()
store = IntegratedPerceptionOverlayStore(
jobs_root=tmp_path / "jobs",
results_root=tmp_path / "results",
lidar_packs_root=tmp_path / "lidar-packs",
cache_root=tmp_path / "cache",
ffmpeg_path=tmp_path / "ffmpeg",
)
result_id = f"e10-integrated-perception-{'a' * 64}"
result = SimpleNamespace(
result_id=result_id,
created_at_utc="2026-07-29T12:00:00Z",
)
monkeypatch.setattr(store, "_read_admitted_cache", lambda *_args: None)
monkeypatch.setattr(store, "_latest", lambda _session_id: result)
monkeypatch.setattr(store, "_write_admission", lambda _result: None)
active = 0
maximum_active = 0
active_lock = threading.Lock()
def render_overlay(*_args: object, **_kwargs: object) -> bytes:
nonlocal active, maximum_active
with active_lock:
active += 1
maximum_active = max(maximum_active, active)
time.sleep(0.05)
with active_lock:
active -= 1
return b"RRF2materialized"
monkeypatch.setattr(integrated_module, "_render", render_overlay)
with ThreadPoolExecutor(max_workers=2) as executor:
results = tuple(
executor.map(
lambda item: store.render(
item,
application_id="nodedc_mission_core_recorded",
recording_id=f"recording-{item}",
),
("session-1", "session-2"),
)
)
assert results == (b"RRF2materialized", b"RRF2materialized")
assert maximum_active == 1
def test_integrated_overlay_uses_verified_central_cache_before_local_result_scan(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
for name in ("jobs", "results", "lidar-packs"):
(tmp_path / name).mkdir()
recording_id = "recording-1"
result_id = f"e10-integrated-perception-{'a' * 64}"
payload = b"RRF2central-overlay"
artifact_path = tmp_path / "artifact-cache" / "overlay.rrd"
artifact_path.parent.mkdir()
artifact_path.write_bytes(payload)
member = ArtifactMember(
role=f"integrated-overlay:{recording_id}",
media_type="application/vnd.rerun.rrd",
sha256=hashlib.sha256(payload).hexdigest(),
byte_length=len(payload),
)
manifest = ArtifactManifest(
manifest_id="b" * 64,
artifact_type="recorded-session",
subject_id="session-1",
created_at_utc="2026-07-30T00:00:00Z",
members=(member,),
metadata={"integrated-result-id": result_id},
)
class FakeGateway:
def resolve_role(self, namespace: str, key: str, role: str) -> ResolvedArtifact:
assert (namespace, key, role) == (
"sessions",
"session-1",
f"integrated-overlay:{recording_id}",
)
return ResolvedArtifact(
manifest=manifest,
member=member,
path=artifact_path,
cache_hit=False,
central_available=True,
)
store = IntegratedPerceptionOverlayStore(
jobs_root=tmp_path / "jobs",
results_root=tmp_path / "results",
lidar_packs_root=tmp_path / "lidar-packs",
cache_root=tmp_path / "cache",
ffmpeg_path=tmp_path / "ffmpeg",
artifact_gateway=cast(ArtifactGateway, FakeGateway()),
)
monkeypatch.setattr(
store,
"_latest_descriptor",
lambda _session_id: (_ for _ in ()).throw(
AssertionError("central cache hit must not scan local results")
),
)
artifact = store.materialize(
"session-1",
application_id="nodedc_mission_core_recorded",
recording_id=recording_id,
)
assert artifact is not None
assert artifact.path == artifact_path
assert artifact.sha256 == member.sha256
def test_integrated_overlay_does_not_render_when_central_store_is_offline(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
for name in ("jobs", "results", "lidar-packs"):
(tmp_path / name).mkdir()
class OfflineGateway:
def resolve_role(
self,
namespace: str,
key: str,
role: str,
) -> object:
assert (namespace, key, role) == (
"sessions",
"session-1",
"integrated-overlay:recording-1",
)
raise ArtifactStoreUnavailable("offline cache miss")
store = IntegratedPerceptionOverlayStore(
jobs_root=tmp_path / "jobs",
results_root=tmp_path / "results",
lidar_packs_root=tmp_path / "lidar-packs",
cache_root=tmp_path / "cache",
ffmpeg_path=tmp_path / "ffmpeg",
artifact_gateway=OfflineGateway(), # type: ignore[arg-type]
)
monkeypatch.setattr(
store,
"_latest",
lambda _session_id: (_ for _ in ()).throw(
AssertionError("offline CAS miss must not scan local results")
),
)
with pytest.raises(
RecordedPerceptionOverlayError,
match="not present in the local artifact cache",
):
store.materialize(
"session-1",
application_id="nodedc_mission_core_recorded",
recording_id="recording-1",
)
def test_e10_profile_pins_integrated_realtime_budget() -> None:
_fusion, runner = _worker_modules()
profile_path = (
Path(__file__).resolve().parents[1]
/ "experiments"
/ "perception"
/ "worker"
/ "e10_integrated_perception_profile.json"
)
profile, digest = runner.read_profile(profile_path)
assert len(digest) == 64
assert profile["replay"] == {
"speed": 1.0,
"detector_queue_capacity": 2,
"semantic_queue_capacity": 1,
"semantic_sample_every_frames": 5,
"semantic_ttl_ms": 750.0,
}
assert profile["acceptance"]["detector_minimum_effective_fps"] == 9.5
assert profile["acceptance"]["maximum_p95_world_state_age_ms"] == 175.0
assert profile["acceptance"]["minimum_lidar_fused_frames"] == 450
def test_e10_semantic_loss_profile_is_explicit_and_fail_closed() -> None:
_fusion, runner = _worker_modules()
profile_path = (
Path(__file__).resolve().parents[1]
/ "experiments"
/ "perception"
/ "worker"
/ "e10_semantic_loss_profile.json"
)
profile, digest = runner.read_profile(profile_path)
assert len(digest) == 64
assert profile["mode"] == "semantic-loss-negative-control"
assert profile["semantic_loss"]["stop_after_completed_results"] == 20
assert profile["acceptance"]["minimum_stale_detector_frames"] == 450
assert profile["replay"]["semantic_ttl_ms"] == 750.0
def test_e10_full_session_profile_pins_complete_camera_epoch() -> None:
_fusion, runner = _worker_modules()
profile_path = (
Path(__file__).resolve().parents[1]
/ "experiments"
/ "perception"
/ "worker"
/ "e10_full_session_profile.json"
)
profile, digest = runner.read_profile(profile_path)
assert len(digest) == 64
assert profile["mode"] == "full-session-qualification"
assert profile["selection"] == {
"required_frame_count": 4489,
"required_source_start_frame_index": 0,
"required_source_end_frame_index": 4488,
"minimum_source_span_seconds": 448.0,
}
assert profile["acceptance"]["detector_maximum_drop_fraction"] == 0.0
assert profile["acceptance"]["minimum_lidar_fused_frames"] == 3500
def test_e13_profile_pins_provenance_marked_amodal_completion() -> None:
_fusion, runner = _worker_modules()
profile_path = (
Path(__file__).resolve().parents[1]
/ "experiments"
/ "perception"
/ "worker"
/ "e13_amodal_cuboid_profile.json"
)
profile, digest = runner.read_profile(profile_path)
assert len(digest) == 64
assert profile["mode"] == "pilot"
assert profile["cuboid_completion"]["mode"] == "class-prior-amodal-v1"
assert profile["cuboid_completion"]["classes"]["car"]["nominal_size_m"] == [
4.5,
1.85,
1.55,
]
assert profile["cuboid_completion"]["temporal"]["confirmation_hits"] == 3
def test_e19_profile_adds_ground_aware_support_without_mutating_e14() -> None:
_fusion, runner = _worker_modules()
root = Path(__file__).resolve().parents[1] / "experiments" / "perception" / "worker"
e14, _e14_digest = runner.read_profile(root / "e14_full_session_amodal_profile.json")
e19, _e19_digest = runner.read_profile(root / "e19_ground_aware_cuboid_profile.json")
assert "object_support_ground_filter" not in e14["association"]
assert "support_duplicate_overlap_threshold" not in e14["association"]
assert e19["profile_id"] == "lab-e19-ground-aware-cuboids-v1"
assert (
e19["association"]["object_support_ground_filter"]["mode"]
== "local-ground-relative-object-support-v1"
)
assert e19["association"]["support_duplicate_overlap_threshold"] == 0.6
def test_e14_profile_combines_full_session_and_amodal_gates() -> None:
_fusion, runner = _worker_modules()
profile_path = (
Path(__file__).resolve().parents[1]
/ "experiments"
/ "perception"
/ "worker"
/ "e14_full_session_amodal_profile.json"
)
profile, digest = runner.read_profile(profile_path)
assert len(digest) == 64
assert profile["mode"] == "full-session-qualification"
assert profile["selection"] == {
"required_frame_count": 4489,
"required_source_start_frame_index": 0,
"required_source_end_frame_index": 4488,
"minimum_source_span_seconds": 448.0,
}
assert profile["cuboid_completion"]["mode"] == "class-prior-amodal-v1"
assert profile["cuboid_completion"]["failure_policy"] == "reject"
assert profile["acceptance"]["minimum_lidar_fused_frames"] == 3500
assert profile["acceptance"]["minimum_accepted_cuboids"] == 1500
def test_recorded_cuboid_presentation_holds_one_failed_association_then_expires() -> None:
state = _CuboidPresentationState(hold_ns=500_000_000)
accepted = {
"association_group": "vehicle",
"clustered_points": 14,
"cuboid_status": "accepted-class-prior-amodal-v1",
"distance_smoothed_m": 8.25,
"label": "car",
"track_id": 7,
}
initial = state.update(
1_000_000_000,
[accepted],
np.asarray([[1.0, 2.0, 3.0]], dtype=np.float32),
np.asarray([[2.25, 0.925, 0.775]], dtype=np.float32),
np.asarray([[0.0, 0.0, 0.0, 1.0]], dtype=np.float32),
np.asarray([[118, 204, 132, 88]], dtype=np.uint8),
)
assert initial is not None
assert initial[4] == ["vehicle #7 car · 8.2 m · 14 pts"]
rejected = {"cuboid_status": "rejected-no-semantic-lidar-support", "track_id": 7}
held = state.update(
1_200_000_000,
[rejected],
np.empty((0, 3), dtype=np.float32),
np.empty((0, 3), dtype=np.float32),
np.empty((0, 4), dtype=np.float32),
np.empty((0, 4), dtype=np.uint8),
)
assert held is not None
assert held[4] == ["vehicle #7 car · 8.2 m · 14 pts · hold 200 ms"]
assert int(held[3][0, 3]) < 88
expired = state.update(
1_600_000_001,
[],
np.empty((0, 3), dtype=np.float32),
np.empty((0, 3), dtype=np.float32),
np.empty((0, 4), dtype=np.float32),
np.empty((0, 4), dtype=np.uint8),
)
assert expired is None
def test_recorded_cuboid_presentation_omits_unavailable_distance() -> None:
state = _CuboidPresentationState(hold_ns=500_000_000)
accepted = {
"association_group": "person",
"clustered_points": 3,
"cuboid_status": "accepted-world-track-provisional-e24-v1",
"distance_smoothed_m": None,
"label": "person",
"track_id": 9,
}
presented = state.update(
1_000_000_000,
[accepted],
np.asarray([[1.0, 2.0, 3.0]], dtype=np.float32),
np.asarray([[0.35, 0.35, 0.9]], dtype=np.float32),
np.asarray([[0.0, 0.0, 0.0, 1.0]], dtype=np.float32),
np.asarray([[118, 204, 132, 88]], dtype=np.uint8),
)
assert presented is not None
assert presented[4] == ["person #9 person · 3 pts"]
def test_e13_completes_a_visible_car_face_away_from_the_sensor() -> None:
fusion, runner = _worker_modules()
profile, _digest = runner.read_profile(
Path(__file__).resolve().parents[1]
/ "experiments"
/ "perception"
/ "worker"
/ "e13_amodal_cuboid_profile.json"
)
y = np.linspace(-0.8, 0.8, 12)
z = np.linspace(0.35, 1.3, 5)
support = np.asarray(
[[10.0 + 0.01 * (index % 2), side, height] for index, side in enumerate(y) for height in z],
dtype=np.float64,
)
ground = np.asarray(
[[x, side, 0.0] for x in np.linspace(8.0, 12.0, 8) for side in (-1.5, 0.0, 1.5)],
dtype=np.float64,
)
observed = fusion._cuboid(support, profile["association"], "vehicle")
assert observed is not None
tracker = fusion.CuboidCompletionTracker(profile["cuboid_completion"])
completed = tracker.complete(
track_id=4,
label="car",
support_points_map=support,
all_points_map=np.concatenate((support, ground)),
sensor_position_map=(0.0, 0.0, 0.0),
session_seconds=1.0,
observed_cuboid=observed,
)
assert completed is not None
assert completed.orientation_source == "support-face-normal"
completed_size = np.asarray(completed.cuboid.half_size) * 2.0
assert completed_size[0] == 4.5
assert 1.85 <= completed_size[1] <= 2.0
assert completed_size[2] == 1.55
assert completed.cuboid.center_map[0] > 12.0
assert completed.ground_z_map == 0.0
assert completed.completion_fraction > 0.8
assert completed.support_coverage_fraction >= 0.75
def test_e13_temporal_filter_reduces_cuboid_center_jitter() -> None:
fusion, runner = _worker_modules()
profile, _digest = runner.read_profile(
Path(__file__).resolve().parents[1]
/ "experiments"
/ "perception"
/ "worker"
/ "e13_amodal_cuboid_profile.json"
)
tracker = fusion.CuboidCompletionTracker(profile["cuboid_completion"])
base = np.asarray(
[
[x, 2.0 + 0.02 * (index % 2), z]
for index, x in enumerate(np.linspace(8.0, 11.5, 20))
for z in (0.4, 0.9, 1.3)
],
dtype=np.float64,
)
ground = np.asarray([[x, y, 0.0] for x in np.linspace(7.0, 13.0, 10) for y in (0.5, 2.0, 3.5)])
outputs = []
for frame, lateral_jitter in enumerate((0.0, 0.4, 0.2), start=1):
support = base + np.asarray([0.0, lateral_jitter, 0.0])
observed = fusion._cuboid(support, profile["association"], "vehicle")
assert observed is not None
value = tracker.complete(
track_id=9,
label="car",
support_points_map=support,
all_points_map=np.concatenate((support, ground)),
sensor_position_map=(0.0, 0.0, 0.0),
session_seconds=frame * 0.1,
observed_cuboid=observed,
)
assert value is not None
outputs.append(value)
lateral_shift = abs(outputs[1].cuboid.center_map[1] - outputs[0].cuboid.center_map[1])
assert lateral_shift < 0.4
assert outputs[-1].temporal_status == "confirmed"
def test_e19_ground_filter_preserves_cloud_and_excludes_ground_from_box_support() -> None:
fusion, runner = _worker_modules()
profile, _digest = runner.read_profile(
Path(__file__).resolve().parents[1]
/ "experiments"
/ "perception"
/ "worker"
/ "e19_ground_aware_cuboid_profile.json"
)
ground = np.asarray(
[[x, y, 0.0] for x in (9.5, 10.0, 10.5) for y in (-0.6, 0.0, 0.6)],
dtype=np.float64,
)
vehicle = np.asarray(
[[x, y, z] for x in (9.8, 10.2) for y in (-0.4, 0.4) for z in (0.3, 0.9, 1.4)],
dtype=np.float64,
)
cloud = np.concatenate((ground, vehicle))
unchanged = cloud.copy()
indices = np.arange(cloud.shape[0], dtype=np.int64)
filtered, ground_z, rejected = fusion._filter_object_support_by_ground(
indices,
cloud,
group="vehicle",
profile=profile["association"]["object_support_ground_filter"],
)
assert ground_z == 0.0
assert rejected == ground.shape[0]
assert filtered.tolist() == list(range(ground.shape[0], cloud.shape[0]))
np.testing.assert_array_equal(cloud, unchanged)
def test_e19_duplicate_tracks_cannot_publish_the_same_lidar_support_twice() -> None:
fusion, _runner = _worker_modules()
cuboid = fusion.Cuboid(
center_map=(10.0, 0.0, 0.8),
half_size=(2.25, 0.925, 0.775),
quaternion_xyzw=(0.0, 0.0, 0.0, 1.0),
)
def item(track_id: int, score: float, indices: list[int]) -> object:
source = np.asarray(indices, dtype=np.int64)
return fusion.TrackFusion(
track_id=track_id,
label="car",
association_group="vehicle",
score=score,
bbox_xyxy=(100.0, 100.0, 200.0, 200.0),
candidate_points=source.size,
semantic_points=source.size,
clustered_points=source.size,
distance_p10_m=9.5,
distance_median_m=10.0,
distance_smoothed_m=10.0,
status="accepted-class-prior-amodal-v1",
cuboid=cuboid,
source_indices=source,
)
result = fusion._suppress_duplicate_support_fusions(
[
item(10, 0.91, [1, 2, 3, 4, 5]),
item(11, 0.72, [1, 2, 3, 4]),
item(12, 0.80, [20, 21, 22, 23]),
],
overlap_threshold=0.6,
)
by_track = {value.track_id: value for value in result}
assert by_track[10].cuboid is not None
assert by_track[11].cuboid is None
assert by_track[11].status == "rejected-duplicate-lidar-support"
assert by_track[11].source_indices.size == 0
assert by_track[12].cuboid is not None
def test_e10_semantic_binding_is_explicit_about_freshness() -> None:
_fusion, runner = _worker_modules()
assert runner.semantic_binding(None, 2.0, 750.0) == ("unavailable", None)
value = runner.SemanticResult(
frame_index=1,
source_frame_index=1001,
session_seconds=1.0,
completion_age_ms=200.0,
completed_monotonic=3.0,
mask=np.zeros((600, 800), dtype=np.uint8),
mask_sha256="0" * 64,
class_pixels={},
)
assert runner.semantic_binding(value, 1.7, 750.0) == ("fresh", 700.0)
assert runner.semantic_binding(value, 1.8, 750.0) == (
"stale",
800.0,
)
def test_e10_projection_keeps_only_nearest_point_per_pixel() -> None:
fusion, _runner = _worker_modules()
profile = fusion.ProjectionProfile(
width=800,
height=600,
intrinsic_fx_fy_cx_cy=(100.0, 100.0, 400.0, 300.0),
distortion_kb4=(0.0, 0.0, 0.0, 0.0),
t_camera_from_lidar=np.eye(4, dtype=np.float64),
)
points = np.asarray([[0.0, 0.0, 2.0], [0.0, 0.0, 3.0]], dtype=np.float64)
pixels, depths, source, _lidar = fusion.project_points(
points,
(0.0, 0.0, 0.0),
(0.0, 0.0, 0.0, 1.0),
profile,
)
assert pixels.shape == (1, 2)
assert depths.tolist() == [2.0]
assert source.tolist() == [0]