diff --git a/regress/alerts.sh b/regress/alerts.sh index 60d9a05f7..3f97b5328 100644 --- a/regress/alerts.sh +++ b/regress/alerts.sh @@ -2,8 +2,9 @@ # 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 +# flags (window_activity_flag, window_bell_flag, window_silence_flag, +# their session equivalents 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 @@ -111,6 +112,24 @@ flags_have() esac } +assert_session_flag() +{ + flag="#{session_${1}_flag}" + expected=$2 + + # Only mon exists here. Check both window contexts and list-sessions. + for target in mon:w0 "mon:$3"; do + value=$($TMUX display -pt "$target" "$flag") || + fail "display $flag failed" + [ "$value" = "$expected" ] || + fail "expected $flag for $target to be '$expected' but got '$value'" + done + value=$($TMUX list-sessions -F "$flag") || + fail "list-sessions $flag failed" + [ "$value" = "$expected" ] || + fail "expected list-sessions $flag to be '$expected' but got '$value'" +} + flags_lack() { target=$1 @@ -150,10 +169,12 @@ $TMUX set-hook -g alert-silence \ # 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 +assert_session_flag bell 0 bellw flags_lack mon:bellw '!' bell mon:bellw wait_for @log '|alert-bell:mon:bellw' wait_for_fmt mon:bellw '#{window_bell_flag}' 1 +assert_session_flag bell 1 bellw flags_have mon:bellw '!' # Bells are not deduplicated: a second bell fires the hook again even @@ -164,6 +185,7 @@ 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 +assert_session_flag bell 0 bellw flags_lack mon:bellw '!' $TMUX selectw -t mon:w0 || fail "select-window w0 failed" @@ -173,6 +195,7 @@ $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 +assert_session_flag bell 1 bellw $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 @@ -199,6 +222,7 @@ $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_session_flag activity 0 actw assert_unchanged @log '' # With monitor-activity on, output fires alert-activity and sets the @@ -208,6 +232,7 @@ $TMUX set -wt mon:actw monitor-activity on || activity mon:actw wait_for @log '|alert-activity:mon:actw' wait_for_fmt mon:actw '#{window_activity_flag}' 1 +assert_session_flag activity 1 actw flags_have mon:actw '#' # While the flag is set further activity does not fire the hook again. @@ -234,6 +259,7 @@ 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 +assert_session_flag activity 0 actw flags_lack mon:actw '#' $TMUX selectw -t mon:w0 || fail "select-window w0 failed" exec 3>&- @@ -250,6 +276,7 @@ $TMUX set -wt mon:w0 monitor-activity on || $TMUX set -g @log '' || fail "reset @log failed" activity mon:w0 wait_for_fmt mon:w0 '#{window_activity_flag}' 1 +assert_session_flag activity 1 actw assert_unchanged @log '' $TMUX set -wut mon:w0 monitor-activity || fail "unset monitor-activity w0 failed" @@ -273,16 +300,19 @@ $TMUX selectw -t mon:w0 || fail "select-window w0 failed" # running. $TMUX neww -d -t mon: -n silw cat || fail "new-window silw failed" assert_fmt_unchanged mon:silw '#{window_silence_flag}' 0 +assert_session_flag silence 0 silw $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 +assert_session_flag silence 1 silw 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 +assert_session_flag silence 0 silw flags_lack mon:silw '~' $TMUX selectw -t mon:w0 || fail "select-window w0 failed" diff --git a/regress/codepoint-widths.sh b/regress/codepoint-widths.sh new file mode 100644 index 000000000..ccf8fcf71 --- /dev/null +++ b/regress/codepoint-widths.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +# Width cache ranges must include their endpoint without overflowing wchar_t. + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -LtestA$$ -f/dev/null" +SERVER= +WATCHDOG= + +cleanup() +{ + if [ -n "$WATCHDOG" ]; then + kill "$WATCHDOG" 2>/dev/null + wait "$WATCHDOG" 2>/dev/null + fi + [ -n "$SERVER" ] && kill -9 "$SERVER" 2>/dev/null +} +trap cleanup 0 +trap 'exit 1' 1 2 15 + +$TMUX new-session -d 'exec sleep 60' || exit 1 +SERVER=$($TMUX display-message -p '#{pid}') || exit 1 + +# kill-server cannot stop a server stuck rebuilding the width cache. +( + sleep 5 + kill -9 "$SERVER" 2>/dev/null +) & +WATCHDOG=$! + +$TMUX set -g codepoint-widths 'U+1F600=2' || exit 1 + +# Cover both signed and unsigned 32-bit wchar_t. Values above WCHAR_MAX +# are ignored by the parser on platforms where they cannot be represented. +for value in U+7FFFFFFF=1 U+7FFFFFFE-U+7FFFFFFF=2 \ + U+FFFFFFFF=1 U+FFFFFFFE-U+FFFFFFFF=2; do + $TMUX set -g codepoint-widths "$value" || exit 1 + [ "$($TMUX display-message -p alive)" = alive ] || exit 1 +done + +# Check that an ordinary range still includes both endpoints and stops there. +$TMUX set -g codepoint-widths 'U+03B1-U+03B3=2' || exit 1 +$TMUX set -g @text 'αβγδ' || exit 1 +[ "$($TMUX display-message -p '#{w:@text}')" = 7 ] || exit 1 +$TMUX set -gu codepoint-widths || exit 1 +[ "$($TMUX display-message -p '#{w:@text}')" = 4 ] || exit 1 + +$TMUX kill-server || exit 1 +SERVER= +exit 0 diff --git a/regress/kill-session-zoomed.sh b/regress/kill-session-zoomed.sh new file mode 100755 index 000000000..c27135ba2 --- /dev/null +++ b/regress/kill-session-zoomed.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +# Killing a session whose window is zoomed must not crash the server: +# window_destroy used to unzoom the window, which resized the panes and +# fired pane-resized with the window in the payload, and releasing that +# reference destroyed the window a second time. + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -LtestA$$ -f/dev/null" +$TMUX kill-server 2>/dev/null + +$TMUX -f/dev/null new -d -sfoo || exit 1 +$TMUX split-window -d -h -tfoo:0 || exit 1 +$TMUX resize-pane -Z -tfoo:0 || exit 1 +$TMUX new -d -sbar || exit 1 +$TMUX kill-session -tfoo || exit 1 +$TMUX has-session -tbar || exit 1 + +# The same with the zoomed window in a session killed by kill-server. +$TMUX new -d -sbaz || exit 1 +$TMUX split-window -d -v -tbaz:0 || exit 1 +$TMUX resize-pane -Z -tbaz:0 || exit 1 +$TMUX kill-server || exit 1 + +exit 0 diff --git a/regress/layout-custom.sh b/regress/layout-custom.sh index e9537b9e5..3b7a2de13 100644 --- a/regress/layout-custom.sh +++ b/regress/layout-custom.sh @@ -50,7 +50,8 @@ # layout it names is a single cell or a split; # - a window whose only tiled pane has been killed, which leaves it with a # floating cell as its layout root or with a root node holding nothing but -# floating cells, producing no v1 dump at all, and being parsed as v1; +# floating cells, producing an empty v1 body with a checksum, and being +# parsed as v1; # - the %layout-change notification, in both formats at once: two control # clients watching one layout change, only one of which has asked for new # layouts, and the number of notifications a change produces in each format; @@ -838,12 +839,12 @@ must_equal 'Panes left with one floating pane' \ "$($TMUX display-message -p -t L:gone1 '#{window_panes}')" '1' # The floating cell is the root and there is nothing tiled under it, so there is -# no v1 dump to make. In particular the floating cell must not be written out on +# only an empty v1 body to dump. The floating cell must not be written out on # its own, which would be a layout claiming the window is the size and position # of the floating pane with no pane in it at all. got=$(v1_layout L:gone1) check_ok display-message -p alive -must_equal 'v1 dump with one floating pane and no tiled panes' "$got" '' +must_equal 'v1 dump with one floating pane and no tiled panes' "$got" '0000,' # Two floating panes left, so the node keeps two children, does not collapse, # and stays the root with nothing but floating cells in it. @@ -856,11 +857,11 @@ must_equal 'Panes left with two floating panes' \ "$($TMUX display-message -p -t L:gone2 '#{window_panes}')" '2' # The node is the root this time rather than the floating cell, but it has no -# tiled cell anywhere under it either, so there is still no v1 dump to make - +# tiled cell anywhere under it either, so the v1 body is still empty - # and making one must not take the server with it. got=$(v1_layout L:gone2) check_ok display-message -p alive -must_equal 'v1 dump with two floating panes and no tiled panes' "$got" '' +must_equal 'v1 dump with two floating panes and no tiled panes' "$got" '0000,' # Nor must parsing a v1 layout against it. There is no tiled pane for the # layout to name, so whether it is applied or rejected is the format's business; diff --git a/regress/modal-pane.sh b/regress/modal-pane.sh index b4217aa0a..0ae913b0b 100644 --- a/regress/modal-pane.sh +++ b/regress/modal-pane.sh @@ -337,9 +337,9 @@ $TMUX set -g @modal-prefix no $TMUX set -g @modal-root no $TMUX bind -n z set -g @modal-root yes -modal=$($TMUX new-pane -OKPF '#{pane_id}' -t "$p0" \ +modal=$($TMUX new-pane -ODKPF '#{pane_id}' -t "$p0" \ -x 20 -y 5 -X 20 -Y 10 'cat') || - fail "new-pane -OK failed" + fail "new-pane -ODK failed" sleep 1 $TMUX2 send-keys -t "$OUTER" C-b x z Enter sleep 1 @@ -356,9 +356,50 @@ new_left=$(fmt "$modal" '#{pane_left}') new_top=$(fmt "$modal" '#{pane_top}') [ "$new_left" -gt "$left" ] || [ "$new_top" -gt "$top" ] || fail "key-capturing modal pane did not move" +$TMUX2 send-keys -t "$OUTER" Escape +sleep 1 +must_equal "$(fmt modal:0 '#{window_modal_pane}')" '' + +modal=$($TMUX new-pane -ODKPF '#{pane_id}' -t "$p0" \ + -x 20 -y 5 -X 20 -Y 10 'trap "" INT; exec cat') || + fail "new-pane -ODK failed" +sleep 1 +$TMUX2 send-keys -t "$OUTER" C-c +sleep 1 +must_equal "$(fmt modal:0 '#{window_modal_pane}')" '' + +# A dead modal does not close on Escape or C-c without -D. +modal=$($TMUX new-pane -OPF '#{pane_id}' -t "$p0" \ + -x 20 -y 5 -X 20 -Y 10 'sleep 1') || + fail "new-pane -O failed" +check_ok set-option -p -t "$modal" remain-on-exit on +sleep 2 +must_equal "$(fmt "$modal" '#{pane_dead}:#{pane_modal_flag}')" 1:1 +$TMUX2 send-keys -t "$OUTER" Escape +sleep 1 +must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal" check_ok kill-pane -t "$modal" sleep 1 +# failed-key closes successful panes and retains failed panes until a key. +modal=$($TMUX new-pane -OPF '#{pane_id}' -t "$p0" \ + -x 20 -y 5 -X 20 -Y 10 'sleep 1') || + fail "new-pane -O failed" +check_ok set-option -p -t "$modal" remain-on-exit failed-key +sleep 2 +must_equal "$(fmt modal:0 '#{window_modal_pane}')" '' + +modal=$($TMUX new-pane -OPF '#{pane_id}' -t "$p0" \ + -x 20 -y 5 -X 20 -Y 10 'sleep 1; exit 1') || + fail "new-pane -O failed" +check_ok set-option -p -t "$modal" remain-on-exit failed-key +sleep 2 +must_equal "$(fmt "$modal" '#{pane_dead}:#{pane_modal_flag}')" 1:1 +must_equal "$($TMUX show-options -pv -t "$modal" remain-on-exit)" failed-key +$TMUX2 send-keys -t "$OUTER" a +sleep 1 +must_equal "$(fmt modal:0 '#{window_modal_pane}')" '' + # A nonmodal floating pane may remain above zoom, and switching between it and # the zoomed tiled pane must not unzoom the window. check_ok new-window -d -t modal: -n float-over-zoom 'cat' diff --git a/regress/run-shell-cwd.sh b/regress/run-shell-cwd.sh new file mode 100644 index 000000000..abcc89ee6 --- /dev/null +++ b/regress/run-shell-cwd.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +# run-shell -c should expand formats using the target pane. + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -LtestA$$ -f/dev/null" + +TMP=$(mktemp -d) +trap '$TMUX kill-server 2>/dev/null; rm -rf "$TMP"' 0 1 15 +TMP=$(cd "$TMP" && pwd -P) +mkdir "$TMP/first" "$TMP/second dir" || exit 1 + +check_directory() +{ + actual=$(cat "$TMP/out") + if [ "$actual" != "$1" ]; then + echo "Expected directory '$1', got '$actual'" + exit 1 + fi +} + +# Expand the current pane's directory in the same command queue as creation. +$TMUX new-session -d -s test -c "$TMP/first" 'sleep 60' \; \ + run-shell -c '#{pane_current_path}' "pwd >'$TMP/out'" || exit 1 +check_directory "$TMP/first" + +# An explicit target must supply the format context, including with a delay. +pane=$($TMUX new-window -d -P -F '#{pane_id}' -t test \ + -c "$TMP/second dir" 'sleep 60') || exit 1 +$TMUX run-shell -t "$pane" -d 0.1 -c '#{pane_current_path}' \ + "pwd >'$TMP/out'" || exit 1 +check_directory "$TMP/second dir" + +# Literal directories and the default client directory still work. +$TMUX run-shell -c "$TMP/second dir" "pwd >'$TMP/out'" || exit 1 +check_directory "$TMP/second dir" +(cd "$TMP/first" && $TMUX run-shell "pwd >'$TMP/out'") || exit 1 +check_directory "$TMP/first" + +exit 0 diff --git a/regress/tty-keys.sh b/regress/tty-keys.sh index 19b1623dd..0a3ef318c 100644 --- a/regress/tty-keys.sh +++ b/regress/tty-keys.sh @@ -293,6 +293,16 @@ assert_key 'Escape [24@' 'C-S-F12' assert_key 'Escape [I' 'FocusIn' assert_key 'Escape [O' 'FocusOut' +# Focus events do not leave the prefix table. +$TMUX2 bind C-c set -g @focus-prefix yes +$TMUX2 set -g focus-events on +$TMUX send-keys C-b Escape '[O' Escape '[I' C-c +sleep 0.05 +if [ "$($TMUX2 show -gv @focus-prefix)" != yes ]; then + echo "[FAIL] FocusIn and FocusOut left the prefix table" + exit_status=1 +fi + # Paste keys assert_key 'Escape [200~' 'PasteStart' assert_key 'Escape [201~' 'PasteEnd'