From f37d4c055368772d72936db62712881cc145705f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 2 Oct 2026 16:21:13 +0100 Subject: [PATCH] Test for 5593. --- regress/menu-live-output.sh | 164 ++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 regress/menu-live-output.sh diff --git a/regress/menu-live-output.sh b/regress/menu-live-output.sh new file mode 100644 index 000000000..3e127ea32 --- /dev/null +++ b/regress/menu-live-output.sh @@ -0,0 +1,164 @@ +#!/bin/sh + +# Inspect an attached client's terminal while panes write behind a menu. +# Incremental output must agree with the scene renderer, including floating +# panes and a client viewport smaller than the window. + +PATH=/bin:/usr/bin +TERM=screen +LC_ALL=C.UTF-8 +export TERM LC_ALL + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -LtestA$$ -f/dev/null" +TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null" +TMP=$(mktemp -d) + +cleanup() { + $TMUX kill-server 2>/dev/null + $TMUX2 kill-server 2>/dev/null + rm -f "$TMP/output" "$TMP/float" "$TMP/actual" "$TMP/expected" + rmdir "$TMP" +} +trap cleanup 0 1 15 + +fail() { + echo "$*" >&2 + exit 1 +} + +# A full scene redraw is the reference for each incremental update. Check an +# uncovered marker before refreshing, so a frozen pane cannot pass the test. +check_update() { + printf '%b' "$2" >&3 + printf '\033[1;1H\033[2K%s' "$1" >&3 + sleep 1 + $TMUX capturep -p >"$TMP/actual" || exit 1 + grep -q "^$1" "$TMP/actual" || fail "$1: pane stopped updating" + $TMUX capturep -pe >"$TMP/actual" || exit 1 + $TMUX2 refresh-client || exit 1 + sleep 1 + $TMUX capturep -pe >"$TMP/expected" || exit 1 + if ! cmp -s "$TMP/actual" "$TMP/expected"; then + diff -u "$TMP/expected" "$TMP/actual" >&2 + fail "$1: incremental output differs from scene redraw" + fi +} + +menu() { + $TMUX2 display-menu -b simple -T live -C 0 "$@" \ + alpha a "" beta b "" || exit 1 + sleep 1 + $TMUX capturep -p >"$TMP/actual" || exit 1 + grep -q alpha "$TMP/actual" || fail "menu did not appear" +} + +mkfifo "$TMP/output" "$TMP/float" || exit 1 +$TMUX2 new -d -x40 -y14 "exec cat '$TMP/output'" || exit 1 +$TMUX2 set -g status off || exit 1 +$TMUX2 set -g window-size manual || exit 1 +$TMUX2 resizew -x40 -y14 || exit 1 +$TMUX2 set -as terminal-features ',screen:clipboard' || exit 1 +$TMUX2 set -g set-clipboard on || exit 1 +exec 3>"$TMP/output" + +$TMUX new -d -x40 -y14 "$TMUX2 attach" || exit 1 +$TMUX set -g status off || exit 1 +$TMUX set -g window-size manual || exit 1 +$TMUX resizew -x40 -y14 || exit 1 +$TMUX set -g set-clipboard on || exit 1 +sleep 1 + +menu -x6 -y8 +check_update text '\033[6;1Habcdefghijklmnopqrstuvwxyz0123456789' +check_update clear-line '\033[6;1H\033[2K' +check_update clear-end-line '\033[6;1H\033[K' +check_update clear-start-line '\033[6;40H\033[1K' +check_update erase-character '\033[6;1H\033[40X' +check_update clear-screen '\033[2J' +check_update clear-end-screen '\033[3;1H\033[J' +check_update clear-start-screen '\033[10;40H\033[1J' +check_update scroll '\033[14;1Hone\r\ntwo\r\nthree\r\nfour\r\n' +check_update reverse-scroll '\033[1;1H\033M' +check_update insert-character '\033[6;1Habcdefghijklmnopqrstuvwxyz\033[6;1H\033[3@' +check_update delete-character '\033[6;1Habcdefghijklmnopqrstuvwxyz\033[6;1H\033[3P' +check_update insert-line '\033[5;1H\033[2L' +check_update delete-line '\033[5;1H\033[2M' +check_update wide-text '\033[6;6H\347\225\214\347\225\214\347\225\214' +check_update overwrite-wide '\033[6;5H\347\225\214\033[6;6Hx' +check_update insert-mode '\033[6;1H\033[4habc\033[4l' +check_update sync '\033[?2026h\033[6;1Habcdefghijklmnopqrstuvwxyz\033[?2026l' +check_update sync-scroll '\033[?2026h\033[14;1Hone\r\ntwo\r\nthree\r\n\033[?2026l' + +# Text on the right of the menu crosses the client viewport edge. A terminal +# fallback which redraws the entire pane line would overwrite the menu. +$TMUX2 resizew -x60 -y14 || exit 1 +sleep 1 +check_update viewport '\033[6;1Habcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwx' +$TMUX2 resizew -x40 -y14 || exit 1 + +# Window coordinates must still be used when the status line is above it. +$TMUX2 set -g status on || exit 1 +$TMUX2 set -g status-position top || exit 1 +$TMUX2 set -g status-format[0] '' || exit 1 +$TMUX2 resizew -y13 || exit 1 +sleep 1 +check_update top-status '\033[6;1Habcdefghijklmnopqrstuvwxyz0123456789' +$TMUX2 set -g status off || exit 1 +$TMUX2 resizew -y14 || exit 1 + +# Both the menu and floating panes obscure the base pane. The menu also +# obscures incremental output from the floating pane itself. +FLOAT=$($TMUX2 new-pane -dPF '#{pane_id}' -x24 -y8 -X4 -Y2 \ + "exec cat '$TMP/float'") || exit 1 +exec 4>"$TMP/float" +sleep 1 +check_update behind-float '\033[6;1Habcdefghijklmnopqrstuvwxyz0123456789' +printf '\033[4;1HABCDEFGHIJKLMNOPQRSTUVWX' >&4 +check_update float-text '' +printf '\033[4;1H\033[4hxyz\033[4l' >&4 +check_update float-insert '' +printf '\033[8;1Hone\r\ntwo\r\nthree\r\n' >&4 +check_update float-scroll '' +$TMUX2 kill-pane -t "$FLOAT" || exit 1 + +# Without a menu, the same whole-line and insert-mode paths must continue to +# respect floating panes. This also exercises split visible ranges. +$TMUX send Escape || exit 1 +FLOAT=$($TMUX2 new-pane -dPF '#{pane_id}' -x12 -y6 -X6 -Y3 \ + 'exec sleep 100') || exit 1 +$TMUX2 resizew -x60 -y14 || exit 1 +sleep 1 +check_update float-viewport '\033[6;1Habcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwx' +check_update float-insert-mode '\033[6;1H\033[4habc\033[4l' +$TMUX2 kill-pane -t "$FLOAT" || exit 1 +$TMUX2 resizew -x40 -y14 || exit 1 + +# A menu covering an entire line leaves an empty visible range. Borderless +# menus and menus larger than the window must use their actual screen size. +menu -b none -x0 -y8 +check_update borderless '\033[7;1Habcdefghijklmnopqrstuvwxyz0123456789' +$TMUX send Escape || exit 1 +$TMUX2 display-menu -b simple -T live -C 0 -x0 -y8 \ + 'This menu is wider than the whole window' a '' beta b '' || exit 1 +sleep 1 +check_update full-width '\033[6;1Habcdefghijklmnopqrstuvwxyz0123456789' + +# Nonvisual output is still delivered while a menu is open. +printf '\033]52;c;bWVudS1jbGlwYm9hcmQ=\007' >&3 +sleep 1 +[ "$($TMUX show-buffer 2>/dev/null)" = menu-clipboard ] || + fail "menu blocked the clipboard update" + +# The pane grid must keep updating under the menu, both for capture-pane and +# for the redraw when the menu closes. +printf '\033[2J\033[6;7Hlatest content' >&3 +sleep 1 +$TMUX2 capturep -p -t%0 >"$TMP/actual" || exit 1 +grep -q 'latest content' "$TMP/actual" || fail "underlying pane is stale" +$TMUX send Escape || exit 1 +sleep 1 +$TMUX capturep -p >"$TMP/actual" || exit 1 +grep -q 'latest content' "$TMP/actual" || fail "menu close left stale content" + +exit 0