File size: 3,578 Bytes
b20c82e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
"""R0-1 — golden-image gate.

Renders the reference matrix (bundle x tile) through golden_render and compares
against the committed goldens. Any drift beyond tolerance fails with a
side-by-side (golden | current | amplified diff) written to verify_out/.

Usage:
    python verify_goldens.py            # check against goldens (CI mode)
    python verify_goldens.py --bless    # regenerate goldens (intentional change)

Tolerances: renders are deterministic numpy, so genuine engine changes show up
as large diffs; the small allowance absorbs PIL/numpy version drift only.
"""

import os
import sys

import numpy as np
from PIL import Image

import golden_render

HERE = os.path.dirname(os.path.abspath(__file__))
TILES = os.path.join(HERE, "..", "..", "frontend", "viz2d-demo", "src", "assets", "tiles")
GOLDEN_DIR = os.path.join(HERE, "goldens")
OUT = os.path.join(HERE, "verify_out")

MEAN_TOL = 0.5   # mean abs diff per channel
P999_TOL = 8.0   # 99.9th percentile abs diff

MATRIX = [
    # (golden name, bundle, tile)  — tiles cover the three texture-prep paths:
    # checkered = period-snap, rustic-wood = masked-shift, basalt = native wrap
    ("desk_checkered", "data/current_bundle.vizbundle.json", "checkered.jpeg"),
    ("desk_rustic", "data/current_bundle.vizbundle.json", "rustic-wood.jpg"),
    ("desk_basalt", "data/current_bundle.vizbundle.json", "basalt-outside-wal.jpg"),
    ("kitchen_checkered", "data/ref_kitchen.vizbundle.json", "checkered.jpeg"),
    ("kitchen_rustic", "data/ref_kitchen.vizbundle.json", "rustic-wood.jpg"),
    ("kitchen_basalt", "data/ref_kitchen.vizbundle.json", "basalt-outside-wal.jpg"),
]


def main():
    bless = "--bless" in sys.argv
    os.makedirs(GOLDEN_DIR, exist_ok=True)
    os.makedirs(OUT, exist_ok=True)
    ok = True

    for name, bundle, tile in MATRIX:
        img = golden_render.render(os.path.join(HERE, bundle), os.path.join(TILES, tile))
        golden_path = os.path.join(GOLDEN_DIR, f"{name}.png")

        if bless:
            img.save(golden_path)
            print(f"  blessed {name}.png ({img.width}x{img.height})")
            continue

        if not os.path.exists(golden_path):
            print(f"  [FAIL] {name}: golden missing — run `make bless`")
            ok = False
            continue

        cur = np.asarray(img).astype(np.float64)
        gold = np.asarray(Image.open(golden_path).convert("RGB")).astype(np.float64)
        if cur.shape != gold.shape:
            print(f"  [FAIL] {name}: size changed {gold.shape} -> {cur.shape}")
            ok = False
            continue

        diff = np.abs(cur - gold)
        mean_d = float(diff.mean())
        p999 = float(np.percentile(diff, 99.9))
        passed = mean_d <= MEAN_TOL and p999 <= P999_TOL
        print(f"  [{'PASS' if passed else 'FAIL'}] {name}: mean={mean_d:.3f} p99.9={p999:.1f}")
        if not passed:
            ok = False
            amplified = np.clip(diff * 8, 0, 255).astype(np.uint8)
            panel = np.concatenate(
                [gold.astype(np.uint8), cur.astype(np.uint8), amplified], axis=1
            )
            fail_path = os.path.join(OUT, f"golden_fail_{name}.png")
            Image.fromarray(panel).save(fail_path)
            print(f"         side-by-side: {fail_path}  (golden | current | diff x8)")

    if bless:
        print("goldens regenerated — commit backend/floor-visualizer/goldens/")
        return 0
    print("\n" + ("ALL GOLDEN CHECKS PASSED" if ok else "GOLDEN CHECKS FAILED"))
    return 0 if ok else 1


if __name__ == "__main__":
    raise SystemExit(main())