mirror of
https://github.com/tmux/tmux.git
synced 2026-07-08 10:29:32 +00:00
A few more tests.
This commit is contained in:
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 -Ltest -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
|
||||
562
regress/pane-ops.sh
Normal file
562
regress/pane-ops.sh
Normal file
@@ -0,0 +1,562 @@
|
||||
#!/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
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -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
|
||||
|
||||
# -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'
|
||||
|
||||
# -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
|
||||
214
regress/session-ops.sh
Normal file
214
regress/session-ops.sh
Normal file
@@ -0,0 +1,214 @@
|
||||
#!/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
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -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_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
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 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
|
||||
|
||||
# -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
|
||||
403
regress/window-ops.sh
Normal file
403
regress/window-ops.sh
Normal file
@@ -0,0 +1,403 @@
|
||||
#!/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
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -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