Source: tests/test_dynamic_ttt_pilot.py · Plain text

tests/test_dynamic_ttt_pilot.py

import importlib.util
import json
from pathlib import Path

import numpy as np
import pytest
from PIL import Image

SPEC = importlib.util.spec_from_file_location(
    "pilot_preparation",
    Path(__file__).resolve().parents[1] / "scripts/prepare_dynamic_ttt_pilot.py",
)
pilot = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(pilot)


def fixture_scene(root, split, scene, count=16):
    directory = root / split / scene
    directory.mkdir(parents=True)
    frames = []
    for index in range(count):
        # Deliberate gaps and reversed serialization exercise real frame ordering.
        frame_id = index * 2
        image = f"{frame_id}.png"
        depth = f"{frame_id}.depth.npy"
        normal = f"{frame_id}.normal.npy"
        Image.fromarray(np.zeros((4, 6, 3), dtype=np.uint8)).save(directory / image)
        np.save(directory / depth, np.ones((4, 6), dtype=np.float32))
        np.save(directory / normal, np.zeros((4, 6, 3), dtype=np.float32))
        frames.append(
            {
                "frame_id": frame_id,
                "rgb": image,
                "depth": depth,
                "normal": normal,
                "c2w": np.eye(4).tolist(),
                "intrinsics": np.eye(3).tolist(),
            }
        )
    payload = {
        "schema_version": 1,
        "scene_id": scene,
        "coordinate_convention": "opencv_c2w",
        "image_size": [4, 6],
        "bounds": [[-1, -1, -1], [1, 1, 1]],
        "frames": frames[::-1],
    }
    path = directory / "manifest.json"
    path.write_text(json.dumps(payload), encoding="utf-8")
    return path


def test_real_order_disjoint_views_and_no_query_content_read(tmp_path):
    path = fixture_scene(tmp_path, "train", "scene_a")
    # Query content is intentionally unreadable; existence checks alone should pass.
    (path.parent / "24.depth.npy").write_bytes(b"query must not be opened")
    (path.parent / "24.normal.npy").write_bytes(b"query must not be opened")
    (path.parent / "24.png").write_bytes(b"query must not be opened")
    episode = pilot.make_episode(path, "cam_00", "train")
    assert [x["frame_id"] for x in episode["warmup"]] == [0, 2, 4, 6]
    assert [x["frame_id"] for x in episode["stream"]] == list(range(8, 24, 2))
    assert [x["frame_id"] for x in episode["query"]] == [24, 26, 28, 30]
    assert episode == pilot.make_episode(path, "cam_00", "train")


def test_short_scene_is_not_padded(tmp_path):
    path = fixture_scene(tmp_path, "train", "scene_a", count=15)
    with pytest.raises(ValueError, match="Too few"):
        pilot.make_episode(path, "cam_00", "train")


def test_cross_split_scene_is_rejected(tmp_path):
    fixture_scene(tmp_path, "train", "same_scene")
    fixture_scene(tmp_path, "val", "same_scene")
    partitions = tmp_path / "partitions.csv"
    partitions.write_text(
        "scene_name,protocol_partition,selected_camera\nsame_scene,train,cam_00\n"
    )
    with pytest.raises(ValueError, match="across splits"):
        pilot.build_plan(tmp_path, partitions, scenes_per_split=1)


def test_missing_data_fails_instead_of_selecting_another_scene(tmp_path):
    path = fixture_scene(tmp_path, "train", "scene_a")
    (path.parent / "0.depth.npy").unlink()
    with pytest.raises(FileNotFoundError):
        pilot.make_episode(path, "cam_00", "train")