Files
pxpipe/legacy/python/diff_render.py
T
teamchong 437afa4dfe Restructure: archive Python+Rust, prep for TypeScript rewrite
- Move Python proxy + scripts to legacy/python/ (parity oracle during port)
- Delete Rust port (src/rust/) - history preserved in commits 482c967, f094649
- Delete Zig atlas builder (src/zig/) - will regenerate from TTF via Node
- Move JetBrainsMono-Regular.ttf to assets/
- Update .gitignore for Node/TS/Wrangler

Next: scaffold TypeScript with dual Node + Cloudflare Workers targets.
2026-05-18 15:22:41 -04:00

137 lines
4.5 KiB
Python

#!/usr/bin/env python3
"""
Diff harness: compare Python render_chunks vs Rust render_chunks pixel-by-pixel.
Strategy:
1. For each sample text, call Python render_chunks() -> list[PNG bytes]
2. Call Rust diff_render binary -> writes PNGs to disk
3. Decode both sides' PNGs and compare pixel arrays
4. Report: pass / fail with first diff coordinates
Run:
python3 scripts/diff_render.py
"""
import hashlib
import json
import subprocess
import sys
import tempfile
from pathlib import Path
# Make src/proxy.py importable
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "src"))
import proxy as py_proxy # noqa: E402
from PIL import Image # noqa: E402
import io # noqa: E402
RUST_BIN = ROOT / "src" / "rust" / "target" / "release" / "examples" / "diff_render"
SAMPLES = {
"short_ascii": "hello world\n",
"multiline_code": (
"def fibonacci(n):\n"
" if n <= 1: return n\n"
" return fibonacci(n-1) + fibonacci(n-2)\n"
"print(fibonacci(10))\n"
),
"long_with_blanks": (
"# Header\n\n"
+ "\n".join(f"line {i}: " + "x" * (i % 60) for i in range(200))
+ "\n\n# Footer\n"
),
"many_short_lines": "\n".join(f"L{i}" for i in range(500)) + "\n",
"wide_lines": "\n".join("." * 80 for _ in range(100)) + "\n",
"tools_like_json": json.dumps(
{"tools": [{"name": f"tool_{i}", "input_schema": {"type": "object",
"properties": {f"p{j}": {"type": "string"} for j in range(5)}}}
for i in range(20)]}, indent=2),
}
def png_to_pixels(png_bytes: bytes):
"""Decode PNG to (width, height, raw_grayscale_bytes)."""
img = Image.open(io.BytesIO(png_bytes))
img = img.convert("L") # force 8-bit grayscale
return img.width, img.height, img.tobytes()
def run_rust(samples: dict, out_dir: Path) -> dict:
"""Invoke rust diff_render binary, returns name -> list[png_bytes]."""
spec = {name: text for name, text in samples.items()}
spec_path = out_dir / "spec.json"
spec_path.write_text(json.dumps(spec))
res = subprocess.run(
[str(RUST_BIN), str(spec_path), str(out_dir)],
capture_output=True, text=True,
)
if res.returncode != 0:
print("RUST FAILED:", res.stderr, file=sys.stderr)
sys.exit(2)
manifest = json.loads((out_dir / "manifest.json").read_text())
out = {}
for name, files in manifest.items():
out[name] = [(out_dir / f).read_bytes() for f in files]
return out
def diff_pixels(py_bytes: bytes, rs_bytes: bytes):
"""Return None if pixel-identical, else (reason, details)."""
pw, ph, pp = png_to_pixels(py_bytes)
rw, rh, rp = png_to_pixels(rs_bytes)
if (pw, ph) != (rw, rh):
return ("dim_mismatch", f"py={pw}x{ph} rs={rw}x{rh}")
if pp == rp:
return None
# Find first diff
for i, (a, b) in enumerate(zip(pp, rp)):
if a != b:
x = i % pw
y = i // pw
return ("pixel_mismatch", f"first diff at ({x},{y}): py={a} rs={b}")
return ("byte_len_mismatch", f"py_len={len(pp)} rs_len={len(rp)}")
def main():
if not RUST_BIN.exists():
print(f"build rust first: cd src/rust && cargo build --release --example diff_render")
print(f"missing: {RUST_BIN}")
sys.exit(1)
with tempfile.TemporaryDirectory() as td:
out_dir = Path(td)
rust_out = run_rust(SAMPLES, out_dir)
total = 0
fail = 0
for name, text in SAMPLES.items():
py_pngs = py_proxy.render_chunks(text)
rs_pngs = rust_out.get(name, [])
total += 1
print(f"\n[{name}] py_chunks={len(py_pngs)} rs_chunks={len(rs_pngs)}")
if len(py_pngs) != len(rs_pngs):
fail += 1
print(f" FAIL: chunk count differs")
continue
any_fail = False
for i, (a, b) in enumerate(zip(py_pngs, rs_pngs)):
ah = hashlib.sha256(a).hexdigest()[:12]
bh = hashlib.sha256(b).hexdigest()[:12]
diff = diff_pixels(a, b)
if diff is None:
print(f" chunk {i}: OK py={ah} rs={bh} bytes={len(a)}/{len(b)}")
else:
any_fail = True
print(f" chunk {i}: FAIL {diff[0]}: {diff[1]} py={ah} rs={bh}")
if any_fail:
fail += 1
print(f"\n=== {total - fail}/{total} samples pixel-identical ===")
sys.exit(0 if fail == 0 else 1)
if __name__ == "__main__":
main()