Source: tests/test_dynamic_checkpoint_geometry.py · Plain text

tests/test_dynamic_checkpoint_geometry.py

"""Geometry buffers must agree with the declared checkpoint configuration."""

from dataclasses import replace

import pytest
import torch

from mcss.dynamic.carrier import DynamicSceneCarrier
from mcss.dynamic.checkpoint import load_dynamic_checkpoint, save_dynamic_checkpoint
from mcss.dynamic.config import CarrierConfig, WriteConfig
from mcss.dynamic.write_rule import DirectWriteRule

CENTERED = ((-6.0, -4.0, -6.0), (6.0, 4.0, 6.0))


def _save(path, config=None):
    config = config or CarrierConfig()
    carrier = DynamicSceneCarrier(config)
    writer = DirectWriteRule(config, WriteConfig())
    save_dynamic_checkpoint(path, carrier, writer, phase="geometry-test", provenance={})
    return carrier, writer


@pytest.mark.parametrize("centered", [False, True])
@pytest.mark.parametrize("tokens", [1, 128])
def test_geometry_roundtrip_preserves_legacy_and_centered_bounds(tmp_path, centered, tokens):
    config = CarrierConfig(token_count=tokens)
    if centered:
        config = replace(config, local_bounds_m=CENTERED)
    path = tmp_path / "weights.pt"
    original, _ = _save(path, config)
    restored, _, metadata = load_dynamic_checkpoint(path)
    assert metadata["schema_version"] == "mcss.dynamic.v1"
    assert restored.config == config
    for name, value in original.state_dict().items():
        assert torch.equal(value, restored.state_dict()[name])


def test_geometry_rejects_changed_config_without_changed_buffers(tmp_path):
    path = tmp_path / "weights.pt"
    _save(path)
    payload = torch.load(path, weights_only=True)
    payload["carrier_config"]["local_bounds_m"] = CENTERED
    torch.save(payload, path)
    with pytest.raises(ValueError, match="geometry/config mismatch"):
        load_dynamic_checkpoint(path)


@pytest.mark.parametrize(
    "name", ["_bounds", "_candidate_points", "_candidate_ids", "_candidate_normalized_xyz"]
)
def test_geometry_rejects_tampered_buffer_on_load_and_save(tmp_path, name):
    path = tmp_path / "weights.pt"
    carrier, writer = _save(path)
    payload = torch.load(path, weights_only=True)
    payload["carrier_state_dict"][name].reshape(-1)[0] += 1
    torch.save(payload, path)
    with pytest.raises(ValueError, match="geometry/config mismatch"):
        load_dynamic_checkpoint(path)
    with torch.no_grad():
        getattr(carrier, name).reshape(-1)[0] += 1
    output = tmp_path / "invalid.pt"
    with pytest.raises(ValueError, match="geometry/config mismatch"):
        save_dynamic_checkpoint(output, carrier, writer, phase="test", provenance={})
    assert not output.exists()


def test_geometry_validation_does_not_consume_rng_during_save(tmp_path):
    carrier = DynamicSceneCarrier(CarrierConfig())
    writer = DirectWriteRule(carrier.config, WriteConfig())
    before = torch.get_rng_state().clone()
    save_dynamic_checkpoint(
        tmp_path / "weights.pt", carrier, writer, phase="rng-test", provenance={}
    )
    assert torch.equal(before, torch.get_rng_state())