a73x

9a964981

scenario: size-check golden in cell_matches_golden predicate

a73x   2026-04-19 09:38

Previously indexed into golden.pixels using image.width as stride,
silently misreading if dimensions differed. Now returns
pass=false with "golden size mismatch" reason.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

diff --git a/src/scenario.zig b/src/scenario.zig
index 042249e..1f6007a 100644
--- a/src/scenario.zig
+++ b/src/scenario.zig
@@ -1302,6 +1302,9 @@ pub fn evalPredicate(
            const golden = golden_for_cell_matches orelse {
                return .{ .pass = false, .reason = "missing golden" };
            };
            if (golden.width != image.width or golden.height != image.height) {
                return .{ .pass = false, .reason = "golden size mismatch" };
            }
            // Compute RMSE over the cell rect inline.
            var sum_sq: f64 = 0;
            const total_px: u32 = geom.cell_w_px * geom.cell_h_px;
@@ -1499,6 +1502,28 @@ test "evalPredicate: cell-matches-golden with null golden fails with 'missing go
    try std.testing.expect(std.mem.indexOf(u8, r.reason, "missing golden") != null);
}

test "evalPredicate: cell-matches-golden fails when golden has different dimensions" {
    const alloc = std.testing.allocator;
    const img = try makeSolid(alloc, 80, 24, .{ 10, 20, 30, 255 });
    defer alloc.free(img.pixels);
    const golden = try makeSolid(alloc, 40, 24, .{ 10, 20, 30, 255 }); // half-width
    defer alloc.free(golden.pixels);
    const r = evalPredicate(img, 0, 0, .{ .cell_w_px = 8, .cell_h_px = 16 }, .cell_matches_golden, golden);
    try std.testing.expect(!r.pass);
    try std.testing.expect(std.mem.indexOf(u8, r.reason, "size mismatch") != null);
}

test "evalPredicate: cell-matches-golden fails when golden height differs" {
    const alloc = std.testing.allocator;
    const img = try makeSolid(alloc, 80, 24, .{ 10, 20, 30, 255 });
    defer alloc.free(img.pixels);
    const golden = try makeSolid(alloc, 80, 12, .{ 10, 20, 30, 255 }); // half-height
    defer alloc.free(golden.pixels);
    const r = evalPredicate(img, 0, 0, .{ .cell_w_px = 8, .cell_h_px = 16 }, .cell_matches_golden, golden);
    try std.testing.expect(!r.pass);
    try std.testing.expect(std.mem.indexOf(u8, r.reason, "size mismatch") != null);
}

// End-to-end tests: parse + state + predicate

test "tick+eval: assert-cell evaluates against last capture" {