A few more tests.

This commit is contained in:
Nicholas Marriott
2026-07-03 07:13:20 +01:00
parent f0ec51e78d
commit f645f0abe8
4 changed files with 1466 additions and 0 deletions

287
regress/buffers.sh Normal file
View 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
View 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
View 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
View 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