108 lines
3.8 KiB
Python
108 lines
3.8 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
import pytest
|
|
from fastapi import APIRouter
|
|
from fastapi.routing import APIRoute
|
|
|
|
from k1link.datasets import (
|
|
DatasetFrameError,
|
|
dataset_gateway_catalog,
|
|
read_semantic_kitti_frame,
|
|
)
|
|
from k1link.web.lidar_api import build_lidar_router
|
|
|
|
|
|
def _endpoint(router: APIRouter, path: str) -> object:
|
|
for route in router.routes:
|
|
if isinstance(route, APIRoute) and route.path == path and "GET" in route.methods:
|
|
return route.endpoint
|
|
raise AssertionError(f"GET {path} route is missing")
|
|
|
|
|
|
def test_goose_semantickitti_frame_retains_xyzi_and_packed_labels(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
points_path = tmp_path / "frame_vls128.bin"
|
|
labels_path = tmp_path / "frame_goose.label"
|
|
np.asarray(
|
|
[
|
|
[1.0, 2.0, 3.0, 0.25],
|
|
[-4.0, 5.0, 0.5, 0.75],
|
|
],
|
|
dtype="<f4",
|
|
).tofile(points_path)
|
|
np.asarray(
|
|
[
|
|
(12 << 16) | 3,
|
|
(99 << 16) | 42,
|
|
],
|
|
dtype="<u4",
|
|
).tofile(labels_path)
|
|
|
|
frame = read_semantic_kitti_frame(points_path, labels_path)
|
|
|
|
assert frame.representation == "native-scan"
|
|
assert frame.point_count == 2
|
|
assert frame.points_xyz_m.tolist() == [[1.0, 2.0, 3.0], [-4.0, 5.0, 0.5]]
|
|
assert frame.remission.tolist() == [0.25, 0.75]
|
|
assert frame.semantic_labels.tolist() == [3, 42]
|
|
assert frame.instance_labels.tolist() == [12, 99]
|
|
assert frame.points_xyz_m.flags.writeable is False
|
|
|
|
|
|
def test_dataset_frame_fails_closed_on_misaligned_or_nonfinite_input(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
points_path = tmp_path / "frame.bin"
|
|
labels_path = tmp_path / "frame.label"
|
|
np.asarray([[1.0, 2.0, 3.0, 0.25]], dtype="<f4").tofile(points_path)
|
|
np.asarray([1, 2], dtype="<u4").tofile(labels_path)
|
|
with pytest.raises(DatasetFrameError, match="counts do not match"):
|
|
read_semantic_kitti_frame(points_path, labels_path)
|
|
|
|
np.asarray([[np.nan, 2.0, 3.0, 0.25]], dtype="<f4").tofile(points_path)
|
|
np.asarray([1], dtype="<u4").tofile(labels_path)
|
|
with pytest.raises(DatasetFrameError, match="non-finite"):
|
|
read_semantic_kitti_frame(points_path, labels_path)
|
|
|
|
|
|
def test_dataset_gateway_enforces_d_storage_and_representation_boundaries(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
blocked = dataset_gateway_catalog(tmp_path / "datasets")
|
|
admitted = dataset_gateway_catalog(Path("/mnt/d/NDC_MISSIONCORE/datasets"))
|
|
|
|
assert blocked["storage"]["status"] == "blocked-storage-policy" # type: ignore[index]
|
|
assert admitted["storage"]["status"] == "ready" # type: ignore[index]
|
|
assert admitted["next_action"] == "download-goose-validation-to-d"
|
|
representations = admitted["representations"]
|
|
assert isinstance(representations, list)
|
|
assert [item["id"] for item in representations] == [ # type: ignore[index]
|
|
"native-scan",
|
|
"normalized-scan",
|
|
"rolling-local-map",
|
|
]
|
|
inputs = admitted["known_inputs"]
|
|
assert isinstance(inputs, list)
|
|
assert inputs[0]["admitted_for_patchworkpp"] is False # type: ignore[index]
|
|
|
|
|
|
def test_dataset_gateway_api_is_read_only_and_path_free(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setenv("MISSIONCORE_DATASET_ROOT", "/mnt/d/NDC_MISSIONCORE/datasets")
|
|
route = _endpoint(build_lidar_router(), "/api/v1/lidar/dataset-gateway")
|
|
response = route() # type: ignore[operator]
|
|
|
|
assert response["schema_version"] == "missioncore.dataset-gateway-catalog/v1"
|
|
assert response["access"] == "read-only"
|
|
assert response["storage"]["admitted"] is True
|
|
assert response["storage"]["path_exposed"] is False
|
|
assert "/mnt/d/NDC_MISSIONCORE/datasets" not in repr(response).replace(
|
|
response["storage"]["required_wsl_root"], # type: ignore[index]
|
|
"",
|
|
)
|