8d60e36e
test: e2e zooms the second stripe and types into that session
a73x 2026-08-19 17:17
Commit message
test/e2e.sh
| Old | New | ||
|---|---|---|---|
| @@ -279,6 +279,13 @@ D27PID="" | |||
| 279 | SOCK31="${TMPDIR:-/tmp}/muxd-e2e-selfattach-$$.sock" | 279 | SOCK31="${TMPDIR:-/tmp}/muxd-e2e-selfattach-$$.sock" |
| 280 | D28PID="" | 280 | D28PID="" |
| 281 | 281 | ||
| 282 | # The wall's zoom: its own daemon because the leg asserts on a session's | ||
| 283 | # GRID SIZE — the zoomed client claims it, the tiles must not — and a | ||
| 284 | # session another block created at another size would make that number | ||
| 285 | # say nothing about this one. | ||
| 286 | SOCK32="${TMPDIR:-/tmp}/muxd-e2e-wallzoom-$$.sock" | ||
| 287 | D29PID="" | ||
| 288 | |||
| 282 | # One counter out of a MUX_PREDICT_STATS line. The client prints exactly one | 289 | # One counter out of a MUX_PREDICT_STATS line. The client prints exactly one |
| 283 | # such line on exit; every field is a key=value pair, so a rename or reorder | 290 | # such line on exit; every field is a key=value pair, so a rename or reorder |
| 284 | # in the client shows up here as an empty read rather than a wrong number. | 291 | # in the client shows up here as an empty read rather than a wrong number. |
| @@ -804,6 +811,7 @@ cleanup() { | |||
| 804 | [ -n "$D26PID" ] && kill "$D26PID" 2>/dev/null || true | 811 | [ -n "$D26PID" ] && kill "$D26PID" 2>/dev/null || true |
| 805 | [ -n "$D27PID" ] && kill "$D27PID" 2>/dev/null || true | 812 | [ -n "$D27PID" ] && kill "$D27PID" 2>/dev/null || true |
| 806 | [ -n "$D28PID" ] && kill "$D28PID" 2>/dev/null || true | 813 | [ -n "$D28PID" ] && kill "$D28PID" 2>/dev/null || true |
| 814 | [ -n "$D29PID" ] && kill "$D29PID" 2>/dev/null || true | ||
| 807 | # The stops still precede the socket rm below, like SOCK14-17 above: | 815 | # The stops still precede the socket rm below, like SOCK14-17 above: |
| 808 | # unlinking a socket first would leave a live daemon nothing could reach | 816 | # unlinking a socket first would leave a live daemon nothing could reach |
| 809 | # by path. | 817 | # by path. |
| @@ -817,6 +825,7 @@ cleanup() { | |||
| 817 | [ -S "$SOCK29" ] && "$MUXD" stop --sock "$SOCK29" 2>/dev/null || true | 825 | [ -S "$SOCK29" ] && "$MUXD" stop --sock "$SOCK29" 2>/dev/null || true |
| 818 | [ -S "$SOCK30" ] && "$MUXD" stop --sock "$SOCK30" 2>/dev/null || true | 826 | [ -S "$SOCK30" ] && "$MUXD" stop --sock "$SOCK30" 2>/dev/null || true |
| 819 | [ -S "$SOCK31" ] && "$MUXD" stop --sock "$SOCK31" 2>/dev/null || true | 827 | [ -S "$SOCK31" ] && "$MUXD" stop --sock "$SOCK31" 2>/dev/null || true |
| 828 | [ -S "$SOCK32" ] && "$MUXD" stop --sock "$SOCK32" 2>/dev/null || true | ||
| 820 | 829 | ||
| 821 | # ---- the leak sweep (hygiene kit, 6a) ---- | 830 | # ---- the leak sweep (hygiene kit, 6a) ---- |
| 822 | # Here rather than at the bottom of the file, which `set -e` reaches only | 831 | # Here rather than at the bottom of the file, which `set -e` reaches only |
| @@ -829,7 +838,7 @@ cleanup() { | |||
| 829 | "$GPID" "$APID" "$PAPID" "$HAPID" "$HDPID" \ | 838 | "$GPID" "$APID" "$PAPID" "$HAPID" "$HDPID" \ |
| 830 | "$D14PID" "$D15PID" "$D16PID" "$D17PID" "$D18PID" "$D19PID" \ | 839 | "$D14PID" "$D15PID" "$D16PID" "$D17PID" "$D18PID" "$D19PID" \ |
| 831 | "$D20PID" "$D21PID" "$D22PID" "$D23PID" "$D24PID" "$D25PID" \ | 840 | "$D20PID" "$D21PID" "$D22PID" "$D23PID" "$D24PID" "$D25PID" \ |
| 832 | "$D26PID" "$D27PID" "$D28PID" | 841 | "$D26PID" "$D27PID" "$D28PID" "$D29PID" |
| 833 | _leak=0 | 842 | _leak=0 |
| 834 | leak_sweep "$_rc" || _leak=1 | 843 | leak_sweep "$_rc" || _leak=1 |
| 835 | 844 | ||
| @@ -4886,6 +4895,124 @@ assert_stopped "$SOCK31" "$D28PID" "self attach" "$OUT.sastop" | |||
| 4886 | D28PID="" | 4895 | D28PID="" |
| 4887 | ok "a session shell carries MUX_SOCK/MUX_SESSION, and mux refuses to attach to itself" | 4896 | ok "a session shell carries MUX_SOCK/MUX_SESSION, and mux refuses to attach to itself" |
| 4888 | 4897 | ||
| 4898 | # ---- the wall zooms: Enter types into the selected session -------------- | ||
| 4899 | # | ||
| 4900 | # Scenario 33 proved the wall WATCHES two sessions. This proves the wall | ||
| 4901 | # can be typed through without ever forwarding a keystroke itself: `2` | ||
| 4902 | # selects the second stripe, `Enter` spawns a real client on this | ||
| 4903 | # terminal, and what is typed there is the shell's, not the wall's. | ||
| 4904 | # | ||
| 4905 | # Four claims, four witnesses: | ||
| 4906 | # | ||
| 4907 | # * the selection moved — b's label bar carries the `> ` marker, which | ||
| 4908 | # it cannot have before the `2` (a's is marked from the start, so only | ||
| 4909 | # b's bar is evidence of a move). | ||
| 4910 | # * the zoom typed into the SELECTED session — b's grid holds wzoom-pin | ||
| 4911 | # and a's does not. A shell-EXPANDED marker (the M18 trick), so a hit | ||
| 4912 | # is the shell's work and never an echo of what this script sent. | ||
| 4913 | # * the zoom was a REAL attach, not a tile that started forwarding — | ||
| 4914 | # b's grid is 100 cols wide, this pty's size, claimed the legitimate | ||
| 4915 | # way. a's is still 80: the wall's own tiles attach 0x0 and claimed | ||
| 4916 | # nothing, scenario 33's passivity check made again with a full-size | ||
| 4917 | # client in the same process tree. | ||
| 4918 | # * the wall came BACK — session A's marker paints a SECOND time after | ||
| 4919 | # the detach, asserted both in order (the expect) and by COUNT (twice | ||
| 4920 | # in the capture). Session a is the tile the zoom never touched: | ||
| 4921 | # nothing was typed at it, its grid was never resized, so no frame | ||
| 4922 | # arrives to trigger a paint and only the stale-generation repaint | ||
| 4923 | # the zoom leaves behind can put those bytes on the screen again. | ||
| 4924 | # Asserted on A rather than on the zoomed session for exactly that | ||
| 4925 | # reason — measured, not assumed: with the generation bump removed, | ||
| 4926 | # b's stripe repaints anyway (the client that detached moved b's | ||
| 4927 | # world), and a leg anchored on b passes with the lever gone. | ||
| 4928 | # | ||
| 4929 | # The wall is entered on ONE expect, not one per tile, for scenario 33's | ||
| 4930 | # reason learned here the hard way: tile threads race, `expect` is a | ||
| 4931 | # stream cursor, and the SECOND of two markers is buried for good whenever | ||
| 4932 | # it painted first — a leg that then hangs on a wall that painted | ||
| 4933 | # perfectly. One anchor cannot be buried, and the settle after it is what | ||
| 4934 | # says both stripes have finished. Only ONE stripe is expected after the | ||
| 4935 | # detach for the same reason. | ||
| 4936 | # | ||
| 4937 | # Both `\x1b[?1049h` expects are alternate-screen TRANSITIONS, not the | ||
| 4938 | # wall's own entry: the first is the zoomed client's (the wall left the | ||
| 4939 | # alternate screen to hand over a sane tty), the second is the wall | ||
| 4940 | # re-entering after `\x1cd`. Anchoring on them rather than on a settle is | ||
| 4941 | # what keeps the leg honest while a terminal is torn down and rebuilt | ||
| 4942 | # twice. | ||
| 4943 | "$MUXD" run --sock "$SOCK32" --shell /bin/sh > "$OUT.wz.d" 2>&1 & | ||
| 4944 | D29PID=$! | ||
| 4945 | wait_sock "$SOCK32" "$OUT.wz.d" "wall-zoom daemon never bound" | ||
| 4946 | |||
| 4947 | # Two sessions, each with its own marker, created exactly as scenario 33 | ||
| 4948 | # creates them: no tty on either client, so both grids are the default 80 | ||
| 4949 | # wide — the number the passivity check reads back later. | ||
| 4950 | { printf 'printf "wza-%%s\\n" pin\n'; sleep 2; printf '\034\034'; } | \ | ||
| 4951 | timeout 40 "$MUX" --sock "$SOCK32" --session a > "$OUT.wza" 2> "$OUT.wza.err" | ||
| 4952 | wait_grid "$SOCK32" "wza-pin" "wall zoom: session a's marker" a | ||
| 4953 | { printf 'printf "wzb-%%s\\n" pin\n'; sleep 2; printf '\034\034'; } | \ | ||
| 4954 | timeout 40 "$MUX" --sock "$SOCK32" --session b > "$OUT.wzb" 2> "$OUT.wzb.err" | ||
| 4955 | wait_grid "$SOCK32" "wzb-pin" "wall zoom: session b's marker" b | ||
| 4956 | |||
| 4957 | set +e | ||
| 4958 | timeout 90 "$PTYCLIENT" --cols 100 --rows 30 --out "$OUT.wzcap" --err "$OUT.wzcap.err" -- \ | ||
| 4959 | "$MUX" wall "--sock $SOCK32#a" "--sock $SOCK32#b" > "$OUT.wzpc" 2>&1 <<'EOF' | ||
| 4960 | expect wzb-pin 20000 | ||
| 4961 | settle 700 20000 | ||
| 4962 | send 2 | ||
| 4963 | settle 500 15000 | ||
| 4964 | send \r | ||
| 4965 | expect \x1b[?1049h 15000 | ||
| 4966 | settle 700 20000 | ||
| 4967 | send printf 'wzoom-%s\\n' pin\n | ||
| 4968 | expect wzoom-pin 15000 | ||
| 4969 | settle 400 15000 | ||
| 4970 | send \x1cd | ||
| 4971 | expect \x1b[?1049h 15000 | ||
| 4972 | expect wza-pin 20000 | ||
| 4973 | settle 500 15000 | ||
| 4974 | send q | ||
| 4975 | waitexit 10000 | ||
| 4976 | EOF | ||
| 4977 | RC=$? | ||
| 4978 | set -e | ||
| 4979 | [ "$RC" -eq 0 ] || { | ||
| 4980 | echo "e2e FAIL: wall zoom: ptyclient leg exited $RC (did \\r zoom, did \\x1cd come back?):" | ||
| 4981 | cat "$OUT.wzpc"; exit 1; } | ||
| 4982 | # The move itself, on bytes only a selected bar can carry. | ||
| 4983 | grep -q -- "> --sock $SOCK32#b" "$OUT.wzcap" || { | ||
| 4984 | echo "e2e FAIL: wall zoom: session b's bar never showed the selection marker"; exit 1; } | ||
| 4985 | # The repaint counted rather than merely ordered: a's marker is on this | ||
| 4986 | # terminal twice, once for the stripe's own paint and once for the one the | ||
| 4987 | # zoom's exit demanded. `grep -o`, not `grep -c`: a stripe paint is escape | ||
| 4988 | # sequences with no newlines in them, so a line count reads 1 either way. | ||
| 4989 | _wza_paints=$(grep -o "wza-pin" "$OUT.wzcap" | wc -l) | ||
| 4990 | [ "$_wza_paints" -ge 2 ] || { | ||
| 4991 | echo "e2e FAIL: wall zoom: session a's stripe painted $_wza_paints time(s), so the wall never came back:" | ||
| 4992 | cat "$OUT.wzpc"; exit 1; } | ||
| 4993 | # What the user typed landed in the session the digit picked, and only | ||
| 4994 | # there. The negative is the whole point of the digit. | ||
| 4995 | timeout 20 "$MUXA" capture --sock "$SOCK32" --session b > "$OUT.wzcapb" 2>&1 | ||
| 4996 | grep -q "wzoom-pin" "$OUT.wzcapb" || { | ||
| 4997 | echo "e2e FAIL: wall zoom: session b never got what the zoom typed:" | ||
| 4998 | cat "$OUT.wzcapb"; exit 1; } | ||
| 4999 | timeout 20 "$MUXA" capture --sock "$SOCK32" --session a > "$OUT.wzcapa" 2>&1 | ||
| 5000 | grep -q "wzoom-pin" "$OUT.wzcapa" && { | ||
| 5001 | echo "e2e FAIL: wall zoom: the zoom typed into session a, which was not selected:" | ||
| 5002 | cat "$OUT.wzcapa"; exit 1; } | ||
| 5003 | # The size split: the zoom claimed the grid, the tiles never did. | ||
| 5004 | timeout 20 "$MUXA" status --sock "$SOCK32" --session b > "$OUT.wzstb" 2>&1 | ||
| 5005 | grep -q '"cols":100' "$OUT.wzstb" || { | ||
| 5006 | echo "e2e FAIL: wall zoom: the zoomed client did not claim session b's grid:" | ||
| 5007 | cat "$OUT.wzstb"; exit 1; } | ||
| 5008 | timeout 20 "$MUXA" status --sock "$SOCK32" --session a > "$OUT.wzsta" 2>&1 | ||
| 5009 | grep -q '"cols":80' "$OUT.wzsta" || { | ||
| 5010 | echo "e2e FAIL: wall zoom: a 0x0 wall attach moved session a's grid:" | ||
| 5011 | cat "$OUT.wzsta"; exit 1; } | ||
| 5012 | assert_stopped "$SOCK32" "$D29PID" "wall zoom" "$OUT.wzstop" | ||
| 5013 | D29PID="" | ||
| 5014 | ok "the wall zooms: Enter types into the selected session, Ctrl-\\ d comes back" | ||
| 5015 | |||
| 4889 | # The long-lived daemon has served every scenario that wanted it; stop it | 5016 | # The long-lived daemon has served every scenario that wanted it; stop it |
| 4890 | # NOW so its allocator verdict is written while the suite is still running | 5017 | # NOW so its allocator verdict is written while the suite is still running |
| 4891 | # and can say so. SIGTERM runs the clean-shutdown path, so the defer chain | 5018 | # and can say so. SIGTERM runs the clean-shutdown path, so the defer chain |
| @@ -4901,7 +5028,7 @@ DPID="" | |||
| 4901 | 5028 | ||
| 4902 | # The pins. Literals, not variables set from counting something else — | 5029 | # The pins. Literals, not variables set from counting something else — |
| 4903 | # "assert the literal, never the constant the code under test reads" | 5030 | # "assert the literal, never the constant the code under test reads" |
| 4904 | # (decisions.md, M10). 38 scenario checkpoints; 35 convergence points. | 5031 | # (decisions.md, M10). 39 scenario checkpoints; 35 convergence points. |
| 4905 | # Anyone adding a scenario updates these by hand, on purpose. | 5032 | # Anyone adding a scenario updates these by hand, on purpose. |
| 4906 | # | 5033 | # |
| 4907 | # M18 added three checkpoints and no convergence points: its wall block | 5034 | # M18 added three checkpoints and no convergence points: its wall block |
| @@ -4938,9 +5065,14 @@ DPID="" | |||
| 4938 | # which no dump of one grid can witness. The 38th is the self-attach | 5065 | # which no dump of one grid can witness. The 38th is the self-attach |
| 4939 | # refusal, and no convergence point because it has no client to converge: | 5066 | # refusal, and no convergence point because it has no client to converge: |
| 4940 | # the leg exists to prove no client was ever created, and its evidence is a | 5067 | # the leg exists to prove no client was ever created, and its evidence is a |
| 4941 | # status field and a shell's exit code, neither of which is a grid. | 5068 | # status field and a shell's exit code, neither of which is a grid. The |
| 4942 | [ "$OK_COUNT" = "38" ] || { | 5069 | # 39th is the wall's zoom, and no convergence point for the CLI wall's |
| 4943 | echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 38 —" | 5070 | # reason a third time: its subject is a ptyclient capture — one terminal |
| 5071 | # torn down and rebuilt around a child client — and the two grid checks it | ||
| 5072 | # does make are `muxa status` sizes, which assert_converged does not speak | ||
| 5073 | # about at all. | ||
| 5074 | [ "$OK_COUNT" = "39" ] || { | ||
| 5075 | echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 39 —" | ||
| 4944 | echo " a scenario was added (update the pin) or silently lost" | 5076 | echo " a scenario was added (update the pin) or silently lost" |
| 4945 | exit 1 | 5077 | exit 1 |
| 4946 | } | 5078 | } |
| @@ -4948,4 +5080,4 @@ DPID="" | |||
| 4948 | echo "e2e FAIL: $CONV_COUNT convergence points ran, the pin says 35" | 5080 | echo "e2e FAIL: $CONV_COUNT convergence points ran, the pin says 35" |
| 4949 | exit 1 | 5081 | exit 1 |
| 4950 | } | 5082 | } |
| 4951 | echo "e2e OK (38 scenarios, 35 convergence points)" | 5083 | echo "e2e OK (39 scenarios, 35 convergence points)" |