summaryrefslogtreecommitdiff
path: root/tests_sdl.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-07-02 20:06:25 -0300
committerGabriel Schneider <[email protected]>2026-08-01 15:02:07 -0300
commitbead9f0b78380d1428f40fdab339c1c8f88fa626 (patch)
tree960255de5a2cef661ec5d0ad3220a4b2b1738842 /tests_sdl.zig
parent62ef1882ab7878210f05f06504796849e412c318 (diff)
downloadpardes-bead9f0b78380d1428f40fdab339c1c8f88fa626.tar.gz
pardes-bead9f0b78380d1428f40fdab339c1c8f88fa626.zip
sdl ui backend
Diffstat (limited to 'tests_sdl.zig')
-rw-r--r--tests_sdl.zig165
1 files changed, 165 insertions, 0 deletions
diff --git a/tests_sdl.zig b/tests_sdl.zig
new file mode 100644
index 00000000..857fc0a2
--- /dev/null
+++ b/tests_sdl.zig
@@ -0,0 +1,165 @@
+// E2E tests for the SDL3 GPU backend.
+//
+// Unlike the terminal e2e suite, this harness validates the rendered framebuffer:
+// the SDL app runs its real GPU/font/shader path and writes the latest frame as
+// a PPM image in a temp capture directory. These assertions are intentionally
+// visual/pixel-level, not retained-grid text assertions.
+const std = @import("std");
+const h = @import("e2e_harness_sdl.zig");
+const config = @import("config");
+const Harness = h.SdlHarness;
+const Image = h.Image;
+
+fn expectPixelSize(hs: *Harness, width: u32, height: u32, msg: []const u8) !void {
+ const img = hs.image orelse {
+ hs.dump(msg);
+ return error.ExpectFailed;
+ };
+ if (img.width != width or img.height != height) {
+ hs.dump(msg);
+ return error.ExpectFailed;
+ }
+}
+
+fn expectChangedPixels(hs: *Harness, before: Image, min_changed: usize, msg: []const u8) !void {
+ const img = hs.image orelse {
+ hs.dump(msg);
+ return error.ExpectFailed;
+ };
+ if (img.changedPixels(before, 30) < min_changed) {
+ hs.dump(msg);
+ return error.ExpectFailed;
+ }
+}
+
+pub fn main(init: std.process.Init) !void {
+ const gpa = init.gpa;
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(init.arena.allocator());
+ if (args.len != 2) {
+ std.debug.print("usage: {s} <pardes-sdl-exe>\n", .{args[0]});
+ return error.BadArgs;
+ }
+ const exe = args[1];
+
+ var hs = try Harness.init(gpa, io, exe.ptr, 24, 80);
+ defer hs.deinit();
+ hs.trace = config.trace;
+
+ try hs.expectWaitForImage(8000, "SDL: app never produced a rendered frame");
+ try hs.pump(2000);
+ try hs.expectGridPixels("SDL: initial rendered frame is not aligned to the pty grid");
+ const cw = hs.cellWidth();
+ const ch = hs.cellHeight();
+ try hs.expectBrightRows(0, 1, 80, "SDL: top bar text did not render as bright glyph pixels");
+ try hs.expectBrightRows(2, 20, 3000, "SDL: terminal body did not render directory-list glyph pixels");
+
+ // Resize must change the actual framebuffer dimensions, not just retained
+ // state. This catches the real SDL window/swapchain path.
+ try hs.resize(30, 100);
+ const before_resize = hs.imageHash();
+ try hs.expectWaitForChange(before_resize, 8000, "SDL: resize did not produce a new rendered frame");
+ try expectPixelSize(&hs, cw * 100, ch * 30, "SDL: resized framebuffer does not match 100x30 cells");
+ try hs.expectBrightRows(0, 1, 80, "SDL: top bar disappeared after resize");
+
+ // TTY toggle should visibly change the active pane tag/body because prompts
+ // become visible and the mode tag flips.
+ var before = try hs.imageCopy();
+ defer before.deinit(gpa);
+ try hs.send("\x02");
+ try hs.expectWaitForChange(before.hash(), 6000, "SDL: Ctrl-b did not visibly enter tty mode");
+ try expectChangedPixels(&hs, before, 100, "SDL: tty mode visual delta was too small");
+
+ // Shell output should show up in the real rendered framebuffer.
+ before.deinit(gpa);
+ before = try hs.imageCopy();
+ try hs.send("echo SDL_E2E_MARKER_42\r");
+ try hs.expectWaitForChange(before.hash(), 8000, "SDL: shell command did not visibly update rendered output");
+ try expectChangedPixels(&hs, before, 100, "SDL: shell output visual delta was too small");
+
+ // Leaving tty should hide prompt/command rows again, producing another real
+ // rendered change.
+ before.deinit(gpa);
+ before = try hs.imageCopy();
+ try hs.send("\x02");
+ try hs.expectWaitForChange(before.hash(), 6000, "SDL: Ctrl-b did not visibly return to normal mode");
+ try expectChangedPixels(&hs, before, 100, "SDL: normal-mode visual delta was too small");
+
+ // Global tagbar mouse execution: middle-click Tutor should visibly add a
+ // file-like tutor pane.
+ before.deinit(gpa);
+ before = try hs.imageCopy();
+ try hs.send("\x1b[<1;14;1M\x1b[<1;14;1m");
+ try hs.expectWaitForChange(before.hash(), 10000, "SDL: Tutor tagbar action did not visibly change the render");
+ try expectChangedPixels(&hs, before, 1000, "SDL: Tutor visual delta was too small");
+ try hs.expectBrightRows(1, 8, 300, "SDL: Tutor pane did not render bright text in the top rows");
+
+ // Fresh app: terminal char-mode overlay is visible, can be edited, hides in
+ // raw tty, and returns after leaving tty. Every step is asserted through
+ // rendered pixels.
+ {
+ var hs2 = try Harness.init(gpa, io, exe.ptr, 24, 80);
+ defer hs2.deinit();
+ hs2.trace = config.trace;
+ try hs2.expectWaitForImage(8000, "SDL overlay: no initial frame");
+ try hs2.send("\x1b[<0;5;8M\x1b[<0;5;8m");
+ try hs2.pump(400);
+ try hs2.send("i");
+ try hs2.pump(300);
+
+ var base = try hs2.imageCopy();
+ defer base.deinit(gpa);
+ try hs2.send("ZZSDLINSXYZ");
+ try hs2.expectWaitForChange(base.hash(), 5000, "SDL: typed overlay did not render");
+ try expectChangedPixels(&hs2, base, 80, "SDL: typed overlay visual delta was too small");
+
+ base.deinit(gpa);
+ base = try hs2.imageCopy();
+ try hs2.send("\x7f\x7f\x7f");
+ try hs2.expectWaitForChange(base.hash(), 5000, "SDL: overlay backspace did not render");
+
+ try hs2.send("\x1b");
+ try hs2.pump(300);
+ base.deinit(gpa);
+ base = try hs2.imageCopy();
+ try hs2.send("\x02");
+ try hs2.expectWaitForChange(base.hash(), 6000, "SDL: tty mode did not hide overlay visually");
+ try expectChangedPixels(&hs2, base, 40, "SDL: tty-hide overlay delta was too small");
+
+ base.deinit(gpa);
+ base = try hs2.imageCopy();
+ try hs2.send("\x02");
+ try hs2.expectWaitForChange(base.hash(), 6000, "SDL: leaving tty did not restore overlay visually");
+ }
+
+ // CLI mode parity: --tty starts as a raw shell; typed shell input must
+ // visibly alter the framebuffer.
+ {
+ var hs3 = try Harness.initArgs(gpa, io, exe.ptr, 24, 80, "--tty");
+ defer hs3.deinit();
+ hs3.trace = config.trace;
+ try hs3.expectWaitForImage(8000, "SDL --tty: no initial frame");
+ before.deinit(gpa);
+ before = try hs3.imageCopy();
+ try hs3.send("i=0; while [ $i -lt 30 ]; do echo ZZSDLTTYFLAG_$i; i=$((i+1)); done\r");
+ try hs3.pump(2500);
+ try expectChangedPixels(&hs3, before, 80, "SDL --tty: shell visual delta was too small");
+ try hs3.expectBrightRows(18, 23, 350, "SDL --tty: lower terminal rows were not rendered");
+ }
+
+ // Configurable tty toggle: Ctrl-g should visibly enter tty mode when the
+ // toggle is moved off Ctrl-b.
+ {
+ var hs4 = try Harness.initArgs(gpa, io, exe.ptr, 24, 80, "--tty-toggle=g");
+ defer hs4.deinit();
+ hs4.trace = config.trace;
+ try hs4.expectWaitForImage(8000, "SDL --tty-toggle=g: no initial frame");
+ before.deinit(gpa);
+ before = try hs4.imageCopy();
+ try hs4.send("\x07");
+ try hs4.expectWaitForChange(before.hash(), 6000, "SDL --tty-toggle=g: Ctrl-g did not visibly enter tty mode");
+ try expectChangedPixels(&hs4, before, 80, "SDL --tty-toggle=g: visual delta was too small");
+ }
+
+ std.debug.print("SDL rendered-image e2e: all tests passed\n", .{});
+}