mirror of
https://github.com/tmux/tmux.git
synced 2026-10-09 11:44:23 +00:00
Merge branch 'master' into command_parser
This commit is contained in:
77
regress/control-client-exit-stalled.sh
Executable file
77
regress/control-client-exit-stalled.sh
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/bin/sh
|
||||
|
||||
# A control client whose terminal has stopped accepting output (for example
|
||||
# its ssh connection or terminal emulator died) never drains the output that
|
||||
# server_client_check_exit waits on before sending MSG_EXIT, and may then
|
||||
# never close its socket either. This must not prevent the server from
|
||||
# exiting: after kill-server the server previously stayed alive forever with
|
||||
# server_exit set, closing every new connection immediately, so every new
|
||||
# client failed with "server exited unexpectedly" until the server was killed
|
||||
# by hand. The server now bounds the exit handshake: it discards output it
|
||||
# can never deliver and drops the client if it still does not close.
|
||||
#
|
||||
# The stalled terminal is simulated by a fifo whose read end is held open but
|
||||
# never read.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
DIR=$(mktemp -d)
|
||||
FIFO=$DIR/fifo
|
||||
SERVER=
|
||||
|
||||
mkfifo "$FIFO" || exit 1
|
||||
|
||||
cleanup() {
|
||||
[ -n "$SERVER" ] && kill -9 "$SERVER" 2>/dev/null
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exec 8<&- 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
# A detached session whose pane floods printable output forever.
|
||||
$TMUX -f/dev/null new -d -x 80 -y 24 -s rt 'cat /dev/zero | tr "\000" x' || exit 1
|
||||
SERVER=$($TMUX display -pt rt '#{pid}')
|
||||
|
||||
# Attach a control client with output down the fifo; stdin held open by sleep
|
||||
# so it stays attached. The fifo fills and is never read, so a backlog forms
|
||||
# in the server that can never be delivered.
|
||||
( sleep 60 ) | $TMUX -f/dev/null -C attach -t rt >"$FIFO" 2>&1 &
|
||||
exec 8<"$FIFO"
|
||||
|
||||
n=0
|
||||
while [ $n -lt 50 ]; do
|
||||
$TMUX lsc -F '#{client_name}' 2>/dev/null | grep -q . && break
|
||||
sleep 0.1
|
||||
n=$((n + 1))
|
||||
done
|
||||
$TMUX lsc -F '#{client_name}' 2>/dev/null | grep -q . ||
|
||||
{ echo "control client did not attach"; exit 1; }
|
||||
|
||||
# Let the pane flood fill the fifo so the client's output is stuck.
|
||||
sleep 2
|
||||
|
||||
# Ask the server to exit. This sets CLIENT_EXIT on the stalled client, whose
|
||||
# output can never drain.
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
# The server must exit within the exit timeout (10 seconds) plus slack,
|
||||
# rather than staying in limbo forever rejecting new clients.
|
||||
n=0
|
||||
while [ $n -lt 100 ]; do
|
||||
if ! kill -0 "$SERVER" 2>/dev/null; then
|
||||
SERVER=
|
||||
exit 0
|
||||
fi
|
||||
sleep 0.2
|
||||
n=$((n + 1))
|
||||
done
|
||||
|
||||
echo "server did not exit after kill-server; new clients see:"
|
||||
$TMUX ls 2>&1
|
||||
exit 1
|
||||
158
regress/control-notify-guard.sh
Executable file
158
regress/control-notify-guard.sh
Executable file
@@ -0,0 +1,158 @@
|
||||
#!/bin/sh
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
LANG=C.UTF-8
|
||||
export TERM LC_ALL LANG
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
TMPDIR=$(mktemp -d)
|
||||
IN="$TMPDIR/in"
|
||||
OUT="$TMPDIR/out"
|
||||
BADCFG="$TMPDIR/bad.conf"
|
||||
PID=
|
||||
|
||||
cleanup()
|
||||
{
|
||||
exec 3>&- 2>/dev/null
|
||||
[ -n "$PID" ] && kill "$PID" 2>/dev/null
|
||||
$TMUX kill-server 2>/dev/null
|
||||
rm -rf "$TMPDIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$1" >&2
|
||||
[ -s "$OUT" ] && cat "$OUT" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
wait_for()
|
||||
{
|
||||
pattern=$1
|
||||
timeout=${2:-60}
|
||||
i=0
|
||||
|
||||
while [ "$i" -lt "$timeout" ]; do
|
||||
grep -F -- "$pattern" "$OUT" >/dev/null 2>&1 && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "missing: $pattern"
|
||||
}
|
||||
|
||||
wait_for_count()
|
||||
{
|
||||
pattern=$1
|
||||
expected=$2
|
||||
timeout=${3:-60}
|
||||
i=0
|
||||
|
||||
while [ "$i" -lt "$timeout" ]; do
|
||||
count=$(grep -F -c -- "$pattern" "$OUT" 2>/dev/null)
|
||||
[ "$count" -ge "$expected" ] && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "missing count $expected for: $pattern"
|
||||
}
|
||||
|
||||
send()
|
||||
{
|
||||
printf '%s\n' "$1" >&3 || fail "failed to send: $1"
|
||||
}
|
||||
|
||||
# A notification is a standalone protocol message. It must never be part of
|
||||
# the response between the real %begin and matching %end or %error for a
|
||||
# command. Ignore guard-looking command output while a real guard is open.
|
||||
check_guards()
|
||||
{
|
||||
awk '
|
||||
function notification(line) {
|
||||
return line ~ /^%(sessions-changed|session-changed |unlinked-window-add )/ ||
|
||||
line ~ /^%(pause |continue |config-error )/
|
||||
}
|
||||
!open && $0 ~ /^%begin [0-9]+ [0-9]+ [0-9]+$/ {
|
||||
open = 1
|
||||
time = $2
|
||||
number = $3
|
||||
flags = $4
|
||||
next
|
||||
}
|
||||
open && ($0 == "%end " time " " number " " flags ||
|
||||
$0 == "%error " time " " number " " flags) {
|
||||
open = 0
|
||||
next
|
||||
}
|
||||
notification($0) {
|
||||
seen[$1]++
|
||||
if (open) {
|
||||
print "notification inside guard: " $0 > "/dev/stderr"
|
||||
bad = 1
|
||||
}
|
||||
}
|
||||
END {
|
||||
if (open) {
|
||||
print "unterminated command guard" > "/dev/stderr"
|
||||
bad = 1
|
||||
}
|
||||
if (seen["%sessions-changed"] < 2 ||
|
||||
seen["%session-changed"] < 1 ||
|
||||
seen["%unlinked-window-add"] < 2 ||
|
||||
seen["%pause"] < 1 || seen["%continue"] < 1 ||
|
||||
seen["%config-error"] < 1) {
|
||||
print "missing expected notification" > "/dev/stderr"
|
||||
bad = 1
|
||||
}
|
||||
exit bad
|
||||
}' "$OUT" || fail "bad control protocol notification ordering"
|
||||
}
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX new-session -d -s guard -x 80 -y 24 || exit 1
|
||||
pane=$($TMUX display-message -p -t guard:0.0 '#{pane_id}') || exit 1
|
||||
|
||||
mkfifo "$IN" || exit 1
|
||||
: >"$OUT"
|
||||
$TMUX -C attach-session -t guard <"$IN" >"$OUT" 2>&1 &
|
||||
PID=$!
|
||||
exec 3>"$IN"
|
||||
|
||||
# Attaching generates a session notification synchronously from inside the
|
||||
# attach command. It must follow the closing guard.
|
||||
wait_for '%session-changed '
|
||||
|
||||
# Guard-looking output is arbitrary command output, not protocol state. Make
|
||||
# new-session both generate notifications and print an unmatched fake %begin;
|
||||
# it must not leave those notifications permanently deferred.
|
||||
send "set-option -g @fake-begin '%begin 1 2 3'"
|
||||
send "new-session -d -P -F '#{@fake-begin}' -s fake-begin"
|
||||
wait_for_count '%sessions-changed' 1
|
||||
|
||||
# Likewise, fake %end output from a command that generates multiple
|
||||
# notifications must not flush them before the command actually ends.
|
||||
send "set-option -g @fake-end '%end 1 2 3'"
|
||||
send "new-session -d -P -F '#{@fake-end}' -s fake-end"
|
||||
wait_for_count '%sessions-changed' 2
|
||||
|
||||
# Exercise notification paths outside control-notify.c.
|
||||
send "refresh-client -A '$pane:pause'"
|
||||
wait_for "%pause $pane"
|
||||
send "refresh-client -A '$pane:continue'"
|
||||
wait_for "%continue $pane"
|
||||
|
||||
printf '%s\n' 'not-a-command' >"$BADCFG"
|
||||
send "source-file '$BADCFG'"
|
||||
wait_for '%config-error '
|
||||
|
||||
# A final command guarantees all earlier command guards have completed before
|
||||
# the transcript is checked.
|
||||
send "display-message -p 'guard-check-done'"
|
||||
wait_for 'guard-check-done'
|
||||
check_guards
|
||||
|
||||
exit 0
|
||||
@@ -114,5 +114,30 @@ $TMUX send-keys -X next-space-end
|
||||
$TMUX send-keys -X copy-selection
|
||||
[ "$($TMUX show-buffer)" = "500xyz" ] || exit 1
|
||||
|
||||
# Test that vi cursor movement does not stop on the padding cell of a wide
|
||||
# character at the end of a line.
|
||||
$TMUX kill-server 2>/dev/null
|
||||
sleep 1
|
||||
$TMUX new -d -x20 -y5 \
|
||||
"printf 'abc中\nxyz\n'; exec cat" || exit 1
|
||||
$TMUX set-window-option -g mode-keys vi
|
||||
$TMUX copy-mode
|
||||
$TMUX send-keys -X history-top
|
||||
$TMUX send-keys -X start-of-line
|
||||
$TMUX send-keys -X cursor-right
|
||||
$TMUX send-keys -X cursor-right
|
||||
$TMUX send-keys -X cursor-right
|
||||
[ "$($TMUX display -p '#{copy_cursor_x},#{copy_cursor_y}')" = "3,0" ] ||
|
||||
exit 1
|
||||
$TMUX send-keys -X cursor-right
|
||||
[ "$($TMUX display -p '#{copy_cursor_x},#{copy_cursor_y}')" = "0,1" ] ||
|
||||
exit 1
|
||||
$TMUX send-keys -X cursor-left
|
||||
[ "$($TMUX display -p '#{copy_cursor_x},#{copy_cursor_y}')" = "3,0" ] ||
|
||||
exit 1
|
||||
$TMUX send-keys -X cursor-left
|
||||
[ "$($TMUX display -p '#{copy_cursor_x},#{copy_cursor_y}')" = "2,0" ] ||
|
||||
exit 1
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 0
|
||||
|
||||
@@ -192,13 +192,13 @@ check_fail "invalid window name: $(printf 'a\377b')" \
|
||||
break-pane -d -n "$(printf 'a\377b')" -s "$p1" -t P:
|
||||
|
||||
# join-pane can move a pane from one window to another without destroying
|
||||
# the source window if other panes remain. (On this branch move-pane is
|
||||
# reserved for floating panes, covered by floating-pane-geometry.sh.)
|
||||
# the source window if other panes remain. move-pane does the same when no
|
||||
# floating-pane movement flags are given.
|
||||
check_ok new-window -d -t P:5 -n other
|
||||
check_ok join-pane -d -s "$p1" -t P:5.0
|
||||
check_fmt 'P:5' '#{window_panes}' '2'
|
||||
check_fmt 'P:0' '#{window_panes}' '3'
|
||||
check_ok join-pane -d -v -s "$p1" -t "$p2"
|
||||
check_ok move-pane -d -v -s "$p1" -t "$p2"
|
||||
check_fmt 'P:0' '#{window_panes}' '4'
|
||||
check_fmt 'P:5' '#{window_panes}' '1'
|
||||
|
||||
|
||||
@@ -35,9 +35,9 @@ check_alive()
|
||||
|
||||
# A pipe-pane -I child may write after the pane process has exited. With
|
||||
# remain-on-exit, the pane stays around but its bufferevent has been freed.
|
||||
check_ok new-session -d -s pipe -x 80 -y 24 'sleep 0.2'
|
||||
check_ok new-session -d -s pipe -x 80 -y 24 'sleep 1'
|
||||
check_ok set-option -t pipe:0 remain-on-exit on
|
||||
check_ok pipe-pane -t pipe:0.0 -I 'sleep 0.6; printf x'
|
||||
check_ok pipe-pane -t pipe:0.0 -I 'sleep 2; printf x'
|
||||
|
||||
i=0
|
||||
while [ "$($TMUX display-message -p -t pipe:0.0 '#{pane_dead}')" != "1" ]; do
|
||||
@@ -46,7 +46,7 @@ while [ "$($TMUX display-message -p -t pipe:0.0 '#{pane_dead}')" != "1" ]; do
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
sleep 0.7
|
||||
sleep 2
|
||||
check_alive
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
49
regress/server-socket-error.sh
Executable file
49
regress/server-socket-error.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Server socket creation failures must produce a nonzero client exit status.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
export TERM
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
# Root bypasses directory mode bits, so it cannot trigger this failure.
|
||||
[ "$(id -u)" -eq 0 ] && exit 0
|
||||
|
||||
TMP=$(mktemp -d) || exit 1
|
||||
SOCKET="$TMP/ro/socket"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
chmod 700 "$TMP/ro" 2>/dev/null
|
||||
rm -rf "$TMP"
|
||||
}
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$1" >&2
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
|
||||
mkdir "$TMP/ro" || exit 1
|
||||
chmod 500 "$TMP/ro" || exit 1
|
||||
trap cleanup 0 1 15
|
||||
|
||||
check_failure()
|
||||
{
|
||||
output=$($TEST_TMUX -S "$SOCKET" -f/dev/null "$@" 2>&1)
|
||||
retval=$?
|
||||
|
||||
[ "$retval" -ne 0 ] || fail "tmux $* exited zero: $output"
|
||||
case "$output" in
|
||||
"error creating $SOCKET ("*) ;;
|
||||
*) fail "tmux $* produced unexpected error: $output" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
check_failure start-server
|
||||
check_failure new-session -d
|
||||
|
||||
exit 0
|
||||
43
regress/tab-cell-background.sh
Executable file
43
regress/tab-cell-background.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/sh
|
||||
|
||||
# When a write breaks up a wide character or a tab, the columns it covered must
|
||||
# keep the background colour, both in the grid and on the terminal. The second
|
||||
# server is attached to the first so that what is actually sent is checked, not
|
||||
# just what tmux has stored.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
trap "rm -f $TMP" 0 1 15
|
||||
|
||||
# 1. Write inside a tab. 2. Overwrite the right half of a wide character.
|
||||
# 3. Two tabs of different colours, the first partly deleted so that padding
|
||||
# from both ends up next to each other, then written over.
|
||||
# The sleep is so that the client is attached before anything is written and
|
||||
# the changes are sent as they happen rather than as one redraw.
|
||||
$TMUX2 -f/dev/null new -d -x24 -y3 "
|
||||
sleep 2
|
||||
printf '\033[44m\033[K\t\033[4GX'
|
||||
printf '\033[2;1H\033[44m\033[K\344\270\226\033[2GX'
|
||||
printf '\033[3;1H\033[42m\033[K\t\033[43m\033[K\t\033[0m\033[4G\033[8P\rx'
|
||||
cat" || exit 1
|
||||
$TMUX2 set -g status off || exit 1
|
||||
|
||||
$TMUX -f/dev/null new -x24 -y4 -d "$TMUX2 attach" || exit 1
|
||||
sleep 4
|
||||
|
||||
$TMUX capturep -pe -S0 -E2 >$TMP || exit 1
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
printf '\033[44m X\n X\n\033[49mx\033[42m \033[43m \033[49m\n' |
|
||||
cmp - $TMP || exit 1
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user