From 35c612c5f6152476321f7a7abf4b9bd812fee184 Mon Sep 17 00:00:00 2001 From: Said Turksever Date: Mon, 19 Oct 2026 16:50:21 +0100 Subject: [PATCH] test(cctv): lock the still draw path and fit-aware dedupe Drive drawProjectionFrame with a fake runtime and a recording canvas: a 640x480 still draws at its fitted rectangle over the matte, the same pixels at the same size skip the redraw, and the same pixels at a new aspect redraw at the new fit. The last case fails on the pixel-only signature; the first fails if drawImage stretches to the canvas again. Expected rectangles now read the canvas size from policy.js. Co-Authored-By: Claude Opus 5.5 --- src/layers/cctv/frames.test.mjs | 108 +++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/src/layers/cctv/frames.test.mjs b/src/layers/cctv/frames.test.mjs index 5f21e4abd..0d03e9457 100644 --- a/src/layers/cctv/frames.test.mjs +++ b/src/layers/cctv/frames.test.mjs @@ -3,9 +3,25 @@ // 1920x1080 projection canvas, instead of being stretched to fill it. import { test } from 'node:test'; import assert from 'node:assert/strict'; -import { fitToProjection } from './frames.js'; +import { createFrames, fitToProjection } from './frames.js'; +import { + FRAME_SIGNATURE_W, + FRAME_SIGNATURE_H, + PROJECTION_CANVAS_WIDTH, + PROJECTION_CANVAS_HEIGHT, + PROJECTION_MATTE_COLOR, +} from './policy.js'; -const FULL_CANVAS = { x: 0, y: 0, w: 1920, h: 1080 }; +// The expected rectangles below are written for a 1920x1080 canvas. +assert.equal(PROJECTION_CANVAS_WIDTH, 1920); +assert.equal(PROJECTION_CANVAS_HEIGHT, 1080); + +const FULL_CANVAS = { + x: 0, + y: 0, + w: PROJECTION_CANVAS_WIDTH, + h: PROJECTION_CANVAS_HEIGHT, +}; test('a 4:3 still is pillarboxed at full height', () => { assert.deepEqual(fitToProjection(640, 480), { @@ -35,3 +51,91 @@ test('a missing or zero dimension falls back to the full canvas', () => { assert.deepEqual(fitToProjection(640, undefined), FULL_CANVAS); assert.deepEqual(fitToProjection(undefined, undefined), FULL_CANVAS); }); + +// Draw path: the fit reaches drawImage, and the unchanged-frame skip keys on the +// fitted size as well as the pixels (the 64x36 hash alone drops aspect). +function stillHarness() { + const calls = []; + const ctx = { + fillStyle: null, + fillRect: (...rect) => calls.push(['fillRect', ctx.fillStyle, ...rect]), + clearRect: () => {}, + drawImage: (_image, ...rect) => calls.push(['drawImage', ...rect]), + }; + // Every frame downsamples to the same pixels, so only the size differs. + const pixels = new Uint8ClampedArray( + FRAME_SIGNATURE_W * FRAME_SIGNATURE_H * 4, + ).fill(7); + const runtime = { + mode: 'image', + ctx, + image: { naturalWidth: 0, naturalHeight: 0 }, + imageReady: true, + // In flight, so refreshProjectionImage returns before touching image.src. + imageLoading: true, + imageStamp: 0, + drawnImageStamp: -1, + lastFrameSignature: null, + canvasStamp: 0, + signatureCtx: { + clearRect: () => {}, + drawImage: () => {}, + getImageData: () => ({ data: pixels }), + }, + }; + const record = { camera: { id: 'still' }, projection: runtime }; + const { drawProjectionFrame } = createFrames({ + state: { _healthById: new Map(), _activeCameraId: null }, + services: {}, + parts: {}, + source: {}, + }); + const load = (width, height) => { + runtime.image = { naturalWidth: width, naturalHeight: height }; + runtime.imageStamp += 1; + calls.length = 0; + drawProjectionFrame(record); + return calls.slice(); + }; + return { runtime, load }; +} + +test('a still is drawn into its fitted rectangle over a full matte', () => { + const { runtime, load } = stillHarness(); + assert.deepEqual(load(640, 480), [ + [ + 'fillRect', + PROJECTION_MATTE_COLOR, + 0, + 0, + PROJECTION_CANVAS_WIDTH, + PROJECTION_CANVAS_HEIGHT, + ], + ['drawImage', 240, 0, 1440, 1080], + ]); + assert.equal(runtime.canvasStamp, 1); +}); + +test('same pixels at the same size skip the redraw', () => { + const { runtime, load } = stillHarness(); + load(640, 480); + assert.deepEqual(load(640, 480), []); + assert.equal(runtime.canvasStamp, 1); +}); + +test('same pixels at a new aspect redraw at the new fit', () => { + const { runtime, load } = stillHarness(); + load(640, 480); + assert.deepEqual(load(1920, 1080), [ + [ + 'fillRect', + PROJECTION_MATTE_COLOR, + 0, + 0, + PROJECTION_CANVAS_WIDTH, + PROJECTION_CANVAS_HEIGHT, + ], + ['drawImage', 0, 0, 1920, 1080], + ]); + assert.equal(runtime.canvasStamp, 2); +});