Files
hyperframes/packages/engine/src/services/drawElementService.test.ts
T
Vance IngallsandClaude Opus 5 4520cd240b fix(engine): bucket gpu_renderer + cover the failure cohort (review)
Three review findings on the win32 drawElement PR:

1. gpu_renderer shipped the raw UNMASKED_RENDERER_WEBGL string — unbounded,
   driver-authored, GPU-model-specific, and |-joined across parallel
   sessions, i.e. high cardinality by construction, against this file's own
   convention of sanitizing engine-sourced strings (deGateReason is a
   bucket; error messages go through redactTelemetryString). Now bucketed at
   the source by classifyGpuRenderer to <backend>/<vendor>
   (metal/apple, d3d11/nvidia, swiftshader/other, ...), which is the whole
   analytic signal the win32 rollout needs and nothing else. The raw string
   never leaves the engine.

2. gpu_renderer reached render_complete only, so a crashed render — the
   cohort the field exists to attribute — carried no backend. It now rides
   RenderCaptureObservability (deGpuRenderer, sourced from the live probe
   session like the de_* counters), so both render_complete and
   render_error carry it and a hard failure still reports its GPU backend.
   On render_complete the perfSummary value still wins by spread order.

3. Restore the fallow-ignore-next-line suppression above
   __resetDeParallelRouterTrialStateForTests: CLI test files are not fallow
   entry points, so removing it fails the CI dead-code audit (local
   pre-commit passed only because of its changed-file scope).

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

111 lines
4.5 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import type { Page } from "puppeteer-core";
import {
classifyGpuRenderer,
detectGpuBackend,
detectSwiftShader,
resolveDrawElementCaptureMode,
} from "./drawElementService.js";
// ── detectGpuBackend / detectSwiftShader ───────────────────────────────────────
describe("detectGpuBackend", () => {
function makePage(evaluateResult: unknown): Page {
return {
evaluate: vi.fn().mockResolvedValue(evaluateResult),
} as unknown as Page;
}
it("carries the raw renderer string alongside the SwiftShader verdict", async () => {
const page = makePage({
isSwiftShader: false,
renderer: "ANGLE (NVIDIA, D3D11 vs_5_0 ps_5_0, D3D11)",
});
expect(await detectGpuBackend(page)).toEqual({
isSwiftShader: false,
renderer: "ANGLE (NVIDIA, D3D11 vs_5_0 ps_5_0, D3D11)",
});
});
it("reports null renderer when WebGL is unavailable", async () => {
const page = makePage({ isSwiftShader: false, renderer: null });
expect(await detectGpuBackend(page)).toEqual({ isSwiftShader: false, renderer: null });
});
it("detectSwiftShader wrapper returns true when renderer is SwiftShader", async () => {
const page = makePage({ isSwiftShader: true, renderer: "Google SwiftShader" });
expect(await detectSwiftShader(page)).toBe(true);
});
it("detectSwiftShader wrapper returns false for a hardware renderer", async () => {
const page = makePage({ isSwiftShader: false, renderer: "ANGLE (Apple, ANGLE Metal)" });
expect(await detectSwiftShader(page)).toBe(false);
});
it("passes a function to page.evaluate", async () => {
const page = makePage({ isSwiftShader: false, renderer: null });
await detectGpuBackend(page);
expect(page.evaluate).toHaveBeenCalledWith(expect.any(Function));
});
});
// ── classifyGpuRenderer ────────────────────────────────────────────────────────
describe("classifyGpuRenderer", () => {
it("buckets real ANGLE renderer strings to <backend>/<vendor>", () => {
expect(
classifyGpuRenderer("ANGLE (Apple, ANGLE Metal Renderer: Apple M4 Pro, Unspecified Version)"),
).toBe("metal/apple");
expect(
classifyGpuRenderer(
"ANGLE (NVIDIA, NVIDIA GeForce RTX 3080 Direct3D11 vs_5_0 ps_5_0, D3D11)",
),
).toBe("d3d11/nvidia");
expect(
classifyGpuRenderer("ANGLE (Intel, Intel(R) UHD Graphics 630 Direct3D11 vs_5_0 ps_5_0)"),
).toBe("d3d11/intel");
expect(classifyGpuRenderer("ANGLE (AMD, AMD Radeon RX 6800 Direct3D11 vs_5_0 ps_5_0)")).toBe(
"d3d11/amd",
);
expect(classifyGpuRenderer("Google SwiftShader")).toBe("swiftshader/other");
});
it("drops the GPU model — the bucket must stay low cardinality", () => {
// Two different NVIDIA cards must collapse to ONE bucket, otherwise the
// property is unbounded and useless for aggregation.
expect(classifyGpuRenderer("ANGLE (NVIDIA, NVIDIA GeForce RTX 4090 Direct3D11)")).toBe(
classifyGpuRenderer("ANGLE (NVIDIA, NVIDIA GeForce GTX 1060 Direct3D11)"),
);
});
it("returns undefined for missing input rather than a bogus bucket", () => {
expect(classifyGpuRenderer(null)).toBeUndefined();
expect(classifyGpuRenderer(undefined)).toBeUndefined();
expect(classifyGpuRenderer("")).toBeUndefined();
});
});
// ── resolveDrawElementCaptureMode ──────────────────────────────────────────────
describe("resolveDrawElementCaptureMode", () => {
// signature: (isSwiftShader, transparent)
it("opaque + SwiftShader → screenshot (no GPU egress to skip — parity at best)", () => {
expect(resolveDrawElementCaptureMode(true, false)).toBe("screenshot");
});
it("transparent + SwiftShader → screenshot (also drops sub-layers; crbug 521434899)", () => {
expect(resolveDrawElementCaptureMode(true, true)).toBe("screenshot");
});
it("transparent + GPU → drawelement (GPU handles transparent correctly)", () => {
expect(resolveDrawElementCaptureMode(false, true)).toBe("drawelement");
});
it("opaque + GPU → drawelement", () => {
expect(resolveDrawElementCaptureMode(false, false)).toBe("drawelement");
});
// The <video> gate (proxy for the caption-pattern bug, crbug 521861819) was
// removed once Chrome 151 fixed it — video comps now take the drawElement path.
});