summaryrefslogtreecommitdiff
path: root/test/e2e_harness.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 19:49:19 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:17 -0300
commitd1e93d4cdc2e2625d5a1724a7167a5c3e64fe6a2 (patch)
tree32ec6983d7ef0c8b828a041447dce53928961d11 /test/e2e_harness.zig
parent156891c9346ee15aea5e10edbc80850f0cf36797 (diff)
downloadpardes-d1e93d4cdc2e2625d5a1724a7167a5c3e64fe6a2.tar.gz
pardes-d1e93d4cdc2e2625d5a1724a7167a5c3e64fe6a2.zip
Over a slow link the tty keeps one frame in flight: it asks the terminal to answer after each, and draws the latest state when it does
Diffstat (limited to 'test/e2e_harness.zig')
-rw-r--r--test/e2e_harness.zig28
1 files changed, 24 insertions, 4 deletions
diff --git a/test/e2e_harness.zig b/test/e2e_harness.zig
index eaa19b43..9e67c49f 100644
--- a/test/e2e_harness.zig
+++ b/test/e2e_harness.zig
@@ -64,6 +64,8 @@ pub const Harness = struct {
/// when true, print the captured screen state after each pump/waitFor and on
/// every assertion, so live test runs can be inspected (zig build test -Dtrace).
trace: bool = false,
+ /// How much of a `CSI 5 n` the output has ended on (`feed`).
+ status_request: u8 = 0,
/// every raw byte the app has emitted (accumulated in pump). Lets tests assert
/// on control sequences the emulator consumes and never renders (e.g. OSC 52
/// clipboard writes). Capture is bounded explicitly so a runaway child cannot
@@ -147,6 +149,24 @@ pub const Harness = struct {
/// Read pty output and feed it to our ghostty terminal for `ms` ms. After
/// this, the grid reflects everything the app rendered so far.
+ /// The app's output into the emulator. The one query answered is the
+ /// device status request (`CSI 5 n` → `CSI 0 n`), as every terminal
+ /// does: the tty asks it after each frame and draws no other until the
+ /// answer (FrameAck in src/tty/tty.zig).
+ fn feed(self: *Harness, bytes: []const u8) void {
+ self.stream.nextSlice(bytes);
+ const request = "\x1b[5n";
+ for (bytes) |b| {
+ if (b == request[self.status_request]) {
+ self.status_request += 1;
+ } else self.status_request = if (b == 0x1b) 1 else 0;
+ if (self.status_request == request.len) {
+ self.status_request = 0;
+ _ = libc.write(self.master, "\x1b[0n", 4);
+ }
+ }
+ }
+
pub fn pump(self: *Harness, ms: i64) !void {
const deadline = nowMs() + ms;
var buf: [4096]u8 = undefined;
@@ -157,7 +177,7 @@ pub const Harness = struct {
const n = posix.read(self.master, &buf) catch break;
if (n == 0) break;
self.recordRaw(buf[0..n]);
- self.stream.nextSlice(buf[0..n]);
+ self.feed(buf[0..n]);
}
}
if (self.trace) self.traceScreen("pump");
@@ -176,7 +196,7 @@ pub const Harness = struct {
const n = posix.read(self.master, &buf) catch return false;
if (n == 0) return false;
self.recordRaw(buf[0..n]);
- self.stream.nextSlice(buf[0..n]);
+ self.feed(buf[0..n]);
return true;
}
@@ -243,7 +263,7 @@ pub const Harness = struct {
const n = posix.read(self.master, &buf) catch break;
if (n == 0) break;
self.recordRaw(buf[0..n]);
- self.stream.nextSlice(buf[0..n]);
+ self.feed(buf[0..n]);
}
const text = try self.screenText();
defer self.gpa.free(text);
@@ -270,7 +290,7 @@ pub const Harness = struct {
const n = posix.read(self.master, &buf) catch break;
if (n == 0) break;
self.recordRaw(buf[0..n]);
- self.stream.nextSlice(buf[0..n]);
+ self.feed(buf[0..n]);
if (std.mem.indexOf(u8, self.raw.items, needle) != null) return true;
}
return false;