From b868c9aa1408feb504badcba0de51c43eea71cf0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 12 Oct 2026 11:54:19 +0100 Subject: [PATCH] Tests for 5722-5725. --- regress/alt-screen-saved-cursor.sh | 87 ++++++++++++++++++++++++++++++ regress/client-exit-resize.sh | 72 +++++++++++++++++++++++++ regress/format-modifiers.sh | 22 ++++++-- regress/menu-pane-killed.sh | 29 ++++++++++ 4 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 regress/alt-screen-saved-cursor.sh create mode 100755 regress/client-exit-resize.sh create mode 100755 regress/menu-pane-killed.sh diff --git a/regress/alt-screen-saved-cursor.sh b/regress/alt-screen-saved-cursor.sh new file mode 100644 index 000000000..26ef20fef --- /dev/null +++ b/regress/alt-screen-saved-cursor.sh @@ -0,0 +1,87 @@ +#!/bin/sh + +# 1049h saves the cursor when it enters the alternate screen, and neither 47l +# nor 1049l clears it. If the screen was resized after it was saved, 1049l can +# restore a row below the saved grid it reflows. + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -Ltest$$ -f/dev/null" +DIR=$(mktemp -d) +trap '$TMUX kill-server 2>/dev/null; rm -rf $DIR' 0 1 15 + +exit_status=0 + +# Wait until the pane's program has set its path (OSC 7) to $1. +wait_path() { + _i=0 + while [ "$($TMUX display -p '#{pane_path}' 2>/dev/null)" != "$1" ]; do + if ! $TMUX has 2>/dev/null; then + echo "FAIL: $2: the server died" + exit 1 + fi + _i=$((_i + 1)) + if [ $_i -ge 400 ]; then + echo "FAIL: $2: timed out waiting for $1" + exit_status=1 + return 1 + fi + sleep 0.05 + done +} + +# Respawn the pane, $2 by $3, running $1. Where the program waits for a file +# in $DIR, the test resizes the window and then creates it. +run() { + rm -f $DIR/go* + $TMUX resize-window -x $2 -y $3 || exit 1 + $TMUX respawn-pane -k "$1; exec sleep 1000" || exit 1 +} + +check() { + if [ "$2" != "$3" ]; then + echo "FAIL: $1: '$2', not '$3'" + exit_status=1 + fi +} + +$TMUX new -d -x 10 -y 5 'sleep 1000' || exit 1 +$TMUX set -g window-size manual || exit 1 + +# Outside the alternate screen (47l): the cursor after a full line goes to +# the last column, as with no 47l. Unchanged. +run "printf 'abcdefghij\\033[?1049h\\033[?47l\\033[?1049lX\\033]7;done\\007'" 10 5 +if wait_path done "1049l outside the alternate screen"; then + check "1049l outside the alternate screen" \ + "$($TMUX capture-pane -p | head -1)" abcdefghiX +fi + +# A full line, 1049h, a wider screen, 1049l: the cursor is after the line's +# last character and X follows it. Unchanged. +run "printf 'abcdefghij\\033[?1049h\\033]7;in\\007'; while [ ! -e $DIR/go ]; do sleep 0.05; done; printf '\\033[?1049lX\\033]7;done\\007'" 10 5 +if wait_path in "full line, then wider"; then + $TMUX resize-window -x 20 || exit 1 + touch $DIR/go + wait_path done "full line, then wider" && + check "full line, then wider" \ + "$($TMUX capture-pane -p | head -1)" abcdefghijX +fi + +# cy 8 saved 10 high; 47l, 20x4, 47h, 40x4, 1049l. The cursor goes to the +# last row of the saved grid, which is 4 high. +run "printf '\\033[9;1Hrow\\033[?1049h\\033[?47l\\033]7;one\\007'; while [ ! -e $DIR/go1 ]; do sleep 0.05; done; printf '\\033[?47h\\033]7;two\\007'; while [ ! -e $DIR/go2 ]; do sleep 0.05; done; printf '\\033[?1049lX\\033]7;done\\007'" 40 10 +if wait_path one "saved taller"; then + $TMUX resize-window -x 20 -y 4 || exit 1 + touch $DIR/go1 + if wait_path two "saved taller"; then + $TMUX resize-window -x 40 -y 4 || exit 1 + touch $DIR/go2 + wait_path done "saved taller" && + check "saved taller" \ + "$($TMUX capture-pane -p | grep row)" rowX + fi +fi + +exit $exit_status diff --git a/regress/client-exit-resize.sh b/regress/client-exit-resize.sh new file mode 100755 index 000000000..a1dfab023 --- /dev/null +++ b/regress/client-exit-resize.sh @@ -0,0 +1,72 @@ +#!/bin/sh + +# A client that has sent MSG_EXITING can still send MSG_RESIZE. The server +# must not use the tty it closed for MSG_EXITING. Here the client is stopped +# and sent SIGTERM and then SIGWINCH for a size with no pixel size, so the +# server would ask the terminal for one. + +PATH=/bin:/usr/bin:/usr/local/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -Ltest$$ -f/dev/null" +trap '$TMUX kill-server 2>/dev/null' 0 1 15 + +$TMUX new -d 'sleep 1000' || exit 1 +$TMUX set-hook -g client-resized 'set -g @resized 1' || exit 1 +python3 - $TMUX <<'EOF2' || exit 1 +import fcntl, os, pty, signal, struct, subprocess, sys, termios, time + +tmux = sys.argv[1:] + +def size(fd, rows, cols, xpixel, ypixel): + fcntl.ioctl(fd, termios.TIOCSWINSZ, + struct.pack("HHHH", rows, cols, cols * xpixel, rows * ypixel)) + +master, slave = pty.openpty() +size(slave, 20, 60, 8, 16) +client = os.fork() +if client == 0: + os.setsid() + fcntl.ioctl(slave, termios.TIOCSCTTY, 0) + for fd in (0, 1, 2): + os.dup2(slave, fd) + os.environ["TERM"] = "xterm" + os.execvp(tmux[0], tmux + ["attach"]) +os.set_blocking(master, False) + +# Wait until the server has handled the resize the client sends when it +# attaches. +for i in range(200): + try: + os.read(master, 65536) + except OSError: + pass + out = subprocess.run(tmux + ["show", "-gv", "@resized"], + stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout + if out.strip() == b"1": + break + time.sleep(0.05) +else: + print("the client did not attach") + sys.exit(1) + +# SIGTERM is sent before SIGWINCH, so the client handles it first whether +# it handles pending signals in signal number order or in the order sent. +os.kill(client, signal.SIGSTOP) +os.waitpid(client, os.WUNTRACED) +os.kill(client, signal.SIGTERM) +size(master, 23, 61, 0, 0) +os.kill(client, signal.SIGWINCH) +os.kill(client, signal.SIGCONT) +for i in range(200): + if os.waitpid(client, os.WNOHANG)[0] != 0: + break + time.sleep(0.05) +else: + print("the client did not exit") + sys.exit(1) +EOF2 +sleep 1 +$TMUX has || { echo "the server died"; exit 1; } +exit 0 diff --git a/regress/format-modifiers.sh b/regress/format-modifiers.sh index 3fdfb59bd..648c19232 100644 --- a/regress/format-modifiers.sh +++ b/regress/format-modifiers.sh @@ -226,10 +226,24 @@ test_format "#{e|/|f|2:10,3}" "3.33" # Default number of decimal places for float is two. test_format "#{e|*|f:2.5,2}" "5.00" -# Division by zero must not crash the server (result is unspecified). -$TMUX display-message -p "#{e|/|:5,0}" >/dev/null 2>&1 -$TMUX display-message -p "#{e|/|f:5,0}" >/dev/null 2>&1 -assert_alive "division by zero" +# An integer expression with a side or result that is NaN or out of the range +# of a long long expands to empty: division or modulus by zero, nan or inf, or +# 1e30. LLONG_MAX written out is 2^63 as a double, which is out of range; the +# largest double below 2^63 and -2^63 still convert. Comparisons are integer +# expressions too. Floating point gives inf. +test_format "#{e|/|:5,0}" "" +test_format "#{e|m|:0,0}" "" +test_format "#{e|m|:7,0}" "" +test_format "#{e|+|:nan,0}" "" +test_format "#{e|+|:-inf,1}" "" +test_format "#{e|-|:1e30,1e30}" "" +test_format "#{e|*|:1e30,1}" "" +test_format "#{e|+|:9223372036854775807,0}" "" +test_format "#{e|<|:nan,1}" "" +test_format "#{e|+|:9223372036854774784,0}" "9223372036854774784" +test_format "#{e|-|:-9223372036854775808,0}" "-9223372036854775808" +test_format "#{e|/|f:5,0}" "inf" +assert_alive "expressions with no integer value" # --- ASCII and repeat ---------------------------------------------------- diff --git a/regress/menu-pane-killed.sh b/regress/menu-pane-killed.sh new file mode 100755 index 000000000..4290ab6a6 --- /dev/null +++ b/regress/menu-pane-killed.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# A menu opened for a pane is drawn again after the pane is killed. Drawing it +# must not use the pane. The terminal is a pane of a second server. + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -Ltest$$ -f/dev/null" +TMUX2="$TEST_TMUX -Ltest2$$ -f/dev/null" +trap '$TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null' 0 1 15 + +$TMUX new -d 'sleep 1000' || exit 1 +$TMUX2 new -d "unset TMUX; exec $TMUX attach" || exit 1 +n=0 +while [ -z "$($TMUX lsc 2>/dev/null)" ]; do + n=$((n + 1)) + [ $n -gt 100 ] && exit 1 + sleep 0.1 +done + +P=$($TMUX splitw -d -P -F '#{pane_id}' 'sleep 1000') || exit 1 +$TMUX display-menu -c "$($TMUX lsc -F '#{client_name}')" -t $P one 1 '' || + exit 1 +$TMUX kill-pane -t $P +sleep 1 +$TMUX has || { echo "the server died"; exit 1; } +exit 0