diff --git a/regress/choose-buffer.sh b/regress/choose-buffer.sh new file mode 100644 index 000000000..0f3e0d8da --- /dev/null +++ b/regress/choose-buffer.sh @@ -0,0 +1,199 @@ +#!/bin/sh + +# Tests of buffer mode (window-buffer.c) as driven by choose-buffer: that the +# -f filter removes buffers that do not match (by name and by content), that +# a filter matching nothing falls back to showing everything, that -O and -r +# change the sort order, that d deletes the selected buffer and C-t and D +# delete all tagged buffers, and that Enter runs the default command +# (paste-buffer) with the selected buffer. +# +# The list is drawn on a mode screen which capture-pane does not show, so a +# second server provides a client: an inner "tmux attach" runs inside a pane +# of the second server, and that pane is captured to read what the inner +# client rendered. Each choose-buffer call uses a distinct -F marker so a +# capture can be tied to the call it belongs to. + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -Ltest -f/dev/null" +TMUX2="$TEST_TMUX -Ltest2 -f/dev/null" + +cleanup() +{ + $TMUX kill-server 2>/dev/null + $TMUX2 kill-server 2>/dev/null +} +fail() +{ + echo "$1" + cleanup + exit 1 +} + +# capture the screen rendered by the inner client +capture() +{ + $TMUX2 capture-pane -p -t out:0 2>/dev/null +} + +# wait_for $marker +# +# Wait (up to ~10s) until the rendered screen contains $marker, so the +# capture is known to show the mode instance under test. +wait_for() +{ + i=0 + while [ "$i" -lt 50 ]; do + if capture | grep -q "$1"; then + sleep 0.2 + return 0 + fi + sleep 0.2 + i=$((i + 1)) + done + fail "timed out waiting for '$1'" +} + +# wait_buffers $n +# +# Wait (up to ~10s) until the test server has exactly $n paste buffers. +wait_buffers() +{ + i=0 + while [ "$i" -lt 50 ]; do + c=$($TMUX list-buffers -F x 2>/dev/null | grep -c x) + [ "$c" -eq "$1" ] && return 0 + sleep 0.2 + i=$((i + 1)) + done + fail "expected $1 buffers, have $c" +} + +# wait_clients $n +# +# Wait (up to ~10s) until the test server has exactly $n clients attached. +wait_clients() +{ + i=0 + while [ "$i" -lt 10 ]; do + c=$($TMUX list-clients -F x 2>/dev/null | grep -c x) + [ "$c" -eq "$1" ] && return 0 + sleep 1 + i=$((i + 1)) + done + return 1 +} + +$TMUX kill-server 2>/dev/null +$TMUX2 kill-server 2>/dev/null + +$TMUX new-session -d -s aaa -x 80 -y 24 || exit 1 + +$TMUX2 new-session -d -s out -x 80 -y 24 "$TMUX attach -t aaa" || exit 1 +wait_clients 1 || fail "no client attached to test server" + +# Two named buffers with distinct contents; bufa is created first. +$TMUX set-buffer -b bufa "hello buffer" || exit 1 +$TMUX set-buffer -b bufz "other buffer" || exit 1 + +# --- filter by buffer name --------------------------------------------------- +$TMUX choose-buffer -t aaa:0 -F 'B1' -f '#{==:#{buffer_name},bufa}' || exit 1 +wait_for 'B1' +out=$(capture) +echo "$out" | grep -q 'bufa: B1' || fail "bufa missing when it matches" +echo "$out" | grep -q 'bufz: B1' && fail "bufz shown but does not match" +[ "$(echo "$out" | grep -c ': B1')" -eq 1 ] || fail "expected 1 buffer" +$TMUX send-keys -t aaa:0 q + +# --- filter by buffer content ------------------------------------------------ +$TMUX choose-buffer -t aaa:0 -F 'B2' -f '#{m:*hello*,#{buffer_sample}}' || \ + exit 1 +wait_for 'B2' +out=$(capture) +echo "$out" | grep -q 'bufa: B2' || fail "bufa missing when content matches" +echo "$out" | grep -q 'bufz: B2' && fail "bufz shown but content not matched" +$TMUX send-keys -t aaa:0 q + +# --- no filter shows both buffers --------------------------------------------- +$TMUX choose-buffer -t aaa:0 -F 'B3' || exit 1 +wait_for 'B3' +out=$(capture) +echo "$out" | grep -q 'bufa: B3' || fail "bufa missing with no filter" +echo "$out" | grep -q 'bufz: B3' || fail "bufz missing with no filter" +[ "$(echo "$out" | grep -c ': B3')" -eq 2 ] || fail "expected 2 buffers" +$TMUX send-keys -t aaa:0 q + +# --- filter matching nothing --------------------------------------------------- +# +# Everything is shown and the filter indicator reports no matches. +$TMUX choose-buffer -t aaa:0 -F 'B4' -f '#{==:#{buffer_name},nosuch}' || \ + exit 1 +wait_for 'B4' +out=$(capture) +echo "$out" | grep -q 'bufa: B4' || fail "bufa missing with no-match filter" +echo "$out" | grep -q 'bufz: B4' || fail "bufz missing with no-match filter" +echo "$out" | grep -q 'no matches' || fail "no matches indicator missing" +$TMUX send-keys -t aaa:0 q + +# --- sort orders --------------------------------------------------------------- +# +# By name bufa sorts first and -r reverses. +$TMUX choose-buffer -t aaa:0 -F 'B5' -O name || exit 1 +wait_for 'B5' +capture | grep ': B5' | head -1 | grep -q 'bufa: B5' || \ + fail "bufa not first with -O name" +$TMUX send-keys -t aaa:0 q + +$TMUX choose-buffer -t aaa:0 -F 'B6' -O name -r || exit 1 +wait_for 'B6' +capture | grep ': B6' | head -1 | grep -q 'bufz: B6' || \ + fail "bufz not first with -O name -r" +$TMUX send-keys -t aaa:0 q + +# --- d deletes the selected buffer -------------------------------------------- +# +# The filter leaves only bufz listed and selected; d deletes it. +$TMUX choose-buffer -t aaa:0 -F 'G1' -f '#{==:#{buffer_name},bufz}' || exit 1 +wait_for 'bufz: G1' +$TMUX send-keys -t aaa:0 d +wait_buffers 1 +$TMUX list-buffers -F '#{buffer_name}' | grep -q 'bufa' || \ + fail "wrong buffer deleted" +$TMUX send-keys -t aaa:0 q + +# --- C-t tags all buffers and D deletes the tagged ------------------------------ +$TMUX set-buffer -b bufz "other buffer" || exit 1 +$TMUX set-buffer -b bufb "third buffer" || exit 1 +$TMUX choose-buffer -t aaa:0 -F 'G2' || exit 1 +wait_for ': G2' +$TMUX send-keys -t aaa:0 C-t D +wait_buffers 0 +$TMUX send-keys -t aaa:0 q + +# --- Enter runs the default command (paste-buffer) ------------------------------ +# +# The only buffer is listed and selected; Enter leaves the mode and pastes it +# into the shell in the pane, where it appears on the screen. +$TMUX set-buffer -b bufa "hello buffer" || exit 1 +$TMUX choose-buffer -t aaa:0 -F 'G3' || exit 1 +wait_for 'bufa: G3' +$TMUX send-keys -t aaa:0 Enter +i=0 +while [ "$i" -lt 50 ]; do + [ "$($TMUX display -p -t aaa:0 '#{pane_in_mode}')" = "0" ] && break + sleep 0.2 + i=$((i + 1)) +done +[ "$i" -lt 50 ] || fail "mode did not exit after Enter" +i=0 +while [ "$i" -lt 50 ]; do + $TMUX capture-pane -p -t aaa:0 | grep -q 'hello buffer' && break + sleep 0.2 + i=$((i + 1)) +done +[ "$i" -lt 50 ] || fail "buffer not pasted into pane" + +cleanup +exit 0 diff --git a/regress/choose-client.sh b/regress/choose-client.sh new file mode 100644 index 000000000..e299b9852 --- /dev/null +++ b/regress/choose-client.sh @@ -0,0 +1,140 @@ +#!/bin/sh + +# Tests of client mode (window-client.c) as driven by choose-client: that the +# -f filter removes clients that do not match, that a filter matching nothing +# falls back to showing everything, and that Enter runs the default command +# (detach-client) on the selected client. Sort orders are not tested here +# because clients are named after their ttys, which are not predictable. +# +# The list is drawn on a mode screen which capture-pane does not show, so a +# second server provides the clients: two inner "tmux attach" commands run in +# panes of the second server, and the pane holding the client the mode is +# displayed on is captured. Each choose-client call uses a distinct -F marker +# so a capture can be tied to the call it belongs to. + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -Ltest -f/dev/null" +TMUX2="$TEST_TMUX -Ltest2 -f/dev/null" + +cleanup() +{ + $TMUX kill-server 2>/dev/null + $TMUX2 kill-server 2>/dev/null +} +fail() +{ + echo "$1" + cleanup + exit 1 +} + +# capture the screen rendered by the inner client attached to aaa +capture() +{ + $TMUX2 capture-pane -p -t out:0 2>/dev/null +} + +# wait_for $marker +# +# Wait (up to ~10s) until the rendered screen contains $marker, so the +# capture is known to show the mode instance under test. +wait_for() +{ + i=0 + while [ "$i" -lt 50 ]; do + if capture | grep -q "$1"; then + sleep 0.2 + return 0 + fi + sleep 0.2 + i=$((i + 1)) + done + fail "timed out waiting for '$1'" +} + +# wait_clients $n +# +# Wait (up to ~10s) until the test server has exactly $n clients attached. +wait_clients() +{ + i=0 + while [ "$i" -lt 10 ]; do + c=$($TMUX list-clients -F x 2>/dev/null | grep -c x) + [ "$c" -eq "$1" ] && return 0 + sleep 1 + i=$((i + 1)) + done + return 1 +} + +$TMUX kill-server 2>/dev/null +$TMUX2 kill-server 2>/dev/null + +# One client attached to each of two sessions; the mode is displayed on the +# client attached to aaa (in window 0 of the outer server) and the filters +# tell the clients apart by their attached session. +$TMUX new-session -d -s aaa -x 80 -y 24 || exit 1 +$TMUX new-session -d -s bbb -x 80 -y 24 || exit 1 + +$TMUX2 new-session -d -s out -x 80 -y 24 "$TMUX attach -t aaa" || exit 1 +$TMUX2 new-window -d -t out: "$TMUX attach -t bbb" || exit 1 +wait_clients 2 || fail "expected two clients attached to test server" + +# --- filter keeping only the aaa client ------------------------------------- +$TMUX choose-client -t aaa:0 -F 'C1=#{client_session}' \ + -f '#{==:#{client_session},aaa}' || exit 1 +wait_for 'C1=' +out=$(capture) +echo "$out" | grep -q 'C1=aaa' || fail "aaa client missing when it matches" +echo "$out" | grep -q 'C1=bbb' && fail "bbb client shown but does not match" +[ "$(echo "$out" | grep -c 'C1=')" -eq 1 ] || fail "expected 1 client" +$TMUX send-keys -t aaa:0 q + +# --- filter keeping only the bbb client ------------------------------------- +$TMUX choose-client -t aaa:0 -F 'C2=#{client_session}' \ + -f '#{==:#{client_session},bbb}' || exit 1 +wait_for 'C2=' +out=$(capture) +echo "$out" | grep -q 'C2=bbb' || fail "bbb client missing when it matches" +echo "$out" | grep -q 'C2=aaa' && fail "aaa client shown but does not match" +[ "$(echo "$out" | grep -c 'C2=')" -eq 1 ] || fail "expected 1 client" +$TMUX send-keys -t aaa:0 q + +# --- no filter shows both clients ------------------------------------------- +$TMUX choose-client -t aaa:0 -F 'C3=#{client_session}' || exit 1 +wait_for 'C3=' +out=$(capture) +echo "$out" | grep -q 'C3=aaa' || fail "aaa client missing with no filter" +echo "$out" | grep -q 'C3=bbb' || fail "bbb client missing with no filter" +[ "$(echo "$out" | grep -c 'C3=')" -eq 2 ] || fail "expected 2 clients" +$TMUX send-keys -t aaa:0 q + +# --- filter matching nothing ------------------------------------------------ +# +# Everything is shown and the filter indicator reports no matches. +$TMUX choose-client -t aaa:0 -F 'C4=#{client_session}' \ + -f '#{==:#{client_session},nosuch}' || exit 1 +wait_for 'C4=' +out=$(capture) +echo "$out" | grep -q 'C4=aaa' || fail "aaa client missing with no-match filter" +echo "$out" | grep -q 'C4=bbb' || fail "bbb client missing with no-match filter" +echo "$out" | grep -q 'no matches' || fail "no matches indicator missing" +$TMUX send-keys -t aaa:0 q + +# --- Enter runs the default command (detach-client) -------------------------- +# +# The filter leaves only the bbb client listed and selected; Enter detaches +# it, leaving only the aaa client attached. +$TMUX choose-client -t aaa:0 -F 'G1=#{client_session}' \ + -f '#{==:#{client_session},bbb}' || exit 1 +wait_for 'G1=bbb' +$TMUX send-keys -t aaa:0 Enter +wait_clients 1 || fail "bbb client did not detach" +[ "$($TMUX list-clients -F '#{client_session}')" = "aaa" ] || \ + fail "wrong client detached" + +cleanup +exit 0 diff --git a/regress/choose-tree.sh b/regress/choose-tree.sh new file mode 100644 index 000000000..a82e42c20 --- /dev/null +++ b/regress/choose-tree.sh @@ -0,0 +1,247 @@ +#!/bin/sh + +# Tests of tree mode (window-tree.c) as driven by choose-tree. +# +# Filtering: the -f filter is applied per pane and removes panes, windows and +# sessions with no matching panes (a window with more than one pane and no +# matching panes must disappear - GitHub issue 5326); -h keeps a window +# listed when its only matching pane is the pane the tree is drawn in; a +# filter matching nothing falls back to showing everything. +# +# Sorting: -O and -r change the sort order. +# +# Keys: h and l collapse and expand; f prompts for a filter and c clears it; +# g goes to the top; Enter runs the default command (switch-client); x kills +# the current item after a confirmation prompt. +# +# The tree is drawn on a mode screen which capture-pane does not show, so - as +# in environ-update.sh - a second server provides a client: an inner "tmux +# attach" runs inside a pane of the second server, and that pane is captured +# to read what the inner client rendered. Each choose-tree call uses a +# distinct -F marker so a capture can be tied to the call it belongs to. + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -Ltest -f/dev/null" +TMUX2="$TEST_TMUX -Ltest2 -f/dev/null" + +cleanup() +{ + $TMUX kill-server 2>/dev/null + $TMUX2 kill-server 2>/dev/null +} +fail() +{ + echo "$1" + cleanup + exit 1 +} + +# capture the screen rendered by the inner client +capture() +{ + $TMUX2 capture-pane -p -t out:0 2>/dev/null +} + +# wait_for $marker +# +# Wait (up to ~10s) until the rendered screen contains $marker, so the +# capture is known to show the mode instance under test. +wait_for() +{ + i=0 + while [ "$i" -lt 50 ]; do + if capture | grep -q "$1"; then + sleep 0.2 + return 0 + fi + sleep 0.2 + i=$((i + 1)) + done + fail "timed out waiting for '$1'" +} + +# wait_count $marker $n +# +# Wait (up to ~10s) until exactly $n rendered lines contain $marker. +wait_count() +{ + i=0 + while [ "$i" -lt 50 ]; do + [ "$(capture | grep -c "$1")" -eq "$2" ] && return 0 + sleep 0.2 + i=$((i + 1)) + done + fail "timed out waiting for $2 lines of '$1' (have $(capture | grep -c "$1"))" +} + +# wait_clients $n +# +# Wait (up to ~10s) until the test server has exactly $n clients attached. +wait_clients() +{ + i=0 + while [ "$i" -lt 10 ]; do + c=$($TMUX list-clients -F x 2>/dev/null | grep -c x) + [ "$c" -eq "$1" ] && return 0 + sleep 1 + i=$((i + 1)) + done + return 1 +} + +$TMUX kill-server 2>/dev/null +$TMUX2 kill-server 2>/dev/null + +# Session zzz is created first, so it sorts first by index, and has a +# two-pane window 0 and a single-pane window 1. Session aaa has one window +# with one pane and is where the tree is displayed. With everything expanded +# and no filter the tree is nine lines: +# +# 0 zzz 1 window 0 2 pane 0 3 pane 1 4 window 1 5 pane 0 +# 6 aaa 7 window 0 8 pane 0 +$TMUX new-session -d -s zzz -x 80 -y 24 || exit 1 +$TMUX split-window -t zzz:0 || exit 1 +$TMUX new-window -t zzz || exit 1 +$TMUX new-session -d -s aaa -x 80 -y 24 || exit 1 + +$TMUX2 new-session -d -s out -x 80 -y 24 "$TMUX attach -t aaa" || exit 1 +wait_clients 1 || fail "no client attached to test server" + +# --- filter keeping only aaa ------------------------------------------------ +# +# zzz must disappear entirely: its single-pane window 1 and - the GitHub 5326 +# regression - its two-pane window 0. aaa contributes exactly three lines +# (session, window, pane). +$TMUX choose-tree -t aaa:0 -F 'F1' -f '#{==:#{session_name},aaa}' || exit 1 +wait_count 'F1' 3 +out=$(capture) +echo "$out" | grep -q 'aaa: F1' || fail "aaa missing when filter matches it" +echo "$out" | grep -q 'zzz: F1' && fail "zzz shown but no pane matches" +$TMUX send-keys -t aaa:0 q + +# --- filter keeping only zzz ------------------------------------------------ +# +# zzz contributes six lines (session, two windows, three panes); aaa must +# disappear. +$TMUX choose-tree -t aaa:0 -F 'F2' -f '#{==:#{session_name},zzz}' || exit 1 +wait_count 'F2' 6 +out=$(capture) +echo "$out" | grep -q 'zzz: F2' || fail "zzz missing when filter matches it" +echo "$out" | grep -q 'aaa: F2' && fail "aaa shown but no pane matches" +$TMUX send-keys -t aaa:0 q + +# --- filter matching a single pane ------------------------------------------ +# +# Only pane 1 of zzz:0 matches, so the tree is exactly session zzz, window 0 +# and that pane; zzz:1 and all of aaa must disappear. +$TMUX choose-tree -t aaa:0 -F 'F3' -f '#{==:#{pane_index},1}' || exit 1 +wait_count 'F3' 3 +out=$(capture) +echo "$out" | grep -q 'zzz: F3' || fail "zzz missing when its pane matches" +echo "$out" | grep -q 'aaa: F3' && fail "aaa shown but no pane matches" +echo "$out" | grep -q '1: F3' || fail "matching pane missing" +$TMUX send-keys -t aaa:0 q + +# --- filter matching nothing ------------------------------------------------ +# +# Everything is shown and the filter indicator reports no matches. +$TMUX choose-tree -t aaa:0 -F 'F4' -f '#{==:#{session_name},nosuch}' || exit 1 +wait_for 'F4' +out=$(capture) +echo "$out" | grep -q 'aaa: F4' || fail "aaa missing with no-match filter" +echo "$out" | grep -q 'zzz: F4' || fail "zzz missing with no-match filter" +echo "$out" | grep -q 'no matches' || fail "no matches indicator missing" +$TMUX send-keys -t aaa:0 q + +# --- -h with the tree pane as the only match -------------------------------- +# +# With -h the pane the tree is drawn in is hidden, but it still counts as a +# match, so session and window aaa stay listed: two lines, no pane line. +$TMUX choose-tree -h -t aaa:0 -F 'F5' -f '#{==:#{session_name},aaa}' || \ + exit 1 +wait_count 'F5' 2 +capture | grep -q 'aaa: F5' || fail "aaa missing with -h" +$TMUX send-keys -t aaa:0 q + +# --- sort orders ------------------------------------------------------------ +# +# By index zzz (created first) sorts first, by name aaa does, and -r reverses. +$TMUX choose-tree -t aaa:0 -F 'F6' -O index || exit 1 +wait_for 'F6' +capture | grep 'F6' | head -1 | grep -q 'zzz: F6' || \ + fail "zzz not first with -O index" +$TMUX send-keys -t aaa:0 q + +$TMUX choose-tree -t aaa:0 -F 'F7' -O name || exit 1 +wait_for 'F7' +capture | grep 'F7' | head -1 | grep -q 'aaa: F7' || \ + fail "aaa not first with -O name" +$TMUX send-keys -t aaa:0 q + +$TMUX choose-tree -t aaa:0 -F 'F8' -O name -r || exit 1 +wait_for 'F8' +capture | grep 'F8' | head -1 | grep -q 'zzz: F8' || \ + fail "zzz not first with -O name -r" +$TMUX send-keys -t aaa:0 q + +# --- collapse and expand with h and l ----------------------------------------- +# +# g moves to the top (session zzz); h collapses it, hiding its five children; +# l expands it again. +$TMUX choose-tree -t aaa:0 -F 'G1' -O index || exit 1 +wait_count 'G1' 9 +$TMUX send-keys -t aaa:0 g h +wait_count 'G1' 4 +$TMUX send-keys -t aaa:0 l +wait_count 'G1' 9 +$TMUX send-keys -t aaa:0 q + +# --- filter entered at the prompt with f, cleared with c ---------------------- +$TMUX choose-tree -t aaa:0 -F 'G2' -O index || exit 1 +wait_count 'G2' 9 +$TMUX send-keys -t aaa:0 f +$TMUX send-keys -t aaa:0 -l '#{==:#{session_name},aaa}' +$TMUX send-keys -t aaa:0 Enter +wait_count 'G2' 3 +out=$(capture) +echo "$out" | grep -q 'aaa: G2' || fail "aaa missing with prompt filter" +echo "$out" | grep -q 'zzz: G2' && fail "zzz shown with prompt filter" +$TMUX send-keys -t aaa:0 c +wait_count 'G2' 9 +$TMUX send-keys -t aaa:0 q + +# --- Enter runs the default command (switch-client) ---------------------------- +# +# g selects session zzz and Enter switches the client to it. +$TMUX choose-tree -t aaa:0 -F 'G3' -O index || exit 1 +wait_count 'G3' 9 +$TMUX send-keys -t aaa:0 g Enter +i=0 +while [ "$i" -lt 50 ]; do + [ "$($TMUX list-clients -F '#{client_session}')" = "zzz" ] && break + sleep 0.2 + i=$((i + 1)) +done +[ "$i" -lt 50 ] || fail "client did not switch to zzz" +$TMUX switch-client -c "$($TMUX list-clients -F '#{client_name}')" -t aaa || \ + exit 1 + +# --- x kills the current item after confirmation ------------------------------- +# +# g and four times j select window 1 of zzz; x asks for confirmation and y +# kills it, leaving zzz with one window and the tree with seven lines. +$TMUX choose-tree -t aaa:0 -F 'G4' -O index || exit 1 +wait_count 'G4' 9 +$TMUX send-keys -t aaa:0 g j j j j x +wait_for 'Kill window 1' +$TMUX send-keys -t aaa:0 y +wait_count 'G4' 7 +[ "$($TMUX list-windows -t zzz -F x | grep -c x)" -eq 1 ] || \ + fail "window 1 of zzz not killed" +$TMUX send-keys -t aaa:0 q + +cleanup +exit 0