test(image-pipeline): tighten parity quality gates

This commit is contained in:
Matthias
2026-04-04 22:25:45 +02:00
parent 455458cb1e
commit c4ca4b001b
3 changed files with 238 additions and 4 deletions

View File

@@ -11,6 +11,8 @@ import {
} from "@/tests/image-pipeline/parity/fixtures";
describe("cpu vs webgl parity", () => {
// Contract-parity coverage in jsdom with a mocked WebGL context.
// This suite intentionally does not attempt driver-level GPU conformance.
afterEach(() => {
restoreParityWebglContextMock();
});
@@ -25,6 +27,7 @@ describe("cpu vs webgl parity", () => {
expect(metrics.histogramSimilarity).toBeGreaterThanOrEqual(
parityTolerances.curvesOnly.histogramSimilarity,
);
expect(metrics.spatialRmse).toBeLessThanOrEqual(parityTolerances.curvesOnly.spatialRmse);
});
it("keeps color-adjust-only pipeline within parity tolerance", () => {
@@ -39,6 +42,7 @@ describe("cpu vs webgl parity", () => {
expect(metrics.histogramSimilarity).toBeGreaterThanOrEqual(
parityTolerances.colorAdjustOnly.histogramSimilarity,
);
expect(metrics.spatialRmse).toBeLessThanOrEqual(parityTolerances.colorAdjustOnly.spatialRmse);
});
it("keeps curves + color-adjust chain within parity tolerance", () => {
@@ -53,5 +57,69 @@ describe("cpu vs webgl parity", () => {
expect(metrics.histogramSimilarity).toBeGreaterThanOrEqual(
parityTolerances.curvesPlusColorAdjust.histogramSimilarity,
);
expect(metrics.spatialRmse).toBeLessThanOrEqual(
parityTolerances.curvesPlusColorAdjust.spatialRmse,
);
});
it("keeps channel-specific curves pressure case within parity tolerance", () => {
const pipelines = createParityPipelines();
installParityWebglContextMock();
const metrics = evaluateCpuWebglParity(pipelines.curvesChannelPressure);
expect(metrics.maxChannelDelta).toBeLessThanOrEqual(
parityTolerances.curvesChannelPressure.maxChannelDelta,
);
expect(metrics.histogramSimilarity).toBeGreaterThanOrEqual(
parityTolerances.curvesChannelPressure.histogramSimilarity,
);
expect(metrics.spatialRmse).toBeLessThanOrEqual(
parityTolerances.curvesChannelPressure.spatialRmse,
);
});
it("keeps strong color-adjust pressure case within parity tolerance", () => {
const pipelines = createParityPipelines();
installParityWebglContextMock();
const metrics = evaluateCpuWebglParity(pipelines.colorAdjustPressure);
expect(metrics.maxChannelDelta).toBeLessThanOrEqual(
parityTolerances.colorAdjustPressure.maxChannelDelta,
);
expect(metrics.histogramSimilarity).toBeGreaterThanOrEqual(
parityTolerances.colorAdjustPressure.histogramSimilarity,
);
expect(metrics.spatialRmse).toBeLessThanOrEqual(
parityTolerances.colorAdjustPressure.spatialRmse,
);
});
it("keeps curves + color-adjust pressure chain within parity tolerance", () => {
const pipelines = createParityPipelines();
installParityWebglContextMock();
const metrics = evaluateCpuWebglParity(pipelines.curvesColorPressureChain);
expect(metrics.maxChannelDelta).toBeLessThanOrEqual(
parityTolerances.curvesColorPressureChain.maxChannelDelta,
);
expect(metrics.histogramSimilarity).toBeGreaterThanOrEqual(
parityTolerances.curvesColorPressureChain.histogramSimilarity,
);
expect(metrics.spatialRmse).toBeLessThanOrEqual(
parityTolerances.curvesColorPressureChain.spatialRmse,
);
});
it("is deterministic across repeated parity evaluations", () => {
const pipelines = createParityPipelines();
installParityWebglContextMock();
const first = evaluateCpuWebglParity(pipelines.curvesColorPressureChain);
const second = evaluateCpuWebglParity(pipelines.curvesColorPressureChain);
expect(second).toEqual(first);
});
});