9b404327
test: e2e walks the ring three sessions wide, both ways
a73x 2026-08-19 17:17
Commit message
test/e2e.sh
| Old | New | ||
|---|---|---|---|
| @@ -258,6 +258,13 @@ D24PID="" | |||
| 258 | SOCK28="${TMPDIR:-/tmp}/muxd-e2e-newsess-$$.sock" | 258 | SOCK28="${TMPDIR:-/tmp}/muxd-e2e-newsess-$$.sock" |
| 259 | D25PID="" | 259 | D25PID="" |
| 260 | 260 | ||
| 261 | # Ctrl-\ n / Ctrl-\ p (step around the ring): its own daemon for the same | ||
| 262 | # reason as SOCK28, and more sharply — the ring IS the list of sessions on | ||
| 263 | # one daemon, so a stray session from another block would change where a | ||
| 264 | # step lands. | ||
| 265 | SOCK29="${TMPDIR:-/tmp}/muxd-e2e-ring-$$.sock" | ||
| 266 | D26PID="" | ||
| 267 | |||
| 261 | # One counter out of a MUX_PREDICT_STATS line. The client prints exactly one | 268 | # One counter out of a MUX_PREDICT_STATS line. The client prints exactly one |
| 262 | # such line on exit; every field is a key=value pair, so a rename or reorder | 269 | # such line on exit; every field is a key=value pair, so a rename or reorder |
| 263 | # in the client shows up here as an empty read rather than a wrong number. | 270 | # in the client shows up here as an empty read rather than a wrong number. |
| @@ -780,6 +787,7 @@ cleanup() { | |||
| 780 | [ -n "$D23PID" ] && kill "$D23PID" 2>/dev/null || true | 787 | [ -n "$D23PID" ] && kill "$D23PID" 2>/dev/null || true |
| 781 | [ -n "$D24PID" ] && kill "$D24PID" 2>/dev/null || true | 788 | [ -n "$D24PID" ] && kill "$D24PID" 2>/dev/null || true |
| 782 | [ -n "$D25PID" ] && kill "$D25PID" 2>/dev/null || true | 789 | [ -n "$D25PID" ] && kill "$D25PID" 2>/dev/null || true |
| 790 | [ -n "$D26PID" ] && kill "$D26PID" 2>/dev/null || true | ||
| 783 | # The stops still precede the socket rm below, like SOCK14-17 above: | 791 | # The stops still precede the socket rm below, like SOCK14-17 above: |
| 784 | # unlinking a socket first would leave a live daemon nothing could reach | 792 | # unlinking a socket first would leave a live daemon nothing could reach |
| 785 | # by path. | 793 | # by path. |
| @@ -790,6 +798,7 @@ cleanup() { | |||
| 790 | [ -S "$SOCK26" ] && "$MUXD" stop --sock "$SOCK26" 2>/dev/null || true | 798 | [ -S "$SOCK26" ] && "$MUXD" stop --sock "$SOCK26" 2>/dev/null || true |
| 791 | [ -S "$SOCK27" ] && "$MUXD" stop --sock "$SOCK27" 2>/dev/null || true | 799 | [ -S "$SOCK27" ] && "$MUXD" stop --sock "$SOCK27" 2>/dev/null || true |
| 792 | [ -S "$SOCK28" ] && "$MUXD" stop --sock "$SOCK28" 2>/dev/null || true | 800 | [ -S "$SOCK28" ] && "$MUXD" stop --sock "$SOCK28" 2>/dev/null || true |
| 801 | [ -S "$SOCK29" ] && "$MUXD" stop --sock "$SOCK29" 2>/dev/null || true | ||
| 793 | 802 | ||
| 794 | # ---- the leak sweep (hygiene kit, 6a) ---- | 803 | # ---- the leak sweep (hygiene kit, 6a) ---- |
| 795 | # Here rather than at the bottom of the file, which `set -e` reaches only | 804 | # Here rather than at the bottom of the file, which `set -e` reaches only |
| @@ -801,7 +810,8 @@ cleanup() { | |||
| 801 | "$D7PID" "$D9PID" "$D10PID" "$D12PID" "$D13PID" "$SPID" "$TPID" \ | 810 | "$D7PID" "$D9PID" "$D10PID" "$D12PID" "$D13PID" "$SPID" "$TPID" \ |
| 802 | "$GPID" "$APID" "$PAPID" "$HAPID" "$HDPID" \ | 811 | "$GPID" "$APID" "$PAPID" "$HAPID" "$HDPID" \ |
| 803 | "$D14PID" "$D15PID" "$D16PID" "$D17PID" "$D18PID" "$D19PID" \ | 812 | "$D14PID" "$D15PID" "$D16PID" "$D17PID" "$D18PID" "$D19PID" \ |
| 804 | "$D20PID" "$D21PID" "$D22PID" "$D23PID" "$D24PID" "$D25PID" | 813 | "$D20PID" "$D21PID" "$D22PID" "$D23PID" "$D24PID" "$D25PID" \ |
| 814 | "$D26PID" | ||
| 805 | _leak=0 | 815 | _leak=0 |
| 806 | leak_sweep "$_rc" || _leak=1 | 816 | leak_sweep "$_rc" || _leak=1 |
| 807 | 817 | ||
| @@ -4582,6 +4592,86 @@ assert_stopped "$SOCK28" "$D25PID" "new session" "$OUT.nswstop" | |||
| 4582 | D25PID="" | 4592 | D25PID="" |
| 4583 | ok "Ctrl-\\ c: a new session is created and switched to, the old one intact; a full daemon refuses and the client stays where it was" | 4593 | ok "Ctrl-\\ c: a new session is created and switched to, the old one intact; a full daemon refuses and the client stays where it was" |
| 4584 | 4594 | ||
| 4595 | # ---- M3: Ctrl-\ n / Ctrl-\ p step around the session ring ---------------- | ||
| 4596 | # | ||
| 4597 | # THREE sessions, not two, and that is the whole point: with two, next and | ||
| 4598 | # previous land on the same place and the leg would pass with the two | ||
| 4599 | # directions swapped. Three makes a wrong direction land on a different | ||
| 4600 | # marker. | ||
| 4601 | # | ||
| 4602 | # Each session is given its own marker before the stepping starts, so the | ||
| 4603 | # ring's answer is read off the SCREEN rather than off a name: after a | ||
| 4604 | # step, the marker that arrives says which grid the daemon just sent. | ||
| 4605 | # | ||
| 4606 | # * `\x1cn` from the last slot must wrap to the first — m3a-pin, the | ||
| 4607 | # default session's marker. A step in the other direction would bring | ||
| 4608 | # m3b-pin, and the expect would time out on it. | ||
| 4609 | # * `\x1cp` from the first slot must wrap back to the last — m3c-pin. | ||
| 4610 | # | ||
| 4611 | # The markers arrive a second time because a switch is an attach and an | ||
| 4612 | # attach repaints a snapshot; ptyclient's expect cursor consumes matches, | ||
| 4613 | # so the m3a-pin the leg waited for at the top cannot satisfy the one it | ||
| 4614 | # waits for after the wrap. | ||
| 4615 | # | ||
| 4616 | # The captures afterwards pin which marker lives where — three sessions | ||
| 4617 | # that each kept their own line, so the stepping moved the CLIENT and left | ||
| 4618 | # the sessions where they were. | ||
| 4619 | "$MUXD" run --sock "$SOCK29" --shell /bin/sh > "$OUT.ring.d" 2>&1 & | ||
| 4620 | D26PID=$! | ||
| 4621 | wait_sock "$SOCK29" "$OUT.ring.d" "session-ring daemon never bound" | ||
| 4622 | |||
| 4623 | set +e | ||
| 4624 | timeout 60 "$PTYCLIENT" --cols 80 --rows 24 --out "$OUT.ring" --err "$OUT.ring.err" \ | ||
| 4625 | -- "$MUX" --sock "$SOCK29" > "$OUT.ring.log" 2>&1 <<'EOF' | ||
| 4626 | expect \x1b[?1049h 15000 | ||
| 4627 | settle 400 15000 | ||
| 4628 | send printf 'm3a-%s\\n' pin\n | ||
| 4629 | expect m3a-pin 15000 | ||
| 4630 | settle 400 15000 | ||
| 4631 | send \x1cc | ||
| 4632 | expect \x1b[?1049h 15000 | ||
| 4633 | settle 400 15000 | ||
| 4634 | send printf 'm3b-%s\\n' pin\n | ||
| 4635 | expect m3b-pin 15000 | ||
| 4636 | settle 400 15000 | ||
| 4637 | send \x1cc | ||
| 4638 | expect \x1b[?1049h 15000 | ||
| 4639 | settle 400 15000 | ||
| 4640 | send printf 'm3c-%s\\n' pin\n | ||
| 4641 | expect m3c-pin 15000 | ||
| 4642 | settle 400 15000 | ||
| 4643 | send \x1cn | ||
| 4644 | expect \x1b[?1049h 15000 | ||
| 4645 | expect m3a-pin 15000 | ||
| 4646 | settle 400 15000 | ||
| 4647 | send \x1cp | ||
| 4648 | expect \x1b[?1049h 15000 | ||
| 4649 | expect m3c-pin 15000 | ||
| 4650 | settle 400 15000 | ||
| 4651 | send \x1c\x1c | ||
| 4652 | waitexit 10000 | ||
| 4653 | EOF | ||
| 4654 | RC=$? | ||
| 4655 | set -e | ||
| 4656 | [ "$RC" -eq 0 ] || { | ||
| 4657 | echo "e2e FAIL: session ring: ptyclient leg exited $RC (did \\x1cn wrap?):" | ||
| 4658 | cat "$OUT.ring.log"; exit 1; } | ||
| 4659 | for s in 0:m3a 1:m3b 2:m3c; do | ||
| 4660 | _sess="${s%%:*}"; _mark="${s##*:}-pin" | ||
| 4661 | timeout 20 "$MUXA" capture --sock "$SOCK29" --session "$_sess" > "$OUT.ringcap" 2>&1 | ||
| 4662 | grep -q "$_mark" "$OUT.ringcap" || { | ||
| 4663 | echo "e2e FAIL: session ring: session $_sess does not hold $_mark:" | ||
| 4664 | cat "$OUT.ringcap"; exit 1; } | ||
| 4665 | # Its neighbours' markers are not in it: the client moved between | ||
| 4666 | # sessions, it did not drag one session's shell along behind it. | ||
| 4667 | grep -v "$_mark" "$OUT.ringcap" | grep -qE 'm3[abc]-pin' && { | ||
| 4668 | echo "e2e FAIL: session ring: session $_sess holds another session's marker:" | ||
| 4669 | cat "$OUT.ringcap"; exit 1; } | ||
| 4670 | done | ||
| 4671 | assert_stopped "$SOCK29" "$D26PID" "session ring" "$OUT.ringstop" | ||
| 4672 | D26PID="" | ||
| 4673 | ok "Ctrl-\\ n / Ctrl-\\ p: the ring steps both ways and wraps at both ends" | ||
| 4674 | |||
| 4585 | # The long-lived daemon has served every scenario that wanted it; stop it | 4675 | # The long-lived daemon has served every scenario that wanted it; stop it |
| 4586 | # NOW so its allocator verdict is written while the suite is still running | 4676 | # NOW so its allocator verdict is written while the suite is still running |
| 4587 | # and can say so. SIGTERM runs the clean-shutdown path, so the defer chain | 4677 | # and can say so. SIGTERM runs the clean-shutdown path, so the defer chain |
| @@ -4597,7 +4687,7 @@ DPID="" | |||
| 4597 | 4687 | ||
| 4598 | # The pins. Literals, not variables set from counting something else — | 4688 | # The pins. Literals, not variables set from counting something else — |
| 4599 | # "assert the literal, never the constant the code under test reads" | 4689 | # "assert the literal, never the constant the code under test reads" |
| 4600 | # (decisions.md, M10). 35 scenario checkpoints; 35 convergence points. | 4690 | # (decisions.md, M10). 36 scenario checkpoints; 35 convergence points. |
| 4601 | # Anyone adding a scenario updates these by hand, on purpose. | 4691 | # Anyone adding a scenario updates these by hand, on purpose. |
| 4602 | # | 4692 | # |
| 4603 | # M18 added three checkpoints and no convergence points: its wall block | 4693 | # M18 added three checkpoints and no convergence points: its wall block |
| @@ -4626,9 +4716,11 @@ DPID="" | |||
| 4626 | # that never saw the bytes cannot testify to either way. The 35th is | 4716 | # that never saw the bytes cannot testify to either way. The 35th is |
| 4627 | # Ctrl-\ c, and no convergence point either: what it compares is TWO | 4717 | # Ctrl-\ c, and no convergence point either: what it compares is TWO |
| 4628 | # sessions' grids against each other — one holding a marker, one refusing | 4718 | # sessions' grids against each other — one holding a marker, one refusing |
| 4629 | # to — and assert_converged speaks about one session at a time. | 4719 | # to — and assert_converged speaks about one session at a time. The 36th is |
| 4630 | [ "$OK_COUNT" = "35" ] || { | 4720 | # the session ring, and no convergence point for that same reason a second |
| 4631 | echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 35 —" | 4721 | # time: it compares THREE sessions' grids against each other. |
| 4722 | [ "$OK_COUNT" = "36" ] || { | ||
| 4723 | echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 36 —" | ||
| 4632 | echo " a scenario was added (update the pin) or silently lost" | 4724 | echo " a scenario was added (update the pin) or silently lost" |
| 4633 | exit 1 | 4725 | exit 1 |
| 4634 | } | 4726 | } |
| @@ -4636,4 +4728,4 @@ DPID="" | |||
| 4636 | echo "e2e FAIL: $CONV_COUNT convergence points ran, the pin says 35" | 4728 | echo "e2e FAIL: $CONV_COUNT convergence points ran, the pin says 35" |
| 4637 | exit 1 | 4729 | exit 1 |
| 4638 | } | 4730 | } |
| 4639 | echo "e2e OK (35 scenarios, 35 convergence points)" | 4731 | echo "e2e OK (36 scenarios, 35 convergence points)" |