diff options
| author | Gabriel Schneider <[email protected]> | 2026-07-28 13:21:42 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-01 15:02:07 -0300 |
| commit | 0b5ad7a30ba9e7c6eedcb426be294bd94219310f (patch) | |
| tree | 1a991acfb2a14f25ece19e13e65915dbbb48c7fc /test/e2e_harness.zig | |
| parent | 95202b9b22751c86f100aae9ff1c64e535dcbd70 (diff) | |
| download | pardes-0b5ad7a30ba9e7c6eedcb426be294bd94219310f.tar.gz pardes-0b5ad7a30ba9e7c6eedcb426be294bd94219310f.zip | |
build.zig runners and testing improvements
Diffstat (limited to 'test/e2e_harness.zig')
| -rw-r--r-- | test/e2e_harness.zig | 32 |
1 files changed, 31 insertions, 1 deletions
diff --git a/test/e2e_harness.zig b/test/e2e_harness.zig index 9099f13e..eb4c7a2f 100644 --- a/test/e2e_harness.zig +++ b/test/e2e_harness.zig @@ -15,7 +15,9 @@ extern "c" fn forkpty( extern "c" fn execvp(file: [*:0]const u8, argv: [*:null]const ?[*:0]const u8) c_int; extern "c" fn _exit(status: c_int) noreturn; -fn nowMs() i64 { +/// Monotonic milliseconds; the harness's only clock (std.time lost +/// milliTimestamp in 0.16). +pub fn nowMs() i64 { var ts: libc.timespec = undefined; _ = libc.clock_gettime(.MONOTONIC, &ts); return @as(i64, @intCast(ts.sec)) * 1000 + @divFloor(@as(i64, @intCast(ts.nsec)), 1_000_000); @@ -133,6 +135,34 @@ pub const Harness = struct { if (self.trace) self.traceScreen("pump"); } + /// Wait up to `ms` for the app to say something, feed whatever arrived to + /// the emulator, and report whether anything did. `ms` = 0 polls. This is + /// the event-driven half of `pump`: a caller that only cares about the + /// SETTLED grid sleeps in poll until the app actually writes, instead of + /// burning a fixed window per check (see snapshot.zig's waitStable). + pub fn pumpOnce(self: *Harness, ms: i64) !bool { + var fds = [_]posix.pollfd{.{ .fd = self.master, .events = posix.POLL.IN, .revents = 0 }}; + const ready = posix.poll(&fds, @intCast(@max(0, ms))) catch 0; + if (ready == 0 or (fds[0].revents & posix.POLL.IN) == 0) return false; + var buf: [4096]u8 = undefined; + const n = posix.read(self.master, &buf) catch return false; + if (n == 0) return false; + self.raw.appendSlice(self.gpa, buf[0..n]) catch {}; + self.stream.nextSlice(buf[0..n]); + return true; + } + + /// Bytes sitting in the pty that nobody has picked up — keystrokes the app + /// has not read yet, or output we have not read yet (the master counts both + /// directions). Zero is the proof that the app has actually CONSUMED the + /// input just sent: without it, an app still asleep in its event loop looks + /// exactly like an app that has finished. See snapshot.zig's waitStable. + pub fn pending(self: *Harness) usize { + var n: c_int = 0; + if (posix.system.ioctl(self.master, posix.T.FIONREAD, @intFromPtr(&n)) != 0) return 0; + return if (n > 0) @intCast(n) else 0; + } + /// Resize both the pty (SIGWINCH to the app) and our terminal (grid reflows /// to the new size). Harness terminal first so it's ready for the app's /// re-render at the new size. |
