55be7cab
test: the wall claims its rows, and a neighbour survives a scroll
a73x 2026-08-24 11:32
Commit message
test/e2e.sh
| Old | New | ||
|---|---|---|---|
| @@ -366,6 +366,12 @@ SOCK42="${TMPDIR:-/tmp}/muxd-e2e-wallappmouse-$$.sock" | |||
| 366 | D39PID="" | 366 | D39PID="" |
| 367 | ZMOUSESH="${TMPDIR:-/tmp}/mux-e2e-wallappmouse-$$.sh" | 367 | ZMOUSESH="${TMPDIR:-/tmp}/mux-e2e-wallappmouse-$$.sh" |
| 368 | 368 | ||
| 369 | # The scrollback-rect leg: a focused tile's history page must own only its | ||
| 370 | # sub-rect, so its daemon gets a socket of its own for the grid-size | ||
| 371 | # reason every wall leg has. | ||
| 372 | SOCK53="${TMPDIR:-/tmp}/muxd-e2e-sbrect-$$.sock" | ||
| 373 | D54PID="" | ||
| 374 | |||
| 369 | # The wall as attach HISTORY (phase 2). A daemon AND a state home of its | 375 | # The wall as attach HISTORY (phase 2). A daemon AND a state home of its |
| 370 | # own, for the dynamic-wall leg's reason turned up one notch: what these | 376 | # own, for the dynamic-wall leg's reason turned up one notch: what these |
| 371 | # two blocks read back is the wall FILE, and every `mux` in this suite now | 377 | # two blocks read back is the wall FILE, and every `mux` in this suite now |
| @@ -1307,6 +1313,7 @@ cleanup() { | |||
| 1307 | [ -S "$SOCK49" ] && "$MUXD" stop --sock "$SOCK49" 2>/dev/null || true | 1313 | [ -S "$SOCK49" ] && "$MUXD" stop --sock "$SOCK49" 2>/dev/null || true |
| 1308 | [ -S "$SOCK50" ] && "$MUXD" stop --sock "$SOCK50" 2>/dev/null || true | 1314 | [ -S "$SOCK50" ] && "$MUXD" stop --sock "$SOCK50" 2>/dev/null || true |
| 1309 | [ -S "$SOCK52" ] && "$MUXD" stop --sock "$SOCK52" 2>/dev/null || true | 1315 | [ -S "$SOCK52" ] && "$MUXD" stop --sock "$SOCK52" 2>/dev/null || true |
| 1316 | [ -S "$SOCK53" ] && "$MUXD" stop --sock "$SOCK53" 2>/dev/null || true | ||
| 1310 | 1317 | ||
| 1311 | # ---- the leak sweep (hygiene kit, 6a) ---- | 1318 | # ---- the leak sweep (hygiene kit, 6a) ---- |
| 1312 | # Here rather than at the bottom of the file, which `set -e` reaches only | 1319 | # Here rather than at the bottom of the file, which `set -e` reaches only |
| @@ -1321,7 +1328,7 @@ cleanup() { | |||
| 1321 | "$D20PID" "$D21PID" "$D22PID" "$D23PID" "$D24PID" "$D25PID" \ | 1328 | "$D20PID" "$D21PID" "$D22PID" "$D23PID" "$D24PID" "$D25PID" \ |
| 1322 | "$D26PID" "$D27PID" "$D28PID" "$D29PID" "$D30PID" "$D31PID" \ | 1329 | "$D26PID" "$D27PID" "$D28PID" "$D29PID" "$D30PID" "$D31PID" \ |
| 1323 | "$D32PID" "$D33PID" "$D34PID" "$D35PID" "$D36PID" "$D37PID" \ | 1330 | "$D32PID" "$D33PID" "$D34PID" "$D35PID" "$D36PID" "$D37PID" \ |
| 1324 | "$D38PID" "$D39PID" "$D40PID" "$D41PID" "$D42PID" "$D43PID" | 1331 | "$D38PID" "$D39PID" "$D40PID" "$D41PID" "$D42PID" "$D43PID" "$D54PID" |
| 1325 | _leak=0 | 1332 | _leak=0 |
| 1326 | leak_sweep "$_rc" || _leak=1 | 1333 | leak_sweep "$_rc" || _leak=1 |
| 1327 | 1334 | ||
| @@ -1388,7 +1395,9 @@ cleanup() { | |||
| 1388 | "$OUT.whlstop" "$OUT.msestop" | 1395 | "$OUT.whlstop" "$OUT.msestop" |
| 1389 | rm -f "$OUT.pgr" "$OUT.pgr.err" "$OUT.pgr.log" "$OUT.pgr.d" "$LESSSH" \ | 1396 | rm -f "$OUT.pgr" "$OUT.pgr.err" "$OUT.pgr.log" "$OUT.pgr.d" "$LESSSH" \ |
| 1390 | "$LESSDATA" "$OUT.pgrcap" "$OUT.pgrstop" "$SOCK35" \ | 1397 | "$LESSDATA" "$OUT.pgrcap" "$OUT.pgrstop" "$SOCK35" \ |
| 1391 | "$OUT.pipe" "$OUT.pipe.d" "$OUT.pipecap" "$OUT.pipestop" "$SOCK36" | 1398 | "$OUT.pipe" "$OUT.pipe.d" "$OUT.pipecap" "$OUT.pipestop" "$SOCK36" \ |
| 1399 | "$OUT.sb2.d" "$OUT.sb2a" "$OUT.sb2a.err" "$OUT.sb2b" "$OUT.sb2b.err" \ | ||
| 1400 | "$OUT.sb2cap" "$OUT.sb2cap.err" "$OUT.sb2pc" "$OUT.sb2stop" "$SOCK53" | ||
| 1392 | # ...and the non-tty capture that leg's session feeds. | 1401 | # ...and the non-tty capture that leg's session feeds. |
| 1393 | rm -f "$OUT.nogate" | 1402 | rm -f "$OUT.nogate" |
| 1394 | # ...and its other half: the paste capture and the file nvim wrote, which | 1403 | # ...and its other half: the paste capture and the file nvim wrote, which |
| @@ -5704,13 +5713,18 @@ grep -q "zs-two" "$OUT.zsfa" && { | |||
| 5704 | assert_attach_delta "$ZSATT_BEFORE" "$ZSATT_AFTER" 2 "focus skip" | 5713 | assert_attach_delta "$ZSATT_BEFORE" "$ZSATT_AFTER" 2 "focus skip" |
| 5705 | assert_never_two_clients "$OUT.zswatch" a "focus skip" | 5714 | assert_never_two_clients "$OUT.zswatch" a "focus skip" |
| 5706 | assert_never_two_clients "$OUT.zswatch" b "focus skip" | 5715 | assert_never_two_clients "$OUT.zswatch" b "focus skip" |
| 5707 | # Both tiles claimed their rect: 92 cols each (this leg's own width), the | 5716 | # Both tiles claimed their rect: 92 cols each (this leg's own width) and 14 |
| 5708 | # size the wall attached at. A focus move resizes nothing. | 5717 | # rows each (30 terminal rows cut into two stripes of 15, minus one label |
| 5718 | # bar each — layoutStripes' arithmetic, held to the size the wall attached | ||
| 5719 | # at). A focus move resizes nothing. | ||
| 5709 | for _s in a b; do | 5720 | for _s in a b; do |
| 5710 | timeout 20 "$MUXA" status --sock "$SOCK37" --session "$_s" > "$OUT.zsst$_s" 2>&1 | 5721 | timeout 20 "$MUXA" status --sock "$SOCK37" --session "$_s" > "$OUT.zsst$_s" 2>&1 |
| 5711 | grep -q '"cols":92' "$OUT.zsst$_s" || { | 5722 | grep -q '"cols":92' "$OUT.zsst$_s" || { |
| 5712 | echo "e2e FAIL: focus skip: session $_s is not at the width its tile claimed:" | 5723 | echo "e2e FAIL: focus skip: session $_s is not at the width its tile claimed:" |
| 5713 | cat "$OUT.zsst$_s"; exit 1; } | 5724 | cat "$OUT.zsst$_s"; exit 1; } |
| 5725 | grep -q '"rows":14' "$OUT.zsst$_s" || { | ||
| 5726 | echo "e2e FAIL: focus skip: session $_s is not at the height its tile claimed:" | ||
| 5727 | cat "$OUT.zsst$_s"; exit 1; } | ||
| 5714 | done | 5728 | done |
| 5715 | assert_stopped "$SOCK37" "$D34PID" "focus skip" "$OUT.zsstop" | 5729 | assert_stopped "$SOCK37" "$D34PID" "focus skip" "$OUT.zsstop" |
| 5716 | D34PID="" | 5730 | D34PID="" |
| @@ -7343,6 +7357,77 @@ assert_stopped "$SOCK52" "$D52PID" "cursor-ownership" "$OUT.custop" | |||
| 7343 | D52PID="" | 7357 | D52PID="" |
| 7344 | ok "the cursor rests in the focused tile, whichever pump painted last" | 7358 | ok "the cursor rests in the focused tile, whichever pump painted last" |
| 7345 | 7359 | ||
| 7360 | # ---- a focused tile's scrollback page owns only its sub-rect ---------- | ||
| 7361 | # | ||
| 7362 | # The one-tile wheel leg above has no label bar, so its geometry is the | ||
| 7363 | # plain client's: the one paint at row 0 that DOES own the whole screen. | ||
| 7364 | # That exemption is what let a scrollback page at a row_off through that | ||
| 7365 | # opened with `\x1b[H\x1b[2J` — clearing the whole terminal and painting | ||
| 7366 | # history at row 1, while the exit path repaints only the focused tile's | ||
| 7367 | # own rect, so every neighbour stayed blank. This leg is the twin that | ||
| 7368 | # catches it: two tiles over 30 rows, the focused one scrolls back on the | ||
| 7369 | # wheel, and the neighbour's pin must still be the last content painted in | ||
| 7370 | # its rows, with no whole-screen clear following it. | ||
| 7371 | # | ||
| 7372 | # Two sessions on one daemon. The focused tile (a, the first spelling) | ||
| 7373 | # fills a scrollback the wheel can page into; the neighbour (b) holds a | ||
| 7374 | # pin and is never touched again. Both shells are /bin/sh and the content | ||
| 7375 | # is typed, the focus-skip leg's pattern — the neighbour's pin is the | ||
| 7376 | # session's own output, not an echo of the command that made it. | ||
| 7377 | "$MUXD" run --sock "$SOCK53" --shell /bin/sh > "$OUT.sb2.d" 2>&1 & | ||
| 7378 | D54PID=$! | ||
| 7379 | wait_sock "$SOCK53" "$OUT.sb2.d" "scrollback-rect daemon never bound" | ||
| 7380 | |||
| 7381 | # Session a: 100 lines of history, then cat. The wall attaches only once | ||
| 7382 | # seq has finished, so the page the wheel fetches is content this tile was | ||
| 7383 | # never sent live — the wheel leg's reason, verbatim. | ||
| 7384 | pipe_mux "$OUT.sb2a" "$OUT.sb2a.err" timeout 40 "$MUX" --sock "$SOCK53" --session a | ||
| 7385 | pipe_send 'seq 1 100 | sed '\''s/^/wln/'\''; exec cat\n' | ||
| 7386 | await_out "$OUT.sb2a" "wln100" "scrollback-rect: session a never finished seq" | ||
| 7387 | pipe_detach | ||
| 7388 | wait_grid "$SOCK53" "wln100" "scrollback-rect: session a's tail" a | ||
| 7389 | |||
| 7390 | # Session b: a pin the capture can grep for, then cat. `printf "sb2%s\n" | ||
| 7391 | # nbr` so the pin is the output, not the echoed command — the focus-skip | ||
| 7392 | # leg's prefix trick. | ||
| 7393 | pipe_mux "$OUT.sb2b" "$OUT.sb2b.err" timeout 40 "$MUX" --sock "$SOCK53" --session b | ||
| 7394 | pipe_send 'printf "sb2%%s\\n" nbr; exec cat\n' | ||
| 7395 | await_out "$OUT.sb2b" "sb2nbr" "scrollback-rect: session b's pin never reached the client" | ||
| 7396 | pipe_detach | ||
| 7397 | wait_grid "$SOCK53" "sb2nbr" "scrollback-rect: session b's pin" b | ||
| 7398 | |||
| 7399 | set +e | ||
| 7400 | timeout 90 "$PTYCLIENT" --cols 92 --rows 30 --out "$OUT.sb2cap" --err "$OUT.sb2cap.err" -- \ | ||
| 7401 | "$MUX" wall "--sock $SOCK53#a" "--sock $SOCK53#b" > "$OUT.sb2pc" 2>&1 <<'EOF' | ||
| 7402 | expect sb2nbr 20000 | ||
| 7403 | settle 700 20000 | ||
| 7404 | send \x1b[<64;10;5M\x1b[<64;10;5M\x1b[<64;10;5M\x1b[<64;10;5M\x1b[<64;10;5M\x1b[<64;10;5M\x1b[<64;10;5M\x1b[<64;10;5M | ||
| 7405 | expect \x1b[7m[scroll] 15000 | ||
| 7406 | settle 500 15000 | ||
| 7407 | send \x1cd | ||
| 7408 | waitexit 10000 | ||
| 7409 | EOF | ||
| 7410 | RC=$? | ||
| 7411 | set -e | ||
| 7412 | [ "$RC" -eq 0 ] || { | ||
| 7413 | echo "e2e FAIL: scrollback-rect: ptyclient leg exited $RC (did the focused wheel scroll?):" | ||
| 7414 | cat "$OUT.sb2pc"; exit 1; } | ||
| 7415 | # The neighbour's pin is still the last content painted in its rows: the | ||
| 7416 | # capture holds it, and nothing after it cleared the whole screen. A | ||
| 7417 | # scrollback page that owned the whole terminal (the bug) would open with | ||
| 7418 | # `\x1b[H\x1b[2J` right here, wiping the neighbour's rows; the fix clears | ||
| 7419 | # per row inside the focused tile's sub-rect only. | ||
| 7420 | grep -qaF "sb2nbr" "$OUT.sb2cap" || { | ||
| 7421 | echo "e2e FAIL: scrollback-rect: the neighbour's pin left the capture:" | ||
| 7422 | cat "$OUT.sb2pc"; exit 1; } | ||
| 7423 | _sb2last=$(grep -aboF -- "sb2nbr" "$OUT.sb2cap" | tail -1 | cut -d: -f1) | ||
| 7424 | tail -c +$((_sb2last + 1)) "$OUT.sb2cap" | grep -aF -- "$(printf '\x1b[2J')" && { | ||
| 7425 | echo "e2e FAIL: scrollback-rect: a whole-screen clear followed the neighbour's paint:" | ||
| 7426 | cat "$OUT.sb2pc"; exit 1; } | ||
| 7427 | assert_stopped "$SOCK53" "$D54PID" "scrollback-rect" "$OUT.sb2stop" | ||
| 7428 | D54PID="" | ||
| 7429 | ok "a focused tile's scrollback page owns only its sub-rect, and the neighbour's pin stays" | ||
| 7430 | |||
| 7346 | # The long-lived daemon has served every scenario that wanted it; stop it | 7431 | # The long-lived daemon has served every scenario that wanted it; stop it |
| 7347 | # NOW so its allocator verdict is written while the suite is still running | 7432 | # NOW so its allocator verdict is written while the suite is still running |
| 7348 | # and can say so. SIGTERM runs the clean-shutdown path, so the defer chain | 7433 | # and can say so. SIGTERM runs the clean-shutdown path, so the defer chain |
| @@ -7488,8 +7573,8 @@ DPID="" | |||
| 7488 | # it is the one leg that holds an ssh-agent under SIGSTOP, and a trap that | 7573 | # it is the one leg that holds an ssh-agent under SIGSTOP, and a trap that |
| 7489 | # has to CONT before it kills is cheaper to reason about with nothing | 7574 | # has to CONT before it kills is cheaper to reason about with nothing |
| 7490 | # after it. | 7575 | # after it. |
| 7491 | [ "$OK_COUNT" = "62" ] || { | 7576 | [ "$OK_COUNT" = "63" ] || { |
| 7492 | echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 62 —" | 7577 | echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 63 —" |
| 7493 | echo " a scenario was added (update the pin) or silently lost" | 7578 | echo " a scenario was added (update the pin) or silently lost" |
| 7494 | exit 1 | 7579 | exit 1 |
| 7495 | } | 7580 | } |
| @@ -7497,4 +7582,4 @@ DPID="" | |||
| 7497 | echo "e2e FAIL: $CONV_COUNT convergence points ran, the pin says 37" | 7582 | echo "e2e FAIL: $CONV_COUNT convergence points ran, the pin says 37" |
| 7498 | exit 1 | 7583 | exit 1 |
| 7499 | } | 7584 | } |
| 7500 | echo "e2e OK (61 scenarios, 37 convergence points)" | 7585 | echo "e2e OK (62 scenarios, 37 convergence points)" |