461245b2
test: a tile the zoom is not on paints nothing at all
a73x 2026-08-20 04:19
Commit message
test/e2e.sh
| Old | New | ||
|---|---|---|---|
| @@ -345,6 +345,18 @@ SOCK42="${TMPDIR:-/tmp}/muxd-e2e-zoomappmouse-$$.sock" | |||
| 345 | D39PID="" | 345 | D39PID="" |
| 346 | ZMOUSESH="${TMPDIR:-/tmp}/mux-e2e-zoomappmouse-$$.sh" | 346 | ZMOUSESH="${TMPDIR:-/tmp}/mux-e2e-zoomappmouse-$$.sh" |
| 347 | 347 | ||
| 348 | # A tile the zoom is NOT on. Two daemons because the leg needs two sessions | ||
| 349 | # with DIFFERENT shells — one typed at through the zoom, one blocking on a | ||
| 350 | # flag that shell creates — and a daemon has one shell for all its sessions. | ||
| 351 | SOCK43="${TMPDIR:-/tmp}/muxd-e2e-hidetile-a-$$.sock" | ||
| 352 | D40PID="" | ||
| 353 | ZHASH="${TMPDIR:-/tmp}/mux-e2e-hidetile-a-$$.sh" | ||
| 354 | SOCK44="${TMPDIR:-/tmp}/muxd-e2e-hidetile-b-$$.sock" | ||
| 355 | D41PID="" | ||
| 356 | ZHBSH="${TMPDIR:-/tmp}/mux-e2e-hidetile-b-$$.sh" | ||
| 357 | ZHFLAG="${TMPDIR:-/tmp}/mux-e2e-hidetile-$$.flag" | ||
| 358 | ZHSCRIPT="${TMPDIR:-/tmp}/mux-e2e-hidetile-$$.script" | ||
| 359 | |||
| 348 | # The wall as attach HISTORY (phase 2). A daemon AND a state home of its | 360 | # The wall as attach HISTORY (phase 2). A daemon AND a state home of its |
| 349 | # own, for the dynamic-wall leg's reason turned up one notch: what these | 361 | # own, for the dynamic-wall leg's reason turned up one notch: what these |
| 350 | # two blocks read back is the wall FILE, and every `mux` in this suite now | 362 | # two blocks read back is the wall FILE, and every `mux` in this suite now |
| @@ -983,6 +995,8 @@ cleanup() { | |||
| 983 | [ -n "$D37PID" ] && kill "$D37PID" 2>/dev/null || true | 995 | [ -n "$D37PID" ] && kill "$D37PID" 2>/dev/null || true |
| 984 | [ -n "$D38PID" ] && kill "$D38PID" 2>/dev/null || true | 996 | [ -n "$D38PID" ] && kill "$D38PID" 2>/dev/null || true |
| 985 | [ -n "$D39PID" ] && kill "$D39PID" 2>/dev/null || true | 997 | [ -n "$D39PID" ] && kill "$D39PID" 2>/dev/null || true |
| 998 | [ -n "$D40PID" ] && kill "$D40PID" 2>/dev/null || true | ||
| 999 | [ -n "$D41PID" ] && kill "$D41PID" 2>/dev/null || true | ||
| 986 | # The stops still precede the socket rm below, like SOCK14-17 above: | 1000 | # The stops still precede the socket rm below, like SOCK14-17 above: |
| 987 | # unlinking a socket first would leave a live daemon nothing could reach | 1001 | # unlinking a socket first would leave a live daemon nothing could reach |
| 988 | # by path. | 1002 | # by path. |
| @@ -1007,6 +1021,8 @@ cleanup() { | |||
| 1007 | [ -S "$SOCK40" ] && "$MUXD" stop --sock "$SOCK40" 2>/dev/null || true | 1021 | [ -S "$SOCK40" ] && "$MUXD" stop --sock "$SOCK40" 2>/dev/null || true |
| 1008 | [ -S "$SOCK41" ] && "$MUXD" stop --sock "$SOCK41" 2>/dev/null || true | 1022 | [ -S "$SOCK41" ] && "$MUXD" stop --sock "$SOCK41" 2>/dev/null || true |
| 1009 | [ -S "$SOCK42" ] && "$MUXD" stop --sock "$SOCK42" 2>/dev/null || true | 1023 | [ -S "$SOCK42" ] && "$MUXD" stop --sock "$SOCK42" 2>/dev/null || true |
| 1024 | [ -S "$SOCK43" ] && "$MUXD" stop --sock "$SOCK43" 2>/dev/null || true | ||
| 1025 | [ -S "$SOCK44" ] && "$MUXD" stop --sock "$SOCK44" 2>/dev/null || true | ||
| 1010 | 1026 | ||
| 1011 | # ---- the leak sweep (hygiene kit, 6a) ---- | 1027 | # ---- the leak sweep (hygiene kit, 6a) ---- |
| 1012 | # Here rather than at the bottom of the file, which `set -e` reaches only | 1028 | # Here rather than at the bottom of the file, which `set -e` reaches only |
| @@ -1021,7 +1037,7 @@ cleanup() { | |||
| 1021 | "$D20PID" "$D21PID" "$D22PID" "$D23PID" "$D24PID" "$D25PID" \ | 1037 | "$D20PID" "$D21PID" "$D22PID" "$D23PID" "$D24PID" "$D25PID" \ |
| 1022 | "$D26PID" "$D27PID" "$D28PID" "$D29PID" "$D30PID" "$D31PID" \ | 1038 | "$D26PID" "$D27PID" "$D28PID" "$D29PID" "$D30PID" "$D31PID" \ |
| 1023 | "$D32PID" "$D33PID" "$D34PID" "$D35PID" "$D36PID" "$D37PID" \ | 1039 | "$D32PID" "$D33PID" "$D34PID" "$D35PID" "$D36PID" "$D37PID" \ |
| 1024 | "$D38PID" "$D39PID" | 1040 | "$D38PID" "$D39PID" "$D40PID" "$D41PID" |
| 1025 | _leak=0 | 1041 | _leak=0 |
| 1026 | leak_sweep "$_rc" || _leak=1 | 1042 | leak_sweep "$_rc" || _leak=1 |
| 1027 | 1043 | ||
| @@ -1081,6 +1097,9 @@ cleanup() { | |||
| 1081 | "$OUT.zwcapg" "$OUT.zwstop" "$ZWHEELSH" \ | 1097 | "$OUT.zwcapg" "$OUT.zwstop" "$ZWHEELSH" \ |
| 1082 | "$SOCK42" "$OUT.zm2.d" "$OUT.zm2cap" "$OUT.zm2cap.err" "$OUT.zm2pc" \ | 1098 | "$SOCK42" "$OUT.zm2.d" "$OUT.zm2cap" "$OUT.zm2cap.err" "$OUT.zm2pc" \ |
| 1083 | "$OUT.zm2capg" "$OUT.zm2stop" "$ZMOUSESH" \ | 1099 | "$OUT.zm2capg" "$OUT.zm2stop" "$ZMOUSESH" \ |
| 1100 | "$SOCK43" "$SOCK44" "$OUT.zh.da" "$OUT.zh.db" "$OUT.zhcap" \ | ||
| 1101 | "$OUT.zhcap.err" "$OUT.zhpc" "$OUT.zhstopa" "$OUT.zhstopb" \ | ||
| 1102 | "$ZHASH" "$ZHBSH" "$ZHFLAG" "$ZHSCRIPT" \ | ||
| 1084 | "$SOCK33" "$SOCK34" "$OUT.whlcap" "$OUT.msecap" \ | 1103 | "$SOCK33" "$SOCK34" "$OUT.whlcap" "$OUT.msecap" \ |
| 1085 | "$OUT.whlstop" "$OUT.msestop" | 1104 | "$OUT.whlstop" "$OUT.msestop" |
| 1086 | rm -f "$OUT.pgr" "$OUT.pgr.err" "$OUT.pgr.log" "$OUT.pgr.d" "$LESSSH" \ | 1105 | rm -f "$OUT.pgr" "$OUT.pgr.err" "$OUT.pgr.log" "$OUT.pgr.d" "$LESSSH" \ |
| @@ -5954,6 +5973,117 @@ assert_stopped "$SOCK42" "$D39PID" "zoom app mouse" "$OUT.zm2stop" | |||
| 5954 | D39PID="" | 5973 | D39PID="" |
| 5955 | ok "an application in a zoomed tile gets the wheel, and the tile does not" | 5974 | ok "an application in a zoomed tile gets the wheel, and the tile does not" |
| 5956 | 5975 | ||
| 5976 | # ---- a tile the zoom is not on paints NOTHING -------------------------- | ||
| 5977 | # | ||
| 5978 | # The other half of "zoom is a lens": while one tile owns the terminal, the | ||
| 5979 | # tiles that do not own it must be silent — not merely cropped to their | ||
| 5980 | # stripes, SILENT. A hidden tile's session goes on producing frames the | ||
| 5981 | # whole time (that is the point of keeping every pump hot), and each one is | ||
| 5982 | # a full-screen repaint waiting to happen on somebody else's zoom. | ||
| 5983 | # | ||
| 5984 | # Phase 1 spelled that decision at four call sites inside the pump; phase 3b | ||
| 5985 | # spells it once, in the paint sink every Core paint now asks | ||
| 5986 | # (`tilePaintBegin`). One place is worth a test that fires when it goes | ||
| 5987 | # wrong, and nothing in this suite could see it before: a capture is a byte | ||
| 5988 | # STREAM, so a stripe and a full-screen paint of the same rows leave the | ||
| 5989 | # same bytes, and overpainting is invisible to grep. What IS visible is a | ||
| 5990 | # marker arriving at a moment it should not have arrived at all. | ||
| 5991 | # | ||
| 5992 | # So the leg is a COUNT, and the marker is produced on demand rather than on | ||
| 5993 | # a timer: | ||
| 5994 | # | ||
| 5995 | # * two daemons, two default sessions, one wall. The second session's | ||
| 5996 | # shell blocks on a flag file and prints `bhidden-pin` when it appears. | ||
| 5997 | # * the zoom goes to the FIRST tile, and it is the first session's own | ||
| 5998 | # shell — typed at, through the zoom — that creates the flag. So the | ||
| 5999 | # hidden tile's output happens strictly INSIDE the zoom, with no sleep | ||
| 6000 | # to race and no ordering to hope for. | ||
| 6001 | # * `bhidden-pin` must therefore appear exactly ONCE in this terminal's | ||
| 6002 | # whole life: the stripe repaint after the unzoom, from the replica that | ||
| 6003 | # was hot the entire time. A second occurrence is the hidden tile having | ||
| 6004 | # painted over the session the user was typing at. | ||
| 6005 | # | ||
| 6006 | # The zoomed session gets its own marker in between, so a leg that lost the | ||
| 6007 | # zoom (and would then pass the count for the wrong reason) fails loudly on | ||
| 6008 | # the expect instead. | ||
| 6009 | cat > "$ZHASH" <<'EOF' | ||
| 6010 | #!/bin/sh | ||
| 6011 | printf 'zha-%s\n' pin | ||
| 6012 | exec /bin/sh | ||
| 6013 | EOF | ||
| 6014 | chmod +x "$ZHASH" | ||
| 6015 | # Written with an UNQUOTED heredoc: the flag path carries this run's pid and | ||
| 6016 | # has to be baked in. Nothing else in it is a shell expansion. | ||
| 6017 | cat > "$ZHBSH" <<EOF | ||
| 6018 | #!/bin/sh | ||
| 6019 | printf 'zhb-%s\n' pin | ||
| 6020 | while [ ! -f "$ZHFLAG" ]; do sleep 0.2; done | ||
| 6021 | printf 'bhidden-%s\n' pin | ||
| 6022 | exec /bin/cat | ||
| 6023 | EOF | ||
| 6024 | chmod +x "$ZHBSH" | ||
| 6025 | rm -f "$ZHFLAG" | ||
| 6026 | "$MUXD" run --sock "$SOCK43" --shell "$ZHASH" > "$OUT.zh.da" 2>&1 & | ||
| 6027 | D40PID=$! | ||
| 6028 | wait_sock "$SOCK43" "$OUT.zh.da" "hidden-tile daemon A never bound" | ||
| 6029 | "$MUXD" run --sock "$SOCK44" --shell "$ZHBSH" > "$OUT.zh.db" 2>&1 & | ||
| 6030 | D41PID=$! | ||
| 6031 | wait_sock "$SOCK44" "$OUT.zh.db" "hidden-tile daemon B never bound" | ||
| 6032 | # ONE anchor for the wall, scenario 33's lesson: tile threads race and | ||
| 6033 | # `expect` is a stream cursor, so the second of two markers is buried for | ||
| 6034 | # good whenever it painted first. The settle after it is what says both | ||
| 6035 | # stripes have finished. | ||
| 6036 | # | ||
| 6037 | # The ptyclient script is a FILE rather than a heredoc for the same reason | ||
| 6038 | # the B shell is unquoted: `$ZHFLAG` has to expand, and this suite's | ||
| 6039 | # scenario heredocs are quoted precisely so that backslash escapes reach | ||
| 6040 | # ptyclient untouched. Nothing here needs one that bash would eat. | ||
| 6041 | cat > "$ZHSCRIPT" <<EOF | ||
| 6042 | expect zhb-pin 20000 | ||
| 6043 | settle 900 20000 | ||
| 6044 | send \r | ||
| 6045 | settle 900 20000 | ||
| 6046 | send : > "$ZHFLAG"\n | ||
| 6047 | settle 1500 20000 | ||
| 6048 | send echo zhzoom-ok\n | ||
| 6049 | expect zhzoom-ok 15000 | ||
| 6050 | settle 700 20000 | ||
| 6051 | send \x1cw | ||
| 6052 | expect bhidden-pin 20000 | ||
| 6053 | settle 900 20000 | ||
| 6054 | send q | ||
| 6055 | waitexit 10000 | ||
| 6056 | EOF | ||
| 6057 | set +e | ||
| 6058 | timeout 90 "$PTYCLIENT" --cols 96 --rows 30 --out "$OUT.zhcap" --err "$OUT.zhcap.err" -- \ | ||
| 6059 | "$MUX" wall "--sock $SOCK43" "--sock $SOCK44" > "$OUT.zhpc" 2>&1 < "$ZHSCRIPT" | ||
| 6060 | RC=$? | ||
| 6061 | set -e | ||
| 6062 | [ "$RC" -eq 0 ] || { | ||
| 6063 | echo "e2e FAIL: hidden tile: ptyclient leg exited $RC (did the zoom take, did the flag land?):" | ||
| 6064 | cat "$OUT.zhpc"; exit 1; } | ||
| 6065 | # The flag really was created by the ZOOMED session's shell, so the hidden | ||
| 6066 | # session really did have something to say while it was hidden. Without this | ||
| 6067 | # the count below would pass on a session that simply never printed. | ||
| 6068 | [ -f "$ZHFLAG" ] || { | ||
| 6069 | echo "e2e FAIL: hidden tile: the zoom never created the flag, so the hidden" | ||
| 6070 | echo " session never printed and the count proves nothing:" | ||
| 6071 | cat "$OUT.zhpc"; exit 1; } | ||
| 6072 | # The money assertion. `grep -o`, not `grep -c`: a stripe paint is escape | ||
| 6073 | # sequences with no newlines in them, so a line count reads 1 either way. | ||
| 6074 | _zh_paints=$(grep -oa "bhidden-pin" "$OUT.zhcap" | wc -l) | ||
| 6075 | [ "$_zh_paints" -eq 1 ] || { | ||
| 6076 | echo "e2e FAIL: hidden tile: the hidden session's marker reached this terminal" | ||
| 6077 | echo " $_zh_paints time(s); exactly one — the stripe repaint after the" | ||
| 6078 | echo " unzoom — is the whole budget, and the rest is a tile painting" | ||
| 6079 | echo " over the session the user was typing at:" | ||
| 6080 | cat "$OUT.zhpc"; exit 1; } | ||
| 6081 | assert_stopped "$SOCK43" "$D40PID" "hidden tile A" "$OUT.zhstopa" | ||
| 6082 | D40PID="" | ||
| 6083 | assert_stopped "$SOCK44" "$D41PID" "hidden tile B" "$OUT.zhstopb" | ||
| 6084 | D41PID="" | ||
| 6085 | ok "a tile the zoom is not on paints nothing at all, however loud its session gets" | ||
| 6086 | |||
| 5957 | # ---- a tile whose pump has died still says something --------------------- | 6087 | # ---- a tile whose pump has died still says something --------------------- |
| 5958 | # | 6088 | # |
| 5959 | # Only pumps answer `repaint_gen`, so a tile whose pump has ENDED has | 6089 | # Only pumps answer `repaint_gen`, so a tile whose pump has ENDED has |
| @@ -6386,8 +6516,8 @@ DPID="" | |||
| 6386 | # convergence point either: what it asserts on is that file shrinking by | 6516 | # convergence point either: what it asserts on is that file shrinking by |
| 6387 | # one line while the session it named goes on answering, which is two | 6517 | # one line while the session it named goes on answering, which is two |
| 6388 | # facts a grid comparison speaks to neither of. | 6518 | # facts a grid comparison speaks to neither of. |
| 6389 | [ "$OK_COUNT" = "51" ] || { | 6519 | [ "$OK_COUNT" = "52" ] || { |
| 6390 | echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 51 —" | 6520 | echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 52 —" |
| 6391 | echo " a scenario was added (update the pin) or silently lost" | 6521 | echo " a scenario was added (update the pin) or silently lost" |
| 6392 | exit 1 | 6522 | exit 1 |
| 6393 | } | 6523 | } |
| @@ -6395,4 +6525,4 @@ DPID="" | |||
| 6395 | echo "e2e FAIL: $CONV_COUNT convergence points ran, the pin says 35" | 6525 | echo "e2e FAIL: $CONV_COUNT convergence points ran, the pin says 35" |
| 6396 | exit 1 | 6526 | exit 1 |
| 6397 | } | 6527 | } |
| 6398 | echo "e2e OK (51 scenarios, 35 convergence points)" | 6528 | echo "e2e OK (52 scenarios, 35 convergence points)" |