mirror of
https://github.com/tmux/tmux.git
synced 2026-07-03 09:22:22 +00:00
Add some tests.
This commit is contained in:
50
regress/cmd-client-argv.sh
Executable file
50
regress/cmd-client-argv.sh
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Client argv parsing and the start-server boundary.
|
||||
#
|
||||
# The command line given to the client is parsed and invoked as one sequence.
|
||||
# This checks: a multi-command argv runs every command and starts the server
|
||||
# (new-session carries CMD_STARTSERVER); a command without start-server fails
|
||||
# cleanly against no server; and a parse error leaves no stray server behind.
|
||||
|
||||
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
|
||||
|
||||
# Multi-command argv: starts the server and runs all commands in order.
|
||||
$TMUX new-session -d -swork -nbase \; set -g @a one \; new-window -dn W2 \; \
|
||||
set -g @b two || exit 1
|
||||
[ "$($TMUX show -gv @a)" = one ] || { echo "argv cmd 2 did not run" >&2; exit 1; }
|
||||
[ "$($TMUX show -gv @b)" = two ] || { echo "argv cmd 4 did not run" >&2; exit 1; }
|
||||
got=$($TMUX list-windows -t work -F '#{window_name}' | tr '\n' ',')
|
||||
[ "$got" = "base,W2," ] || { echo "argv windows: got [$got]" >&2; exit 1; }
|
||||
# kill-server is asynchronous; wait for the server to actually exit before the
|
||||
# checks below that require no server to be running.
|
||||
$TMUX kill-server 2>/dev/null
|
||||
i=0
|
||||
while $TMUX has-session 2>/dev/null; do
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
[ $i -gt 100 ] && break
|
||||
done
|
||||
|
||||
# A command without start-server fails cleanly when no server is running and
|
||||
# does not fork one.
|
||||
$TMUX new-window -dn ZZ 2>/dev/null && { echo "new-window unexpectedly succeeded" >&2; exit 1; }
|
||||
if $TMUX has-session 2>/dev/null; then
|
||||
echo "non-start-server command left a stray server" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# A parse error with no server running starts no server.
|
||||
$TMUX 'if-shell true {' 2>/dev/null && { echo "parse error did not fail" >&2; exit 1; }
|
||||
if $TMUX has-session 2>/dev/null; then
|
||||
echo "parse error left a stray server" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 0
|
||||
87
regress/cmd-command-alias.sh
Executable file
87
regress/cmd-command-alias.sh
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/bin/sh
|
||||
|
||||
# command-alias expansion, against a running server and at server start.
|
||||
#
|
||||
# An alias replaces a command name with parsed command text; arguments after the
|
||||
# alias name are appended to the last command of the expansion. This covers a
|
||||
# single-command alias, a multi-command alias, argument appending, a built-in
|
||||
# default alias, and aliases that are defined and used as the server starts
|
||||
# (both from the startup config and from the client command line that starts
|
||||
# the server).
|
||||
|
||||
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
|
||||
|
||||
CONF=$(mktemp)
|
||||
trap "rm -f $CONF" 0 1 15
|
||||
|
||||
wait_gone() {
|
||||
i=0
|
||||
while $TMUX has-session 2>/dev/null; do
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
[ $i -gt 100 ] && break
|
||||
done
|
||||
}
|
||||
|
||||
# --- Against a running server. ---------------------------------------------
|
||||
$TMUX -f/dev/null start \; new-session -d -swork -nbase 2>/dev/null || exit 1
|
||||
|
||||
# Single-command alias.
|
||||
$TMUX split-window -dt work || exit 1
|
||||
$TMUX set -s command-alias[200] 'zoomit=resize-pane -Z' || exit 1
|
||||
$TMUX zoomit -t work:.0 || exit 1
|
||||
[ "$($TMUX display-message -p -t work '#{window_zoomed_flag}')" = 1 ] || {
|
||||
echo "single-command alias did not zoom" >&2; exit 1; }
|
||||
|
||||
# Multi-command alias: both commands run.
|
||||
$TMUX set -s command-alias[201] 'twowin=new-window -dn AA ; new-window -dn BB' || exit 1
|
||||
$TMUX run-shell -C 'twowin' || exit 1
|
||||
got=$($TMUX list-windows -t work -F '#{window_name}' | tr '\n' ',')
|
||||
[ "$got" = "base,AA,BB," ] || { echo "multi-command alias: got [$got]" >&2; exit 1; }
|
||||
|
||||
# Arguments after the alias name are appended to the expansion.
|
||||
$TMUX set -s command-alias[202] 'namewin=new-window -d -n' || exit 1
|
||||
$TMUX run-shell -C 'namewin CC' || exit 1
|
||||
$TMUX list-windows -t work -F '#{window_name}' | grep -qx CC || {
|
||||
echo "alias argument append did not create window CC" >&2; exit 1; }
|
||||
|
||||
# A built-in default alias (splitp -> split-window).
|
||||
before=$($TMUX list-panes -t work | wc -l)
|
||||
$TMUX run-shell -C 'splitp -d -t work' || exit 1
|
||||
after=$($TMUX list-panes -t work | wc -l)
|
||||
[ "$after" -gt "$before" ] || { echo "built-in alias splitp did not split" >&2; exit 1; }
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
wait_gone
|
||||
|
||||
# --- At server start: alias defined and used in the startup config. --------
|
||||
cat <<'EOF' >$CONF
|
||||
set -s command-alias[100] greet='set -g @g started'
|
||||
set -s command-alias[101] mkwins='new-window -dn SW1 ; new-window -dn SW2'
|
||||
new-session -d -swork -nbase
|
||||
greet
|
||||
mkwins
|
||||
EOF
|
||||
$TMUX -f$CONF start 2>/dev/null || exit 1
|
||||
[ "$($TMUX show -gv @g)" = started ] || { echo "startup-config alias did not run" >&2; exit 1; }
|
||||
got=$($TMUX list-windows -t work -F '#{window_name}' | tr '\n' ',')
|
||||
[ "$got" = "base,SW1,SW2," ] || { echo "startup-config multi alias: got [$got]" >&2; exit 1; }
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
wait_gone
|
||||
|
||||
# --- At server start: alias from config, used on the command line that starts
|
||||
# the server. ---------------------------------------------------------
|
||||
cat <<'EOF' >$CONF
|
||||
set -s command-alias[100] greet='set -g @g argv'
|
||||
EOF
|
||||
$TMUX -f$CONF new-session -d -swork \; greet 2>/dev/null || exit 1
|
||||
[ "$($TMUX show -gv @g)" = argv ] || { echo "argv-start alias did not run" >&2; exit 1; }
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 0
|
||||
102
regress/cmd-invoke-deferred.sh
Executable file
102
regress/cmd-invoke-deferred.sh
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Deferred command callbacks.
|
||||
#
|
||||
# Several commands store a parsed tree and invoke it later through
|
||||
# cmd_invoke_get: if-shell (then/else, sync and background), run-shell -C, and -
|
||||
# once a key is pressed by an attached client - a key binding, confirm-before and
|
||||
# command-prompt. The headless cases run directly on the inner server; the
|
||||
# client cases use the nested-tmux pattern from prompt-mechanics.sh: an outer
|
||||
# server hosts the inner client in a pane and keys are injected with send-keys.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
OUT="$TEST_TMUX -Ltest -f/dev/null"
|
||||
IN="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
|
||||
$OUT kill-server 2>/dev/null
|
||||
$IN kill-server 2>/dev/null
|
||||
trap "$OUT kill-server 2>/dev/null; $IN kill-server 2>/dev/null" 0 1 15
|
||||
|
||||
fail() { echo "[FAIL] $1" >&2; exit 1; }
|
||||
settle() { sleep 0.5; }
|
||||
|
||||
CONF=$(mktemp)
|
||||
trap "rm -f $CONF; $OUT kill-server 2>/dev/null; $IN kill-server 2>/dev/null" 0 1 15
|
||||
|
||||
# --- Headless deferred callbacks: no client needed. ------------------------
|
||||
$IN new -d -x80 -y23 -nbase "sh -c 'exec sleep 1000'" || exit 1
|
||||
$IN set -g status on || exit 1
|
||||
$IN set -g status-keys emacs || exit 1
|
||||
$IN set -g window-size manual || exit 1
|
||||
|
||||
$IN if-shell true 'set -g @t then' 'set -g @t else' || exit 1
|
||||
[ "$($IN show -gv @t)" = then ] || fail "if-shell true ran wrong branch"
|
||||
$IN if-shell false 'set -g @f then' 'set -g @f else' || exit 1
|
||||
[ "$($IN show -gv @f)" = else ] || fail "if-shell false ran wrong branch"
|
||||
|
||||
# Background (-b) if-shell evaluates its condition in a shell asynchronously.
|
||||
$IN if-shell -b 'true' 'set -g @b yes' 'set -g @b no' || exit 1
|
||||
settle
|
||||
[ "$($IN show -gv @b)" = yes ] || fail "background if-shell ran wrong branch"
|
||||
|
||||
# A multi-command then-body runs both commands.
|
||||
$IN if-shell true 'new-window -dn IF1 ; new-window -dn IF2' '' || exit 1
|
||||
$IN list-windows -F '#{window_name}' | grep -qx IF1 || fail "if-shell body cmd1 missing"
|
||||
$IN list-windows -F '#{window_name}' | grep -qx IF2 || fail "if-shell body cmd2 missing"
|
||||
|
||||
# run-shell -C invokes its argument as tmux commands.
|
||||
$IN run-shell -C 'set -g @rc ran' || exit 1
|
||||
[ "$($IN show -gv @rc)" = ran ] || fail "run-shell -C did not run command"
|
||||
|
||||
# --- Client deferred callbacks: key binding, confirm-before, command-prompt. -
|
||||
$IN bind -n M-c confirm-before -p '(ok) ' 'set -g @cb confirmed' || exit 1
|
||||
$IN bind -n M-d command-prompt -I pre -p '(cmd) ' 'set -g @cp %%' || exit 1
|
||||
# A brace body must come through the lexer, so bind it from a config.
|
||||
cat <<'EOF' >$CONF
|
||||
bind -n M-k { new-window -dn K1 ; new-window -dn K2 }
|
||||
EOF
|
||||
$IN source-file $CONF || exit 1
|
||||
|
||||
$OUT new -d -x80 -y24 || exit 1
|
||||
$OUT set -g status off || exit 1
|
||||
$OUT set -g window-size manual || exit 1
|
||||
$OUT send-keys -l "$IN attach" || exit 1
|
||||
$OUT send-keys Enter || exit 1
|
||||
sleep 1
|
||||
|
||||
# Key binding with a stored multi-command body fires on keypress.
|
||||
$OUT send-keys M-k || exit 1
|
||||
settle
|
||||
$IN list-windows -F '#{window_name}' | grep -qx K1 || fail "key binding body cmd1 missing"
|
||||
$IN list-windows -F '#{window_name}' | grep -qx K2 || fail "key binding body cmd2 missing"
|
||||
|
||||
# confirm-before runs its command only when confirmed.
|
||||
$IN set -g @cb none || exit 1
|
||||
$OUT send-keys M-c || exit 1
|
||||
settle
|
||||
$OUT capture-pane -p | tail -1 | grep -qF '(ok)' || fail "confirm-before prompt not shown"
|
||||
$OUT send-keys n || exit 1
|
||||
settle
|
||||
[ "$($IN show -gv @cb)" = none ] || fail "confirm-before ran command after 'n'"
|
||||
$OUT send-keys M-c || exit 1
|
||||
settle
|
||||
$OUT send-keys y || exit 1
|
||||
settle
|
||||
[ "$($IN show -gv @cb)" = confirmed ] || fail "confirm-before did not run command after 'y'"
|
||||
|
||||
# command-prompt feeds the typed line (with -I prefill) into its template.
|
||||
$IN set -g @cp none || exit 1
|
||||
$OUT send-keys M-d || exit 1
|
||||
settle
|
||||
$OUT capture-pane -p | tail -1 | grep -qF '(cmd)' || fail "command-prompt not shown"
|
||||
$OUT send-keys -l X || exit 1
|
||||
$OUT send-keys Enter || exit 1
|
||||
settle
|
||||
[ "$($IN show -gv @cp)" = preX ] || fail "command-prompt recovered '$($IN show -gv @cp)'"
|
||||
|
||||
$OUT kill-server 2>/dev/null
|
||||
$IN kill-server 2>/dev/null
|
||||
exit 0
|
||||
75
regress/cmd-invoke-expand.sh
Executable file
75
regress/cmd-invoke-expand.sh
Executable file
@@ -0,0 +1,75 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Invoke-time expansion and conditionals.
|
||||
#
|
||||
# The parser builds syntax only; environment/tilde expansion, assignments and
|
||||
# %if/%elif/%else are all evaluated when the tree is invoked. This drives every
|
||||
# one of those through a single config and checks the resulting option values:
|
||||
# - FOO=bar assignments set the environment, read back with ${FOO} and $FOO
|
||||
# - %hidden assignments behave the same
|
||||
# - ~ expands to the home directory
|
||||
# - %if / %elif / %else selects the correct branch
|
||||
|
||||
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
|
||||
|
||||
CONF=$(mktemp)
|
||||
trap "rm -f $CONF" 0 1 15
|
||||
|
||||
cat <<'EOF' >$CONF
|
||||
FOO=hello
|
||||
set -g @assign "${FOO}"
|
||||
set -g @dollar "$FOO"
|
||||
%hidden BAR=secret
|
||||
set -g @hidden "${BAR}"
|
||||
set -g @undef "x${NOSUCHVAR_ZZZ}y"
|
||||
set -g @tilde ~
|
||||
set -g @baduser ~nosuchuser_zzz9
|
||||
%if 1
|
||||
set -g @iftrue then
|
||||
%else
|
||||
set -g @iftrue else
|
||||
%endif
|
||||
%if 0
|
||||
set -g @iffalse then
|
||||
%else
|
||||
set -g @iffalse else
|
||||
%endif
|
||||
%if 0
|
||||
set -g @elif a
|
||||
%elif 1
|
||||
set -g @elif b
|
||||
%else
|
||||
set -g @elif c
|
||||
%endif
|
||||
EOF
|
||||
|
||||
$TMUX -f/dev/null start \; new-session -d 2>/dev/null || exit 1
|
||||
$TMUX source-file $CONF || exit 1
|
||||
|
||||
check() {
|
||||
got=$($TMUX show -gv "$1")
|
||||
[ "$got" = "$2" ] || { echo "$1: got [$got] expected [$2]" >&2; exit 1; }
|
||||
}
|
||||
check @assign hello
|
||||
check @dollar hello
|
||||
check @hidden secret
|
||||
check @undef xy # undefined environment variable expands to nothing
|
||||
check @baduser "" # unknown user in ~user expands to nothing
|
||||
check @iftrue then
|
||||
check @iffalse else
|
||||
check @elif b
|
||||
|
||||
# ~ expands to an absolute home directory (value is machine dependent).
|
||||
tilde=$($TMUX show -gv @tilde)
|
||||
case "$tilde" in
|
||||
/*) ;;
|
||||
*) echo "@tilde did not expand to an absolute path: [$tilde]" >&2; exit 1;;
|
||||
esac
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 0
|
||||
72
regress/cmd-invoke-failure-scope.sh
Executable file
72
regress/cmd-invoke-failure-scope.sh
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Command failure scope.
|
||||
#
|
||||
# Failure scope is the active sequence (CMD_PARSE_SEQUENCE): commands joined by
|
||||
# ';' share one scope, so a failure skips the rest of that sequence; commands on
|
||||
# separate lines are independent sequences and are not skipped. After a failed
|
||||
# inner brace the enclosing sequence still resumes.
|
||||
#
|
||||
# Each case below creates windows around a deliberately invalid command and the
|
||||
# resulting window list shows exactly which commands ran.
|
||||
|
||||
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)
|
||||
CONF=$(mktemp)
|
||||
trap "rm -f $TMP $CONF" 0 1 15
|
||||
|
||||
# kill-server is asynchronous, so wait for the old server to actually exit
|
||||
# before starting a fresh one (otherwise the next start races a dying server).
|
||||
fresh_server() {
|
||||
$TMUX kill-server 2>/dev/null
|
||||
i=0
|
||||
while $TMUX list-sessions >/dev/null 2>&1; do
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
[ $i -gt 100 ] && break
|
||||
done
|
||||
$TMUX -f/dev/null start \; new-session -d -swork -nbase || exit 1
|
||||
}
|
||||
|
||||
# $1 label, $2 config body, $3 expected comma-separated window list.
|
||||
run() {
|
||||
fresh_server
|
||||
printf '%s\n' "$2" >$CONF
|
||||
$TMUX source-file $CONF >/dev/null 2>&1
|
||||
got=$($TMUX list-windows -t work -F '#{window_name}' | tr '\n' ',')
|
||||
if [ "$got" != "$3" ]; then
|
||||
echo "$1: got [$got] expected [$3]" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ';' sequence in a brace body: A runs, the bad command skips B (same sequence),
|
||||
# the outer sequence resumes so C runs.
|
||||
run "semicolon body" 'if-shell true { new-window -dn A ; nonexistent_cmd ; new-window -dn B }
|
||||
new-window -dn C' 'base,A,C,'
|
||||
|
||||
# Newlines are independent sequences: the bad command skips only itself, so both
|
||||
# B and C still run.
|
||||
run "newline body" 'if-shell true {
|
||||
new-window -dn A
|
||||
nonexistent_cmd
|
||||
new-window -dn B
|
||||
}
|
||||
new-window -dn C' 'base,A,B,C,'
|
||||
|
||||
# Top-level ';' sequence: failure skips the rest of the line.
|
||||
run "top-level semicolon" 'new-window -dn A ; nonexistent_cmd ; new-window -dn B' 'base,A,'
|
||||
|
||||
# Top-level newlines: independent, both run.
|
||||
run "top-level newline" 'new-window -dn A
|
||||
nonexistent_cmd
|
||||
new-window -dn B' 'base,A,B,'
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 0
|
||||
46
regress/cmd-invoke-hooks.sh
Executable file
46
regress/cmd-invoke-hooks.sh
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Hooks run stored command trees.
|
||||
#
|
||||
# A hook stores a parsed command tree that is invoked through cmd_invoke_get when
|
||||
# the hook fires. Covers a single-command hook, a multi-entry hook array (every
|
||||
# entry fires), and that show-hooks prints the stored commands back.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
$TMUX -f/dev/null start \; new-session -d -sfirst -nbase 2>/dev/null || exit 1
|
||||
|
||||
# Single-command hook fires on session creation.
|
||||
$TMUX set-hook -g session-created 'set -g @hook done' || exit 1
|
||||
$TMUX new-session -d -ssecond || exit 1
|
||||
[ "$($TMUX show -gv @hook)" = done ] || {
|
||||
echo "single-command hook did not fire" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# A hook array fires every entry.
|
||||
$TMUX set-hook -g session-created[10] 'new-window -dn H1' || exit 1
|
||||
$TMUX set-hook -g session-created[11] 'set -g @hook2 two' || exit 1
|
||||
$TMUX new-session -d -sthird || exit 1
|
||||
[ "$($TMUX show -gv @hook2)" = two ] || {
|
||||
echo "hook array entry 11 did not fire" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "$($TMUX list-windows -t third -F '#{window_name}')" | grep -qx H1 || {
|
||||
echo "hook array entry 10 did not create window" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# show-hooks prints the stored hook commands.
|
||||
$TMUX show-hooks -g | grep -q 'session-created.* set -g @hook done' || {
|
||||
echo "show-hooks did not print stored hook command" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 0
|
||||
56
regress/cmd-invoke-readonly.sh
Executable file
56
regress/cmd-invoke-readonly.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Read-only client enforcement.
|
||||
#
|
||||
# When the invoking client is read-only, cmd_invoke builds each command and
|
||||
# rejects it (without running it) unless the command entry is marked read-only.
|
||||
# A read-only client is created by attaching with -r; the nested-tmux pattern
|
||||
# from cmd-invoke-deferred.sh is used so a real read-only client presses a key.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
OUT="$TEST_TMUX -Ltest -f/dev/null"
|
||||
IN="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
|
||||
$OUT kill-server 2>/dev/null
|
||||
$IN kill-server 2>/dev/null
|
||||
trap "$OUT kill-server 2>/dev/null; $IN kill-server 2>/dev/null" 0 1 15
|
||||
|
||||
fail() { echo "[FAIL] $1" >&2; exit 1; }
|
||||
settle() { sleep 0.5; }
|
||||
|
||||
# Inner session: a key bound to a non-read-only command.
|
||||
$IN new -d -x80 -y23 -nbase "sh -c 'exec sleep 1000'" || exit 1
|
||||
$IN set -g status on || exit 1
|
||||
$IN set -g status-keys emacs || exit 1
|
||||
$IN set -g window-size manual || exit 1
|
||||
$IN bind -n M-w new-window -dn ROTRY || exit 1
|
||||
|
||||
# Outer session hosts the inner client, attached read-only with -r.
|
||||
$OUT new -d -x80 -y24 || exit 1
|
||||
$OUT set -g status off || exit 1
|
||||
$OUT set -g window-size manual || exit 1
|
||||
$OUT send-keys -l "$IN attach -r" || exit 1
|
||||
$OUT send-keys Enter || exit 1
|
||||
sleep 1
|
||||
|
||||
[ "$($IN list-clients -F '#{client_readonly}')" = 1 ] || fail "client is not read-only"
|
||||
|
||||
# Pressing the key runs new-window through the read-only client: it must be
|
||||
# rejected, leave no window, and report the error.
|
||||
$OUT send-keys M-w || exit 1
|
||||
settle
|
||||
$IN list-windows -F '#{window_name}' | grep -qx ROTRY && \
|
||||
fail "read-only client was allowed to create a window"
|
||||
$OUT capture-pane -p | grep -qi 'read-only' || fail "no read-only error was shown"
|
||||
|
||||
# Positive control: the same command with no client is not read-only and runs,
|
||||
# proving the command itself is valid and only the read-only client blocked it.
|
||||
$IN new-window -dn OKWIN || exit 1
|
||||
$IN list-windows -F '#{window_name}' | grep -qx OKWIN || fail "control new-window did not run"
|
||||
|
||||
$OUT kill-server 2>/dev/null
|
||||
$IN kill-server 2>/dev/null
|
||||
exit 0
|
||||
51
regress/cmd-parse-errors.sh
Executable file
51
regress/cmd-parse-errors.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Malformed input rejection and crash/leak canary.
|
||||
#
|
||||
# Each input below is a syntax error. For every one we require that:
|
||||
# - parsing reports failure (non-zero exit), and
|
||||
# - the server is still alive afterwards (a follow-up command succeeds).
|
||||
# The second check is the important one: a parser that crashes or corrupts state
|
||||
# on bad input would take the server down here.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
$TMUX -f/dev/null start \; new-session -d 2>/dev/null || exit 1
|
||||
|
||||
n=0
|
||||
check() {
|
||||
n=$((n + 1))
|
||||
# Expect a parse failure.
|
||||
if $TMUX run-shell -C "$1" >/dev/null 2>&1; then
|
||||
echo "case $n: expected failure but succeeded: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Expect the server to still be responsive.
|
||||
if ! $TMUX list-sessions >/dev/null 2>&1; then
|
||||
echo "case $n: server died after: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check 'if-shell true {' # unterminated brace body
|
||||
check 'display-message a }' # stray close brace
|
||||
check 'if-shell true { display-message a' # unclosed nested brace
|
||||
check 'if-shell true { ; }' # empty sequence in body
|
||||
check '%elif 1' # %elif with no %if
|
||||
check '%endif' # %endif with no %if
|
||||
check '%zzz' # unknown % directive
|
||||
check '; display-message a' # leading separator
|
||||
input='%if 1
|
||||
display-message a'
|
||||
check "$input" # unterminated %if
|
||||
input='%if 1
|
||||
%endif'
|
||||
check "$input" # empty %if body
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 0
|
||||
84
regress/cmd-parse-print.sh
Executable file
84
regress/cmd-parse-print.sh
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Parse/print golden suite for the command parser.
|
||||
#
|
||||
# Binds a spread of command syntax forms into a dedicated key table, then prints
|
||||
# them back with list-keys (which calls cmd_parse_print on the stored tree). A
|
||||
# command-valued option is printed too. The normalized output is compared byte
|
||||
# for byte against the expected block below, locking in the current parse/print
|
||||
# behaviour: quoting, separators, braced and nested bodies, and preservation of
|
||||
# unexpanded ${env}, ~ and #{format} syntax inside stored bodies.
|
||||
|
||||
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)
|
||||
CONF=$(mktemp)
|
||||
EXP=$(mktemp)
|
||||
trap "rm -f $TMP $CONF $EXP" 0 1 15
|
||||
|
||||
cat <<'EOF' >$CONF
|
||||
bind -T parsetest a display-message hello
|
||||
bind -T parsetest b display-message "hello world"
|
||||
bind -T parsetest c display-message 'literal $HOME #{p} ~'
|
||||
bind -T parsetest d display-message ""
|
||||
bind -T parsetest e display-message "#{pane_id}"
|
||||
bind -T parsetest f display-message a \; display-message b
|
||||
bind -T parsetest g {
|
||||
display-message one
|
||||
display-message two
|
||||
}
|
||||
bind -T parsetest h if-shell true { display-message yes } { display-message no }
|
||||
bind -T parsetest m if-shell true { display-message "${HOME}" "~" "~root" "#{pane_id}" }
|
||||
bind -T parsetest n if-shell true { display-message "a\nb" "x;y" '#literal' }
|
||||
EOF
|
||||
|
||||
# Expected normalized output. Lines inside the braced bodies are indented with a
|
||||
# single tab.
|
||||
cat <<'EOF' >$EXP
|
||||
bind-key -T parsetest a display-message hello
|
||||
bind-key -T parsetest b display-message 'hello world'
|
||||
bind-key -T parsetest c display-message 'literal $HOME #{p} ~'
|
||||
bind-key -T parsetest d display-message ''
|
||||
bind-key -T parsetest e display-message '#{pane_id}'
|
||||
bind-key -T parsetest f display-message a ; display-message b
|
||||
bind-key -T parsetest g display-message one
|
||||
display-message two
|
||||
bind-key -T parsetest h if-shell true {
|
||||
display-message yes
|
||||
} {
|
||||
display-message no
|
||||
}
|
||||
bind-key -T parsetest m if-shell true {
|
||||
display-message ${HOME} ~ ~root '#{pane_id}'
|
||||
}
|
||||
bind-key -T parsetest n if-shell true {
|
||||
display-message a\nb 'x;y' '#literal'
|
||||
}
|
||||
--- options ---
|
||||
display-message 'hi there'
|
||||
EOF
|
||||
|
||||
$TMUX -f/dev/null start \; new-session -d 2>/dev/null || exit 1
|
||||
$TMUX source-file $CONF || exit 1
|
||||
$TMUX set -g default-client-command 'display-message "hi there"' || exit 1
|
||||
|
||||
{
|
||||
$TMUX list-keys -T parsetest
|
||||
echo "--- options ---"
|
||||
$TMUX show -gv default-client-command
|
||||
} >$TMP 2>&1 || exit 1
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
cmp -s $TMP $EXP || {
|
||||
echo "cmd-parse-print: output differs from expected" >&2
|
||||
diff -u $EXP $TMP >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user