Files
hyperframes/packages/producer/src/services/renderOrchestrator.test.ts
T
Vance IngallsandClaude Opus 5 b99c803898 fix(producer): guard pmset behind shouldTrack + don't pin workers without streaming (review)
Two review findings on the floor/telemetry PR:

1. powerStateFields() is spread into the properties object at the CALL SITE,
   so it ran before trackEvent's own `if (!shouldTrack()) return` guard —
   telemetry-disabled installs paid two blocking `pmset` subprocess spawns
   per render for an event that was then discarded. Now short-circuits on
   shouldTrack() (memoized, so no cost on the tracked path). Regression test
   asserts pmset is not sampled when telemetry is off; fault-injection
   verified it fails without the guard.

2. The DE parallel router pinned workerCount to 3 and skipped calibration
   even when verified parallel DE STREAMING — the entire reason for the pin
   — could not run for that render. The common case is a composition over
   streamingEncodeMaxDurationSeconds (240 s default): the duration cap
   disables streaming before the router's force flag is consulted, so the
   render got a hard-coded 3 workers chosen by a benchmark for a path it was
   not on, instead of the calibrated count. shouldPreferParallelDrawElement
   now takes parallelStreamingAvailable and withholds the bet without it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 00:59:22 -07:00

2144 lines
72 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { afterEach, describe, expect, it, vi } from "vitest";
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { join, win32 } from "node:path";
import { tmpdir } from "node:os";
import type { CaptureOptions, EngineConfig, ExtractedFrames } from "@hyperframes/engine";
import { executeParallelCapture, mergeWorkerFrames } from "@hyperframes/engine";
import type { CompiledComposition } from "./htmlCompiler.js";
// Replace only the two engine functions the adaptive-retry loop uses to touch
// disk; everything else (distributeFrames, types, etc.) stays real so the loop
// runs for real against a temp framesDir.
vi.mock("@hyperframes/engine", async (importOriginal) => {
const actual = await importOriginal<typeof import("@hyperframes/engine")>();
return { ...actual, executeParallelCapture: vi.fn(), mergeWorkerFrames: vi.fn() };
});
import {
buildMissingFrameRetryBatches,
captureAttemptMadeProgress,
describeMemoryExhaustion,
executeDiskCaptureWithAdaptiveRetry,
collectVideoMetadataHints,
collectVideoReadinessSkipIds,
extractStandaloneEntryFromIndex,
findMissingFrameRanges,
getNextRetryWorkerCount,
isRecoverableParallelCaptureError,
MAX_TRANSIENT_CAPTURE_RETRIES,
resolveCaptureForceScreenshotForPageSideCompositing,
resolveRenderWorkDirPrefix,
shouldDiscardProbeSessionForPageSideCompositing,
resolveInversionRetryPlan,
resolveParallelRouterRetryPlan,
resetCaptureAttemptProgress,
shouldRetryViaPinnedFallback,
shouldPreferParallelDrawElement,
shouldPreferSingleWorkerDrawElement,
shouldStreamParallelCapture,
shouldUseStreamingEncode,
} from "./renderOrchestrator.js";
import { ensureFrameWritten } from "./render/stages/captureHdrFrameShared.js";
import { resolveCompositeTransfer, shouldUseLayeredComposite } from "./hdrCompositor.js";
import {
createCaptureCalibrationConfig,
estimateCaptureCostMultiplier,
estimateMeasuredCaptureCostMultiplier,
resolveRenderWorkerCount,
selectCaptureCalibrationFrames,
shouldFallbackToScreenshotAfterCalibrationError,
} from "./render/captureCost.js";
import {
applyRenderModeHints,
createCompiledFrameSrcResolver,
materializeExtractedFramesForCompiledDir,
projectBrowserEndToCompositionTimeline,
resolveDeviceScaleFactor,
writeCompiledArtifacts,
} from "./render/shared.js";
import { formatCaptureFrameName, toExternalAssetKey } from "../utils/paths.js";
describe("resolveRenderWorkDirPrefix", () => {
it("uses a short system temp prefix on Windows instead of the output path", () => {
const outputPath = win32.join("C:\\deep", "nested".repeat(30), "renders", "final.mp4");
expect(resolveRenderWorkDirPrefix(outputPath, "long-render-job-id", "win32", "C:/Temp")).toBe(
join("C:/Temp", "hf-render-"),
);
});
});
describe("extractStandaloneEntryFromIndex", () => {
it("reuses the index wrapper and keeps only the requested composition host", () => {
const indexHtml = `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>body { background: #111; }</style>
</head>
<body>
<div id="main" data-composition-id="root" data-width="1920" data-height="1080">
<div id="intro" data-composition-id="intro" data-composition-src="compositions/intro.html" data-start="5"></div>
<div id="outro" data-composition-id="outro" data-composition-src="compositions/outro.html" data-start="12"></div>
</div>
</body>
</html>`;
const extracted = extractStandaloneEntryFromIndex(indexHtml, "compositions/outro.html");
expect(extracted).toContain('data-composition-id="root"');
expect(extracted).toContain('id="outro"');
expect(extracted).toContain('data-composition-src="compositions/outro.html"');
expect(extracted).toContain('data-start="0"');
expect(extracted).not.toContain('id="intro"');
expect(extracted).toContain("<style>body { background: #111; }</style>");
});
it("matches normalized data-composition-src paths", () => {
const indexHtml = `<!DOCTYPE html>
<html>
<body>
<div data-composition-id="root" data-width="1920" data-height="1080">
<div id="intro" data-composition-id="intro" data-composition-src="./compositions/intro.html" data-start="3"></div>
</div>
</body>
</html>`;
const extracted = extractStandaloneEntryFromIndex(indexHtml, "compositions/intro.html");
expect(extracted).not.toBeNull();
expect(extracted).toContain('data-start="0"');
expect(extracted).toContain('data-composition-src="./compositions/intro.html"');
});
it("returns null when index.html does not mount the requested entry file", () => {
const indexHtml = `<!DOCTYPE html>
<html>
<body>
<div data-composition-id="root" data-width="1920" data-height="1080">
<div id="intro" data-composition-id="intro" data-composition-src="compositions/intro.html"></div>
</div>
</body>
</html>`;
const extracted = extractStandaloneEntryFromIndex(indexHtml, "compositions/outro.html");
expect(extracted).toBeNull();
});
it("re-points the wrapper duration at the scene's own, not the master's", () => {
const indexHtml = `<!DOCTYPE html>
<html>
<body>
<div data-composition-id="master" data-width="640" data-height="360" data-duration="12">
<div id="scene1" data-composition-id="scene1" data-composition-src="compositions/scene1.html" data-start="0" data-duration="2"></div>
</div>
</body>
</html>`;
const sceneHtml = `<template id="scene1-template"><div data-composition-id="scene1" data-width="640" data-height="360" data-duration="3"></div></template>`;
const extracted = extractStandaloneEntryFromIndex(
indexHtml,
"compositions/scene1.html",
sceneHtml,
);
// The extracted standalone advertises the scene file's 3s, not the mount's 2s or master's 12s.
expect(extracted).toContain('data-duration="3"');
expect(extracted).not.toContain('data-duration="12"');
});
it("falls back to the mount's data-duration when the scene file isn't supplied", () => {
const indexHtml = `<!DOCTYPE html>
<html>
<body>
<div data-composition-id="master" data-width="640" data-height="360" data-duration="12">
<div id="scene1" data-composition-id="scene1" data-composition-src="compositions/scene1.html" data-start="0" data-duration="2"></div>
</div>
</body>
</html>`;
const extracted = extractStandaloneEntryFromIndex(indexHtml, "compositions/scene1.html");
expect(extracted).toContain('data-duration="2"');
expect(extracted).not.toContain('data-duration="12"');
});
});
describe("captureAttemptMadeProgress", () => {
it("resets completed frames before a fallback attempt starts", () => {
const job = { framesRendered: 900 };
resetCaptureAttemptProgress(job);
expect(job.framesRendered).toBe(0);
});
it("retries when the attempt captured at least one frame toward its target", () => {
// targeted 100 frames, 40 still missing -> 60 captured -> worth retrying the rest
expect(captureAttemptMadeProgress(100, 40)).toBe(true);
expect(captureAttemptMadeProgress(100, 99)).toBe(true);
});
it("bails when the attempt captured nothing (structurally broken composition)", () => {
// targeted 100 frames, 100 still missing -> zero progress -> don't burn another timeout cycle
expect(captureAttemptMadeProgress(100, 100)).toBe(false);
// defensive: never-greater-than guard, treat >= target as no progress
expect(captureAttemptMadeProgress(100, 120)).toBe(false);
});
});
describe("executeDiskCaptureWithAdaptiveRetry — zero-progress bail (integration)", () => {
const makeLog = () => ({ error: vi.fn(), warn: vi.fn(), info: vi.fn(), debug: vi.fn() });
afterEach(() => {
vi.mocked(executeParallelCapture).mockReset();
vi.mocked(mergeWorkerFrames).mockReset();
});
it("runs exactly one attempt (no worker-halving retries) when an attempt captures zero frames", async () => {
// Capture writes nothing -> framesDir stays empty -> every frame still missing.
vi.mocked(executeParallelCapture).mockResolvedValue([]);
vi.mocked(mergeWorkerFrames).mockResolvedValue(undefined);
const workDir = mkdtempSync(join(tmpdir(), "hf-retry-work-"));
const framesDir = mkdtempSync(join(tmpdir(), "hf-retry-frames-"));
const log = makeLog();
try {
await expect(
executeDiskCaptureWithAdaptiveRetry({
serverUrl: "http://localhost:0",
workDir,
framesDir,
totalFrames: 4,
initialWorkerCount: 4,
allowRetry: true,
frameExt: "jpg",
captureOptions: {} as CaptureOptions,
createBeforeCaptureHook: () => null,
cfg: {} as EngineConfig,
log,
dedupPerfs: [],
}),
).rejects.toThrow(/4 frame\(s\) are missing/);
// The gate under test: without it the loop would walk 4 -> 2 -> 1 workers
// (3 capture calls) before giving up. One call proves it bailed immediately.
expect(vi.mocked(executeParallelCapture)).toHaveBeenCalledTimes(1);
expect(log.warn).toHaveBeenCalledWith(
expect.stringContaining("no forward progress"),
expect.objectContaining({ attempt: 0, frameCount: 4, remainingCount: 4 }),
);
} finally {
rmSync(workDir, { recursive: true, force: true });
rmSync(framesDir, { recursive: true, force: true });
}
});
});
describe("executeDiskCaptureWithAdaptiveRetry — transient Target-closed single retry (integration)", () => {
const makeLog = () => ({ error: vi.fn(), warn: vi.fn(), info: vi.fn(), debug: vi.fn() });
const writeAllFrames = (framesDir: string, totalFrames: number): void => {
for (let i = 0; i < totalFrames; i++) {
writeFileSync(join(framesDir, formatCaptureFrameName(i, "jpg")), "captured-frame");
}
};
afterEach(() => {
vi.mocked(executeParallelCapture).mockReset();
vi.mocked(mergeWorkerFrames).mockReset();
});
it("retries ONCE at the same worker count on a transient Target closed with zero progress", async () => {
const workDir = mkdtempSync(join(tmpdir(), "hf-transient-work-"));
const framesDir = mkdtempSync(join(tmpdir(), "hf-transient-frames-"));
const log = makeLog();
let call = 0;
// First attempt: the tab dies before any frame is captured (frame 0) — zero
// forward progress, which the worker-halving retry deliberately bails on.
// The transient retry recovers it without changing the worker count.
vi.mocked(executeParallelCapture).mockImplementation(async () => {
call++;
if (call === 1) {
throw new Error("Protocol error (Page.captureScreenshot): Target closed");
}
writeAllFrames(framesDir, 4);
return [];
});
vi.mocked(mergeWorkerFrames).mockResolvedValue(undefined);
try {
const attempts = await executeDiskCaptureWithAdaptiveRetry({
serverUrl: "http://localhost:0",
workDir,
framesDir,
totalFrames: 4,
initialWorkerCount: 1,
allowRetry: true,
frameExt: "jpg",
captureOptions: {} as CaptureOptions,
createBeforeCaptureHook: () => null,
cfg: {} as EngineConfig,
log,
dedupPerfs: [],
});
expect(vi.mocked(executeParallelCapture)).toHaveBeenCalledTimes(2);
// Both attempts ran at the same worker count (transient retry doesn't halve).
expect(attempts.map((a) => a.workers)).toEqual([1, 1]);
// The retry attempt is tagged `transient-retry` (vs the worker-halving
// `retry`) so it's countable for telemetry (dashboard 1783183).
expect(attempts.map((a) => a.reason)).toEqual(["initial", "transient-retry"]);
expect(log.warn).toHaveBeenCalledWith(
expect.stringContaining("Transient browser failure"),
expect.objectContaining({ transientRetriesUsed: 1 }),
);
} finally {
rmSync(workDir, { recursive: true, force: true });
rmSync(framesDir, { recursive: true, force: true });
}
});
it("does NOT retry a transient error when the render was aborted", async () => {
const workDir = mkdtempSync(join(tmpdir(), "hf-transient-abort-work-"));
const framesDir = mkdtempSync(join(tmpdir(), "hf-transient-abort-frames-"));
const log = makeLog();
const controller = new AbortController();
// Cancellation tears the browser down, surfacing as a transient-looking
// "Target closed" — but an aborted render must fail immediately, not retry.
vi.mocked(executeParallelCapture).mockImplementation(async () => {
controller.abort();
throw new Error("Target closed");
});
vi.mocked(mergeWorkerFrames).mockResolvedValue(undefined);
try {
await expect(
executeDiskCaptureWithAdaptiveRetry({
serverUrl: "http://localhost:0",
workDir,
framesDir,
totalFrames: 4,
initialWorkerCount: 2,
allowRetry: true,
frameExt: "jpg",
captureOptions: {} as CaptureOptions,
createBeforeCaptureHook: () => null,
abortSignal: controller.signal,
cfg: {} as EngineConfig,
log,
dedupPerfs: [],
}),
).rejects.toThrow(/Target closed/);
// Exactly one attempt — no transient retry burned on a cancelled render.
expect(vi.mocked(executeParallelCapture)).toHaveBeenCalledTimes(1);
expect(log.warn).not.toHaveBeenCalledWith(
expect.stringContaining("Transient browser failure"),
expect.anything(),
);
} finally {
rmSync(workDir, { recursive: true, force: true });
rmSync(framesDir, { recursive: true, force: true });
}
});
it("gives up after MAX_TRANSIENT_CAPTURE_RETRIES when the tab keeps dying", async () => {
const workDir = mkdtempSync(join(tmpdir(), "hf-transient2-work-"));
const framesDir = mkdtempSync(join(tmpdir(), "hf-transient2-frames-"));
const log = makeLog();
vi.mocked(executeParallelCapture).mockRejectedValue(new Error("Session closed"));
vi.mocked(mergeWorkerFrames).mockResolvedValue(undefined);
try {
await expect(
executeDiskCaptureWithAdaptiveRetry({
serverUrl: "http://localhost:0",
workDir,
framesDir,
totalFrames: 4,
initialWorkerCount: 1,
allowRetry: true,
frameExt: "jpg",
captureOptions: {} as CaptureOptions,
createBeforeCaptureHook: () => null,
cfg: {} as EngineConfig,
log,
dedupPerfs: [],
}),
).rejects.toThrow(/Session closed/);
// 1 initial attempt + exactly MAX_TRANSIENT_CAPTURE_RETRIES retries.
expect(vi.mocked(executeParallelCapture)).toHaveBeenCalledTimes(
1 + MAX_TRANSIENT_CAPTURE_RETRIES,
);
} finally {
rmSync(workDir, { recursive: true, force: true });
rmSync(framesDir, { recursive: true, force: true });
}
});
});
describe("describeMemoryExhaustion", () => {
it("returns actionable guidance for a memory-exhaustion error", () => {
const msg = describeMemoryExhaustion(new Error("Set maximum size exceeded"), {
width: 3840,
height: 2160,
totalFrames: 5400,
});
expect(msg).not.toBeNull();
expect(msg).toContain("ran out of memory");
expect(msg).toContain("3840×2160");
expect(msg).toContain("5400 frames");
expect(msg).toContain("Set maximum size exceeded");
expect(msg).toContain("--low-memory-mode");
});
it("omits dimensions when they are unknown", () => {
const msg = describeMemoryExhaustion(new Error("JavaScript heap out of memory"), {});
expect(msg).not.toBeNull();
expect(msg).not.toContain("×");
});
it("returns null for a non-memory error (leaves the original message intact)", () => {
expect(
describeMemoryExhaustion(new Error("Target closed"), {
width: 1920,
height: 1080,
totalFrames: 100,
}),
).toBeNull();
});
});
describe("ensureFrameWritten", () => {
it("returns without throwing when the frame was written", () => {
expect(() => ensureFrameWritten(true, 0)).not.toThrow();
});
it("throws a bare frame-indexed error when no encoder context is supplied", () => {
expect(() => ensureFrameWritten(false, 7)).toThrow(
"Streaming encoder exited before frame 7 was written",
);
});
it("includes the ffmpeg exit reason when the encoder reports one", () => {
const encoder = { getExitError: () => "FFmpeg exited with code 1: Unknown encoder 'libx264'" };
expect(() => ensureFrameWritten(false, 0, encoder)).toThrow(
/Streaming encoder exited before frame 0 was written: FFmpeg exited with code 1: Unknown encoder 'libx264'/,
);
});
it("falls back to the bare message when the encoder has no exit reason yet", () => {
const encoder = { getExitError: () => undefined };
expect(() => ensureFrameWritten(false, 3, encoder)).toThrow(
"Streaming encoder exited before frame 3 was written",
);
});
});
describe("shouldUseStreamingEncode", () => {
const streamingEnabledConfig = {
enableStreamingEncode: true,
streamingEncodeMaxDurationSeconds: 240,
lowMemoryMode: false,
};
it("enables streaming for default single-worker video renders", () => {
expect(shouldUseStreamingEncode(streamingEnabledConfig, "mp4", 1, 240)).toBe(true);
});
it("lets config disable streaming encode", () => {
expect(
shouldUseStreamingEncode(
{ enableStreamingEncode: false, streamingEncodeMaxDurationSeconds: 240 },
"mp4",
1,
240,
),
).toBe(false);
});
it("keeps png-sequence and parallel capture on the non-streaming path", () => {
expect(shouldUseStreamingEncode(streamingEnabledConfig, "png-sequence", 1, 240)).toBe(false);
expect(shouldUseStreamingEncode(streamingEnabledConfig, "mp4", 2, 240)).toBe(false);
});
it("forceParallelStream overrides the parallel-capture clamp for verified multi-worker streaming", () => {
expect(shouldUseStreamingEncode(streamingEnabledConfig, "mp4", 3, 240, true)).toBe(true);
expect(shouldUseStreamingEncode(streamingEnabledConfig, "mp4", 3, 240, false)).toBe(false);
expect(shouldUseStreamingEncode(streamingEnabledConfig, "png-sequence", 3, 240, true)).toBe(
false,
);
});
it("keeps renders over the configured max duration on normal encoding", () => {
expect(shouldUseStreamingEncode(streamingEnabledConfig, "mp4", 1, 240)).toBe(true);
expect(shouldUseStreamingEncode(streamingEnabledConfig, "mp4", 1, 240.001)).toBe(false);
expect(
shouldUseStreamingEncode(
{ enableStreamingEncode: true, streamingEncodeMaxDurationSeconds: 120 },
"mp4",
1,
120.001,
),
).toBe(false);
});
it("keeps long single-worker renders streaming in low-memory mode", () => {
expect(
shouldUseStreamingEncode({ ...streamingEnabledConfig, lowMemoryMode: true }, "mp4", 1, 411),
).toBe(true);
});
});
describe("createCompiledFrameSrcResolver", () => {
it("maps extracted frame paths under compiledDir to encoded server URLs", () => {
const resolver = createCompiledFrameSrcResolver("/tmp/hf job/compiled");
expect(
resolver("/tmp/hf job/compiled/__hyperframes_video_frames/video 1/frame_00001.jpg"),
).toBe("/__hyperframes_video_frames/video%201/frame_00001.jpg");
});
it("returns null for paths outside compiledDir", () => {
const resolver = createCompiledFrameSrcResolver("/tmp/hf-job/compiled");
expect(resolver("/tmp/hf-job/video-frames/frame_00001.jpg")).toBeNull();
});
it("resolves symlinked cache frames when materialized under compiledDir", () => {
const resolver = createCompiledFrameSrcResolver("/tmp/hf-job/compiled");
expect(resolver("/tmp/hf-job/compiled/__hyperframes_video_frames/vid1/frame_00001.jpg")).toBe(
"/__hyperframes_video_frames/vid1/frame_00001.jpg",
);
expect(resolver("/tmp/cache/abc123/frame_00001.jpg")).toBeNull();
});
it("encodes reserved characters in frame path segments", () => {
const resolver = createCompiledFrameSrcResolver("/tmp/hf-job/compiled");
expect(
resolver("/tmp/hf-job/compiled/__hyperframes_video_frames/video#1/frame_00001.jpg"),
).toBe("/__hyperframes_video_frames/video%231/frame_00001.jpg");
expect(
resolver("/tmp/hf-job/compiled/__hyperframes_video_frames/video?q=1/frame_00001.jpg"),
).toBe("/__hyperframes_video_frames/video%3Fq%3D1/frame_00001.jpg");
});
});
describe("materializeExtractedFramesForCompiledDir", () => {
function createExtractedFrames(
outputDir: string,
framePath: string,
): Pick<ExtractedFrames, "videoId" | "outputDir" | "framePaths"> {
return {
videoId: "video-1",
outputDir,
framePaths: new Map([[0, framePath]]),
};
}
it("leaves Windows frame paths already under compiledDir unchanged", () => {
const compiledDir = win32.resolve("C:\\compiled");
const outputDir = win32.join(compiledDir, "__hyperframes_video_frames", "video-1");
const framePath = win32.join(outputDir, "frame_000001.jpg");
const extracted = createExtractedFrames(outputDir, framePath);
materializeExtractedFramesForCompiledDir([extracted], compiledDir, {
pathModule: win32,
fileSystem: {
existsSync: () => {
throw new Error("inside compiledDir should not touch the filesystem");
},
mkdirSync: () => {
throw new Error("inside compiledDir should not mkdir");
},
symlinkSync: () => {
throw new Error("inside compiledDir should not symlink");
},
cpSync: () => {
throw new Error("inside compiledDir should not copy");
},
},
});
expect(extracted.outputDir).toBe(outputDir);
expect(extracted.framePaths.get(0)).toBe(framePath);
});
// fallow-ignore-next-line code-duplication
it("remaps Windows cache frames under compiledDir using only the frame basename", () => {
const compiledDir = win32.resolve("C:\\compiled");
const outputDir = win32.resolve("D:\\cache\\abc123");
const framePath = win32.join(outputDir, "frame_000001.jpg");
const extracted = createExtractedFrames(outputDir, framePath);
const symlinks: Array<{ target: string; path: string }> = [];
// fallow-ignore-next-line code-duplication
materializeExtractedFramesForCompiledDir([extracted], compiledDir, {
pathModule: win32,
fileSystem: {
existsSync: () => false,
mkdirSync: () => undefined,
symlinkSync: (target, path) => {
symlinks.push({ target, path });
},
cpSync: () => {
throw new Error("symlink path should not invoke cpSync");
},
},
});
const linkPath = win32.join(compiledDir, "__hyperframes_video_frames", "video-1");
expect(extracted.outputDir).toBe(linkPath);
expect(extracted.framePaths.get(0)).toBe(win32.join(linkPath, "frame_000001.jpg"));
expect(extracted.framePaths.get(0)).not.toContain(outputDir);
expect(symlinks).toEqual([{ target: outputDir, path: linkPath }]);
});
// fallow-ignore-next-line code-duplication
it("recursively copies frames into compiledDir when materializeSymlinks is true", () => {
// Distributed plan() must produce a self-contained planDir — symlinks
// don't survive S3 / GCS round-trips. With materializeSymlinks=true the
// helper invokes cpSync(recursive) instead of symlinkSync.
const compiledDir = win32.resolve("C:\\compiled");
const outputDir = win32.resolve("D:\\cache\\abc123");
const framePath = win32.join(outputDir, "frame_000001.jpg");
const extracted = createExtractedFrames(outputDir, framePath);
const copies: Array<{ src: string; dest: string; recursive: boolean }> = [];
// fallow-ignore-next-line code-duplication
materializeExtractedFramesForCompiledDir([extracted], compiledDir, {
pathModule: win32,
fileSystem: {
existsSync: () => false,
mkdirSync: () => undefined,
symlinkSync: () => {
throw new Error("copy path should not invoke symlinkSync");
},
cpSync: (src, dest, options) => {
copies.push({ src, dest, recursive: options.recursive });
},
},
materializeSymlinks: true,
});
const linkPath = win32.join(compiledDir, "__hyperframes_video_frames", "video-1");
expect(extracted.outputDir).toBe(linkPath);
expect(extracted.framePaths.get(0)).toBe(win32.join(linkPath, "frame_000001.jpg"));
expect(copies).toEqual([{ src: outputDir, dest: linkPath, recursive: true }]);
});
// fallow-ignore-next-line code-duplication
it("falls back to copying frames when symlinkSync fails with EPERM (Windows, no Developer Mode)", () => {
// Windows without Developer Mode/Administrator rejects symlink creation with
// EPERM — high/standard-quality renders failed here while draft worked. The
// helper must degrade to a recursive copy instead of throwing.
const compiledDir = win32.resolve("C:\\compiled");
const outputDir = win32.resolve("D:\\cache\\abc123");
const framePath = win32.join(outputDir, "frame_000001.jpg");
const extracted = createExtractedFrames(outputDir, framePath);
const copies: Array<{ src: string; dest: string; recursive: boolean }> = [];
// fallow-ignore-next-line code-duplication
materializeExtractedFramesForCompiledDir([extracted], compiledDir, {
pathModule: win32,
fileSystem: {
existsSync: () => false,
mkdirSync: () => undefined,
symlinkSync: () => {
const err: NodeJS.ErrnoException = new Error("EPERM: operation not permitted, symlink");
err.code = "EPERM";
throw err;
},
cpSync: (src, dest, options) => {
copies.push({ src, dest, recursive: options.recursive });
},
},
});
const linkPath = win32.join(compiledDir, "__hyperframes_video_frames", "video-1");
expect(copies).toEqual([{ src: outputDir, dest: linkPath, recursive: true }]);
expect(extracted.outputDir).toBe(linkPath);
expect(extracted.framePaths.get(0)).toBe(win32.join(linkPath, "frame_000001.jpg"));
});
it("rethrows a non-permission symlink error instead of masking it with a copy", () => {
const compiledDir = win32.resolve("C:\\compiled");
const outputDir = win32.resolve("D:\\cache\\abc123");
const framePath = win32.join(outputDir, "frame_000001.jpg");
const extracted = createExtractedFrames(outputDir, framePath);
expect(() =>
materializeExtractedFramesForCompiledDir([extracted], compiledDir, {
pathModule: win32,
fileSystem: {
existsSync: () => false,
mkdirSync: () => undefined,
symlinkSync: () => {
const err: NodeJS.ErrnoException = new Error("ENOSPC: no space left");
err.code = "ENOSPC";
throw err;
},
cpSync: () => {
throw new Error("must not fall back to copy for a non-permission error");
},
},
}),
).toThrow(/ENOSPC/);
});
// fallow-ignore-next-line code-duplication
it("clears a stale dangling entry and re-stages when symlinkSync fails with EEXIST", () => {
// After the extraction cache is GC'd, a symlink from a prior render dangles
// (its target removed). existsSync() follows the dead link so the caller's
// guard reads it as absent and reaches staging, but the link file itself
// still exists, so symlinkSync collides with EEXIST. The helper must clear
// the stale entry (rmSync) and re-stage, not hard-fail the render.
const compiledDir = win32.resolve("C:\\compiled");
const outputDir = win32.resolve("D:\\cache\\abc123");
const framePath = win32.join(outputDir, "frame_000001.jpg");
const extracted = createExtractedFrames(outputDir, framePath);
const linkPath = win32.join(compiledDir, "__hyperframes_video_frames", "video-1");
const removed: string[] = [];
const symlinks: Array<{ target: string; path: string }> = [];
let symlinkCalls = 0;
// fallow-ignore-next-line code-duplication
materializeExtractedFramesForCompiledDir([extracted], compiledDir, {
pathModule: win32,
fileSystem: {
existsSync: () => false,
mkdirSync: () => undefined,
symlinkSync: (target, path) => {
symlinkCalls += 1;
if (symlinkCalls === 1) {
const err: NodeJS.ErrnoException = new Error("EEXIST: file already exists, symlink");
err.code = "EEXIST";
throw err;
}
symlinks.push({ target, path });
},
cpSync: () => {
throw new Error("EEXIST recovery should re-link, not copy");
},
rmSync: (path) => {
removed.push(path);
},
},
});
expect(removed).toEqual([linkPath]);
expect(symlinks).toEqual([{ target: outputDir, path: linkPath }]);
expect(extracted.framePaths.get(0)).toBe(win32.join(linkPath, "frame_000001.jpg"));
});
// fallow-ignore-next-line code-duplication
it("falls back to copying when symlinkSync fails with UNKNOWN (some Windows privilege denials)", () => {
// Some Windows builds surface a no-symlink-privilege denial as an
// UNKNOWN-coded error rather than EPERM/EACCES — it must still degrade to a
// copy, not hard-fail the render.
const compiledDir = win32.resolve("C:\\compiled");
const outputDir = win32.resolve("D:\\cache\\abc123");
const framePath = win32.join(outputDir, "frame_000001.jpg");
const extracted = createExtractedFrames(outputDir, framePath);
const copies: Array<{ src: string; dest: string; recursive: boolean }> = [];
// fallow-ignore-next-line code-duplication
materializeExtractedFramesForCompiledDir([extracted], compiledDir, {
pathModule: win32,
fileSystem: {
existsSync: () => false,
mkdirSync: () => undefined,
symlinkSync: () => {
const err: NodeJS.ErrnoException = new Error("UNKNOWN: unknown error, symlink");
err.code = "UNKNOWN";
throw err;
},
cpSync: (src, dest, options) => {
copies.push({ src, dest, recursive: options.recursive });
},
},
});
const linkPath = win32.join(compiledDir, "__hyperframes_video_frames", "video-1");
expect(copies).toEqual([{ src: outputDir, dest: linkPath, recursive: true }]);
expect(extracted.framePaths.get(0)).toBe(win32.join(linkPath, "frame_000001.jpg"));
});
// fallow-ignore-next-line code-duplication
it("clears a stale entry and re-copies when the eager-copy path (materializeSymlinks) hits EEXIST", () => {
// #2025 routes Windows through the eager-copy branch. Reusing a dir a prior
// Linux run populated with a (now dangling) symlink makes cpSync collide
// with EEXIST — the recovery must clear the stale entry and re-copy, exactly
// like the symlink path does.
const compiledDir = win32.resolve("C:\\compiled");
const outputDir = win32.resolve("D:\\cache\\abc123");
const framePath = win32.join(outputDir, "frame_000001.jpg");
const extracted = createExtractedFrames(outputDir, framePath);
const linkPath = win32.join(compiledDir, "__hyperframes_video_frames", "video-1");
const removed: string[] = [];
const copies: Array<{ src: string; dest: string }> = [];
let cpCalls = 0;
// fallow-ignore-next-line code-duplication
materializeExtractedFramesForCompiledDir([extracted], compiledDir, {
pathModule: win32,
materializeSymlinks: true,
fileSystem: {
existsSync: () => false,
mkdirSync: () => undefined,
symlinkSync: () => {
throw new Error("eager-copy path must not symlink");
},
cpSync: (src, dest) => {
cpCalls += 1;
if (cpCalls === 1) {
const err: NodeJS.ErrnoException = new Error("EEXIST: file already exists, cp");
err.code = "EEXIST";
throw err;
}
copies.push({ src, dest });
},
rmSync: (path) => {
removed.push(path);
},
},
});
expect(removed).toEqual([linkPath]);
expect(copies).toEqual([{ src: outputDir, dest: linkPath }]);
expect(extracted.framePaths.get(0)).toBe(win32.join(linkPath, "frame_000001.jpg"));
});
});
describe("writeCompiledArtifacts — external assets on Windows drive-letter paths (GH #321)", () => {
const tempDirs: string[] = [];
afterEach(() => {
while (tempDirs.length > 0) {
const d = tempDirs.pop();
if (d) {
try {
rmSync(d, { recursive: true, force: true });
} catch {
/* ignore */
}
}
}
});
function makeWorkDir(): string {
const d = mkdtempSync(join(tmpdir(), "hf-orch-"));
tempDirs.push(d);
return d;
}
it("copies an external asset with a Windows-style drive-letter key into compileDir", () => {
const workDir = makeWorkDir();
const sourceDir = mkdtempSync(join(tmpdir(), "hf-src-"));
tempDirs.push(sourceDir);
const srcFile = join(sourceDir, "segment.wav");
writeFileSync(srcFile, "fake wav bytes");
const windowsStyleInput = "D:\\coder\\assets\\segment.wav";
const key = toExternalAssetKey(windowsStyleInput);
expect(key).toBe("hf-ext/D/coder/assets/segment.wav");
const externalAssets = new Map<string, string>([[key, srcFile]]);
const compiled = {
html: "<!doctype html><html><body></body></html>",
subCompositions: new Map<string, string>(),
videos: [],
audios: [],
unresolvedCompositions: [],
externalAssets,
width: 1920,
height: 1080,
staticDuration: 10,
renderModeHints: {
recommendScreenshot: false,
reasons: [],
},
hasShaderTransitions: false,
};
writeCompiledArtifacts(compiled, workDir, false);
const landed = join(workDir, "compiled", key);
expect(existsSync(landed)).toBe(true);
expect(readFileSync(landed, "utf-8")).toBe("fake wav bytes");
});
it("rejects a maliciously crafted key that tries to escape compileDir", () => {
const sandboxRoot = mkdtempSync(join(tmpdir(), "hf-orch-root-"));
tempDirs.push(sandboxRoot);
const workDir = join(sandboxRoot, "work", "inner");
mkdirSync(workDir, { recursive: true });
const sourceDir = mkdtempSync(join(tmpdir(), "hf-src-"));
tempDirs.push(sourceDir);
const srcFile = join(sourceDir, "evil.wav");
writeFileSync(srcFile, "should never be copied");
const externalAssets = new Map<string, string>([["hf-ext/../../etc/passwd", srcFile]]);
const compiled = {
html: "<!doctype html>",
subCompositions: new Map<string, string>(),
videos: [],
audios: [],
unresolvedCompositions: [],
externalAssets,
width: 1920,
height: 1080,
staticDuration: 10,
renderModeHints: {
recommendScreenshot: false,
reasons: [],
},
hasShaderTransitions: false,
};
writeCompiledArtifacts(compiled, workDir, false);
const escapeTarget = join(workDir, "etc", "passwd");
expect(existsSync(escapeTarget)).toBe(false);
});
});
function createCompiledComposition(
reasonCodes: Array<"iframe" | "requestAnimationFrame">,
): CompiledComposition {
return {
html: "<html></html>",
subCompositions: new Map(),
videos: [],
audios: [],
unresolvedCompositions: [],
externalAssets: new Map(),
width: 1920,
height: 1080,
staticDuration: 5,
renderModeHints: {
recommendScreenshot: reasonCodes.length > 0,
reasons: reasonCodes.map((code) => ({
code,
message: `reason: ${code}`,
})),
},
hasShaderTransitions: false,
};
}
// fallow-ignore-next-line code-duplication
function createConfig(): EngineConfig {
return {
fps: 30,
quality: "standard",
format: "jpeg",
jpegQuality: 80,
concurrency: "auto",
coresPerWorker: 2.5,
minParallelFrames: 120,
largeRenderThreshold: 1000,
disableGpu: false,
browserGpuMode: "software",
enableBrowserPool: false,
browserTimeout: 120000,
protocolTimeout: 300000,
forceScreenshot: false,
lowMemoryMode: false,
enableChunkedEncode: false,
chunkSizeFrames: 360,
enableStreamingEncode: false,
streamingEncodeMaxDurationSeconds: 240,
ffmpegEncodeTimeout: 600000,
ffmpegProcessTimeout: 300000,
ffmpegStreamingTimeout: 600000,
hdr: false,
hdrAutoDetect: true,
audioGain: 1,
frameDataUriCacheLimit: 256,
frameDataUriCacheBytesLimitMb: 1500,
playerReadyTimeout: 45000,
renderReadyTimeout: 15000,
verifyRuntime: true,
debug: false,
};
}
describe("applyRenderModeHints", () => {
// fallow-ignore-next-line code-duplication
it("forces screenshot mode when compatibility hints recommend it", () => {
const compiled = createCompiledComposition(["iframe", "requestAnimationFrame"]);
const log = {
error: vi.fn(),
warn: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
};
const result = applyRenderModeHints(false, compiled, log);
expect(result).toEqual({ forceScreenshot: true, autoSelected: true });
expect(log.warn).toHaveBeenCalledOnce();
});
it("does nothing when screenshot mode is already forced", () => {
const compiled = createCompiledComposition(["iframe"]);
const log = {
error: vi.fn(),
warn: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
};
const result = applyRenderModeHints(true, compiled, log);
expect(result).toEqual({ forceScreenshot: true, autoSelected: false });
expect(log.warn).not.toHaveBeenCalled();
});
// fallow-ignore-next-line code-duplication
it("returns false when neither caller nor hint forces", () => {
const compiled = createCompiledComposition([]);
const log = {
error: vi.fn(),
warn: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
};
const result = applyRenderModeHints(false, compiled, log);
expect(result).toEqual({ forceScreenshot: false, autoSelected: false });
expect(log.warn).not.toHaveBeenCalled();
});
});
describe("collectVideoReadinessSkipIds", () => {
it("skips native metadata waits for every injected video with dimensions", () => {
expect(
collectVideoReadinessSkipIds(new Set(["hdr-video"]), [
{ videoId: "video1", metadata: { width: 1920, height: 1080 } },
{ videoId: "video2", metadata: { width: 1920, height: 1080 } },
{ videoId: "video3", metadata: { width: 1920, height: 1080 } },
{ videoId: "hdr-video", metadata: { width: 1920, height: 1080 } },
{ videoId: "bad-metadata", metadata: { width: 0, height: 0 } },
]),
).toEqual(["hdr-video", "video1", "video2", "video3"]);
});
});
describe("collectVideoMetadataHints", () => {
it("passes extracted video dimensions to capture sessions", () => {
expect(
collectVideoMetadataHints([
{ videoId: "video2", metadata: { width: 1080, height: 1920, durationSeconds: 4 } },
{ videoId: "video1", metadata: { width: 1920, height: 1080, durationSeconds: 12 } },
{ videoId: "bad-metadata", metadata: { width: 0, height: 1080, durationSeconds: 1 } },
]),
).toEqual([
{ id: "video1", width: 1920, height: 1080 },
{ id: "video2", width: 1080, height: 1920 },
]);
});
});
describe("resolveRenderWorkerCount", () => {
const cfg = { ...createConfig(), coresPerWorker: 100 };
it("reduces auto workers for expensive capture workloads", () => {
const log = {
error: vi.fn(),
warn: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
};
const workers = resolveRenderWorkerCount(
180,
undefined,
cfg,
{
hasShaderTransitions: true,
renderModeHints: { recommendScreenshot: false, reasons: [] },
},
log,
);
expect(workers).toBe(1);
expect(log.warn).toHaveBeenCalledOnce();
});
it("respects explicit worker requests", () => {
const log = {
error: vi.fn(),
warn: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
};
const workers = resolveRenderWorkerCount(
180,
6,
cfg,
{
hasShaderTransitions: true,
renderModeHints: { recommendScreenshot: false, reasons: [] },
},
log,
);
expect(workers).toBe(6);
expect(log.warn).not.toHaveBeenCalled();
});
it("uses measured capture cost when static hints miss an expensive composition", () => {
const workers = resolveRenderWorkerCount(
180,
undefined,
cfg,
{
hasShaderTransitions: false,
renderModeHints: { recommendScreenshot: false, reasons: [] },
},
undefined,
{ multiplier: 4, reasons: ["calibration-p95=2400ms"] },
);
expect(workers).toBe(1);
});
// fallow-ignore-next-line code-duplication
it("forces single worker when html-in-canvas is detected", () => {
const log = {
error: vi.fn(),
warn: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
};
const workers = resolveRenderWorkerCount(
900,
undefined,
cfg,
{
hasShaderTransitions: false,
renderModeHints: {
recommendScreenshot: false,
reasons: [{ code: "htmlInCanvas", message: "layoutsubtree canvas" }],
},
},
log,
);
expect(workers).toBe(1);
expect(log.warn).toHaveBeenCalledOnce();
});
// fallow-ignore-next-line code-duplication
it("overrides explicit --workers when html-in-canvas is detected", () => {
const log = {
error: vi.fn(),
warn: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
};
const workers = resolveRenderWorkerCount(
900,
8,
cfg,
{
hasShaderTransitions: false,
renderModeHints: {
recommendScreenshot: false,
reasons: [{ code: "htmlInCanvas", message: "layoutsubtree canvas" }],
},
},
log,
);
expect(workers).toBe(1);
expect(log.warn).toHaveBeenCalledOnce();
});
// fallow-ignore-next-line code-duplication
it("pins to 1 worker in low-memory mode when no explicit --workers is set", () => {
const log = {
error: vi.fn(),
warn: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
};
const workers = resolveRenderWorkerCount(
900,
undefined,
{ ...cfg, lowMemoryMode: true },
{
hasShaderTransitions: false,
renderModeHints: { recommendScreenshot: false, reasons: [] },
},
log,
);
expect(workers).toBe(1);
expect(log.info).toHaveBeenCalledOnce();
});
// fallow-ignore-next-line code-duplication
it("respects explicit --workers in low-memory mode (only the pin is bypassed)", () => {
const log = {
error: vi.fn(),
warn: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
};
const workers = resolveRenderWorkerCount(
900,
4,
{ ...cfg, lowMemoryMode: true, coresPerWorker: 2.5 },
{
hasShaderTransitions: false,
renderModeHints: { recommendScreenshot: false, reasons: [] },
},
log,
);
expect(workers).toBe(4);
});
it("keeps baseline auto workers after screenshot fallback when measured capture is cheap", () => {
const log = {
error: vi.fn(),
warn: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
};
const stableCfg = {
...cfg,
concurrency: 2 as const,
largeRenderThreshold: 1_000,
};
const workers = resolveRenderWorkerCount(
180,
undefined,
{ ...stableCfg, forceScreenshot: true },
{
hasShaderTransitions: false,
renderModeHints: { recommendScreenshot: false, reasons: [] },
},
log,
{ multiplier: 1, reasons: [], p95Ms: 180 },
);
expect(workers).toBe(2);
expect(log.warn).not.toHaveBeenCalled();
});
});
describe("resolveCaptureForceScreenshotForPageSideCompositing", () => {
it("forces screenshot capture when page-side shader compositing is active", () => {
expect(
resolveCaptureForceScreenshotForPageSideCompositing({
forceScreenshot: false,
usePageSideCompositing: true,
}),
).toBe(true);
});
it("preserves the existing capture mode when page-side compositing is inactive", () => {
expect(
resolveCaptureForceScreenshotForPageSideCompositing({
forceScreenshot: false,
usePageSideCompositing: false,
}),
).toBe(false);
expect(
resolveCaptureForceScreenshotForPageSideCompositing({
forceScreenshot: true,
usePageSideCompositing: false,
}),
).toBe(true);
});
});
describe("shouldDiscardProbeSessionForPageSideCompositing", () => {
it("discards a previously-loaded probe page when page-side compositing is selected", () => {
expect(
shouldDiscardProbeSessionForPageSideCompositing({
hasProbeSession: true,
usePageSideCompositing: true,
}),
).toBe(true);
});
it("reuses the probe session when no page-side pre-head script is required", () => {
expect(
shouldDiscardProbeSessionForPageSideCompositing({
hasProbeSession: true,
usePageSideCompositing: false,
}),
).toBe(false);
expect(
shouldDiscardProbeSessionForPageSideCompositing({
hasProbeSession: false,
usePageSideCompositing: true,
}),
).toBe(false);
});
});
describe("estimateCaptureCostMultiplier", () => {
it("weights shader transitions and render mode hints without charging static media cost", () => {
const cost = estimateCaptureCostMultiplier({
hasShaderTransitions: true,
renderModeHints: {
recommendScreenshot: true,
reasons: [{ code: "requestAnimationFrame", message: "raw rAF" }],
},
});
expect(cost.multiplier).toBe(4);
expect(cost.reasons).toEqual(["shader-transitions", "requestAnimationFrame"]);
});
});
describe("shouldUseLayeredComposite", () => {
it("uses the layered compositor for SDR shader transition renders", () => {
expect(
shouldUseLayeredComposite({
hasHdrContent: false,
hasShaderTransitions: true,
isPngSequence: false,
}),
).toBe(true);
});
it("does not route PNG sequence shader renders through the streaming layered compositor", () => {
expect(
shouldUseLayeredComposite({
hasHdrContent: false,
hasShaderTransitions: true,
isPngSequence: true,
}),
).toBe(false);
});
it("keeps HDR content on the layered compositor even without shader transitions", () => {
expect(
shouldUseLayeredComposite({
hasHdrContent: true,
hasShaderTransitions: false,
isPngSequence: false,
}),
).toBe(true);
});
});
describe("resolveCompositeTransfer", () => {
it("uses 16-bit-expanded sRGB for SDR layered shader transition renders", () => {
expect(resolveCompositeTransfer(false, undefined)).toBe("srgb");
});
it("uses the active HDR transfer when HDR content is being preserved", () => {
expect(resolveCompositeTransfer(true, { transfer: "hlg" })).toBe("hlg");
});
});
describe("estimateMeasuredCaptureCostMultiplier", () => {
it("turns slow calibration samples into a capture cost multiplier", () => {
const estimate = estimateMeasuredCaptureCostMultiplier([
{ frameIndex: 0, captureTimeMs: 180 },
{ frameIndex: 45, captureTimeMs: 700 },
{ frameIndex: 90, captureTimeMs: 2400 },
{ frameIndex: 135, captureTimeMs: 900 },
]);
expect(estimate.multiplier).toBe(4);
expect(estimate.reasons).toEqual(["calibration-p95=2400ms"]);
});
it("keeps fast calibration samples at baseline cost", () => {
const estimate = estimateMeasuredCaptureCostMultiplier([
{ frameIndex: 0, captureTimeMs: 120 },
{ frameIndex: 60, captureTimeMs: 180 },
{ frameIndex: 119, captureTimeMs: 220 },
]);
expect(estimate.multiplier).toBe(1);
expect(estimate.reasons).toEqual([]);
});
});
describe("selectCaptureCalibrationFrames", () => {
it("samples the start, middle, end, and quartiles without duplicates", () => {
expect(selectCaptureCalibrationFrames(180)).toEqual([0, 45, 90, 135, 179]);
expect(selectCaptureCalibrationFrames(3)).toEqual([0, 1, 2]);
});
});
describe("capture calibration safeguards", () => {
it("caps protocol timeout at calibration ceiling for fast fallback", () => {
const cfg = createConfig();
const calibrationCfg = createCaptureCalibrationConfig(cfg);
// Default 300s is above the 30s calibration ceiling — cap at 30s
// so a wedged BeginFrame times out fast and falls back to screenshot
expect(calibrationCfg.protocolTimeout).toBe(30000);
expect(cfg.protocolTimeout).toBe(300000);
});
it("preserves user timeout when already below calibration ceiling", () => {
const cfg = createConfig();
cfg.protocolTimeout = 5000;
// 5s is below the 30s ceiling — keep the user's value
expect(createCaptureCalibrationConfig(cfg).protocolTimeout).toBe(5000);
});
it("falls back to screenshot mode after beginFrame calibration failures", () => {
expect(
shouldFallbackToScreenshotAfterCalibrationError(
new Error("HeadlessExperimental.beginFrame timed out"),
),
).toBe(true);
expect(shouldFallbackToScreenshotAfterCalibrationError(new Error("ffmpeg exited"))).toBe(false);
});
it("falls back to screenshot mode after Runtime.callFunctionOn timeout during calibration", () => {
expect(
shouldFallbackToScreenshotAfterCalibrationError(
new Error(
"Runtime.callFunctionOn timed out. Increase the 'protocolTimeout' setting in launch/connect calls for a higher timeout if needed.",
),
),
).toBe(true);
expect(
shouldFallbackToScreenshotAfterCalibrationError(
new Error(
"Runtime.evaluate timed out. Increase the 'protocolTimeout' setting in launch/connect calls for a higher timeout if needed.",
),
),
).toBe(true);
});
});
describe("adaptive missing-frame retry helpers", () => {
const tempDirs: string[] = [];
afterEach(() => {
while (tempDirs.length > 0) {
const d = tempDirs.pop();
if (d) rmSync(d, { recursive: true, force: true });
}
});
function makeFramesDir(): string {
const d = mkdtempSync(join(tmpdir(), "hf-missing-frames-"));
tempDirs.push(d);
return d;
}
it("finds contiguous missing frame ranges from captured disk frames", () => {
const framesDir = makeFramesDir();
for (const frameIndex of [0, 1, 4]) {
writeFileSync(
join(framesDir, `frame_${String(frameIndex).padStart(6, "0")}.jpg`),
"captured-frame",
);
}
expect(findMissingFrameRanges(6, framesDir, "jpg")).toEqual([
{ startFrame: 2, endFrame: 4 },
{ startFrame: 5, endFrame: 6 },
]);
});
it("retries a worker placeholder instead of accepting a truncated sequence", () => {
const framesDir = makeFramesDir();
for (let frameIndex = 0; frameIndex < 4; frameIndex++) {
writeFileSync(
join(framesDir, `frame_${String(frameIndex).padStart(6, "0")}.jpg`),
frameIndex === 2 ? "x" : "captured-frame",
);
}
expect(findMissingFrameRanges(4, framesDir, "jpg")).toEqual([{ startFrame: 2, endFrame: 3 }]);
});
it("builds retry batches that cap active workers per attempt", () => {
const batches = buildMissingFrameRetryBatches(
[
{ startFrame: 2, endFrame: 4 },
{ startFrame: 5, endFrame: 6 },
{ startFrame: 9, endFrame: 12 },
],
2,
"/tmp/work",
1,
);
expect(batches).toHaveLength(2);
expect(batches[0]).toMatchObject([
{ workerId: 0, startFrame: 2, endFrame: 4 },
{ workerId: 1, startFrame: 5, endFrame: 6 },
]);
expect(batches[1]).toMatchObject([{ workerId: 0, startFrame: 9, endFrame: 12 }]);
expect(batches[0][0].outputDir).toContain("retry-1-batch-0-worker-0");
});
it("halves retry workers until sequential fallback", () => {
expect(getNextRetryWorkerCount(8)).toBe(4);
expect(getNextRetryWorkerCount(3)).toBe(1);
expect(getNextRetryWorkerCount(2)).toBe(1);
expect(getNextRetryWorkerCount(1)).toBe(1);
});
it("only retries parallel capture timeout failures", () => {
expect(
isRecoverableParallelCaptureError(
new Error("[Parallel] Capture failed: Worker 0: Runtime.callFunctionOn timed out"),
),
).toBe(true);
expect(
isRecoverableParallelCaptureError(
new Error("[Parallel] Capture failed: Worker 1: HeadlessExperimental.beginFrame timed out"),
),
).toBe(true);
expect(
isRecoverableParallelCaptureError(
new Error(
"[Parallel] Capture failed: Worker 0: drawElement worker encode timed out (frame 42)",
),
),
).toBe(true);
expect(isRecoverableParallelCaptureError(new Error("Encoding failed: ffmpeg exited"))).toBe(
false,
);
});
});
describe("projectBrowserEndToCompositionTimeline", () => {
it("keeps end unchanged when browser and compiled starts share the same origin", () => {
expect(projectBrowserEndToCompositionTimeline(2, 2, 6)).toBe(6);
});
it("reprojects a scene-local browser end into the compiled host timeline", () => {
expect(projectBrowserEndToCompositionTimeline(4.417, 0, 85.52)).toBeCloseTo(89.937, 6);
});
it("preserves scene-local media offsets inside compositions that start much later", () => {
expect(projectBrowserEndToCompositionTimeline(21.5, 1.5, 5.5)).toBe(25.5);
});
});
describe("resolveDeviceScaleFactor", () => {
const defaults = {
compositionWidth: 1920,
compositionHeight: 1080,
hdrRequested: false,
alphaRequested: false,
} as const;
it("returns 1 when no outputResolution is set (default behavior)", () => {
expect(resolveDeviceScaleFactor({ ...defaults, outputResolution: undefined })).toBe(1);
});
it("returns 2 for the canonical 1080p → 4K supersample", () => {
expect(resolveDeviceScaleFactor({ ...defaults, outputResolution: "landscape-4k" })).toBe(2);
});
it("returns 2 for portrait 1080p → portrait-4k", () => {
expect(
resolveDeviceScaleFactor({
...defaults,
compositionWidth: 1080,
compositionHeight: 1920,
outputResolution: "portrait-4k",
}),
).toBe(2);
});
it("returns 1 when the composition already matches the requested resolution", () => {
expect(
resolveDeviceScaleFactor({
...defaults,
compositionWidth: 3840,
compositionHeight: 2160,
outputResolution: "landscape-4k",
}),
).toBe(1);
});
it("rejects HDR + outputResolution with a clear message", () => {
expect(() =>
resolveDeviceScaleFactor({
...defaults,
outputResolution: "landscape-4k",
hdrRequested: true,
}),
).toThrow(/hdrMode='force-hdr'/);
});
it("rejects alpha + outputResolution (the alpha capture path doesn't apply DPR yet)", () => {
expect(() =>
resolveDeviceScaleFactor({
...defaults,
outputResolution: "landscape-4k",
alphaRequested: true,
}),
).toThrow(/alpha output/);
});
it("rejects orientation mismatch (landscape comp → portrait-4k)", () => {
expect(() =>
resolveDeviceScaleFactor({ ...defaults, outputResolution: "portrait-4k" }),
).toThrow(/aspect ratio/);
});
it("suggests the matching-orientation preset in the aspect-mismatch message", () => {
// Landscape composition + portrait preset → the message should point at
// the landscape swap so the user isn't left to guess (workstream P1-3).
expect(() => resolveDeviceScaleFactor({ ...defaults, outputResolution: "portrait" })).toThrow(
/--resolution landscape/,
);
});
it("rejects downsampling (4K composition → 1080p output)", () => {
expect(() =>
resolveDeviceScaleFactor({
...defaults,
compositionWidth: 3840,
compositionHeight: 2160,
outputResolution: "landscape",
}),
).toThrow(/Downsampling/);
});
it("rejects non-integer scale factors", () => {
// 1500×844 → 3840×2160 has slightly different ratios in width vs height.
// The aspect-ratio guard fires first; pinning the rejection message
// covers both error paths since either is an acceptable failure here.
expect(() =>
resolveDeviceScaleFactor({
...defaults,
compositionWidth: 1500,
compositionHeight: 844,
outputResolution: "landscape-4k",
}),
).toThrow(/aspect ratio|non-integer/);
});
it("returns 1 for a square comp matching the square preset", () => {
expect(
resolveDeviceScaleFactor({
...defaults,
compositionWidth: 1080,
compositionHeight: 1080,
outputResolution: "square",
}),
).toBe(1);
});
it("returns 2 for square 1080 → square-4k", () => {
expect(
resolveDeviceScaleFactor({
...defaults,
compositionWidth: 1080,
compositionHeight: 1080,
outputResolution: "square-4k",
}),
).toBe(2);
});
it("rejects landscape preset on a square composition", () => {
expect(() =>
resolveDeviceScaleFactor({
...defaults,
compositionWidth: 1080,
compositionHeight: 1080,
outputResolution: "landscape",
}),
).toThrow(/aspect ratio/);
});
});
describe("shouldPreferSingleWorkerDrawElement (DE priority inversion)", () => {
const eligible = {
workerCount: 5,
requestedWorkers: "auto" as const,
useDrawElement: true,
deCompileGate: undefined,
forceScreenshot: false,
outputFormat: "mp4" as const,
totalFrames: 2380,
minFrames: 900,
singleWorkerStreamingOk: true,
layeredOrEffectRoute: false,
supersampling: false,
probeDeGated: false,
experimentalParallelDeOptIn: false,
};
it("inverts an auto-resolved multi-worker render for an eligible long comp", () => {
expect(shouldPreferSingleWorkerDrawElement(eligible)).toBe(true);
});
it("honors explicitly requested workers", () => {
expect(shouldPreferSingleWorkerDrawElement({ ...eligible, requestedWorkers: 3 })).toBe(false);
});
it("inverts for requestedWorkers undefined — the value production actually passes for auto", () => {
expect(shouldPreferSingleWorkerDrawElement({ ...eligible, requestedWorkers: undefined })).toBe(
true,
);
});
it("skips comps routed to layered/HDR/shader paths (drawElement never runs there)", () => {
expect(shouldPreferSingleWorkerDrawElement({ ...eligible, layeredOrEffectRoute: true })).toBe(
false,
);
});
it("skips supersampled renders (engine init-time DE gate)", () => {
expect(shouldPreferSingleWorkerDrawElement({ ...eligible, supersampling: true })).toBe(false);
});
it("skips when the probe session already shows DE gated out", () => {
expect(shouldPreferSingleWorkerDrawElement({ ...eligible, probeDeGated: true })).toBe(false);
});
it("honors the explicit experimental parallel-DE opt-in", () => {
expect(
shouldPreferSingleWorkerDrawElement({ ...eligible, experimentalParallelDeOptIn: true }),
).toBe(false);
});
it("skips below the amortization threshold (measured crossover ~900 frames)", () => {
expect(shouldPreferSingleWorkerDrawElement({ ...eligible, totalFrames: 360 })).toBe(false);
expect(shouldPreferSingleWorkerDrawElement({ ...eligible, totalFrames: 900 })).toBe(true);
});
it("is disabled by minFrames <= 0 (HF_DE_SINGLE_MIN_FRAMES=0 kill switch)", () => {
expect(shouldPreferSingleWorkerDrawElement({ ...eligible, minFrames: 0 })).toBe(false);
expect(shouldPreferSingleWorkerDrawElement({ ...eligible, minFrames: -1 })).toBe(false);
});
it("requires drawElement to be enabled and ungated", () => {
expect(shouldPreferSingleWorkerDrawElement({ ...eligible, useDrawElement: false })).toBe(false);
expect(shouldPreferSingleWorkerDrawElement({ ...eligible, deCompileGate: "3d" })).toBe(false);
expect(shouldPreferSingleWorkerDrawElement({ ...eligible, forceScreenshot: true })).toBe(false);
});
it("only applies to the benchmarked configuration (mp4 + streaming-eligible)", () => {
expect(shouldPreferSingleWorkerDrawElement({ ...eligible, outputFormat: "webm" })).toBe(false);
expect(
shouldPreferSingleWorkerDrawElement({ ...eligible, singleWorkerStreamingOk: false }),
).toBe(false);
});
it("is a no-op when workers already resolved to 1", () => {
expect(shouldPreferSingleWorkerDrawElement({ ...eligible, workerCount: 1 })).toBe(false);
});
});
describe("resolveInversionRetryPlan (self-verify retry rollback)", () => {
const cfg = { enableStreamingEncode: true, streamingEncodeMaxDurationSeconds: 240 };
it("returns null when the render was never inverted", () => {
expect(
resolveInversionRetryPlan({
deWorkerInversion: undefined,
preInversionWorkerCount: 5,
cfg,
outputFormat: "mp4",
durationSeconds: 80,
isMemoryExhaustion: false,
}),
).toBe(null);
expect(
resolveInversionRetryPlan({
deWorkerInversion: "reverted",
preInversionWorkerCount: 5,
cfg,
outputFormat: "mp4",
durationSeconds: 80,
isMemoryExhaustion: false,
}),
).toBe(null);
});
it("restores the pre-inversion worker count and routes multi-worker retries to disk", () => {
const plan = resolveInversionRetryPlan({
deWorkerInversion: "inverted",
preInversionWorkerCount: 5,
cfg,
outputFormat: "mp4",
durationSeconds: 80,
isMemoryExhaustion: false,
});
expect(plan).toEqual({
workerCount: 5,
// shouldUseStreamingEncode is workerCount===1-only — parallel retry
// goes through the disk path.
useStreamingEncode: false,
deWorkerInversion: "reverted",
});
});
it("keeps streaming when the pre-inversion resolution was already single-worker", () => {
const plan = resolveInversionRetryPlan({
deWorkerInversion: "inverted",
preInversionWorkerCount: 1,
cfg,
outputFormat: "mp4",
durationSeconds: 80,
isMemoryExhaustion: false,
});
expect(plan).toEqual({
workerCount: 1,
useStreamingEncode: true,
deWorkerInversion: "reverted",
});
});
it("drops to a single worker on OOM regardless of the pre-inversion count (the actual memory remedy)", () => {
const plan = resolveInversionRetryPlan({
deWorkerInversion: "inverted",
preInversionWorkerCount: 5,
cfg,
outputFormat: "mp4",
durationSeconds: 80,
isMemoryExhaustion: true,
});
expect(plan).toEqual({
workerCount: 1,
useStreamingEncode: true,
deWorkerInversion: "reverted",
});
});
});
describe("shouldPreferParallelDrawElement (DE parallel router)", () => {
const eligible = {
workerCount: 5,
requestedWorkers: "auto" as const,
useDrawElement: true,
deCompileGate: undefined,
forceScreenshot: false,
outputFormat: "mp4" as const,
totalFrames: 2381,
minFrames: 2000,
layeredOrEffectRoute: false,
supersampling: false,
probeDeGated: false,
experimentalParallelDeOptIn: false,
routerEnabled: true,
parallelStreamingAvailable: true,
totalMemoryMb: 32768,
minMemoryMb: 24576,
};
it("routes an auto-resolved multi-worker render for an eligible long comp", () => {
expect(shouldPreferParallelDrawElement(eligible)).toBe(true);
});
it("withholds the bet when parallel streaming can't run (e.g. over the duration cap)", () => {
// The router pins workerCount to 3 and skips calibration to serve the
// verified parallel DE STREAMING path. If streaming is off for this
// render — the >240s duration cap is the common case — firing would pay
// the whole cost of the pin for none of the benefit.
expect(
shouldPreferParallelDrawElement({ ...eligible, parallelStreamingAvailable: false }),
).toBe(false);
});
it("withholds the parallel bet below the RAM floor (16 GB black-slab report)", () => {
expect(shouldPreferParallelDrawElement({ ...eligible, totalMemoryMb: 16384 })).toBe(false);
});
it("routes exactly at the RAM floor", () => {
expect(shouldPreferParallelDrawElement({ ...eligible, totalMemoryMb: 24576 })).toBe(true);
});
it("minMemoryMb <= 0 disables the RAM guard", () => {
expect(
shouldPreferParallelDrawElement({ ...eligible, totalMemoryMb: 8192, minMemoryMb: 0 }),
).toBe(true);
});
it("is disabled by default (routerEnabled: false is the shipped default)", () => {
expect(shouldPreferParallelDrawElement({ ...eligible, routerEnabled: false })).toBe(false);
});
it("honors explicitly requested workers", () => {
expect(shouldPreferParallelDrawElement({ ...eligible, requestedWorkers: 3 })).toBe(false);
});
it("routes for requestedWorkers undefined — the value production actually passes for auto", () => {
expect(shouldPreferParallelDrawElement({ ...eligible, requestedWorkers: undefined })).toBe(
true,
);
});
it("skips comps routed to layered/HDR/shader paths (drawElement never runs there)", () => {
expect(shouldPreferParallelDrawElement({ ...eligible, layeredOrEffectRoute: true })).toBe(
false,
);
});
it("skips supersampled renders (engine init-time DE gate)", () => {
expect(shouldPreferParallelDrawElement({ ...eligible, supersampling: true })).toBe(false);
});
it("skips when the probe session already shows DE gated out", () => {
expect(shouldPreferParallelDrawElement({ ...eligible, probeDeGated: true })).toBe(false);
});
it("honors the explicit experimental parallel-DE opt-in (already parallel, router is a no-op)", () => {
expect(
shouldPreferParallelDrawElement({ ...eligible, experimentalParallelDeOptIn: true }),
).toBe(false);
});
it("skips below the amortization threshold (benchmark: real-work comps clear 1.25x at ~2,000+ frames)", () => {
expect(shouldPreferParallelDrawElement({ ...eligible, totalFrames: 915 })).toBe(false);
expect(shouldPreferParallelDrawElement({ ...eligible, totalFrames: 2000 })).toBe(true);
});
it("is disabled by minFrames <= 0 (HF_DE_PARALLEL_MIN_FRAMES=0 kill switch)", () => {
expect(shouldPreferParallelDrawElement({ ...eligible, minFrames: 0 })).toBe(false);
expect(shouldPreferParallelDrawElement({ ...eligible, minFrames: -1 })).toBe(false);
});
it("requires drawElement to be enabled and ungated", () => {
expect(shouldPreferParallelDrawElement({ ...eligible, useDrawElement: false })).toBe(false);
expect(shouldPreferParallelDrawElement({ ...eligible, deCompileGate: "3d" })).toBe(false);
expect(shouldPreferParallelDrawElement({ ...eligible, forceScreenshot: true })).toBe(false);
});
it("only applies to mp4 (the benchmarked configuration)", () => {
expect(shouldPreferParallelDrawElement({ ...eligible, outputFormat: "webm" })).toBe(false);
});
it("is a no-op when workers already resolved to 1", () => {
expect(shouldPreferParallelDrawElement({ ...eligible, workerCount: 1 })).toBe(false);
});
});
describe("resolveParallelRouterRetryPlan (self-verify retry rollback)", () => {
const cfg = { enableStreamingEncode: true, streamingEncodeMaxDurationSeconds: 240 };
it("returns null when the render was never router-routed", () => {
expect(
resolveParallelRouterRetryPlan({
deParallelRouter: undefined,
preRouterWorkerCount: 5,
cfg,
outputFormat: "mp4",
durationSeconds: 80,
isMemoryExhaustion: false,
}),
).toBe(null);
expect(
resolveParallelRouterRetryPlan({
deParallelRouter: "reverted",
preRouterWorkerCount: 5,
cfg,
outputFormat: "mp4",
durationSeconds: 80,
isMemoryExhaustion: false,
}),
).toBe(null);
});
it("restores the pre-router worker count and routes multi-worker retries to disk", () => {
const plan = resolveParallelRouterRetryPlan({
deParallelRouter: "routed",
preRouterWorkerCount: 5,
cfg,
outputFormat: "mp4",
durationSeconds: 80,
isMemoryExhaustion: false,
});
expect(plan).toEqual({
workerCount: 5,
useStreamingEncode: false,
deParallelRouter: "reverted",
});
});
it("drops to a single worker on OOM regardless of the pre-router count (the actual memory remedy)", () => {
const plan = resolveParallelRouterRetryPlan({
deParallelRouter: "routed",
preRouterWorkerCount: 5,
cfg,
outputFormat: "mp4",
durationSeconds: 80,
isMemoryExhaustion: true,
});
expect(plan).toEqual({
workerCount: 1,
useStreamingEncode: true,
deParallelRouter: "reverted",
});
});
});
describe("shouldRetryViaPinnedFallback (widen the self-verify retry to generic capture failures, including OOM)", () => {
it("always retries a drawElement self-verify failure, pinned or not", () => {
expect(
shouldRetryViaPinnedFallback({
isVerifyError: true,
isCancellation: false,
deWorkerInversion: undefined,
deParallelRouter: undefined,
}),
).toBe(true);
});
it("retries a generic capture failure when the router pinned the worker count", () => {
expect(
shouldRetryViaPinnedFallback({
isVerifyError: false,
isCancellation: false,
deWorkerInversion: undefined,
deParallelRouter: "routed",
}),
).toBe(true);
});
it("retries a generic capture failure when the inversion pinned the worker count", () => {
expect(
shouldRetryViaPinnedFallback({
isVerifyError: false,
isCancellation: false,
deWorkerInversion: "inverted",
deParallelRouter: undefined,
}),
).toBe(true);
});
it("does not retry a generic capture failure when nothing pinned the worker count", () => {
expect(
shouldRetryViaPinnedFallback({
isVerifyError: false,
isCancellation: false,
deWorkerInversion: undefined,
deParallelRouter: undefined,
}),
).toBe(false);
});
it("retries OOM too when the router pinned the worker count (fallback's Chrome processes are already dead by the time this runs, and the fallback is pooled/lighter than the pinned path)", () => {
expect(
shouldRetryViaPinnedFallback({
isVerifyError: false,
isCancellation: false,
deWorkerInversion: undefined,
deParallelRouter: "routed",
}),
).toBe(true);
});
it("retries OOM too when the inversion pinned the worker count", () => {
expect(
shouldRetryViaPinnedFallback({
isVerifyError: false,
isCancellation: false,
deWorkerInversion: "inverted",
deParallelRouter: undefined,
}),
).toBe(true);
});
it("does not retry a generic failure on an already-reverted cohort (no pin left to retreat from)", () => {
expect(
shouldRetryViaPinnedFallback({
isVerifyError: false,
isCancellation: false,
deWorkerInversion: "reverted",
deParallelRouter: undefined,
}),
).toBe(false);
});
it("never retries a cancellation, even on a pinned cohort — must propagate immediately, not detour through a fresh encoder spin-up", () => {
expect(
shouldRetryViaPinnedFallback({
isVerifyError: false,
isCancellation: true,
deWorkerInversion: "inverted",
deParallelRouter: undefined,
}),
).toBe(false);
expect(
shouldRetryViaPinnedFallback({
isVerifyError: false,
isCancellation: true,
deWorkerInversion: undefined,
deParallelRouter: "routed",
}),
).toBe(false);
});
it("cancellation wins even if the error also looks like a self-verify failure", () => {
expect(
shouldRetryViaPinnedFallback({
isVerifyError: true,
isCancellation: true,
deWorkerInversion: undefined,
deParallelRouter: undefined,
}),
).toBe(false);
});
});
describe("shouldStreamParallelCapture (non-DE parallel streaming router)", () => {
const eligible = {
routerEnabled: true,
workerCount: 3,
useDrawElement: false,
outputFormat: "mp4" as const,
streamingOk: true,
layeredOrEffectRoute: false,
};
it("routes an eligible multi-worker non-drawElement render", () => {
expect(shouldStreamParallelCapture(eligible)).toBe(true);
});
it("is disabled by default (kill switch off is the shipped default)", () => {
expect(shouldStreamParallelCapture({ ...eligible, routerEnabled: false })).toBe(false);
});
it("never fires for single-worker renders (those already stream)", () => {
expect(shouldStreamParallelCapture({ ...eligible, workerCount: 1 })).toBe(false);
});
it("never fires when drawElement will capture (the DE routers own that path)", () => {
expect(shouldStreamParallelCapture({ ...eligible, useDrawElement: true })).toBe(false);
});
it("only applies to mp4", () => {
expect(shouldStreamParallelCapture({ ...eligible, outputFormat: "webm" })).toBe(false);
expect(shouldStreamParallelCapture({ ...eligible, outputFormat: "png-sequence" })).toBe(false);
});
it("respects the streaming-encode config/duration gates", () => {
expect(shouldStreamParallelCapture({ ...eligible, streamingOk: false })).toBe(false);
});
it("skips HDR-layered and shader-transition routes", () => {
expect(shouldStreamParallelCapture({ ...eligible, layeredOrEffectRoute: true })).toBe(false);
});
});