Skip to content

Commit 1655176

Browse files
committed
test(shader-transitions): add midpoint (p=0.5) regression invariants for all shaders
Existing smoke tests cover only the endpoints (p=0 ≈ from, p=1 ≈ to), which miss a class of regressions that surface specifically at the midpoint — where the transition is most visible to viewers — and let shaders silently rot in CI: • A shader becomes a no-op (returns input as-is) • A shader prematurely completes (returns target at midpoint) • A shader doesn't write to the output buffer at all • A shader loses determinism (Math.random / Date.now / leaked state) Add four invariants every shader must satisfy at p=0.5, applied via a describe loop over ALL_SHADERS so any new transition added to the registry automatically picks up the same coverage: 1. output ≠ from catches no-ops 2. output ≠ to catches premature completion 3. output is non-zero catches blank output 4. output is deterministic catches accidental non-determinism Uses two distinct uniform input colors (40000/30000/20000 vs 10000/10000/10000) so equality checks have distinct byte patterns to compare against. Even shaders that warp UVs (which would be no-ops on uniform input alone) produce mix16(from, to, 0.5) at every pixel, distinct from both inputs. 60 new tests (4 invariants × 15 shaders), all passing. Follow-up to plans/hdr-followups.md Chunk 9G.
1 parent 5b22904 commit 1655176

1 file changed

Lines changed: 64 additions & 0 deletions

File tree

packages/engine/src/utils/shaderTransitions.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,70 @@ describe("all transitions smoke test", () => {
576576
}
577577
});
578578

579+
// ── all transitions: midpoint regressions (p=0.5) ───────────────────────────
580+
//
581+
// Endpoint smoke tests above lock down p=0 (≈from) and p=1 (≈to). They miss
582+
// regressions where a shader becomes a no-op, prematurely completes, returns
583+
// garbage, or accidentally introduces non-determinism — specifically at the
584+
// midpoint where the transition is most visible to viewers. Four invariants
585+
// every shader must satisfy at p=0.5:
586+
//
587+
// 1. Output ≠ from catches "shader is a no-op, returns input as-is"
588+
// 2. Output ≠ to catches "shader prematurely completes at midpoint"
589+
// 3. Output is non-zero catches "shader didn't write anything to the buf"
590+
// 4. Output is deterministic — catches accidental Math.random / Date.now /
591+
// uninitialized-state regressions that would surface as flaky CI.
592+
//
593+
// Two distinct uniform colors give buffer-equality checks distinct byte
594+
// patterns to compare against. Even shaders that warp UVs (which would be
595+
// no-ops on uniform input alone) produce mix16(from, to, 0.5) = (25000, 20000,
596+
// 15000), distinct from both inputs at every pixel.
597+
describe("all transitions: midpoint regressions (p=0.5)", () => {
598+
for (const name of ALL_SHADERS) {
599+
describe(name, () => {
600+
const w = 8;
601+
const h = 8;
602+
const from = makeBuffer(w, h, 40000, 30000, 20000);
603+
const to = makeBuffer(w, h, 10000, 10000, 10000);
604+
const zeros = Buffer.alloc(w * h * 6);
605+
606+
it("output ≠ from (not a no-op at midpoint)", () => {
607+
const fn = TRANSITIONS[name];
608+
expect(fn).toBeDefined();
609+
const out = Buffer.alloc(w * h * 6);
610+
fn?.(from, to, out, w, h, 0.5);
611+
expect(out.equals(from)).toBe(false);
612+
});
613+
614+
it("output ≠ to (not premature completion at midpoint)", () => {
615+
const fn = TRANSITIONS[name];
616+
expect(fn).toBeDefined();
617+
const out = Buffer.alloc(w * h * 6);
618+
fn?.(from, to, out, w, h, 0.5);
619+
expect(out.equals(to)).toBe(false);
620+
});
621+
622+
it("output is non-zero (shader actually wrote pixels)", () => {
623+
const fn = TRANSITIONS[name];
624+
expect(fn).toBeDefined();
625+
const out = Buffer.alloc(w * h * 6);
626+
fn?.(from, to, out, w, h, 0.5);
627+
expect(out.equals(zeros)).toBe(false);
628+
});
629+
630+
it("output is deterministic across repeated calls", () => {
631+
const fn = TRANSITIONS[name];
632+
expect(fn).toBeDefined();
633+
const out1 = Buffer.alloc(w * h * 6);
634+
const out2 = Buffer.alloc(w * h * 6);
635+
fn?.(from, to, out1, w, h, 0.5);
636+
fn?.(from, to, out2, w, h, 0.5);
637+
expect(out2.equals(out1)).toBe(true);
638+
});
639+
});
640+
}
641+
});
642+
579643
// ── hdrToLinear / linearToHdr roundtrip ────────────────────────────────────
580644

581645
describe("hdrToLinear / linearToHdr", () => {

0 commit comments

Comments
 (0)