Source: tests/test_small_3d_data.py · Plain text

tests/test_small_3d_data.py

import zipfile

import numpy as np
import pytest

from mcss.mechanism_pilot.data_prep import check_geometry, resize_rgb_depth, select_members


def infos(scene="ai_001_001"):
    names = [f"{scene}/_detail/metadata_scene.csv"] + [
        f"{scene}/_detail/cam_00/camera_keyframe_{kind}.hdf5"
        for kind in ("frame_indices", "positions", "orientations")
    ]
    for fid in range(2, 20):
        names.extend(
            [
                f"{scene}/images/scene_cam_00_final_preview/frame.{fid:04d}.color.jpg",
                f"{scene}/images/scene_cam_00_geometry_hdf5/frame.{fid:04d}.depth_meters.hdf5",
            ]
        )
    names.append(f"{scene}/images/scene_cam_00_geometry_hdf5/frame.0002.position.hdf5")
    result = [zipfile.ZipInfo(n) for n in names]
    for info in result:
        info.CRC = 0
    return result


def test_metadata_selection_uses_actual_common_ids_and_rejects_other_splits():
    plan = select_members("ai_001_001", infos())
    assert plan["frame_ids"] == list(range(2, 18))
    assert not any("normal" in n for n in plan["members"])
    with pytest.raises(ValueError, match="whitelist"):
        select_members("ai_004_003", infos("ai_004_003"))


def test_budget_and_insufficient_frames_rejected():
    members = infos()
    members[0].file_size = 1024**3 + 1
    with pytest.raises(ValueError, match="budget"):
        select_members("ai_001_001", members)
    with pytest.raises(ValueError, match="Insufficient"):
        select_members("ai_001_001", infos()[:8])


def test_masked_depth_resize_does_not_spread_nan_or_zero_bias():
    depth = np.array([[2.0, np.nan], [0.0, 2.0]])
    rgb = np.zeros((2, 2, 3), np.uint8)
    resized_rgb, resized_depth = resize_rgb_depth(rgb, depth, (1, 1))
    assert resized_rgb.shape == (1, 1, 3)
    np.testing.assert_allclose(resized_depth, 2.0)


def test_geometry_detects_wrong_scale_or_camera_axes():
    depth = np.ones((3, 4)) * 2
    y, x = np.indices(depth.shape)
    rays = np.stack([x, y, np.ones_like(x)], -1).astype(float)
    rays /= np.linalg.norm(rays, axis=-1, keepdims=True)
    positions = rays * depth[..., None]
    report = check_geometry("fixture", depth, positions, np.eye(4), np.eye(3), 1)
    assert report["p95_error_m"] < 1e-12
    with pytest.raises(ValueError, match="calibration failed"):
        check_geometry("fixture", depth, positions, np.eye(4), np.eye(3), 2)


class Response:
    def __init__(self, status, headers, content=b"abc"):
        self.status_code = status
        self.headers = headers
        self.content = content
        self.body_read = False

    def raise_for_status(self):
        pass

    def close(self):
        pass

    def __enter__(self):
        return self

    def __exit__(self, *args):
        pass

    def iter_content(self, size):
        self.body_read = True
        yield self.content


class Session:
    def __init__(self, response):
        self.response = response
        self.request_count = 0

    def head(self, *args, **kwargs):
        return Response(200, {"Content-Length": "10000000000", "ETag": '"stable"'})

    def get(self, *args, **kwargs):
        assert kwargs["stream"] is True
        self.request_count += 1
        return self.response


def test_full_archive_fallback_rejected_before_reading_body():
    from mcss.mechanism_pilot.data_prep import BoundedRangeReader

    response = Response(200, {"ETag": '"stable"'})
    reader = BoundedRangeReader("fixture", Session(response))
    with pytest.raises(OSError, match="full ZIP fallback prohibited"):
        reader.read(3)
    assert not response.body_read


def test_global_budget_refuses_request_before_network():
    from mcss.mechanism_pilot.data_prep import BoundedRangeReader

    session = Session(Response(206, {}))
    reader = BoundedRangeReader("fixture", session)
    reader.shared_budget = [2 * 1024**3 - 2]
    with pytest.raises(OSError, match="Shared transfer budget"):
        reader.read(3)
    assert session.request_count == 0


def test_exact_range_rejects_changed_identity_before_body():
    from mcss.mechanism_pilot.data_prep import BoundedRangeReader

    response = Response(206, {"ETag": '"changed"', "Content-Range": "bytes 0-2/10000000000"})
    reader = BoundedRangeReader("fixture", Session(response))
    with pytest.raises(OSError, match="changed"):
        reader.read(3)
    assert not response.body_read