diff options
| author | Gabriel Schneider <[email protected]> | 2026-07-02 20:06:25 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-01 15:02:07 -0300 |
| commit | bead9f0b78380d1428f40fdab339c1c8f88fa626 (patch) | |
| tree | 960255de5a2cef661ec5d0ad3220a4b2b1738842 /tests_sdl.zig | |
| parent | 62ef1882ab7878210f05f06504796849e412c318 (diff) | |
| download | pardes-bead9f0b78380d1428f40fdab339c1c8f88fa626.tar.gz pardes-bead9f0b78380d1428f40fdab339c1c8f88fa626.zip | |
sdl ui backend
Diffstat (limited to 'tests_sdl.zig')
| -rw-r--r-- | tests_sdl.zig | 165 |
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", .{}); +} |
