mirror of
https://github.com/tmux/tmux.git
synced 2026-08-26 00:21:37 +00:00
159 lines
3.7 KiB
Bash
Executable File
159 lines
3.7 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
PATH=/bin:/usr/bin
|
|
TERM=screen
|
|
LC_ALL=C.UTF-8
|
|
LANG=C.UTF-8
|
|
export TERM LC_ALL LANG
|
|
|
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
|
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
|
|
|
TMPDIR=$(mktemp -d)
|
|
IN="$TMPDIR/in"
|
|
OUT="$TMPDIR/out"
|
|
BADCFG="$TMPDIR/bad.conf"
|
|
PID=
|
|
|
|
cleanup()
|
|
{
|
|
exec 3>&- 2>/dev/null
|
|
[ -n "$PID" ] && kill "$PID" 2>/dev/null
|
|
$TMUX kill-server 2>/dev/null
|
|
rm -rf "$TMPDIR"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
fail()
|
|
{
|
|
echo "$1" >&2
|
|
[ -s "$OUT" ] && cat "$OUT" >&2
|
|
exit 1
|
|
}
|
|
|
|
wait_for()
|
|
{
|
|
pattern=$1
|
|
timeout=${2:-60}
|
|
i=0
|
|
|
|
while [ "$i" -lt "$timeout" ]; do
|
|
grep -F -- "$pattern" "$OUT" >/dev/null 2>&1 && return 0
|
|
sleep 0.1
|
|
i=$((i + 1))
|
|
done
|
|
fail "missing: $pattern"
|
|
}
|
|
|
|
wait_for_count()
|
|
{
|
|
pattern=$1
|
|
expected=$2
|
|
timeout=${3:-60}
|
|
i=0
|
|
|
|
while [ "$i" -lt "$timeout" ]; do
|
|
count=$(grep -F -c -- "$pattern" "$OUT" 2>/dev/null)
|
|
[ "$count" -ge "$expected" ] && return 0
|
|
sleep 0.1
|
|
i=$((i + 1))
|
|
done
|
|
fail "missing count $expected for: $pattern"
|
|
}
|
|
|
|
send()
|
|
{
|
|
printf '%s\n' "$1" >&3 || fail "failed to send: $1"
|
|
}
|
|
|
|
# A notification is a standalone protocol message. It must never be part of
|
|
# the response between the real %begin and matching %end or %error for a
|
|
# command. Ignore guard-looking command output while a real guard is open.
|
|
check_guards()
|
|
{
|
|
awk '
|
|
function notification(line) {
|
|
return line ~ /^%(sessions-changed|session-changed |unlinked-window-add )/ ||
|
|
line ~ /^%(pause |continue |config-error )/
|
|
}
|
|
!open && $0 ~ /^%begin [0-9]+ [0-9]+ [0-9]+$/ {
|
|
open = 1
|
|
time = $2
|
|
number = $3
|
|
flags = $4
|
|
next
|
|
}
|
|
open && ($0 == "%end " time " " number " " flags ||
|
|
$0 == "%error " time " " number " " flags) {
|
|
open = 0
|
|
next
|
|
}
|
|
notification($0) {
|
|
seen[$1]++
|
|
if (open) {
|
|
print "notification inside guard: " $0 > "/dev/stderr"
|
|
bad = 1
|
|
}
|
|
}
|
|
END {
|
|
if (open) {
|
|
print "unterminated command guard" > "/dev/stderr"
|
|
bad = 1
|
|
}
|
|
if (seen["%sessions-changed"] < 2 ||
|
|
seen["%session-changed"] < 1 ||
|
|
seen["%unlinked-window-add"] < 2 ||
|
|
seen["%pause"] < 1 || seen["%continue"] < 1 ||
|
|
seen["%config-error"] < 1) {
|
|
print "missing expected notification" > "/dev/stderr"
|
|
bad = 1
|
|
}
|
|
exit bad
|
|
}' "$OUT" || fail "bad control protocol notification ordering"
|
|
}
|
|
|
|
$TMUX kill-server 2>/dev/null
|
|
$TMUX new-session -d -s guard -x 80 -y 24 || exit 1
|
|
pane=$($TMUX display-message -p -t guard:0.0 '#{pane_id}') || exit 1
|
|
|
|
mkfifo "$IN" || exit 1
|
|
: >"$OUT"
|
|
$TMUX -C attach-session -t guard <"$IN" >"$OUT" 2>&1 &
|
|
PID=$!
|
|
exec 3>"$IN"
|
|
|
|
# Attaching generates a session notification synchronously from inside the
|
|
# attach command. It must follow the closing guard.
|
|
wait_for '%session-changed '
|
|
|
|
# Guard-looking output is arbitrary command output, not protocol state. Make
|
|
# new-session both generate notifications and print an unmatched fake %begin;
|
|
# it must not leave those notifications permanently deferred.
|
|
send "set-option -g @fake-begin '%begin 1 2 3'"
|
|
send "new-session -d -P -F '#{@fake-begin}' -s fake-begin"
|
|
wait_for_count '%sessions-changed' 1
|
|
|
|
# Likewise, fake %end output from a command that generates multiple
|
|
# notifications must not flush them before the command actually ends.
|
|
send "set-option -g @fake-end '%end 1 2 3'"
|
|
send "new-session -d -P -F '#{@fake-end}' -s fake-end"
|
|
wait_for_count '%sessions-changed' 2
|
|
|
|
# Exercise notification paths outside control-notify.c.
|
|
send "refresh-client -A '$pane:pause'"
|
|
wait_for "%pause $pane"
|
|
send "refresh-client -A '$pane:continue'"
|
|
wait_for "%continue $pane"
|
|
|
|
printf '%s\n' 'not-a-command' >"$BADCFG"
|
|
send "source-file '$BADCFG'"
|
|
wait_for '%config-error '
|
|
|
|
# A final command guarantees all earlier command guards have completed before
|
|
# the transcript is checked.
|
|
send "display-message -p 'guard-check-done'"
|
|
wait_for 'guard-check-done'
|
|
check_guards
|
|
|
|
exit 0
|