264 lines
6.3 KiB
TypeScript
264 lines
6.3 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { describe, expect, it, vi } from "vitest";
|
|
|
|
import type { ImagePipelineBackend } from "@/lib/image-pipeline/backend/backend-types";
|
|
import { createBackendRouter } from "@/lib/image-pipeline/backend/backend-router";
|
|
import {
|
|
type BackendFeatureFlags,
|
|
getBackendFeatureFlags,
|
|
} from "@/lib/image-pipeline/backend/feature-flags";
|
|
|
|
function createStep() {
|
|
return {
|
|
nodeId: "n1",
|
|
type: "color-adjust",
|
|
params: {
|
|
hsl: {
|
|
hue: 0,
|
|
saturation: 0,
|
|
luminance: 0,
|
|
},
|
|
temperature: 0,
|
|
tint: 0,
|
|
vibrance: 0,
|
|
},
|
|
} as const;
|
|
}
|
|
|
|
function createBackends() {
|
|
const cpuPreview = vi.fn();
|
|
const cpuFull = vi.fn();
|
|
const webglPreview = vi.fn();
|
|
const webglFull = vi.fn();
|
|
const wasmPreview = vi.fn();
|
|
const wasmFull = vi.fn();
|
|
|
|
const backends: readonly ImagePipelineBackend[] = [
|
|
{
|
|
id: "cpu",
|
|
runPreviewStep: cpuPreview,
|
|
runFullPipeline: cpuFull,
|
|
},
|
|
{
|
|
id: "webgl",
|
|
runPreviewStep: webglPreview,
|
|
runFullPipeline: webglFull,
|
|
},
|
|
{
|
|
id: "wasm",
|
|
runPreviewStep: wasmPreview,
|
|
runFullPipeline: wasmFull,
|
|
},
|
|
];
|
|
|
|
return {
|
|
backends,
|
|
cpuPreview,
|
|
cpuFull,
|
|
webglPreview,
|
|
webglFull,
|
|
wasmPreview,
|
|
wasmFull,
|
|
};
|
|
}
|
|
|
|
function createRouterFlags(overrides: Partial<BackendFeatureFlags>): BackendFeatureFlags {
|
|
return {
|
|
...getBackendFeatureFlags(),
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("backend feature flags", () => {
|
|
it("forceCpu overrides all backend choices", () => {
|
|
const reasons: string[] = [];
|
|
const backend = createBackends();
|
|
const router = createBackendRouter({
|
|
backends: backend.backends,
|
|
backendAvailability: {
|
|
webgl: {
|
|
supported: true,
|
|
enabled: true,
|
|
},
|
|
wasm: {
|
|
supported: true,
|
|
enabled: true,
|
|
},
|
|
},
|
|
featureFlags: createRouterFlags({
|
|
forceCpu: true,
|
|
webglEnabled: true,
|
|
wasmEnabled: true,
|
|
}),
|
|
onFallback: (event) => {
|
|
reasons.push(event.reason);
|
|
},
|
|
});
|
|
|
|
router.runPreviewStep({
|
|
pixels: new Uint8ClampedArray(4),
|
|
step: createStep(),
|
|
width: 1,
|
|
height: 1,
|
|
backendHint: "webgl",
|
|
});
|
|
|
|
router.runFullPipeline({
|
|
pixels: new Uint8ClampedArray(4),
|
|
steps: [createStep()],
|
|
width: 1,
|
|
height: 1,
|
|
backendHint: "wasm",
|
|
});
|
|
|
|
expect(backend.cpuPreview).toHaveBeenCalledTimes(1);
|
|
expect(backend.cpuFull).toHaveBeenCalledTimes(1);
|
|
expect(backend.webglPreview).not.toHaveBeenCalled();
|
|
expect(backend.webglFull).not.toHaveBeenCalled();
|
|
expect(backend.wasmPreview).not.toHaveBeenCalled();
|
|
expect(backend.wasmFull).not.toHaveBeenCalled();
|
|
expect(reasons).toEqual(["flag_disabled", "flag_disabled"]);
|
|
});
|
|
|
|
it("webgl and wasm can be independently enabled or disabled", () => {
|
|
const reasonA: string[] = [];
|
|
const backendA = createBackends();
|
|
const routerA = createBackendRouter({
|
|
backends: backendA.backends,
|
|
backendAvailability: {
|
|
webgl: {
|
|
supported: true,
|
|
enabled: true,
|
|
},
|
|
wasm: {
|
|
supported: true,
|
|
enabled: true,
|
|
},
|
|
},
|
|
featureFlags: createRouterFlags({
|
|
forceCpu: false,
|
|
webglEnabled: true,
|
|
wasmEnabled: false,
|
|
}),
|
|
onFallback: (event) => {
|
|
reasonA.push(event.reason);
|
|
},
|
|
});
|
|
|
|
routerA.runPreviewStep({
|
|
pixels: new Uint8ClampedArray(4),
|
|
step: createStep(),
|
|
width: 1,
|
|
height: 1,
|
|
backendHint: "webgl",
|
|
});
|
|
routerA.runPreviewStep({
|
|
pixels: new Uint8ClampedArray(4),
|
|
step: createStep(),
|
|
width: 1,
|
|
height: 1,
|
|
backendHint: "wasm",
|
|
});
|
|
|
|
expect(backendA.webglPreview).toHaveBeenCalledTimes(1);
|
|
expect(backendA.wasmPreview).not.toHaveBeenCalled();
|
|
expect(backendA.cpuPreview).toHaveBeenCalledTimes(1);
|
|
expect(reasonA).toEqual(["flag_disabled"]);
|
|
|
|
const reasonB: string[] = [];
|
|
const backendB = createBackends();
|
|
const routerB = createBackendRouter({
|
|
backends: backendB.backends,
|
|
backendAvailability: {
|
|
webgl: {
|
|
supported: true,
|
|
enabled: true,
|
|
},
|
|
wasm: {
|
|
supported: true,
|
|
enabled: true,
|
|
},
|
|
},
|
|
featureFlags: createRouterFlags({
|
|
forceCpu: false,
|
|
webglEnabled: false,
|
|
wasmEnabled: true,
|
|
}),
|
|
onFallback: (event) => {
|
|
reasonB.push(event.reason);
|
|
},
|
|
});
|
|
|
|
routerB.runPreviewStep({
|
|
pixels: new Uint8ClampedArray(4),
|
|
step: createStep(),
|
|
width: 1,
|
|
height: 1,
|
|
backendHint: "webgl",
|
|
});
|
|
routerB.runPreviewStep({
|
|
pixels: new Uint8ClampedArray(4),
|
|
step: createStep(),
|
|
width: 1,
|
|
height: 1,
|
|
backendHint: "wasm",
|
|
});
|
|
|
|
expect(backendB.webglPreview).not.toHaveBeenCalled();
|
|
expect(backendB.wasmPreview).toHaveBeenCalledTimes(1);
|
|
expect(backendB.cpuPreview).toHaveBeenCalledTimes(1);
|
|
expect(reasonB).toEqual(["flag_disabled"]);
|
|
});
|
|
|
|
it("defaults preserve cpu behavior when no explicit flags are set", () => {
|
|
const reasons: string[] = [];
|
|
const backend = createBackends();
|
|
const router = createBackendRouter({
|
|
backends: backend.backends,
|
|
backendAvailability: {
|
|
webgl: {
|
|
supported: true,
|
|
enabled: true,
|
|
},
|
|
wasm: {
|
|
supported: true,
|
|
enabled: true,
|
|
},
|
|
},
|
|
featureFlags: getBackendFeatureFlags(),
|
|
onFallback: (event) => {
|
|
reasons.push(event.reason);
|
|
},
|
|
});
|
|
|
|
router.runPreviewStep({
|
|
pixels: new Uint8ClampedArray(4),
|
|
step: createStep(),
|
|
width: 1,
|
|
height: 1,
|
|
backendHint: "webgl",
|
|
});
|
|
|
|
router.runPreviewStep({
|
|
pixels: new Uint8ClampedArray(4),
|
|
step: createStep(),
|
|
width: 1,
|
|
height: 1,
|
|
backendHint: "wasm",
|
|
});
|
|
|
|
router.runPreviewStep({
|
|
pixels: new Uint8ClampedArray(4),
|
|
step: createStep(),
|
|
width: 1,
|
|
height: 1,
|
|
});
|
|
|
|
expect(backend.cpuPreview).toHaveBeenCalledTimes(3);
|
|
expect(backend.webglPreview).not.toHaveBeenCalled();
|
|
expect(backend.wasmPreview).not.toHaveBeenCalled();
|
|
expect(reasons).toEqual(["flag_disabled", "flag_disabled"]);
|
|
});
|
|
});
|