mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 15:20:13 +00:00
fix(producer): tighten chunk-boundary test gates + narrow VIDEO_EXT indexing
Address @vanceingalls and @miguel-heygen review findings on #852: 1. Asymmetric soft-skip — only the N=1 plan+render+assemble call was wrapped in the host-Chrome-failure catch; an SwiftShader / cold-Chrome flake on the N=4 call would hard-fail instead of soft-skip. Factor a local runRender() helper and wrap both calls. 2. Vacuously-passing length assertion — 'expect(framesOne.length).toBe( framesFour.length)' passes when both runs produce 0 frames. Pin the absolute count (EXPECTED_FRAME_COUNT = 60) so a regression that identically truncates both renders shows red. 3. CDN version drift — anime-boundary loaded gsap@3.14.2 from jsdelivr while every other boundary fixture loaded 3.12.2 from cdnjs. Unify on cdnjs@3.12.2 so the next reader doesn't have to wonder why one fixture diverges. (gsap is an empty duration-driver in all six fixtures so the version was never load-bearing — but the divergence reads as intentional and isn't.) 4. VIDEO_EXT type narrowing — the lookup is Record<"mp4"|"mov"|"webm"> but outputFormat includes "png-sequence". The isPngSequence ternary short-circuits before png-sequence can reach the indexing site, but TS can't narrow through that. Add an explicit cast at the indexing site (not the lookup definition — over-widening to include "png-sequence": undefined would defeat the existence guarantee). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
@@ -722,12 +722,19 @@ async function runTestSuite(
|
||||
// png-sequence output is a directory (basename = "frames"); encoded video
|
||||
// formats produce a single file (basename = "output.<ext>"). One lookup
|
||||
// covers both shapes for the in-temp render and the on-disk baseline.
|
||||
// `VIDEO_EXT` is intentionally typed against only the encoded-video set —
|
||||
// the `isPngSequence` ternary below short-circuits before `outputFormat`
|
||||
// can be `"png-sequence"`, but TS can't narrow through that, so we
|
||||
// assert the narrowing at the indexing site rather than over-widening
|
||||
// the lookup table.
|
||||
const VIDEO_EXT: Record<"mp4" | "mov" | "webm", string> = {
|
||||
mp4: ".mp4",
|
||||
mov: ".mov",
|
||||
webm: ".webm",
|
||||
};
|
||||
const outputBasename = isPngSequence ? "frames" : `output${VIDEO_EXT[outputFormat]}`;
|
||||
const outputBasename = isPngSequence
|
||||
? "frames"
|
||||
: `output${VIDEO_EXT[outputFormat as "mp4" | "mov" | "webm"]}`;
|
||||
const renderedOutputPath = join(tempRoot, outputBasename);
|
||||
|
||||
// Snapshot files stored in test's output/ directory. For png-sequence the
|
||||
|
||||
@@ -37,6 +37,11 @@ const HOST_CHROME_FAILURE_PATTERNS =
|
||||
// assemble pipeline so no `output/` baseline is required.
|
||||
const ADAPTERS = ["gsap", "anime", "three", "lottie", "css", "waapi"] as const;
|
||||
|
||||
// Every adapter fixture is a 2-second composition at 30fps. Pin the absolute
|
||||
// count so a regression that produces fewer frames in both runs (e.g. a
|
||||
// probe stage that reads duration as 0s) doesn't pass vacuously.
|
||||
const EXPECTED_FRAME_COUNT = 60;
|
||||
|
||||
let runRoot: string;
|
||||
let testsDistributedDir: string;
|
||||
|
||||
@@ -122,30 +127,30 @@ describe("per-adapter chunk-boundary byte equality", () => {
|
||||
mkdirSync(workOne, { recursive: true });
|
||||
mkdirSync(workFour, { recursive: true });
|
||||
|
||||
let outOne: string;
|
||||
try {
|
||||
outOne = await planAndAssemble({
|
||||
projectDir,
|
||||
workDir: workOne,
|
||||
chunkSize: 60,
|
||||
});
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
if (HOST_CHROME_FAILURE_PATTERNS.test(message)) {
|
||||
console.warn(
|
||||
`[chunkBoundary.test] skipping ${adapter} — host Chrome can't render. ` +
|
||||
"Docker harness covers the contract. Diagnostic:",
|
||||
message.slice(0, 240),
|
||||
);
|
||||
return;
|
||||
// Soft-skip when host Chrome can't render. Wrap *both* renders —
|
||||
// cold-Chrome / SwiftShader flakes happen on the second render
|
||||
// as readily as the first, and a hard-fail on the N=4 path would
|
||||
// diverge from the rest of the harness's soft-skip convention.
|
||||
const runRender = async (workDir: string, chunkSize: number): Promise<string | null> => {
|
||||
try {
|
||||
return await planAndAssemble({ projectDir, workDir, chunkSize });
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
if (HOST_CHROME_FAILURE_PATTERNS.test(message)) {
|
||||
console.warn(
|
||||
`[chunkBoundary.test] skipping ${adapter} — host Chrome can't render. ` +
|
||||
"Docker harness covers the contract. Diagnostic:",
|
||||
message.slice(0, 240),
|
||||
);
|
||||
return null;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
const outFour = await planAndAssemble({
|
||||
projectDir,
|
||||
workDir: workFour,
|
||||
chunkSize: 15,
|
||||
});
|
||||
};
|
||||
const outOne = await runRender(workOne, 60);
|
||||
if (outOne === null) return;
|
||||
const outFour = await runRender(workFour, 15);
|
||||
if (outFour === null) return;
|
||||
|
||||
// Per-frame byte equality across the two frames directories. A
|
||||
// boundary regression in the adapter's seek-determinism would
|
||||
@@ -157,7 +162,12 @@ describe("per-adapter chunk-boundary byte equality", () => {
|
||||
const framesFour = readdirSync(outFour)
|
||||
.filter((n) => n.toLowerCase().endsWith(".png"))
|
||||
.sort();
|
||||
expect(framesOne.length).toBe(framesFour.length);
|
||||
// Pin the absolute count, not just equality between the two runs.
|
||||
// Otherwise a regression that truncates BOTH renders identically
|
||||
// (e.g. a probe stage that misreads duration as 0s) would pass
|
||||
// vacuously — `0 === 0` is true.
|
||||
expect(framesOne.length).toBe(EXPECTED_FRAME_COUNT);
|
||||
expect(framesFour.length).toBe(EXPECTED_FRAME_COUNT);
|
||||
expect(framesOne).toEqual(framesFour);
|
||||
for (let i = 0; i < framesOne.length; i++) {
|
||||
const frameName = framesOne[i];
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>chunk-boundary: anime.js</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/animejs@4.0.2/lib/anime.iife.min.js"></script>
|
||||
<style>
|
||||
body,
|
||||
|
||||
Reference in New Issue
Block a user