84a7a44f
test(e2e): say why the paste leg waits the way it does
a73x 2026-08-15 17:50
Commit message
test/e2e.sh
| Old | New | ||
|---|---|---|---|
| @@ -20,6 +20,19 @@ MUXWEB="$8" | |||
| 20 | # address a session by name without attaching to it. Everything else muxa | 20 | # address a session by name without attaching to it. Everything else muxa |
| 21 | # does is test/agent.sh's business. | 21 | # does is test/agent.sh's business. |
| 22 | MUXA="$9" | 22 | MUXA="$9" |
| 23 | |||
| 24 | # The one thing this suite needs that it does not build: a real editor, for | ||
| 25 | # the paste scenario far below. Checked HERE rather than there because there | ||
| 26 | # is 25 scenarios, several daemons and a QUIC leg away — minutes of work to | ||
| 27 | # report a missing package. Required rather than guarded; that trade, and why | ||
| 28 | # a `command -v` skip would be worse than a hard failure, is argued at the | ||
| 29 | # scenario itself. | ||
| 30 | command -v nvim > /dev/null 2>&1 || { | ||
| 31 | echo "e2e FAIL: this suite needs nvim (the paste scenario asserts on a file" | ||
| 32 | echo " a real editor wrote); install it, or lose the only check that" | ||
| 33 | echo " proves bracketed paste works rather than merely arrives" | ||
| 34 | exit 1; } | ||
| 35 | |||
| 23 | SOCK="${TMPDIR:-/tmp}/muxd-e2e-$$.sock" | 36 | SOCK="${TMPDIR:-/tmp}/muxd-e2e-$$.sock" |
| 24 | OUT="${TMPDIR:-/tmp}/mux-e2e-out-$$" | 37 | OUT="${TMPDIR:-/tmp}/mux-e2e-out-$$" |
| 25 | # M10: hermetic XDG homes. Key-default scenarios must see OUR key or none, | 38 | # M10: hermetic XDG homes. Key-default scenarios must see OUR key or none, |
| @@ -2754,12 +2767,19 @@ ok "the session's OSC 52 reaches the host terminal" | |||
| 2754 | # nvim is REQUIRED, not guarded with `command -v`. A guard would let a box | 2767 | # nvim is REQUIRED, not guarded with `command -v`. A guard would let a box |
| 2755 | # without nvim skip this quietly, and a silently-skipped scenario is exactly | 2768 | # without nvim skip this quietly, and a silently-skipped scenario is exactly |
| 2756 | # what the OK_COUNT pin at the bottom exists to catch — so the dependency | 2769 | # what the OK_COUNT pin at the bottom exists to catch — so the dependency |
| 2757 | # fails loudly here instead of disappearing. | 2770 | # fails loudly. The check itself is HOISTED to the head of this file, next to |
| 2758 | command -v nvim > /dev/null 2>&1 || { | 2771 | # the argument binding: a box without nvim should hear about it before it |
| 2759 | echo "e2e FAIL: this suite needs nvim (the paste scenario asserts on a file" | 2772 | # spends minutes getting here. |
| 2760 | echo " a real editor wrote); install it, or lose the only check that" | 2773 | # |
| 2761 | echo " proves bracketed paste works rather than merely arrives" | 2774 | # That reverses M9's standing rule (2026-08-08-m9-prediction.md: "rawmode |
| 2762 | exit 1; } | 2775 | # helper, not nvim, in CI"), which gave two reasons. Its PRESENCE half is |
| 2776 | # answered by the check above — refusing to run beats running less. Its | ||
| 2777 | # NONDETERMINISM half — nvim's redraw timing — is answered by construction: | ||
| 2778 | # every wait below is on a CONDITION (a needle that must arrive) or on | ||
| 2779 | # SILENCE (settle), and neither is a bet on when a redraw happens. If this | ||
| 2780 | # scenario ever flakes, that is the property to check first; a wait added | ||
| 2781 | # here that keys off redraw timing would be the regression. | ||
| 2782 | # | ||
| 2763 | # Unquoted heredoc, like the clipboard leg above: the send verb carries | 2783 | # Unquoted heredoc, like the clipboard leg above: the send verb carries |
| 2764 | # $OUT's per-run path. `\x1b`, `\r`, `\n` and `\x1c` are the fixture's | 2784 | # $OUT's per-run path. `\x1b`, `\r`, `\n` and `\x1c` are the fixture's |
| 2765 | # escapes and the shell leaves every one of them alone. | 2785 | # escapes and the shell leaves every one of them alone. |
| @@ -2768,22 +2788,52 @@ command -v nvim > /dev/null 2>&1 || { | |||
| 2768 | # their config, so 'autoindent' is set here and nowhere else — the staircase | 2788 | # their config, so 'autoindent' is set here and nowhere else — the staircase |
| 2769 | # this measures has to come from the option this line spells. | 2789 | # this measures has to come from the option this line spells. |
| 2770 | # | 2790 | # |
| 2771 | # ESC and `:wq` are two sends with a settle between them: delivered in one | 2791 | # `timeout 60` is under the heredoc's own deadlines (120s if every one of |
| 2772 | # read, nvim's input parser is entitled to read `\x1b:` as Alt-: rather than | 2792 | # them ran long), and that is deliberate rather than copied from the leg |
| 2773 | # as leaving insert mode, and the file would never be written. | 2793 | # above: those deadlines are per-verb ceilings for a fixture that is still |
| 2794 | # talking, and a run that genuinely needs more than 60s wall has something | ||
| 2795 | # wrong with it that a bare `exited 124` describes about as well as a | ||
| 2796 | # fixture dump would. The cost is real — 124 loses the "last N bytes | ||
| 2797 | # received" line — so anything raised here should raise this too. | ||
| 2774 | set +e | 2798 | set +e |
| 2775 | timeout 60 "$PTYCLIENT" --cols 80 --rows 24 --out "$OUT.paste" --err "$OUT.paste.err" \ | 2799 | timeout 60 "$PTYCLIENT" --cols 80 --rows 24 --out "$OUT.paste" --err "$OUT.paste.err" \ |
| 2776 | -- "$MUX" --sock "$SOCK" > "$OUT.paste.log" 2>&1 <<EOF | 2800 | -- "$MUX" --sock "$SOCK" > "$OUT.paste.log" 2>&1 <<EOF |
| 2777 | expect \x1b[?1049h 15000 | 2801 | expect \x1b[?1049h 15000 |
| 2778 | settle 400 15000 | 2802 | settle 400 15000 |
| 2779 | send nvim -u NONE -i NONE -c "set autoindent" -c startinsert $OUT.pasted.txt\n | 2803 | send nvim -u NONE -i NONE -c "set autoindent" -c startinsert $OUT.pasted.txt\n |
| 2780 | expect INSERT 20000 | 2804 | # The mode indicator, whole. The line above echoes "startinsert", so a bare |
| 2805 | # INSERT needle would differ from the echo by capitalisation alone (the M12 | ||
| 2806 | # tp2 rule passing on a technicality); the dashes are what that echo cannot | ||
| 2807 | # spell. | ||
| 2808 | # | ||
| 2809 | # And NOT an expect for the 2004h escape, deliberately, however much this | ||
| 2810 | # reads like the place for one. The fixture watches for that escape without | ||
| 2811 | # waiting on it: it brackets a paste when it has seen one and sends bare | ||
| 2812 | # bytes when it has not. A needle for it here would wait, and that changes | ||
| 2813 | # what a broken mirror looks like — instead of the file below holding the | ||
| 2814 | # staircase, the run would time out on the needle and never reach the | ||
| 2815 | # assertion at all. The escape ARRIVING is already pinned, in client.zig's | ||
| 2816 | # unit tests. What THIS leg measures is pasting working, and it can only | ||
| 2817 | # measure that if the escape's absence is allowed through to the editor. | ||
| 2818 | expect -- INSERT -- 20000 | ||
| 2781 | settle 500 20000 | 2819 | settle 500 20000 |
| 2782 | paste if x:\r a = 1\r b = 2\r | 2820 | paste if x:\r a = 1\r b = 2\r |
| 2783 | settle 500 20000 | 2821 | settle 500 20000 |
| 2822 | # Two sends, not one. Delivered in the same read as the colon, nvim's input | ||
| 2823 | # parser is entitled to read the ESC as the Alt- prefix of Alt-colon rather | ||
| 2824 | # than as leaving insert mode, and the file would never be written. The | ||
| 2825 | # settle between them is a condition, not a timer: nvim repaints the mode | ||
| 2826 | # line on leaving insert, so the silence ending this verb IS that repaint | ||
| 2827 | # being over, and it outlasts by far the 50ms ttimeoutlen the ambiguity | ||
| 2828 | # actually turns on. | ||
| 2784 | send \x1b | 2829 | send \x1b |
| 2785 | settle 300 15000 | 2830 | settle 300 15000 |
| 2786 | send :wq\r | 2831 | send :wq\r |
| 2832 | # What makes the file safe to read after this is the ORDERING, not the | ||
| 2833 | # number: nvim writes the buffer before it exits, and its exit repaint is | ||
| 2834 | # the last traffic the master ever sees, so silence after that repaint | ||
| 2835 | # cannot precede the write. The 800 is slack on top of a guarantee, not a | ||
| 2836 | # measurement of how long nvim takes. | ||
| 2787 | settle 800 20000 | 2837 | settle 800 20000 |
| 2788 | send \x1c | 2838 | send \x1c |
| 2789 | waitexit 10000 | 2839 | waitexit 10000 |