Compression-Lens / tests /compare_snapshots.py
Jellyfish042's picture
Checkpoint before tooltip palette update
350392a
"""
Compare two render-model JSON snapshots and report differences.
"""
import argparse
import json
import math
import sys
from pathlib import Path
from typing import Any, List
def load_json(path: Path) -> Any:
with path.open("r", encoding="utf-8") as f:
return json.load(f)
def is_number(value: Any) -> bool:
return isinstance(value, (int, float)) and not isinstance(value, bool)
def nearly_equal(a: float, b: float, tol: float) -> bool:
if math.isnan(a) and math.isnan(b):
return True
return abs(a - b) <= tol
def compare(a: Any, b: Any, path: str, diffs: List[str], tol: float, max_diffs: int) -> None:
if len(diffs) >= max_diffs:
return
if type(a) != type(b):
diffs.append(f"{path}: type {type(a).__name__} != {type(b).__name__}")
return
if isinstance(a, dict):
a_keys = set(a.keys())
b_keys = set(b.keys())
for key in sorted(a_keys - b_keys):
diffs.append(f"{path}.{key}: missing in candidate")
if len(diffs) >= max_diffs:
return
for key in sorted(b_keys - a_keys):
diffs.append(f"{path}.{key}: extra in candidate")
if len(diffs) >= max_diffs:
return
for key in sorted(a_keys & b_keys):
compare(a[key], b[key], f"{path}.{key}", diffs, tol, max_diffs)
if len(diffs) >= max_diffs:
return
return
if isinstance(a, list):
if len(a) != len(b):
diffs.append(f"{path}: list length {len(a)} != {len(b)}")
min_len = min(len(a), len(b))
for idx in range(min_len):
compare(a[idx], b[idx], f"{path}[{idx}]", diffs, tol, max_diffs)
if len(diffs) >= max_diffs:
return
return
if is_number(a) and is_number(b) and tol > 0:
if not nearly_equal(float(a), float(b), tol):
diffs.append(f"{path}: {a} != {b} (tol={tol})")
return
if a != b:
diffs.append(f"{path}: {a!r} != {b!r}")
def main() -> int:
parser = argparse.ArgumentParser(description="Compare render-model JSON snapshots.")
parser.add_argument("--baseline", type=Path, required=True, help="Baseline JSON path")
parser.add_argument("--candidate", type=Path, required=True, help="Candidate JSON path")
parser.add_argument("--float-tol", type=float, default=0.0, help="Float comparison tolerance")
parser.add_argument("--max-diffs", type=int, default=200, help="Max diffs to display")
args = parser.parse_args()
baseline = load_json(args.baseline)
candidate = load_json(args.candidate)
diffs: List[str] = []
compare(baseline, candidate, "$", diffs, args.float_tol, args.max_diffs)
if diffs:
print(f"Differences found: {len(diffs)}")
for line in diffs:
print(line)
return 1
print("No differences found.")
return 0
if __name__ == "__main__":
sys.exit(main())