1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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", .{});
}
|