From e1db23c7725c38bd9abd584da3a90cb9944a8450 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 28 Sep 2026 11:10:24 +0100 Subject: [PATCH] Tests for 5641. (cherry picked from commit 2d7148b3fae58bf459120c9032b3829bd722e8a2) --- regress/floating-pane-covers-tiled-pane.sh | 137 +++++++++++++++++ .../floating-pane-scroll-behind-margins.sh | 141 ++++++++++++++++++ 2 files changed, 278 insertions(+) create mode 100755 regress/floating-pane-covers-tiled-pane.sh create mode 100755 regress/floating-pane-scroll-behind-margins.sh diff --git a/regress/floating-pane-covers-tiled-pane.sh b/regress/floating-pane-covers-tiled-pane.sh new file mode 100755 index 000000000..79da614a3 --- /dev/null +++ b/regress/floating-pane-covers-tiled-pane.sh @@ -0,0 +1,137 @@ +#!/bin/sh + +# A floating pane that is larger than a tiled pane on every side (so none of +# its edges fall inside the tiled pane) must still hide that pane: output from +# the covered pane must not be drawn over the floating pane. + +PATH=/bin:/usr/bin +TERM=screen +LC_ALL=C.UTF-8 +export PATH TERM LC_ALL + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) + +DIR=$(mktemp -d) || exit 1 +INNER="$TEST_TMUX -Lcovers-inner-$$ -f/dev/null" +OUTER="$TEST_TMUX -Lcovers-outer-$$ -f/dev/null" +EMITTER=$DIR/emitter.pl +FILLER=$DIR/filler.pl +TRIGGER=$DIR/trigger +CAPTURE=$DIR/capture + +fail() +{ + echo "$*" >&2 + [ -s "$CAPTURE" ] && cat "$CAPTURE" >&2 + exit 1 +} + +cleanup() +{ + $OUTER kill-server 2>/dev/null + $INNER kill-server 2>/dev/null + rm -rf "$DIR" +} +trap cleanup 0 1 15 + +wait_outer_has() +{ + marker=$1 + i=0 + while [ "$i" -lt 50 ]; do + $OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true + grep -q "$marker" "$CAPTURE" && return 0 + sleep 0.1 + i=$((i + 1)) + done + fail "outer client did not show $marker" +} + +wait_inner_has() +{ + marker=$1 + i=0 + while [ "$i" -lt 50 ]; do + $INNER capture-pane -p -t "$2" 2>/dev/null | + grep -q "$marker" && return 0 + sleep 0.1 + i=$((i + 1)) + done + fail "inner pane $2 did not contain $marker" +} + +# Covered pane: waits for the trigger, then scrolls a small region. +cat >"$EMITTER" <<'PERL' +use strict; +use warnings; + +$| = 1; +while (!-e $ENV{TRIGGER}) { + select undef, undef, undef, 0.01; +} +# Scroll a small region: this is redrawn row by row rather than by a large +# region redraw, so it must not draw over the floating pane. +print "\e[1;2r\e[2;1HCOVEREDCOVERED\n"; +sleep 100; +PERL + +# Floating pane: fill it so any overwriting is visible. +cat >"$FILLER" <<'PERL' +use strict; +use warnings; + +$| = 1; +for my $row (1 .. 8) { + print "\e[$row;1H", 'F' x 30; +} +sleep 100; +PERL + +$INNER new-session -d -s inner -x 60 -y 20 'sleep 100' || exit 1 +$INNER set-option -g status off || exit 1 +$INNER set-option -g window-size manual || exit 1 + +# Three rows of panes; the middle row is short and split into three so that +# the middle pane has other panes on both sides and above and below. +$INNER split-window -d -v -t inner:0.0 'sleep 100' || exit 1 +$INNER split-window -d -v -t inner:0.1 'sleep 100' || exit 1 +$INNER resize-pane -t inner:0.0 -y 6 || exit 1 +$INNER resize-pane -t inner:0.1 -y 4 || exit 1 +MID=$($INNER split-window -d -h -P -F '#{pane_id}' -l 20 -t inner:0.1 \ + 'sleep 100') || exit 1 +$INNER split-window -d -h -l 20 -t inner:0.1 'sleep 100' || exit 1 + +# The covered pane is the one in the middle of the short row. +set -- $($INNER list-panes -F '#{pane_left} #{pane_id}' -f '#{==:#{pane_top},7}' | + sort -n | awk '{ print $2 }') +[ $# -eq 3 ] || fail "expected three panes in the middle row, got $#" +COVERED=$2 +X=$($INNER display-message -p -t "$COVERED" '#{pane_left}') +Y=$($INNER display-message -p -t "$COVERED" '#{pane_top}') +W=$($INNER display-message -p -t "$COVERED" '#{pane_width}') +H=$($INNER display-message -p -t "$COVERED" '#{pane_height}') + +# Bigger than the covered pane on all four sides. +FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x $((W + 6)) -y $((H + 4)) \ + -X $((X - 3)) -Y $((Y - 2)) "perl '$FILLER'") || fail "new-pane failed" + +$INNER respawn-pane -k -t "$COVERED" "TRIGGER='$TRIGGER' perl '$EMITTER'" || + exit 1 + +$OUTER new-session -d -s outer -x 60 -y 20 'sleep 100' || exit 1 +$OUTER set-option -g status off || exit 1 +$OUTER set-option -g window-size manual || exit 1 +$OUTER set-option -g default-terminal screen || exit 1 +$OUTER respawn-pane -k -t outer:0.0 \ + "$TEST_TMUX -Lcovers-inner-$$ -f/dev/null attach-session -t inner" || + exit 1 + +wait_outer_has FFFFFFFFFF +sleep 1 +: >"$TRIGGER" +wait_inner_has COVERED "$COVERED" +sleep 1 +$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" +grep -q COVERED "$CAPTURE" && fail "covered pane drawn over floating pane" + +exit 0 diff --git a/regress/floating-pane-scroll-behind-margins.sh b/regress/floating-pane-scroll-behind-margins.sh new file mode 100755 index 000000000..c7039b19a --- /dev/null +++ b/regress/floating-pane-scroll-behind-margins.sh @@ -0,0 +1,141 @@ +#!/bin/sh + +# A floating pane that scrolls behind a larger floating pane must not disturb +# the larger pane, with or without left/right margin support in the terminal. +# The larger pane spans the smaller one vertically and overlaps it only at its +# left, so with margins the terminal would otherwise scroll the cells of the +# larger pane (including its scrollbar) along with the smaller pane. + +PATH=/bin:/usr/bin +TERM=screen +LC_ALL=C.UTF-8 +export PATH TERM LC_ALL + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) + +DIR=$(mktemp -d) || exit 1 +INNER="$TEST_TMUX -Lscrollbehind-inner-$$ -f/dev/null" +OUTER="$TEST_TMUX -Lscrollbehind-outer-$$ -f/dev/null" +EMITTER=$DIR/emitter.pl +FILLER=$DIR/filler.pl +TRIGGER=$DIR/trigger +CAPTURE=$DIR/capture + +fail() +{ + echo "$*" >&2 + [ -s "$CAPTURE" ] && cat "$CAPTURE" >&2 + exit 1 +} + +cleanup() +{ + $OUTER kill-server 2>/dev/null + $INNER kill-server 2>/dev/null + rm -rf "$DIR" +} +trap cleanup 0 1 15 + +wait_outer_has() +{ + marker=$1 + i=0 + while [ "$i" -lt 50 ]; do + $OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true + grep -q "$marker" "$CAPTURE" && return 0 + sleep 0.1 + i=$((i + 1)) + done + fail "outer client did not show $marker" +} + +wait_inner_has() +{ + marker=$1 + i=0 + while [ "$i" -lt 100 ]; do + $INNER capture-pane -p -t "$2" 2>/dev/null | + grep -q "$marker" && return 0 + sleep 0.1 + i=$((i + 1)) + done + fail "inner pane $2 did not contain $marker" +} + +# Scrolling pane: wait for the trigger, then scroll it well past its height. +cat >"$EMITTER" <<'PERL' +use strict; +use warnings; + +$| = 1; +while (!-e $ENV{TRIGGER}) { + select undef, undef, undef, 0.01; +} +for (1 .. 30) { + print 'd' x 20, "\r\n"; + select undef, undef, undef, 0.02; +} +print "SCROLL-DONE\r\n"; +sleep 100; +PERL + +# Larger pane: numbered rows so any shift or blanking is visible. +cat >"$FILLER" <<'PERL' +use strict; +use warnings; + +$| = 1; +for my $row (1 .. 18) { + printf "\e[%d;1Hrow%02d %s", $row, $row, 'F' x 40; +} +sleep 100; +PERL + +for margins in off on; do + rm -f "$TRIGGER" + $INNER new-session -d -s inner -x 80 -y 24 'sleep 100' || exit 1 + $INNER set -g status off || exit 1 + $INNER set -g window-size manual || exit 1 + $INNER set -g pane-scrollbars on || exit 1 + if [ "$margins" = on ]; then + $INNER set -as terminal-features ',screen-256color:margins' || + exit 1 + fi + + # The small pane is created first so the large one is above it. + SMALL=$($INNER new-pane -d -PF '#{pane_id}' -x 30 -y 10 -X 40 -Y 6 \ + "TRIGGER='$TRIGGER' perl '$EMITTER'") || fail "new-pane failed" + $INNER new-pane -d -x 50 -y 20 -X 2 -Y 2 "perl '$FILLER'" || + fail "new-pane failed" + + $OUTER new-session -d -s outer -x 80 -y 24 'sleep 100' || exit 1 + $OUTER set -g status off || exit 1 + $OUTER set -g window-size manual || exit 1 + $OUTER set -g default-terminal screen-256color || exit 1 + $OUTER respawn-pane -k -t outer:0.0 \ + "$TEST_TMUX -Lscrollbehind-inner-$$ -f/dev/null attach -t inner" || + exit 1 + + wait_outer_has row18 + sleep 0.5 + $OUTER capture-pane -p -t outer:0.0 >"$DIR/before" + + : >"$TRIGGER" + wait_inner_has SCROLL-DONE "$SMALL" + sleep 0.5 + $OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" + + # Columns 1-52 hold the whole large pane, its border and scrollbar. + cut -c1-52 "$DIR/before" >"$DIR/before.cut" + cut -c1-52 "$CAPTURE" >"$DIR/after.cut" + if ! cmp -s "$DIR/before.cut" "$DIR/after.cut"; then + echo "large pane changed (margins $margins):" >&2 + diff "$DIR/before.cut" "$DIR/after.cut" >&2 + exit 1 + fi + + $OUTER kill-server 2>/dev/null + $INNER kill-server 2>/dev/null +done + +exit 0