a73x

439d9e14

test: e2e through the real client/daemon loop

a73x   2026-08-08 14:08

Commit message
test: e2e through the real client/daemon loop

src/client.zig
Old New
@@ -111,6 +111,9 @@ fn ttySize(fd: std.posix.fd_t) ?proto.Size {
111 if (!std.posix.isatty(fd)) return null; 111 if (!std.posix.isatty(fd)) return null;
112 var ws: std.posix.winsize = undefined; 112 var ws: std.posix.winsize = undefined;
113 if (std.os.linux.ioctl(fd, std.os.linux.T.IOCGWINSZ, @intFromPtr(&ws)) != 0) return null; 113 if (std.os.linux.ioctl(fd, std.os.linux.T.IOCGWINSZ, @intFromPtr(&ws)) != 0) return null;
114 // A pty can report 0x0 (e.g. `script` with piped stdin); a zero-sized
115 // grid is invalid for the engine. Treat it as "unknown".
116 if (ws.col < 2 or ws.row < 2) return null;
114 return .{ .cols = ws.col, .rows = ws.row }; 117 return .{ .cols = ws.col, .rows = ws.row };
115 } 118 }
116 119
src/server.zig
Old New
@@ -225,6 +225,9 @@ pub const Server = struct {
225 } 225 }
226 226
227 fn applySize(self: *Server, cols: u16, rows: u16) void { 227 fn applySize(self: *Server, cols: u16, rows: u16) void {
228 // A degenerate size (0x0 pty, buggy client) would trip engine
229 // asserts; keep the current grid instead.
230 if (cols < 2 or rows < 2) return;
228 self.eng.resize(cols, rows) catch return; 231 self.eng.resize(cols, rows) catch return;
229 self.pty.resize(cols, rows) catch {}; 232 self.pty.resize(cols, rows) catch {};
230 } 233 }
test/e2e.sh
Old New
@@ -1,25 +1,37 @@
1 #!/bin/sh 1 #!/bin/sh
2 # End-to-end: run muxd headless with piped stdin, dump the grid from a 2 # End-to-end: muxd daemon + mux client. Input flows client -> daemon -> pty;
3 # second process, verify shell output landed in the ghostty-vt grid. 3 # output flows pty -> engine -> snapshot -> client replica -> client stdout.
4 set -eu 4 set -eu
5 MUXD="$1" 5 MUXD="$1"
6 MUX="$2"
6 SOCK="${TMPDIR:-/tmp}/muxd-e2e-$$.sock" 7 SOCK="${TMPDIR:-/tmp}/muxd-e2e-$$.sock"
8 OUT="${TMPDIR:-/tmp}/mux-e2e-out-$$"
7 9
8 cleanup() { kill "$DPID" 2>/dev/null || true; rm -f "$SOCK"; } 10 cleanup() { kill "$DPID" 2>/dev/null || true; rm -f "$SOCK" "$OUT"; }
9 trap cleanup EXIT INT TERM 11 trap cleanup EXIT INT TERM
10 12
11 { printf 'printf "e2e-%%s\\n" works\n'; sleep 3; } | \ 13 "$MUXD" run --sock "$SOCK" --shell /bin/sh &
12 "$MUXD" run --sock "$SOCK" --shell /bin/sh &
13 DPID=$! 14 DPID=$!
14 15
15 # Wait for the socket, then give the shell a moment to run the command.
16 i=0 16 i=0
17 while [ ! -S "$SOCK" ] && [ "$i" -lt 50 ]; do sleep 0.1; i=$((i+1)); done 17 while [ ! -S "$SOCK" ] && [ "$i" -lt 50 ]; do sleep 0.1; i=$((i+1)); done
18 [ -S "$SOCK" ] || { echo "e2e FAIL: socket never appeared"; exit 1; } 18 [ -S "$SOCK" ] || { echo "e2e FAIL: socket never appeared"; exit 1; }
19 sleep 1
20 19
21 OUT="$("$MUXD" dump --sock "$SOCK")" 20 # Client with piped stdio: types a command, waits, detaches with Ctrl-\ (034).
22 case "$OUT" in 21 { printf 'printf "e2e-%%s\\n" works\n'; sleep 2; printf '\034'; } | \
23 *e2e-works*) echo "e2e OK" ;; 22 "$MUX" --sock "$SOCK" > "$OUT"
24 *) echo "e2e FAIL: grid was:"; echo "$OUT"; exit 1 ;; 23
25 esac 24 # 1. The client's rendered output must contain the command's result.
25 grep -q "e2e-works" "$OUT" || {
26 echo "e2e FAIL: client render missing output; got:"; cat "$OUT"; exit 1;
27 }
28
29 # 2. The daemon kept the session; its grid must match.
30 "$MUXD" dump --sock "$SOCK" | grep -q "e2e-works" || {
31 echo "e2e FAIL: daemon grid missing output"; exit 1;
32 }
33
34 # 3. Detach left the daemon running.
35 kill -0 "$DPID" || { echo "e2e FAIL: daemon died on detach"; exit 1; }
36
37 echo "e2e OK"