mirror of
https://github.com/tmux/tmux.git
synced 2026-10-07 01:55:49 +00:00
Add a test of tiled, floating, zoomed and hidden pane combinations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
404
regress/pane-states.sh
Normal file
404
regress/pane-states.sh
Normal file
@@ -0,0 +1,404 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Combinations of tiled, floating, zoomed and hidden panes.
|
||||
#
|
||||
# Every pane is tiled or floating, zoomed or not, and hidden or not, and these
|
||||
# are independent. For each combination of those states for two panes (and for
|
||||
# three), each pane command that changes or depends on them is run and the
|
||||
# result is compared with what the command should do, using awk to work out the
|
||||
# expectation from the state before. This covers:
|
||||
# - select-pane, including -Z and moving to the next, last or a directional pane;
|
||||
# - resize-pane -Z (toggle a zoom), -H (hide or show), -a -Z (unzoom all) and
|
||||
# -a -H (show desktop), which is run twice to check it restores the panes;
|
||||
# - break-pane -W and join-pane to float and tile panes;
|
||||
# - split-window, new-pane and kill-pane;
|
||||
# - next-layout, select-layout, rotate-window and resize-window.
|
||||
#
|
||||
# It also checks properties that must hold after every command: the active pane
|
||||
# is not hidden if any pane is not, there is one active pane, a window with one
|
||||
# pane is not zoomed, window_zoomed_flag matches the panes and visible panes have
|
||||
# a size. The status line clicks and the cursor are in other tests.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
SOCK=testA$$
|
||||
RUN=0
|
||||
|
||||
tm()
|
||||
{
|
||||
$TEST_TMUX -L"$SOCK$RUN" -f/dev/null "$@"
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
tm kill-server >/dev/null 2>&1
|
||||
}
|
||||
trap cleanup 0
|
||||
trap 'exit 1' 1 2 15
|
||||
|
||||
FMT='#{pane_id} #{pane_floating_flag} #{pane_zoomed_flag} #{pane_hidden_flag} #{pane_active} #{pane_width} #{pane_height} #{window_zoomed_flag}'
|
||||
|
||||
# The oracle. The state before is on lines starting B and the state after on
|
||||
# lines starting A: id floating zoomed hidden active width height window_zoomed.
|
||||
# op is the operation, tgt the target pane, rc the exit status of the command and
|
||||
# mode is "setup" to check that the start state was made as wanted, with want
|
||||
# holding floating, zoomed and hidden for each pane in order.
|
||||
ORACLE='
|
||||
function fail(msg)
|
||||
{
|
||||
printf "%s %s (exit %s): %s\n", op, tgt, rc, msg
|
||||
bad = 1
|
||||
exit 1
|
||||
}
|
||||
function sameF(skip, i, id)
|
||||
{
|
||||
for (i = 1; i <= nb; i++) {
|
||||
id = bid[i]
|
||||
if (id == skip || !(id in ina))
|
||||
continue
|
||||
if (aF[id] != bF[id])
|
||||
fail("pane " id " floating " bF[id] " became " aF[id])
|
||||
}
|
||||
}
|
||||
function sameZ(skip, i, id)
|
||||
{
|
||||
for (i = 1; i <= nb; i++) {
|
||||
id = bid[i]
|
||||
if (id == skip || !(id in ina))
|
||||
continue
|
||||
if (aZ[id] != bZ[id])
|
||||
fail("pane " id " zoomed " bZ[id] " became " aZ[id])
|
||||
}
|
||||
}
|
||||
function sameH(skip, i, id)
|
||||
{
|
||||
for (i = 1; i <= nb; i++) {
|
||||
id = bid[i]
|
||||
if (id == skip || !(id in ina))
|
||||
continue
|
||||
if (aH[id] != bH[id])
|
||||
fail("pane " id " hidden " bH[id] " became " aH[id])
|
||||
}
|
||||
}
|
||||
function noZoom( i)
|
||||
{
|
||||
for (i = 1; i <= na; i++) {
|
||||
if (aZ[aid[i]])
|
||||
fail("pane " aid[i] " is still zoomed")
|
||||
}
|
||||
}
|
||||
function covered(t, i, q)
|
||||
{
|
||||
if (bF[t] || bZ[t])
|
||||
return 0
|
||||
for (i = 1; i <= nb; i++) {
|
||||
q = bid[i]
|
||||
if (q != t && bZ[q] && !bH[q])
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
function newPane( i, k, id)
|
||||
{
|
||||
k = 0
|
||||
for (i = 1; i <= na; i++) {
|
||||
id = aid[i]
|
||||
if (!(id in inb)) {
|
||||
k++
|
||||
created = id
|
||||
}
|
||||
}
|
||||
if (k != 1)
|
||||
fail(k " new panes, expected 1")
|
||||
if (na != nb + 1)
|
||||
fail("pane count " nb " became " na)
|
||||
}
|
||||
$1 == "B" {
|
||||
nb++; id = $2; bid[nb] = id; inb[id] = 1
|
||||
bF[id] = $3; bZ[id] = $4; bH[id] = $5; bA[id] = $6
|
||||
}
|
||||
$1 == "A" {
|
||||
na++; id = $2; aid[na] = id; ina[id] = 1
|
||||
aF[id] = $3; aZ[id] = $4; aH[id] = $5; aA[id] = $6
|
||||
aW[id] = $7; aT[id] = $8; aWZ[id] = $9
|
||||
}
|
||||
END {
|
||||
if (mode == "setup") {
|
||||
split(want, w, " ")
|
||||
for (i = 1; i <= nb; i++) {
|
||||
id = bid[i]; p = substr(id, 2) + 0
|
||||
if (bF[id] != w[3 * p + 1] || bZ[id] != w[3 * p + 2] ||
|
||||
bH[id] != w[3 * p + 3])
|
||||
fail("pane " id " is " bF[id] bZ[id] bH[id] ", wanted " \
|
||||
w[3 * p + 1] w[3 * p + 2] w[3 * p + 3])
|
||||
}
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Properties of any state.
|
||||
if (na < 1)
|
||||
fail("no panes left")
|
||||
nact = 0; anyZ = 0; anyvis = 0
|
||||
for (i = 1; i <= na; i++) {
|
||||
id = aid[i]
|
||||
if (aA[id] == 1) { nact++; act = id }
|
||||
if (aZ[id]) anyZ = 1
|
||||
if (!aH[id]) anyvis = 1
|
||||
}
|
||||
if (nact != 1)
|
||||
fail(nact " active panes")
|
||||
for (i = 1; i <= na; i++) {
|
||||
id = aid[i]
|
||||
if (aWZ[id] != anyZ)
|
||||
fail("window_zoomed_flag " aWZ[id] " with zoomed panes " anyZ)
|
||||
if (!aH[id] && (aW[id] < 1 || aT[id] < 1))
|
||||
fail("pane " id " is " aW[id] "x" aT[id])
|
||||
}
|
||||
if (na == 1 && anyZ)
|
||||
fail("the only pane is zoomed")
|
||||
if (anyvis && aH[act])
|
||||
fail("active pane " act " is hidden but others are not")
|
||||
|
||||
# A command that failed changed nothing.
|
||||
if (rc != 0) {
|
||||
if (na != nb)
|
||||
fail("pane count " nb " became " na)
|
||||
sameF(""); sameZ(""); sameH("")
|
||||
exit 0
|
||||
}
|
||||
|
||||
T = tgt
|
||||
if (op == "select") {
|
||||
if (aA[T] != 1) fail("not active")
|
||||
if (aH[T]) fail("still hidden")
|
||||
if (covered(T)) noZoom(); else sameZ("")
|
||||
sameF(""); sameH(T)
|
||||
} else if (op == "zoom") {
|
||||
sameF("")
|
||||
if (bZ[T]) {
|
||||
if (aZ[T]) fail("not unzoomed")
|
||||
sameZ(T); sameH("")
|
||||
} else {
|
||||
if (!aZ[T]) fail("not zoomed")
|
||||
if (aH[T]) fail("zoomed pane is hidden")
|
||||
if (aA[T] != 1) fail("zoomed pane is not active")
|
||||
sameZ(T); sameH(T)
|
||||
}
|
||||
} else if (op == "hide") {
|
||||
sameF("")
|
||||
if (bH[T]) {
|
||||
if (aH[T]) fail("not shown")
|
||||
if (aA[T] != 1) fail("not active when shown")
|
||||
if (covered(T)) noZoom(); else sameZ("")
|
||||
} else {
|
||||
if (!aH[T]) fail("not hidden")
|
||||
sameZ("")
|
||||
}
|
||||
sameH(T)
|
||||
} else if (op == "desktop") {
|
||||
sameF(""); sameZ("")
|
||||
for (i = 1; i <= nb; i++) {
|
||||
id = bid[i]
|
||||
want = (bH[id] + bF[id] + bZ[id] > 0) ? 1 : 0
|
||||
if (aH[id] != want)
|
||||
fail("pane " id " hidden " bH[id] " floating " bF[id] \
|
||||
" zoomed " bZ[id] " became hidden " aH[id])
|
||||
}
|
||||
} else if (op == "desktop2") {
|
||||
sameF(""); sameZ(""); sameH("")
|
||||
} else if (op == "unzoomall") {
|
||||
sameF(""); sameH(""); noZoom()
|
||||
} else if (op == "break") {
|
||||
if (bF[T] || !aF[T]) fail("not floated")
|
||||
sameF(T); sameZ(""); sameH("")
|
||||
} else if (op == "join") {
|
||||
if (!bF[T] || aF[T]) fail("not tiled")
|
||||
sameF(T); sameZ(""); sameH("")
|
||||
} else if (op == "kill") {
|
||||
if (T in ina) fail("not killed")
|
||||
if (na != nb - 1) fail("pane count " nb " became " na)
|
||||
sameF(T); sameH(T)
|
||||
if (na == 1) noZoom(); else sameZ(T)
|
||||
} else if (op == "split") {
|
||||
newPane()
|
||||
if (aF[created] != bF[T]) fail("new pane floating " aF[created])
|
||||
if (aZ[created] || aH[created]) fail("new pane zoomed or hidden")
|
||||
sameF(""); sameZ("")
|
||||
if (bH[T] && !bF[T]) {
|
||||
if (aH[T]) fail("tiled target not shown")
|
||||
sameH(T)
|
||||
} else
|
||||
sameH("")
|
||||
} else if (op == "newpane") {
|
||||
newPane()
|
||||
if (!aF[created]) fail("new pane is not floating")
|
||||
if (aZ[created] || aH[created]) fail("new pane zoomed or hidden")
|
||||
sameF(""); sameZ(""); sameH("")
|
||||
} else if (op == "nextlayout" || op == "tiled") {
|
||||
sameF(""); sameH(""); noZoom()
|
||||
} else {
|
||||
# Moving around or changing layout: floating panes stay floating
|
||||
# and nothing is hidden.
|
||||
sameF("")
|
||||
for (i = 1; i <= nb; i++) {
|
||||
id = bid[i]
|
||||
if (id in ina && aH[id] > bH[id])
|
||||
fail("pane " id " was hidden by this")
|
||||
}
|
||||
}
|
||||
exit 0
|
||||
}
|
||||
'
|
||||
|
||||
# describe N STATE: print the start state in words.
|
||||
describe()
|
||||
{
|
||||
n=$1
|
||||
s=$2
|
||||
i=0
|
||||
out=
|
||||
while [ $i -lt "$n" ]; do
|
||||
bits=$(( (s >> (3 * i)) & 7 ))
|
||||
k=T
|
||||
[ $((bits & 1)) -ne 0 ] && k=F
|
||||
z=
|
||||
[ $((bits & 2)) -ne 0 ] && z=z
|
||||
h=
|
||||
[ $((bits & 4)) -ne 0 ] && h=h
|
||||
out="$out %$i=$k$z$h"
|
||||
i=$((i + 1))
|
||||
done
|
||||
echo "$out"
|
||||
}
|
||||
|
||||
# run_one N STATE OP TARGET: set up the state, run the operation, check it.
|
||||
run_one()
|
||||
{
|
||||
n=$1
|
||||
s=$2
|
||||
op=$3
|
||||
tgt=$4
|
||||
RUN=$((RUN + 1))
|
||||
|
||||
set -- new-session -d -x 80 -y 24 cat
|
||||
i=1
|
||||
while [ $i -lt "$n" ]; do
|
||||
set -- "$@" ';' split-window -d ''
|
||||
i=$((i + 1))
|
||||
done
|
||||
want=
|
||||
for stage in 1 2 4; do
|
||||
i=0
|
||||
while [ $i -lt "$n" ]; do
|
||||
bits=$(( (s >> (3 * i)) & 7 ))
|
||||
if [ $((bits & stage)) -ne 0 ]; then
|
||||
case $stage in
|
||||
1) set -- "$@" ';' break-pane -d -W -s "%$i" ;;
|
||||
2) set -- "$@" ';' resize-pane -Z -t "%$i" ;;
|
||||
4) set -- "$@" ';' resize-pane -H -t "%$i" ;;
|
||||
esac
|
||||
fi
|
||||
i=$((i + 1))
|
||||
done
|
||||
done
|
||||
i=0
|
||||
while [ $i -lt "$n" ]; do
|
||||
bits=$(( (s >> (3 * i)) & 7 ))
|
||||
want="$want $((bits & 1)) $(((bits >> 1) & 1)) $(((bits >> 2) & 1))"
|
||||
i=$((i + 1))
|
||||
done
|
||||
set -- "$@" ';' list-panes -F "B $FMT"
|
||||
before=$(tm "$@" 2>&1)
|
||||
if ! msg=$(echo "$before" | awk -v mode=setup -v want="$want" \
|
||||
-v op=setup -v tgt=- -v rc=0 "$ORACLE" 2>&1); then
|
||||
echo "setup of $(describe "$n" "$s") failed: $msg"
|
||||
echo "$before"
|
||||
cleanup
|
||||
exit 1
|
||||
fi
|
||||
|
||||
p="-t $tgt"
|
||||
rc=0
|
||||
case $op in
|
||||
select) tm select-pane -t "$tgt" || rc=$? ;;
|
||||
selectZ) tm select-pane -Z -t "$tgt" || rc=$? ;;
|
||||
zoom) tm resize-pane -Z -t "$tgt" || rc=$? ;;
|
||||
hide) tm resize-pane -H -t "$tgt" || rc=$? ;;
|
||||
break) tm break-pane -d -W -s "$tgt" || rc=$? ;;
|
||||
join) tm join-pane -d -s "$tgt" -t "$tgt" || rc=$? ;;
|
||||
kill) tm kill-pane -t "$tgt" || rc=$? ;;
|
||||
split) tm split-window -d -t "$tgt" '' || rc=$? ;;
|
||||
newpane) tm new-pane -d -t "$tgt" -x 20 -y 6 '' || rc=$? ;;
|
||||
desktop) tm resize-pane -a -H -t %0 || rc=$? ;;
|
||||
desktop2)
|
||||
tm resize-pane -a -H -t %0 || rc=$?
|
||||
tm resize-pane -a -H -t %0 || rc=$?
|
||||
;;
|
||||
unzoomall) tm resize-pane -a -Z -t %0 || rc=$? ;;
|
||||
nextlayout) tm next-layout -t %0 || rc=$? ;;
|
||||
tiled) tm select-layout -t %0 tiled || rc=$? ;;
|
||||
rotate) tm rotate-window -t %0 || rc=$? ;;
|
||||
nextpane) tm select-pane -t :.+ || rc=$? ;;
|
||||
lastpane) tm last-pane -t %0 || rc=$? ;;
|
||||
left) tm select-pane -L || rc=$? ;;
|
||||
right) tm select-pane -R || rc=$? ;;
|
||||
up) tm select-pane -U || rc=$? ;;
|
||||
down) tm select-pane -D || rc=$? ;;
|
||||
resize) tm resize-window -t %0 -x 50 -y 14 || rc=$? ;;
|
||||
*) echo "unknown operation $op"; exit 1 ;;
|
||||
esac >/dev/null 2>&1
|
||||
|
||||
after=$(tm list-panes -F "A $FMT" 2>&1)
|
||||
result=$( (echo "$before"; echo "$after") |
|
||||
awk -v mode=check -v op="$op" -v tgt="$tgt" -v rc="$rc" "$ORACLE")
|
||||
if [ $? -ne 0 ] || [ -n "$result" ]; then
|
||||
echo "FAILED with $n panes, start $(describe "$n" "$s"):"
|
||||
echo " $result"
|
||||
echo "before:"; echo "$before" | sed 's/^/ /'
|
||||
echo "after:"; echo "$after" | sed 's/^/ /'
|
||||
cleanup
|
||||
exit 1
|
||||
fi
|
||||
cleanup
|
||||
}
|
||||
|
||||
PER_PANE="select selectZ zoom hide break join kill split newpane"
|
||||
GLOBAL="desktop desktop2 unzoomall nextlayout tiled rotate nextpane lastpane \
|
||||
left right up down resize"
|
||||
|
||||
# Two panes: every state, every operation.
|
||||
s=0
|
||||
while [ $s -lt 64 ]; do
|
||||
for op in $GLOBAL; do
|
||||
run_one 2 $s "$op" %0
|
||||
done
|
||||
for op in $PER_PANE; do
|
||||
run_one 2 $s "$op" %0
|
||||
run_one 2 $s "$op" %1
|
||||
done
|
||||
s=$((s + 1))
|
||||
done
|
||||
|
||||
# Three panes: every state, and one operation on each, rotating so that every
|
||||
# operation and target is used with many different states.
|
||||
ALL="$GLOBAL"
|
||||
for op in $PER_PANE; do
|
||||
ALL="$ALL $op:0 $op:1 $op:2"
|
||||
done
|
||||
set -- $ALL
|
||||
M=$#
|
||||
s=0
|
||||
while [ $s -lt 512 ]; do
|
||||
set -- $ALL
|
||||
shift $(( (s * 7 + s / M) % M ))
|
||||
item=$1
|
||||
case $item in
|
||||
*:*) run_one 3 $s "${item%:*}" "%${item#*:}" ;;
|
||||
*) run_one 3 $s "$item" %0 ;;
|
||||
esac
|
||||
s=$((s + 1))
|
||||
done
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user