96378b1a
test: the wheel works in a zoomed tile, and the demote gives it back
a73x 2026-08-20 04:19
Commit message
src/interact.zig
| Old | New | ||
|---|---|---|---|
| @@ -2551,6 +2551,18 @@ test "interact: a borrowed terminal's claim is the client's, and every teardown | |||
| 2551 | } | 2551 | } |
| 2552 | } | 2552 | } |
| 2553 | 2553 | ||
| 2554 | /// Everything waiting on a NON-BLOCKING pipe, as one slice. One escape | ||
| 2555 | /// sequence per `write` is how these are emitted, so a reader that took the | ||
| 2556 | /// first write for the whole answer would pin half a pair. | ||
| 2557 | fn drainPipe(fd: std.posix.fd_t, buf: []u8) []const u8 { | ||
| 2558 | var n: usize = 0; | ||
| 2559 | while (std.posix.read(fd, buf[n..])) |got| { | ||
| 2560 | if (got == 0) break; | ||
| 2561 | n += got; | ||
| 2562 | } else |_| {} | ||
| 2563 | return buf[0..n]; | ||
| 2564 | } | ||
| 2565 | |||
| 2554 | test "interact: a promote takes the mouse, a demote gives it back, a demote twice writes nothing" { | 2566 | test "interact: a promote takes the mouse, a demote gives it back, a demote twice writes nothing" { |
| 2555 | const alloc = std.testing.allocator; | 2567 | const alloc = std.testing.allocator; |
| 2556 | // Non-blocking, so a half of the pair that stopped writing FAILS here | 2568 | // Non-blocking, so a half of the pair that stopped writing FAILS here |
| @@ -2569,11 +2581,18 @@ test "interact: a promote takes the mouse, a demote gives it back, a demote twic | |||
| 2569 | core.is_tty = true; | 2581 | core.is_tty = true; |
| 2570 | defer core.deinit(); | 2582 | defer core.deinit(); |
| 2571 | 2583 | ||
| 2572 | var buf: [256]u8 = undefined; | 2584 | var buf: [512]u8 = undefined; |
| 2573 | 2585 | ||
| 2574 | try std.testing.expect(core.claimTerminal()); | 2586 | try std.testing.expect(core.claimTerminal()); |
| 2575 | try std.testing.expectEqual(Claim.session, core.claim); | 2587 | try std.testing.expectEqual(Claim.session, core.claim); |
| 2576 | try std.testing.expectEqualStrings(session_claim, buf[0..try std.posix.read(p[0], &buf)]); | 2588 | // Two writes, drained as one: the claim itself and then the SESSION's |
| 2589 | // modes level-set on top of it. | ||
| 2590 | const claimed = drainPipe(p[0], &buf); | ||
| 2591 | try std.testing.expect(std.mem.startsWith(u8, claimed, session_claim)); | ||
| 2592 | // A mode the claim never names, so its presence can only be the | ||
| 2593 | // level-set — the half a promote needs because `resyncSnapshot` answers | ||
| 2594 | // a resize with no `term_modes` at all. | ||
| 2595 | try std.testing.expect(std.mem.indexOf(u8, claimed, "\x1b[?1002l") != null); | ||
| 2577 | 2596 | ||
| 2578 | // A promote onto the tile already zoomed re-asserts the grid, not the | 2597 | // A promote onto the tile already zoomed re-asserts the grid, not the |
| 2579 | // modes: nothing is written a second time, so nothing has to come off | 2598 | // modes: nothing is written a second time, so nothing has to come off |
| @@ -2583,7 +2602,7 @@ test "interact: a promote takes the mouse, a demote gives it back, a demote twic | |||
| 2583 | 2602 | ||
| 2584 | core.releaseTerminal(.write); | 2603 | core.releaseTerminal(.write); |
| 2585 | try std.testing.expectEqual(Claim.none, core.claim); | 2604 | try std.testing.expectEqual(Claim.none, core.claim); |
| 2586 | try std.testing.expectEqualStrings(session_release, buf[0..try std.posix.read(p[0], &buf)]); | 2605 | try std.testing.expectEqualStrings(session_release, drainPipe(p[0], &buf)); |
| 2587 | 2606 | ||
| 2588 | // A release with nothing claimed writes nothing. That is what makes | 2607 | // A release with nothing claimed writes nothing. That is what makes |
| 2589 | // `deinit` safe on a tile nobody ever zoomed — and what stops a wall | 2608 | // `deinit` safe on a tile nobody ever zoomed — and what stops a wall |
test/e2e.sh
| Old | New | ||
|---|---|---|---|
| @@ -332,6 +332,19 @@ D35PID="" | |||
| 332 | SOCK39="${TMPDIR:-/tmp}/muxd-e2e-zoomdead-$$.sock" | 332 | SOCK39="${TMPDIR:-/tmp}/muxd-e2e-zoomdead-$$.sock" |
| 333 | D36PID="" | 333 | D36PID="" |
| 334 | 334 | ||
| 335 | # The wheel inside a zoom (phase 3b). Two daemons for the wheel pair's own | ||
| 336 | # reason, turned up one notch: each needs a session whose SHELL is fixed for | ||
| 337 | # the session's whole life — one that scrolls and one that ASKS for the | ||
| 338 | # mouse — and a mode another scenario's session set would follow the wall's | ||
| 339 | # terminal around. Both use the daemon's DEFAULT session, so the wall | ||
| 340 | # spelling is a bare `--sock PATH` and nothing has to create anything. | ||
| 341 | SOCK41="${TMPDIR:-/tmp}/muxd-e2e-zoomwheel-$$.sock" | ||
| 342 | D38PID="" | ||
| 343 | ZWHEELSH="${TMPDIR:-/tmp}/mux-e2e-zoomwheel-$$.sh" | ||
| 344 | SOCK42="${TMPDIR:-/tmp}/muxd-e2e-zoomappmouse-$$.sock" | ||
| 345 | D39PID="" | ||
| 346 | ZMOUSESH="${TMPDIR:-/tmp}/mux-e2e-zoomappmouse-$$.sh" | ||
| 347 | |||
| 335 | # The wall as attach HISTORY (phase 2). A daemon AND a state home of its | 348 | # The wall as attach HISTORY (phase 2). A daemon AND a state home of its |
| 336 | # own, for the dynamic-wall leg's reason turned up one notch: what these | 349 | # own, for the dynamic-wall leg's reason turned up one notch: what these |
| 337 | # two blocks read back is the wall FILE, and every `mux` in this suite now | 350 | # two blocks read back is the wall FILE, and every `mux` in this suite now |
| @@ -968,6 +981,8 @@ cleanup() { | |||
| 968 | [ -n "$D35PID" ] && kill "$D35PID" 2>/dev/null || true | 981 | [ -n "$D35PID" ] && kill "$D35PID" 2>/dev/null || true |
| 969 | [ -n "$D36PID" ] && kill "$D36PID" 2>/dev/null || true | 982 | [ -n "$D36PID" ] && kill "$D36PID" 2>/dev/null || true |
| 970 | [ -n "$D37PID" ] && kill "$D37PID" 2>/dev/null || true | 983 | [ -n "$D37PID" ] && kill "$D37PID" 2>/dev/null || true |
| 984 | [ -n "$D38PID" ] && kill "$D38PID" 2>/dev/null || true | ||
| 985 | [ -n "$D39PID" ] && kill "$D39PID" 2>/dev/null || true | ||
| 971 | # The stops still precede the socket rm below, like SOCK14-17 above: | 986 | # The stops still precede the socket rm below, like SOCK14-17 above: |
| 972 | # unlinking a socket first would leave a live daemon nothing could reach | 987 | # unlinking a socket first would leave a live daemon nothing could reach |
| 973 | # by path. | 988 | # by path. |
| @@ -990,6 +1005,8 @@ cleanup() { | |||
| 990 | [ -S "$SOCK38" ] && "$MUXD" stop --sock "$SOCK38" 2>/dev/null || true | 1005 | [ -S "$SOCK38" ] && "$MUXD" stop --sock "$SOCK38" 2>/dev/null || true |
| 991 | [ -S "$SOCK39" ] && "$MUXD" stop --sock "$SOCK39" 2>/dev/null || true | 1006 | [ -S "$SOCK39" ] && "$MUXD" stop --sock "$SOCK39" 2>/dev/null || true |
| 992 | [ -S "$SOCK40" ] && "$MUXD" stop --sock "$SOCK40" 2>/dev/null || true | 1007 | [ -S "$SOCK40" ] && "$MUXD" stop --sock "$SOCK40" 2>/dev/null || true |
| 1008 | [ -S "$SOCK41" ] && "$MUXD" stop --sock "$SOCK41" 2>/dev/null || true | ||
| 1009 | [ -S "$SOCK42" ] && "$MUXD" stop --sock "$SOCK42" 2>/dev/null || true | ||
| 993 | 1010 | ||
| 994 | # ---- the leak sweep (hygiene kit, 6a) ---- | 1011 | # ---- the leak sweep (hygiene kit, 6a) ---- |
| 995 | # Here rather than at the bottom of the file, which `set -e` reaches only | 1012 | # Here rather than at the bottom of the file, which `set -e` reaches only |
| @@ -1003,7 +1020,8 @@ cleanup() { | |||
| 1003 | "$D14PID" "$D15PID" "$D16PID" "$D17PID" "$D18PID" "$D19PID" \ | 1020 | "$D14PID" "$D15PID" "$D16PID" "$D17PID" "$D18PID" "$D19PID" \ |
| 1004 | "$D20PID" "$D21PID" "$D22PID" "$D23PID" "$D24PID" "$D25PID" \ | 1021 | "$D20PID" "$D21PID" "$D22PID" "$D23PID" "$D24PID" "$D25PID" \ |
| 1005 | "$D26PID" "$D27PID" "$D28PID" "$D29PID" "$D30PID" "$D31PID" \ | 1022 | "$D26PID" "$D27PID" "$D28PID" "$D29PID" "$D30PID" "$D31PID" \ |
| 1006 | "$D32PID" "$D33PID" "$D34PID" "$D35PID" "$D36PID" "$D37PID" | 1023 | "$D32PID" "$D33PID" "$D34PID" "$D35PID" "$D36PID" "$D37PID" \ |
| 1024 | "$D38PID" "$D39PID" | ||
| 1007 | _leak=0 | 1025 | _leak=0 |
| 1008 | leak_sweep "$_rc" || _leak=1 | 1026 | leak_sweep "$_rc" || _leak=1 |
| 1009 | 1027 | ||
| @@ -1059,6 +1077,10 @@ cleanup() { | |||
| 1059 | # and the daemon logs beside them. | 1077 | # and the daemon logs beside them. |
| 1060 | rm -f "$OUT.whl" "$OUT.whl.err" "$OUT.whl.log" "$OUT.whl.d" "$WHEELSH" \ | 1078 | rm -f "$OUT.whl" "$OUT.whl.err" "$OUT.whl.log" "$OUT.whl.d" "$WHEELSH" \ |
| 1061 | "$OUT.mse" "$OUT.mse.err" "$OUT.mse.log" "$OUT.mse.d" "$MOUSESH" \ | 1079 | "$OUT.mse" "$OUT.mse.err" "$OUT.mse.log" "$OUT.mse.d" "$MOUSESH" \ |
| 1080 | "$SOCK41" "$OUT.zw.d" "$OUT.zwcap" "$OUT.zwcap.err" "$OUT.zwpc" \ | ||
| 1081 | "$OUT.zwcapg" "$OUT.zwstop" "$ZWHEELSH" \ | ||
| 1082 | "$SOCK42" "$OUT.zm2.d" "$OUT.zm2cap" "$OUT.zm2cap.err" "$OUT.zm2pc" \ | ||
| 1083 | "$OUT.zm2capg" "$OUT.zm2stop" "$ZMOUSESH" \ | ||
| 1062 | "$SOCK33" "$SOCK34" "$OUT.whlcap" "$OUT.msecap" \ | 1084 | "$SOCK33" "$SOCK34" "$OUT.whlcap" "$OUT.msecap" \ |
| 1063 | "$OUT.whlstop" "$OUT.msestop" | 1085 | "$OUT.whlstop" "$OUT.msestop" |
| 1064 | rm -f "$OUT.pgr" "$OUT.pgr.err" "$OUT.pgr.log" "$OUT.pgr.d" "$LESSSH" \ | 1086 | rm -f "$OUT.pgr" "$OUT.pgr.err" "$OUT.pgr.log" "$OUT.pgr.d" "$LESSSH" \ |
| @@ -5745,6 +5767,193 @@ assert_stopped "$SOCK38" "$D35PID" "zoom mute" "$OUT.zmstop" | |||
| 5745 | D35PID="" | 5767 | D35PID="" |
| 5746 | ok "an unzoomed wall forwards nothing; the zoom one keystroke later does" | 5768 | ok "an unzoomed wall forwards nothing; the zoom one keystroke later does" |
| 5747 | 5769 | ||
| 5770 | # ---- the wheel inside a zoom, and the mouse handed back on the way out -- | ||
| 5771 | # | ||
| 5772 | # What phase 3b is FOR. A zoomed tile used to be keyboard-only: the wall | ||
| 5773 | # hand-rolled a subset of the client's input path, and the wheel — which | ||
| 5774 | # needs the session's own modes, the tile's scrollback view and the tile's | ||
| 5775 | # transport all in one place — was not in that subset. Now the tile's input | ||
| 5776 | # goes through the same `interact.Core` a plain client's does, so the wheel | ||
| 5777 | # arrives with it. | ||
| 5778 | # | ||
| 5779 | # Two claims, one run, because the second is the first read backwards and | ||
| 5780 | # has to be anchored on it: | ||
| 5781 | # | ||
| 5782 | # * ZOOMED, the wheel scrolls the SESSION's history and never reaches its | ||
| 5783 | # pty. Same arithmetic as the plain client's wheel leg (scenario 42) and | ||
| 5784 | # deliberately the same pty size, so a difference here is the wall's and | ||
| 5785 | # not the geometry's: eight notches in one write are 24 rows, exactly a | ||
| 5786 | # screenful on 80x24, and the page that lands holds `wln60` — a line | ||
| 5787 | # that is in the history and in nothing painted before it. | ||
| 5788 | # * DEMOTED, the wheel reaches nothing at all, and the terminal is left | ||
| 5789 | # with no mouse mode enabled. The demote is what pairs with the promote's | ||
| 5790 | # claim, and it is the pair that turns over many times per wall where a | ||
| 5791 | # client's turns over once per process. | ||
| 5792 | # | ||
| 5793 | # The lines are `wln1`..`wln100` rather than bare numbers because this | ||
| 5794 | # terminal is a WALL: its label bar carries the socket path, which carries | ||
| 5795 | # this run's pid, and a bare `60` in a pid would make the money assertion | ||
| 5796 | # pass for a reason that has nothing to do with the wheel. | ||
| 5797 | # | ||
| 5798 | # The wall's spelling is a bare `--sock PATH` — the daemon's DEFAULT | ||
| 5799 | # session, so nothing has to be created and the session's shell is fixed for | ||
| 5800 | # its whole life. | ||
| 5801 | cat > "$ZWHEELSH" <<'EOF' | ||
| 5802 | #!/bin/sh | ||
| 5803 | seq 1 100 | sed 's/^/wln/' | ||
| 5804 | exec /bin/cat | ||
| 5805 | EOF | ||
| 5806 | chmod +x "$ZWHEELSH" | ||
| 5807 | "$MUXD" run --sock "$SOCK41" --shell "$ZWHEELSH" > "$OUT.zw.d" 2>&1 & | ||
| 5808 | D38PID=$! | ||
| 5809 | wait_sock "$SOCK41" "$OUT.zw.d" "zoom-wheel daemon never bound" | ||
| 5810 | # The wall attaches only once seq has finished, scenario 42's reason: the | ||
| 5811 | # page the wheel fetches has to be content this tile was never sent. | ||
| 5812 | i=0 | ||
| 5813 | until "$MUXD" dump --sock "$SOCK41" | grep -q "wln100"; do | ||
| 5814 | i=$((i+1)); [ "$i" -lt 100 ] || { echo "e2e FAIL: zoom wheel: session never finished seq"; exit 1; } | ||
| 5815 | sleep 0.1 | ||
| 5816 | done | ||
| 5817 | set +e | ||
| 5818 | timeout 90 "$PTYCLIENT" --cols 80 --rows 24 --out "$OUT.zwcap" --err "$OUT.zwcap.err" -- \ | ||
| 5819 | "$MUX" wall "--sock $SOCK41" > "$OUT.zwpc" 2>&1 <<'EOF' | ||
| 5820 | expect wln100 20000 | ||
| 5821 | settle 700 20000 | ||
| 5822 | send \r | ||
| 5823 | settle 900 20000 | ||
| 5824 | 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 | ||
| 5825 | expect wln60 15000 | ||
| 5826 | send \x1b[<65;10;5M\x1b[<65;10;5M\x1b[<65;10;5M\x1b[<65;10;5M\x1b[<65;10;5M\x1b[<65;10;5M\x1b[<65;10;5M\x1b[<65;10;5M | ||
| 5827 | expect wln100 15000 | ||
| 5828 | settle 500 15000 | ||
| 5829 | send zwheelpin\n | ||
| 5830 | expect zwheelpin 15000 | ||
| 5831 | settle 500 15000 | ||
| 5832 | send \x1cw | ||
| 5833 | expect wln100 20000 | ||
| 5834 | settle 900 20000 | ||
| 5835 | send \x1b[<64;10;5M\x1b[<64;10;5M\x1b[<64;10;5M\x1b[<64;10;5Mwallwheel | ||
| 5836 | settle 900 20000 | ||
| 5837 | send q | ||
| 5838 | waitexit 10000 | ||
| 5839 | EOF | ||
| 5840 | RC=$? | ||
| 5841 | set -e | ||
| 5842 | [ "$RC" -eq 0 ] || { | ||
| 5843 | echo "e2e FAIL: zoom wheel: ptyclient leg exited $RC (did the zoomed wheel scroll, and come back?):" | ||
| 5844 | cat "$OUT.zwpc"; exit 1; } | ||
| 5845 | # The anchor first, and it is a POSITIVE: a keystroke typed at the zoom | ||
| 5846 | # reached this session's pty. Everything silent below is measured against | ||
| 5847 | # it, so a leg that fell over early can never pass by having done nothing. | ||
| 5848 | timeout 20 "$MUXA" capture --sock "$SOCK41" > "$OUT.zwcapg" 2>&1 | ||
| 5849 | grep -q "zwheelpin" "$OUT.zwcapg" || { | ||
| 5850 | echo "e2e FAIL: zoom wheel: the zoom never typed into the session, so the" | ||
| 5851 | echo " silent halves below prove nothing:" | ||
| 5852 | cat "$OUT.zwcapg"; cat "$OUT.zwpc"; exit 1; } | ||
| 5853 | # Twenty mouse reports were typed at a shell that echoes everything it is | ||
| 5854 | # given, and the session's grid must hold none: zoomed they were the | ||
| 5855 | # client's own wheel, demoted they were never forwarded at all. | ||
| 5856 | grep -qF -- "[<64;" "$OUT.zwcapg" && { | ||
| 5857 | echo "e2e FAIL: zoom wheel: a mouse report reached the pty as input:" | ||
| 5858 | cat "$OUT.zwcapg"; exit 1; } | ||
| 5859 | ok "a zoomed tile scrolls its session's history on the wheel, and the pty sees none of it" | ||
| 5860 | |||
| 5861 | # The demoted half, on the same capture. Every letter of `wallwheel` is one | ||
| 5862 | # an unzoomed wall swallows — none is a selection key, `q` or `x` — which is | ||
| 5863 | # the same care scenario 45 spells out: a negative that cannot fail is not a | ||
| 5864 | # test. | ||
| 5865 | grep -q "wallwheel" "$OUT.zwcapg" && { | ||
| 5866 | echo "e2e FAIL: zoom wheel: the DEMOTED wall forwarded keystrokes to the session:" | ||
| 5867 | cat "$OUT.zwcapg"; exit 1; } | ||
| 5868 | # ...and the terminal was given back its mouse. `?1000l` is written exactly | ||
| 5869 | # twice on this run — once by the demote and once by the wall's own exit — | ||
| 5870 | # and the promote writes only `h`s, so a count of one is a demote that left | ||
| 5871 | # the user's terminal reporting clicks into their shell for the rest of the | ||
| 5872 | # wall's life. Counted with `grep -o`: these are escape sequences inside a | ||
| 5873 | # paint, with no newlines to make a line count mean anything. | ||
| 5874 | _zw_off=$(grep -oa "$(printf '\033')\[?1000l" "$OUT.zwcap" | wc -l) | ||
| 5875 | [ "$_zw_off" -ge 2 ] || { | ||
| 5876 | echo "e2e FAIL: zoom wheel: mouse reporting was turned off $_zw_off time(s);" | ||
| 5877 | echo " the demote must take it off as well as the wall's exit:" | ||
| 5878 | cat "$OUT.zwpc"; exit 1; } | ||
| 5879 | assert_stopped "$SOCK41" "$D38PID" "zoom wheel" "$OUT.zwstop" | ||
| 5880 | D38PID="" | ||
| 5881 | ok "a demoted tile forwards nothing and leaves no mouse mode on the wall's terminal" | ||
| 5882 | |||
| 5883 | # ---- an application in a ZOOMED tile owns the mouse --------------------- | ||
| 5884 | # | ||
| 5885 | # Scenario 43 said this about a plain client; this says it about a tile, | ||
| 5886 | # where it is a stronger claim than it looks. A tile learns the session's | ||
| 5887 | # modes while it is a STRIPE — `interact.Core` decodes every `term_modes` | ||
| 5888 | # whatever the tile's claim is, and only the WRITE to the terminal is gated | ||
| 5889 | # — and the promote is answered by `resyncSnapshot`, which carries no modes | ||
| 5890 | # at all. So the handover cannot come from a frame: it has to come from what | ||
| 5891 | # the Core already knew, level-set onto the terminal at the moment of the | ||
| 5892 | # claim. Measured before that existed: `?1002h` never reached this capture | ||
| 5893 | # and the wheel was eaten as scrollback. | ||
| 5894 | # | ||
| 5895 | # 1002 is what makes the leg sharp. The wall asks its terminal for NO mouse | ||
| 5896 | # mode, and a promoted tile's own capture set is 1000+1006, so a `?1002h` on | ||
| 5897 | # this terminal can only be the session's, mirrored. | ||
| 5898 | cat > "$ZMOUSESH" <<'EOF' | ||
| 5899 | #!/bin/sh | ||
| 5900 | seq 1 100 | sed 's/^/mln/' | ||
| 5901 | printf '\033[?1000h\033[?1002h\033[?1006h' | ||
| 5902 | printf 'mapp-holds-the-mouse\n' | ||
| 5903 | exec /bin/cat | ||
| 5904 | EOF | ||
| 5905 | chmod +x "$ZMOUSESH" | ||
| 5906 | "$MUXD" run --sock "$SOCK42" --shell "$ZMOUSESH" > "$OUT.zm2.d" 2>&1 & | ||
| 5907 | D39PID=$! | ||
| 5908 | wait_sock "$SOCK42" "$OUT.zm2.d" "zoom-app-mouse daemon never bound" | ||
| 5909 | i=0 | ||
| 5910 | until "$MUXD" dump --sock "$SOCK42" | grep -q "mapp-holds-the-mouse"; do | ||
| 5911 | i=$((i+1)); [ "$i" -lt 100 ] || { echo "e2e FAIL: zoom app mouse: session never armed"; exit 1; } | ||
| 5912 | sleep 0.1 | ||
| 5913 | done | ||
| 5914 | # The echo is the assertion: cat is in canonical mode with ECHOCTL, so bytes | ||
| 5915 | # that reach the pty come back as `^[[<64;10;5M` and bytes that do not, do | ||
| 5916 | # not. | ||
| 5917 | set +e | ||
| 5918 | timeout 90 "$PTYCLIENT" --cols 80 --rows 24 --out "$OUT.zm2cap" --err "$OUT.zm2cap.err" -- \ | ||
| 5919 | "$MUX" wall "--sock $SOCK42" > "$OUT.zm2pc" 2>&1 <<'EOF' | ||
| 5920 | expect mapp-holds-the-mouse 20000 | ||
| 5921 | settle 700 20000 | ||
| 5922 | send \r | ||
| 5923 | settle 900 20000 | ||
| 5924 | send \x1b[<64;10;5M | ||
| 5925 | expect [<64;10;5M 15000 | ||
| 5926 | settle 500 15000 | ||
| 5927 | send \x1cw | ||
| 5928 | settle 700 20000 | ||
| 5929 | send q | ||
| 5930 | waitexit 10000 | ||
| 5931 | EOF | ||
| 5932 | RC=$? | ||
| 5933 | set -e | ||
| 5934 | [ "$RC" -eq 0 ] || { | ||
| 5935 | echo "e2e FAIL: zoom app mouse: ptyclient leg exited $RC (did the wheel reach the app?):" | ||
| 5936 | cat "$OUT.zm2pc"; exit 1; } | ||
| 5937 | # The session's own grid, not just this terminal: the echo is the pty saying | ||
| 5938 | # it received the bytes. | ||
| 5939 | timeout 20 "$MUXA" capture --sock "$SOCK42" > "$OUT.zm2capg" 2>&1 | ||
| 5940 | grep -qF -- "[<64;10;5M" "$OUT.zm2capg" || { | ||
| 5941 | echo "e2e FAIL: zoom app mouse: the wheel never reached the application's pty:" | ||
| 5942 | cat "$OUT.zm2capg"; exit 1; } | ||
| 5943 | # The mirror: this terminal was asked for the SESSION's modes at the | ||
| 5944 | # promote, not for the tile's own set. | ||
| 5945 | grep -qaF "$(printf '\033')[?1002h" "$OUT.zm2cap" || { | ||
| 5946 | echo "e2e FAIL: zoom app mouse: the promote never mirrored the session's drag mode:" | ||
| 5947 | cat "$OUT.zm2pc"; exit 1; } | ||
| 5948 | # And the negative that makes the pair a pair: this session has the same 77 | ||
| 5949 | # rows of history as the leg above, and the same wheel byte moved none of it. | ||
| 5950 | grep -qa "mln60" "$OUT.zm2cap" && { | ||
| 5951 | echo "e2e FAIL: zoom app mouse: the tile scrolled back on a wheel the app owned:" | ||
| 5952 | cat "$OUT.zm2pc"; exit 1; } | ||
| 5953 | assert_stopped "$SOCK42" "$D39PID" "zoom app mouse" "$OUT.zm2stop" | ||
| 5954 | D39PID="" | ||
| 5955 | ok "an application in a zoomed tile gets the wheel, and the tile does not" | ||
| 5956 | |||
| 5748 | # ---- a tile whose pump has died still says something --------------------- | 5957 | # ---- a tile whose pump has died still says something --------------------- |
| 5749 | # | 5958 | # |
| 5750 | # Only pumps answer `repaint_gen`, so a tile whose pump has ENDED has | 5959 | # Only pumps answer `repaint_gen`, so a tile whose pump has ENDED has |
| @@ -6177,8 +6386,8 @@ DPID="" | |||
| 6177 | # convergence point either: what it asserts on is that file shrinking by | 6386 | # convergence point either: what it asserts on is that file shrinking by |
| 6178 | # one line while the session it named goes on answering, which is two | 6387 | # one line while the session it named goes on answering, which is two |
| 6179 | # facts a grid comparison speaks to neither of. | 6388 | # facts a grid comparison speaks to neither of. |
| 6180 | [ "$OK_COUNT" = "48" ] || { | 6389 | [ "$OK_COUNT" = "51" ] || { |
| 6181 | echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 48 —" | 6390 | echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 51 —" |
| 6182 | echo " a scenario was added (update the pin) or silently lost" | 6391 | echo " a scenario was added (update the pin) or silently lost" |
| 6183 | exit 1 | 6392 | exit 1 |
| 6184 | } | 6393 | } |
| @@ -6186,4 +6395,4 @@ DPID="" | |||
| 6186 | echo "e2e FAIL: $CONV_COUNT convergence points ran, the pin says 35" | 6395 | echo "e2e FAIL: $CONV_COUNT convergence points ran, the pin says 35" |
| 6187 | exit 1 | 6396 | exit 1 |
| 6188 | } | 6397 | } |
| 6189 | echo "e2e OK (48 scenarios, 35 convergence points)" | 6398 | echo "e2e OK (51 scenarios, 35 convergence points)" |