mirror of
https://github.com/tmux/tmux.git
synced 2026-10-10 20:24:21 +00:00
Merge remote-tracking branch 'origin/master' into command_parser
# Conflicts: # cmd-queue.c # notify.c # options.c
This commit is contained in:
@@ -1,19 +1,29 @@
|
||||
TESTS!= echo *.sh
|
||||
LOGDIR=logs
|
||||
|
||||
.PHONY: all $(TESTS)
|
||||
.NOTPARALLEL: all $(TESTS)
|
||||
.PHONY: all
|
||||
.NOTPARALLEL: all
|
||||
|
||||
all:
|
||||
@failed=0; failures=; \
|
||||
@mkdir -p "$(LOGDIR)"; \
|
||||
rm -f "$(LOGDIR)"/*.log; \
|
||||
failed=0; failures=; \
|
||||
for test in $(TESTS); do \
|
||||
base=$${test##*/}; \
|
||||
log="$(LOGDIR)/$${base%.sh}.log"; \
|
||||
rm -f "$$log"; \
|
||||
printf '%-40s ' "$$test"; \
|
||||
start=$$(date +%s); \
|
||||
if sh "$$test" >/dev/null 2>&1; then \
|
||||
env -i LC_CTYPE=C.UTF-8 MallocNanoZone=0 \
|
||||
sh -x "$$test" >"$$log" 2>&1; \
|
||||
if [ $$? -eq 0 ]; then \
|
||||
end=$$(date +%s); \
|
||||
rm -f "$$log"; \
|
||||
echo "PASS ($$((end - start))s)"; \
|
||||
else \
|
||||
end=$$(date +%s); \
|
||||
echo "FAIL ($$((end - start))s)"; \
|
||||
echo " log: $$log"; \
|
||||
failed=1; \
|
||||
failures="$$failures $$test"; \
|
||||
fi; \
|
||||
@@ -25,9 +35,7 @@ all:
|
||||
for test in $$failures; do \
|
||||
echo " $$test"; \
|
||||
done; \
|
||||
else \
|
||||
rmdir "$(LOGDIR)" 2>/dev/null || true; \
|
||||
fi; \
|
||||
exit $$failed
|
||||
|
||||
$(TESTS):
|
||||
sh $@
|
||||
sleep 1
|
||||
|
||||
291
regress/alerts.sh
Normal file
291
regress/alerts.sh
Normal file
@@ -0,0 +1,291 @@
|
||||
#!/bin/sh
|
||||
|
||||
# monitor-activity, monitor-bell and monitor-silence: both the
|
||||
# alert-activity, alert-bell and alert-silence hooks and the winlink alert
|
||||
# flags (window_activity_flag, window_bell_flag, window_silence_flag and
|
||||
# the #, !, ~ characters in window_flags). The sessions are detached so
|
||||
# alert flags are set even on the current window; the *-action options
|
||||
# still decide whether the hooks fire. Panes run cat: activity is
|
||||
# generated by the tty echo of send-keys and a bell by sending a BEL and
|
||||
# newline for cat to write back.
|
||||
|
||||
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)
|
||||
OUT=$(mktemp -d)
|
||||
TMUX_TMPDIR="$OUT"
|
||||
export TMUX_TMPDIR
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
wait_for()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 30 ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] && return 0
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
fail "expected $option to be '$expected' but got '$value'"
|
||||
}
|
||||
|
||||
assert_unchanged()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 10 ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] || \
|
||||
fail "expected $option to remain '$expected' but got '$value'"
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
}
|
||||
|
||||
wait_for_fmt()
|
||||
{
|
||||
target=$1
|
||||
fmt=$2
|
||||
expected=$3
|
||||
i=0
|
||||
|
||||
while [ $i -lt 30 ]; do
|
||||
value=$($TMUX display -pt "$target" "$fmt" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] && return 0
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
fail "expected $fmt for $target to be '$expected' but got '$value'"
|
||||
}
|
||||
|
||||
assert_fmt_unchanged()
|
||||
{
|
||||
target=$1
|
||||
fmt=$2
|
||||
expected=$3
|
||||
i=0
|
||||
|
||||
while [ $i -lt 10 ]; do
|
||||
value=$($TMUX display -pt "$target" "$fmt" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] || \
|
||||
fail "expected $fmt for $target to remain '$expected' but got '$value'"
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
}
|
||||
|
||||
flags_have()
|
||||
{
|
||||
target=$1
|
||||
char=$2
|
||||
|
||||
flags=$($TMUX display -pt "$target" '#{window_flags}') ||
|
||||
fail "display window_flags failed"
|
||||
case "$flags" in
|
||||
*"$char"*) ;;
|
||||
*) fail "expected '$char' in window_flags for $target: '$flags'" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
flags_lack()
|
||||
{
|
||||
target=$1
|
||||
char=$2
|
||||
|
||||
flags=$($TMUX display -pt "$target" '#{window_flags}') ||
|
||||
fail "display window_flags failed"
|
||||
case "$flags" in
|
||||
*"$char"*) fail "unexpected '$char' in window_flags for $target: '$flags'" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
bell()
|
||||
{
|
||||
$TMUX send-keys -t "$1" -H 07 0a || fail "send-keys bell failed"
|
||||
}
|
||||
|
||||
activity()
|
||||
{
|
||||
$TMUX send-keys -t "$1" -l x || fail "send-keys activity failed"
|
||||
}
|
||||
|
||||
$TMUX new -d -s mon -n w0 cat || fail "new-session mon failed"
|
||||
|
||||
$TMUX set -g @log '' || fail "set @log failed"
|
||||
$TMUX set-hook -g alert-bell \
|
||||
'set -gF @log "#{@log}|alert-bell:#{hook_session_name}:#{hook_window_name}"' ||
|
||||
fail "set-hook alert-bell failed"
|
||||
$TMUX set-hook -g alert-activity \
|
||||
'set -gF @log "#{@log}|alert-activity:#{hook_session_name}:#{hook_window_name}"' ||
|
||||
fail "set-hook alert-activity failed"
|
||||
$TMUX set-hook -g alert-silence \
|
||||
'set -gF @log "#{@log}|alert-silence:#{hook_session_name}:#{hook_window_name}"' ||
|
||||
fail "set-hook alert-silence failed"
|
||||
|
||||
# A bell in a non-current window (monitor-bell defaults to on) fires
|
||||
# alert-bell and sets the bell flag, shown as ! in window_flags.
|
||||
$TMUX neww -d -t mon: -n bellw cat || fail "new-window bellw failed"
|
||||
assert_fmt_unchanged mon:bellw '#{window_bell_flag}' 0
|
||||
flags_lack mon:bellw '!'
|
||||
bell mon:bellw
|
||||
wait_for @log '|alert-bell:mon:bellw'
|
||||
wait_for_fmt mon:bellw '#{window_bell_flag}' 1
|
||||
flags_have mon:bellw '!'
|
||||
|
||||
# Bells are not deduplicated: a second bell fires the hook again even
|
||||
# though the flag is still set.
|
||||
bell mon:bellw
|
||||
wait_for @log '|alert-bell:mon:bellw|alert-bell:mon:bellw'
|
||||
|
||||
# Selecting the window clears the alert flags.
|
||||
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
|
||||
wait_for_fmt mon:bellw '#{window_bell_flag}' 0
|
||||
flags_lack mon:bellw '!'
|
||||
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||
|
||||
# A bell in the current window of a detached session still sets the flag
|
||||
# and bell-action any (the default) fires the hook for the current window.
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
bell mon:w0
|
||||
wait_for @log '|alert-bell:mon:w0'
|
||||
wait_for_fmt mon:w0 '#{window_bell_flag}' 1
|
||||
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
|
||||
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||
wait_for_fmt mon:w0 '#{window_bell_flag}' 0
|
||||
|
||||
# bell-action none: the flag is still set but the hook does not fire.
|
||||
$TMUX set -t mon bell-action none || fail "set bell-action none failed"
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
bell mon:bellw
|
||||
wait_for_fmt mon:bellw '#{window_bell_flag}' 1
|
||||
assert_unchanged @log ''
|
||||
$TMUX set -ut mon bell-action || fail "unset bell-action failed"
|
||||
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
|
||||
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||
|
||||
# monitor-bell off: neither the flag nor the hook.
|
||||
$TMUX set -wt mon:bellw monitor-bell off || fail "set monitor-bell failed"
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
bell mon:bellw
|
||||
assert_fmt_unchanged mon:bellw '#{window_bell_flag}' 0
|
||||
assert_unchanged @log ''
|
||||
|
||||
# monitor-activity defaults to off: output sets neither flag nor hook.
|
||||
$TMUX neww -d -t mon: -n actw cat || fail "new-window actw failed"
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
activity mon:actw
|
||||
assert_fmt_unchanged mon:actw '#{window_activity_flag}' 0
|
||||
assert_unchanged @log ''
|
||||
|
||||
# With monitor-activity on, output fires alert-activity and sets the
|
||||
# activity flag, shown as # in window_flags.
|
||||
$TMUX set -wt mon:actw monitor-activity on ||
|
||||
fail "set monitor-activity failed"
|
||||
activity mon:actw
|
||||
wait_for @log '|alert-activity:mon:actw'
|
||||
wait_for_fmt mon:actw '#{window_activity_flag}' 1
|
||||
flags_have mon:actw '#'
|
||||
|
||||
# While the flag is set further activity does not fire the hook again.
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
activity mon:actw
|
||||
assert_unchanged @log ''
|
||||
|
||||
# On a detached session selecting the window does not clear the activity
|
||||
# flag: session_set_current treats the selection itself as activity and
|
||||
# the flag is raised again at once because the session is not attached.
|
||||
# No hook fires as activity-action other does not apply to the current
|
||||
# window.
|
||||
$TMUX selectw -t mon:actw || fail "select-window actw failed"
|
||||
assert_fmt_unchanged mon:actw '#{window_activity_flag}' 1
|
||||
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||
assert_fmt_unchanged mon:actw '#{window_activity_flag}' 1
|
||||
assert_unchanged @log ''
|
||||
|
||||
# With a client attached, selecting the window does clear the activity
|
||||
# flag and it stays clear for the current window.
|
||||
mkfifo "$OUT/fifo" || fail "mkfifo failed"
|
||||
$TMUX -C attach -t mon <"$OUT/fifo" >"$OUT/control.out" 2>&1 &
|
||||
exec 3>"$OUT/fifo"
|
||||
wait_for_fmt mon: '#{session_attached}' 1
|
||||
$TMUX selectw -t mon:actw || fail "select-window actw failed"
|
||||
wait_for_fmt mon:actw '#{window_activity_flag}' 0
|
||||
flags_lack mon:actw '#'
|
||||
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||
exec 3>&-
|
||||
wait_for_fmt mon: '#{session_attached}' 0
|
||||
|
||||
# Once the flag is clear the alert is re-armed.
|
||||
activity mon:actw
|
||||
wait_for @log '|alert-activity:mon:actw'
|
||||
|
||||
# Activity in the current window of a detached session sets the flag but
|
||||
# activity-action other (the default) does not fire the hook.
|
||||
$TMUX set -wt mon:w0 monitor-activity on ||
|
||||
fail "set monitor-activity w0 failed"
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
activity mon:w0
|
||||
wait_for_fmt mon:w0 '#{window_activity_flag}' 1
|
||||
assert_unchanged @log ''
|
||||
$TMUX set -wut mon:w0 monitor-activity ||
|
||||
fail "unset monitor-activity w0 failed"
|
||||
|
||||
# monitor-silence: a quiet window fires alert-silence after the interval
|
||||
# and sets the silence flag, shown as ~ in window_flags. The hook fires
|
||||
# only once while the flag remains set even though the timer keeps
|
||||
# running.
|
||||
$TMUX neww -d -t mon: -n silw cat || fail "new-window silw failed"
|
||||
assert_fmt_unchanged mon:silw '#{window_silence_flag}' 0
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
$TMUX set -wt mon:silw monitor-silence 1 || fail "set monitor-silence failed"
|
||||
wait_for @log '|alert-silence:mon:silw'
|
||||
wait_for_fmt mon:silw '#{window_silence_flag}' 1
|
||||
flags_have mon:silw '~'
|
||||
assert_unchanged @log '|alert-silence:mon:silw'
|
||||
$TMUX set -wt mon:silw monitor-silence 0 ||
|
||||
fail "reset monitor-silence failed"
|
||||
$TMUX selectw -t mon:silw || fail "select-window silw failed"
|
||||
wait_for_fmt mon:silw '#{window_silence_flag}' 0
|
||||
flags_lack mon:silw '~'
|
||||
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
|
||||
|
||||
# A window linked into two sessions: the hook fires once per winlink and
|
||||
# the flag is set in both sessions. Selecting the window in one session
|
||||
# clears the flags on every winlink.
|
||||
$TMUX new -d -s mon2 || fail "new-session mon2 failed"
|
||||
$TMUX neww -d -t mon: -n shw cat || fail "new-window shw failed"
|
||||
$TMUX link-window -d -s mon:shw -t mon2:5 || fail "link-window failed"
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
bell mon:shw
|
||||
wait_for @log '|alert-bell:mon:shw|alert-bell:mon2:shw'
|
||||
wait_for_fmt mon:shw '#{window_bell_flag}' 1
|
||||
wait_for_fmt mon2:5 '#{window_bell_flag}' 1
|
||||
$TMUX selectw -t mon2:5 || fail "select-window mon2:5 failed"
|
||||
wait_for_fmt mon2:5 '#{window_bell_flag}' 0
|
||||
wait_for_fmt mon:shw '#{window_bell_flag}' 0
|
||||
|
||||
exit 0
|
||||
@@ -4,9 +4,9 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -11,9 +11,9 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
TMUX_OUTER="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX_OUTER="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
$TMUX_OUTER kill-server 2>/dev/null
|
||||
|
||||
trap "$TMUX kill-server 2>/dev/null; $TMUX_OUTER kill-server 2>/dev/null" 0 1 15
|
||||
|
||||
287
regress/buffers.sh
Normal file
287
regress/buffers.sh
Normal file
@@ -0,0 +1,287 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Tests of paste buffer command semantics, as implemented in cmd-set-buffer.c
|
||||
# (set-buffer and delete-buffer), cmd-paste-buffer.c, cmd-load-buffer.c,
|
||||
# cmd-save-buffer.c (save-buffer and show-buffer), cmd-list-buffers.c and
|
||||
# paste.c.
|
||||
#
|
||||
# This exercises:
|
||||
# - set-buffer creating automatic buffers (buffer0, buffer1, ... with the
|
||||
# most recent first), -b creating/replacing a named buffer, -a appending,
|
||||
# -n renaming and the error paths (no data, unknown buffer);
|
||||
# - show-buffer for the top and for named buffers;
|
||||
# - delete-buffer for the top and named buffers, and when nothing exists;
|
||||
# - list-buffers -F custom formats and -f filters;
|
||||
# - paste-buffer into a pane: newline-to-CR translation by default, -r raw,
|
||||
# -s custom separator, -d delete-after-paste, unknown buffer error;
|
||||
# - the buffer-limit option evicting the oldest automatic buffers but not
|
||||
# named buffers;
|
||||
# - load-buffer/save-buffer round trips including control characters and
|
||||
# UTF-8, save-buffer -a appending and errors for missing files/buffers.
|
||||
|
||||
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
|
||||
|
||||
TMP=$(mktemp)
|
||||
TMP2=$(mktemp)
|
||||
trap 'rm -f "$TMP" "$TMP2"; $TMUX kill-server 2>/dev/null' 0 1 15
|
||||
|
||||
# check_ok $cmd...
|
||||
#
|
||||
# Run a command and require that it succeeds.
|
||||
check_ok()
|
||||
{
|
||||
if ! $TMUX "$@"; then
|
||||
echo "Command failed (expected success): $*"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# check_fail $expected_error $cmd...
|
||||
#
|
||||
# Run a command and require that it fails with the given error message.
|
||||
check_fail()
|
||||
{
|
||||
exp="$1"
|
||||
shift
|
||||
out=$($TMUX "$@" 2>&1)
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Command succeeded (expected failure): $*"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$out" != "$exp" ]; then
|
||||
echo "Wrong error for: $*"
|
||||
echo "Expected: '$exp'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# check_buffers $expected
|
||||
#
|
||||
# Compare the buffer list (as "name=content ...", most recent first) with
|
||||
# $expected.
|
||||
check_buffers()
|
||||
{
|
||||
out=$(echo $($TMUX list-buffers -F \
|
||||
'#{buffer_name}=#{buffer_sample}'))
|
||||
if [ "$out" != "$1" ]; then
|
||||
echo "Buffer list wrong."
|
||||
echo "Expected: '$1'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# check_show $args $expected
|
||||
#
|
||||
# Compare show-buffer output with $expected.
|
||||
check_show()
|
||||
{
|
||||
out=$($TMUX show-buffer $1 2>&1)
|
||||
if [ "$out" != "$2" ]; then
|
||||
echo "show-buffer $1 wrong."
|
||||
echo "Expected: '$2'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_alive()
|
||||
{
|
||||
if [ "$($TMUX display-message -p alive 2>&1)" != "alive" ]; then
|
||||
echo "Server died: $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_ok new-session -d -s B -x 80 -y 24
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# set-buffer, show-buffer, delete-buffer, list-buffers.
|
||||
|
||||
# Automatic buffers stack with the most recent first.
|
||||
check_ok set-buffer one
|
||||
check_ok set-buffer two
|
||||
check_buffers 'buffer1=two buffer0=one'
|
||||
check_show '' 'two'
|
||||
check_show '-b buffer0' 'one'
|
||||
|
||||
# -a only appends to a buffer named with -b: without -b it creates a new
|
||||
# automatic buffer. Empty data is silently ignored.
|
||||
check_ok set-buffer -a '!'
|
||||
check_buffers 'buffer2=! buffer1=two buffer0=one'
|
||||
check_ok delete-buffer
|
||||
check_ok set-buffer ''
|
||||
check_buffers 'buffer1=two buffer0=one'
|
||||
|
||||
# -b names a buffer explicitly; setting it again replaces the content;
|
||||
# -a appends to it.
|
||||
check_ok set-buffer -b named abc
|
||||
check_buffers 'named=abc buffer1=two buffer0=one'
|
||||
check_ok set-buffer -b named xyz
|
||||
check_ok set-buffer -a -b named 123
|
||||
check_buffers 'named=xyz123 buffer1=two buffer0=one'
|
||||
check_show '-b named' 'xyz123'
|
||||
|
||||
# -n renames; renaming to a bad source is an error, as is no data at all.
|
||||
check_ok set-buffer -b named -n other
|
||||
check_buffers 'other=xyz123 buffer1=two buffer0=one'
|
||||
check_fail 'unknown buffer: nosuch' set-buffer -b nosuch -n foo
|
||||
check_fail 'no data specified' set-buffer -b other
|
||||
check_fail 'no buffer nosuch' show-buffer -b nosuch
|
||||
|
||||
# list-buffers -f filters.
|
||||
out=$($TMUX list-buffers -f '#{==:#{buffer_name},other}' -F '#{buffer_name}')
|
||||
if [ "$out" != "other" ]; then
|
||||
echo "list-buffers -f wrong: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# delete-buffer -b removes one buffer; without -b the most recent automatic
|
||||
# buffer goes - named buffers are not candidates for the top, for
|
||||
# show-buffer and delete-buffer alike.
|
||||
check_ok delete-buffer -b buffer1
|
||||
check_buffers 'other=xyz123 buffer0=one'
|
||||
check_fail 'unknown buffer: buffer1' delete-buffer -b buffer1
|
||||
check_ok delete-buffer
|
||||
check_buffers 'other=xyz123'
|
||||
check_fail 'no buffers' show-buffer
|
||||
check_fail 'no buffer' delete-buffer
|
||||
check_ok delete-buffer -b other
|
||||
check_fail 'no buffers' show-buffer
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# buffer-limit.
|
||||
|
||||
# Only automatic buffers count against buffer-limit and the oldest are
|
||||
# evicted; named buffers survive. (Automatic buffer numbers keep counting
|
||||
# up over the life of the server, so compare content only.)
|
||||
check_ok set-option -g buffer-limit 3
|
||||
check_ok set-buffer -b keepme precious
|
||||
check_ok set-buffer a1
|
||||
check_ok set-buffer a2
|
||||
check_ok set-buffer a3
|
||||
check_ok set-buffer a4
|
||||
out=$(echo $($TMUX list-buffers -F '#{buffer_sample}'))
|
||||
if [ "$out" != 'a4 a3 a2 precious' ]; then
|
||||
echo "buffer-limit eviction wrong: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
check_ok set-option -g buffer-limit 50
|
||||
check_ok delete-buffer -b keepme
|
||||
check_ok delete-buffer; check_ok delete-buffer; check_ok delete-buffer
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# paste-buffer.
|
||||
|
||||
# Paste into a raw, echo-free pane running cat -v so control characters are
|
||||
# visible; a fresh window per paste keeps assertions simple.
|
||||
|
||||
# paste_line $bufdata $pasteargs $expected
|
||||
#
|
||||
# Set a buffer, paste it into a fresh cat -v pane and compare the first
|
||||
# screen line with $expected.
|
||||
paste_line()
|
||||
{
|
||||
$TMUX kill-window -t B:9 2>/dev/null
|
||||
check_ok new-window -d -t B:9 'stty raw -echo && exec cat -v'
|
||||
i=0
|
||||
while [ "$($TMUX display-message -p -t B:9.0 \
|
||||
'#{pane_current_command}')" != "cat" ]; do
|
||||
i=$((i + 1))
|
||||
[ $i -gt 50 ] && { echo "cat did not start."; exit 1; }
|
||||
sleep 0.1
|
||||
done
|
||||
check_ok set-buffer -b paste "$1"
|
||||
check_ok paste-buffer $2 -b paste -t B:9.0
|
||||
i=0
|
||||
while out=$($TMUX capture-pane -p -t B:9.0 | sed -n 1p) && \
|
||||
[ "$out" != "$3" ]; do
|
||||
i=$((i + 1))
|
||||
if [ $i -gt 50 ]; then
|
||||
echo "Paste of '$1' ($2) wrong."
|
||||
echo "Expected: '$3'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
}
|
||||
|
||||
# By default linefeeds are replaced with carriage returns (shown as ^M by
|
||||
# cat -v); -r pastes raw and -s sets an explicit separator.
|
||||
paste_line 'one
|
||||
two' '' 'one^Mtwo'
|
||||
paste_line 'one
|
||||
two' '-r' 'one'
|
||||
paste_line 'one
|
||||
two' '-s |' 'one|two'
|
||||
paste_line 'one
|
||||
two' '-s XX' 'oneXXtwo'
|
||||
|
||||
# -d deletes the buffer after pasting.
|
||||
paste_line 'gone' '-d' 'gone'
|
||||
check_fail 'no buffer paste' show-buffer -b paste
|
||||
|
||||
# Unknown buffer is an error.
|
||||
check_fail 'no buffer nosuch' paste-buffer -b nosuch -t B:9.0
|
||||
check_ok kill-window -t B:9
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# load-buffer and save-buffer.
|
||||
|
||||
# Round trip a file with control characters and UTF-8 through load-buffer
|
||||
# and save-buffer.
|
||||
printf 'line1\tx\033[31m\001\002\303\251\n' >"$TMP"
|
||||
check_ok load-buffer -b file "$TMP"
|
||||
check_ok save-buffer -b file "$TMP2"
|
||||
if ! cmp -s "$TMP" "$TMP2"; then
|
||||
echo "load-buffer/save-buffer round trip differs."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# save-buffer -a appends.
|
||||
check_ok save-buffer -a -b file "$TMP2"
|
||||
cat "$TMP" "$TMP" >"$TMP".x
|
||||
if ! cmp -s "$TMP".x "$TMP2"; then
|
||||
rm -f "$TMP".x
|
||||
echo "save-buffer -a did not append."
|
||||
exit 1
|
||||
fi
|
||||
rm -f "$TMP".x
|
||||
|
||||
# show-buffer prints the loaded content (text form).
|
||||
check_ok delete-buffer -b file
|
||||
|
||||
# load-buffer of a missing file and save-buffer of a missing buffer or to a
|
||||
# bad path are errors.
|
||||
check_fail "No such file or directory: $TMP.nosuch" \
|
||||
load-buffer -b x "$TMP.nosuch"
|
||||
check_fail 'no buffer nosuch' save-buffer -b nosuch "$TMP2"
|
||||
check_ok set-buffer -b sb data
|
||||
out=$($TMUX save-buffer -b sb /nonexistent/dir/file 2>&1)
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "save-buffer to bad path succeeded."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# save-buffer - writes to stdout and load-buffer - reads from stdin.
|
||||
out=$($TMUX save-buffer -b sb -)
|
||||
if [ "$out" != "data" ]; then
|
||||
echo "save-buffer - wrong: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
check_ok delete-buffer -b sb
|
||||
printf 'from stdin' | $TMUX load-buffer -b stdinbuf -
|
||||
check_show '-b stdinbuf' 'from stdin'
|
||||
check_ok delete-buffer -b stdinbuf
|
||||
|
||||
assert_alive
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 0
|
||||
@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMP=$(mktemp)
|
||||
TMP2=$(mktemp)
|
||||
trap "rm -f $TMP $TMP2" 0 1 15
|
||||
|
||||
@@ -7,7 +7,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
fail()
|
||||
|
||||
271
regress/choose-buffer.sh
Normal file
271
regress/choose-buffer.sh
Normal file
@@ -0,0 +1,271 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Tests of buffer mode (window-buffer.c) as driven by choose-buffer: that the
|
||||
# -f filter removes buffers that do not match (by name and by content), that
|
||||
# a filter matching nothing falls back to showing everything, that -O and -r
|
||||
# change the sort order, that d deletes the selected buffer and C-t and D
|
||||
# delete all tagged buffers, and that Enter runs the default command
|
||||
# (paste-buffer) with the selected buffer.
|
||||
#
|
||||
# The list is drawn on a mode screen which capture-pane does not show, so a
|
||||
# second server provides a client: an inner "tmux attach" runs inside a pane
|
||||
# of the second server, and that pane is captured to read what the inner
|
||||
# client rendered. Each choose-buffer call uses a distinct -F marker so a
|
||||
# capture can be tied to the call it belongs to.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMP=$(mktemp -d) || exit 1
|
||||
TMUX_TMPDIR="$TMP"
|
||||
export TMUX_TMPDIR
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
rm -rf "$TMP"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# capture the screen rendered by the inner client
|
||||
capture()
|
||||
{
|
||||
$TMUX2 capture-pane -p -t out:0 2>/dev/null
|
||||
}
|
||||
|
||||
# wait_for $marker
|
||||
#
|
||||
# Wait (up to ~10s) until the rendered screen contains $marker, so the
|
||||
# capture is known to show the mode instance under test.
|
||||
wait_for()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CAPTURED=$(capture)
|
||||
if printf '%s\n' "$CAPTURED" | grep -F -q "$1"; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "timed out waiting for '$1'"
|
||||
}
|
||||
|
||||
# wait_gone $marker
|
||||
#
|
||||
# Wait (up to ~10s) until the rendered screen no longer contains $marker.
|
||||
wait_gone()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CAPTURED=$(capture)
|
||||
if ! printf '%s\n' "$CAPTURED" | grep -F -q "$1"; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "timed out waiting for '$1' to disappear"
|
||||
}
|
||||
|
||||
# wait_count $marker $n
|
||||
#
|
||||
# Wait (up to ~10s) until exactly $n rendered lines contain $marker. The
|
||||
# matching capture is left in CAPTURED.
|
||||
wait_count()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CAPTURED=$(capture)
|
||||
c=$(printf '%s\n' "$CAPTURED" | grep -F -c "$1")
|
||||
[ "$c" -eq "$2" ] && return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "timed out waiting for $2 lines of '$1' (have $c)"
|
||||
}
|
||||
|
||||
# wait_buffers $n
|
||||
#
|
||||
# Wait (up to ~10s) until the test server has exactly $n paste buffers.
|
||||
wait_buffers()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
c=$($TMUX list-buffers -F x 2>/dev/null | grep -c x)
|
||||
[ "$c" -eq "$1" ] && return 0
|
||||
sleep 0.5
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "expected $1 buffers, have $c"
|
||||
}
|
||||
|
||||
# wait_clients $n
|
||||
#
|
||||
# Wait (up to ~10s) until the test server has exactly $n clients attached.
|
||||
wait_clients()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 10 ]; do
|
||||
c=$($TMUX list-clients -F x 2>/dev/null | grep -c x)
|
||||
[ "$c" -eq "$1" ] && return 0
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# wait_mode $target $state
|
||||
#
|
||||
# Wait (up to ~10s) until a pane enters or leaves mode.
|
||||
wait_mode()
|
||||
{
|
||||
t=$1
|
||||
want=$2
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
got=$($TMUX display-message -p -t "$t" '#{pane_in_mode}' \
|
||||
2>/dev/null)
|
||||
[ "$got" = "$want" ] && return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "pane $t mode state is $got, expected $want"
|
||||
}
|
||||
|
||||
exit_mode()
|
||||
{
|
||||
marker=$1
|
||||
shift
|
||||
|
||||
$TMUX send-keys -t aaa:0 "$@" || fail "send-keys $* failed"
|
||||
wait_mode aaa:0 0
|
||||
wait_gone "$marker"
|
||||
}
|
||||
|
||||
$TMUX new-session -d -s aaa -x 80 -y 24 'cat' || exit 1
|
||||
|
||||
$TMUX2 new-session -d -s out -x 80 -y 24 "$TMUX attach -t aaa" || exit 1
|
||||
wait_clients 1 || fail "no client attached to test server"
|
||||
|
||||
# Two named buffers with distinct contents; bufa is created first.
|
||||
$TMUX set-buffer -b bufa "hello buffer" || exit 1
|
||||
$TMUX set-buffer -b bufz "other buffer" || exit 1
|
||||
|
||||
# --- filter by buffer name ---------------------------------------------------
|
||||
$TMUX choose-buffer -t aaa:0 -F 'B1' -f '#{==:#{buffer_name},bufa}' || exit 1
|
||||
wait_count ': B1' 1
|
||||
out=$CAPTURED
|
||||
printf '%s\n' "$out" | grep -F -q 'bufa: B1' || \
|
||||
fail "bufa missing when it matches"
|
||||
printf '%s\n' "$out" | grep -F -q 'bufz: B1' && \
|
||||
fail "bufz shown but does not match"
|
||||
[ "$(printf '%s\n' "$out" | grep -F -c ': B1')" -eq 1 ] || \
|
||||
fail "expected 1 buffer"
|
||||
exit_mode ': B1' q
|
||||
|
||||
# --- filter by buffer content ------------------------------------------------
|
||||
$TMUX choose-buffer -t aaa:0 -F 'B2' -f '#{m:*hello*,#{buffer_sample}}' || \
|
||||
exit 1
|
||||
wait_count ': B2' 1
|
||||
out=$CAPTURED
|
||||
printf '%s\n' "$out" | grep -F -q 'bufa: B2' || \
|
||||
fail "bufa missing when content matches"
|
||||
printf '%s\n' "$out" | grep -F -q 'bufz: B2' && \
|
||||
fail "bufz shown but content not matched"
|
||||
exit_mode ': B2' q
|
||||
|
||||
# --- no filter shows both buffers ---------------------------------------------
|
||||
$TMUX choose-buffer -t aaa:0 -F 'B3' || exit 1
|
||||
wait_count ': B3' 2
|
||||
out=$CAPTURED
|
||||
printf '%s\n' "$out" | grep -F -q 'bufa: B3' || \
|
||||
fail "bufa missing with no filter"
|
||||
printf '%s\n' "$out" | grep -F -q 'bufz: B3' || \
|
||||
fail "bufz missing with no filter"
|
||||
[ "$(printf '%s\n' "$out" | grep -F -c ': B3')" -eq 2 ] || \
|
||||
fail "expected 2 buffers"
|
||||
exit_mode ': B3' q
|
||||
|
||||
# --- filter matching nothing ---------------------------------------------------
|
||||
#
|
||||
# Everything is shown and the filter indicator reports no matches.
|
||||
$TMUX choose-buffer -t aaa:0 -F 'B4' -f '#{==:#{buffer_name},nosuch}' || \
|
||||
exit 1
|
||||
wait_count ': B4' 2
|
||||
out=$CAPTURED
|
||||
printf '%s\n' "$out" | grep -F -q 'bufa: B4' || \
|
||||
fail "bufa missing with no-match filter"
|
||||
printf '%s\n' "$out" | grep -F -q 'bufz: B4' || \
|
||||
fail "bufz missing with no-match filter"
|
||||
printf '%s\n' "$out" | grep -F -q 'no matches' || \
|
||||
fail "no matches indicator missing"
|
||||
exit_mode ': B4' q
|
||||
|
||||
# --- sort orders ---------------------------------------------------------------
|
||||
#
|
||||
# By name bufa sorts first and -r reverses.
|
||||
$TMUX choose-buffer -t aaa:0 -F 'B5' -O name || exit 1
|
||||
wait_count ': B5' 2
|
||||
printf '%s\n' "$CAPTURED" | grep -F ': B5' | head -1 | \
|
||||
grep -F -q 'bufa: B5' || \
|
||||
fail "bufa not first with -O name"
|
||||
exit_mode ': B5' q
|
||||
|
||||
$TMUX choose-buffer -t aaa:0 -F 'B6' -O name -r || exit 1
|
||||
wait_count ': B6' 2
|
||||
printf '%s\n' "$CAPTURED" | grep -F ': B6' | head -1 | \
|
||||
grep -F -q 'bufz: B6' || \
|
||||
fail "bufz not first with -O name -r"
|
||||
exit_mode ': B6' q
|
||||
|
||||
# --- d deletes the selected buffer --------------------------------------------
|
||||
#
|
||||
# The filter leaves only bufz listed and selected; d deletes it.
|
||||
$TMUX choose-buffer -t aaa:0 -F 'G1' -f '#{==:#{buffer_name},bufz}' || exit 1
|
||||
wait_for 'bufz: G1'
|
||||
$TMUX send-keys -t aaa:0 d
|
||||
wait_buffers 1
|
||||
$TMUX list-buffers -F '#{buffer_name}' | grep -F -q 'bufa' || \
|
||||
fail "wrong buffer deleted"
|
||||
exit_mode ': G1' q
|
||||
|
||||
# --- C-t tags all buffers and D deletes the tagged ------------------------------
|
||||
$TMUX set-buffer -b bufz "other buffer" || exit 1
|
||||
$TMUX set-buffer -b bufb "third buffer" || exit 1
|
||||
$TMUX choose-buffer -t aaa:0 -F 'G2' || exit 1
|
||||
wait_count ': G2' 3
|
||||
$TMUX send-keys -t aaa:0 C-t D
|
||||
wait_buffers 0
|
||||
wait_mode aaa:0 0
|
||||
wait_gone ': G2'
|
||||
|
||||
# --- Enter runs the default command (paste-buffer) ------------------------------
|
||||
#
|
||||
# The only buffer is listed and selected; Enter leaves the mode and pastes it
|
||||
# into the shell in the pane, where it appears on the screen.
|
||||
$TMUX set-buffer -b bufa "hello buffer" || exit 1
|
||||
$TMUX choose-buffer -t aaa:0 -F 'G3' || exit 1
|
||||
wait_count ': G3' 1
|
||||
exit_mode ': G3' Enter
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$TMUX capture-pane -p -t aaa:0 | grep -F -q 'hello buffer' && \
|
||||
break
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ "$i" -lt 50 ] || fail "buffer not pasted into pane"
|
||||
|
||||
exit 0
|
||||
218
regress/choose-client.sh
Normal file
218
regress/choose-client.sh
Normal file
@@ -0,0 +1,218 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Tests of client mode (window-client.c) as driven by choose-client: that the
|
||||
# -f filter removes clients that do not match, that a filter matching nothing
|
||||
# falls back to showing everything, and that Enter runs the default command
|
||||
# (detach-client) on the selected client. Sort orders are not tested here
|
||||
# because clients are named after their ttys, which are not predictable.
|
||||
#
|
||||
# The list is drawn on a mode screen which capture-pane does not show, so a
|
||||
# second server provides the clients: two inner "tmux attach" commands run in
|
||||
# panes of the second server, and the pane holding the client the mode is
|
||||
# displayed on is captured. Each choose-client call uses a distinct -F marker
|
||||
# so a capture can be tied to the call it belongs to.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMP=$(mktemp -d) || exit 1
|
||||
TMUX_TMPDIR="$TMP"
|
||||
export TMUX_TMPDIR
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
rm -rf "$TMP"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# capture the screen rendered by the inner client attached to aaa
|
||||
capture()
|
||||
{
|
||||
$TMUX2 capture-pane -p -t out:0 2>/dev/null
|
||||
}
|
||||
|
||||
# wait_for $marker
|
||||
#
|
||||
# Wait (up to ~10s) until the rendered screen contains $marker, so the
|
||||
# capture is known to show the mode instance under test.
|
||||
wait_for()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CAPTURED=$(capture)
|
||||
if printf '%s\n' "$CAPTURED" | grep -F -q "$1"; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "timed out waiting for '$1'"
|
||||
}
|
||||
|
||||
# wait_gone $marker
|
||||
#
|
||||
# Wait (up to ~10s) until the rendered screen no longer contains $marker.
|
||||
wait_gone()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CAPTURED=$(capture)
|
||||
if ! printf '%s\n' "$CAPTURED" | grep -F -q "$1"; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "timed out waiting for '$1' to disappear"
|
||||
}
|
||||
|
||||
# wait_count $marker $n
|
||||
#
|
||||
# Wait (up to ~10s) until exactly $n rendered lines contain $marker. The
|
||||
# matching capture is left in CAPTURED.
|
||||
wait_count()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CAPTURED=$(capture)
|
||||
c=$(printf '%s\n' "$CAPTURED" | grep -F -c "$1")
|
||||
[ "$c" -eq "$2" ] && return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "timed out waiting for $2 lines of '$1' (have $c)"
|
||||
}
|
||||
|
||||
# wait_clients $n
|
||||
#
|
||||
# Wait (up to ~10s) until the test server has exactly $n clients attached.
|
||||
wait_clients()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 10 ]; do
|
||||
c=$($TMUX list-clients -F x 2>/dev/null | grep -c x)
|
||||
[ "$c" -eq "$1" ] && return 0
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# wait_mode $target $state
|
||||
#
|
||||
# Wait (up to ~10s) until a pane enters or leaves mode.
|
||||
wait_mode()
|
||||
{
|
||||
t=$1
|
||||
want=$2
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
got=$($TMUX display-message -p -t "$t" '#{pane_in_mode}' \
|
||||
2>/dev/null)
|
||||
[ "$got" = "$want" ] && return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "pane $t mode state is $got, expected $want"
|
||||
}
|
||||
|
||||
exit_mode()
|
||||
{
|
||||
marker=$1
|
||||
shift
|
||||
|
||||
$TMUX send-keys -t aaa:0 "$@" || fail "send-keys $* failed"
|
||||
wait_mode aaa:0 0
|
||||
wait_gone "$marker"
|
||||
}
|
||||
|
||||
# One client attached to each of two sessions; the mode is displayed on the
|
||||
# client attached to aaa (in window 0 of the outer server) and the filters
|
||||
# tell the clients apart by their attached session.
|
||||
$TMUX new-session -d -s aaa -x 80 -y 24 'cat' || exit 1
|
||||
$TMUX new-session -d -s bbb -x 80 -y 24 'cat' || exit 1
|
||||
|
||||
$TMUX2 new-session -d -s out -x 80 -y 24 "$TMUX attach -t aaa" || exit 1
|
||||
$TMUX2 new-window -d -t out: "$TMUX attach -t bbb" || exit 1
|
||||
wait_clients 2 || fail "expected two clients attached to test server"
|
||||
|
||||
# --- filter keeping only the aaa client -------------------------------------
|
||||
$TMUX choose-client -t aaa:0 -F 'C1=#{client_session}' \
|
||||
-f '#{==:#{client_session},aaa}' || exit 1
|
||||
wait_count 'C1=' 1
|
||||
out=$CAPTURED
|
||||
printf '%s\n' "$out" | grep -F -q 'C1=aaa' || \
|
||||
fail "aaa client missing when it matches"
|
||||
printf '%s\n' "$out" | grep -F -q 'C1=bbb' && \
|
||||
fail "bbb client shown but does not match"
|
||||
[ "$(printf '%s\n' "$out" | grep -F -c 'C1=')" -eq 1 ] || \
|
||||
fail "expected 1 client"
|
||||
exit_mode 'C1=' q
|
||||
|
||||
# --- filter keeping only the bbb client -------------------------------------
|
||||
$TMUX choose-client -t aaa:0 -F 'C2=#{client_session}' \
|
||||
-f '#{==:#{client_session},bbb}' || exit 1
|
||||
wait_count 'C2=' 1
|
||||
out=$CAPTURED
|
||||
printf '%s\n' "$out" | grep -F -q 'C2=bbb' || \
|
||||
fail "bbb client missing when it matches"
|
||||
printf '%s\n' "$out" | grep -F -q 'C2=aaa' && \
|
||||
fail "aaa client shown but does not match"
|
||||
[ "$(printf '%s\n' "$out" | grep -F -c 'C2=')" -eq 1 ] || \
|
||||
fail "expected 1 client"
|
||||
exit_mode 'C2=' q
|
||||
|
||||
# --- no filter shows both clients -------------------------------------------
|
||||
$TMUX choose-client -t aaa:0 -F 'C3=#{client_session}' || exit 1
|
||||
wait_count 'C3=' 2
|
||||
out=$CAPTURED
|
||||
printf '%s\n' "$out" | grep -F -q 'C3=aaa' || \
|
||||
fail "aaa client missing with no filter"
|
||||
printf '%s\n' "$out" | grep -F -q 'C3=bbb' || \
|
||||
fail "bbb client missing with no filter"
|
||||
[ "$(printf '%s\n' "$out" | grep -F -c 'C3=')" -eq 2 ] || \
|
||||
fail "expected 2 clients"
|
||||
exit_mode 'C3=' q
|
||||
|
||||
# --- filter matching nothing ------------------------------------------------
|
||||
#
|
||||
# Everything is shown and the filter indicator reports no matches.
|
||||
$TMUX choose-client -t aaa:0 -F 'C4=#{client_session}' \
|
||||
-f '#{==:#{client_session},nosuch}' || exit 1
|
||||
wait_count 'C4=' 2
|
||||
out=$CAPTURED
|
||||
printf '%s\n' "$out" | grep -F -q 'C4=aaa' || \
|
||||
fail "aaa client missing with no-match filter"
|
||||
printf '%s\n' "$out" | grep -F -q 'C4=bbb' || \
|
||||
fail "bbb client missing with no-match filter"
|
||||
printf '%s\n' "$out" | grep -F -q 'no matches' || \
|
||||
fail "no matches indicator missing"
|
||||
exit_mode 'C4=' q
|
||||
|
||||
# --- Enter runs the default command (detach-client) --------------------------
|
||||
#
|
||||
# The filter leaves only the bbb client listed and selected; Enter detaches
|
||||
# it, leaving only the aaa client attached.
|
||||
$TMUX choose-client -t aaa:0 -F 'G1=#{client_session}' \
|
||||
-f '#{==:#{client_session},bbb}' || exit 1
|
||||
wait_for 'G1=bbb'
|
||||
$TMUX send-keys -t aaa:0 Enter || fail "send-keys Enter failed"
|
||||
wait_clients 1 || fail "bbb client did not detach"
|
||||
wait_mode aaa:0 0
|
||||
wait_gone 'G1='
|
||||
[ "$($TMUX list-clients -F '#{client_session}')" = "aaa" ] || \
|
||||
fail "wrong client detached"
|
||||
|
||||
exit 0
|
||||
293
regress/choose-tree.sh
Normal file
293
regress/choose-tree.sh
Normal file
@@ -0,0 +1,293 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Tests of tree mode (window-tree.c) as driven by choose-tree.
|
||||
#
|
||||
# Filtering: the -f filter is applied per pane and removes panes, windows and
|
||||
# sessions with no matching panes (a window with more than one pane and no
|
||||
# matching panes must disappear - GitHub issue 5326); -h keeps a window
|
||||
# listed when its only matching pane is the pane the tree is drawn in; a
|
||||
# filter matching nothing falls back to showing everything.
|
||||
#
|
||||
# Sorting: -O and -r change the sort order.
|
||||
#
|
||||
# Keys: h and l collapse and expand; f prompts for a filter and c clears it;
|
||||
# g goes to the top; Enter runs the default command (switch-client); x kills
|
||||
# the current item after a confirmation prompt.
|
||||
#
|
||||
# The tree is drawn on a mode screen which capture-pane does not show, so - as
|
||||
# in environ-update.sh - a second server provides a client: an inner "tmux
|
||||
# attach" runs inside a pane of the second server, and that pane is captured
|
||||
# to read what the inner client rendered. Each choose-tree call uses a
|
||||
# distinct -F marker so a capture can be tied to the call it belongs to.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMP=$(mktemp -d) || exit 1
|
||||
TMUX_TMPDIR="$TMP"
|
||||
export TMUX_TMPDIR
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
rm -rf "$TMP"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# capture the screen rendered by the inner client
|
||||
capture()
|
||||
{
|
||||
$TMUX2 capture-pane -p -t out:0 2>/dev/null
|
||||
}
|
||||
|
||||
# wait_for $marker
|
||||
#
|
||||
# Wait (up to ~10s) until the rendered screen contains $marker, so the
|
||||
# capture is known to show the mode instance under test.
|
||||
wait_for()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CAPTURED=$(capture)
|
||||
if printf '%s\n' "$CAPTURED" | grep -F -q "$1"; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "timed out waiting for '$1'"
|
||||
}
|
||||
|
||||
# wait_count $row-marker $n
|
||||
#
|
||||
# Wait (up to ~10s) until exactly $n rendered list rows contain $row-marker.
|
||||
wait_count()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CAPTURED=$(capture)
|
||||
c=$(printf '%s\n' "$CAPTURED" | grep -F -c "$1")
|
||||
[ "$c" -eq "$2" ] && return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "timed out waiting for $2 lines of '$1' (have $c)"
|
||||
}
|
||||
|
||||
# wait_clients $n
|
||||
#
|
||||
# Wait (up to ~10s) until the test server has exactly $n clients attached.
|
||||
wait_clients()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 10 ]; do
|
||||
c=$($TMUX list-clients -F x 2>/dev/null | grep -c x)
|
||||
[ "$c" -eq "$1" ] && return 0
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# wait_mode $target $state
|
||||
#
|
||||
# Wait (up to ~10s) until a pane enters or leaves mode.
|
||||
wait_mode()
|
||||
{
|
||||
t=$1
|
||||
want=$2
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
got=$($TMUX display-message -p -t "$t" '#{pane_in_mode}' \
|
||||
2>/dev/null)
|
||||
[ "$got" = "$want" ] && return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "pane $t mode state is $got, expected $want"
|
||||
}
|
||||
|
||||
exit_mode()
|
||||
{
|
||||
$TMUX send-keys -t aaa:0 "$@" || fail "send-keys $* failed"
|
||||
wait_mode aaa:0 0
|
||||
}
|
||||
|
||||
# Session zzz is created first, so it sorts first by index, and has a
|
||||
# two-pane window 0 and a single-pane window 1. Session aaa has one window
|
||||
# with one pane and is where the tree is displayed. With everything expanded
|
||||
# and no filter the tree is nine lines:
|
||||
#
|
||||
# 0 zzz 1 window 0 2 pane 0 3 pane 1 4 window 1 5 pane 0
|
||||
# 6 aaa 7 window 0 8 pane 0
|
||||
$TMUX new-session -d -s zzz -x 80 -y 24 'cat' || exit 1
|
||||
$TMUX split-window -t zzz:0 'cat' || exit 1
|
||||
$TMUX new-window -t zzz 'cat' || exit 1
|
||||
$TMUX new-session -d -s aaa -x 80 -y 24 'cat' || exit 1
|
||||
|
||||
$TMUX2 new-session -d -s out -x 80 -y 24 "$TMUX attach -t aaa" || exit 1
|
||||
wait_clients 1 || fail "no client attached to test server"
|
||||
|
||||
# --- filter keeping only aaa ------------------------------------------------
|
||||
#
|
||||
# zzz must disappear entirely: its single-pane window 1 and - the GitHub 5326
|
||||
# regression - its two-pane window 0. aaa contributes exactly three lines
|
||||
# (session, window, pane).
|
||||
$TMUX choose-tree -t aaa:0 -F 'F1' -f '#{==:#{session_name},aaa}' || exit 1
|
||||
wait_count ': F1' 3
|
||||
out=$CAPTURED
|
||||
printf '%s\n' "$out" | grep -F -q 'aaa: F1' || \
|
||||
fail "aaa missing when filter matches it"
|
||||
printf '%s\n' "$out" | grep -F -q 'zzz: F1' && \
|
||||
fail "zzz shown but no pane matches"
|
||||
exit_mode q
|
||||
|
||||
# --- filter keeping only zzz ------------------------------------------------
|
||||
#
|
||||
# zzz contributes six lines (session, two windows, three panes); aaa must
|
||||
# disappear.
|
||||
$TMUX choose-tree -t aaa:0 -F 'F2' -f '#{==:#{session_name},zzz}' || exit 1
|
||||
wait_count ': F2' 6
|
||||
out=$CAPTURED
|
||||
printf '%s\n' "$out" | grep -F -q 'zzz: F2' || \
|
||||
fail "zzz missing when filter matches it"
|
||||
printf '%s\n' "$out" | grep -F -q 'aaa: F2' && \
|
||||
fail "aaa shown but no pane matches"
|
||||
exit_mode q
|
||||
|
||||
# --- filter matching a single pane ------------------------------------------
|
||||
#
|
||||
# Only pane 1 of zzz:0 matches, so the tree is exactly session zzz, window 0
|
||||
# and that pane; zzz:1 and all of aaa must disappear.
|
||||
$TMUX choose-tree -t aaa:0 -F 'F3' -f '#{==:#{pane_index},1}' || exit 1
|
||||
wait_count ': F3' 3
|
||||
out=$CAPTURED
|
||||
printf '%s\n' "$out" | grep -F -q 'zzz: F3' || \
|
||||
fail "zzz missing when its pane matches"
|
||||
printf '%s\n' "$out" | grep -F -q 'aaa: F3' && \
|
||||
fail "aaa shown but no pane matches"
|
||||
printf '%s\n' "$out" | grep -F -q '1: F3' || \
|
||||
fail "matching pane missing"
|
||||
exit_mode q
|
||||
|
||||
# --- filter matching nothing ------------------------------------------------
|
||||
#
|
||||
# Everything is shown and the filter indicator reports no matches.
|
||||
$TMUX choose-tree -t aaa:0 -F 'F4' -f '#{==:#{session_name},nosuch}' || exit 1
|
||||
wait_count ': F4' 9
|
||||
out=$CAPTURED
|
||||
printf '%s\n' "$out" | grep -F -q 'aaa: F4' || \
|
||||
fail "aaa missing with no-match filter"
|
||||
printf '%s\n' "$out" | grep -F -q 'zzz: F4' || \
|
||||
fail "zzz missing with no-match filter"
|
||||
printf '%s\n' "$out" | grep -F -q 'no matches' || \
|
||||
fail "no matches indicator missing"
|
||||
exit_mode q
|
||||
|
||||
# --- -h with the tree pane as the only match --------------------------------
|
||||
#
|
||||
# With -h the pane the tree is drawn in is hidden, but it still counts as a
|
||||
# match, so session and window aaa stay listed: two lines, no pane line.
|
||||
$TMUX choose-tree -h -t aaa:0 -F 'F5' -f '#{==:#{session_name},aaa}' || \
|
||||
exit 1
|
||||
wait_count ': F5' 2
|
||||
printf '%s\n' "$CAPTURED" | grep -F -q 'aaa: F5' || \
|
||||
fail "aaa missing with -h"
|
||||
exit_mode q
|
||||
|
||||
# --- sort orders ------------------------------------------------------------
|
||||
#
|
||||
# By index zzz (created first) sorts first, by name aaa does, and -r reverses.
|
||||
$TMUX choose-tree -t aaa:0 -F 'F6' -O index || exit 1
|
||||
wait_count ': F6' 9
|
||||
printf '%s\n' "$CAPTURED" | grep -F ': F6' | head -1 | \
|
||||
grep -F -q 'zzz: F6' || \
|
||||
fail "zzz not first with -O index"
|
||||
exit_mode q
|
||||
|
||||
$TMUX choose-tree -t aaa:0 -F 'F7' -O name || exit 1
|
||||
wait_count ': F7' 9
|
||||
printf '%s\n' "$CAPTURED" | grep -F ': F7' | head -1 | \
|
||||
grep -F -q 'aaa: F7' || \
|
||||
fail "aaa not first with -O name"
|
||||
exit_mode q
|
||||
|
||||
$TMUX choose-tree -t aaa:0 -F 'F8' -O name -r || exit 1
|
||||
wait_count ': F8' 9
|
||||
printf '%s\n' "$CAPTURED" | grep -F ': F8' | head -1 | \
|
||||
grep -F -q 'zzz: F8' || \
|
||||
fail "zzz not first with -O name -r"
|
||||
exit_mode q
|
||||
|
||||
# --- collapse and expand with h and l -----------------------------------------
|
||||
#
|
||||
# g moves to the top (session zzz); h collapses it, hiding its five children;
|
||||
# l expands it again.
|
||||
$TMUX choose-tree -t aaa:0 -F 'G1' -O index || exit 1
|
||||
wait_count ': G1' 9
|
||||
$TMUX send-keys -t aaa:0 g h || fail "send-keys collapse failed"
|
||||
wait_count ': G1' 4
|
||||
$TMUX send-keys -t aaa:0 l || fail "send-keys expand failed"
|
||||
wait_count ': G1' 9
|
||||
exit_mode q
|
||||
|
||||
# --- filter entered at the prompt with f, cleared with c ----------------------
|
||||
$TMUX choose-tree -t aaa:0 -F 'G2' -O index || exit 1
|
||||
wait_count ': G2' 9
|
||||
$TMUX send-keys -t aaa:0 f || fail "send-keys f failed"
|
||||
$TMUX send-keys -t aaa:0 -l '#{==:#{session_name},aaa}' || \
|
||||
fail "send-keys filter failed"
|
||||
$TMUX send-keys -t aaa:0 Enter || fail "send-keys Enter failed"
|
||||
wait_count ': G2' 3
|
||||
out=$CAPTURED
|
||||
printf '%s\n' "$out" | grep -F -q 'aaa: G2' || \
|
||||
fail "aaa missing with prompt filter"
|
||||
printf '%s\n' "$out" | grep -F -q 'zzz: G2' && \
|
||||
fail "zzz shown with prompt filter"
|
||||
$TMUX send-keys -t aaa:0 c || fail "send-keys c failed"
|
||||
wait_count ': G2' 9
|
||||
exit_mode q
|
||||
|
||||
# --- Enter runs the default command (switch-client) ----------------------------
|
||||
#
|
||||
# g selects session zzz and Enter switches the client to it.
|
||||
$TMUX choose-tree -t aaa:0 -F 'G3' -O index || exit 1
|
||||
wait_count ': G3' 9
|
||||
$TMUX send-keys -t aaa:0 g Enter || fail "send-keys Enter failed"
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
[ "$($TMUX list-clients -F '#{client_session}')" = "zzz" ] && break
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ "$i" -lt 50 ] || fail "client did not switch to zzz"
|
||||
$TMUX switch-client -c "$($TMUX list-clients -F '#{client_name}')" -t aaa || \
|
||||
exit 1
|
||||
wait_mode aaa:0 0
|
||||
|
||||
# --- x kills the current item after confirmation -------------------------------
|
||||
#
|
||||
# g and four times j select window 1 of zzz; x asks for confirmation and y
|
||||
# kills it, leaving zzz with one window and the tree with seven lines.
|
||||
$TMUX choose-tree -t aaa:0 -F 'G4' -O index || exit 1
|
||||
wait_count ': G4' 9
|
||||
$TMUX send-keys -t aaa:0 g j j j j x || fail "send-keys x failed"
|
||||
wait_for 'Kill window 1'
|
||||
$TMUX send-keys -t aaa:0 y || fail "send-keys y failed"
|
||||
wait_count ': G4' 7
|
||||
[ "$($TMUX list-windows -t zzz -F x | grep -c x)" -eq 1 ] || \
|
||||
fail "window 1 of zzz not killed"
|
||||
exit_mode q
|
||||
|
||||
exit 0
|
||||
@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
$TMUX new-session -d -sfoo || exit 1
|
||||
|
||||
@@ -4,11 +4,10 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
TMUX="$TEST_TMUX -LtestA$$-1 -f/dev/null"
|
||||
|
||||
TMP=$(mktemp)
|
||||
trap "rm -f $TMP" 0 1 15
|
||||
trap 'rm -f "$TMP"; $TMUX kill-server 2>/dev/null' 0 1 15
|
||||
|
||||
cat <<EOF >$TMP
|
||||
new -sfoo -nfoo0; neww -nfoo1; neww -nfoo2
|
||||
@@ -27,6 +26,7 @@ foo,foo1
|
||||
foo,foo2
|
||||
EOF
|
||||
|
||||
TMUX="$TEST_TMUX -LtestA$$-2 -f/dev/null"
|
||||
cat <<EOF >$TMP
|
||||
new -sfoo -nfoo0
|
||||
neww -nfoo1
|
||||
|
||||
@@ -4,10 +4,13 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
TMUX=
|
||||
n=0
|
||||
trap '[ -n "$TMUX" ] && $TMUX kill-server 2>/dev/null' 0 1 15
|
||||
|
||||
for i in conf/*.conf; do
|
||||
n=$((n + 1))
|
||||
TMUX="$TEST_TMUX -LtestA$$-$n -f/dev/null"
|
||||
$TMUX -f/dev/null start \; source -n $i || exit 1
|
||||
done
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ bind -n C-h run "(tmux display-message -p '#{pane_current_command}' | grep -iq v
|
||||
bind -n C-j run "(tmux display-message -p '#{pane_current_command}' | grep -iq vim && tmux send-keys C-j) || tmux select-pane -D"
|
||||
bind -n C-k run "(tmux display-message -p '#{pane_current_command}' | grep -iq vim && tmux send-keys C-k) || tmux select-pane -U"
|
||||
bind -n C-l run "(tmux display-message -p '#{pane_current_command}' | grep -iq vim && tmux send-keys C-l) || tmux select-pane -R"
|
||||
bind -n C-\ run "(tmux display-message -p '#{pane_current_command}' | grep -iq vim && tmux send-keys 'C-\\') || tmux select-pane -l"
|
||||
bind -n C-\\ run "(tmux display-message -p '#{pane_current_command}' | grep -iq vim && tmux send-keys 'C-\\') || tmux select-pane -l"
|
||||
|
||||
# C-l is taken oer by vim style pane navigation
|
||||
bind C-l send-keys 'C-l'
|
||||
|
||||
@@ -183,7 +183,7 @@ bind t swap-window -t 1 # swap the current window's position with window # 1,
|
||||
unbind & # unbind default binding for `split-window -h`
|
||||
bind - split-window -v -c '#{pane_current_path}' # vertical split
|
||||
bind _ split-window -v -c '#{pane_current_path}' -f # full vertical split (v2.3+)
|
||||
bind \ split-window -h -c '#{pane_current_path}' # horizontal split
|
||||
bind \\ split-window -h -c '#{pane_current_path}' # horizontal split
|
||||
bind | split-window -h -c '#{pane_current_path}' -f # full horizontal split (v2.3+)
|
||||
# https://www.reddit.com/r/tmux/comments/3paqoi/tmux_21_has_been_released/cw5wy00
|
||||
bind w switch-client -Tsplit_wind
|
||||
|
||||
111
regress/control-client-exit.sh
Normal file
111
regress/control-client-exit.sh
Normal file
@@ -0,0 +1,111 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Once a control client is marked for exit (by detach-client here, or by the
|
||||
# "too far behind" eviction in control_check_age), the server must stop
|
||||
# queueing new output and notifications for it. The exit handshake only waits
|
||||
# for already-queued blocks to drain (control_all_done), so anything queued
|
||||
# after that point is written to a client that will never read it, and can hold
|
||||
# the handshake - and the pane buffer it pins - open. This checks that a
|
||||
# notification generated while the client is exiting never reaches its output.
|
||||
#
|
||||
# To see this in seconds rather than waiting out CONTROL_MAXIMUM_AGE (300s) the
|
||||
# control client's output goes down a fifo that a helper drains only slowly.
|
||||
# The slow drain keeps a backlog stuck in the server so the exit handshake
|
||||
# cannot complete; while it is stuck we detach the client (which sets the exit
|
||||
# flag) and rename the window to a marker. Draining the fifo must then not turn
|
||||
# up the marker as a %window-renamed line.
|
||||
|
||||
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
|
||||
OUT=$DIR/out
|
||||
MARKER=exitmarker
|
||||
READER=
|
||||
|
||||
mkfifo "$FIFO" || exit 1
|
||||
: >"$OUT"
|
||||
|
||||
cleanup() {
|
||||
[ -n "$READER" ] && kill "$READER" 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
|
||||
$TMUX setw -g automatic-rename off || exit 1
|
||||
|
||||
# Attach a control client: output to the fifo, stdin held open by sleep so it
|
||||
# stays attached while we drive the session from outside. Open the read end
|
||||
# after the client has opened the write end.
|
||||
( sleep 30 ) | $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; }
|
||||
|
||||
# Drain the control stream slowly (~2.5KB/s). The pane floods far faster, so a
|
||||
# backlog stays stuck in the server and the fifo stays full, which is what
|
||||
# keeps the exit handshake from completing below.
|
||||
( while :; do
|
||||
dd bs=256 count=1 <&8 >>"$OUT" 2>/dev/null || exit 0
|
||||
sleep 0.1
|
||||
done ) &
|
||||
READER=$!
|
||||
|
||||
# Wait until the client is demonstrably backed up: the output only keeps
|
||||
# growing at the slow-drain rate while the flood is outrunning the reader.
|
||||
n=0
|
||||
while [ $n -lt 100 ]; do
|
||||
[ "$(wc -c <"$OUT")" -ge 8000 ] && break
|
||||
sleep 0.1
|
||||
n=$((n + 1))
|
||||
done
|
||||
[ "$(wc -c <"$OUT")" -ge 8000 ] || { echo "client output did not back up"; exit 1; }
|
||||
|
||||
# Detach the control client: this sets CLIENT_EXIT immediately. The stuck
|
||||
# backlog holds the window between CLIENT_EXIT and the exit handshake open.
|
||||
$TMUX detach-client -s rt
|
||||
|
||||
# While the client is exiting, rename the window to the marker. Vanilla queues
|
||||
# these %window-renamed notifications for the exiting client; the fix drops them.
|
||||
i=0
|
||||
while [ $i -lt 5 ]; do
|
||||
$TMUX rename-window -t rt:0 "$MARKER$i"
|
||||
i=$((i + 1))
|
||||
done
|
||||
sleep 0.5
|
||||
|
||||
# Drain fast (bounded) so the backlog, and any notification queued behind it,
|
||||
# reaches OUT. A watchdog keeps this from blocking.
|
||||
kill "$READER" 2>/dev/null
|
||||
READER=
|
||||
( dd bs=4096 count=128 <&8 >>"$OUT" 2>/dev/null ) &
|
||||
DRAIN=$!
|
||||
( sleep 3; kill "$DRAIN" 2>/dev/null ) &
|
||||
WATCHDOG=$!
|
||||
wait "$DRAIN" 2>/dev/null
|
||||
kill "$WATCHDOG" 2>/dev/null
|
||||
|
||||
# The marker must never appear as a notification to the exiting client.
|
||||
if grep -q "%window-renamed .*$MARKER" "$OUT"; then
|
||||
echo "notification queued to exiting control client:"
|
||||
grep "%window-renamed .*$MARKER" "$OUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -8,12 +8,11 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
TMUX="$TEST_TMUX -LtestA$$-1 -f/dev/null"
|
||||
|
||||
TMP=$(mktemp)
|
||||
OUT=$(mktemp)
|
||||
trap "rm -f $TMP $OUT" 0 1 15
|
||||
trap 'rm -f "$TMP" "$OUT"; $TMUX kill-server 2>/dev/null' 0 1 15
|
||||
|
||||
$TMUX -f/dev/null new -d || exit 1
|
||||
sleep 1
|
||||
@@ -26,6 +25,7 @@ $TMUX ls -F':#{window_width} #{window_height}' >>$OUT
|
||||
printf ":80 24\n:100 50\n"|cmp -s $OUT - || exit 1
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMUX="$TEST_TMUX -LtestA$$-2 -f/dev/null"
|
||||
$TMUX -f/dev/null new -d || exit 1
|
||||
sleep 1
|
||||
cat <<EOF|$TMUX -f/dev/null -C a >$TMP
|
||||
@@ -37,6 +37,7 @@ $TMUX ls -F':#{window_width} #{window_height}' >>$OUT
|
||||
printf ":80 24\n:80 24\n"|cmp -s $OUT - || exit 1
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMUX="$TEST_TMUX -LtestA$$-3 -f/dev/null"
|
||||
cat <<EOF|$TMUX -f/dev/null -C new -x 100 -y 50 >$TMP
|
||||
ls -F':#{window_width} #{window_height}'
|
||||
refresh -C 80,24
|
||||
|
||||
63
regress/control-client-wait-exit.sh
Normal file
63
regress/control-client-wait-exit.sh
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/bin/sh
|
||||
|
||||
# A control client with the wait-exit flag lingers after %exit until its input
|
||||
# sends an empty line or closes. The terminating empty line must be honoured
|
||||
# even when several lines arrive in a single read (as from a pipe or a raw
|
||||
# terminal): reading input with stdio would pull them all into the FILE buffer
|
||||
# and leave the empty line where poll never sees it, hanging the client.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
FIFO=$(mktemp -u)
|
||||
OUT=$(mktemp)
|
||||
mkfifo "$FIFO" || exit 1
|
||||
trap "$TMUX kill-server 2>/dev/null; rm -f $FIFO $OUT" 0 1 15
|
||||
|
||||
# Start a control client that reads its input from the fifo. Keep the write
|
||||
# end open (fd 3) so the client never sees EOF: it must exit on the empty
|
||||
# line, not on the pipe closing.
|
||||
$TMUX -f/dev/null -C new -s wait-exit <"$FIFO" >"$OUT" 2>&1 &
|
||||
CLIENT=$!
|
||||
exec 3>"$FIFO"
|
||||
sleep 1
|
||||
|
||||
# Ask to linger after exit, then detach so the client prints %exit and enters
|
||||
# the wait-exit loop.
|
||||
printf 'refresh-client -f wait-exit\n' >&3
|
||||
sleep 1
|
||||
$TMUX detach-client -s wait-exit
|
||||
|
||||
# Wait for the client to print %exit and enter the wait-exit loop.
|
||||
i=0
|
||||
while [ $i -lt 5 ]; do
|
||||
grep -q '^%exit' "$OUT" && break
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
grep -q '^%exit' "$OUT" || exit 1
|
||||
|
||||
# Deliver the terminating empty line in one write, after a non-empty line, so
|
||||
# a single read returns "a\n" and "\n" together. The empty line must still end
|
||||
# the wait.
|
||||
printf 'a\n\n' >&3
|
||||
|
||||
# The client should exit promptly. If it is still alive after the timeout the
|
||||
# empty line was lost in a buffer and the client has hung.
|
||||
i=0
|
||||
while [ $i -lt 10 ]; do
|
||||
kill -0 $CLIENT 2>/dev/null || break
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
if kill -0 $CLIENT 2>/dev/null; then
|
||||
kill $CLIENT 2>/dev/null
|
||||
exit 1
|
||||
fi
|
||||
exec 3>&-
|
||||
|
||||
exit 0
|
||||
102
regress/control-notify-events.sh
Normal file
102
regress/control-notify-events.sh
Normal file
@@ -0,0 +1,102 @@
|
||||
#!/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"
|
||||
PID=
|
||||
|
||||
cleanup()
|
||||
{
|
||||
[ -n "$PID" ] && kill "$PID" 2>/dev/null
|
||||
$TMUX kill-server 2>/dev/null
|
||||
rm -rf "$TMPDIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
wait_for()
|
||||
{
|
||||
pattern=$1
|
||||
timeout=${2:-6}
|
||||
i=0
|
||||
|
||||
while [ "$i" -lt "$timeout" ]; do
|
||||
if grep -F -- "$pattern" "$OUT" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
echo "missing: $pattern"
|
||||
cat "$OUT"
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_for_count()
|
||||
{
|
||||
pattern=$1
|
||||
expected=$2
|
||||
timeout=${3:-6}
|
||||
i=0
|
||||
|
||||
while [ "$i" -lt "$timeout" ]; do
|
||||
count=$(grep -F -- "$pattern" "$OUT" 2>/dev/null | wc -l)
|
||||
if [ "$count" -ge "$expected" ]; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
echo "missing count $expected for: $pattern"
|
||||
cat "$OUT"
|
||||
return 1
|
||||
}
|
||||
|
||||
send()
|
||||
{
|
||||
printf '%s\n' "$*" >&3
|
||||
}
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX new-session -d -s notify -x 80 -y 24 || exit 1
|
||||
wid=$($TMUX display-message -p -t notify:0 '#{window_id}') || exit 1
|
||||
pane=$($TMUX display-message -p -t notify:0.0 '#{pane_id}') || exit 1
|
||||
|
||||
mkfifo "$IN"
|
||||
: >"$OUT"
|
||||
$TMUX -C attach-session -t notify <"$IN" >"$OUT" 2>&1 &
|
||||
PID=$!
|
||||
exec 3>"$IN"
|
||||
|
||||
send 'display-message -p ready'
|
||||
wait_for 'ready' 3 || exit 1
|
||||
|
||||
send 'rename-window notify-renamed'
|
||||
wait_for "%window-renamed $wid notify-renamed" || exit 1
|
||||
|
||||
send 'split-window -h'
|
||||
wait_for "%layout-change $wid " || exit 1
|
||||
|
||||
send 'select-pane -t:.0'
|
||||
wait_for "%window-pane-changed $wid $pane" || exit 1
|
||||
|
||||
send 'copy-mode -t:.0'
|
||||
wait_for "%pane-mode-changed $pane" || exit 1
|
||||
|
||||
sessions_changed=$(grep -F -- '%sessions-changed' "$OUT" 2>/dev/null | \
|
||||
wc -l)
|
||||
send 'new-session -d -s notify-extra'
|
||||
wait_for_count '%sessions-changed' $((sessions_changed + 1)) || exit 1
|
||||
|
||||
send 'kill-session -t notify-extra'
|
||||
wait_for_count '%sessions-changed' $((sessions_changed + 2)) || exit 1
|
||||
|
||||
exit 0
|
||||
172
regress/control-subscriptions.sh
Normal file
172
regress/control-subscriptions.sh
Normal file
@@ -0,0 +1,172 @@
|
||||
#!/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"
|
||||
PID=
|
||||
|
||||
cleanup()
|
||||
{
|
||||
[ -n "$PID" ] && kill "$PID" 2>/dev/null
|
||||
$TMUX kill-server 2>/dev/null
|
||||
rm -rf "$TMPDIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
wait_for()
|
||||
{
|
||||
pattern=$1
|
||||
timeout=${2:-6}
|
||||
i=0
|
||||
|
||||
while [ "$i" -lt "$timeout" ]; do
|
||||
if grep -F -- "$pattern" "$OUT" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
echo "missing: $pattern"
|
||||
cat "$OUT"
|
||||
return 1
|
||||
}
|
||||
|
||||
reject_for()
|
||||
{
|
||||
pattern=$1
|
||||
timeout=${2:-3}
|
||||
i=0
|
||||
|
||||
while [ "$i" -lt "$timeout" ]; do
|
||||
if grep -F -- "$pattern" "$OUT" >/dev/null 2>&1; then
|
||||
echo "unexpected: $pattern"
|
||||
cat "$OUT"
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
wait_tmux()
|
||||
{
|
||||
target=$1
|
||||
timeout=${2:-6}
|
||||
i=0
|
||||
|
||||
while [ "$i" -lt "$timeout" ]; do
|
||||
if $TMUX display-message -p -t "$target" '#{window_id}' \
|
||||
>/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
window_fields()
|
||||
{
|
||||
$TMUX display-message -p -t "$1" '#{window_id} #{window_index}'
|
||||
}
|
||||
|
||||
pane_fields()
|
||||
{
|
||||
$TMUX display-message -p -t "$1" '#{window_id} #{window_index} #{pane_id}'
|
||||
}
|
||||
|
||||
send()
|
||||
{
|
||||
printf '%s\n' "$*" >&3
|
||||
}
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX new-session -d -s subs -x 80 -y 24 || exit 1
|
||||
sid=$($TMUX display-message -p -t subs '#{session_id}')
|
||||
|
||||
mkfifo "$IN"
|
||||
: >"$OUT"
|
||||
$TMUX -C attach-session -t subs <"$IN" >"$OUT" 2>&1 &
|
||||
PID=$!
|
||||
exec 3>"$IN"
|
||||
|
||||
send 'display-message -p ready'
|
||||
wait_for 'ready' 3 || exit 1
|
||||
|
||||
send "refresh-client -B 'sw::#{session_windows}'"
|
||||
wait_for "%subscription-changed sw $sid - - - : 1" || exit 1
|
||||
|
||||
send 'new-window'
|
||||
wait_for "%subscription-changed sw $sid - - - : 2" || exit 1
|
||||
|
||||
send 'refresh-client -B sw'
|
||||
send 'new-window'
|
||||
reject_for "%subscription-changed sw $sid - - - : 3" || exit 1
|
||||
|
||||
send 'new-window -n pane-test'
|
||||
wait_tmux subs:pane-test || exit 1
|
||||
set -- $(pane_fields subs:pane-test)
|
||||
wid=$1
|
||||
widx=$2
|
||||
pane=$3
|
||||
|
||||
send "refresh-client -B 'ap:%*:#{pane_id}'"
|
||||
wait_for "%subscription-changed ap $sid $wid $widx $pane : $pane" || exit 1
|
||||
|
||||
send 'split-window -t subs:pane-test'
|
||||
i=0
|
||||
while [ "$i" -lt 6 ]; do
|
||||
[ "$($TMUX list-panes -t subs:pane-test | wc -l)" -eq 2 ] && break
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ "$i" -lt 6 ] || exit 1
|
||||
newpane=$($TMUX list-panes -t subs:pane-test -F '#{pane_id}' | tail -n 1)
|
||||
wait_for "%subscription-changed ap $sid $wid $widx $newpane : $newpane" ||
|
||||
exit 1
|
||||
|
||||
$TMUX new-session -d -s other || exit 1
|
||||
$TMUX link-window -s subs:pane-test -t other:1 || exit 1
|
||||
|
||||
send "refresh-client -B 'sp:$newpane:#{pane_id}:#{window_id}'"
|
||||
wait_for "%subscription-changed sp $sid $wid $widx $newpane : $newpane:$wid" ||
|
||||
exit 1
|
||||
|
||||
send "refresh-client -B 'cw:$wid:#{window_id}:#{window_index}'"
|
||||
wait_for "%subscription-changed cw $sid $wid $widx - : $wid:$widx" ||
|
||||
exit 1
|
||||
|
||||
send "refresh-client -B 'aw:@*:#{window_id}'"
|
||||
send 'new-window -n window-test'
|
||||
wait_tmux subs:window-test || exit 1
|
||||
set -- $(window_fields subs:window-test)
|
||||
awid=$1
|
||||
awidx=$2
|
||||
wait_for "%subscription-changed aw $sid $awid $awidx - : $awid" || exit 1
|
||||
|
||||
send "refresh-client -B 'dup::#{session_windows}'"
|
||||
wcount=$($TMUX display-message -p -t subs '#{session_windows}')
|
||||
wait_for "%subscription-changed dup $sid - - - : $wcount" || exit 1
|
||||
send "refresh-client -B 'dup::#{session_name}'"
|
||||
wait_for "%subscription-changed dup $sid - - - : subs" || exit 1
|
||||
send 'new-window -n dup-test'
|
||||
wait_tmux subs:dup-test || exit 1
|
||||
wcount=$($TMUX display-message -p -t subs '#{session_windows}')
|
||||
reject_for "%subscription-changed dup $sid - - - : $wcount" || exit 1
|
||||
|
||||
send "refresh-client -B 'missing-pane:%999999:#{pane_id}'"
|
||||
send "refresh-client -B 'missing-window:@999999:#{window_id}'"
|
||||
reject_for '%subscription-changed missing-pane' || exit 1
|
||||
reject_for '%subscription-changed missing-window' || exit 1
|
||||
|
||||
exit 0
|
||||
37
regress/copy-mode-scroll-exit.sh
Normal file
37
regress/copy-mode-scroll-exit.sh
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
|
||||
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
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null
|
||||
}
|
||||
trap cleanup 0
|
||||
trap 'exit 1' 1 2 3 15
|
||||
|
||||
$TMUX new -d -x40 -y10 \
|
||||
'i=0; while [ $i -lt 80 ]; do echo "line $i"; i=$((i + 1)); done; cat' ||
|
||||
exit 1
|
||||
$TMUX set -g window-size manual || exit 1
|
||||
|
||||
$TMUX copy-mode -e || exit 1
|
||||
$TMUX send-keys -X history-top || exit 1
|
||||
$TMUX send-keys -X start-of-line || exit 1
|
||||
$TMUX send-keys -X begin-selection || exit 1
|
||||
$TMUX send-keys -X cursor-down || exit 1
|
||||
|
||||
[ "$($TMUX display-message -p '#{selection_present}')" = "1" ] || exit 1
|
||||
$TMUX send-keys -N200 -X scroll-down || exit 1
|
||||
[ "$($TMUX display-message -p '#{pane_in_mode} #{scroll_position}')" = "1 0" ] ||
|
||||
exit 1
|
||||
|
||||
$TMUX send-keys -X clear-selection || exit 1
|
||||
$TMUX send-keys -X scroll-down || exit 1
|
||||
[ "$($TMUX display-message -p '#{pane_in_mode}')" = "0" ] || exit 1
|
||||
|
||||
exit 0
|
||||
@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
$TMUX new -d -x40 -y10 \
|
||||
|
||||
@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -f/dev/null -Ltest"
|
||||
TMUX="$TEST_TMUX -f/dev/null -LtestA$$"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
$TMUX new -d -x40 -y10 \
|
||||
|
||||
@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -9,7 +9,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
sleep 1
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
# A second server on its own socket hosts the pane that runs the inner client.
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
# check_value $args $expected
|
||||
@@ -151,7 +151,7 @@ cat > "$CONF" <<EOF
|
||||
CFGVAR=fromconfig
|
||||
%hidden CFGHID=hiddencfg
|
||||
EOF
|
||||
CTMUX="$TEST_TMUX -Ltest2 -f$CONF"
|
||||
CTMUX="$TEST_TMUX -LtestB$$ -f$CONF"
|
||||
$CTMUX kill-server 2>/dev/null
|
||||
$CTMUX new-session -d -s c -x 80 -y 24 || { rm -f "$CONF"; exit 1; }
|
||||
out=$($CTMUX show-environment -g CFGVAR 2>&1)
|
||||
|
||||
@@ -15,7 +15,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
fail()
|
||||
|
||||
@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
# test_format $format $expected_result
|
||||
test_format()
|
||||
|
||||
@@ -12,10 +12,12 @@
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
TZ=UTC
|
||||
export TZ
|
||||
LANG=C.UTF-8
|
||||
LC_ALL=C.UTF-8
|
||||
export TZ LANG LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
ESC=$(printf '\033')
|
||||
|
||||
@@ -98,6 +100,11 @@ $TMUX set -g @host 'myhost' || exit 1
|
||||
$TMUX set -g @ts '1000000000' || exit 1 # 2001-09-09 01:46:40 UTC
|
||||
$TMUX set -g @sp 'a b$c' || exit 1 # shell-special characters for q:
|
||||
$TMUX set -g @hash 'a#b' || exit 1 # a "#" for q/e:
|
||||
$TMUX set -g @sq "a'b" || exit 1 # a single quote for q/s:
|
||||
$TMUX set -g @sub 'abABab' || exit 1
|
||||
$TMUX set -g @slash 'foo/bar foo/' || exit 1
|
||||
$TMUX set -g @nl "$(printf 'a\nb')" || exit 1
|
||||
q_s_nl=$(printf "'a\nb'")
|
||||
|
||||
|
||||
# --- Comparisons and matching --------------------------------------------
|
||||
@@ -151,6 +158,10 @@ test_format "#{!!:non-empty}" "1"
|
||||
|
||||
# q: escapes shell special characters with a backslash.
|
||||
test_format "#{q:@sp}" 'a\ b\$c'
|
||||
# q/s quotes with POSIX shell single quotes.
|
||||
test_format "#{q/s:@sp}" "'a b\$c'"
|
||||
test_format "#{q/s:@sq}" "'a'\\''b'"
|
||||
test_format "#{q/s:@nl}" "$q_s_nl"
|
||||
# q/e and q/h escape "#" for the format/style parser by doubling it.
|
||||
test_format "#{q/e:@hash}" 'a##b'
|
||||
test_format "#{q/h:@hash}" 'a##b'
|
||||
@@ -280,6 +291,15 @@ if [ -z "$($TMUX display-message -p '#{t/r:@ts}')" ]; then
|
||||
echo "Format test failed for '#{t/r:@ts}': empty result"
|
||||
exit 1
|
||||
fi
|
||||
# t/d: difference from the current time in seconds.
|
||||
diff=$($TMUX display-message -p '#{t/d:@ts}')
|
||||
case "$diff" in
|
||||
[0-9]*) ;;
|
||||
*)
|
||||
echo "Format test failed for '#{t/d:@ts}': expected positive value, got '$diff'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# t/f: custom strftime format applied to the variable's time. Tested in a
|
||||
# format_expand context (list-windows -F), where a single strftime specifier is
|
||||
@@ -315,6 +335,14 @@ done
|
||||
# A time in the future has no relative form.
|
||||
$TMUX set -g @future "$((now + 100000))"
|
||||
test_format "#{t/r:@future}" ""
|
||||
diff=$($TMUX display-message -p '#{t/d:@future}')
|
||||
case "$diff" in
|
||||
-[0-9]*) ;;
|
||||
*)
|
||||
echo "Format test failed for '#{t/d:@future}': expected negative value, got '$diff'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
# --- Content search (C) --------------------------------------------------
|
||||
@@ -417,9 +445,23 @@ test_format "#{=/3/#,:@s}" "abc," # escaped comma in the marker
|
||||
# The truncation marker is itself expanded as a format.
|
||||
test_format "#{=/3/#{l:>}:@s}" "abc>"
|
||||
|
||||
# Substitution flags: a third argument of "i" is case-insensitive; an invalid
|
||||
# regular expression leaves the text unchanged.
|
||||
# Substitution, including regular expressions, back references, different
|
||||
# delimiters, empty matches and the "i" case-insensitive flag.
|
||||
test_format "#{s/z/X/:@s}" "abcdefghij"
|
||||
test_format "#{s/[bd]/X/:@s}" "aXcXefghij"
|
||||
test_format "#{s/A/X/i:@s}" "Xbcdefghij"
|
||||
test_format "#{s/a(.)/\\1x/i:@sub}" "bxBxbx"
|
||||
test_format "#{s/(.)(.)/\\2\\1/:@s}" "badcfehgji"
|
||||
test_format "#{s|foo/|bar/|:@slash}" "bar/bar bar/"
|
||||
test_format "#{s/^abc/ABC/:@s}" "ABCdefghij"
|
||||
test_format "#{s/^(.)(.)/\\2\\1/:@s}" "bacdefghij"
|
||||
test_format "#{s/^x*//:@s}" "abcdefghij"
|
||||
test_format "#{s/^/X/:@s}" "Xabcdefghij"
|
||||
test_format "#{s/^x*/X/:@s}" "Xabcdefghij"
|
||||
test_format "#{s/$/X/:@s}" "abcdefghijX"
|
||||
test_format "#{s/x*//:@s}" "abcdefghij"
|
||||
test_format "#{s/x*/X/:@s}" "aXbXcXdXeXfXgXhXiXjX"
|
||||
# An invalid regular expression leaves the text unchanged.
|
||||
test_format "#{s/[/X/:@s}" "abcdefghij"
|
||||
|
||||
|
||||
@@ -517,11 +559,17 @@ $TMUX split-window -h -t zeta:charlie
|
||||
$TMUX split-window -h -t zeta:charlie
|
||||
test_format "#{P:#{pane_index}}" "012" "zeta:charlie"
|
||||
test_format "#{P/r:#{pane_index}}" "210" "zeta:charlie"
|
||||
# A pane-sort argument is accepted; for panes only the r (reverse) suffix has an
|
||||
# effect, so these all keep the count and exercise the argument branch.
|
||||
# Pane sort accepts i (pane-list order) and z (z-order). Other sort letters
|
||||
# fall back to the default creation order; r reverses whichever order is used.
|
||||
test_format "#{P/i:x}" "xxx" "zeta:charlie"
|
||||
test_format "#{P/i:#{pane_index}}" "012" "zeta:charlie"
|
||||
test_format "#{P/z:x}" "xxx" "zeta:charlie"
|
||||
test_format "#{P/n:x}" "xxx" "zeta:charlie"
|
||||
test_format "#{P/t:x}" "xxx" "zeta:charlie"
|
||||
$TMUX new-pane -d -t zeta:charlie -x 20 -y 10 -X 1 -Y 1
|
||||
test_format "#{P/i:#{pane_index}}" "0123" "zeta:charlie"
|
||||
test_format "#{P/z:#{pane_index}}" "3012" "zeta:charlie"
|
||||
test_format "#{P/zr:#{pane_index}}" "0123" "zeta:charlie"
|
||||
|
||||
# Verbose expansion of the loops, to exercise their logging paths.
|
||||
$TMUX display-message -v -p "#{S:#{session_name}}" >/dev/null 2>&1
|
||||
|
||||
@@ -18,8 +18,8 @@ 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"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
|
||||
314
regress/format-render-contexts.sh
Normal file
314
regress/format-render-contexts.sh
Normal file
@@ -0,0 +1,314 @@
|
||||
#!/bin/sh
|
||||
# Exercise format and style rendering in live contexts.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LANG=C.UTF-8
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LANG LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
LIMIT=20000
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
}
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$1" >&2
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
|
||||
run_cmd()
|
||||
{
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
timeout 10 "$@"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
bounded()
|
||||
{
|
||||
name=$1
|
||||
text=$2
|
||||
|
||||
n=$(printf '%s' "$text" | wc -c)
|
||||
[ "$n" -le "$LIMIT" ] || fail "$name too large: $n bytes"
|
||||
}
|
||||
|
||||
tmux_run()
|
||||
{
|
||||
name=$1
|
||||
shift
|
||||
|
||||
out=$(
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
timeout 10 $TMUX "$@" 2>&1
|
||||
else
|
||||
$TMUX "$@" 2>&1
|
||||
fi
|
||||
)
|
||||
rc=$?
|
||||
bounded "$name" "$out"
|
||||
[ "$rc" -eq 0 ] || fail "$name failed: $out"
|
||||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
capture()
|
||||
{
|
||||
out=$(
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
timeout 10 $TMUX2 capture-pane -p -t out:0 2>/dev/null
|
||||
else
|
||||
$TMUX2 capture-pane -p -t out:0 2>/dev/null
|
||||
fi
|
||||
)
|
||||
rc=$?
|
||||
bounded "capture-pane" "$out"
|
||||
[ "$rc" -eq 0 ] || fail "capture-pane failed"
|
||||
printf '%s\n' "$out"
|
||||
}
|
||||
|
||||
capture_esc()
|
||||
{
|
||||
out=$(
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
timeout 10 $TMUX2 capture-pane -pe -t out:0 \
|
||||
2>/dev/null
|
||||
else
|
||||
$TMUX2 capture-pane -pe -t out:0 2>/dev/null
|
||||
fi
|
||||
)
|
||||
rc=$?
|
||||
bounded "capture-pane -e" "$out"
|
||||
[ "$rc" -eq 0 ] || fail "capture-pane -e failed"
|
||||
printf '%s\n' "$out"
|
||||
}
|
||||
|
||||
assert_alive()
|
||||
{
|
||||
tmux_run "server alive ($1)" display-message -p alive >/dev/null
|
||||
}
|
||||
|
||||
wait_for()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
if capture | grep -F "$marker" >/dev/null 2>&1; then
|
||||
sleep 0.1
|
||||
return 0
|
||||
fi
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "timed out waiting for $marker"
|
||||
}
|
||||
|
||||
render_status_options()
|
||||
{
|
||||
label=$1
|
||||
fmt=$2
|
||||
|
||||
tmux_run "$label status-left" \
|
||||
set-option -g status-left "SL$label: $fmt" >/dev/null
|
||||
tmux_run "$label clear status-right" \
|
||||
set-option -g status-right '' >/dev/null
|
||||
tmux_run "$label status-format left" \
|
||||
set-option -g status-format[0] '#{E:status-left}' >/dev/null
|
||||
tmux_run "$label refresh status-left" refresh-client -S >/dev/null
|
||||
wait_for "SL$label:"
|
||||
|
||||
tmux_run "$label clear status-left" \
|
||||
set-option -g status-left '' >/dev/null
|
||||
tmux_run "$label status-right" \
|
||||
set-option -g status-right "SR$label: $fmt" >/dev/null
|
||||
tmux_run "$label status-format right" \
|
||||
set-option -g status-format[0] \
|
||||
'#[align=right]#{E:status-right}' >/dev/null
|
||||
tmux_run "$label refresh status-right" refresh-client -S >/dev/null
|
||||
wait_for "SR$label:"
|
||||
assert_alive "$label status-left/right"
|
||||
}
|
||||
|
||||
render_status_format()
|
||||
{
|
||||
label=$1
|
||||
fmt=$2
|
||||
|
||||
tmux_run "$label status-format" \
|
||||
set-option -g status-format[0] "SF$label: $fmt" >/dev/null
|
||||
tmux_run "$label refresh status-format" refresh-client -S >/dev/null
|
||||
wait_for "SF$label:"
|
||||
assert_alive "$label status-format"
|
||||
}
|
||||
|
||||
render_message()
|
||||
{
|
||||
label=$1
|
||||
fmt=$2
|
||||
|
||||
tmux_run "$label display-message" \
|
||||
display-message -t fmt:0 -d 1000 "DM$label: $fmt" >/dev/null
|
||||
wait_for "DM$label:"
|
||||
assert_alive "$label display-message"
|
||||
}
|
||||
|
||||
render_choose_tree()
|
||||
{
|
||||
label=$1
|
||||
fmt=$2
|
||||
|
||||
tmux_run "$label choose-tree" \
|
||||
choose-tree -t fmt:0 -F "CT$label: $fmt" >/dev/null
|
||||
wait_for "CT$label:"
|
||||
tmux_run "$label quit choose-tree" send-keys -t fmt:0 q >/dev/null
|
||||
assert_alive "$label choose-tree"
|
||||
}
|
||||
|
||||
render_customize()
|
||||
{
|
||||
label=$1
|
||||
fmt=$2
|
||||
|
||||
tmux_run "$label customize-mode" \
|
||||
customize-mode -t fmt:0 -F "CM$label: $fmt" >/dev/null
|
||||
sleep 0.5
|
||||
out=$(capture)
|
||||
bounded "$label customize-mode capture" "$out"
|
||||
tmux_run "$label quit customize-mode" send-keys -t fmt:0 q >/dev/null
|
||||
assert_alive "$label customize-mode"
|
||||
}
|
||||
|
||||
render_list_output()
|
||||
{
|
||||
label=$1
|
||||
fmt=$2
|
||||
|
||||
tmux_run "$label bind-key" \
|
||||
bind-key -T root F12 display-message "LK$label: $fmt" >/dev/null
|
||||
out=$(tmux_run "$label list-keys" list-keys -T root F12)
|
||||
bounded "$label list-keys" "$out"
|
||||
printf '%s' "$out" | grep -F "LK$label:" >/dev/null 2>&1 ||
|
||||
fail "$label list-keys missing rendered command"
|
||||
|
||||
tmux_run "$label set show option" \
|
||||
set-option -g "@format_render_$label" "SO$label: $fmt" >/dev/null
|
||||
out=$(tmux_run "$label show-options" show-options -g "@format_render_$label")
|
||||
bounded "$label show-options" "$out"
|
||||
printf '%s' "$out" | grep -F "SO$label:" >/dev/null 2>&1 ||
|
||||
fail "$label show-options missing option"
|
||||
|
||||
assert_alive "$label list output"
|
||||
}
|
||||
|
||||
run_corpus()
|
||||
{
|
||||
label=$1
|
||||
fmt=$2
|
||||
|
||||
render_status_options "$label" "$fmt"
|
||||
render_status_format "$label" "$fmt"
|
||||
render_message "$label" "$fmt"
|
||||
render_choose_tree "$label" "$fmt"
|
||||
render_customize "$label" "$fmt"
|
||||
render_list_output "$label" "$fmt"
|
||||
}
|
||||
|
||||
check_sgr_sanity()
|
||||
{
|
||||
esc=$(printf '\033')
|
||||
|
||||
tmux_run "set message style fill" \
|
||||
set-option -g message-style \
|
||||
"fg=#080808,bg=#ffff00,fill=#ffff00,bold" >/dev/null
|
||||
tmux_run "show filled message" \
|
||||
display-message -t fmt:0 -d 1000 \
|
||||
'#[bg=blue,italics] hello, #[fg=#080808,bg=default] world!' \
|
||||
>/dev/null
|
||||
wait_for "hello, world!"
|
||||
|
||||
out=$(capture_esc)
|
||||
printf '%s' "$out" | grep -F 'world!' >/dev/null 2>&1 ||
|
||||
fail "message text missing from SGR capture"
|
||||
printf '%s' "$out" | grep "$esc" >/dev/null 2>&1 ||
|
||||
fail "SGR capture has no escapes"
|
||||
|
||||
world_sgr=$(
|
||||
printf '%s\n' "$out" |
|
||||
awk -v esc="$esc" '
|
||||
/world!/ {
|
||||
i = index($0, "world!")
|
||||
pre = substr($0, 1, i - 1)
|
||||
n = split(pre, parts, esc "\\[")
|
||||
print parts[n]
|
||||
exit
|
||||
}'
|
||||
)
|
||||
|
||||
case "$world_sgr" in
|
||||
*'48;'*) ;;
|
||||
*) fail "world segment missing explicit background SGR: $world_sgr" ;;
|
||||
esac
|
||||
case "$world_sgr" in
|
||||
*'49'*)
|
||||
fail "world segment used default background instead of message fill: $world_sgr"
|
||||
;;
|
||||
esac
|
||||
|
||||
assert_alive "message SGR sanity"
|
||||
}
|
||||
|
||||
wide_fmt()
|
||||
{
|
||||
printf 'wide: \0316\0225\0316\0273\0316\0273\0316\0267\0316\0275\0316\0271\0316\0272\0316\0254 \0344\0270\0255\0346\0226\0207 #{=12:\0344\0270\0255\0346\0226\0207abc}'
|
||||
}
|
||||
|
||||
long_style_fmt()
|
||||
{
|
||||
printf '%s' '#[fg=colour1,bg=colour2,us=colour3,acs,bright,dim,underscore,blink,reverse,hidden,italics,strikethrough,double-underscore,curly-underscore,dotted-underscore,dashed-underscore,overline,range=user|aaaaaaaaaaaaaaaa,align=absolute-centre,list=on,fill=colour200,width=4294967295,pad=4294967295]OVERLONG-STYLE#[default]'
|
||||
}
|
||||
|
||||
trap cleanup 0 1 15
|
||||
cleanup
|
||||
|
||||
tmux_run "new inner session" \
|
||||
new-session -d -s fmt -x 100 -y 30 "exec sleep 1000" >/dev/null
|
||||
tmux_run "new inner window" \
|
||||
new-window -t fmt -n second "exec sleep 1000" >/dev/null
|
||||
tmux_run "select first window" select-window -t fmt:0 >/dev/null
|
||||
tmux_run "status interval" set-option -g status-interval 1 >/dev/null
|
||||
tmux_run "base status" set-option -g status on >/dev/null
|
||||
tmux_run "nested option" \
|
||||
set-option -g @nested 'NESTED-#{session_name}-#{window_index}' >/dev/null
|
||||
|
||||
run_cmd $TMUX2 new-session -d -s out -x 100 -y 30 "$TMUX attach -t fmt" \
|
||||
>/dev/null 2>&1 || fail "failed to start outer client"
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
c=$(tmux_run "wait clients" list-clients -F x | grep -c x)
|
||||
[ "$c" -eq 1 ] && break
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ "$i" -lt 50 ] || fail "inner client did not attach"
|
||||
|
||||
run_corpus A 'plain #{session_name}:#{window_index} #{@nested}'
|
||||
run_corpus B "$(wide_fmt)"
|
||||
run_corpus C '#[fg=colour10,bg=colour17,bold,italics]styled #{pane_current_command}#[default]'
|
||||
run_corpus D '#[push-default]#[fg=red,bg=blue]push #{?pane_active,active,inactive}#[pop-default] after'
|
||||
run_corpus E "$(long_style_fmt)"
|
||||
|
||||
check_sgr_sanity
|
||||
|
||||
cleanup
|
||||
exit 0
|
||||
@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
# test_format $format $expected_result
|
||||
test_format()
|
||||
|
||||
@@ -15,10 +15,10 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
# A second server on its own socket provides a real terminal (an inner client
|
||||
# attached inside one of its panes) so client terminal variables are populated.
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
# Every variable name in format_table[]. Kept as a plain word list so it can be
|
||||
# iterated with normal shell word splitting.
|
||||
@@ -133,6 +133,7 @@ pane_pipe_pid
|
||||
pane_right
|
||||
pane_search_string
|
||||
pane_start_command
|
||||
pane_start_command_list
|
||||
pane_start_path
|
||||
pane_synchronized
|
||||
pane_tabs
|
||||
@@ -295,7 +296,7 @@ sleep 1
|
||||
# write end of a FIFO open, so client_* variables have a client to read.
|
||||
rm -f "$FIFO"
|
||||
mkfifo "$FIFO" || exit 1
|
||||
sleep 30 >"$FIFO" &
|
||||
while :; do sleep 1; done >"$FIFO" &
|
||||
HOLD=$!
|
||||
$TMUX -C attach -t cov <"$FIFO" >/dev/null 2>&1 &
|
||||
CC=$!
|
||||
@@ -343,6 +344,8 @@ test_var pane_at_top "1" -t "$TGT"
|
||||
test_var pane_at_left "1" -t "$TGT"
|
||||
test_var last_window_index "1" -t "$TGT"
|
||||
test_var pid "$($TMUX display-message -p '#{pid}')" -t "$TGT"
|
||||
test_var pane_start_command "cat" -t "$TGT"
|
||||
test_var pane_start_command_list "'cat'" -t "$TGT"
|
||||
|
||||
# The grouped session is reported as such.
|
||||
test_var session_grouped "1" -t "cov:"
|
||||
@@ -384,5 +387,29 @@ esac
|
||||
[ -n "$($TMUX display-message -p '#{t/r:start_time}')" ] ||
|
||||
fail "Empty #{t/r:start_time}."
|
||||
|
||||
# pane_start_command_list quotes each argv word for sh, so evaluating the
|
||||
# expansion reconstructs the original argv exactly - including words with
|
||||
# quotes, spaces, newlines and empty words. sh -c ignores the extra words
|
||||
# (they become positional parameters), so the pane stays alive. -u stops
|
||||
# the server sanitizing the newline away when printing to a non-UTF-8
|
||||
# client (the test runs without a locale in the environment).
|
||||
$TMUX new-session -d -s quot -x 80 -y 24 -- sh -c 'sleep 100' arg0 \
|
||||
"it's a 'test'" 'two words' '' 'new
|
||||
line' || fail "Failed to create quoting test session."
|
||||
LIST=$($TMUX -u display-message -t 'quot:0.0' -p '#{pane_start_command_list}')
|
||||
eval "set -- $LIST"
|
||||
GOT=$(for a; do printf '<%s>' "$a"; done)
|
||||
EXP=$(for a in sh -c 'sleep 100' arg0 "it's a 'test'" 'two words' '' 'new
|
||||
line'; do printf '<%s>' "$a"; done)
|
||||
if [ "$GOT" != "$EXP" ]; then
|
||||
echo "pane_start_command_list did not round-trip."
|
||||
echo "Expected: $EXP"
|
||||
echo "But got: $GOT"
|
||||
fail "Expansion was: $LIST"
|
||||
fi
|
||||
# A pane started with the default shell has an empty start command.
|
||||
$TMUX new-window -d -t 'quot:' || fail "Failed to create shell window."
|
||||
test_var pane_start_command_list "" -t "quot:1.0"
|
||||
|
||||
cleanup
|
||||
exit 0
|
||||
|
||||
@@ -7,7 +7,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
$TMUX -f/dev/null has -tfoo </dev/null 2>/dev/null && exit 1
|
||||
|
||||
146
regress/hooks-after.sh
Normal file
146
regress/hooks-after.sh
Normal file
@@ -0,0 +1,146 @@
|
||||
#!/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)
|
||||
OUT=$(mktemp -d)
|
||||
TMUX_TMPDIR="$OUT"
|
||||
export TMUX_TMPDIR
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
wait_for()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 30 ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] && return 0
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
fail "expected $option to be '$expected' but got '$value'"
|
||||
}
|
||||
|
||||
assert_unchanged()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 10 ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] || \
|
||||
fail "expected $option to remain '$expected' but got '$value'"
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
}
|
||||
|
||||
$TMUX new -d -s one || fail "new-session one failed"
|
||||
$TMUX new -d -s two || fail "new-session two failed"
|
||||
|
||||
# An after hook fires with the command arguments as formats.
|
||||
$TMUX set -g @after 0 || fail "set @after failed"
|
||||
$TMUX set-hook -g after-rename-window \
|
||||
'set -gF @after "#{hook}|#{hook_arguments}|#{hook_argument_0}|#{hook_flag_t}"' ||
|
||||
fail "set-hook -g after-rename-window failed"
|
||||
$TMUX rename-window -t one:0 first || fail "rename-window first failed"
|
||||
wait_for @after 'after-rename-window|-t one:0 first|first|one:0'
|
||||
|
||||
# An appended hook command runs after the first.
|
||||
$TMUX set -g @after2 0 || fail "set @after2 failed"
|
||||
$TMUX set-hook -ga after-rename-window \
|
||||
'set -gF @after2 "#{@after}+2"' ||
|
||||
fail "set-hook -ga after-rename-window failed"
|
||||
$TMUX rename-window -t one:0 second || fail "rename-window second failed"
|
||||
wait_for @after 'after-rename-window|-t one:0 second|second|one:0'
|
||||
wait_for @after2 'after-rename-window|-t one:0 second|second|one:0+2'
|
||||
$TMUX set-hook -gu after-rename-window || fail "set-hook -gu failed"
|
||||
|
||||
# A hook command is inserted after the command that fired it, before the next
|
||||
# command in the same command list.
|
||||
$TMUX set -g @order '' || fail "set @order failed"
|
||||
$TMUX set-hook -g after-rename-window \
|
||||
'set -gF @order "#{@order}H"' ||
|
||||
fail "set-hook -g after-rename-window order failed"
|
||||
$TMUX rename-window -t one:0 ordered \; set -gF @order '#{@order}N' ||
|
||||
fail "rename-window order failed"
|
||||
wait_for @order 'HN'
|
||||
$TMUX set-hook -gu after-rename-window || fail "set-hook -gu order failed"
|
||||
|
||||
# Repeated flag values are available as numbered hook flag formats.
|
||||
$TMUX set -g @flags 0 || fail "set @flags failed"
|
||||
$TMUX set-hook -g after-split-window \
|
||||
'set -gF @flags "#{hook_arguments}|#{hook_argument_0}|#{hook_flag_t}|#{hook_flag_e}|#{hook_flag_e_0}|#{hook_flag_e_1}"' ||
|
||||
fail "set-hook -g after-split-window failed"
|
||||
$TMUX split-window -d -t one:0 -e A=1 -e B=2 'sleep 60' ||
|
||||
fail "split-window flags failed"
|
||||
wait_for @flags '-d -e A=1 -e B=2 -t one:0 "sleep 60"|sleep 60|one:0|B=2|A=1|B=2'
|
||||
$TMUX set-hook -gu after-split-window ||
|
||||
fail "set-hook -gu after-split-window failed"
|
||||
|
||||
# A session after hook only fires for commands targeting that session and
|
||||
# the hook commands run with the command target as current state.
|
||||
$TMUX set -g @safter 0 || fail "set @safter failed"
|
||||
$TMUX set-hook -t two after-rename-window \
|
||||
'set -gF @safter "#{hook}:#{session_name}"' ||
|
||||
fail "set-hook -t two after-rename-window failed"
|
||||
$TMUX rename-window -t one:0 third || fail "rename-window third failed"
|
||||
assert_unchanged @safter 0
|
||||
$TMUX rename-window -t two:0 fourth || fail "rename-window fourth failed"
|
||||
wait_for @safter 'after-rename-window:two'
|
||||
$TMUX set-hook -u -t two after-rename-window ||
|
||||
fail "set-hook -u -t two failed"
|
||||
|
||||
# The command-error hook fires when a command fails.
|
||||
$TMUX set -g @error 0 || fail "set @error failed"
|
||||
$TMUX set-hook -g command-error 'set -gF @error "#{hook}"' ||
|
||||
fail "set-hook -g command-error failed"
|
||||
if $TMUX rename-window -t nosuchsession:0 x 2>/dev/null; then
|
||||
fail "rename-window to missing session succeeded"
|
||||
fi
|
||||
wait_for @error 'command-error'
|
||||
$TMUX set-hook -gu command-error || fail "set-hook -gu command-error failed"
|
||||
|
||||
# Commands run from a hook do not fire their own after hooks.
|
||||
$TMUX set -g @copy 0 || fail "set @copy failed"
|
||||
$TMUX set-hook -g after-copy-mode 'set -gF @copy "#{hook}"' ||
|
||||
fail "set-hook -g after-copy-mode failed"
|
||||
$TMUX copy-mode -t one:0 || fail "copy-mode failed"
|
||||
wait_for @copy 'after-copy-mode'
|
||||
$TMUX send-keys -t one:0 -X cancel || fail "cancel failed"
|
||||
$TMUX set -g @copy 0 || fail "reset @copy failed"
|
||||
$TMUX set -g @ran 0 || fail "set @ran failed"
|
||||
$TMUX set-hook -g after-rename-window 'copy-mode -t one:0' ||
|
||||
fail "set-hook -g after-rename-window nested failed"
|
||||
$TMUX set-hook -ga after-rename-window 'set -g @ran 1' ||
|
||||
fail "set-hook -ga after-rename-window nested failed"
|
||||
$TMUX rename-window -t one:0 fifth || fail "rename-window fifth failed"
|
||||
wait_for @ran 1
|
||||
mode=$($TMUX display -pt one:0 '#{pane_in_mode}') ||
|
||||
fail "display pane_in_mode failed"
|
||||
[ "$mode" = 1 ] || fail "hook did not enter copy mode"
|
||||
assert_unchanged @copy 0
|
||||
|
||||
exit 0
|
||||
176
regress/hooks-lifecycle.sh
Normal file
176
regress/hooks-lifecycle.sh
Normal file
@@ -0,0 +1,176 @@
|
||||
#!/bin/sh
|
||||
|
||||
# End-of-life hooks: pane-exited, pane-died, window-unlinked and
|
||||
# session-closed when the pane, window or session they refer to is being
|
||||
# or has been destroyed. Each hook appends to @log so the order hooks fire
|
||||
# in is checked as well as the hook formats for the dead objects.
|
||||
|
||||
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)
|
||||
OUT=$(mktemp -d)
|
||||
TMUX_TMPDIR="$OUT"
|
||||
export TMUX_TMPDIR
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
wait_for()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 30 ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] && return 0
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
fail "expected $option to be '$expected' but got '$value'"
|
||||
}
|
||||
|
||||
assert_unchanged()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 10 ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] || \
|
||||
fail "expected $option to remain '$expected' but got '$value'"
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
}
|
||||
|
||||
$TMUX new -d -s main || fail "new-session main failed"
|
||||
|
||||
$TMUX set -g @log '' || fail "set @log failed"
|
||||
$TMUX set-hook -g pane-exited \
|
||||
'set -gF @log "#{@log}|pane-exited:#{hook_pane}"' ||
|
||||
fail "set-hook pane-exited failed"
|
||||
$TMUX set-hook -g window-unlinked \
|
||||
'set -gF @log "#{@log}|window-unlinked:#{hook_session_name}:#{hook_window_name}"' ||
|
||||
fail "set-hook window-unlinked failed"
|
||||
$TMUX set-hook -g session-closed \
|
||||
'set -gF @log "#{@log}|session-closed:#{hook_session_name}"' ||
|
||||
fail "set-hook session-closed failed"
|
||||
|
||||
# The only pane of the only window of a session exits: pane-exited, then
|
||||
# window-unlinked, then session-closed, each seeing the dead object in the
|
||||
# hook formats.
|
||||
pane=$($TMUX new -d -s doomed -n dwin -P -F '#{pane_id}' 'true') ||
|
||||
fail "new-session doomed failed"
|
||||
wait_for @log \
|
||||
"|pane-exited:$pane|window-unlinked:doomed:dwin|session-closed:doomed"
|
||||
assert_unchanged @log \
|
||||
"|pane-exited:$pane|window-unlinked:doomed:dwin|session-closed:doomed"
|
||||
|
||||
# The dead pane, window and session cannot be used as targets but the
|
||||
# server survives.
|
||||
if $TMUX select-pane -t "$pane" 2>/dev/null; then
|
||||
fail "dead pane still a valid target"
|
||||
fi
|
||||
if $TMUX list-windows -t doomed >/dev/null 2>&1; then
|
||||
fail "dead session still a valid target"
|
||||
fi
|
||||
$TMUX list-panes -sat main >/dev/null || fail "list-panes failed"
|
||||
$TMUX has -t main || fail "server died after pane exit chain"
|
||||
|
||||
# A pane-exited hook command can run after the pane has been removed. It
|
||||
# should not retain only the event payload's temporary target references.
|
||||
$TMUX set -g @queued-pane-exited 0 || fail "set @queued-pane-exited failed"
|
||||
$TMUX set-hook -g pane-exited \
|
||||
'display-message -p "queued #{hook_pane}" ; set -g @queued-pane-exited 1' ||
|
||||
fail "set-hook queued pane-exited failed"
|
||||
pane=$($TMUX new -d -s queued -n qwin -P -F '#{pane_id}' 'true') ||
|
||||
fail "new-session queued failed"
|
||||
wait_for @queued-pane-exited 1
|
||||
$TMUX has -t main || fail "server died after queued pane-exited hook"
|
||||
$TMUX set-hook -g pane-exited \
|
||||
'set -gF @log "#{@log}|pane-exited:#{hook_pane}"' ||
|
||||
fail "restore pane-exited hook failed"
|
||||
|
||||
# kill-window on the last window: window-unlinked then session-closed but
|
||||
# no pane-exited for the panes in the killed window.
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
$TMUX new -d -s doomed2 -n dwin2 || fail "new-session doomed2 failed"
|
||||
$TMUX splitw -d -t doomed2:0 || fail "split-window doomed2 failed"
|
||||
$TMUX kill-window -t doomed2:0 || fail "kill-window failed"
|
||||
wait_for @log '|window-unlinked:doomed2:dwin2|session-closed:doomed2'
|
||||
assert_unchanged @log '|window-unlinked:doomed2:dwin2|session-closed:doomed2'
|
||||
$TMUX has -t main || fail "server died after kill-window chain"
|
||||
|
||||
# kill-session: session-closed fires first, then window-unlinked for its
|
||||
# windows. A window linked into another session survives.
|
||||
$TMUX new -d -s shareA -n shared || fail "new-session shareA failed"
|
||||
$TMUX new -d -s shareB -n bwin || fail "new-session shareB failed"
|
||||
$TMUX link-window -s shareA:shared -t shareB:7 || fail "link-window failed"
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
$TMUX kill-session -t shareA || fail "kill-session shareA failed"
|
||||
wait_for @log '|session-closed:shareA|window-unlinked:shareA:shared'
|
||||
name=$($TMUX display -pt shareB:7 '#{window_name}') ||
|
||||
fail "shared window did not survive"
|
||||
[ "$name" = shared ] || fail "expected window shared but got $name"
|
||||
|
||||
# Killing the surviving session destroys the shared window for real while
|
||||
# the session is being destroyed.
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
$TMUX kill-window -t shareB:0 || fail "kill-window bwin failed"
|
||||
wait_for @log '|window-unlinked:shareB:bwin'
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
$TMUX kill-session -t shareB || fail "kill-session shareB failed"
|
||||
wait_for @log '|session-closed:shareB|window-unlinked:shareB:shared'
|
||||
$TMUX has -t main || fail "server died after kill-session chain"
|
||||
|
||||
# A pane-died hook can kill its own dead pane (the hook runs with the dead
|
||||
# pane as current target). The kill-pane runs without hooks so pane-exited
|
||||
# does not fire.
|
||||
$TMUX set -g @log '' || fail "reset @log failed"
|
||||
$TMUX new -d -s roe -n rwin || fail "new-session roe failed"
|
||||
$TMUX set -wt roe:0 remain-on-exit on || fail "set remain-on-exit failed"
|
||||
$TMUX set-hook -g pane-died \
|
||||
'set -gF @log "#{@log}|pane-died:#{hook_pane}" ; kill-pane' ||
|
||||
fail "set-hook pane-died failed"
|
||||
pane=$($TMUX splitw -d -t roe:0 -P -F '#{pane_id}' 'true') ||
|
||||
fail "split-window roe failed"
|
||||
wait_for @log "|pane-died:$pane"
|
||||
assert_unchanged @log "|pane-died:$pane"
|
||||
if $TMUX select-pane -t "$pane" 2>/dev/null; then
|
||||
fail "pane-died hook did not kill its pane"
|
||||
fi
|
||||
$TMUX list-panes -t roe:0 >/dev/null || fail "surviving pane broken"
|
||||
$TMUX has -t roe || fail "session roe died"
|
||||
$TMUX set-hook -gu pane-died || fail "unset pane-died failed"
|
||||
$TMUX kill-session -t roe || fail "kill-session roe failed"
|
||||
|
||||
# Killing the last session with end-of-life hooks still set: the server
|
||||
# runs the hooks and exits cleanly.
|
||||
$TMUX kill-session -t main || fail "kill-session main failed"
|
||||
i=0
|
||||
while $TMUX has 2>/dev/null; do
|
||||
i=$((i + 1))
|
||||
[ $i -lt 30 ] || fail "server still running after last session killed"
|
||||
sleep 0.2
|
||||
done
|
||||
|
||||
exit 0
|
||||
292
regress/hooks-notify.sh
Normal file
292
regress/hooks-notify.sh
Normal file
@@ -0,0 +1,292 @@
|
||||
#!/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)
|
||||
OUT=$(mktemp -d)
|
||||
TMUX_TMPDIR="$OUT"
|
||||
export TMUX_TMPDIR
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
wait_for()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 30 ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] && return 0
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
fail "expected $option to be '$expected' but got '$value'"
|
||||
}
|
||||
|
||||
$TMUX new -d -s main || fail "new-session main failed"
|
||||
|
||||
# session-created, session-renamed, session-closed.
|
||||
$TMUX set -g @s 0 || fail "set @s failed"
|
||||
$TMUX set-hook -g session-created \
|
||||
'set -gF @s "#{hook}:#{hook_session_name}"' ||
|
||||
fail "set-hook session-created failed"
|
||||
$TMUX new -d -s tmp || fail "new-session tmp failed"
|
||||
wait_for @s 'session-created:tmp'
|
||||
$TMUX set-hook -gu session-created || fail "unset session-created failed"
|
||||
|
||||
$TMUX set-hook -g session-renamed \
|
||||
'set -gF @s "#{hook}:#{hook_session_name}"' ||
|
||||
fail "set-hook session-renamed failed"
|
||||
$TMUX rename-session -t tmp tmp2 || fail "rename-session failed"
|
||||
wait_for @s 'session-renamed:tmp2'
|
||||
$TMUX set-hook -gu session-renamed || fail "unset session-renamed failed"
|
||||
|
||||
$TMUX set-hook -g session-closed \
|
||||
'set -gF @s "#{hook}:#{hook_session_name}"' ||
|
||||
fail "set-hook session-closed failed"
|
||||
$TMUX kill-session -t tmp2 || fail "kill-session failed"
|
||||
wait_for @s 'session-closed:tmp2'
|
||||
$TMUX set-hook -gu session-closed || fail "unset session-closed failed"
|
||||
|
||||
# window-linked and window-unlinked from new-window and kill-window.
|
||||
$TMUX set -g @w 0 || fail "set @w failed"
|
||||
$TMUX set-hook -g window-linked \
|
||||
'set -gF @w "#{hook}:#{hook_session_name}:#{hook_window_name}"' ||
|
||||
fail "set-hook window-linked failed"
|
||||
$TMUX set-hook -g window-unlinked \
|
||||
'set -gF @w "#{hook}:#{hook_session_name}:#{hook_window_name}"' ||
|
||||
fail "set-hook window-unlinked failed"
|
||||
$TMUX neww -d -t main: -n mywin || fail "new-window failed"
|
||||
wait_for @w 'window-linked:main:mywin'
|
||||
|
||||
# window-linked and window-unlinked from link-window and unlink-window.
|
||||
$TMUX new -d -s other || fail "new-session other failed"
|
||||
$TMUX set -g @w 0 || fail "reset @w failed"
|
||||
$TMUX link-window -s main:mywin -t other:5 || fail "link-window failed"
|
||||
wait_for @w 'window-linked:other:mywin'
|
||||
$TMUX set -g @w 0 || fail "reset @w failed"
|
||||
$TMUX unlink-window -t other:5 || fail "unlink-window failed"
|
||||
wait_for @w 'window-unlinked:other:mywin'
|
||||
$TMUX set -g @w 0 || fail "reset @w failed"
|
||||
$TMUX kill-window -t main:mywin || fail "kill-window failed"
|
||||
wait_for @w 'window-unlinked:main:mywin'
|
||||
$TMUX set-hook -gu window-linked || fail "unset window-linked failed"
|
||||
$TMUX set-hook -gu window-unlinked || fail "unset window-unlinked failed"
|
||||
|
||||
# window-renamed sees the window id.
|
||||
window=$($TMUX display -pt main:0 '#{window_id}') ||
|
||||
fail "display window_id failed"
|
||||
$TMUX set -g @r 0 || fail "set @r failed"
|
||||
$TMUX set-hook -g window-renamed \
|
||||
'set -gF @r "#{hook}:#{hook_window}:#{hook_window_name}"' ||
|
||||
fail "set-hook window-renamed failed"
|
||||
$TMUX rename-window -t main:0 renamed || fail "rename-window failed"
|
||||
wait_for @r "window-renamed:$window:renamed"
|
||||
$TMUX set-hook -gu window-renamed || fail "unset window-renamed failed"
|
||||
|
||||
# window-layout-changed from split-window.
|
||||
$TMUX set -g @l 0 || fail "set @l failed"
|
||||
$TMUX set-hook -g window-layout-changed \
|
||||
'set -gF @l "#{hook}:#{hook_window}"' ||
|
||||
fail "set-hook window-layout-changed failed"
|
||||
$TMUX splitw -d -t main:0 || fail "split-window failed"
|
||||
wait_for @l "window-layout-changed:$window"
|
||||
$TMUX set-hook -gu window-layout-changed ||
|
||||
fail "unset window-layout-changed failed"
|
||||
|
||||
# window-pane-changed from select-pane.
|
||||
$TMUX set -g @p 0 || fail "set @p failed"
|
||||
$TMUX set-hook -g window-pane-changed \
|
||||
'set -gF @p "#{hook}:#{hook_window}"' ||
|
||||
fail "set-hook window-pane-changed failed"
|
||||
$TMUX selectp -t main:0.1 || fail "select-pane failed"
|
||||
wait_for @p "window-pane-changed:$window"
|
||||
$TMUX set-hook -gu window-pane-changed ||
|
||||
fail "unset window-pane-changed failed"
|
||||
|
||||
# session-window-changed from select-window.
|
||||
$TMUX neww -d -t main: -n w2 || fail "new-window w2 failed"
|
||||
$TMUX set -g @c 0 || fail "set @c failed"
|
||||
$TMUX set-hook -g session-window-changed \
|
||||
'set -gF @c "#{hook}:#{hook_session_name}"' ||
|
||||
fail "set-hook session-window-changed failed"
|
||||
$TMUX selectw -t main:w2 || fail "select-window failed"
|
||||
wait_for @c 'session-window-changed:main'
|
||||
$TMUX set-hook -gu session-window-changed ||
|
||||
fail "unset session-window-changed failed"
|
||||
|
||||
# pane-mode-changed from entering and leaving copy mode.
|
||||
$TMUX set -g @m 0 || fail "set @m failed"
|
||||
pane=$($TMUX display -pt main:0.0 '#{pane_id}') ||
|
||||
fail "display pane_id failed"
|
||||
$TMUX set-hook -g pane-mode-changed \
|
||||
'set -gF @m "#{hook}:#{hook_pane}:#{pane_in_mode}"' ||
|
||||
fail "set-hook pane-mode-changed failed"
|
||||
$TMUX copy-mode -t main:0.0 || fail "copy-mode failed"
|
||||
wait_for @m "pane-mode-changed:$pane:1"
|
||||
$TMUX send-keys -t main:0.0 -X cancel || fail "cancel failed"
|
||||
wait_for @m "pane-mode-changed:$pane:0"
|
||||
$TMUX set-hook -gu pane-mode-changed || fail "unset pane-mode-changed failed"
|
||||
|
||||
# pane-exited when a pane's command exits with remain-on-exit off.
|
||||
$TMUX set -g @x 0 || fail "set @x failed"
|
||||
$TMUX set-hook -g pane-exited 'set -gF @x "#{hook}:#{hook_pane}"' ||
|
||||
fail "set-hook pane-exited failed"
|
||||
pane=$($TMUX splitw -d -t main:0 -P -F '#{pane_id}' 'true') ||
|
||||
fail "split-window true failed"
|
||||
wait_for @x "pane-exited:$pane"
|
||||
$TMUX set-hook -gu pane-exited || fail "unset pane-exited failed"
|
||||
|
||||
# pane-died when a pane's command exits with remain-on-exit on.
|
||||
$TMUX set -g remain-on-exit on || fail "set remain-on-exit failed"
|
||||
$TMUX set -g @d 0 || fail "set @d failed"
|
||||
$TMUX set-hook -g pane-died 'set -gF @d "#{hook}:#{hook_pane}"' ||
|
||||
fail "set-hook pane-died failed"
|
||||
pane=$($TMUX splitw -d -t main:0 -P -F '#{pane_id}' 'true') ||
|
||||
fail "split-window remain-on-exit failed"
|
||||
wait_for @d "pane-died:$pane"
|
||||
$TMUX set-hook -gu pane-died || fail "unset pane-died failed"
|
||||
$TMUX killp -t "$pane" || fail "kill-pane failed"
|
||||
$TMUX set -g remain-on-exit off || fail "reset remain-on-exit failed"
|
||||
|
||||
# pane-title-changed when a pane sets its title.
|
||||
$TMUX set -g @t 0 || fail "set @t failed"
|
||||
$TMUX set-hook -g pane-title-changed \
|
||||
'set -gF @t "#{hook}:#{hook_pane}:#{pane_title}"' ||
|
||||
fail "set-hook pane-title-changed failed"
|
||||
pane=$($TMUX splitw -d -t main:0 -P -F '#{pane_id}' \
|
||||
'printf "\033]2;mytitle\007"; sleep 30') ||
|
||||
fail "split-window title failed"
|
||||
wait_for @t "pane-title-changed:$pane:mytitle"
|
||||
$TMUX set-hook -gu pane-title-changed ||
|
||||
fail "unset pane-title-changed failed"
|
||||
$TMUX killp -t "$pane" || fail "kill-pane title failed"
|
||||
|
||||
# pane-created payload from a command pane, an empty pane, and a respawn.
|
||||
$TMUX set -g @pc 0 || fail "set @pc failed"
|
||||
$TMUX set-hook -g pane-created \
|
||||
'set -gF @pc "#{hook}:#{hook_pane}:#{hook_pane_command}:#{hook_created_empty}:#{hook_created_respawn}"' ||
|
||||
fail "set-hook pane-created failed"
|
||||
pane=$($TMUX splitw -d -t main:0 -P -F '#{pane_id}' 'sleep 30') ||
|
||||
fail "split-window pane-created command failed"
|
||||
wait_for @pc "pane-created:$pane:\"sleep 30\":0:0"
|
||||
$TMUX killp -t "$pane" || fail "kill-pane pane-created command failed"
|
||||
|
||||
$TMUX set -g @pc 0 || fail "reset @pc failed"
|
||||
empty_command=$($TMUX show -gqv default-shell) ||
|
||||
fail "show default-shell failed"
|
||||
pane=$($TMUX splitw -d -E -t main:0 -P -F '#{pane_id}') ||
|
||||
fail "split-window pane-created empty failed"
|
||||
wait_for @pc "pane-created:$pane:$empty_command:1:0"
|
||||
|
||||
$TMUX set -g @pc 0 || fail "reset @pc respawn failed"
|
||||
$TMUX respawnp -k -t "$pane" 'sleep 30' || fail "respawn-pane failed"
|
||||
wait_for @pc "pane-created:$pane:\"sleep 30\":0:1"
|
||||
$TMUX killp -t "$pane" || fail "kill-pane pane-created respawn failed"
|
||||
$TMUX set-hook -gu pane-created || fail "unset pane-created failed"
|
||||
|
||||
# pane-resized carries old and new dimensions.
|
||||
pane=$($TMUX splitw -d -t main:0 -P -F '#{pane_id}' 'sleep 30') ||
|
||||
fail "split-window pane-resized failed"
|
||||
old_size=$($TMUX display -pt "$pane" '#{pane_width},#{pane_height}') ||
|
||||
fail "display old pane size failed"
|
||||
$TMUX set -g @pr 0 || fail "set @pr failed"
|
||||
$TMUX set-hook -g pane-resized \
|
||||
'set -gF @pr "#{hook}:#{hook_pane}:#{hook_old_width},#{hook_old_height}->#{hook_width},#{hook_height}"' ||
|
||||
fail "set-hook pane-resized failed"
|
||||
$TMUX resizep -t "$pane" -x 20 -y 10 || fail "resize-pane failed"
|
||||
new_size=$($TMUX display -pt "$pane" '#{pane_width},#{pane_height}') ||
|
||||
fail "display new pane size failed"
|
||||
wait_for @pr "pane-resized:$pane:$old_size->$new_size"
|
||||
$TMUX set-hook -gu pane-resized || fail "unset pane-resized failed"
|
||||
$TMUX killp -t "$pane" || fail "kill-pane pane-resized failed"
|
||||
|
||||
# pane-mode-entered and pane-mode-exited expose split transition payloads.
|
||||
$TMUX set -g @me 0 || fail "set @me failed"
|
||||
$TMUX set -g @mx 0 || fail "set @mx failed"
|
||||
pane=$($TMUX splitw -d -t main:0 -P -F '#{pane_id}' 'sleep 30') ||
|
||||
fail "split-window pane-mode failed"
|
||||
$TMUX set-hook -g pane-mode-entered \
|
||||
'set -gF @me "#{hook}:#{hook_pane}:#{hook_mode_entered}:#{hook_current_mode}:#{hook_previous_mode}"' ||
|
||||
fail "set-hook pane-mode-entered failed"
|
||||
$TMUX set-hook -g pane-mode-exited \
|
||||
'set -gF @mx "#{hook}:#{hook_pane}:#{hook_mode_entered}:#{hook_current_mode}:#{hook_previous_mode}"' ||
|
||||
fail "set-hook pane-mode-exited failed"
|
||||
$TMUX copy-mode -t "$pane" || fail "copy-mode split event failed"
|
||||
wait_for @me "pane-mode-entered:$pane:1:copy-mode:"
|
||||
$TMUX send-keys -t "$pane" -X cancel || fail "cancel split mode failed"
|
||||
wait_for @mx "pane-mode-exited:$pane:0::copy-mode"
|
||||
$TMUX set-hook -gu pane-mode-entered || fail "unset pane-mode-entered failed"
|
||||
$TMUX set-hook -gu pane-mode-exited || fail "unset pane-mode-exited failed"
|
||||
$TMUX killp -t "$pane" || fail "kill-pane pane-mode failed"
|
||||
|
||||
# marked-pane-changed carries the new/old marked pane and marked flag.
|
||||
$TMUX set -g @mk 0 || fail "set @mk failed"
|
||||
pane=$($TMUX splitw -d -t main:0 -P -F '#{pane_id}' 'sleep 30') ||
|
||||
fail "split-window marked-pane failed"
|
||||
$TMUX set-hook -g marked-pane-changed \
|
||||
'set -gF @mk "#{hook}:#{hook_marked}:#{hook_pane}:#{hook_new_pane}:#{hook_old_pane}"' ||
|
||||
fail "set-hook marked-pane-changed failed"
|
||||
$TMUX selectp -t "$pane" -m || fail "mark pane failed"
|
||||
wait_for @mk "marked-pane-changed:1:$pane:$pane:"
|
||||
$TMUX selectp -t "$pane" -m || fail "unmark pane failed"
|
||||
wait_for @mk "marked-pane-changed:0:$pane::$pane"
|
||||
$TMUX set-hook -gu marked-pane-changed ||
|
||||
fail "unset marked-pane-changed failed"
|
||||
$TMUX killp -t "$pane" || fail "kill-pane marked-pane failed"
|
||||
|
||||
# window-zoomed and window-unzoomed fire on resize-pane -Z.
|
||||
$TMUX set -g @zoom 0 || fail "set @zoom failed"
|
||||
pane=$($TMUX splitw -d -t main:0 -P -F '#{pane_id}' 'sleep 30') ||
|
||||
fail "split-window zoom failed"
|
||||
window=$($TMUX display -pt "$pane" '#{window_id}') ||
|
||||
fail "display zoom window failed"
|
||||
$TMUX set-hook -g window-zoomed \
|
||||
'set -gF @zoom "#{hook}:#{hook_window}"' ||
|
||||
fail "set-hook window-zoomed failed"
|
||||
$TMUX set-hook -g window-unzoomed \
|
||||
'set -gF @zoom "#{hook}:#{hook_window}"' ||
|
||||
fail "set-hook window-unzoomed failed"
|
||||
$TMUX resizep -Z -t "$pane" || fail "zoom pane failed"
|
||||
wait_for @zoom "window-zoomed:$window"
|
||||
$TMUX resizep -Z -t "$pane" || fail "unzoom pane failed"
|
||||
wait_for @zoom "window-unzoomed:$window"
|
||||
$TMUX set-hook -gu window-zoomed || fail "unset window-zoomed failed"
|
||||
$TMUX set-hook -gu window-unzoomed || fail "unset window-unzoomed failed"
|
||||
$TMUX killp -t "$pane" || fail "kill-pane zoom failed"
|
||||
|
||||
# client-attached and client-detached using a control client.
|
||||
$TMUX set -g @a 0 || fail "set @a failed"
|
||||
$TMUX set-hook -g client-attached 'set -gF @a "#{hook}"' ||
|
||||
fail "set-hook client-attached failed"
|
||||
$TMUX set-hook -g client-detached 'set -gF @a "#{hook}"' ||
|
||||
fail "set-hook client-detached failed"
|
||||
mkfifo "$OUT/fifo" || fail "mkfifo failed"
|
||||
$TMUX -C attach -t main <"$OUT/fifo" >"$OUT/control.out" 2>&1 &
|
||||
exec 3>"$OUT/fifo"
|
||||
wait_for @a 'client-attached'
|
||||
exec 3>&-
|
||||
wait_for @a 'client-detached'
|
||||
$TMUX set-hook -gu client-attached || fail "unset client-attached failed"
|
||||
$TMUX set-hook -gu client-detached || fail "unset client-detached failed"
|
||||
|
||||
exit 0
|
||||
155
regress/hooks.sh
Normal file
155
regress/hooks.sh
Normal file
@@ -0,0 +1,155 @@
|
||||
#!/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)
|
||||
OUT=$(mktemp -d)
|
||||
TMUX_TMPDIR="$OUT"
|
||||
export TMUX_TMPDIR
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
wait_for()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 30 ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] && return 0
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
fail "expected $option to be '$expected' but got '$value'"
|
||||
}
|
||||
|
||||
assert_unchanged()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 10 ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] || \
|
||||
fail "expected $option to remain '$expected' but got '$value'"
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
}
|
||||
|
||||
$TMUX new -d -s one || fail "new-session one failed"
|
||||
|
||||
# A global hook fires and sees the hook formats.
|
||||
$TMUX set -g @created 0 || fail "set @created failed"
|
||||
$TMUX set-hook -g session-created \
|
||||
'set -gF @created "#{hook}:#{hook_session_name}"' ||
|
||||
fail "set-hook -g session-created failed"
|
||||
$TMUX new -d -s two || fail "new-session two failed"
|
||||
wait_for @created 'session-created:two'
|
||||
|
||||
# Hooks are arrays: an appended command runs after the first.
|
||||
$TMUX set -g @second 0 || fail "set @second failed"
|
||||
$TMUX set-hook -ga session-created \
|
||||
'set -gF @second "#{@created}+second"' ||
|
||||
fail "set-hook -ga session-created failed"
|
||||
$TMUX new -d -s three || fail "new-session three failed"
|
||||
wait_for @created 'session-created:three'
|
||||
wait_for @second 'session-created:three+second'
|
||||
|
||||
# show-hooks lists both commands.
|
||||
shown=$($TMUX show-hooks -g session-created) ||
|
||||
fail "show-hooks -g failed"
|
||||
[ "$(echo "$shown" | wc -l)" -eq 2 ] ||
|
||||
fail "unexpected show-hooks output: $shown"
|
||||
echo "$shown" | grep -q '^session-created\[0\]' ||
|
||||
fail "missing first array item: $shown"
|
||||
echo "$shown" | grep -q '^session-created\[1\]' ||
|
||||
fail "missing second array item: $shown"
|
||||
|
||||
# Unsetting removes the whole hook.
|
||||
$TMUX set-hook -gu session-created || fail "set-hook -gu failed"
|
||||
shown=$($TMUX show-hooks -g session-created) ||
|
||||
fail "show-hooks -g after unset failed"
|
||||
if echo "$shown" | grep -q '\['; then
|
||||
fail "show-hooks showed removed hook: $shown"
|
||||
fi
|
||||
$TMUX set -g @created 0 || fail "reset @created failed"
|
||||
$TMUX new -d -s four || fail "new-session four failed"
|
||||
assert_unchanged @created 0
|
||||
|
||||
# An unknown hook name is rejected.
|
||||
if $TMUX set-hook -g no-such-hook 'display x' 2>/dev/null; then
|
||||
fail "unknown hook name was accepted"
|
||||
fi
|
||||
|
||||
# A session hook only fires for events in that session.
|
||||
$TMUX set -g @renamed 0 || fail "set @renamed failed"
|
||||
$TMUX set-hook -t one window-renamed \
|
||||
'set -gF @renamed "#{hook}:#{hook_window_name}"' ||
|
||||
fail "set-hook -t one window-renamed failed"
|
||||
$TMUX rename-window -t two:0 twoname || fail "rename-window two failed"
|
||||
assert_unchanged @renamed 0
|
||||
$TMUX rename-window -t one:0 onename || fail "rename-window one failed"
|
||||
wait_for @renamed 'window-renamed:onename'
|
||||
$TMUX set-hook -u -t one window-renamed || fail "set-hook -u failed"
|
||||
|
||||
# A pane hook only fires for events in that pane.
|
||||
pane2=$($TMUX splitw -d -t one:0 -P -F '#{pane_id}') ||
|
||||
fail "split-window failed"
|
||||
$TMUX set -g @mode 0 || fail "set @mode failed"
|
||||
$TMUX set-hook -p -t "$pane2" pane-mode-changed \
|
||||
'set -gF @mode "#{hook}:#{hook_pane}"' ||
|
||||
fail "set-hook -p pane-mode-changed failed"
|
||||
$TMUX copy-mode -t one:0.0 || fail "copy-mode pane 0 failed"
|
||||
assert_unchanged @mode 0
|
||||
$TMUX copy-mode -t "$pane2" || fail "copy-mode pane 1 failed"
|
||||
wait_for @mode "pane-mode-changed:$pane2"
|
||||
$TMUX send-keys -t "$pane2" -X cancel || fail "cancel failed"
|
||||
$TMUX set-hook -pu -t "$pane2" pane-mode-changed ||
|
||||
fail "set-hook -pu failed"
|
||||
|
||||
# A window hook only fires for events in that window.
|
||||
$TMUX set -g @wmode 0 || fail "set @wmode failed"
|
||||
$TMUX set-hook -w -t two:0 pane-mode-changed \
|
||||
'set -gF @wmode "#{hook}:#{hook_window_name}"' ||
|
||||
fail "set-hook -w pane-mode-changed failed"
|
||||
$TMUX copy-mode -t three:0.0 || fail "copy-mode three failed"
|
||||
assert_unchanged @wmode 0
|
||||
$TMUX copy-mode -t two:0.0 || fail "copy-mode two failed"
|
||||
wait_for @wmode 'pane-mode-changed:twoname'
|
||||
$TMUX set-hook -wu -t two:0 pane-mode-changed ||
|
||||
fail "set-hook -wu failed"
|
||||
|
||||
# Commands run from a hook do not fire hooks again.
|
||||
$TMUX set -g @renames '' || fail "set @renames failed"
|
||||
$TMUX set-hook -g window-renamed \
|
||||
'set -gF @renames "#{@renames}x" ; rename-window -t three:0 inner' ||
|
||||
fail "set-hook -g window-renamed failed"
|
||||
$TMUX rename-window -t three:0 outer || fail "rename-window outer failed"
|
||||
wait_for @renames 'x'
|
||||
assert_unchanged @renames 'x'
|
||||
name=$($TMUX display -pt three:0 '#{window_name}') ||
|
||||
fail "display window_name failed"
|
||||
[ "$name" = inner ] || fail "expected window name inner but got $name"
|
||||
|
||||
exit 0
|
||||
@@ -7,7 +7,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -7,7 +7,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -7,7 +7,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -2,7 +2,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
TMP=$(mktemp)
|
||||
EXP=$(mktemp)
|
||||
|
||||
@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
sleep 1
|
||||
$TMUX -f/dev/null new -x20 -y2 -d \; set -g escape-time 0 || exit 1
|
||||
|
||||
494
regress/input-reflow-stress.sh
Normal file
494
regress/input-reflow-stress.sh
Normal file
@@ -0,0 +1,494 @@
|
||||
#!/bin/sh
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LANG=C.UTF-8
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LANG 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 "${TMPDIR:-/tmp}/input-reflow-stress.XXXXXX") || exit 1
|
||||
EXP=$(mktemp "${TMPDIR:-/tmp}/input-reflow-stress.XXXXXX") || exit 1
|
||||
exit_status=0
|
||||
|
||||
WIDTHS="80 40 20 10 7 5 4 3 2 1 2 3 4 5 7 10 20 40 80"
|
||||
LIVE_WIDTHS="68 24 80"
|
||||
HEIGHT=24
|
||||
HISTORY_LIMIT=220
|
||||
HISTORY_BOUND=12000
|
||||
JOINED_BOUND=500000
|
||||
RAW_BOUND=5000000
|
||||
COPY_BOUND=20000
|
||||
LIVE_BOUND=50000
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -f "$TMP" "$EXP"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
record_fail()
|
||||
{
|
||||
echo "FAIL: $1" >&2
|
||||
exit_status=1
|
||||
}
|
||||
|
||||
u8()
|
||||
{
|
||||
printf "$1"
|
||||
}
|
||||
|
||||
wide_a()
|
||||
{
|
||||
u8 '\0343\0201\0202'
|
||||
}
|
||||
|
||||
wide_b()
|
||||
{
|
||||
u8 '\0347\0225\0214'
|
||||
}
|
||||
|
||||
combining_acute()
|
||||
{
|
||||
u8 '\0314\0201'
|
||||
}
|
||||
|
||||
zero_width_space()
|
||||
{
|
||||
u8 '\0342\0200\0213'
|
||||
}
|
||||
|
||||
zero_width_joiner()
|
||||
{
|
||||
u8 '\0342\0200\0215'
|
||||
}
|
||||
|
||||
replacement()
|
||||
{
|
||||
u8 '\0357\0277\0275'
|
||||
}
|
||||
|
||||
assert_alive()
|
||||
{
|
||||
$TMUX display-message -p -t stress: alive >/dev/null 2>&1 ||
|
||||
record_fail "server exited after $1"
|
||||
}
|
||||
|
||||
make_orphan_payload()
|
||||
{
|
||||
tag=$1
|
||||
|
||||
printf 'OP%s-01|ECH-half|left' "$tag"
|
||||
wide_b
|
||||
printf 'right'
|
||||
printf '\033[6D\033[1X\n'
|
||||
|
||||
printf 'OP%s-02|EL-inside|left' "$tag"
|
||||
wide_b
|
||||
printf 'right'
|
||||
printf '\033[6D\033[K\n'
|
||||
|
||||
printf 'OP%s-03|overwrite-leading|left' "$tag"
|
||||
wide_b
|
||||
printf 'right'
|
||||
printf '\rOP%s-03|overwrite-leading|left!\n' "$tag"
|
||||
|
||||
printf 'OP%s-04|right-edge|' "$tag"
|
||||
i=0
|
||||
while [ "$i" -lt 60 ]; do
|
||||
printf '.'
|
||||
i=$((i + 1))
|
||||
done
|
||||
wide_b
|
||||
printf 'Z'
|
||||
printf '\033[2D\033[1X\n'
|
||||
|
||||
printf 'OP%s-05|ERASED|left' "$tag"
|
||||
wide_b
|
||||
printf 'right\r\033[2KOP%s-05|ERASED|left clear\n' "$tag"
|
||||
|
||||
printf 'SENT-%s|ORPHAN-PHASE|complete\n' "$tag"
|
||||
}
|
||||
|
||||
make_payload()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 16 ]; do
|
||||
printf 'ASCII%02d|abcdefghijklmnopqrstuvwxyz\n' "$i"
|
||||
|
||||
printf 'WIDE%02d|' "$i"
|
||||
wide_a
|
||||
printf '|'
|
||||
wide_b
|
||||
printf '|tail\n'
|
||||
|
||||
printf 'COMB%02d|e' "$i"
|
||||
combining_acute
|
||||
printf '|zero'
|
||||
zero_width_space
|
||||
zero_width_joiner
|
||||
printf '|done\n'
|
||||
|
||||
printf 'STYLE%02d|\033[1;31mred\033[0m|\033[4munder\033[0m\n' \
|
||||
"$i"
|
||||
|
||||
printf 'LINK%02d|\033]8;;https://example.invalid/%d\007link%d\033]8;;\007|end\n' \
|
||||
"$i" "$i" "$i"
|
||||
|
||||
printf 'WRAP%02d|%064d\n' "$i" "$i"
|
||||
|
||||
printf 'CRBS%02d|abcdef\rCRBS%02d|XYZ\n' "$i" "$i"
|
||||
printf 'BS%02d|abc\bZ\n' "$i"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 6 ]; do
|
||||
printf 'L%04d|stable|abcdefghijklmnopqrstuvwxyz\n' "$i"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
printf 'SENT-A|SURVIVES|plain logical line\n'
|
||||
printf 'SENT-B|SURVIVES|wide-free after erases\n'
|
||||
make_orphan_payload PRE
|
||||
printf 'SENT-C|SURVIVES|tail logical line\n'
|
||||
}
|
||||
|
||||
make_alternate_payload()
|
||||
{
|
||||
printf '\033[?1049h'
|
||||
printf 'ALT-SENT|alternate screen|'
|
||||
wide_a
|
||||
printf '\nALT-SENT|resize target\n'
|
||||
}
|
||||
|
||||
exit_alternate_payload()
|
||||
{
|
||||
printf '\033[?1049l'
|
||||
}
|
||||
|
||||
load_and_paste()
|
||||
{
|
||||
buffer=$1
|
||||
shift
|
||||
|
||||
"$@" >"$EXP"
|
||||
$TMUX load-buffer -b "$buffer" "$EXP" || exit 1
|
||||
$TMUX paste-buffer -d -b "$buffer" -t stress:0.0 || exit 1
|
||||
sleep 0.3
|
||||
}
|
||||
|
||||
capture_joined()
|
||||
{
|
||||
$TMUX capture-pane -pNJ -t stress: -S -5000 -E - >"$TMP"
|
||||
}
|
||||
|
||||
assert_joined_sane()
|
||||
{
|
||||
label=$1
|
||||
|
||||
capture_joined || {
|
||||
record_fail "joined capture failed after $label"
|
||||
return
|
||||
}
|
||||
|
||||
bytes=$(wc -c <"$TMP")
|
||||
if [ "$bytes" -gt "$JOINED_BOUND" ]; then
|
||||
record_fail "joined capture too large after $label: $bytes"
|
||||
fi
|
||||
|
||||
if grep -q "$(replacement)" "$TMP"; then
|
||||
record_fail "replacement character in joined capture after $label"
|
||||
fi
|
||||
|
||||
out=$(
|
||||
awk -v label="$label" '
|
||||
{
|
||||
line = $0
|
||||
seen_on_line = 0
|
||||
while (match(line, /L[0-9][0-9][0-9][0-9]\|/)) {
|
||||
id = substr(line, RSTART + 1, 4) + 0
|
||||
seen_on_line++
|
||||
seen[id]++
|
||||
ids[++count] = id
|
||||
found = 1
|
||||
last = id
|
||||
line = substr(line, RSTART + RLENGTH)
|
||||
}
|
||||
if (seen_on_line > 1)
|
||||
printf("fused IDs after %s: %s\n", label, $0)
|
||||
}
|
||||
END {
|
||||
if (!found) {
|
||||
printf("no line IDs after %s\n", label)
|
||||
exit
|
||||
}
|
||||
for (i = 2; i <= count; i++) {
|
||||
if (ids[i] < ids[i - 1])
|
||||
drops++
|
||||
}
|
||||
if (drops > 1)
|
||||
printf("IDs out of order after %s\n", label)
|
||||
for (id in seen) {
|
||||
if (seen[id] > 2)
|
||||
printf("duplicate ID after %s: L%04d\n", label, id)
|
||||
}
|
||||
}' "$TMP"
|
||||
)
|
||||
[ -z "$out" ] || record_fail "$out"
|
||||
|
||||
for marker in \
|
||||
'SENT-A|SURVIVES|plain logical line' \
|
||||
'SENT-B|SURVIVES|wide-free after erases' \
|
||||
'SENT-C|SURVIVES|tail logical line'
|
||||
do
|
||||
grep -F "$marker" "$TMP" >/dev/null 2>&1 ||
|
||||
record_fail "missing sentinel after $label: $marker"
|
||||
done
|
||||
|
||||
if grep '^OP.*-05|ERASED|.*' "$TMP" | grep -q "$(wide_b)"; then
|
||||
record_fail "erased wide character visible after $label"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_final_logical_text()
|
||||
{
|
||||
label=$1
|
||||
|
||||
capture_joined || {
|
||||
record_fail "joined capture failed after $label"
|
||||
return
|
||||
}
|
||||
|
||||
for marker in \
|
||||
'SENT-A|SURVIVES|plain logical line' \
|
||||
'SENT-B|SURVIVES|wide-free after erases' \
|
||||
'SENT-C|SURVIVES|tail logical line' \
|
||||
'OPPRE-03|overwrite-leading|left!' \
|
||||
'OPPOST-03|overwrite-leading|left!' \
|
||||
'SENT-POST|ORPHAN-PHASE|complete'
|
||||
do
|
||||
grep -F "$marker" "$TMP" >/dev/null 2>&1 ||
|
||||
record_fail "missing expected logical text after $label: $marker"
|
||||
done
|
||||
}
|
||||
|
||||
assert_raw_sane()
|
||||
{
|
||||
label=$1
|
||||
|
||||
$TMUX capture-pane -pR -t stress: >"$TMP" ||
|
||||
record_fail "raw capture failed after $label"
|
||||
|
||||
bytes=$(wc -c <"$TMP")
|
||||
if [ "$bytes" -gt "$RAW_BOUND" ]; then
|
||||
record_fail "raw capture too large after $label: $bytes"
|
||||
fi
|
||||
|
||||
width=$($TMUX display-message -p -t stress: '#{pane_width}' 2>/dev/null)
|
||||
case "$width" in
|
||||
''|*[!0-9]*) width=0 ;;
|
||||
esac
|
||||
|
||||
out=$(
|
||||
awk -v label="$label" -v width="$width" '
|
||||
{
|
||||
sub(/^[ ]+/, "")
|
||||
if ($1 != "C")
|
||||
next
|
||||
|
||||
coord = $2
|
||||
sub(/^[0-9]*,/, "", coord)
|
||||
sub(/[^0-9].*$/, "", coord)
|
||||
col = coord + 0
|
||||
if (width > 0 && (col < 0 || col >= width))
|
||||
printf("cell column outside width after %s: width %d: %s\n", label, width, $0)
|
||||
|
||||
data = $0
|
||||
if (data !~ /data=\(/) {
|
||||
printf("cell without data after %s: %s\n", label, $0)
|
||||
next
|
||||
}
|
||||
sub(/^.*data=\(/, "", data)
|
||||
split(data, parts, ",")
|
||||
cell_width = parts[1] + 0
|
||||
padding = ($0 ~ /flags=[^ ]*PADDING/)
|
||||
if (!padding && cell_width == 0)
|
||||
printf("visible zero-width cell after %s: %s\n", label, $0)
|
||||
}' "$TMP"
|
||||
)
|
||||
[ -z "$out" ] || record_fail "$out"
|
||||
}
|
||||
|
||||
assert_history_sane()
|
||||
{
|
||||
label=$1
|
||||
size=$($TMUX display-message -p -t stress: '#{history_size}' 2>/dev/null)
|
||||
case "$size" in
|
||||
''|*[!0-9]*)
|
||||
record_fail "bad history size after $label: $size"
|
||||
;;
|
||||
*)
|
||||
if [ "$size" -gt "$HISTORY_BOUND" ]; then
|
||||
record_fail "history too large after $label: $size"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
assert_copy_mode_sane()
|
||||
{
|
||||
label=$1
|
||||
|
||||
$TMUX capture-pane -pM -S -5000 -E - -t stress: >"$TMP" ||
|
||||
record_fail "copy-mode capture failed after $label"
|
||||
|
||||
bytes=$(wc -c <"$TMP")
|
||||
if [ "$bytes" -gt "$COPY_BOUND" ]; then
|
||||
record_fail "copy-mode capture too large after $label: $bytes"
|
||||
fi
|
||||
|
||||
if grep -q "$(replacement)" "$TMP"; then
|
||||
record_fail "replacement character in copy-mode after $label"
|
||||
fi
|
||||
|
||||
if ! grep -Eq 'L[0-9][0-9][0-9][0-9]\|' "$TMP"; then
|
||||
record_fail "no line ID in copy-mode after $label"
|
||||
fi
|
||||
}
|
||||
|
||||
run_resize_checks()
|
||||
{
|
||||
for width in $WIDTHS; do
|
||||
$TMUX resize-window -t stress: -x "$width" -y "$HEIGHT" || exit 1
|
||||
sleep 0.1
|
||||
assert_alive "resize to $width"
|
||||
assert_joined_sane "resize to $width"
|
||||
assert_raw_sane "resize to $width"
|
||||
assert_history_sane "resize to $width"
|
||||
done
|
||||
}
|
||||
|
||||
wait_outer_contains()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$TMUX2 capture-pane -p -t out:0 >"$TMP" 2>/dev/null &&
|
||||
grep -F "$marker" "$TMP" >/dev/null 2>&1 &&
|
||||
return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
assert_live_client_redraw()
|
||||
{
|
||||
$TMUX set-option -t stress: status on >/dev/null || exit 1
|
||||
$TMUX set-option -t stress: status-interval 1 >/dev/null || exit 1
|
||||
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
$TMUX2 new-session -d -s out -x 100 -y 12 "$TMUX attach -t stress" ||
|
||||
exit 1
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
clients=$($TMUX list-clients -F x 2>/dev/null | grep -c x)
|
||||
[ "$clients" -ge 1 ] && break
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ "$i" -lt 50 ] || {
|
||||
record_fail "nested client did not attach"
|
||||
return
|
||||
}
|
||||
|
||||
for width in $LIVE_WIDTHS; do
|
||||
$TMUX set-option -t stress: status-left "REDRAW-$width " >/dev/null ||
|
||||
exit 1
|
||||
$TMUX2 resize-window -t out: -x "$width" -y 12 || exit 1
|
||||
sleep 0.2
|
||||
done
|
||||
|
||||
wait_outer_contains 'REDRAW-80' ||
|
||||
record_fail "outer capture missing final redraw marker"
|
||||
|
||||
$TMUX2 capture-pane -p -t out:0 >"$TMP" 2>/dev/null ||
|
||||
record_fail "outer capture failed"
|
||||
|
||||
bytes=$(wc -c <"$TMP")
|
||||
if [ "$bytes" -gt "$LIVE_BOUND" ]; then
|
||||
record_fail "outer capture too large: $bytes"
|
||||
fi
|
||||
|
||||
if grep -q "$(replacement)" "$TMP"; then
|
||||
record_fail "replacement character in outer capture"
|
||||
fi
|
||||
|
||||
grep -F 'SENT-POST|ORPHAN-PHASE|complete' "$TMP" >/dev/null 2>&1 ||
|
||||
record_fail "outer capture missing expected sentinel"
|
||||
|
||||
grep -F 'REDRAW-24' "$TMP" >/dev/null 2>&1 &&
|
||||
record_fail "outer capture contains stale width marker"
|
||||
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
$TMUX set-option -t stress: status off >/dev/null || exit 1
|
||||
}
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
sleep 0.1
|
||||
|
||||
$TMUX new-session -d -x 1 -y 1 -s test-setup "sleep 2" || exit 1
|
||||
$TMUX set-option -g history-limit "$HISTORY_LIMIT" || exit 1
|
||||
$TMUX new-session -d -x 80 -y "$HEIGHT" -s stress 'stty -echo; exec cat' || exit 1
|
||||
$TMUX kill-session -t test-setup || exit 1
|
||||
sleep 0.3
|
||||
|
||||
load_and_paste stress-data make_payload
|
||||
assert_joined_sane "initial payload"
|
||||
assert_raw_sane "initial payload"
|
||||
assert_history_sane "initial payload"
|
||||
|
||||
$TMUX resize-window -t stress: -x 40 -y "$HEIGHT" || exit 1
|
||||
sleep 0.1
|
||||
load_and_paste stress-orphan-post make_orphan_payload POST
|
||||
assert_joined_sane "post-resize orphan payload"
|
||||
assert_raw_sane "post-resize orphan payload"
|
||||
|
||||
run_resize_checks
|
||||
assert_final_logical_text "return to original width"
|
||||
|
||||
$TMUX copy-mode -H -t stress: || exit 1
|
||||
$TMUX send-keys -t stress: -X history-top
|
||||
sleep 0.1
|
||||
assert_alive "copy-mode"
|
||||
assert_copy_mode_sane "copy-mode history-top"
|
||||
$TMUX send-keys -t stress: -X cancel
|
||||
sleep 0.1
|
||||
|
||||
assert_live_client_redraw
|
||||
|
||||
load_and_paste stress-alt make_alternate_payload
|
||||
for width in 30 12 80; do
|
||||
$TMUX resize-window -t stress: -x "$width" -y "$HEIGHT" || exit 1
|
||||
sleep 0.1
|
||||
assert_alive "alternate resize to $width"
|
||||
assert_raw_sane "alternate resize to $width"
|
||||
done
|
||||
load_and_paste stress-alt-exit exit_alternate_payload
|
||||
sleep 0.3
|
||||
assert_alive "alternate screen exit"
|
||||
assert_joined_sane "alternate screen exit"
|
||||
assert_raw_sane "alternate screen exit"
|
||||
assert_final_logical_text "alternate screen exit"
|
||||
|
||||
cleanup
|
||||
exit $exit_status
|
||||
@@ -4,10 +4,10 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
sleep 0.1
|
||||
sleep 0.5
|
||||
|
||||
TMP=$(mktemp)
|
||||
EXP=$(mktemp)
|
||||
@@ -18,7 +18,7 @@ $TMUX new-session -d -x 80 -y 24 -s replies \; \
|
||||
$TMUX set-option -s set-clipboard on || exit 1
|
||||
$TMUX set-option -s get-clipboard buffer || exit 1
|
||||
printf Hello | $TMUX load-buffer -
|
||||
sleep 0.3
|
||||
sleep 0.5
|
||||
|
||||
exit_status=0
|
||||
|
||||
@@ -53,7 +53,7 @@ query_timeout()
|
||||
|
||||
$TMUX respawn-window -k -t replies:0 \
|
||||
"stty raw -echo min 0 time 5; printf '$setup'; printf '$seq'; sleep 0.1; dd bs=1 count=128 2>/dev/null | cat -v >$TMP"
|
||||
sleep 0.7
|
||||
sleep 1
|
||||
printf "%s" "$expected" >"$EXP"
|
||||
cmp "$TMP" "$EXP" || fail "$name"
|
||||
}
|
||||
@@ -62,26 +62,26 @@ query "dsr-ok" '^[[0n' '\033[5n' 4 ''
|
||||
query "dsr-cursor" '^[[1;1R' '\033[6n' 6 ''
|
||||
query "da-primary" '^[[?1;2c' '\033[c' 7 ''
|
||||
query "da-secondary" '^[[>84;0;0c' '\033[>c' 10 ''
|
||||
query "decrqm-irm-reset" '^[[4;2$y' '\033[4$p' 8 ''
|
||||
query "decrqm-irm-set" '^[[4;1$y' '\033[4$p' 8 '\033[4h'
|
||||
query "decrqm-cursor-keys-reset" '^[[?1;2$y' '\033[?1$p' 9 ''
|
||||
query "decrqm-cursor-keys-set" '^[[?1;1$y' '\033[?1$p' 9 '\033[?1h'
|
||||
query "decrqm-columns" '^[[?3;4$y' '\033[?3$p' 9 ''
|
||||
query "decrqm-origin-reset" '^[[?6;2$y' '\033[?6$p' 9 ''
|
||||
query "decrqm-origin-set" '^[[?6;1$y' '\033[?6$p' 9 '\033[?6h'
|
||||
query "decrqm-wrap-set" '^[[?7;1$y' '\033[?7$p' 9 ''
|
||||
query "decrqm-wrap-reset" '^[[?7;2$y' '\033[?7$p' 9 '\033[?7l'
|
||||
query "decrqm-cursor-visible-set" '^[[?25;1$y' '\033[?25$p' 10 ''
|
||||
query "decrqm-cursor-visible-reset" '^[[?25;2$y' '\033[?25$p' 10 '\033[?25l'
|
||||
query "decrqm-mouse-standard-set" '^[[?1000;1$y' '\033[?1000$p' 12 '\033[?1000h'
|
||||
query "decrqm-mouse-button-set" '^[[?1002;1$y' '\033[?1002$p' 12 '\033[?1002h'
|
||||
query "decrqm-mouse-all-set" '^[[?1003;1$y' '\033[?1003$p' 12 '\033[?1003h'
|
||||
query "decrqm-focus-set" '^[[?1004;1$y' '\033[?1004$p' 12 '\033[?1004h'
|
||||
query "decrqm-mouse-utf8-set" '^[[?1005;1$y' '\033[?1005$p' 12 '\033[?1005h'
|
||||
query "decrqm-mouse-sgr-set" '^[[?1006;1$y' '\033[?1006$p' 12 '\033[?1006h'
|
||||
query "decrqm-bracket-paste-set" '^[[?2004;1$y' '\033[?2004$p' 12 '\033[?2004h'
|
||||
query "decrqm-theme-updates-set" '^[[?2031;1$y' '\033[?2031$p' 12 '\033[?2031h'
|
||||
query "decrqss-cursor-style" '^[P1$r q0 q' '\033P$q q\033\\' 10 ''
|
||||
query "decrqm-irm-reset" '^[[4;2$y' '\033[4$p' 7 ''
|
||||
query "decrqm-irm-set" '^[[4;1$y' '\033[4$p' 7 '\033[4h'
|
||||
query "decrqm-cursor-keys-reset" '^[[?1;2$y' '\033[?1$p' 8 ''
|
||||
query "decrqm-cursor-keys-set" '^[[?1;1$y' '\033[?1$p' 8 '\033[?1h'
|
||||
query "decrqm-columns" '^[[?3;4$y' '\033[?3$p' 8 ''
|
||||
query "decrqm-origin-reset" '^[[?6;2$y' '\033[?6$p' 8 ''
|
||||
query "decrqm-origin-set" '^[[?6;1$y' '\033[?6$p' 8 '\033[?6h'
|
||||
query "decrqm-wrap-set" '^[[?7;1$y' '\033[?7$p' 8 ''
|
||||
query "decrqm-wrap-reset" '^[[?7;2$y' '\033[?7$p' 8 '\033[?7l'
|
||||
query "decrqm-cursor-visible-set" '^[[?25;1$y' '\033[?25$p' 9 ''
|
||||
query "decrqm-cursor-visible-reset" '^[[?25;2$y' '\033[?25$p' 9 '\033[?25l'
|
||||
query "decrqm-mouse-standard-set" '^[[?1000;1$y' '\033[?1000$p' 11 '\033[?1000h'
|
||||
query "decrqm-mouse-button-set" '^[[?1002;1$y' '\033[?1002$p' 11 '\033[?1002h'
|
||||
query "decrqm-mouse-all-set" '^[[?1003;1$y' '\033[?1003$p' 11 '\033[?1003h'
|
||||
query "decrqm-focus-set" '^[[?1004;1$y' '\033[?1004$p' 11 '\033[?1004h'
|
||||
query "decrqm-mouse-utf8-set" '^[[?1005;1$y' '\033[?1005$p' 11 '\033[?1005h'
|
||||
query "decrqm-mouse-sgr-set" '^[[?1006;1$y' '\033[?1006$p' 11 '\033[?1006h'
|
||||
query "decrqm-bracket-paste-set" '^[[?2004;1$y' '\033[?2004$p' 11 '\033[?2004h'
|
||||
query "decrqm-theme-updates-set" '^[[?2031;1$y' '\033[?2031$p' 11 '\033[?2031h'
|
||||
query "decrqss-cursor-style" '^[P1$r q0 q^[\' '\033P$q q\033\\' 12 ''
|
||||
|
||||
query_timeout "osc-10-query" '^[]10;rgb:ffff/0000/0000^G' '\033]10;?\007' '\033]10;red\007'
|
||||
query_timeout "osc-11-query" '^[]11;rgb:0000/0000/ffff^G' '\033]11;?\007' '\033]11;blue\007'
|
||||
|
||||
@@ -15,7 +15,8 @@ import tempfile
|
||||
import time
|
||||
|
||||
tmux = sys.argv[1]
|
||||
server = [tmux, "-Ltest", "-f/dev/null"]
|
||||
label = "testA%d" % os.getpid()
|
||||
server = [tmux, "-L" + label, "-f/dev/null"]
|
||||
|
||||
def run(*args, check=True):
|
||||
return subprocess.run(server + list(args), check=check,
|
||||
@@ -25,7 +26,7 @@ def attach():
|
||||
pid, fd = os.forkpty()
|
||||
if pid == 0:
|
||||
os.environ["TERM"] = "xterm-256color"
|
||||
os.execl(tmux, tmux, "-Ltest", "-f/dev/null", "attach-session",
|
||||
os.execl(tmux, tmux, "-L" + label, "-f/dev/null", "attach-session",
|
||||
"-t", "requests")
|
||||
os.set_blocking(fd, False)
|
||||
return pid, fd
|
||||
@@ -85,7 +86,7 @@ try:
|
||||
palette_out = f.name
|
||||
respawn("stty raw -echo min 1 time 50; "
|
||||
"printf '\\033]4;99;?\\033\\\\'; "
|
||||
"dd bs=1 count=64 2>/dev/null | cat -v >%s; sleep 1" %
|
||||
"dd bs=1 count=27 2>/dev/null | cat -v >%s; sleep 1" %
|
||||
palette_out)
|
||||
read_until(fd, b"\033]4;99;?\033\\")
|
||||
os.write(fd, b"\033]4;99;rgb:0101/0202/0303\033\\")
|
||||
@@ -101,7 +102,7 @@ try:
|
||||
clip_out = f.name
|
||||
respawn("stty raw -echo min 1 time 50; "
|
||||
"printf '\\033]52;c;?\\033\\\\'; "
|
||||
"dd bs=1 count=64 2>/dev/null | cat -v >%s; sleep 1" %
|
||||
"dd bs=1 count=21 2>/dev/null | cat -v >%s; sleep 1" %
|
||||
clip_out)
|
||||
data = read_until(fd, b"]52;")
|
||||
if b"?" not in data:
|
||||
|
||||
@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
sleep 1
|
||||
|
||||
|
||||
216
regress/lifecycle-deferred.sh
Normal file
216
regress/lifecycle-deferred.sh
Normal file
@@ -0,0 +1,216 @@
|
||||
#!/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"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
TMPDIR=$(mktemp -d)
|
||||
IN="$TMPDIR/in"
|
||||
OUT="$TMPDIR/out"
|
||||
CONTROL_PID=
|
||||
|
||||
cleanup()
|
||||
{
|
||||
exec 3>&- 2>/dev/null
|
||||
[ -n "$CONTROL_PID" ] && kill "$CONTROL_PID" 2>/dev/null
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
rm -rf "$TMPDIR"
|
||||
}
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$1" >&2
|
||||
[ -s "$OUT" ] && sed -n '1,120p' "$OUT" >&2
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
|
||||
run_tmux()
|
||||
{
|
||||
out=
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
out=$(timeout 10 $TMUX "$@" 2>&1)
|
||||
else
|
||||
out=$($TMUX "$@" 2>&1)
|
||||
fi
|
||||
rc=$?
|
||||
[ "$rc" -eq 0 ] || fail "tmux $* failed ($rc): $out"
|
||||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
send_control()
|
||||
{
|
||||
printf '%s\n' "$1" >&3 || fail "failed to write control command: $1"
|
||||
}
|
||||
|
||||
wait_clients()
|
||||
{
|
||||
want=$1
|
||||
i=0
|
||||
|
||||
while [ "$i" -lt 50 ]; do
|
||||
have=$($TMUX list-clients -F x 2>/dev/null | grep -c x)
|
||||
[ "$have" -eq "$want" ] && return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_format()
|
||||
{
|
||||
target=$1
|
||||
format=$2
|
||||
want=$3
|
||||
i=0
|
||||
|
||||
while [ "$i" -lt 50 ]; do
|
||||
have=$($TMUX display-message -p -t "$target" "$format" 2>/dev/null)
|
||||
[ "$have" = "$want" ] && return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
assert_alive()
|
||||
{
|
||||
run_tmux has-session -t life >/dev/null
|
||||
fields=$(run_tmux display-message -p -t life \
|
||||
'#{session_name}:#{window_id}:#{pane_id}:#{session_windows}:#{window_panes}')
|
||||
case "$fields" in
|
||||
life:@*:%*:*) ;;
|
||||
*) fail "bad current fields after $1: $fields" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
check_control_output()
|
||||
{
|
||||
sleep 1
|
||||
|
||||
if grep -E '(^%error |server exited|lost server|\(null\)|no current)' \
|
||||
"$OUT" >/dev/null 2>&1; then
|
||||
fail "control client reported an error or invalid object"
|
||||
fi
|
||||
|
||||
awk '
|
||||
$1 == "%session-window-changed" {
|
||||
if (NF != 3 || $2 !~ /^\$[0-9]+$/ || $3 !~ /^@[0-9]+$/)
|
||||
bad = 1
|
||||
}
|
||||
$1 == "%subscription-changed" {
|
||||
colon = 0
|
||||
for (i = 2; i <= NF; i++)
|
||||
if ($i == ":")
|
||||
colon = 1
|
||||
if (NF < 7 || !colon)
|
||||
bad = 1
|
||||
}
|
||||
END { exit bad }
|
||||
' "$OUT" || fail "control client received a malformed notification"
|
||||
}
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
run_tmux new-session -d -s life -n prompt -x 80 -y 24 'sleep 1000' \
|
||||
>/dev/null
|
||||
run_tmux set-option -g detach-on-destroy off >/dev/null
|
||||
run_tmux new-window -t life -n tree 'sleep 1000' >/dev/null
|
||||
run_tmux new-window -t life -n work 'sleep 1000' >/dev/null
|
||||
run_tmux select-window -t life:prompt >/dev/null
|
||||
|
||||
run_tmux set-hook -g after-new-window \
|
||||
'display-message -p "hook-new #{session_name}:#{window_id}:#{pane_id}"' \
|
||||
>/dev/null
|
||||
run_tmux set-hook -g after-split-window \
|
||||
'display-message -p "hook-split #{session_name}:#{window_id}:#{pane_id}"' \
|
||||
>/dev/null
|
||||
run_tmux set-hook -g after-kill-pane \
|
||||
'display-message -p "hook-kill #{session_name}:#{window_id}:#{pane_id}"' \
|
||||
>/dev/null
|
||||
run_tmux set-hook -g pane-exited \
|
||||
'display-message -p "hook-exit #{session_name}:#{window_id}:#{pane_id}"' \
|
||||
>/dev/null
|
||||
run_tmux set-hook -g window-layout-changed \
|
||||
'display-message -p "hook-layout #{session_name}:#{window_id}:#{pane_id}"' \
|
||||
>/dev/null
|
||||
run_tmux set-hook -g session-window-changed \
|
||||
'display-message -p "hook-current #{session_name}:#{window_id}:#{pane_id}"' \
|
||||
>/dev/null
|
||||
run_tmux bind-key -n M-p command-prompt -P -p '(life)' \
|
||||
"set -g @lifecycle-prompt '%% #{session_name}:#{window_id}:#{pane_id}'" \
|
||||
>/dev/null
|
||||
|
||||
$TMUX2 new-session -d -s outer -n prompt -x 80 -y 24 "$TMUX attach -t life" \
|
||||
|| fail "failed to start first attached client"
|
||||
$TMUX2 new-window -t outer -n tree "$TMUX attach -t life" \
|
||||
|| fail "failed to start second attached client"
|
||||
wait_clients 2 || fail "normal clients did not attach"
|
||||
|
||||
$TMUX2 send-keys -t outer:prompt M-p || fail "failed to open command prompt"
|
||||
sleep 1
|
||||
run_tmux choose-tree -t life:tree.0 \
|
||||
-F 'tree #{session_name}:#{window_id}:#{pane_id}' >/dev/null
|
||||
wait_format life:tree.0 '#{pane_mode}' tree-mode || \
|
||||
fail "choose-tree did not enter tree-mode"
|
||||
|
||||
mkfifo "$IN" || fail "failed to create control fifo"
|
||||
(cat "$IN" | $TMUX -C attach -t life >"$OUT" 2>&1) &
|
||||
CONTROL_PID=$!
|
||||
exec 3>"$IN"
|
||||
wait_clients 3 || fail "control client did not attach"
|
||||
|
||||
CONTROL_CLIENT=$($TMUX list-clients -F '#{client_name} #{client_control_mode}' |
|
||||
awk '$2 == 1 { print $1; exit }')
|
||||
[ -n "$CONTROL_CLIENT" ] || fail "missing control client"
|
||||
|
||||
send_control "refresh-client -B 'all:%*:#{session_name}:#{window_id}:#{pane_id}:#{session_windows}:#{window_panes}'"
|
||||
send_control "refresh-client -B 'windows:@*:#{session_name}:#{window_id}:#{window_index}:#{window_panes}'"
|
||||
sleep 1
|
||||
|
||||
run_tmux kill-pane -t life:prompt.0 >/dev/null
|
||||
run_tmux kill-window -t life:tree >/dev/null
|
||||
assert_alive "killing prompt and tree panes"
|
||||
|
||||
i=1
|
||||
while [ "$i" -le 20 ]; do
|
||||
# Keep this sequence fixed: failures should reproduce on the same pass.
|
||||
s=ld$i
|
||||
ctl=ctl$i
|
||||
idx=$((50 + i))
|
||||
|
||||
run_tmux new-session -d -s "$s" -n base 'sleep 1000' >/dev/null
|
||||
run_tmux split-window -t "$s:base" 'sleep 1000' >/dev/null
|
||||
run_tmux respawn-pane -k -t "$s:base.1" 'sleep 1000' >/dev/null
|
||||
run_tmux kill-pane -t "$s:base.1" >/dev/null
|
||||
|
||||
run_tmux new-window -t "$s" -n second 'sleep 1000' >/dev/null
|
||||
run_tmux link-window -s "$s:second" -t "life:$idx" >/dev/null
|
||||
run_tmux unlink-window -t "life:$idx" >/dev/null
|
||||
|
||||
run_tmux new-window -t "$s" -n single 'sleep 1000' >/dev/null
|
||||
run_tmux kill-pane -t "$s:single.0" >/dev/null
|
||||
run_tmux kill-window -t "$s:base" >/dev/null
|
||||
|
||||
run_tmux new-session -d -s "$ctl" -n ctl 'sleep 1000' >/dev/null
|
||||
run_tmux switch-client -c "$CONTROL_CLIENT" -t "$ctl" >/dev/null
|
||||
run_tmux kill-session -t "$ctl" >/dev/null
|
||||
|
||||
run_tmux kill-session -t "$s" >/dev/null
|
||||
assert_alive "iteration $i"
|
||||
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
check_control_output
|
||||
cleanup
|
||||
exit 0
|
||||
171
regress/mode-kill.sh
Executable file
171
regress/mode-kill.sh
Executable file
@@ -0,0 +1,171 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Tests of the -k flag on the mode-entering commands (cmd-copy-mode.c and
|
||||
# cmd-choose-tree.c). With -k the pane is killed when the mode is exited: this
|
||||
# is stored on the mode entry in window_pane_set_mode() and acted on in
|
||||
# window_pane_reset_mode() (window.c). It is exercised here for:
|
||||
#
|
||||
# - copy-mode -k (window-copy.c);
|
||||
# - choose-tree -k (window-tree.c);
|
||||
# - choose-buffer -k (window-buffer.c).
|
||||
#
|
||||
# choose-tree and choose-buffer share cmd_choose_tree_exec(), which also backs
|
||||
# choose-client and customize-mode, so those are not repeated.
|
||||
#
|
||||
# Each mode is entered in the active pane of a two-pane window: exiting with
|
||||
# -k must remove that pane and leave the other. copy-mode is left with the
|
||||
# server-side "-X cancel" and needs no client, so those tests run first. The
|
||||
# tree modes only act on a key once the client has drawn the mode, so - as in
|
||||
# choose-tree.sh - a second server then provides a client (an inner "tmux
|
||||
# attach") and its pane is captured to wait until the mode has rendered before
|
||||
# the exit key is sent.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMP=$(mktemp -d) || exit 1
|
||||
TMUX_TMPDIR="$TMP"
|
||||
export TMUX_TMPDIR
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
rm -rf "$TMP"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# capture the screen rendered by the inner client.
|
||||
capture()
|
||||
{
|
||||
$TMUX2 capture-pane -p -t out:0 2>/dev/null
|
||||
}
|
||||
|
||||
# wait_clients $n: wait until the test server has exactly $n clients.
|
||||
wait_clients()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
c=$($TMUX list-clients -F x 2>/dev/null | grep -c x)
|
||||
[ "$c" -eq "$1" ] && return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "expected $1 clients, have $c"
|
||||
}
|
||||
|
||||
# wait_mode $target $state: wait until a pane enters (1) or leaves (0) mode.
|
||||
wait_mode()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
got=$($TMUX display-message -p -t "$1" '#{pane_in_mode}' \
|
||||
2>/dev/null)
|
||||
[ "$got" = "$2" ] && return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "pane $1 mode state is '$got', expected '$2'"
|
||||
}
|
||||
|
||||
# wait_for $marker: wait until the rendered screen contains $marker.
|
||||
wait_for()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
capture | grep -F -q "$1" && return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "timed out waiting for '$1' to render"
|
||||
}
|
||||
|
||||
# pane_gone $pane-id: true if the pane no longer exists. (display-message -t
|
||||
# on a missing pane id falls back to a default target and succeeds, so the
|
||||
# pane list is searched instead.)
|
||||
pane_gone()
|
||||
{
|
||||
! $TMUX list-panes -s -t m -F '#{pane_id}' 2>/dev/null | \
|
||||
grep -q -x "$1"
|
||||
}
|
||||
|
||||
# open_window: make a fresh two-pane window and leave the new (active) pane
|
||||
# as $active and the other as $other.
|
||||
open_window()
|
||||
{
|
||||
$TMUX new-window -t m: -n w 'cat' || fail "new-window failed"
|
||||
$TMUX split-window -t m:w 'cat' || fail "split-window failed"
|
||||
other=$($TMUX display-message -p -t m:w.0 '#{pane_id}')
|
||||
active=$($TMUX display-message -p -t m:w '#{pane_id}')
|
||||
}
|
||||
|
||||
# check_killed $label: wait for the active pane to be killed, leaving only the
|
||||
# other pane, then drop the window.
|
||||
check_killed()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
pane_gone "$active" && break
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
pane_gone "$active" || fail "$1: pane not killed on exit"
|
||||
panes=$($TMUX list-panes -t m:w -F '#{pane_id}' | tr '\n' ' ')
|
||||
[ "$panes" = "$other " ] || \
|
||||
fail "$1: expected only $other left, have $panes"
|
||||
$TMUX kill-window -t m:w 2>/dev/null
|
||||
}
|
||||
|
||||
# Session m; window 0 keeps a live pane so the session (and later the client)
|
||||
# survives each test killing a pane.
|
||||
$TMUX new-session -d -s m -x 80 -y 24 'cat' || exit 1
|
||||
|
||||
# --- copy-mode -k kills the pane, plain copy-mode does not -------------------
|
||||
#
|
||||
# copy-mode is exited with the server-side "-X cancel", so no client is needed
|
||||
# and none is attached yet.
|
||||
open_window
|
||||
$TMUX copy-mode -k -t m:w || fail "copy-mode -k failed"
|
||||
wait_mode "$active" 1
|
||||
$TMUX send-keys -t m:w -X cancel || fail "copy cancel failed"
|
||||
check_killed 'copy-mode -k'
|
||||
|
||||
open_window
|
||||
$TMUX copy-mode -t m:w || fail "copy-mode failed"
|
||||
wait_mode "$active" 1
|
||||
$TMUX send-keys -t m:w -X cancel || fail "copy cancel failed"
|
||||
wait_mode "$active" 0
|
||||
pane_gone "$active" && fail 'copy-mode: pane killed without -k'
|
||||
$TMUX kill-window -t m:w 2>/dev/null
|
||||
|
||||
# --- choose-tree -k and choose-buffer -k kill the pane ----------------------
|
||||
#
|
||||
# These need the client to draw the mode before a key acts, so attach one now.
|
||||
# A paste buffer is needed for choose-buffer to have something to show, and a
|
||||
# distinct -F marker per call is waited for in the capture so the exit key is
|
||||
# only sent once the mode is drawn.
|
||||
$TMUX set-buffer 'mode-kill buffer' || exit 1
|
||||
$TMUX2 new-session -d -s out -x 80 -y 24 "$TMUX attach -t m" || exit 1
|
||||
wait_clients 1
|
||||
open_window
|
||||
$TMUX choose-tree -k -F 'TREEMARK' -t m:w || fail "choose-tree -k failed"
|
||||
wait_for 'TREEMARK'
|
||||
$TMUX send-keys -t m:w q || fail "choose-tree exit failed"
|
||||
check_killed 'choose-tree -k'
|
||||
|
||||
open_window
|
||||
$TMUX choose-buffer -k -F 'BUFMARK' -t m:w || fail "choose-buffer -k failed"
|
||||
wait_for 'BUFMARK'
|
||||
$TMUX send-keys -t m:w q || fail "choose-buffer exit failed"
|
||||
check_killed 'choose-buffer -k'
|
||||
|
||||
exit 0
|
||||
307
regress/mode-mutation.sh
Normal file
307
regress/mode-mutation.sh
Normal file
@@ -0,0 +1,307 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Exercise modes while their backing objects are changed from outside the
|
||||
# client displaying the mode. This catches stale selection indexes and pointers
|
||||
# after a mode list is shrunk or rebuilt.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMP=$(mktemp -d) || exit 1
|
||||
TMUX_TMPDIR="$TMP"
|
||||
export TMUX_TMPDIR
|
||||
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
cleanup_servers()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
sleep 0.5
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
cleanup_servers
|
||||
rm -rf "$TMP"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$1" >&2
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
|
||||
capture()
|
||||
{
|
||||
$TMUX2 capture-pane -p -t out:0 2>/dev/null
|
||||
}
|
||||
|
||||
assert_alive()
|
||||
{
|
||||
$TMUX display-message -p 'alive' >/dev/null 2>&1 || \
|
||||
fail "$1: server exited"
|
||||
}
|
||||
|
||||
wait_clients()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
c=$($TMUX list-clients -F x 2>/dev/null | grep -c x)
|
||||
[ "$c" -eq "$1" ] && return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "expected $1 clients, have $c"
|
||||
}
|
||||
|
||||
wait_for()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
capture | grep -F -q "$1" && return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "timed out waiting for '$1'"
|
||||
}
|
||||
|
||||
wait_mode()
|
||||
{
|
||||
t=$1
|
||||
want=$2
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
got=$($TMUX display-message -p -t "$t" '#{pane_in_mode}' \
|
||||
2>/dev/null)
|
||||
[ "$got" = "$want" ] && return 0
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "pane $t mode state is $got, expected $want"
|
||||
}
|
||||
|
||||
repeat_key()
|
||||
{
|
||||
t=$1
|
||||
key=$2
|
||||
count=$3
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt "$count" ]; do
|
||||
$TMUX send-keys -t "$t" "$key" || \
|
||||
fail "failed to send $key to $t"
|
||||
i=$((i + 1))
|
||||
done
|
||||
}
|
||||
|
||||
start_client()
|
||||
{
|
||||
s=$1
|
||||
cmd=${2:-cat}
|
||||
|
||||
cleanup_servers
|
||||
$TMUX new-session -d -s "$s" -n main -x 80 -y 24 "$cmd" || \
|
||||
fail "$s: new-session failed"
|
||||
$TMUX2 new-session -d -s out -x 80 -y 24 "$TMUX attach -t $s" || \
|
||||
fail "$s: outer client failed"
|
||||
wait_clients 1
|
||||
}
|
||||
|
||||
new_outer_client()
|
||||
{
|
||||
s=$1
|
||||
|
||||
$TMUX2 new-window -d -t out: "$TMUX attach -t $s" || \
|
||||
fail "$s: outer client failed"
|
||||
}
|
||||
|
||||
client_for_session()
|
||||
{
|
||||
$TMUX list-clients -F '#{client_name} #{client_session}' |
|
||||
awk -v s="$1" '$2 == s { print $1; exit }'
|
||||
}
|
||||
|
||||
test_choose_tree()
|
||||
{
|
||||
start_client tree-a
|
||||
$TMUX new-session -d -s tree-b -n one 'cat' || fail "tree-b failed"
|
||||
$TMUX new-window -d -t tree-b -n two 'cat' || fail "tree-b:1 failed"
|
||||
$TMUX new-session -d -s tree-c -n one 'cat' || fail "tree-c failed"
|
||||
$TMUX new-session -d -s tree-d -n one 'cat' || fail "tree-d failed"
|
||||
$TMUX split-window -d -t tree-a:0 'cat' || fail "tree split failed"
|
||||
|
||||
$TMUX choose-tree -t tree-a:0 -O index -F 'MT #{session_name}:#{window_index}.#{pane_index}' || \
|
||||
fail "choose-tree failed"
|
||||
wait_for 'MT '
|
||||
repeat_key tree-a:0 j 40
|
||||
|
||||
$TMUX kill-session -t tree-d || fail "tree kill-session failed"
|
||||
$TMUX kill-session -t tree-c || fail "tree kill-session failed"
|
||||
$TMUX rename-session -t tree-b tree-renamed || fail "tree rename failed"
|
||||
$TMUX rename-window -t tree-renamed:0 renamed || fail "tree rename-window failed"
|
||||
$TMUX kill-window -t tree-renamed:1 || fail "tree kill-window failed"
|
||||
side=$($TMUX split-window -d -P -F '#{pane_id}' -t tree-a:0 'cat') || \
|
||||
fail "tree side split failed"
|
||||
$TMUX break-pane -d -s "$side" || fail "tree break-pane failed"
|
||||
$TMUX join-pane -d -s "$side" -t tree-a:0.0 || \
|
||||
fail "tree join-pane failed"
|
||||
i=0
|
||||
while [ "$i" -lt 12 ]; do
|
||||
$TMUX new-window -d -t tree-a -n "new$i" 'cat' || \
|
||||
fail "tree new-window failed"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
assert_alive "choose-tree mutation"
|
||||
$TMUX send-keys -t tree-a:0 k j l h Enter || \
|
||||
fail "choose-tree keys failed"
|
||||
wait_mode tree-a:0 0
|
||||
assert_alive "choose-tree exit"
|
||||
}
|
||||
|
||||
test_choose_buffer()
|
||||
{
|
||||
start_client buffer-a
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 30 ]; do
|
||||
$TMUX set-buffer -b "mbuf$i" "buffer mutation $i" || \
|
||||
fail "set-buffer failed"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
$TMUX choose-buffer -t buffer-a:0 -F 'MB #{buffer_name}' || \
|
||||
fail "choose-buffer failed"
|
||||
wait_for 'MB '
|
||||
repeat_key buffer-a:0 j 40
|
||||
|
||||
i=8
|
||||
while [ "$i" -lt 30 ]; do
|
||||
$TMUX delete-buffer -b "mbuf$i" || fail "delete-buffer failed"
|
||||
i=$((i + 1))
|
||||
done
|
||||
i=30
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$TMUX set-buffer -b "mbuf$i" "new buffer mutation $i" || \
|
||||
fail "new set-buffer failed"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
assert_alive "choose-buffer mutation"
|
||||
$TMUX send-keys -t buffer-a:0 k j Enter || \
|
||||
fail "choose-buffer keys failed"
|
||||
wait_mode buffer-a:0 0
|
||||
assert_alive "choose-buffer exit"
|
||||
}
|
||||
|
||||
test_choose_client()
|
||||
{
|
||||
start_client client-a
|
||||
$TMUX new-session -d -s client-b -n main 'cat' || fail "client-b failed"
|
||||
$TMUX new-session -d -s client-c -n main 'cat' || fail "client-c failed"
|
||||
new_outer_client client-b
|
||||
new_outer_client client-c
|
||||
wait_clients 3
|
||||
|
||||
$TMUX choose-client -t client-a:0 -F 'MC #{client_session}' || \
|
||||
fail "choose-client failed"
|
||||
wait_for 'MC '
|
||||
repeat_key client-a:0 j 20
|
||||
|
||||
c=$(client_for_session client-c)
|
||||
[ -n "$c" ] || fail "client-c client not found"
|
||||
$TMUX detach-client -t "$c" || fail "detach client-c failed"
|
||||
c=$(client_for_session client-b)
|
||||
[ -n "$c" ] || fail "client-b client not found"
|
||||
$TMUX detach-client -t "$c" || fail "detach client-b failed"
|
||||
|
||||
$TMUX new-session -d -s client-d -n main 'cat' || fail "client-d failed"
|
||||
new_outer_client client-d
|
||||
wait_clients 2
|
||||
|
||||
assert_alive "choose-client mutation"
|
||||
$TMUX send-keys -t client-a:0 k j Enter || \
|
||||
fail "choose-client keys failed"
|
||||
wait_mode client-a:0 0
|
||||
assert_alive "choose-client exit"
|
||||
}
|
||||
|
||||
test_customize_mode()
|
||||
{
|
||||
start_client option-a
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 30 ]; do
|
||||
$TMUX set-option -g "@mode_mut_$i" "$i" || \
|
||||
fail "set option failed"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
$TMUX customize-mode -t option-a:0 -F 'MO #{option_name}=#{option_value}' || \
|
||||
fail "customize-mode failed"
|
||||
wait_mode option-a:0 1
|
||||
repeat_key option-a:0 j 80
|
||||
|
||||
i=10
|
||||
while [ "$i" -lt 30 ]; do
|
||||
$TMUX set-option -gu "@mode_mut_$i" || fail "unset option failed"
|
||||
i=$((i + 1))
|
||||
done
|
||||
i=30
|
||||
while [ "$i" -lt 55 ]; do
|
||||
$TMUX set-option -g "@mode_mut_$i" "$i" || \
|
||||
fail "new option failed"
|
||||
i=$((i + 1))
|
||||
done
|
||||
$TMUX set-option -g status-left 'mutated' || fail "status-left failed"
|
||||
$TMUX rename-session -t option-a option-renamed || fail "option rename failed"
|
||||
|
||||
assert_alive "customize-mode mutation"
|
||||
$TMUX send-keys -t option-renamed:0 k j C-d C-u q || \
|
||||
fail "customize-mode keys failed"
|
||||
wait_mode option-renamed:0 0
|
||||
assert_alive "customize-mode exit"
|
||||
}
|
||||
|
||||
test_copy_mode()
|
||||
{
|
||||
start_client copy-a 'i=0; while [ $i -lt 200 ]; do echo "copy mutation line $i"; i=$((i + 1)); done; cat'
|
||||
$TMUX set-window-option -g mode-keys vi || fail "mode-keys failed"
|
||||
$TMUX split-window -d -t copy-a:0 'cat' || fail "copy split failed"
|
||||
|
||||
$TMUX copy-mode -t copy-a:0 || fail "copy-mode failed"
|
||||
wait_mode copy-a:0 1
|
||||
repeat_key copy-a:0 k 20
|
||||
|
||||
$TMUX rename-window -t copy-a:0 renamed || fail "copy rename-window failed"
|
||||
side=$($TMUX split-window -d -P -F '#{pane_id}' -t copy-a:renamed 'cat') || \
|
||||
fail "copy side split failed"
|
||||
$TMUX break-pane -d -s "$side" || fail "copy break-pane failed"
|
||||
$TMUX join-pane -d -s "$side" -t copy-a:renamed.0 || \
|
||||
fail "copy join-pane failed"
|
||||
$TMUX kill-pane -t copy-a:renamed.1 || fail "copy kill-pane failed"
|
||||
$TMUX new-window -d -t copy-a -n extra 'cat' || fail "copy new-window failed"
|
||||
$TMUX kill-window -t copy-a:extra || fail "copy kill-window failed"
|
||||
$TMUX rename-session -t copy-a copy-renamed || fail "copy rename failed"
|
||||
|
||||
assert_alive "copy-mode mutation"
|
||||
$TMUX send-keys -t copy-renamed:renamed.0 j k C-d C-u q || \
|
||||
fail "copy-mode keys failed"
|
||||
wait_mode copy-renamed:renamed.0 0
|
||||
assert_alive "copy-mode exit"
|
||||
}
|
||||
|
||||
cleanup_servers
|
||||
test_choose_tree
|
||||
test_choose_buffer
|
||||
test_choose_client
|
||||
test_customize_mode
|
||||
test_copy_mode
|
||||
cleanup
|
||||
exit 0
|
||||
@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
PATH=/bin:/usr/bin
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TERM=$($TMUX start \; show -gv default-terminal)
|
||||
|
||||
@@ -8,7 +8,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -6,11 +6,10 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
TMUX="$TEST_TMUX -LtestA$$-1 -f/dev/null"
|
||||
|
||||
TMP=$(mktemp)
|
||||
trap "rm -f $TMP" 0 1 15
|
||||
trap 'rm -f "$TMP"; $TMUX kill-server 2>/dev/null' 0 1 15
|
||||
|
||||
$TMUX -f/dev/null new -d </dev/null || exit 1
|
||||
sleep 1
|
||||
@@ -18,6 +17,7 @@ $TMUX ls -F "#{window_width} #{window_height}" >$TMP
|
||||
printf "80 24\n"|cmp -s $TMP - || exit 1
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMUX="$TEST_TMUX -LtestA$$-2 -f/dev/null"
|
||||
$TMUX -f/dev/null new -d -x 100 -y 50 </dev/null || exit 1
|
||||
sleep 1
|
||||
$TMUX ls -F "#{window_width} #{window_height}" >$TMP
|
||||
|
||||
@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
# Tests of array options in the options engine (options_array_* in options.c
|
||||
# and the array handling in cmd-set-option.c / cmd-show-options.c).
|
||||
#
|
||||
# Array options are indexed by integer. This exercises: setting a whole array
|
||||
# from a separator-delimited string; per-index set with option[N]; -a append
|
||||
# (which lands at the next free index); show ordering by ascending index and
|
||||
# preservation of gaps; per-index unset with -u; show -v of a single index and
|
||||
# of a missing index; and per-option separators (user-keys splits only on
|
||||
# comma, update-environment on space or comma).
|
||||
# Array options are keyed by string, with numeric-looking keys kept compatible
|
||||
# with the old numeric forms. This exercises: setting a whole array from a
|
||||
# separator-delimited string; per-key set with option[key]; -a append (which
|
||||
# lands at the next free numeric key); show ordering by numeric keys first in
|
||||
# ascending order followed by string keys in strcmp order; preservation of gaps;
|
||||
# per-key unset with -u; show -v of a single key and of a missing key; and
|
||||
# per-option separators (user-keys splits only on comma, update-environment on
|
||||
# space or comma).
|
||||
#
|
||||
# update-environment (session), status-format (session), user-keys (server)
|
||||
# and command-alias (server) are used as representative array options.
|
||||
@@ -20,7 +22,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
check_value()
|
||||
@@ -34,6 +36,21 @@ check_value()
|
||||
fi
|
||||
}
|
||||
|
||||
check_wait_value()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 30 ]; do
|
||||
out=$($TMUX show $1 2>&1)
|
||||
[ "$out" = "$2" ] && return 0
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
echo "show $1 failed."
|
||||
echo "Expected: '$2'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# check_array $args $expected
|
||||
#
|
||||
# Compare the full (multi-line) show output for an array option with a
|
||||
@@ -87,41 +104,59 @@ $TMUX new-session -d -s main -x 80 -y 24 || exit 1
|
||||
# --- whole-array assignment splits on the separator -----------------------
|
||||
#
|
||||
# update-environment has the default " ," separator, so a single string value
|
||||
# is split into consecutive indices starting at 0.
|
||||
# is split into consecutive numeric keys starting at 0.
|
||||
check_ok set -g update-environment "AAA BBB,CCC"
|
||||
check_array "-g update-environment" "update-environment[0] AAA
|
||||
update-environment[1] BBB
|
||||
update-environment[2] CCC"
|
||||
|
||||
# --- -a append goes to the next free index --------------------------------
|
||||
# --- -a append goes to the next free numeric key --------------------------
|
||||
check_ok set -ga update-environment "DDD"
|
||||
check_array "-g update-environment" "update-environment[0] AAA
|
||||
update-environment[1] BBB
|
||||
update-environment[2] CCC
|
||||
update-environment[3] DDD"
|
||||
|
||||
# --- per-index unset leaves a gap; show preserves order and gaps ----------
|
||||
# --- per-key unset leaves a gap; show preserves order and gaps ------------
|
||||
check_ok set -gu update-environment[1]
|
||||
check_array "-g update-environment" "update-environment[0] AAA
|
||||
update-environment[2] CCC
|
||||
update-environment[3] DDD"
|
||||
# show -v of an existing index returns its value; a missing index is empty.
|
||||
# show -v of an existing key returns its value; a missing key is empty.
|
||||
check_value "-gv update-environment[0]" "AAA"
|
||||
check_value "-gv update-environment[1]" ""
|
||||
check_ok set -g update-environment[notify] "EEE"
|
||||
check_ok set -ga update-environment "FFF"
|
||||
check_array "-g update-environment" "update-environment[0] AAA
|
||||
update-environment[1] FFF
|
||||
update-environment[2] CCC
|
||||
update-environment[3] DDD
|
||||
update-environment[notify] EEE"
|
||||
|
||||
# --- explicit indexed set, including out-of-order and gaps ----------------
|
||||
# --- explicit keyed set, including out-of-order and gaps ------------------
|
||||
#
|
||||
# status-format is a session array; assigning an empty string first clears its
|
||||
# multi-index default, then set specific indices out of order and confirm show
|
||||
# sorts by ascending index and keeps the gap at [1].
|
||||
# multi-index default, then set specific keys out of order and confirm show
|
||||
# sorts by ascending numeric key and keeps the gap at [1].
|
||||
check_ok set -g status-format ""
|
||||
check_array "-g status-format" "status-format"
|
||||
check_ok set -g status-format[5] "five"
|
||||
check_ok set -g status-format[0] "zero"
|
||||
check_ok set -g status-format[2] "two"
|
||||
check_ok set -g status-format[01] "one"
|
||||
check_ok set -g status-format[zoom] "zoom"
|
||||
check_ok set -g status-format[foo-bar] "foo-bar"
|
||||
check_ok set -g status-format[xterm-256color] "xterm"
|
||||
check_array "-g status-format" "status-format[0] zero
|
||||
status-format[1] one
|
||||
status-format[2] two
|
||||
status-format[5] five"
|
||||
status-format[5] five
|
||||
status-format[foo-bar] foo-bar
|
||||
status-format[xterm-256color] xterm
|
||||
status-format[zoom] zoom"
|
||||
check_value "-gv status-format[01]" "one"
|
||||
check_ok set -gu status-format[zoom]
|
||||
check_value "-gv status-format[zoom]" ""
|
||||
|
||||
# --- comma-only separator (user-keys) -------------------------------------
|
||||
#
|
||||
@@ -133,11 +168,48 @@ user-keys[1] "Two Three"'
|
||||
|
||||
# --- command-type array (a hook) ------------------------------------------
|
||||
#
|
||||
# Hooks are command arrays: an indexed value is parsed as a command when set
|
||||
# Hooks are command arrays: a keyed value is parsed as a command when set
|
||||
# and re-printed from the parsed command list; a syntax error is reported.
|
||||
check_ok set -g alert-bell[0] "display-message hi"
|
||||
check_value "-gv alert-bell[0]" "display-message hi"
|
||||
check_fail "syntax error" set -g alert-bell[0] "if -x {"
|
||||
check_ok set-hook -g window-renamed[notify] "display-message renamed"
|
||||
check_value "-gv window-renamed[notify]" "display-message renamed"
|
||||
check_ok set-hook -gu window-renamed[notify]
|
||||
check_value "-gv window-renamed[notify]" ""
|
||||
check_ok set -g @hook_first 0
|
||||
check_ok set -g @hook_second 0
|
||||
check_ok set-hook -g window-renamed[first] "set -g @hook_first 1"
|
||||
check_ok set-hook -g window-renamed[second] "set -g @hook_second 1"
|
||||
shown=$($TMUX show-hooks -g window-renamed) || {
|
||||
echo "show-hooks -g window-renamed failed"
|
||||
exit 1
|
||||
}
|
||||
echo "$shown" | grep -q '^window-renamed\[first\]' || {
|
||||
echo "missing first hook key: $shown"
|
||||
exit 1
|
||||
}
|
||||
echo "$shown" | grep -q '^window-renamed\[second\]' || {
|
||||
echo "missing second hook key: $shown"
|
||||
exit 1
|
||||
}
|
||||
check_ok rename-window -t main:0 array-hooks
|
||||
check_wait_value "-gqv @hook_first" "1"
|
||||
check_wait_value "-gqv @hook_second" "1"
|
||||
check_ok set-hook -gu window-renamed[first]
|
||||
shown=$($TMUX show-hooks -g window-renamed) || {
|
||||
echo "show-hooks -g window-renamed after unset failed"
|
||||
exit 1
|
||||
}
|
||||
echo "$shown" | grep -q '^window-renamed\[first\]' && {
|
||||
echo "first hook key was not unset: $shown"
|
||||
exit 1
|
||||
}
|
||||
echo "$shown" | grep -q '^window-renamed\[second\]' || {
|
||||
echo "second hook key did not remain: $shown"
|
||||
exit 1
|
||||
}
|
||||
check_ok set-hook -gu window-renamed[second]
|
||||
|
||||
# --- colour-type array ----------------------------------------------------
|
||||
#
|
||||
@@ -146,11 +218,11 @@ check_ok set -w pane-colours[0] red
|
||||
check_value "-wv pane-colours[0]" "red"
|
||||
check_fail "bad colour: xxxyyy" set -w pane-colours[1] xxxyyy
|
||||
|
||||
# --- -o refuses to overwrite an already-set index -------------------------
|
||||
# --- -o refuses to overwrite an already-set key ---------------------------
|
||||
check_ok set -g command-alias[9] "x=list-keys"
|
||||
check_fail "already set: command-alias[9]" set -go command-alias[9] "y=list-keys"
|
||||
|
||||
# --- non-array option rejects index syntax --------------------------------
|
||||
# --- non-array option rejects key syntax ----------------------------------
|
||||
#
|
||||
# status-left is a plain string; indexing it is an error.
|
||||
check_fail "not an array: status-left[0]" set -g status-left[0] "x"
|
||||
|
||||
@@ -17,7 +17,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
# check_value $args $expected
|
||||
|
||||
@@ -17,7 +17,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
check_value()
|
||||
|
||||
@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
$TMUX new -d
|
||||
|
||||
640
regress/pane-ops.sh
Normal file
640
regress/pane-ops.sh
Normal file
@@ -0,0 +1,640 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Tests of pane management command semantics (not parsing), as implemented in
|
||||
# cmd-split-window.c, cmd-break-pane.c, cmd-join-pane.c (join-pane and
|
||||
# move-pane), cmd-swap-pane.c, cmd-kill-pane.c, cmd-respawn-pane.c,
|
||||
# cmd-respawn-window.c, cmd-resize-pane.c and cmd-select-pane.c.
|
||||
#
|
||||
# This exercises:
|
||||
# - split-window -h/-v with -l in cells and percent, -b placing the new pane
|
||||
# before (left/top of) the target and -f spanning the full window size;
|
||||
# - break-pane moving a pane into a new window (-d, -n name, -a after, -P -F
|
||||
# printing the new location);
|
||||
# - join-pane moving a window's only pane into another window (destroying the
|
||||
# source window), -b before, -l size, and the identical-panes error;
|
||||
# - move-pane as an alias for join-pane;
|
||||
# - swap-pane -U/-D/-s/-t, -d keeping the active pane, and the marked pane
|
||||
# (select-pane -m/-M) as the default swap source;
|
||||
# - kill-pane, kill-pane -a keeping only the target;
|
||||
# - respawn-pane/respawn-window refusing a live pane without -k, working on a
|
||||
# dead pane (remain-on-exit) and killing with -k;
|
||||
# - resize-pane -x/-y in cells and percent, -L/-R/-U/-D adjustments and -Z
|
||||
# zoom/unzoom (including implicit unzoom on split).
|
||||
#
|
||||
# window-ops.sh covers window-level commands and buffers.sh paste buffers.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LANG=C.UTF-8
|
||||
LC_ALL=C.UTF-8
|
||||
export TERM LANG LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
# check_ok $cmd...
|
||||
#
|
||||
# Run a command and require that it succeeds.
|
||||
check_ok()
|
||||
{
|
||||
if ! $TMUX "$@"; then
|
||||
echo "Command failed (expected success): $*"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# check_fail $expected_error $cmd...
|
||||
#
|
||||
# Run a command and require that it fails with the given error message.
|
||||
check_fail()
|
||||
{
|
||||
exp="$1"
|
||||
shift
|
||||
out=$($TMUX "$@" 2>&1)
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Command succeeded (expected failure): $*"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$out" != "$exp" ]; then
|
||||
echo "Wrong error for: $*"
|
||||
echo "Expected: '$exp'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# check_fmt $target $format $expected
|
||||
#
|
||||
# Expand a format in a target's context and compare with $expected.
|
||||
check_fmt()
|
||||
{
|
||||
out=$($TMUX display-message -p -t "$1" "$2" 2>&1)
|
||||
if [ "$out" != "$3" ]; then
|
||||
echo "Format '$2' for '$1' wrong."
|
||||
echo "Expected: '$3'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# check_panes $target $expected
|
||||
#
|
||||
# Compare the pane list of a window (as "index:id ...", in index order) with
|
||||
# $expected.
|
||||
check_panes()
|
||||
{
|
||||
out=$(echo $($TMUX list-panes -t "$1" -F '#{pane_index}:#{pane_id}'))
|
||||
if [ "$out" != "$2" ]; then
|
||||
echo "Pane list of '$1' wrong."
|
||||
echo "Expected: '$2'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_alive()
|
||||
{
|
||||
if [ "$($TMUX display-message -p alive 2>&1)" != "alive" ]; then
|
||||
echo "Server died: $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# split-window geometry.
|
||||
|
||||
check_ok new-session -d -s P -x 80 -y 24 -n main
|
||||
p0=$($TMUX display-message -p -t P:0.0 '#{pane_id}')
|
||||
|
||||
# Horizontal split with -l in cells: new pane gets exactly that width and the
|
||||
# old pane the rest minus the separator line.
|
||||
check_ok split-window -d -h -l 20 -t "$p0"
|
||||
p1=$($TMUX display-message -p -t P:0.1 '#{pane_id}')
|
||||
check_fmt "$p1" '#{pane_width}x#{pane_height}' '20x24'
|
||||
check_fmt "$p0" '#{pane_width}x#{pane_height}' '59x24'
|
||||
|
||||
# Vertical split with a percentage of the pane being split.
|
||||
check_ok split-window -d -v -l 25% -t "$p0"
|
||||
p2=$($TMUX display-message -p -t P:0.1 '#{pane_id}')
|
||||
check_fmt "$p2" '#{pane_width}x#{pane_height}' '59x6'
|
||||
check_fmt "$p0" '#{pane_width}x#{pane_height}' '59x17'
|
||||
|
||||
# -b puts the new pane to the left of the target; -f makes it span the full
|
||||
# window height.
|
||||
check_ok split-window -d -h -b -f -l 10 -t "$p0"
|
||||
p3=$($TMUX display-message -p -t P:0.0 '#{pane_id}')
|
||||
check_fmt "$p3" '#{pane_width}x#{pane_height}' '10x24'
|
||||
check_fmt "$p3" '#{pane_left},#{pane_top}' '0,0'
|
||||
check_fmt "$p0" '#{pane_width}x#{pane_height}' '50x17'
|
||||
check_panes P:0 "0:$p3 1:$p0 2:$p2 3:$p1"
|
||||
|
||||
# The new pane becomes active unless -d is given.
|
||||
check_ok select-pane -t "$p0"
|
||||
check_ok split-window -d -v -t "$p0"
|
||||
check_fmt 'P:0' '#{pane_id}' "$p0"
|
||||
p4=$($TMUX display-message -p -t P:0.2 '#{pane_id}')
|
||||
check_ok split-window -v -t "$p4"
|
||||
p5=$($TMUX display-message -p -t 'P:0' '#{pane_id}')
|
||||
check_ok kill-pane -t "$p5"
|
||||
check_ok kill-pane -t "$p4"
|
||||
check_panes P:0 "0:$p3 1:$p0 2:$p2 3:$p1"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# break-pane and join-pane.
|
||||
|
||||
# break-pane moves a pane to a new window; -P -F prints where it went and -n
|
||||
# names the new window.
|
||||
out=$($TMUX break-pane -d -P -F '#{window_index}:#{pane_id}' -n broken \
|
||||
-s "$p1" -t P:)
|
||||
if [ "$out" != "1:$p1" ]; then
|
||||
echo "break-pane -P output wrong: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
check_fmt 'P:1' '#{window_name}:#{window_panes}' 'broken:1'
|
||||
check_fmt 'P:0' '#{window_panes}' '3'
|
||||
|
||||
# join-pane -v moves it back (the source window, left empty, is destroyed).
|
||||
check_ok join-pane -d -v -s P:broken.0 -t "$p2"
|
||||
check_fmt 'P:0' '#{window_panes}' '4'
|
||||
if $TMUX has-session -t P:broken 2>/dev/null; then
|
||||
echo "Window 'broken' still exists after join-pane."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The joined pane is below the target (-v, no -b).
|
||||
top=$($TMUX display-message -p -t "$p2" '#{pane_bottom}')
|
||||
joined=$($TMUX display-message -p -t "$p1" '#{pane_top}')
|
||||
if [ "$joined" -le "$top" ]; then
|
||||
echo "Joined pane is not below target ($joined <= $top)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# join-pane -h -b puts the source to the left of the target; -l sets size.
|
||||
check_ok break-pane -d -n broken -s "$p1" -t P:
|
||||
check_ok join-pane -d -h -b -l 30 -s P:broken.0 -t "$p2"
|
||||
check_fmt "$p1" '#{pane_width}' '30'
|
||||
l1=$($TMUX display-message -p -t "$p1" '#{pane_left}')
|
||||
l2=$($TMUX display-message -p -t "$p2" '#{pane_left}')
|
||||
if [ "$l1" -ge "$l2" ]; then
|
||||
echo "Joined pane is not left of target ($l1 >= $l2)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Joining a pane to itself is an error.
|
||||
check_fail 'source and target panes must be different' \
|
||||
join-pane -d -s "$p0" -t "$p0"
|
||||
|
||||
# break-pane to an occupied window index or with an invalid (non-UTF-8) name
|
||||
# is an error.
|
||||
check_fail 'index in use: 0' break-pane -d -s "$p1" -t P:0
|
||||
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.)
|
||||
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_fmt 'P:0' '#{window_panes}' '4'
|
||||
check_fmt 'P:5' '#{window_panes}' '1'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# swap-pane.
|
||||
|
||||
check_panes P:0 "0:$p3 1:$p0 2:$p2 3:$p1"
|
||||
|
||||
# -s/-t swap two panes.
|
||||
check_ok swap-pane -d -s "$p3" -t "$p1"
|
||||
check_panes P:0 "0:$p1 1:$p0 2:$p2 3:$p3"
|
||||
check_ok swap-pane -d -s "$p3" -t "$p1"
|
||||
check_panes P:0 "0:$p3 1:$p0 2:$p2 3:$p1"
|
||||
|
||||
# -U swaps the target pane with the previous pane, -D with the next; without
|
||||
# -s the target is the active pane.
|
||||
check_ok swap-pane -d -U -t "$p0"
|
||||
check_panes P:0 "0:$p0 1:$p3 2:$p2 3:$p1"
|
||||
check_ok swap-pane -d -D -t "$p0"
|
||||
check_panes P:0 "0:$p3 1:$p0 2:$p2 3:$p1"
|
||||
|
||||
# Without -d the target pane becomes the active pane (it arrives at the
|
||||
# source pane's position).
|
||||
check_ok select-pane -t "$p0"
|
||||
check_ok swap-pane -s "$p0" -t "$p2"
|
||||
check_fmt 'P:0' '#{pane_id}' "$p2"
|
||||
check_panes P:0 "0:$p3 1:$p2 2:$p0 3:$p1"
|
||||
check_ok swap-pane -s "$p2" -t "$p0"
|
||||
check_fmt 'P:0' '#{pane_id}' "$p0"
|
||||
check_panes P:0 "0:$p3 1:$p0 2:$p2 3:$p1"
|
||||
|
||||
# With a marked pane and no -s, the marked pane is the swap source.
|
||||
check_ok select-pane -m -t "$p3"
|
||||
check_fmt "$p3" '#{pane_marked}' '1'
|
||||
check_ok swap-pane -d -t "$p1"
|
||||
check_panes P:0 "0:$p1 1:$p0 2:$p2 3:$p3"
|
||||
check_ok swap-pane -d -t "$p1"
|
||||
check_panes P:0 "0:$p3 1:$p0 2:$p2 3:$p1"
|
||||
|
||||
# select-pane -M clears the mark.
|
||||
check_ok select-pane -M
|
||||
check_fmt "$p3" '#{pane_marked}' '0'
|
||||
check_fmt 'P:0' '#{pane_marked_set}' '0'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# resize-pane and zoom.
|
||||
|
||||
# Absolute -x on a horizontal split and percentage.
|
||||
check_ok resize-pane -t "$p3" -x 20
|
||||
check_fmt "$p3" '#{pane_width}' '20'
|
||||
check_ok resize-pane -t "$p3" -x 25%
|
||||
check_fmt "$p3" '#{pane_width}' '20'
|
||||
check_ok resize-pane -t "$p3" -x 10
|
||||
check_fmt "$p3" '#{pane_width}' '10'
|
||||
|
||||
# Relative adjustments: -R grows a left pane, -L shrinks it back; a count may
|
||||
# be given.
|
||||
check_ok resize-pane -t "$p3" -R
|
||||
check_fmt "$p3" '#{pane_width}' '11'
|
||||
check_ok resize-pane -t "$p3" -L
|
||||
check_fmt "$p3" '#{pane_width}' '10'
|
||||
check_ok resize-pane -t "$p3" -R 5
|
||||
check_fmt "$p3" '#{pane_width}' '15'
|
||||
check_ok resize-pane -t "$p3" -L 5
|
||||
check_fmt "$p3" '#{pane_width}' '10'
|
||||
|
||||
# -y on a vertical split.
|
||||
check_ok resize-pane -t "$p2" -y 10
|
||||
check_fmt "$p2" '#{pane_height}' '10'
|
||||
|
||||
# p2 is the bottom pane, so its bottom border cannot move down: -D instead
|
||||
# grows it by taking lines from the pane above and -U gives them back.
|
||||
check_ok resize-pane -t "$p2" -D 2
|
||||
check_fmt "$p2" '#{pane_height}' '12'
|
||||
check_ok resize-pane -t "$p2" -U 2
|
||||
check_fmt "$p2" '#{pane_height}' '10'
|
||||
|
||||
# Bad adjustment, width and height are errors.
|
||||
check_fail 'adjustment invalid' resize-pane -t "$p2" -U nonsense
|
||||
check_fail 'width invalid' resize-pane -t "$p2" -x nonsense
|
||||
check_fail 'height invalid' resize-pane -t "$p2" -y nonsense
|
||||
|
||||
# -Z zooms: the pane temporarily fills the window and the flags show it.
|
||||
check_ok resize-pane -Z -t "$p0"
|
||||
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_width}x#{pane_height}' \
|
||||
'1:80x24'
|
||||
|
||||
# Zoom is transparent to pane commands on other panes, and -Z again unzooms.
|
||||
check_ok resize-pane -Z -t "$p0"
|
||||
check_fmt "$p0" '#{window_zoomed_flag}' '0'
|
||||
|
||||
# Splitting while zoomed unzooms first.
|
||||
check_ok resize-pane -Z -t "$p0"
|
||||
check_fmt 'P:0' '#{window_zoomed_flag}' '1'
|
||||
check_ok split-window -d -v -t "$p0"
|
||||
check_fmt 'P:0' '#{window_zoomed_flag}' '0'
|
||||
check_fmt 'P:0' '#{window_panes}' '5'
|
||||
p6=$($TMUX display-message -p -t P:0.2 '#{pane_id}')
|
||||
check_ok kill-pane -t "$p6"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# kill-pane.
|
||||
|
||||
check_fmt 'P:0' '#{window_panes}' '4'
|
||||
check_ok kill-pane -t "$p3"
|
||||
check_panes P:0 "0:$p0 1:$p2 2:$p1"
|
||||
|
||||
# -a kills every pane except the target.
|
||||
check_ok kill-pane -a -t "$p0"
|
||||
check_panes P:0 "0:$p0"
|
||||
|
||||
# Killing the last pane in a window kills the window.
|
||||
check_ok new-window -d -t P:7 -n goner
|
||||
check_ok kill-pane -t P:7.0
|
||||
if $TMUX has-session -t P:goner 2>/dev/null; then
|
||||
echo "Window 'goner' still exists after killing its only pane."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# respawn-pane and respawn-window.
|
||||
|
||||
# Respawning a pane whose process is alive fails without -k.
|
||||
check_fail "respawn pane failed: pane P:0.0 still active" \
|
||||
respawn-pane -t P:0.0
|
||||
check_fail "respawn window failed: window P:0 still active" \
|
||||
respawn-window -t P:0
|
||||
|
||||
# With remain-on-exit a pane whose command exited stays as a dead pane and
|
||||
# may be respawned without -k.
|
||||
check_ok set-option -g remain-on-exit on
|
||||
check_ok new-window -d -t P:8 -n dead 'true'
|
||||
i=0
|
||||
while [ "$($TMUX display-message -p -t P:8.0 '#{pane_dead}')" != "1" ]; do
|
||||
i=$((i + 1))
|
||||
[ $i -gt 50 ] && { echo "Pane did not die."; exit 1; }
|
||||
sleep 0.1
|
||||
done
|
||||
check_ok respawn-pane -t P:8.0 'sleep 100'
|
||||
check_fmt 'P:8.0' '#{pane_dead}' '0'
|
||||
|
||||
# -k kills the live process and respawns.
|
||||
check_ok respawn-pane -k -t P:8.0 'sleep 200'
|
||||
check_fmt 'P:8.0' '#{pane_dead}' '0'
|
||||
|
||||
# respawn-window -k replaces the whole window (all panes) with one pane.
|
||||
check_ok split-window -d -t P:8
|
||||
check_fmt 'P:8' '#{window_panes}' '2'
|
||||
check_ok respawn-window -k -t P:8 'sleep 300'
|
||||
check_fmt 'P:8' '#{window_panes}' '1'
|
||||
check_fmt 'P:8.0' '#{pane_dead}' '0'
|
||||
check_ok set-option -g remain-on-exit off
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# select-pane.
|
||||
|
||||
# A 2x2-ish arrangement: q0 on top, q1 bottom-left, q2 bottom-right.
|
||||
check_ok new-window -d -t P:2 -n sel
|
||||
q0=$($TMUX display-message -p -t P:2.0 '#{pane_id}')
|
||||
check_ok split-window -d -v -t "$q0"
|
||||
q1=$($TMUX display-message -p -t P:2.1 '#{pane_id}')
|
||||
check_ok split-window -d -h -t "$q1"
|
||||
q2=$($TMUX display-message -p -t P:2.2 '#{pane_id}')
|
||||
|
||||
# Directional selection: -D, -R and -U move by pane position.
|
||||
check_ok select-pane -t "$q0"
|
||||
check_ok select-pane -D -t P:2
|
||||
check_fmt 'P:2' '#{pane_id}' "$q1"
|
||||
check_ok select-pane -R -t P:2
|
||||
check_fmt 'P:2' '#{pane_id}' "$q2"
|
||||
check_ok select-pane -U -t P:2
|
||||
check_fmt 'P:2' '#{pane_id}' "$q0"
|
||||
|
||||
# -l returns to the previously active pane; a window that never had another
|
||||
# active pane has no last pane.
|
||||
check_ok select-pane -l -t P:2
|
||||
check_fmt 'P:2' '#{pane_id}' "$q2"
|
||||
check_fail 'no last pane' select-pane -l -t P:8.0
|
||||
|
||||
# -d disables input to a pane, -e enables it again and -T sets the title.
|
||||
check_ok select-pane -d -t "$q0"
|
||||
check_fmt "$q0" '#{pane_input_off}' '1'
|
||||
check_ok select-pane -e -t "$q0"
|
||||
check_fmt "$q0" '#{pane_input_off}' '0'
|
||||
check_ok select-pane -T mytitle -t "$q0"
|
||||
check_fmt "$q0" '#{pane_title}' 'mytitle'
|
||||
check_ok kill-window -t P:2
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# more split-window variants.
|
||||
|
||||
check_ok new-window -d -t P:2 -n splits
|
||||
|
||||
# new-window -E creates an empty initial pane, running no command.
|
||||
check_ok new-window -d -E -t P:9 -n empty
|
||||
check_fmt 'P:9.0' '#{pane_dead}' '0'
|
||||
check_fmt 'P:9.0' '#{pane_pid}' ''
|
||||
check_fail 'command cannot be given for empty pane' \
|
||||
new-window -d -E -t P:10 -n empty 'true'
|
||||
|
||||
# An empty string as the sole argument is equivalent to -E: the pane is
|
||||
# created empty, running no command.
|
||||
check_ok new-window -d -t P:12 -n empty-str ''
|
||||
check_fmt 'P:12.0' '#{pane_dead}' '0'
|
||||
check_fmt 'P:12.0' '#{pane_pid}' ''
|
||||
check_ok kill-window -t P:12
|
||||
|
||||
# A missing command (rather than an empty one) runs the default command, so
|
||||
# the pane is not empty and has a process.
|
||||
check_ok new-window -d -t P:12 -n default-cmd
|
||||
check_fmt 'P:12.0' '#{?pane_pid,live,empty}' 'live'
|
||||
check_ok kill-window -t P:12
|
||||
|
||||
# respawn-pane -E stores the command and cwd without starting it.
|
||||
tmp=${TMPDIR:-/tmp}/tmux-pane-ops-empty-$$
|
||||
rm -f "$tmp"
|
||||
check_ok new-window -d -E -t P:10 -n empty-respawn
|
||||
check_ok respawn-pane -E -c /tmp -t P:10.0 "pwd > $tmp"
|
||||
if [ -e "$tmp" ]; then
|
||||
echo "respawn-pane -E started command unexpectedly"
|
||||
exit 1
|
||||
fi
|
||||
check_ok respawn-pane -t P:10.0
|
||||
i=0
|
||||
while [ ! -e "$tmp" ]; do
|
||||
i=$((i + 1))
|
||||
[ $i -gt 50 ] && echo "respawn-pane did not start stored command" && \
|
||||
exit 1
|
||||
sleep 0.1
|
||||
done
|
||||
if [ "$(cat "$tmp")" != "/tmp" ]; then
|
||||
echo "respawn-pane did not use stored cwd"
|
||||
exit 1
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
check_ok kill-window -t P:9
|
||||
|
||||
# respawn-window -E stores the command and cwd without starting it.
|
||||
tmp=${TMPDIR:-/tmp}/tmux-window-ops-empty-$$
|
||||
rm -f "$tmp"
|
||||
check_ok new-window -d -E -t P:11 -n empty-respawn-window
|
||||
check_ok respawn-window -E -c /tmp -t P:11 "pwd > $tmp"
|
||||
check_fmt 'P:11.0' '#{pane_pid}' ''
|
||||
if [ -e "$tmp" ]; then
|
||||
echo "respawn-window -E started command unexpectedly"
|
||||
exit 1
|
||||
fi
|
||||
check_ok respawn-window -t P:11
|
||||
i=0
|
||||
while [ ! -e "$tmp" ]; do
|
||||
i=$((i + 1))
|
||||
[ $i -gt 50 ] && echo "respawn-window did not start stored command" && \
|
||||
exit 1
|
||||
sleep 0.1
|
||||
done
|
||||
if [ "$(cat "$tmp")" != "/tmp" ]; then
|
||||
echo "respawn-window did not use stored cwd"
|
||||
exit 1
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
|
||||
# -E splits with an empty pane, running no command; giving one is an error.
|
||||
check_ok split-window -d -E -t P:2.0
|
||||
check_fmt 'P:2' '#{window_panes}' '2'
|
||||
check_fail 'command cannot be given for empty pane' \
|
||||
split-window -d -E -t P:2.0 'sleep 5'
|
||||
|
||||
# An empty string as the sole argument splits with an empty pane, like -E.
|
||||
eid=$($TMUX split-window -d -P -F '#{pane_id}' -t P:2.0 '')
|
||||
check_fmt "$eid" '#{pane_dead}' '0'
|
||||
check_fmt "$eid" '#{pane_pid}' ''
|
||||
check_ok kill-pane -t "$eid"
|
||||
check_fmt 'P:2' '#{window_panes}' '2'
|
||||
|
||||
# -e adds to the new pane's environment.
|
||||
eid=$($TMUX split-window -d -P -F '#{pane_id}' -e GREETING=hello -t P:2.0 \
|
||||
'echo $GREETING; exec cat')
|
||||
i=0
|
||||
while out=$($TMUX capture-pane -p -t "$eid" | sed -n 1p) && \
|
||||
[ "$out" != "hello" ]; do
|
||||
i=$((i + 1))
|
||||
[ $i -gt 50 ] && { echo "split-window -e wrong: '$out'"; exit 1; }
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
# A bad -l size is an error.
|
||||
check_fail 'invalid tiled geometry invalid' \
|
||||
split-window -d -v -l invalid -t P:2.0
|
||||
|
||||
# -Z zooms the new pane.
|
||||
check_ok split-window -d -Z -t P:2.0
|
||||
check_fmt 'P:2' '#{window_zoomed_flag}' '1'
|
||||
check_ok kill-window -t P:2
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# more swap-pane: wrapping, cross-window, self and zoomed swaps.
|
||||
|
||||
check_ok new-window -d -t P:3 -n swaps
|
||||
check_ok split-window -d -v -t P:3.0
|
||||
check_ok split-window -d -v -t P:3.0
|
||||
r0=$($TMUX display-message -p -t P:3.0 '#{pane_id}')
|
||||
r1=$($TMUX display-message -p -t P:3.1 '#{pane_id}')
|
||||
r2=$($TMUX display-message -p -t P:3.2 '#{pane_id}')
|
||||
o0=$($TMUX display-message -p -t P:5.0 '#{pane_id}')
|
||||
|
||||
# -D on the last pane and -U on the first wrap around to the other end.
|
||||
check_ok swap-pane -d -D -t "$r2"
|
||||
check_panes P:3 "0:$r2 1:$r1 2:$r0"
|
||||
check_ok swap-pane -d -s "$r0" -t "$r2"
|
||||
check_ok swap-pane -d -U -t "$r0"
|
||||
check_panes P:3 "0:$r2 1:$r1 2:$r0"
|
||||
check_ok swap-pane -d -s "$r0" -t "$r2"
|
||||
check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
||||
|
||||
# Swapping a pane with itself quietly does nothing.
|
||||
check_ok swap-pane -d -s "$r1" -t "$r1"
|
||||
check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
||||
|
||||
# Panes can be swapped between different windows.
|
||||
check_ok swap-pane -d -s "$o0" -t "$r1"
|
||||
check_panes P:3 "0:$r0 1:$o0 2:$r2"
|
||||
check_panes P:5 "0:$r1"
|
||||
check_ok swap-pane -d -s "$r1" -t "$o0"
|
||||
check_panes P:3 "0:$r0 1:$r1 2:$r2"
|
||||
check_panes P:5 "0:$o0"
|
||||
|
||||
# -Z keeps the window zoomed across the swap.
|
||||
check_ok resize-pane -Z -t "$r0"
|
||||
check_ok swap-pane -d -Z -s "$r0" -t "$r1"
|
||||
check_fmt 'P:3' '#{window_zoomed_flag}' '1'
|
||||
check_ok resize-pane -Z -t P:3
|
||||
check_panes P:3 "0:$r1 1:$r0 2:$r2"
|
||||
|
||||
# kill-pane -a -f only kills other panes matching the filter.
|
||||
check_ok kill-pane -a -f '#{==:#{pane_id},'"$r2"'}' -t "$r1"
|
||||
check_panes P:3 "0:$r1 1:$r0"
|
||||
check_ok kill-window -t P:3
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# split-window -I and -s.
|
||||
|
||||
check_ok new-window -d -t P:3 -n splits2
|
||||
|
||||
# -I fills the new (empty) pane from standard input.
|
||||
printf 'stdin-stuff' | $TMUX split-window -d -I -t P:3.0
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "split-window -I failed."
|
||||
exit 1
|
||||
fi
|
||||
i=0
|
||||
while out=$($TMUX capture-pane -p -t P:3.1 | sed -n 1p) && \
|
||||
[ "$out" != "stdin-stuff" ]; do
|
||||
i=$((i + 1))
|
||||
[ $i -gt 50 ] && { echo "split-window -I wrong: '$out'"; exit 1; }
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
# -s sets the new pane's window-style.
|
||||
sid=$($TMUX split-window -d -P -F '#{pane_id}' -s 'bg=red' -t P:3.0)
|
||||
out=$($TMUX show-options -v -p -t "$sid" window-style)
|
||||
if [ "$out" != "bg=red" ]; then
|
||||
echo "split-window -s style wrong: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
check_ok kill-window -t P:3
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# more break-pane: -a insertion, selection and single-pane windows.
|
||||
|
||||
# check_windows $session $expected
|
||||
#
|
||||
# Compare the window list of a session (as "index:name ...") with $expected.
|
||||
check_windows()
|
||||
{
|
||||
out=$(echo $($TMUX list-windows -t "$1" -F \
|
||||
'#{window_index}:#{window_name}'))
|
||||
if [ "$out" != "$2" ]; then
|
||||
echo "Window list of '$1' wrong."
|
||||
echo "Expected: '$2'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_ok new-session -d -s Q -x 80 -y 24 -n q0
|
||||
|
||||
# -a breaks into a new window inserted after the target index, shuffling the
|
||||
# following windows up.
|
||||
check_ok new-window -d -t Q:1 -n q1
|
||||
check_ok split-window -d -t Q:1
|
||||
check_ok break-pane -d -a -s Q:1.1 -n qa -t Q:0
|
||||
check_windows Q '0:q0 1:qa 2:q1'
|
||||
|
||||
# Without -d the new window is selected.
|
||||
check_ok split-window -d -t Q:2
|
||||
check_ok select-window -t Q:0
|
||||
check_ok break-pane -s Q:2.1 -n qcur -t Q:
|
||||
check_fmt 'Q:' '#{window_name}' 'qcur'
|
||||
check_windows Q '0:q0 1:qa 2:q1 3:qcur'
|
||||
|
||||
# Breaking the only pane of a window relinks the window at a new index; -n
|
||||
# still renames it.
|
||||
check_ok new-window -d -t Q:5 -n qsolo
|
||||
out=$($TMUX break-pane -d -P -F '#{window_name}' -s Q:5.0 -n qmoved -t Q:)
|
||||
if [ "$out" != "qmoved" ]; then
|
||||
echo "single-pane break-pane output wrong: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
if $TMUX has-session -t Q:qsolo 2>/dev/null; then
|
||||
echo "Window 'qsolo' still exists after single-pane break-pane."
|
||||
exit 1
|
||||
fi
|
||||
check_ok has-session -t Q:qmoved
|
||||
check_ok kill-session -t Q
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# resize-pane -T.
|
||||
|
||||
# -T trims the history: lines below the cursor position are removed and the
|
||||
# cursor moves to the bottom. seq writes 100 lines (leaving 77 in history on
|
||||
# a 24-line screen) and the escape sequence puts the cursor on line 5.
|
||||
check_ok new-window -d -t P:2 'seq 1 100; printf "\033[5;1H"; exec cat'
|
||||
i=0
|
||||
while [ "$($TMUX display-message -p -t P:2.0 '#{history_size}')" != "77" ]
|
||||
do
|
||||
i=$((i + 1))
|
||||
[ $i -gt 50 ] && { echo "History did not fill."; exit 1; }
|
||||
sleep 0.1
|
||||
done
|
||||
check_fmt 'P:2.0' '#{cursor_y}' '4'
|
||||
check_ok resize-pane -T -t P:2.0
|
||||
check_fmt 'P:2.0' '#{history_size}' '58'
|
||||
check_fmt 'P:2.0' '#{cursor_y}' '23'
|
||||
check_ok kill-window -t P:2
|
||||
|
||||
assert_alive
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 0
|
||||
@@ -7,8 +7,8 @@ LANG=C.UTF-8
|
||||
export TERM LC_ALL LANG
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
OUT="$TEST_TMUX -Ltest -f/dev/null" # outer (host for the client)
|
||||
IN="$TEST_TMUX -Ltest2 -f/dev/null" # inner (under test)
|
||||
OUT="$TEST_TMUX -LtestA$$ -f/dev/null" # outer (host for the client)
|
||||
IN="$TEST_TMUX -LtestB$$ -f/dev/null" # inner (under test)
|
||||
|
||||
$OUT kill-server 2>/dev/null
|
||||
$IN kill-server 2>/dev/null
|
||||
|
||||
@@ -20,8 +20,8 @@ LANG=C.UTF-8
|
||||
export TERM LC_ALL LANG
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
OUT="$TEST_TMUX -Ltest -f/dev/null" # outer (host for the client)
|
||||
IN="$TEST_TMUX -Ltest2 -f/dev/null" # inner (under test)
|
||||
OUT="$TEST_TMUX -LtestA$$ -f/dev/null" # outer (host for the client)
|
||||
IN="$TEST_TMUX -LtestB$$ -f/dev/null" # inner (under test)
|
||||
|
||||
$OUT kill-server 2>/dev/null
|
||||
$IN kill-server 2>/dev/null
|
||||
@@ -49,6 +49,19 @@ status_line() {
|
||||
got() {
|
||||
$IN show -gv @r
|
||||
}
|
||||
wait_opt() {
|
||||
option=$1
|
||||
expected=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 30 ]; do
|
||||
value=$($IN show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] && return 0
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
fail "expected $option to be '$expected' but got '$value'"
|
||||
}
|
||||
reset() {
|
||||
$IN set -g @r "SENTINEL" || exit 1
|
||||
}
|
||||
@@ -85,6 +98,12 @@ $IN bind -n M-j command-prompt -P -I hello -p '(pre)' "set -g @r '%%'" || e
|
||||
$IN bind -n M-m command-prompt -p 'first,second' "set -g @r '%1/%2'" || exit 1
|
||||
$IN bind -n M-c command-prompt -p '(cmd)' "set -g @r '%%'" || exit 1
|
||||
$IN bind -n M-h command-prompt -T search -p '(srch)' "set -g @r '%%'" || exit 1
|
||||
$IN set-hook -g pane-prompt-opened \
|
||||
'set -gF @prompt_open "#{hook}:#{hook_pane}:#{hook_prompt_type}"' ||
|
||||
exit 1
|
||||
$IN set-hook -g pane-prompt-closed \
|
||||
'set -gF @prompt_close "#{hook}:#{hook_pane}:#{hook_prompt_type}"' ||
|
||||
exit 1
|
||||
|
||||
# --- Outer session: attach the inner one inside its pane. -------------------
|
||||
$OUT new -d -x80 -y24 || exit 1
|
||||
@@ -111,14 +130,17 @@ settle
|
||||
|
||||
# --- 1b. window.c: drawn over the pane, not on the status line. ---
|
||||
reset
|
||||
pane=$($IN display-message -p '#{pane_id}') || exit 1
|
||||
$OUT send-keys M-p || exit 1
|
||||
settle
|
||||
wait_opt @prompt_open "pane-prompt-opened:$pane:command"
|
||||
capture | grep -qF '(pane)' || fail "pane prompt not drawn in the pane"
|
||||
status_line | grep -qF '(pane)' && \
|
||||
fail "pane prompt drawn on the status line, not over the pane"
|
||||
$OUT send-keys -l "deep" || exit 1
|
||||
$OUT send-keys Enter || exit 1
|
||||
settle
|
||||
wait_opt @prompt_close "pane-prompt-closed:$pane:command"
|
||||
[ "$(got)" = "deep" ] || fail "pane prompt accept recovered '$(got)', wanted 'deep'"
|
||||
|
||||
# --- 1c. mode-tree.c: search prompt drawn in the pane. ---
|
||||
|
||||
@@ -7,7 +7,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -17,8 +17,8 @@ LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
RESULTS=screen-redraw-results
|
||||
|
||||
TMP=$(mktemp)
|
||||
@@ -42,6 +42,25 @@ compare() {
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_pane() {
|
||||
i=0
|
||||
while [ $i -lt 50 ]; do
|
||||
case "$($TMUX2 capturep -p -t "$1")" in
|
||||
*"$2"*) return ;;
|
||||
esac
|
||||
i=$((i + 1))
|
||||
sleep 0.1
|
||||
done
|
||||
fail "pane $1 did not contain: $2"
|
||||
}
|
||||
|
||||
attach_scene() {
|
||||
# Start with a fresh outer screen and attach only after the complete
|
||||
# scene exists, so it receives one initial redraw.
|
||||
$TMUX respawnp -k "$TMUX2 attach" || exit 1
|
||||
sleep 1
|
||||
}
|
||||
|
||||
new_scene() {
|
||||
$TMUX2 neww -d "sh -c 'printf \"BIDI-LEFT 12345\nBIDI-RIGHT 67890\"; exec sleep 100'" || exit 1
|
||||
$TMUX2 selectw -t:\$ || exit 1
|
||||
@@ -63,17 +82,19 @@ $TMUX new -d -x40 -y12 || exit 1
|
||||
$TMUX set -g status off || exit 1
|
||||
$TMUX set -g window-size manual || exit 1
|
||||
$TMUX set -g default-terminal "tmux-256color" || exit 1
|
||||
$TMUX send -l "$TMUX2 attach" || exit 1
|
||||
$TMUX send Enter || exit 1
|
||||
sleep 1
|
||||
|
||||
# Single pane: content is wrapped in isolates.
|
||||
new_scene
|
||||
wait_for_pane ':.0' 'BIDI-RIGHT 67890'
|
||||
attach_scene
|
||||
compare bidi-single
|
||||
|
||||
# Two panes: borders and both panes are isolated.
|
||||
new_scene
|
||||
$TMUX2 splitw -h "$C" || exit 1
|
||||
wait_for_pane ':.0' 'BIDI-RIGHT 67890'
|
||||
wait_for_pane ':.1' 'BIDI-PANE 67890'
|
||||
attach_scene
|
||||
compare bidi-split
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -18,8 +18,8 @@ LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
RESULTS=screen-redraw-results
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -16,8 +16,8 @@ LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
RESULTS=screen-redraw-results
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -18,8 +18,8 @@ LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
RESULTS=screen-redraw-results
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -18,8 +18,8 @@ LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
RESULTS=screen-redraw-results
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -17,13 +17,19 @@ LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX=
|
||||
TMUX2=
|
||||
SETUP=0
|
||||
RESULTS=screen-redraw-results
|
||||
|
||||
TMP=$(mktemp)
|
||||
trap "rm -f $TMP; $TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null" \
|
||||
0 1 15
|
||||
|
||||
cleanup() {
|
||||
rm -f "$TMP"
|
||||
[ -n "$TMUX" ] && $TMUX kill-server 2>/dev/null
|
||||
[ -n "$TMUX2" ] && $TMUX2 kill-server 2>/dev/null
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
fail() {
|
||||
echo "$*" >&2
|
||||
@@ -46,8 +52,11 @@ C="sh -c 'i=0; while [ \$i -lt 13 ]; do printf \"POP%02d abcdefghij\n\" \$i; i=\
|
||||
|
||||
# setup: fresh inner window attached inside a fresh outer pane, 40x14.
|
||||
setup() {
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
[ -n "$TMUX" ] && $TMUX kill-server 2>/dev/null
|
||||
[ -n "$TMUX2" ] && $TMUX2 kill-server 2>/dev/null
|
||||
SETUP=$((SETUP + 1))
|
||||
TMUX="$TEST_TMUX -LtestA$$-$SETUP -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$-$SETUP -f/dev/null"
|
||||
$TMUX2 new -d -x40 -y14 "$C" || exit 1
|
||||
$TMUX2 set -g status off || exit 1
|
||||
$TMUX2 set -g window-size manual || exit 1
|
||||
|
||||
@@ -16,8 +16,8 @@ LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
RESULTS=screen-redraw-results
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -20,8 +20,8 @@ LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
RESULTS=screen-redraw-results
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -24,8 +24,8 @@ LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
RESULTS=screen-redraw-results
|
||||
|
||||
TMP=$(mktemp)
|
||||
@@ -49,16 +49,17 @@ compare() {
|
||||
fi
|
||||
}
|
||||
|
||||
C="sh -c 'exec sleep 1000'"
|
||||
|
||||
# Fresh inner window of fixed size, with one command running.
|
||||
new_scene() {
|
||||
$TMUX2 selectw -t:0 || exit 1
|
||||
$TMUX2 kill-window -a 2>/dev/null
|
||||
$TMUX2 neww -d "sh -c 'exec sleep 100'" || exit 1
|
||||
$TMUX2 neww -d "$C" || exit 1
|
||||
$TMUX2 selectw -t:\$ || exit 1
|
||||
$TMUX2 resizew -x40 -y14 || exit 1
|
||||
}
|
||||
|
||||
C="sh -c 'exec sleep 100'"
|
||||
|
||||
# Layouts. Each produces one kind of junction. Splits at fixed window size are
|
||||
# deterministic.
|
||||
|
||||
@@ -13,8 +13,8 @@ LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
RESULTS=screen-redraw-results
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -10,7 +10,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP1=$(mktemp)
|
||||
|
||||
347
regress/session-ops.sh
Normal file
347
regress/session-ops.sh
Normal file
@@ -0,0 +1,347 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Tests of session management command semantics, as implemented in
|
||||
# cmd-new-session.c, cmd-rename-session.c, cmd-kill-session.c and
|
||||
# cmd-has-session.c, plus grouped sessions (new-session -t).
|
||||
#
|
||||
# This exercises:
|
||||
# - new-session naming: explicit -s, invalid and duplicate names, automatic
|
||||
# numeric names, -n naming the initial window and -A attaching to (here:
|
||||
# not duplicating) an existing session;
|
||||
# - session_id/session_name/session_windows formats and has-session;
|
||||
# - rename-session, including duplicate and invalid names, and that the
|
||||
# session keeps its id when renamed;
|
||||
# - kill-session, kill-session -a (all but target), the "-f only valid with
|
||||
# -a" guard, and that killing the last session stops the server;
|
||||
# - grouped sessions: new-session -t shares the window list (a window made
|
||||
# in one session appears in the other; killed windows disappear), current
|
||||
# windows are tracked independently and destroying one grouped session
|
||||
# leaves the windows in the other.
|
||||
#
|
||||
# session-group-resize.sh covers sizing of grouped sessions.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LANG=C.UTF-8
|
||||
LC_ALL=C.UTF-8
|
||||
export TERM LANG LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
# check_ok $cmd...
|
||||
#
|
||||
# Run a command and require that it succeeds.
|
||||
check_ok()
|
||||
{
|
||||
if ! $TMUX "$@" </dev/null; then
|
||||
echo "Command failed (expected success): $*"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# check_fail $expected_error $cmd...
|
||||
#
|
||||
# Run a command and require that it fails with the given error message.
|
||||
check_fail()
|
||||
{
|
||||
exp="$1"
|
||||
shift
|
||||
out=$($TMUX "$@" </dev/null 2>&1)
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Command succeeded (expected failure): $*"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$out" != "$exp" ]; then
|
||||
echo "Wrong error for: $*"
|
||||
echo "Expected: '$exp'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# check_fmt $target $format $expected
|
||||
#
|
||||
# Expand a format in a target's context and compare with $expected.
|
||||
check_fmt()
|
||||
{
|
||||
out=$($TMUX display-message -p -t "$1" "$2" 2>&1)
|
||||
if [ "$out" != "$3" ]; then
|
||||
echo "Format '$2' for '$1' wrong."
|
||||
echo "Expected: '$3'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# check_sessions $expected
|
||||
#
|
||||
# Compare the session list (as "name name ...", sorted by name) with
|
||||
# $expected.
|
||||
check_sessions()
|
||||
{
|
||||
out=$(echo $($TMUX list-sessions -F '#{session_name}' | LC_ALL=C sort))
|
||||
if [ "$out" != "$1" ]; then
|
||||
echo "Session list wrong."
|
||||
echo "Expected: '$1'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# check_windows $session $expected
|
||||
#
|
||||
# Compare the window list of a session (as "index:name ...") with $expected.
|
||||
check_windows()
|
||||
{
|
||||
out=$(echo $($TMUX list-windows -t "$1" -F \
|
||||
'#{window_index}:#{window_name}'))
|
||||
if [ "$out" != "$2" ]; then
|
||||
echo "Window list of '$1' wrong."
|
||||
echo "Expected: '$2'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# wait_client_session $expected
|
||||
#
|
||||
# Wait for the only test client to be attached to $expected.
|
||||
wait_client_session()
|
||||
{
|
||||
expected=$1
|
||||
i=0
|
||||
|
||||
while [ $i -lt 30 ]; do
|
||||
out=$($TMUX list-clients -F '#{client_session}' 2>/dev/null ||
|
||||
true)
|
||||
if [ "$out" = "$expected" ]; then
|
||||
return
|
||||
fi
|
||||
i=$((i + 1))
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
echo "Client session wrong."
|
||||
echo "Expected: '$expected'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# attach_control_client $session
|
||||
#
|
||||
# Attach a control client to $session and keep its input open on fd 9.
|
||||
attach_control_client()
|
||||
{
|
||||
fifo=$(mktemp -u)
|
||||
|
||||
mkfifo "$fifo" || exit 1
|
||||
$TMUX -C attach-session -t "$1" <"$fifo" >/dev/null 2>&1 &
|
||||
control_pid=$!
|
||||
exec 9>"$fifo"
|
||||
rm -f "$fifo"
|
||||
wait_client_session "$1"
|
||||
}
|
||||
|
||||
# check_attached_destroy $mode
|
||||
#
|
||||
# Run an attached tmux inside a pane and destroy its only session.
|
||||
check_attached_destroy()
|
||||
{
|
||||
mode=$1
|
||||
outer=DODouter$mode
|
||||
outdir=$(mktemp -d) || exit 1
|
||||
script=$outdir/inner.sh
|
||||
rcfile=$outdir/rc
|
||||
|
||||
cat >"$script" <<-EOF
|
||||
#!/bin/sh
|
||||
"$TEST_TMUX" -LtestInner$$-$mode -f/dev/null new \\; \
|
||||
set -g detach-on-destroy $mode \\; send exit Enter
|
||||
printf '%s\n' \$? >"$rcfile"
|
||||
EOF
|
||||
chmod +x "$script" || exit 1
|
||||
|
||||
check_ok new-session -d -s "$outer" -x 80 -y 24
|
||||
check_ok send-keys -t "$outer:0.0" "sh $script" Enter
|
||||
|
||||
i=0
|
||||
while [ $i -lt 50 ]; do
|
||||
[ -f "$rcfile" ] && break
|
||||
i=$((i + 1))
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
pane=$($TMUX capture-pane -pt "$outer:0.0" -S -)
|
||||
if [ ! -f "$rcfile" ]; then
|
||||
echo "Inner tmux did not exit."
|
||||
echo "$pane"
|
||||
$TMUX kill-session -t "$outer" 2>/dev/null || true
|
||||
rm -rf "$outdir"
|
||||
exit 1
|
||||
fi
|
||||
rc=$(cat "$rcfile")
|
||||
if [ "$rc" != 0 ]; then
|
||||
echo "Inner tmux exited with status $rc."
|
||||
echo "$pane"
|
||||
$TMUX kill-session -t "$outer" 2>/dev/null || true
|
||||
rm -rf "$outdir"
|
||||
exit 1
|
||||
fi
|
||||
case "$pane" in
|
||||
*"server exited unexpectedly"*)
|
||||
echo "Inner tmux server crashed."
|
||||
echo "$pane"
|
||||
$TMUX kill-session -t "$outer" 2>/dev/null || true
|
||||
rm -rf "$outdir"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
check_ok kill-session -t "$outer"
|
||||
rm -rf "$outdir"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# new-session and has-session.
|
||||
|
||||
check_ok new-session -d -s S1 -x 80 -y 24 -n first
|
||||
check_fmt 'S1:' '#{session_name}:#{window_name}:#{session_windows}' \
|
||||
'S1:first:1'
|
||||
|
||||
# A duplicate name is an error. Only invalid UTF-8 is rejected as a name:
|
||||
# colons, periods and even an empty string are allowed (such sessions can
|
||||
# only be targeted by id).
|
||||
check_fail 'duplicate session: S1' new-session -d -s S1
|
||||
badname=$(printf 'a\377b')
|
||||
check_fail "invalid session name: $badname" new-session -d -s "$badname"
|
||||
oddid=$($TMUX new-session -d -s 'a:b.c' -x 80 -y 24 -P -F '#{session_id}')
|
||||
check_fmt "$oddid" '#{session_name}' 'a:b.c'
|
||||
check_ok kill-session -t "$oddid"
|
||||
emptyid=$($TMUX new-session -d -s '' -x 80 -y 24 -P -F '#{session_id}')
|
||||
check_fmt "$emptyid" '#{session_name}' ''
|
||||
check_ok kill-session -t "$emptyid"
|
||||
|
||||
# Without -s, sessions get a numeric name matching their id counter.
|
||||
autoname=$($TMUX new-session -d -x 80 -y 24 -P -F '#{session_name}')
|
||||
autoid=$($TMUX display-message -p -t "=$autoname:" '#{session_id}')
|
||||
if [ "\$$autoname" != "$autoid" ]; then
|
||||
echo "Automatic session name '$autoname' does not match id '$autoid'."
|
||||
exit 1
|
||||
fi
|
||||
check_ok has-session -t "=$autoname"
|
||||
check_ok rename-session -t "=$autoname" S2
|
||||
check_sessions 'S1 S2'
|
||||
|
||||
# -A creates the session only if it does not exist; if it does, -A means
|
||||
# attach, which a detached client without a terminal cannot do.
|
||||
check_ok new-session -d -A -s S3
|
||||
check_sessions 'S1 S2 S3'
|
||||
check_fail 'open terminal failed: not a terminal' new-session -d -A -s S3
|
||||
check_sessions 'S1 S2 S3'
|
||||
check_ok kill-session -t S3
|
||||
|
||||
# has-session fails for a missing session.
|
||||
check_fail "can't find session: nosuch" has-session -t nosuch
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# rename-session.
|
||||
|
||||
# The id survives a rename and the old name is gone.
|
||||
id=$($TMUX display-message -p -t S2: '#{session_id}')
|
||||
check_ok rename-session -t S2 newname
|
||||
check_sessions 'S1 newname'
|
||||
check_fmt "$id" '#{session_name}' 'newname'
|
||||
check_fail "can't find session: S2" has-session -t S2
|
||||
|
||||
# Renaming to an existing or invalid name is an error.
|
||||
check_fail 'duplicate session: S1' rename-session -t newname S1
|
||||
check_fail "invalid session name: $badname" rename-session -t newname \
|
||||
"$badname"
|
||||
check_ok rename-session -t newname S2
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# grouped sessions (new-session -t).
|
||||
|
||||
check_ok new-session -d -s G1 -x 80 -y 24 -n shared
|
||||
check_ok new-session -d -s G2 -t G1
|
||||
check_fmt 'G1:' '#{session_grouped}:#{session_group_size}' '1:2'
|
||||
check_fmt 'G2:' '#{session_grouped}:#{session_group_list}' '1:G1,G2'
|
||||
|
||||
# The window list is shared: windows created or killed in one session
|
||||
# appear and disappear in the other.
|
||||
check_ok new-window -d -t G2: -n added
|
||||
check_windows G1 '0:shared 1:added'
|
||||
check_windows G2 '0:shared 1:added'
|
||||
check_ok kill-window -t G1:added
|
||||
check_windows G2 '0:shared'
|
||||
|
||||
# The current window is tracked per session.
|
||||
check_ok new-window -d -t G2:1 -n other
|
||||
check_ok select-window -t G1:0
|
||||
check_ok select-window -t G2:1
|
||||
check_fmt 'G1:' '#{window_name}' 'shared'
|
||||
check_fmt 'G2:' '#{window_name}' 'other'
|
||||
|
||||
# Killing one grouped session leaves the windows in the other (the group
|
||||
# itself survives with a single member).
|
||||
check_ok kill-session -t G2
|
||||
check_windows G1 '0:shared 1:other'
|
||||
check_fmt 'G1:' '#{session_grouped}:#{session_group_size}' '1:1'
|
||||
check_ok kill-window -t G1:other
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# kill-session.
|
||||
|
||||
check_sessions 'G1 S1 S2'
|
||||
check_fail '-f only valid with -a' kill-session -f 'x' -t S1
|
||||
|
||||
# -C only clears alerts; the session survives.
|
||||
check_ok kill-session -C -t S2
|
||||
check_ok has-session -t S2
|
||||
|
||||
# detach-on-destroy previous and next move attached clients in
|
||||
# alphabetical order and must not crash when a session is destroyed.
|
||||
check_ok new-session -d -s DODa -x 80 -y 24
|
||||
check_ok new-session -d -s DODb -x 80 -y 24
|
||||
check_ok new-session -d -s DODc -x 80 -y 24
|
||||
check_ok set-option -t DODb detach-on-destroy previous
|
||||
attach_control_client DODb
|
||||
check_ok kill-session -t DODb
|
||||
wait_client_session DODa
|
||||
exec 9>&-
|
||||
wait "$control_pid" 2>/dev/null || true
|
||||
check_ok kill-session -t DODa
|
||||
check_ok kill-session -t DODc
|
||||
|
||||
check_ok new-session -d -s DODa -x 80 -y 24
|
||||
check_ok new-session -d -s DODb -x 80 -y 24
|
||||
check_ok new-session -d -s DODc -x 80 -y 24
|
||||
check_ok set-option -t DODb detach-on-destroy next
|
||||
attach_control_client DODb
|
||||
check_ok kill-session -t DODb
|
||||
wait_client_session DODc
|
||||
exec 9>&-
|
||||
wait "$control_pid" 2>/dev/null || true
|
||||
check_ok kill-session -t DODa
|
||||
check_ok kill-session -t DODc
|
||||
|
||||
# With only one session, previous and next have no replacement session; the
|
||||
# attached client must exit cleanly rather than being moved to the dying
|
||||
# session.
|
||||
check_attached_destroy previous
|
||||
check_attached_destroy next
|
||||
|
||||
# -a kills every other session.
|
||||
check_ok kill-session -a -t S1
|
||||
check_sessions 'S1'
|
||||
|
||||
# Killing the last session stops the server.
|
||||
check_ok kill-session -t S1
|
||||
if $TMUX has-session -t S1 2>/dev/null; then
|
||||
echo "Server still up after killing the last session."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
160
regress/set-hook-B.sh
Normal file
160
regress/set-hook-B.sh
Normal file
@@ -0,0 +1,160 @@
|
||||
#!/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)
|
||||
OUT=$(mktemp -d)
|
||||
TMUX_TMPDIR="$OUT"
|
||||
export TMUX_TMPDIR
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
wait_for()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 30 ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] && return 0
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
fail "expected $option to be '$expected' but got '$value'"
|
||||
}
|
||||
|
||||
assert_unchanged()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 15 ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] || \
|
||||
fail "expected $option to remain '$expected' but got '$value'"
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
}
|
||||
|
||||
$TMUX new -d -s one || fail "new-session failed"
|
||||
|
||||
$TMUX set -g @seen 0 || fail "set @seen failed"
|
||||
$TMUX set-hook -g -B '@session-name::#{session_name}' \
|
||||
'set -g @seen "#{hook}:#{hook_value}"' ||
|
||||
fail "set-hook -B failed"
|
||||
shown=$($TMUX show-hooks -g -B @session-name) ||
|
||||
fail "show-hooks -B failed"
|
||||
[ "$shown" = '@session-name::#{session_name}' ] ||
|
||||
fail "unexpected show-hooks -B output: $shown"
|
||||
assert_unchanged @seen 0
|
||||
|
||||
$TMUX rename-session two || fail "rename-session two failed"
|
||||
wait_for @seen '@session-name:two'
|
||||
|
||||
$TMUX set -g @seen-last 0 || fail "set @seen-last failed"
|
||||
$TMUX set-hook -g -B '@session-name::#{session_name}' \
|
||||
'set -g @seen-last "#{hook_last}->#{hook_value}"' ||
|
||||
fail "set-hook -B replacement failed"
|
||||
assert_unchanged @seen-last 0
|
||||
$TMUX rename-session one || fail "rename-session one failed"
|
||||
wait_for @seen-last 'two->one'
|
||||
|
||||
$TMUX set-hook -gu -B @session-name || fail "set-hook -gu -B failed"
|
||||
shown=$($TMUX show-hooks -g -B @session-name) ||
|
||||
fail "show-hooks -B after remove failed"
|
||||
[ -z "$shown" ] || fail "show-hooks -B showed removed monitor: $shown"
|
||||
last=$($TMUX show -gqv @seen-last)
|
||||
$TMUX rename-session three || fail "rename-session three failed"
|
||||
assert_unchanged @seen-last "$last"
|
||||
|
||||
$TMUX set -gu @value || fail "unset @value failed"
|
||||
$TMUX set -g @empty-seen 0 || fail "set @empty-seen failed"
|
||||
$TMUX set-hook -g -B '@empty::#{@value}' \
|
||||
'set -g @empty-seen "#{hook_last}->#{hook_value}"' ||
|
||||
fail "set-hook -B empty failed"
|
||||
assert_unchanged @empty-seen 0
|
||||
$TMUX set -g @value changed || fail "set @value failed"
|
||||
wait_for @empty-seen '->changed'
|
||||
|
||||
if $TMUX set-hook -g -B 'bad::#{session_name}' 'display-message x' \
|
||||
>"$OUT/bad.out" 2>"$OUT/bad.err"; then
|
||||
fail "non-@ monitor hook name was accepted"
|
||||
fi
|
||||
|
||||
session=$($TMUX display -p '#{session_id}')
|
||||
window=$($TMUX display -p '#{window_id}')
|
||||
pane=$($TMUX display -p '#{pane_id}')
|
||||
pane_number=${pane#%}
|
||||
|
||||
$TMUX set -gu @pane-value || fail "unset @pane-value failed"
|
||||
$TMUX set -g @pane-seen 0 || fail "set @pane-seen failed"
|
||||
$TMUX set-hook -g -B "@pane:%$pane_number:#{pane_width}" \
|
||||
'set -g @pane-seen "#{hook_session}:#{hook_window}:#{hook_window_index}:#{hook_pane}:#{hook_value}"' ||
|
||||
fail "set-hook -B pane selector failed"
|
||||
assert_unchanged @pane-seen 0
|
||||
$TMUX set-hook -g -B "@pane:%$pane_number:#{@pane-value}" \
|
||||
'set -g @pane-seen "#{hook_session}:#{hook_window}:#{hook_window_index}:#{hook_pane}:#{hook_value}"' ||
|
||||
fail "set-hook -B pane replacement failed"
|
||||
assert_unchanged @pane-seen 0
|
||||
$TMUX set -g @pane-value changed || fail "set @pane-value failed"
|
||||
wait_for @pane-seen "$session:$window:0:$pane:changed"
|
||||
|
||||
$TMUX set -g @exact-value one || fail "set @exact-value failed"
|
||||
$TMUX set -g @exact-seen 0 || fail "set @exact-seen failed"
|
||||
$TMUX set -gw @foo 'set -g @exact-seen inherited' ||
|
||||
fail "set global @foo failed"
|
||||
$TMUX set-hook -w -B '@foo::#{@exact-value}' ||
|
||||
fail "set-hook -B exact scope monitor failed"
|
||||
assert_unchanged @exact-seen 0
|
||||
$TMUX set -g @exact-value two || fail "set @exact-value two failed"
|
||||
assert_unchanged @exact-seen 0
|
||||
$TMUX set-hook -w -B '@foo::#{@exact-value}' \
|
||||
'set -g @exact-seen "#{hook_value}"' ||
|
||||
fail "set-hook -B exact scope command failed"
|
||||
assert_unchanged @exact-seen 0
|
||||
$TMUX set -g @exact-value three || fail "set @exact-value three failed"
|
||||
wait_for @exact-seen three
|
||||
|
||||
target_pane=$($TMUX splitw -P -F '#{pane_id}') ||
|
||||
fail "split-window failed"
|
||||
$TMUX set -g @target-pane 0 || fail "set @target-pane failed"
|
||||
$TMUX set-hook -g -B '@target:%*:#{@target-value}' \
|
||||
'set -g @target-pane "#{pane_id}"' ||
|
||||
fail "set-hook -B target pane failed"
|
||||
assert_unchanged @target-pane 0
|
||||
$TMUX set -pt "$target_pane" @target-value changed ||
|
||||
fail "set pane @target-value failed"
|
||||
wait_for @target-pane "$target_pane"
|
||||
|
||||
$TMUX new -d -s zzz-survivor || fail "new survivor session failed"
|
||||
$TMUX set -g @global-after-destroy 0 ||
|
||||
fail "set @global-after-destroy failed"
|
||||
$TMUX set-hook -g -t three -B '@global-after-destroy-session::#{session_name}' \
|
||||
'set -g @global-after-destroy "#{hook_last}->#{hook_value}"' ||
|
||||
fail "set-hook -B global after destroy failed"
|
||||
assert_unchanged @global-after-destroy 0
|
||||
$TMUX kill-session -t three || fail "kill destroyed monitor session failed"
|
||||
wait_for @global-after-destroy 'three->zzz-survivor'
|
||||
|
||||
exit 0
|
||||
150
regress/set-hook-E.sh
Executable file
150
regress/set-hook-E.sh
Executable file
@@ -0,0 +1,150 @@
|
||||
#!/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)
|
||||
OUT=$(mktemp -d)
|
||||
TMUX_TMPDIR="$OUT"
|
||||
export TMUX_TMPDIR
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
wait_channel()
|
||||
{
|
||||
channel=$1
|
||||
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
timeout 10 $TMUX wait-for "$channel" ||
|
||||
fail "wait-for $channel timed out"
|
||||
return
|
||||
fi
|
||||
|
||||
$TMUX wait-for "$channel" &
|
||||
pid=$!
|
||||
i=0
|
||||
while kill -0 "$pid" 2>/dev/null; do
|
||||
[ $i -lt 50 ] || {
|
||||
kill "$pid" 2>/dev/null || true
|
||||
fail "wait-for $channel timed out"
|
||||
}
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
wait "$pid" || fail "wait-for $channel failed"
|
||||
}
|
||||
|
||||
wait_for()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 30 ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] && return 0
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
fail "expected $option to be '$expected' but got '$value'"
|
||||
}
|
||||
|
||||
assert_unchanged()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
count=${3:-15}
|
||||
i=0
|
||||
|
||||
while [ $i -lt "$count" ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] ||
|
||||
fail "expected $option to remain '$expected' but got '$value'"
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
}
|
||||
|
||||
wait_list()
|
||||
{
|
||||
name=$1
|
||||
i=0
|
||||
|
||||
while [ $i -lt 50 ]; do
|
||||
value=$($TMUX wait-for -E -l "$name" 2>/dev/null || true)
|
||||
if [ -n "$value" ]; then
|
||||
printf '%s\n' "$value" | sed -n '1p'
|
||||
return
|
||||
fi
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
fail "wait-for -E -l $name found no waiters"
|
||||
}
|
||||
|
||||
$TMUX new -d -s one || fail "new-session one failed"
|
||||
$TMUX new -d -s two || fail "new-session two failed"
|
||||
|
||||
$TMUX set -g @event_seen 0 || fail "set @event_seen failed"
|
||||
$TMUX wait-for -E @manual-event \; set -g @event_seen 1 \; \
|
||||
wait-for -S she-event &
|
||||
event_pid=$!
|
||||
wait_list @manual-event >/dev/null
|
||||
|
||||
$TMUX set-hook -E @manual-event || fail "set-hook -E @manual-event failed"
|
||||
wait_channel she-event
|
||||
wait "$event_pid" || fail "wait-for -E @manual-event failed"
|
||||
wait_for @event_seen 1
|
||||
|
||||
$TMUX set-hook -E @no-sink || fail "set-hook -E @no-sink failed"
|
||||
|
||||
pane=$($TMUX display -pt two:0.0 '#{pane_id}') ||
|
||||
fail "display-message pane failed"
|
||||
$TMUX set -g @hook_seen 0 || fail "set @hook_seen failed"
|
||||
$TMUX set-hook -g @manual-hook \
|
||||
'set -gF @hook_seen "#{hook}:#{session_name}:#{window_index}:#{pane_id}"' ||
|
||||
fail "set-hook @manual-hook failed"
|
||||
|
||||
$TMUX set-hook -E -t two:0.0 @manual-hook ||
|
||||
fail "set-hook -E @manual-hook failed"
|
||||
wait_for @hook_seen "@manual-hook:two:0:$pane"
|
||||
|
||||
$TMUX set -g @r_hook 0 || fail "set @r_hook failed"
|
||||
$TMUX set -g @r_event 0 || fail "set @r_event failed"
|
||||
$TMUX set-hook -g @manual-r 'set -g @r_hook 1' ||
|
||||
fail "set-hook @manual-r failed"
|
||||
$TMUX wait-for -E @manual-r \; set -g @r_event 1 \; wait-for -S she-r &
|
||||
r_pid=$!
|
||||
r_client=$(wait_list @manual-r)
|
||||
|
||||
$TMUX set-hook -R @manual-r || fail "set-hook -R @manual-r failed"
|
||||
wait_for @r_hook 1
|
||||
assert_unchanged @r_event 0 5
|
||||
|
||||
$TMUX wait-for -E -w "$r_client" @manual-r ||
|
||||
fail "wait-for -E -w @manual-r failed"
|
||||
wait_channel she-r
|
||||
wait "$r_pid" || fail "wait-for -E @manual-r failed"
|
||||
|
||||
if $TMUX set-hook -E window-renamed 2>/dev/null; then
|
||||
fail "set-hook -E window-renamed succeeded"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
63
regress/set-hook-R.sh
Normal file
63
regress/set-hook-R.sh
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/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)
|
||||
OUT=$(mktemp -d)
|
||||
TMUX_TMPDIR="$OUT"
|
||||
export TMUX_TMPDIR
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
wait_for()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 30 ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] && return 0
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
fail "expected $option to be '$expected' but got '$value'"
|
||||
}
|
||||
|
||||
$TMUX new -d -s one || fail "new-session one failed"
|
||||
$TMUX new -d -s two || fail "new-session two failed"
|
||||
|
||||
pane=$($TMUX display -pt two:0.0 '#{pane_id}') ||
|
||||
fail "display-message pane failed"
|
||||
|
||||
$TMUX set -g @seen 0 || fail "set @seen failed"
|
||||
$TMUX set-hook -g @manual \
|
||||
'set -gF @seen "#{hook}:#{session_name}:#{window_index}:#{pane_id}"' ||
|
||||
fail "set-hook @manual failed"
|
||||
|
||||
[ "$($TMUX show -gqv @seen)" = 0 ] ||
|
||||
fail "hook ran before set-hook -R"
|
||||
|
||||
$TMUX set-hook -g -R -t two:0.0 @manual ||
|
||||
fail "set-hook -R @manual failed"
|
||||
wait_for @seen "@manual:two:0:$pane"
|
||||
|
||||
exit 0
|
||||
@@ -17,9 +17,9 @@ if command -v bash >/dev/null 2>&1; then
|
||||
fi
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
$TMUX2 -f/dev/null new -d "$TMUX -f/dev/null new -- $shell"
|
||||
|
||||
@@ -27,7 +27,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
# check $target $expected [format]
|
||||
|
||||
@@ -29,7 +29,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
# check $target $expected [format]
|
||||
|
||||
57
regress/theme-report.sh
Executable file
57
regress/theme-report.sh
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Test that the theme reported to a pane (mode 2031 / DSR 996) follows the
|
||||
# terminal's reported theme, not a guess from the background colour.
|
||||
#
|
||||
# An inner client is attached inside an outer tmux pane. The outer pane has a
|
||||
# light background, so the outer server answers the inner client's DSR 996 with
|
||||
# light. The inner server is given a dark window background, so a guess from the
|
||||
# background colour would say dark. A pane in the inner server then queries DSR
|
||||
# 996: the answer must be light (2), following the client's reported theme, not
|
||||
# dark (1) from the background.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
INNER="$TEST_TMUX -LtestI$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -LtestO$$ -f/dev/null"
|
||||
$INNER kill-server 2>/dev/null
|
||||
$OUTER kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
trap "rm -f $TMP; $INNER kill-server 2>/dev/null; $OUTER kill-server 2>/dev/null" \
|
||||
0 1 15
|
||||
|
||||
# Inner server, dark background, keep panes alive across the query respawn.
|
||||
$INNER new-session -d -x80 -y24 || exit 1
|
||||
$INNER set -g remain-on-exit on
|
||||
$INNER set -g window-style 'bg=black'
|
||||
|
||||
# Outer server with a light background, running the inner client attached so the
|
||||
# inner client has a real terminal that reports a theme.
|
||||
$OUTER new-session -d -x80 -y24 || exit 1
|
||||
$OUTER set -g window-style 'bg=white'
|
||||
$OUTER new-window "$INNER attach" || exit 1
|
||||
sleep 2
|
||||
|
||||
# Query DSR 996 from an inner pane and capture the CSI ? 997 ; Ps n reply.
|
||||
$INNER respawnw -k -t:0 -- sh -c "
|
||||
exec 2>/dev/null
|
||||
stty raw -echo
|
||||
printf '\033[?996n'
|
||||
dd bs=1 count=9 2>/dev/null | cat -v > $TMP
|
||||
sleep 1
|
||||
"
|
||||
sleep 2
|
||||
|
||||
actual=$(cat "$TMP")
|
||||
expected='^[[?997;2n' # 2 = light (from the terminal), not 1 = dark (from bg)
|
||||
|
||||
if [ "$actual" = "$expected" ]; then
|
||||
[ -n "$VERBOSE" ] && echo "[PASS] reported terminal theme ($actual)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "[FAIL] expected '$expected' (light, from terminal), got '$actual'"
|
||||
exit 1
|
||||
@@ -10,8 +10,8 @@ LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
fail() {
|
||||
echo "$*" >&2
|
||||
|
||||
@@ -4,9 +4,9 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMP=$(mktemp)
|
||||
trap "rm -f $TMP" 0 1 15
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
206
regress/wait-for-E.sh
Normal file
206
regress/wait-for-E.sh
Normal file
@@ -0,0 +1,206 @@
|
||||
#!/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)
|
||||
OUT=$(mktemp -d)
|
||||
TMUX_TMPDIR="$OUT"
|
||||
export TMUX_TMPDIR
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
wait_channel()
|
||||
{
|
||||
channel=$1
|
||||
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
timeout 10 $TMUX wait-for "$channel" ||
|
||||
fail "wait-for $channel timed out"
|
||||
return
|
||||
fi
|
||||
|
||||
$TMUX wait-for "$channel" &
|
||||
pid=$!
|
||||
i=0
|
||||
while kill -0 "$pid" 2>/dev/null; do
|
||||
[ $i -lt 50 ] || {
|
||||
kill "$pid" 2>/dev/null || true
|
||||
fail "wait-for $channel timed out"
|
||||
}
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
wait "$pid" || fail "wait-for $channel failed"
|
||||
}
|
||||
|
||||
assert_unchanged()
|
||||
{
|
||||
option=$1
|
||||
expected=$2
|
||||
count=${3:-15}
|
||||
i=0
|
||||
|
||||
while [ $i -lt "$count" ]; do
|
||||
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
|
||||
[ "$value" = "$expected" ] ||
|
||||
fail "expected $option to remain '$expected' but got '$value'"
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
}
|
||||
|
||||
wait_list()
|
||||
{
|
||||
event=$1
|
||||
name=$2
|
||||
i=0
|
||||
|
||||
while [ $i -lt 50 ]; do
|
||||
if [ "$event" = 1 ]; then
|
||||
value=$($TMUX wait-for -E -l "$name" 2>/dev/null || true)
|
||||
else
|
||||
value=$($TMUX wait-for -l "$name" 2>/dev/null || true)
|
||||
fi
|
||||
if [ -n "$value" ]; then
|
||||
printf '%s\n' "$value" | sed -n '1p'
|
||||
return
|
||||
fi
|
||||
i=$((i + 1))
|
||||
sleep 0.2
|
||||
done
|
||||
fail "wait-for -l $name found no waiters"
|
||||
}
|
||||
|
||||
$TMUX new -d -s wf || fail "new-session failed"
|
||||
|
||||
$TMUX wait-for wf-list &
|
||||
list_pid=$!
|
||||
client=$(wait_list 0 wf-list)
|
||||
$TMUX wait-for -w "$client" wf-list || fail "wait-for -w wf-list failed"
|
||||
wait "$list_pid" || fail "wait-for -w did not wake channel waiter"
|
||||
|
||||
$TMUX set -g @forced 0 || fail "set @forced failed"
|
||||
$TMUX wait-for -E @forced-event \; set -g @forced 1 &
|
||||
forced_pid=$!
|
||||
client=$(wait_list 1 @forced-event)
|
||||
$TMUX wait-for -E -w "$client" @forced-event ||
|
||||
fail "wait-for -E -w @forced-event failed"
|
||||
wait "$forced_pid" || fail "wait-for -E -w did not wake event waiter"
|
||||
[ "$($TMUX show -gqv @forced)" = 1 ] ||
|
||||
fail "wait-for -E -w did not continue event waiter"
|
||||
|
||||
if $TMUX wait-for -E foobar 2>/dev/null; then
|
||||
fail "wait-for -E accepted invalid event"
|
||||
fi
|
||||
|
||||
$TMUX wait-for -E @not-yet-fired &
|
||||
not_yet_pid=$!
|
||||
client=$(wait_list 1 @not-yet-fired)
|
||||
$TMUX wait-for -E -w "$client" @not-yet-fired ||
|
||||
fail "wait-for -E -w @not-yet-fired failed"
|
||||
wait "$not_yet_pid" || fail "wait-for -E @not-yet-fired failed"
|
||||
|
||||
$TMUX set -g @wf_value 0 || fail "set @wf_value failed"
|
||||
$TMUX set-hook -g -B '@wf::#{@wf_value}' 'wait-for -S wf-hook' ||
|
||||
fail "set-hook -B failed"
|
||||
|
||||
$TMUX wait-for -E @wf \; wait-for -S wf-event &
|
||||
event_pid=$!
|
||||
|
||||
# Let the monitor take its first sample so the next change is reported.
|
||||
sleep 1.5
|
||||
|
||||
$TMUX set -g @wf_value 1 || fail "set @wf_value 1 failed"
|
||||
|
||||
wait_channel wf-event
|
||||
wait_channel wf-hook
|
||||
wait "$event_pid" || fail "wait-for -E command failed"
|
||||
|
||||
$TMUX set -g @late 0 || fail "set @late failed"
|
||||
$TMUX wait-for -E @wf \; set -g @late 1 \; wait-for -S wf-late &
|
||||
late_pid=$!
|
||||
assert_unchanged @late 0 5
|
||||
|
||||
$TMUX set -g @wf_value 2 || fail "set @wf_value 2 failed"
|
||||
wait_channel wf-late
|
||||
wait "$late_pid" || fail "late wait-for -E command failed"
|
||||
|
||||
$TMUX set -g @filtered 0 || fail "set @filtered failed"
|
||||
$TMUX wait-for -E -F '#{==:#{value},3}' @wf \; set -g @filtered 1 \; \
|
||||
wait-for -S wf-filtered &
|
||||
filtered_pid=$!
|
||||
assert_unchanged @filtered 0 5
|
||||
|
||||
$TMUX set -g @wf_value unmatched || fail "set @wf_value unmatched failed"
|
||||
assert_unchanged @filtered 0 5
|
||||
|
||||
$TMUX set -g @wf_value 3 || fail "set @wf_value 3 failed"
|
||||
wait_channel wf-filtered
|
||||
wait "$filtered_pid" || fail "filtered wait-for -E command failed"
|
||||
|
||||
verbose_file="$OUT/verbose"
|
||||
$TMUX wait-for -E -v @wf \; wait-for -S wf-verbose >"$verbose_file" &
|
||||
verbose_pid=$!
|
||||
|
||||
sleep 0.5
|
||||
$TMUX set -g @wf_value 4 || fail "set @wf_value 4 failed"
|
||||
wait_channel wf-verbose
|
||||
wait "$verbose_pid" || fail "verbose wait-for -E command failed"
|
||||
grep '^event=@wf$' "$verbose_file" >/dev/null ||
|
||||
fail "verbose wait-for -E did not print event payload"
|
||||
grep '^value=4$' "$verbose_file" >/dev/null ||
|
||||
fail "verbose wait-for -E did not print value payload"
|
||||
grep '^_hook_monitor=' "$verbose_file" >/dev/null &&
|
||||
fail "verbose wait-for -E printed private payload"
|
||||
|
||||
$TMUX new -d -s wf2 || fail "new-session wf2 failed"
|
||||
|
||||
$TMUX wait-for -E window-renamed \; wait-for -S wf-renamed &
|
||||
renamed_pid=$!
|
||||
|
||||
sleep 0.5
|
||||
$TMUX rename-window -t wf2:0 renamed || fail "rename-window failed"
|
||||
wait_channel wf-renamed
|
||||
wait "$renamed_pid" || fail "wait-for -E window-renamed failed"
|
||||
|
||||
$TMUX set-hook -g window-renamed 'wait-for -S wf-hook-renamed' ||
|
||||
fail "set-hook window-renamed failed"
|
||||
$TMUX rename-window -t wf2:0 renamed-again ||
|
||||
fail "rename-window renamed-again failed"
|
||||
wait_channel wf-hook-renamed
|
||||
|
||||
$TMUX set -g @builtin_filtered 0 || fail "set @builtin_filtered failed"
|
||||
target=$($TMUX splitw -d -t wf2:0 -P -F '#{pane_id}' 'sleep 30') ||
|
||||
fail "split-window target failed"
|
||||
$TMUX wait-for -E -F "#{==:#{pane},$target}" pane-exited \; \
|
||||
set -g @builtin_filtered 1 \; wait-for -S wf-builtin-filtered &
|
||||
builtin_filtered_pid=$!
|
||||
assert_unchanged @builtin_filtered 0 5
|
||||
|
||||
$TMUX splitw -d -t wf2:0 'true' || fail "split-window nonmatching failed"
|
||||
assert_unchanged @builtin_filtered 0 5
|
||||
|
||||
$TMUX send-keys -t "$target" C-c || fail "send C-c to target failed"
|
||||
wait_channel wf-builtin-filtered
|
||||
wait "$builtin_filtered_pid" ||
|
||||
fail "filtered wait-for -E pane-exited command failed"
|
||||
|
||||
exit 0
|
||||
406
regress/window-ops.sh
Normal file
406
regress/window-ops.sh
Normal file
@@ -0,0 +1,406 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Tests of window management command semantics (not parsing), as implemented
|
||||
# in cmd-new-window.c, cmd-move-window.c (move-window and link-window),
|
||||
# cmd-unlink-window.c, cmd-swap-window.c, cmd-rotate-window.c,
|
||||
# cmd-kill-window.c and cmd-select-window.c.
|
||||
#
|
||||
# This exercises:
|
||||
# - new-window placement: next free index, explicit index, index in use with
|
||||
# and without -k, -a (after) and -b (before) insertion with shuffling, and
|
||||
# -S selecting an existing window by name instead of creating;
|
||||
# - move-window to a free index, to an occupied index with and without -k,
|
||||
# -a insertion and -r renumbering (including base-index);
|
||||
# - renumber-windows closing gaps;
|
||||
# - link-window sharing a window between two sessions (window_linked and
|
||||
# window_linked_sessions), unlink-window removing one link and refusing to
|
||||
# unlink the last link without -k;
|
||||
# - swap-window within and between sessions, -d keeping the active window,
|
||||
# and the grouped-sessions error;
|
||||
# - rotate-window -U/-D rotating pane positions;
|
||||
# - kill-window switching to the last (previously current) window, kill-window
|
||||
# -a killing all other windows and the "-f only valid with -a" guard.
|
||||
#
|
||||
# pane-ops.sh covers pane-level commands and buffers.sh covers paste buffers.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LANG=C.UTF-8
|
||||
LC_ALL=C.UTF-8
|
||||
export TERM LANG LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
# check_ok $cmd...
|
||||
#
|
||||
# Run a command and require that it succeeds.
|
||||
check_ok()
|
||||
{
|
||||
if ! $TMUX "$@"; then
|
||||
echo "Command failed (expected success): $*"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# check_fail $expected_error $cmd...
|
||||
#
|
||||
# Run a command and require that it fails with the given error message.
|
||||
check_fail()
|
||||
{
|
||||
exp="$1"
|
||||
shift
|
||||
out=$($TMUX "$@" 2>&1)
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Command succeeded (expected failure): $*"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$out" != "$exp" ]; then
|
||||
echo "Wrong error for: $*"
|
||||
echo "Expected: '$exp'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# check_windows $session $expected
|
||||
#
|
||||
# Compare the window list of a session (as "index:name index:name ...", in
|
||||
# index order) with $expected.
|
||||
check_windows()
|
||||
{
|
||||
out=$(echo $($TMUX list-windows -t "$1" -F \
|
||||
'#{window_index}:#{window_name}'))
|
||||
if [ "$out" != "$2" ]; then
|
||||
echo "Window list of '$1' wrong."
|
||||
echo "Expected: '$2'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# check_fmt $target $format $expected
|
||||
#
|
||||
# Expand a format in a target's context and compare with $expected.
|
||||
check_fmt()
|
||||
{
|
||||
out=$($TMUX display-message -p -t "$1" "$2" 2>&1)
|
||||
if [ "$out" != "$3" ]; then
|
||||
echo "Format '$2' for '$1' wrong."
|
||||
echo "Expected: '$3'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_alive()
|
||||
{
|
||||
if [ "$($TMUX display-message -p alive 2>&1)" != "alive" ]; then
|
||||
echo "Server died: $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# new-window placement.
|
||||
|
||||
check_ok new-session -d -s W -x 80 -y 24 -n w0
|
||||
|
||||
# Next free index.
|
||||
check_ok new-window -d -t W: -n w1
|
||||
check_ok new-window -d -t W: -n w2
|
||||
check_windows W '0:w0 1:w1 2:w2'
|
||||
|
||||
# Explicit index, then the next new window fills the first free index, not
|
||||
# one past the highest.
|
||||
check_ok new-window -d -t W:9 -n w9
|
||||
check_ok new-window -d -t W: -n w3
|
||||
check_windows W '0:w0 1:w1 2:w2 3:w3 9:w9'
|
||||
|
||||
# Occupied index fails without -k and replaces with -k.
|
||||
check_fail 'create window failed: index 9 in use' \
|
||||
new-window -d -t W:9 -n dup
|
||||
check_ok new-window -d -k -t W:9 -n w9k
|
||||
check_windows W '0:w0 1:w1 2:w2 3:w3 9:w9k'
|
||||
|
||||
# -a inserts after the target, shuffling the following windows up.
|
||||
check_ok new-window -d -a -t W:1 -n wA
|
||||
check_windows W '0:w0 1:w1 2:wA 3:w2 4:w3 9:w9k'
|
||||
|
||||
# -b inserts before the target, shuffling the target and followers up.
|
||||
check_ok new-window -d -b -t W:0 -n wB
|
||||
check_windows W '0:wB 1:w0 2:w1 3:wA 4:w2 5:w3 9:w9k'
|
||||
|
||||
# -S selects an existing window with the same name instead of creating (with
|
||||
# -d it would not switch, so no -d here).
|
||||
check_ok select-window -t W:0
|
||||
check_ok new-window -S -t W: -n w3
|
||||
check_windows W '0:wB 1:w0 2:w1 3:wA 4:w2 5:w3 9:w9k'
|
||||
check_fmt 'W:' '#{window_index}:#{window_name}' '5:w3'
|
||||
|
||||
# Clean up to a known arrangement.
|
||||
check_ok kill-window -t W:wB
|
||||
check_ok kill-window -t W:wA
|
||||
check_ok kill-window -t W:w9k
|
||||
check_ok move-window -r -t W:
|
||||
check_windows W '0:w0 1:w1 2:w2 3:w3'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# move-window.
|
||||
|
||||
# To a free index.
|
||||
check_ok move-window -d -s W:2 -t W:7
|
||||
check_windows W '0:w0 1:w1 3:w3 7:w2'
|
||||
|
||||
# To an occupied index, without and with -k.
|
||||
check_fail 'index in use: 7' move-window -d -s W:3 -t W:7
|
||||
check_ok move-window -d -k -s W:3 -t W:7
|
||||
check_windows W '0:w0 1:w1 7:w3'
|
||||
|
||||
# -a inserts after the target and shuffles.
|
||||
check_ok move-window -d -a -s W:7 -t W:0
|
||||
check_windows W '0:w0 1:w3 2:w1'
|
||||
|
||||
# -r renumbers in order, respecting base-index.
|
||||
check_ok move-window -d -s W:2 -t W:8
|
||||
check_ok set-option -t W base-index 5
|
||||
check_ok move-window -r -t W:
|
||||
check_windows W '5:w0 6:w3 7:w1'
|
||||
check_ok set-option -t W base-index 0
|
||||
check_ok move-window -r -t W:
|
||||
check_windows W '0:w0 1:w3 2:w1'
|
||||
|
||||
# With the renumber-windows option on, killing a window renumbers the rest
|
||||
# automatically.
|
||||
check_ok set-option -t W renumber-windows on
|
||||
check_ok new-window -d -t W:9 -n wtmp
|
||||
check_windows W '0:w0 1:w3 2:w1 9:wtmp'
|
||||
check_ok kill-window -t W:1
|
||||
check_windows W '0:w0 1:w1 2:wtmp'
|
||||
check_ok kill-window -t W:2
|
||||
check_ok set-option -t W renumber-windows off
|
||||
check_ok new-window -d -t W:2 -n w3
|
||||
check_windows W '0:w0 1:w1 2:w3'
|
||||
|
||||
# Without -s, the current window of the client/session moves.
|
||||
check_ok select-window -t W:2
|
||||
check_ok move-window -d -t W:6
|
||||
check_windows W '0:w0 1:w1 6:w3'
|
||||
check_ok move-window -r -t W:
|
||||
check_windows W '0:w0 1:w1 2:w3'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# link-window and unlink-window.
|
||||
|
||||
check_ok new-session -d -s L -x 80 -y 24 -n l0
|
||||
|
||||
# Link a window from W into L and check it is shared.
|
||||
check_ok link-window -d -s W:w1 -t L:5
|
||||
check_windows L '0:l0 5:w1'
|
||||
check_fmt 'W:w1' '#{window_linked}' '1'
|
||||
check_fmt 'L:5' '#{window_linked_sessions}' '2'
|
||||
|
||||
# The linked window is the same window: renaming in one session shows in the
|
||||
# other.
|
||||
check_ok rename-window -t L:5 shared
|
||||
check_windows W '0:w0 1:shared 2:w3'
|
||||
check_ok rename-window -t W:1 w1
|
||||
|
||||
# Linking again to an occupied index fails without -k.
|
||||
check_fail 'index in use: 0' link-window -d -s W:w3 -t L:0
|
||||
|
||||
# Unlink removes one link; the window survives in the other session.
|
||||
check_ok unlink-window -t L:5
|
||||
check_windows L '0:l0'
|
||||
check_windows W '0:w0 1:w1 2:w3'
|
||||
check_fmt 'W:w1' '#{window_linked}' '0'
|
||||
|
||||
# Unlinking a window linked to only one session needs -k.
|
||||
check_fail 'window only linked to one session' unlink-window -t W:w3
|
||||
check_ok unlink-window -k -t W:w3
|
||||
check_windows W '0:w0 1:w1'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# swap-window.
|
||||
|
||||
check_ok new-window -d -t W:2 -n w2
|
||||
check_ok new-window -d -t W:3 -n w3
|
||||
|
||||
# Swap within a session: indices are exchanged.
|
||||
check_ok swap-window -d -s W:0 -t W:3
|
||||
check_windows W '0:w3 1:w1 2:w2 3:w0'
|
||||
check_ok swap-window -d -s W:0 -t W:3
|
||||
check_windows W '0:w0 1:w1 2:w2 3:w3'
|
||||
|
||||
# Without -d the current index does not change, so the window that arrives
|
||||
# there becomes current; with -d the swapped windows are selected, so the
|
||||
# source window stays current at its new index.
|
||||
check_ok select-window -t W:0
|
||||
check_ok swap-window -s W:0 -t W:3
|
||||
check_fmt 'W:' '#{window_index}:#{window_name}' '0:w3'
|
||||
check_ok swap-window -s W:3 -t W:0
|
||||
check_fmt 'W:' '#{window_index}:#{window_name}' '0:w0'
|
||||
check_ok swap-window -d -s W:0 -t W:3
|
||||
check_fmt 'W:' '#{window_index}:#{window_name}' '3:w0'
|
||||
check_ok swap-window -d -s W:3 -t W:0
|
||||
check_fmt 'W:' '#{window_index}:#{window_name}' '0:w0'
|
||||
|
||||
# Swap between two different sessions.
|
||||
check_ok swap-window -d -s W:w2 -t L:l0
|
||||
check_windows W '0:w0 1:w1 2:l0 3:w3'
|
||||
check_windows L '0:w2'
|
||||
check_ok swap-window -d -s W:2 -t L:0
|
||||
check_windows W '0:w0 1:w1 2:w2 3:w3'
|
||||
check_windows L '0:l0'
|
||||
|
||||
# Swapping between two sessions in the same group is an error.
|
||||
check_ok new-session -d -s WG -t W
|
||||
check_fail "can't move window, sessions are grouped" \
|
||||
swap-window -d -s W:0 -t WG:1
|
||||
check_ok kill-session -t WG
|
||||
check_windows W '0:w0 1:w1 2:w2 3:w3'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# rotate-window.
|
||||
|
||||
check_ok new-session -d -s R -x 80 -y 24
|
||||
check_ok split-window -d -t R:0
|
||||
check_ok split-window -d -t R:0
|
||||
p0=$($TMUX display-message -p -t R:0.0 '#{pane_id}')
|
||||
p1=$($TMUX display-message -p -t R:0.1 '#{pane_id}')
|
||||
p2=$($TMUX display-message -p -t R:0.2 '#{pane_id}')
|
||||
|
||||
# check_panes $target $expected
|
||||
#
|
||||
# Compare the pane list of a window (as "index:id ...") with $expected.
|
||||
check_panes()
|
||||
{
|
||||
out=$(echo $($TMUX list-panes -t "$1" -F \
|
||||
'#{pane_index}:#{pane_id}'))
|
||||
if [ "$out" != "$2" ]; then
|
||||
echo "Pane list of '$1' wrong."
|
||||
echo "Expected: '$2'"
|
||||
echo "But got: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_panes R:0 "0:$p0 1:$p1 2:$p2"
|
||||
|
||||
# -U rotates panes up (each pane moves to the previous position); -D rotates
|
||||
# down. -U then -D restores the original order.
|
||||
check_ok rotate-window -U -t R:0
|
||||
check_panes R:0 "0:$p1 1:$p2 2:$p0"
|
||||
check_ok rotate-window -D -t R:0
|
||||
check_panes R:0 "0:$p0 1:$p1 2:$p2"
|
||||
check_ok rotate-window -D -t R:0
|
||||
check_panes R:0 "0:$p2 1:$p0 2:$p1"
|
||||
check_ok rotate-window -U -t R:0
|
||||
|
||||
# The active position is preserved across rotation: the pane that arrives at
|
||||
# the active position becomes the active pane.
|
||||
check_ok select-pane -t R:0.0
|
||||
check_ok rotate-window -U -t R:0
|
||||
check_fmt 'R:0' '#{pane_index}:#{pane_id}' "0:$p1"
|
||||
check_ok rotate-window -D -t R:0
|
||||
check_fmt 'R:0' '#{pane_index}:#{pane_id}' "0:$p0"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# kill-window.
|
||||
|
||||
# Killing the current window switches to the last (previously current)
|
||||
# window.
|
||||
check_ok select-window -t W:1
|
||||
check_ok select-window -t W:3
|
||||
check_fmt 'W:' '#{window_index}' '3'
|
||||
check_ok kill-window -t W:3
|
||||
check_fmt 'W:' '#{window_index}' '1'
|
||||
check_windows W '0:w0 1:w1 2:w2'
|
||||
|
||||
# -f is only valid with -a.
|
||||
check_fail '-f only valid with -a' kill-window -f 'x' -t W:0
|
||||
|
||||
# -a kills every window except the target.
|
||||
check_ok kill-window -a -t W:w1
|
||||
check_windows W '1:w1'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# select-window, next-window, previous-window.
|
||||
|
||||
# -P prints where the new window went; an invalid (non-UTF-8) name is an
|
||||
# error.
|
||||
out=$($TMUX new-window -d -t W:0 -n wa -P -F '#{window_index}:#{window_name}')
|
||||
if [ "$out" != "0:wa" ]; then
|
||||
echo "new-window -P output wrong: '$out'"
|
||||
exit 1
|
||||
fi
|
||||
check_fail "invalid window name: $(printf 'a\377b')" \
|
||||
new-window -d -t W: -n "$(printf 'a\377b')"
|
||||
check_ok new-window -d -t W:2 -n wc
|
||||
check_windows W '0:wa 1:w1 2:wc'
|
||||
|
||||
# -n and -p select the next and previous window, wrapping at the ends.
|
||||
check_ok select-window -t W:0
|
||||
check_ok select-window -n -t W:
|
||||
check_fmt 'W:' '#{window_index}' '1'
|
||||
check_ok select-window -n -t W:
|
||||
check_fmt 'W:' '#{window_index}' '2'
|
||||
check_ok select-window -n -t W:
|
||||
check_fmt 'W:' '#{window_index}' '0'
|
||||
check_ok select-window -p -t W:
|
||||
check_fmt 'W:' '#{window_index}' '2'
|
||||
|
||||
# next-window and previous-window are the same code.
|
||||
check_ok next-window -t W:
|
||||
check_fmt 'W:' '#{window_index}' '0'
|
||||
check_ok previous-window -t W:
|
||||
check_fmt 'W:' '#{window_index}' '2'
|
||||
|
||||
# -l selects the previously current window and select-window -T on the
|
||||
# already-current window does the same.
|
||||
check_ok select-window -t W:1
|
||||
check_ok select-window -l -t W:
|
||||
check_fmt 'W:' '#{window_index}' '2'
|
||||
check_ok select-window -T -t W:2
|
||||
check_fmt 'W:' '#{window_index}' '1'
|
||||
check_ok select-window -T -t W:2
|
||||
check_fmt 'W:' '#{window_index}' '2'
|
||||
|
||||
# With -a, next-window looks for a window with an alert and fails if there
|
||||
# is none; a fresh session has no last window.
|
||||
check_fail 'no next window' next-window -a -t W:
|
||||
check_fail 'no previous window' previous-window -a -t W:
|
||||
check_ok new-session -d -s F -x 80 -y 24
|
||||
check_fail 'no last window' select-window -l -t F:
|
||||
check_fail 'no last window' select-window -T -t F:0
|
||||
check_ok kill-session -t F
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# more kill-window -a: no-op, filters and multiply-linked windows.
|
||||
|
||||
# -a with a single window in the session does nothing.
|
||||
check_ok kill-window -a -t L:0
|
||||
check_windows L '0:l0'
|
||||
|
||||
# -a -f only kills other windows matching the filter.
|
||||
check_ok kill-window -a -f '#{==:#{window_name},wc}' -t W:0
|
||||
check_windows W '0:wa 1:w1'
|
||||
|
||||
# If the current window is linked into the session more than once, -a kills
|
||||
# it too - taking the whole session with it here.
|
||||
check_ok new-session -d -s D -x 80 -y 24 -n d0
|
||||
check_ok link-window -d -s D:0 -t D:5
|
||||
check_ok new-window -d -t D:1 -n dx
|
||||
check_ok select-window -t D:0
|
||||
check_ok kill-window -a -t D:0
|
||||
if $TMUX has-session -t D 2>/dev/null; then
|
||||
echo "Session D survived kill-window -a on multiply-linked window."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
check_fmt 'R:0' '#{window_panes}' '3'
|
||||
assert_alive
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 0
|
||||
Reference in New Issue
Block a user