packing-benchmark / tests /test_verifier.py
NathanRoll's picture
Harden verifier and record metric validation
3b06380 verified
from __future__ import annotations
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from packing_benchmark.renderer import svg_markup # noqa: E402
from packing_benchmark.store import SolutionStore # noqa: E402
from packing_benchmark.verifier import load_solution_json, verify_record_solution, verify_solution # noqa: E402
def test_seed_records_verify() -> None:
for path in sorted((ROOT / "data" / "solutions").glob("*.json")):
result = verify_solution(json.loads(path.read_text()))
assert result.ok, (path.name, result.errors)
def test_rejects_overlapping_pair() -> None:
solution = {
"case": "squinsqu@2",
"item": {"type": "regular_polygon", "sides": 4, "side_length": 1},
"container": {"type": "regular_polygon", "sides": 4, "side_length": 3},
"placements": [
{"x": 0, "y": 0, "rotation_radians": 0},
{"x": 0, "y": 0, "rotation_radians": 0},
],
}
result = verify_solution(solution)
assert not result.ok
assert result.max_pair_overlap_depth > 0
def test_rejects_boundary_protrusion() -> None:
solution = {
"case": "squinsqu@1",
"item": {"type": "regular_polygon", "sides": 4, "side_length": 1},
"container": {"type": "regular_polygon", "sides": 4, "side_length": 1},
"placements": [
{"x": 0.6, "y": 0.0, "rotation_radians": 0},
],
}
result = verify_solution(solution)
assert not result.ok
assert result.max_boundary_excess > 0
def test_rejects_wrong_case_family() -> None:
solution = {
"case": "triintri@1",
"item": {"type": "circle", "radius": 0.5},
"container": {"type": "regular_polygon", "sides": 4, "side_length": 2},
"placements": [
{"x": 0.0, "y": 0.0, "rotation_radians": 0},
],
}
result = verify_solution(solution)
assert not result.ok
assert any("does not match item/container geometry" in error for error in result.errors)
def test_rejects_wrong_setup_field() -> None:
solution = {
"case": "cirinsqu@1",
"setup": "triintri",
"item": {"type": "circle", "radius": 0.5},
"container": {"type": "rectangle", "width": 2.0, "height": 2.0},
"placements": [
{"x": 0.0, "y": 0.0, "rotation_radians": 0},
],
}
result = verify_solution(solution)
assert not result.ok
assert any("setup" in error and "does not match" in error for error in result.errors)
def test_circle_container_metric_is_radius() -> None:
solution = {
"case": "triincir@1",
"item": {"type": "regular_polygon", "sides": 3, "side_length": 1},
"container": {"type": "circle", "radius": 1.0},
"placements": [
{"x": 0.0, "y": 0.0, "rotation_radians": 0},
],
}
result = verify_solution(solution)
assert result.ok
assert result.side == 2.0
assert result.metric_symbol == "r"
assert result.metric_value == 1.0
def test_rejects_non_finite_json_numbers() -> None:
try:
load_solution_json('{"case": "cirincir@1", "item": {"type": "circle", "radius": NaN}}')
except ValueError as exc:
assert "invalid JSON numeric constant" in str(exc)
else:
raise AssertionError("NaN JSON was accepted")
def test_record_solution_mismatch_is_detected() -> None:
solution = {
"case": "cirinsqu@1",
"item": {"type": "circle", "radius": 0.5},
"container": {"type": "rectangle", "width": 2.0, "height": 2.0},
"placements": [
{"x": 0.0, "y": 0.0, "rotation_radians": 0},
],
}
errors = verify_record_solution(
{
"case": "cirinsqu@1",
"setup": "cirinsqu",
"n": 1,
"side": 3.0,
"metric_symbol": "s",
"metric_value": 3.0,
},
solution,
)
assert any("record side" in error for error in errors)
def test_store_and_svg_paths_exist() -> None:
store = SolutionStore(ROOT / "data")
records = store.best_records("All")
assert records
record = records[0]
assert store.ensure_svg(record).exists()
assert svg_markup(store.solution_for_record(record)).startswith("<svg")