From fe0be63b65dfc89eb1bf64191d542dc4a7d4304a Mon Sep 17 00:00:00 2001 From: Eric J Date: Sat, 29 Aug 2026 17:58:13 -0700 Subject: [PATCH] Freeze the Vulcanus render parity to fixtures (#227) The second of the three render parity specs, using the machinery #350 added, so this is the conversion only. `vulcanus:render` holds 26 rows: 4 terrain windows, the 5 routed views, 4 rocks, 5 resources, 4 composite and 4 cliffs. Each was recorded only after the Rust and TypeScript arms were compared and agreed. The Nauvis section came through the re-record byte-identical, which is the merge behaviour `flushRecording` is meant to have and is worth having seen rather than assumed. The three `renderTiled` helpers are deliberately NOT frozen, checked rather than assumed: every one of their `runRenderRequest` calls passes the engine, so they compare tiled against whole rather than Rust against TypeScript. The deletion leaves them grading exactly what they grade now. The coverage guard is here from the start rather than added after review. Planted: deleting the terrain `freeze` call site leaves **all 16 other tests green** and is caught only by the guard, at "expected 22 to be 26". That is the second spec where a deleted call site was invisible to every pre-existing assertion. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_015W4pLtEAk7Evo14mBkQ1HN --- test/fixtures/tier3-render-checksums.json | 28 +++++++++ test/wasmVulcanusRenderParity.spec.ts | 70 ++++++++++++++++++++++- 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/test/fixtures/tier3-render-checksums.json b/test/fixtures/tier3-render-checksums.json index 2b58e5cf..dc6ef1e8 100644 --- a/test/fixtures/tier3-render-checksums.json +++ b/test/fixtures/tier3-render-checksums.json @@ -73,5 +73,33 @@ "wide, offset | rocks": "0x9d67c26e606466f2", "wide, offset | terrain": "0x4babfc28a8b1e8c2", "wide, offset | trees": "0x98d9f0592e35f2af" + }, + "vulcanus:render": { + "coarse, all four entries | resources": "0xed733e24441722d2", + "fine, far field | all": "0x8fddaf34cfe294b9", + "fine, far field | cliffs": "0xb82d2e38be144659", + "fine, far field | rocks": "0x060e9f2448decb11", + "fine, far field | terrain": "0x99448f85a6c82c51", + "fine, fractional origin | resources": "0xcd65b95f7a66a95d", + "routes all | all": "0x6f85c24f8f751e7c", + "routes cliffs | cliffs": "0x64487f9330ae3acd", + "routes resources | resources": "0x30e6f99f8120a068", + "routes rocks | rocks": "0x8c3b15b002ab2cc4", + "routes terrain | terrain": "0x30e6f99f8120a068", + "square at origin | all": "0x6f85c24f8f751e7c", + "square at origin | cliffs": "0x64487f9330ae3acd", + "square at origin | rocks": "0x8c3b15b002ab2cc4", + "square at origin | terrain": "0x30e6f99f8120a068", + "square on a coal patch | resources": "0x7f43991be2199bd6", + "tall, coarse | all": "0xa2a15e3cfbd84bf0", + "tall, coarse | cliffs": "0x4b4cbe1b6f0b52dc", + "tall, coarse | resources": "0x5905256a2edce517", + "tall, coarse | rocks": "0x6f6227bf751044da", + "tall, coarse | terrain": "0xec7b122399c8e8d3", + "wide, offset | all": "0xe0e4f025147ce7e2", + "wide, offset | cliffs": "0xb6e42d366a8884a0", + "wide, offset | resources": "0x380c73e2d55f5b91", + "wide, offset | rocks": "0x8842b641dd4c5f05", + "wide, offset | terrain": "0x909cf2ad51797bcb" } } diff --git a/test/wasmVulcanusRenderParity.spec.ts b/test/wasmVulcanusRenderParity.spec.ts index 1ca9d6a9..0b420941 100644 --- a/test/wasmVulcanusRenderParity.spec.ts +++ b/test/wasmVulcanusRenderParity.spec.ts @@ -1,7 +1,17 @@ import { readFileSync } from "node:fs"; import { join } from "node:path"; import { inflateSync } from "node:zlib"; -import { describe, expect, it } from "vite-plus/test"; +import { afterAll, describe, expect, it } from "vite-plus/test"; + +import { + RECORDING, + consultedCount, + expectFrozen, + expectRecordedRows, + flushRecording, + foldPixels, + frozenCount, +} from "./tier3Frozen"; import { withDiffArtifacts } from "./diffArtifacts"; import { decodePng } from "./oracle/decodePng"; @@ -39,6 +49,38 @@ import { const FIXTURES = join(import.meta.dirname, "fixtures"); const SIZE = 1024; +/** + * The tier-3 freeze section for this spec. See `tier3Frozen.ts`. + * + * Every Rust-against-TypeScript render below is ALSO checked against a frozen + * checksum, so the assertion survives #227 deleting the TypeScript arm. Without + * it, `runRenderRequest(req)` with the engine left off stops being an + * independent arm the moment the Vulcanus branch goes, and the comparison would + * pass while grading nothing. + */ +const SECTION = "vulcanus:render"; + +/** + * Rows this spec must record, as a literal - the same guard tier 2 documents: + * adding a case makes a record run DROP the section until this is updated. + * + * 4 terrain windows + 5 routed views + 4 rocks + 5 resources + 4 composite + * + 4 cliffs. + * + * The three `renderTiled` helpers are NOT here: every one of their calls passes + * the engine, so they compare tiled against whole rather than Rust against + * TypeScript, and the deletion leaves them intact. + */ +const ROWS = 26; + +expectRecordedRows(SECTION, ROWS); +afterAll(flushRecording); + +/** Freeze one render, and compare the two arms while both exist. */ +function freeze(label: string, name: string, wasm: ArrayLike, ts: ArrayLike): void { + expectFrozen(SECTION, label, name, foldPixels(wasm), foldPixels(ts)); +} + /** `surfaceSeedForPlanet("vulcanus", 123456)`. */ const VULCANUS_SURFACE_SEED = 1249936247; @@ -175,6 +217,7 @@ describe("the WASM engine renders Vulcanus terrain exactly as the TypeScript doe const ts = new Uint8ClampedArray(runRenderRequest(req).buffer); expect(wasm.length, `${w.label}: length`).toBe(w.width * w.height * 4); expect(Array.from(wasm), `${w.label}: pixels`).toEqual(Array.from(ts)); + freeze(w.label, "terrain", wasm, ts); } }, 300000); @@ -219,6 +262,9 @@ describe("the WASM engine renders Vulcanus terrain exactly as the TypeScript doe const withEngine = new Uint8ClampedArray(runRenderRequest(composite, e).buffer); const withoutEngine = new Uint8ClampedArray(runRenderRequest(composite).buffer); expect(Array.from(withEngine), `${view}: engine vs none`).toEqual(Array.from(withoutEngine)); + // Prefixed: this block sweeps WINDOWS[0] through every view, so a bare + // window label would collide with the per-view blocks below. + freeze(`routes ${view}`, view, withEngine, withoutEngine); } // And `all` really does differ from bare terrain here, so the assertion @@ -268,6 +314,7 @@ describe("the WASM engine renders the Vulcanus rock overlay exactly as the TypeS const ts = new Uint8ClampedArray(runRenderRequest(req).buffer); expect(wasm.length, `${w.label}: length`).toBe(w.width * w.height * 4); expect(Array.from(wasm), `${w.label}: pixels`).toEqual(Array.from(ts)); + freeze(w.label, "rocks", wasm, ts); } }, 300000); @@ -442,6 +489,7 @@ describe("the WASM engine renders the Vulcanus resource overlay exactly as the T const ts = new Uint8ClampedArray(runRenderRequest(req).buffer); expect(wasm.length, `${w.label}: length`).toBe(w.width * w.height * 4); expect(Array.from(wasm), `${w.label}: pixels`).toEqual(Array.from(ts)); + freeze(w.label, "resources", wasm, ts); } }, 300000); @@ -543,6 +591,7 @@ describe("the WASM engine renders the Vulcanus composite exactly as the TypeScri const wasm = new Uint8ClampedArray(runRenderRequest(req, e).buffer); const ts = new Uint8ClampedArray(runRenderRequest(req).buffer); expect(Array.from(wasm), `${w.label}: pixels`).toEqual(Array.from(ts)); + freeze(w.label, "all", wasm, ts); counts.push(paintedOver(wasm, new Uint8ClampedArray(runRenderRequest(request(w), e).buffer))); } expect(counts).toEqual(ALL_PIXELS_PER_WINDOW); @@ -622,6 +671,7 @@ describe("the WASM engine renders Vulcanus cliffs exactly as the TypeScript does const ts = new Uint8ClampedArray(runRenderRequest(req).buffer); expect(wasm.length, `${w.label}: length`).toBe(w.width * w.height * 4); expect(Array.from(wasm), `${w.label}: pixels`).toEqual(Array.from(ts)); + freeze(w.label, "cliffs", wasm, ts); } }, 300000); @@ -837,3 +887,21 @@ describe("the WASM engine agrees with the game's own Vulcanus preview PNG", () = expect(differing).toBeGreaterThan(DIFFERING_PX * 10); }, 300000); }); + +describe("the tier-3 freeze covers this spec rather than merely existing", () => { + // Declared last on purpose: tests run in declaration order within a file, so + // this sees every `freeze` call the run made. See the same guard on + // `wasmNauvisRenderParity.spec.ts`, where deleting one call site left all 37 + // other tests green. + // + // `expectRecordedRows` guards only a RECORD run, so without this a deleted + // `freeze` call site would leave its row in the table un-consulted while + // every gate stayed green. Both numbers are asserted because they fail on + // opposite mistakes: the table count catches a re-record that wrote a + // different surface, the consulted count catches a call site that stopped + // asking. + it.skipIf(RECORDING)("consults every frozen row exactly once", () => { + expect(frozenCount(SECTION), "rows in the committed table").toBe(ROWS); + expect(consultedCount(SECTION), "distinct rows this run looked up").toBe(ROWS); + }); +});