summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-07-05 20:21:50 -0300
committerGabriel Schneider <[email protected]>2026-08-01 15:02:07 -0300
commitde4def3548a6729b0dfd2120495a61beef8c8c2c (patch)
tree12144f0f64dc96bb4741459c1f9db57f44349330 /tests
parent7988d9bc6e31210ff13994f18d5622dd57ba9341 (diff)
downloadpardes-de4def3548a6729b0dfd2120495a61beef8c8c2c.tar.gz
pardes-de4def3548a6729b0dfd2120495a61beef8c8c2c.zip
pardes v2: the rewrite, complete and organized. src/ (core + three shells), test/ (snapshot parity harness + 18 frozen goldens). One sans-IO core, vaxis tty + SDL3 GPU native + wasm web shells, 18/18 parity with the purged prototype, 7.6k lines vs 12.1k. Fix: gui shell pre-sized the core at init so the greet-releasing resize never fired (blank panes until first interaction); live sessions now init at defaults and get the real grid as a resize event (the shell contract, documented on Options).
Diffstat (limited to 'tests')
-rw-r--r--tests/browser_replay_dump.zon54
-rw-r--r--tests/browser_replay_e2e.mjs356
2 files changed, 0 insertions, 410 deletions
diff --git a/tests/browser_replay_dump.zon b/tests/browser_replay_dump.zon
deleted file mode 100644
index bee296d7..00000000
--- a/tests/browser_replay_dump.zon
+++ /dev/null
@@ -1,54 +0,0 @@
-.{
- .screen = .{ .cols = 80, .rows = 24 },
- .active = 0,
- .topbar = "pardes replay browser e2e",
- .theme = "dark",
- .columns = .{
- .{ .weight = 1, .panes = .{ 0 } },
- .{ .weight = 1, .panes = .{ 1 } },
- },
- .panes = .{
- .{
- .kind = .file,
- .tag = "nm browser_replay.zig Del",
- .body =
- \\const message = "browser replay shader path";
- \\pub fn main() void {
- \\ // This fixture should render in WebGL.
- \\}
- ,
- .scroll = 0,
- .cols = 80,
- .rows = 12,
- .vweight = 1,
- .file = .{
- .path = "browser_replay.zig",
- .content =
- \\const message = "browser replay shader path";
- \\pub fn main() void {
- \\ // This fixture should render in WebGL.
- \\}
- ,
- },
- },
- .{
- .kind = .terminal,
- .tag = "sh /tmp Del",
- .body =
- \\$ zig build replay -Dtarget=wasm32-emscripten
- \\browser e2e marker 4200
- ,
- .scroll = 0,
- .cols = 80,
- .rows = 10,
- .vweight = 1,
- .terminal = .{
- .cwd = "/tmp",
- .stream =
- \\$ zig build replay -Dtarget=wasm32-emscripten
- \\browser e2e marker 4200
- ,
- },
- },
- },
-}
diff --git a/tests/browser_replay_e2e.mjs b/tests/browser_replay_e2e.mjs
deleted file mode 100644
index 2d8e202c..00000000
--- a/tests/browser_replay_e2e.mjs
+++ /dev/null
@@ -1,356 +0,0 @@
-import { createServer } from "node:http";
-import { mkdtemp, readFile, rm, stat } from "node:fs/promises";
-import { spawn } from "node:child_process";
-import { createRequire } from "node:module";
-import { tmpdir } from "node:os";
-import { resolve, join, extname } from "node:path";
-import { inflateSync } from "node:zlib";
-
-const webDir = resolve(process.argv[2] ?? "zig-out/web");
-const screenshotPath = resolve(process.argv[3] ?? "zig-out/browser-replay.png");
-const backingScale = Number(process.argv[4] ?? "1.5");
-if (!Number.isFinite(backingScale) || backingScale <= 0) {
- throw new Error(`invalid browser backing scale: ${process.argv[4]}`);
-}
-
-const mime = new Map([
- [".html", "text/html; charset=utf-8"],
- [".js", "text/javascript; charset=utf-8"],
- [".wasm", "application/wasm"],
- [".png", "image/png"],
-]);
-
-function run(cmd, args) {
- return new Promise((resolvePromise, reject) => {
- const child = spawn(cmd, args, { stdio: "inherit" });
- child.on("error", reject);
- child.on("exit", (code) => {
- if (code === 0) resolvePromise();
- else reject(new Error(`${cmd} ${args.join(" ")} exited with ${code}`));
- });
- });
-}
-
-async function loadPlaywright() {
- try {
- return { api: await import("playwright"), cleanup: async () => {} };
- } catch (err) {
- if (process.env.PARDES_BROWSER_SKIP_INSTALL === "1") throw err;
- }
-
- const dir = await mkdtemp(join(tmpdir(), "pardes-playwright-"));
- try {
- await run("npm", ["--prefix", dir, "install", "--silent", "playwright"]);
- await run("npm", ["--prefix", dir, "exec", "--", "playwright", "install", "chromium"]);
- const require = createRequire(join(dir, "package.json"));
- return {
- api: require("playwright"),
- cleanup: async () => {
- await rm(dir, { recursive: true, force: true });
- },
- };
- } catch (err) {
- await rm(dir, { recursive: true, force: true });
- throw err;
- }
-}
-
-async function serve(root) {
- await stat(join(root, "replay.html"));
- await stat(join(root, "pardes-replay.js"));
-
- const server = createServer(async (req, res) => {
- try {
- const url = new URL(req.url ?? "/", "http://127.0.0.1");
- const path = url.pathname === "/" ? "/replay.html" : url.pathname;
- if (path.includes("..")) throw new Error("bad path");
- const file = join(root, path.slice(1));
- const body = await readFile(file);
- res.writeHead(200, {
- "content-type": mime.get(extname(file)) ?? "application/octet-stream",
- "cache-control": "no-store",
- });
- res.end(body);
- } catch {
- res.writeHead(404);
- res.end("not found");
- }
- });
-
- await new Promise((resolvePromise) => server.listen(0, "127.0.0.1", resolvePromise));
- const address = server.address();
- if (!address || typeof address === "string") throw new Error("failed to bind test server");
- return { server, url: `http://127.0.0.1:${address.port}/replay.html` };
-}
-
-function parsePng(buf) {
- const sig = "89504e470d0a1a0a";
- if (buf.subarray(0, 8).toString("hex") !== sig) throw new Error("not a png");
-
- let offset = 8;
- let width = 0;
- let height = 0;
- let bitDepth = 0;
- let colorType = 0;
- const idat = [];
- while (offset < buf.length) {
- const len = buf.readUInt32BE(offset);
- const type = buf.subarray(offset + 4, offset + 8).toString("ascii");
- const data = buf.subarray(offset + 8, offset + 8 + len);
- offset += 12 + len;
- if (type === "IHDR") {
- width = data.readUInt32BE(0);
- height = data.readUInt32BE(4);
- bitDepth = data[8];
- colorType = data[9];
- } else if (type === "IDAT") {
- idat.push(data);
- } else if (type === "IEND") {
- break;
- }
- }
- if (bitDepth !== 8) throw new Error(`unsupported png bit depth ${bitDepth}`);
- const channels = colorType === 6 ? 4 : colorType === 2 ? 3 : colorType === 0 ? 1 : 0;
- if (channels === 0) throw new Error(`unsupported png color type ${colorType}`);
-
- const raw = inflateSync(Buffer.concat(idat));
- const stride = width * channels;
- const rows = Buffer.alloc(width * height * channels);
- let rawOffset = 0;
- for (let y = 0; y < height; y += 1) {
- const filter = raw[rawOffset];
- rawOffset += 1;
- const rowStart = y * stride;
- for (let x = 0; x < stride; x += 1) {
- const left = x >= channels ? rows[rowStart + x - channels] : 0;
- const up = y > 0 ? rows[rowStart + x - stride] : 0;
- const upLeft = y > 0 && x >= channels ? rows[rowStart + x - stride - channels] : 0;
- const value = raw[rawOffset + x];
- let recon;
- if (filter === 0) recon = value;
- else if (filter === 1) recon = value + left;
- else if (filter === 2) recon = value + up;
- else if (filter === 3) recon = value + Math.floor((left + up) / 2);
- else if (filter === 4) {
- const p = left + up - upLeft;
- const pa = Math.abs(p - left);
- const pb = Math.abs(p - up);
- const pc = Math.abs(p - upLeft);
- recon = value + (pa <= pb && pa <= pc ? left : pb <= pc ? up : upLeft);
- } else {
- throw new Error(`unsupported png filter ${filter}`);
- }
- rows[rowStart + x] = recon & 0xff;
- }
- rawOffset += stride;
- }
- return { width, height, channels, rows };
-}
-
-function validateScreenshot(png) {
- let bright = 0;
- let colored = 0;
- let nonDark = 0;
- const pixels = png.width * png.height;
- for (let i = 0; i < pixels; i += 1) {
- const off = i * png.channels;
- const r = png.rows[off];
- const g = png.channels === 1 ? r : png.rows[off + 1];
- const b = png.channels === 1 ? r : png.rows[off + 2];
- if (r > 90 || g > 90 || b > 90) bright += 1;
- if (Math.max(r, g, b) - Math.min(r, g, b) > 20) colored += 1;
- if (r > 35 || g > 35 || b > 35) nonDark += 1;
- }
- if (png.width < 640 || png.height < 360) {
- throw new Error(`screenshot too small: ${png.width}x${png.height}`);
- }
- if (bright < 500 || nonDark < 2000) {
- throw new Error(`browser replay appears blank: bright=${bright} nonDark=${nonDark} colored=${colored}`);
- }
- console.log(`browser replay screenshot ok: ${png.width}x${png.height}, bright=${bright}, nonDark=${nonDark}, colored=${colored}`);
-}
-
-function pixelRgb(png, x, y) {
- const off = (y * png.width + x) * png.channels;
- const r = png.rows[off];
- return {
- r,
- g: png.channels === 1 ? r : png.rows[off + 1],
- b: png.channels === 1 ? r : png.rows[off + 2],
- };
-}
-
-function countChangedPixels(a, b, region = {}) {
- if (a.width !== b.width || a.height !== b.height) {
- throw new Error(`png sizes differ: ${a.width}x${a.height} vs ${b.width}x${b.height}`);
- }
- const x0 = Math.max(0, Math.floor(region.x0 ?? 0));
- const y0 = Math.max(0, Math.floor(region.y0 ?? 0));
- const x1 = Math.min(a.width, Math.ceil(region.x1 ?? a.width));
- const y1 = Math.min(a.height, Math.ceil(region.y1 ?? a.height));
- let changed = 0;
- for (let y = y0; y < y1; y += 1) {
- for (let x = x0; x < x1; x += 1) {
- const ap = pixelRgb(a, x, y);
- const bp = pixelRgb(b, x, y);
- const dr = Math.abs(ap.r - bp.r);
- const dg = Math.abs(ap.g - bp.g);
- const db = Math.abs(ap.b - bp.b);
- if (dr + dg + db > 45 && Math.max(dr, dg, db) > 12) changed += 1;
- }
- }
- return changed;
-}
-
-async function screenshotPng(page) {
- return parsePng(await page.screenshot({ fullPage: true }));
-}
-
-async function waitForNextDraw(page, action) {
- const before = await page.evaluate(() => window.__pardesReplayE2E?.draws ?? 0);
- await action();
- await page.waitForFunction((draws) => (window.__pardesReplayE2E?.draws ?? 0) > draws, before, { timeout: 5000 });
- await page.waitForTimeout(50);
-}
-
-async function findHoverBoundary(page, baseline) {
- const canvasSize = await page.evaluate(() => {
- const canvas = document.querySelector("canvas");
- return { width: canvas.clientWidth, height: canvas.clientHeight };
- });
- const y = Math.floor(canvasSize.height * 0.55);
- const center = canvasSize.width / 2;
- const offsets = [];
- for (let dx = -48; dx <= 48; dx += 3) offsets.push(dx);
-
- for (const dx of offsets) {
- const x = Math.round(center + dx);
- await waitForNextDraw(page, () => page.mouse.move(x, y));
- const hover = await screenshotPng(page);
- const changed = countChangedPixels(baseline, hover, {
- x0: x - 20,
- x1: x + 20,
- y0: 20,
- y1: canvasSize.height - 20,
- });
- if (changed >= 80) return { x, y, changed, canvasSize };
- }
-
- throw new Error("browser replay hover did not visibly draw the dashed resize handle");
-}
-
-const { server, url } = await serve(webDir);
-let cleanupPlaywright = async () => {};
-let browser;
-try {
- const { api, cleanup } = await loadPlaywright();
- cleanupPlaywright = cleanup;
- const { chromium } = api;
-
- browser = await chromium.launch({
- headless: true,
- args: ["--use-angle=swiftshader", "--enable-unsafe-swiftshader"],
- });
- const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
- const consoleMessages = [];
- const pageErrors = [];
- page.on("console", (msg) => consoleMessages.push(`${msg.type()}: ${msg.text()}`));
- page.on("pageerror", (err) => pageErrors.push(err.stack ?? err.message));
-
- await page.addInitScript(() => {
- window.__pardesReplayE2E = { draws: 0, contextLost: 0 };
- window.addEventListener("webglcontextlost", () => {
- window.__pardesReplayE2E.contextLost += 1;
- }, true);
-
- const wrapDraws = (target) => {
- if (!target) return;
- for (const name of ["drawArraysInstanced", "drawArrays", "drawElements", "drawElementsInstanced"]) {
- if (typeof target[name] !== "function" || target[name].__pardesReplayE2E) continue;
- const oldDraw = target[name];
- target[name] = function(...args) {
- window.__pardesReplayE2E.draws += 1;
- return oldDraw.apply(this, args);
- };
- target[name].__pardesReplayE2E = true;
- }
- };
-
- wrapDraws(globalThis.WebGLRenderingContext?.prototype);
- wrapDraws(globalThis.WebGL2RenderingContext?.prototype);
-
- const oldGetContext = HTMLCanvasElement.prototype.getContext;
- HTMLCanvasElement.prototype.getContext = function(type, attrs) {
- const ctx = oldGetContext.call(this, type, attrs);
- if (ctx && !ctx.__pardesReplayE2E && (type === "webgl2" || type === "webgl" || type === "experimental-webgl")) {
- ctx.__pardesReplayE2E = true;
- wrapDraws(ctx);
- }
- return ctx;
- };
- });
-
- await page.goto(url, { waitUntil: "load" });
- await page.waitForSelector("canvas", { timeout: 5000 });
- await page.waitForFunction(() => window.__pardesReplayE2E?.draws >= 2, null, { timeout: 5000 });
- await page.waitForTimeout(250);
- const trace = await page.evaluate(() => window.__pardesReplayE2E);
- if (trace.contextLost !== 0) {
- throw new Error(`browser WebGL context was lost during replay: ${JSON.stringify(trace)}`);
- }
- if (pageErrors.length !== 0) {
- throw new Error(`browser page error: ${pageErrors[0]}`);
- }
- const lostMessage = consoleMessages.find((msg) => msg.includes("CONTEXT_LOST_WEBGL"));
- if (lostMessage) throw new Error(`browser WebGL context lost: ${lostMessage}`);
-
- const initialCanvasSize = await page.evaluate(() => {
- const canvas = document.querySelector("canvas");
- return {
- clientWidth: canvas.clientWidth,
- clientHeight: canvas.clientHeight,
- width: canvas.width,
- height: canvas.height,
- };
- });
- if (
- initialCanvasSize.clientWidth !== 1280 ||
- initialCanvasSize.clientHeight !== 720 ||
- initialCanvasSize.width !== Math.round(1280 * backingScale) ||
- initialCanvasSize.height !== Math.round(720 * backingScale)
- ) {
- throw new Error(`browser replay canvas did not fill initial viewport: ${JSON.stringify(initialCanvasSize)}`);
- }
-
- await page.setViewportSize({ width: 913, height: 617 });
- await page.waitForFunction((scale) => {
- const canvas = document.querySelector("canvas");
- return canvas?.clientWidth === 913 &&
- canvas?.clientHeight === 617 &&
- canvas?.width === Math.round(913 * scale) &&
- canvas?.height === Math.round(617 * scale);
- }, backingScale, { timeout: 5000 });
- await page.waitForFunction(() => window.__pardesReplayE2E?.draws >= 3, null, { timeout: 5000 });
-
- const baseline = await screenshotPng(page);
- const hover = await findHoverBoundary(page, baseline);
- console.log(`browser replay hover ok: changed=${hover.changed} x=${hover.x} y=${hover.y}`);
-
- const dragToX = Math.min(hover.canvasSize.width - 24, hover.x + 120);
- await waitForNextDraw(page, () => page.mouse.down());
- await waitForNextDraw(page, () => page.mouse.move(dragToX, hover.y, { steps: 8 }));
- await waitForNextDraw(page, () => page.mouse.up());
- const dragged = await screenshotPng(page);
- const dragChanged = countChangedPixels(baseline, dragged);
- if (dragChanged < 2500) {
- throw new Error(`browser replay drag did not visibly resize panes: changed=${dragChanged}`);
- }
- console.log(`browser replay drag ok: changed=${dragChanged}`);
-
- await page.screenshot({ path: screenshotPath, fullPage: true });
- validateScreenshot(parsePng(await readFile(screenshotPath)));
-} finally {
- if (browser) await browser.close();
- await cleanupPlaywright();
- server.close();
-}