a73x

9a964981

scenario: size-check golden in cell_matches_golden predicate

a73x   2026-04-19 09:38

Commit message
scenario: size-check golden in cell_matches_golden predicate

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>

src/scenario.zig
Old New
@@ -1302,6 +1302,9 @@ pub fn evalPredicate(
1302 const golden = golden_for_cell_matches orelse { 1302 const golden = golden_for_cell_matches orelse {
1303 return .{ .pass = false, .reason = "missing golden" }; 1303 return .{ .pass = false, .reason = "missing golden" };
1304 }; 1304 };
1305 if (golden.width != image.width or golden.height != image.height) {
1306 return .{ .pass = false, .reason = "golden size mismatch" };
1307 }
1305 // Compute RMSE over the cell rect inline. 1308 // Compute RMSE over the cell rect inline.
1306 var sum_sq: f64 = 0; 1309 var sum_sq: f64 = 0;
1307 const total_px: u32 = geom.cell_w_px * geom.cell_h_px; 1310 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
1499 try std.testing.expect(std.mem.indexOf(u8, r.reason, "missing golden") != null); 1502 try std.testing.expect(std.mem.indexOf(u8, r.reason, "missing golden") != null);
1500 } 1503 }
1501 1504
1505 test "evalPredicate: cell-matches-golden fails when golden has different dimensions" {
1506 const alloc = std.testing.allocator;
1507 const img = try makeSolid(alloc, 80, 24, .{ 10, 20, 30, 255 });
1508 defer alloc.free(img.pixels);
1509 const golden = try makeSolid(alloc, 40, 24, .{ 10, 20, 30, 255 }); // half-width
1510 defer alloc.free(golden.pixels);
1511 const r = evalPredicate(img, 0, 0, .{ .cell_w_px = 8, .cell_h_px = 16 }, .cell_matches_golden, golden);
1512 try std.testing.expect(!r.pass);
1513 try std.testing.expect(std.mem.indexOf(u8, r.reason, "size mismatch") != null);
1514 }
1515
1516 test "evalPredicate: cell-matches-golden fails when golden height differs" {
1517 const alloc = std.testing.allocator;
1518 const img = try makeSolid(alloc, 80, 24, .{ 10, 20, 30, 255 });
1519 defer alloc.free(img.pixels);
1520 const golden = try makeSolid(alloc, 80, 12, .{ 10, 20, 30, 255 }); // half-height
1521 defer alloc.free(golden.pixels);
1522 const r = evalPredicate(img, 0, 0, .{ .cell_w_px = 8, .cell_h_px = 16 }, .cell_matches_golden, golden);
1523 try std.testing.expect(!r.pass);
1524 try std.testing.expect(std.mem.indexOf(u8, r.reason, "size mismatch") != null);
1525 }
1526
1502 // End-to-end tests: parse + state + predicate 1527 // End-to-end tests: parse + state + predicate
1503 1528
1504 test "tick+eval: assert-cell evaluates against last capture" { 1529 test "tick+eval: assert-cell evaluates against last capture" {