Source: tests/test_rgbd_train_ext_data.py · Plain text

tests/test_rgbd_train_ext_data.py

"""TRAIN-EXT candidate rule: splits, exclusions, DEV-volume fence, asset uniqueness, order."""

import importlib.util
from pathlib import Path

SCRIPT = Path(__file__).resolve().parents[1] / "scripts" / "prepare_rgbd_train_ext_data.py"
spec = importlib.util.spec_from_file_location("prepare_rgbd_train_ext_data", SCRIPT)
ext = importlib.util.module_from_spec(spec)
spec.loader.exec_module(ext)


def row(sid, split="train", partition=None):
    return {"scene_name": sid, "official_split": split, "protocol_partition": partition or split}


def assets_for(ids, shared=()):
    """One unique asset per scene; scenes in `shared` reuse the asset of the first one."""
    out = {sid: {"archive_file": f"AI_{sid}.rar", "asset_file": "01"} for sid in ids}
    for sid in shared[1:]:
        out[sid] = out[shared[0]]
    return out


def run(rows, **kw):
    ids = [r["scene_name"] for r in rows]
    options = {
        "assets": assets_for(ids),
        "allowed": {sid: list(range(16)) for sid in ids},
        "trajectories": {f"{sid}_cam_00": {"Scene type": "OK"} for sid in ids},
        "excluded_sets": [],
        "blocked_assets": set(),
        "fence": set(),
    }
    options.update(kw)
    return ext.select_candidates(rows, **options)


def test_train_and_val_are_kept_but_test_and_mismatched_partitions_are_not():
    rows = [
        row("ai_001_001"),
        row("ai_002_001", "val"),
        row("ai_003_001", "test", "final_holdout"),
        row("ai_004_001", "train", "val"),
    ]
    assert sorted(run(rows)) == ["ai_001_001", "ai_002_001"]


def test_exclusions_fence_short_and_bad_trajectories_are_dropped():
    rows = [row(f"ai_00{i}_001") for i in range(1, 7)]
    ids = [r["scene_name"] for r in rows]
    allowed = {sid: list(range(16)) for sid in ids} | {"ai_004_001": list(range(15))}
    trajectories = {f"{sid}_cam_00": {"Scene type": "OK"} for sid in ids}
    trajectories["ai_005_001_cam_00"] = {"Scene type": "BAD"}
    kept = run(
        rows,
        excluded_sets=[{"ai_001_001"}, {"ai_002_001"}],
        fence={"003"},
        allowed=allowed,
        trajectories=trajectories,
    )
    assert kept == ["ai_006_001"]


def test_source_assets_are_unique_against_blocked_and_within_the_set():
    rows = [row(f"ai_00{i}_001") for i in range(1, 5)]
    ids = [r["scene_name"] for r in rows]
    assets = assets_for(ids, shared=("ai_001_001", "ai_002_001"))
    blocked = {ext.asset_identity(assets["ai_003_001"])}
    kept = run(rows, assets=assets, blocked_assets=blocked)
    assert "ai_003_001" not in kept and "ai_004_001" in kept
    assert len({"ai_001_001", "ai_002_001"} & set(kept)) == 1


def test_order_is_the_seeded_hash_order():
    rows = [row(f"ai_0{i:02d}_001") for i in range(1, 20)]
    kept = run(rows)
    assert kept == sorted(kept, key=ext.selection_key)
    assert ext.SELECTION_SEED == "TRAIN-EXT-20260930" and ext.TRAIN_EXT_MIN == 40