Add alerts test.

This commit is contained in:
Nicholas Marriott
2026-07-06 08:47:57 +01:00
parent fcce73af42
commit 2c0f06782d

291
regress/alerts.sh Normal file
View File

@@ -0,0 +1,291 @@
#!/bin/sh
# monitor-activity, monitor-bell and monitor-silence: both the
# alert-activity, alert-bell and alert-silence hooks and the winlink alert
# flags (window_activity_flag, window_bell_flag, window_silence_flag and
# the #, !, ~ characters in window_flags). The sessions are detached so
# alert flags are set even on the current window; the *-action options
# still decide whether the hooks fire. Panes run cat: activity is
# generated by the tty echo of send-keys and a bell by sending a BEL and
# newline for cat to write back.
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)
OUT=$(mktemp -d)
TMUX_TMPDIR="$OUT"
export TMUX_TMPDIR
TMUX="$TEST_TMUX -Ltest-alerts-$$ -f/dev/null"
fail()
{
echo "$*" >&2
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
exit 1
}
cleanup()
{
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
}
trap cleanup EXIT
wait_for()
{
option=$1
expected=$2
i=0
while [ $i -lt 30 ]; do
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
[ "$value" = "$expected" ] && return 0
i=$((i + 1))
sleep 0.2
done
fail "expected $option to be '$expected' but got '$value'"
}
assert_unchanged()
{
option=$1
expected=$2
i=0
while [ $i -lt 10 ]; do
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
[ "$value" = "$expected" ] || \
fail "expected $option to remain '$expected' but got '$value'"
i=$((i + 1))
sleep 0.2
done
}
wait_for_fmt()
{
target=$1
fmt=$2
expected=$3
i=0
while [ $i -lt 30 ]; do
value=$($TMUX display -pt "$target" "$fmt" 2>/dev/null || true)
[ "$value" = "$expected" ] && return 0
i=$((i + 1))
sleep 0.2
done
fail "expected $fmt for $target to be '$expected' but got '$value'"
}
assert_fmt_unchanged()
{
target=$1
fmt=$2
expected=$3
i=0
while [ $i -lt 10 ]; do
value=$($TMUX display -pt "$target" "$fmt" 2>/dev/null || true)
[ "$value" = "$expected" ] || \
fail "expected $fmt for $target to remain '$expected' but got '$value'"
i=$((i + 1))
sleep 0.2
done
}
flags_have()
{
target=$1
char=$2
flags=$($TMUX display -pt "$target" '#{window_flags}') ||
fail "display window_flags failed"
case "$flags" in
*"$char"*) ;;
*) fail "expected '$char' in window_flags for $target: '$flags'" ;;
esac
}
flags_lack()
{
target=$1
char=$2
flags=$($TMUX display -pt "$target" '#{window_flags}') ||
fail "display window_flags failed"
case "$flags" in
*"$char"*) fail "unexpected '$char' in window_flags for $target: '$flags'" ;;
esac
}
bell()
{
$TMUX send-keys -t "$1" -H 07 0a || fail "send-keys bell failed"
}
activity()
{
$TMUX send-keys -t "$1" -l x || fail "send-keys activity failed"
}
$TMUX new -d -s mon -n w0 cat || fail "new-session mon failed"
$TMUX set -g @log '' || fail "set @log failed"
$TMUX set-hook -g alert-bell \
'set -gF @log "#{@log}|alert-bell:#{hook_session_name}:#{hook_window_name}"' ||
fail "set-hook alert-bell failed"
$TMUX set-hook -g alert-activity \
'set -gF @log "#{@log}|alert-activity:#{hook_session_name}:#{hook_window_name}"' ||
fail "set-hook alert-activity failed"
$TMUX set-hook -g alert-silence \
'set -gF @log "#{@log}|alert-silence:#{hook_session_name}:#{hook_window_name}"' ||
fail "set-hook alert-silence failed"
# A bell in a non-current window (monitor-bell defaults to on) fires
# alert-bell and sets the bell flag, shown as ! in window_flags.
$TMUX neww -d -t mon: -n bellw cat || fail "new-window bellw failed"
assert_fmt_unchanged mon:bellw '#{window_bell_flag}' 0
flags_lack mon:bellw '!'
bell mon:bellw
wait_for @log '|alert-bell:mon:bellw'
wait_for_fmt mon:bellw '#{window_bell_flag}' 1
flags_have mon:bellw '!'
# Bells are not deduplicated: a second bell fires the hook again even
# though the flag is still set.
bell mon:bellw
wait_for @log '|alert-bell:mon:bellw|alert-bell:mon:bellw'
# Selecting the window clears the alert flags.
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
wait_for_fmt mon:bellw '#{window_bell_flag}' 0
flags_lack mon:bellw '!'
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
# A bell in the current window of a detached session still sets the flag
# and bell-action any (the default) fires the hook for the current window.
$TMUX set -g @log '' || fail "reset @log failed"
bell mon:w0
wait_for @log '|alert-bell:mon:w0'
wait_for_fmt mon:w0 '#{window_bell_flag}' 1
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
wait_for_fmt mon:w0 '#{window_bell_flag}' 0
# bell-action none: the flag is still set but the hook does not fire.
$TMUX set -t mon bell-action none || fail "set bell-action none failed"
$TMUX set -g @log '' || fail "reset @log failed"
bell mon:bellw
wait_for_fmt mon:bellw '#{window_bell_flag}' 1
assert_unchanged @log ''
$TMUX set -ut mon bell-action || fail "unset bell-action failed"
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
# monitor-bell off: neither the flag nor the hook.
$TMUX set -wt mon:bellw monitor-bell off || fail "set monitor-bell failed"
$TMUX set -g @log '' || fail "reset @log failed"
bell mon:bellw
assert_fmt_unchanged mon:bellw '#{window_bell_flag}' 0
assert_unchanged @log ''
# monitor-activity defaults to off: output sets neither flag nor hook.
$TMUX neww -d -t mon: -n actw cat || fail "new-window actw failed"
$TMUX set -g @log '' || fail "reset @log failed"
activity mon:actw
assert_fmt_unchanged mon:actw '#{window_activity_flag}' 0
assert_unchanged @log ''
# With monitor-activity on, output fires alert-activity and sets the
# activity flag, shown as # in window_flags.
$TMUX set -wt mon:actw monitor-activity on ||
fail "set monitor-activity failed"
activity mon:actw
wait_for @log '|alert-activity:mon:actw'
wait_for_fmt mon:actw '#{window_activity_flag}' 1
flags_have mon:actw '#'
# While the flag is set further activity does not fire the hook again.
$TMUX set -g @log '' || fail "reset @log failed"
activity mon:actw
assert_unchanged @log ''
# On a detached session selecting the window does not clear the activity
# flag: session_set_current treats the selection itself as activity and
# the flag is raised again at once because the session is not attached.
# No hook fires as activity-action other does not apply to the current
# window.
$TMUX selectw -t mon:actw || fail "select-window actw failed"
assert_fmt_unchanged mon:actw '#{window_activity_flag}' 1
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
assert_fmt_unchanged mon:actw '#{window_activity_flag}' 1
assert_unchanged @log ''
# With a client attached, selecting the window does clear the activity
# flag and it stays clear for the current window.
mkfifo "$OUT/fifo" || fail "mkfifo failed"
$TMUX -C attach -t mon <"$OUT/fifo" >"$OUT/control.out" 2>&1 &
exec 3>"$OUT/fifo"
wait_for_fmt mon: '#{session_attached}' 1
$TMUX selectw -t mon:actw || fail "select-window actw failed"
wait_for_fmt mon:actw '#{window_activity_flag}' 0
flags_lack mon:actw '#'
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
exec 3>&-
wait_for_fmt mon: '#{session_attached}' 0
# Once the flag is clear the alert is re-armed.
activity mon:actw
wait_for @log '|alert-activity:mon:actw'
# Activity in the current window of a detached session sets the flag but
# activity-action other (the default) does not fire the hook.
$TMUX set -wt mon:w0 monitor-activity on ||
fail "set monitor-activity w0 failed"
$TMUX set -g @log '' || fail "reset @log failed"
activity mon:w0
wait_for_fmt mon:w0 '#{window_activity_flag}' 1
assert_unchanged @log ''
$TMUX set -wut mon:w0 monitor-activity ||
fail "unset monitor-activity w0 failed"
# monitor-silence: a quiet window fires alert-silence after the interval
# and sets the silence flag, shown as ~ in window_flags. The hook fires
# only once while the flag remains set even though the timer keeps
# running.
$TMUX neww -d -t mon: -n silw cat || fail "new-window silw failed"
assert_fmt_unchanged mon:silw '#{window_silence_flag}' 0
$TMUX set -g @log '' || fail "reset @log failed"
$TMUX set -wt mon:silw monitor-silence 1 || fail "set monitor-silence failed"
wait_for @log '|alert-silence:mon:silw'
wait_for_fmt mon:silw '#{window_silence_flag}' 1
flags_have mon:silw '~'
assert_unchanged @log '|alert-silence:mon:silw'
$TMUX set -wt mon:silw monitor-silence 0 ||
fail "reset monitor-silence failed"
$TMUX selectw -t mon:silw || fail "select-window silw failed"
wait_for_fmt mon:silw '#{window_silence_flag}' 0
flags_lack mon:silw '~'
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
# A window linked into two sessions: the hook fires once per winlink and
# the flag is set in both sessions. Selecting the window in one session
# clears the flags on every winlink.
$TMUX new -d -s mon2 || fail "new-session mon2 failed"
$TMUX neww -d -t mon: -n shw cat || fail "new-window shw failed"
$TMUX link-window -d -s mon:shw -t mon2:5 || fail "link-window failed"
$TMUX set -g @log '' || fail "reset @log failed"
bell mon:shw
wait_for @log '|alert-bell:mon:shw|alert-bell:mon2:shw'
wait_for_fmt mon:shw '#{window_bell_flag}' 1
wait_for_fmt mon2:5 '#{window_bell_flag}' 1
$TMUX selectw -t mon2:5 || fail "select-window mon2:5 failed"
wait_for_fmt mon2:5 '#{window_bell_flag}' 0
wait_for_fmt mon:shw '#{window_bell_flag}' 0
exit 0