diff --git a/Dockerfile.test b/Dockerfile.test index a20d795b1..d6e83f9c2 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -53,7 +53,12 @@ ENV CONTAINER=true # Install chrome-headless-shell for deterministic BeginFrame rendering. # This lightweight Chrome binary supports HeadlessExperimental.beginFrame. # Install to ~/.cache/puppeteer/ where resolveHeadlessShellPath() looks. -RUN npx --yes @puppeteer/browsers install chrome-headless-shell@stable \ +# +# Pinned to a specific build (NOT @stable) so the regression-test golden +# baselines in packages/producer/tests/*/output/output.mp4 stay reproducible. +# Each Chrome stable bump shifts pixel output enough to fail PSNR. Bump this +# version together with regenerating baselines via `docker:test:update`. +RUN npx --yes @puppeteer/browsers install chrome-headless-shell@148.0.7778.167 \ --path /root/.cache/puppeteer \ && find /root/.cache/puppeteer/chrome-headless-shell -name "chrome-headless-shell" -type f \ && echo "chrome-headless-shell installed" diff --git a/packages/producer/src/regression-harness.ts b/packages/producer/src/regression-harness.ts index 015cae007..b3cef543a 100644 --- a/packages/producer/src/regression-harness.ts +++ b/packages/producer/src/regression-harness.ts @@ -1096,16 +1096,19 @@ async function runTestSuite( videoMetadata.durationSeconds, snapshotMetadata.durationSeconds, ); + const fps = fpsToNumber(suite.meta.renderConfig.fps); + // Container duration includes audio padding past the last video frame + // (e.g. many-cuts: 5.654s container vs 5.6s of video). At i=99 the + // raw container duration maps to a frame index past nb_frames, and + // ffmpeg's PSNR filter emits no `average:` line for a non-existent + // frame. Subtract one frame interval so the last checkpoint always + // lands on a frame the video stream actually contains. + const sampleDuration = Math.max(0, videoDuration - 1 / fps); const minPsnrForMode = resolveMinPsnrForMode(options.mode, suite.meta.minPsnr); for (let i = 0; i < 100; i++) { - const time = (videoDuration * i) / 100; - const psnr = psnrAtCheckpoint( - renderedOutputPath, - snapshotVideoPath, - time, - fpsToNumber(suite.meta.renderConfig.fps), - ); + const time = (sampleDuration * i) / 100; + const psnr = psnrAtCheckpoint(renderedOutputPath, snapshotVideoPath, time, fps); visualCheckpoints.push({ time, psnr,