mirror of
https://github.com/tmux/tmux.git
synced 2026-07-03 10:12:31 +00:00
Prompt regress (will not pass yet).
This commit is contained in:
300
regress/prompt-keys.sh
Normal file
300
regress/prompt-keys.sh
Normal file
@@ -0,0 +1,300 @@
|
||||
#!/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)
|
||||
OUT="$TEST_TMUX -Ltest -f/dev/null" # outer (host for the client)
|
||||
IN="$TEST_TMUX -Ltest2 -f/dev/null" # inner (under test)
|
||||
|
||||
$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" EXIT
|
||||
|
||||
fail() {
|
||||
echo "[FAIL] $1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Capture the outer pane: what the inner client rendered. No -e, so we match
|
||||
# plain visible text, not styles or escape sequences.
|
||||
capture() {
|
||||
$OUT capture-pane -p
|
||||
}
|
||||
|
||||
# The "(search) ..." prompt row of a mode prompt.
|
||||
search_row() {
|
||||
capture | grep '(search)' | head -1
|
||||
}
|
||||
|
||||
# The inner status line is the last row of the outer capture (status is at the
|
||||
# bottom). Used to assert a prompt is in the pane and not on the status line.
|
||||
status_line() {
|
||||
capture | tail -1
|
||||
}
|
||||
|
||||
# Inner mode must still be a tree mode (the prompt did not close or crash it).
|
||||
in_tree_mode() {
|
||||
[ "$($IN display-message -p '#{pane_mode}')" = "tree-mode" ]
|
||||
}
|
||||
|
||||
# Small settle for the key -> inner server -> inner client -> outer pane round
|
||||
# trip to redraw before we capture. Matches the short waits other regress tests
|
||||
# use; we do not depend on exact timing beyond the redraw completing.
|
||||
settle() {
|
||||
sleep 0.5
|
||||
}
|
||||
|
||||
# Assert the mode search prompt currently shows exactly "(search) <want>".
|
||||
search_is() {
|
||||
want=$1; msg=$2
|
||||
search_row | grep -qF "(search) $want" || \
|
||||
fail "$msg (wanted '(search) $want', got '$(search_row)')"
|
||||
}
|
||||
|
||||
# --- Inner session under test. ---------------------------------------------
|
||||
#
|
||||
# Two windows so the tree has content; status on so we can distinguish the pane
|
||||
# from the status line; fixed size and manual sizing so the layout is stable.
|
||||
# A root-table key opens a status-line command prompt whose accept action writes
|
||||
# the final buffer into @r, so we can recover it exactly.
|
||||
$IN new -d -x80 -y24 "sh -c 'exec sleep 1000'" || exit 1
|
||||
$IN set -g status on || exit 1
|
||||
$IN set -g status-position bottom || exit 1
|
||||
$IN set -g status-keys emacs || exit 1
|
||||
$IN set -g window-size manual || exit 1
|
||||
$IN new-window -d "sh -c 'exec sleep 1000'" || exit 1
|
||||
$IN bind -n M-r command-prompt -p ">" "set -g @r '%%'" || exit 1
|
||||
|
||||
# --- Outer session: attach the inner one inside its pane. -------------------
|
||||
$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
|
||||
|
||||
# ===========================================================================
|
||||
# Mode prompt (choose-tree search): the thorough engine vehicle.
|
||||
# ===========================================================================
|
||||
|
||||
$IN choose-tree || exit 1
|
||||
settle
|
||||
in_tree_mode || fail "choose-tree did not enter tree-mode"
|
||||
|
||||
# --- 1. Search prompt is drawn in the pane, not on the status line. ---
|
||||
$IN send-keys C-s || exit 1
|
||||
settle
|
||||
search_row | grep -q '(search)' || fail "search prompt not drawn in the pane"
|
||||
status_line | grep -q '(search)' && \
|
||||
fail "search prompt drawn on the status line, not in the pane"
|
||||
|
||||
# --- 2. emacs editing: insert and delete at middle, start and end. ---
|
||||
# Cursor position is checked behaviourally: move, insert a marker, read the row.
|
||||
# This needs no cursor coordinates and fails if a movement/edit key is wrong.
|
||||
|
||||
# Middle insert: "abcd", Left Left (cursor between b and c), insert X -> abXcd.
|
||||
$IN send-keys -l "abcd" || exit 1
|
||||
settle
|
||||
search_is "abcd" "literal input not shown in search prompt"
|
||||
$IN send-keys Left Left || exit 1
|
||||
$IN send-keys -l "X" || exit 1
|
||||
settle
|
||||
search_is "abXcd" "middle insert wrong (Left/insert)"
|
||||
|
||||
# Middle delete: BSpace removes X (before cursor), DC removes c (at cursor).
|
||||
$IN send-keys BSpace || exit 1
|
||||
$IN send-keys DC || exit 1
|
||||
settle
|
||||
search_is "abd" "middle delete wrong (BSpace/Delete)"
|
||||
|
||||
# Clear, then start/end insert with C-a and C-e.
|
||||
$IN send-keys C-u || exit 1
|
||||
$IN send-keys -l "mno" || exit 1
|
||||
$IN send-keys C-a || exit 1
|
||||
$IN send-keys -l "S" || exit 1
|
||||
$IN send-keys C-e || exit 1
|
||||
$IN send-keys -l "E" || exit 1
|
||||
settle
|
||||
search_is "SmnoE" "C-a/C-e start/end insert wrong"
|
||||
|
||||
# Word kill: "hello world", C-w removes the last word leaving "hello " (with the
|
||||
# separating space). capture-pane trims trailing spaces, so make the space
|
||||
# visible by inserting a marker after it: the buffer becomes "hello Z".
|
||||
$IN send-keys C-u || exit 1
|
||||
$IN send-keys -l "hello world" || exit 1
|
||||
$IN send-keys C-w || exit 1
|
||||
$IN send-keys -l "Z" || exit 1
|
||||
settle
|
||||
search_is "hello Z" "C-w did not kill a word"
|
||||
|
||||
# C-a then C-k kills the whole line.
|
||||
$IN send-keys C-a || exit 1
|
||||
$IN send-keys C-k || exit 1
|
||||
settle
|
||||
search_row | grep -q '(search) [^ ]' && fail "C-a C-k did not clear the line"
|
||||
|
||||
# --- 3. Editing kept the prompt open the whole time. ---
|
||||
in_tree_mode || fail "editing keys closed the mode"
|
||||
search_row | grep -q '(search)' || fail "editing keys closed the prompt"
|
||||
|
||||
# --- 4. Unicode wide character: insert, render, delete as one unit. ---
|
||||
$IN send-keys C-u || exit 1
|
||||
$IN send-keys -l "a中b" || exit 1
|
||||
settle
|
||||
search_is "a中b" "wide character not shown"
|
||||
# Left moves over "b" (one column); BSpace deletes the wide "中" as a single
|
||||
# width-2 unit, leaving "ab".
|
||||
$IN send-keys Left || exit 1
|
||||
$IN send-keys BSpace || exit 1
|
||||
settle
|
||||
search_is "ab" "wide character not deleted as one unit"
|
||||
|
||||
# --- 5. Control character: quote-next inserts it literally, shown as ^G. ---
|
||||
$IN send-keys C-u || exit 1
|
||||
$IN send-keys -l "a" || exit 1
|
||||
$IN send-keys C-v || exit 1 # quote next key
|
||||
$IN send-keys C-g || exit 1 # literal BEL -> displayed as ^G
|
||||
$IN send-keys -l "b" || exit 1
|
||||
settle
|
||||
search_is "a^Gb" "control character not shown as ^G"
|
||||
# Deleted as a single unit too.
|
||||
$IN send-keys Left || exit 1
|
||||
$IN send-keys BSpace || exit 1
|
||||
settle
|
||||
search_is "ab" "control character not deleted as one unit"
|
||||
|
||||
# --- 6. Kill and yank: C-w fills the yank buffer, C-y pastes it at the cursor. ---
|
||||
# (prompt_key only fills the yank buffer from C-w; C-y then yanks that text, or
|
||||
# the top paste buffer if nothing has been killed. Establish our own kill here
|
||||
# so the result is deterministic.)
|
||||
$IN send-keys C-u || exit 1
|
||||
$IN send-keys -l "one two" || exit 1
|
||||
$IN send-keys C-w || exit 1 # kill "two", buffer "one "
|
||||
$IN send-keys C-y || exit 1 # yank it back -> "one two"
|
||||
settle
|
||||
search_is "one two" "C-y did not yank the killed text"
|
||||
$IN send-keys C-y || exit 1 # yank again at cursor -> "one twotwo"
|
||||
settle
|
||||
search_is "one twotwo" "second C-y did not yank again"
|
||||
|
||||
# --- 7. History: accept a string, reopen, Up recalls it. ---
|
||||
$IN send-keys C-u || exit 1
|
||||
$IN send-keys -l "alpha" || exit 1
|
||||
$IN send-keys Enter || exit 1
|
||||
settle
|
||||
$IN send-keys C-s || exit 1
|
||||
settle
|
||||
$IN send-keys Up || exit 1
|
||||
settle
|
||||
search_is "alpha" "history (Up) did not recall the previous entry"
|
||||
|
||||
# --- 8. Escape closes the prompt but leaves the mode open. ---
|
||||
$IN send-keys Escape || exit 1
|
||||
settle
|
||||
search_row | grep -q '(search)' && fail "Escape left a dangling search prompt"
|
||||
in_tree_mode || fail "Escape closed the mode as well as the prompt"
|
||||
|
||||
# Leave the mode.
|
||||
$IN send-keys q || exit 1
|
||||
settle
|
||||
|
||||
# ===========================================================================
|
||||
# Status-line prompt (command-prompt): the same engine on the status line.
|
||||
# Keys go through the inner client's terminal (outer send-keys); the accepted
|
||||
# buffer is recovered exactly via %% -> @r.
|
||||
# ===========================================================================
|
||||
|
||||
# --- 10. Prompt is drawn on the status line (the last row). ---
|
||||
$IN set -g @r "" || exit 1
|
||||
$OUT send-keys M-r || exit 1
|
||||
settle
|
||||
status_line | grep -q '>' || fail "status-line prompt not drawn on the status line"
|
||||
|
||||
# --- 11. emacs cursor-marker edit, accept recovers the exact buffer. ---
|
||||
$OUT send-keys -l "abc" || exit 1
|
||||
$OUT send-keys Home || exit 1
|
||||
$OUT send-keys -l "X" || exit 1
|
||||
settle
|
||||
status_line | grep -qF "> Xabc" || \
|
||||
fail "status-line edit wrong (got '$(status_line)')"
|
||||
$OUT send-keys Enter || exit 1
|
||||
settle
|
||||
[ "$($IN show -gv @r)" = "Xabc" ] || \
|
||||
fail "status-line accept recovered '$($IN show -gv @r)', wanted 'Xabc'"
|
||||
|
||||
# --- 12. Unicode on the status line: insert, move, delete wide char. ---
|
||||
$IN set -g @r "" || exit 1
|
||||
$OUT send-keys M-r || exit 1
|
||||
settle
|
||||
$OUT send-keys -l "a㋡b" || exit 1
|
||||
settle
|
||||
status_line | grep -qF "a㋡b" || \
|
||||
fail "status-line wide character not shown (got '$(status_line)')"
|
||||
# Home, insert Z (start); End, BSpace (delete b); BSpace (delete wide char).
|
||||
$OUT send-keys Home || exit 1
|
||||
$OUT send-keys -l "Z" || exit 1
|
||||
$OUT send-keys End || exit 1
|
||||
$OUT send-keys BSpace || exit 1
|
||||
$OUT send-keys BSpace || exit 1
|
||||
settle
|
||||
$OUT send-keys Enter || exit 1
|
||||
settle
|
||||
[ "$($IN show -gv @r)" = "Za" ] || \
|
||||
fail "status-line wide edit recovered '$($IN show -gv @r)', wanted 'Za'"
|
||||
|
||||
# --- 13. Overflow: more text than fits stays within the line and is kept. ---
|
||||
big="0123456789012345678901234567890123456789012345678901234567890123456789ABCDEFGHIJ"
|
||||
$IN set -g @r "" || exit 1
|
||||
$OUT send-keys M-r || exit 1
|
||||
settle
|
||||
$OUT send-keys -l "$big" || exit 1
|
||||
settle
|
||||
# The drawn status line must not exceed the client width (80): no wrap, no crash.
|
||||
width=$(status_line | awk '{print length($0)}')
|
||||
[ "$width" -le 80 ] || fail "overflowing prompt drew $width columns, wider than 80"
|
||||
$OUT send-keys Enter || exit 1
|
||||
settle
|
||||
# The whole buffer was kept despite only part being visible.
|
||||
[ "$($IN show -gv @r)" = "$big" ] || fail "overflowing prompt lost buffer content"
|
||||
|
||||
# --- 14. Escape closes the status-line prompt cleanly. ---
|
||||
$IN set -g @r "SENTINEL" || exit 1
|
||||
$OUT send-keys M-r || exit 1
|
||||
settle
|
||||
$OUT send-keys -l "discard" || exit 1
|
||||
$OUT send-keys Escape || exit 1
|
||||
settle
|
||||
status_line | grep -q '> discard' && fail "Escape left a dangling status-line prompt"
|
||||
[ "$($IN show -gv @r)" = "SENTINEL" ] || fail "Escape ran the prompt's accept action"
|
||||
|
||||
# ===========================================================================
|
||||
# Two clients attached to the same window: a mode prompt must render on both.
|
||||
# ===========================================================================
|
||||
|
||||
$OUT new-window || exit 1
|
||||
$OUT set -g status off || exit 1
|
||||
$OUT send-keys -l "$IN attach" || exit 1
|
||||
$OUT send-keys Enter || exit 1
|
||||
sleep 1
|
||||
|
||||
$IN choose-tree || exit 1
|
||||
settle
|
||||
$IN send-keys C-s || exit 1
|
||||
settle
|
||||
$IN send-keys -l "dual" || exit 1
|
||||
settle
|
||||
for w in $($OUT list-windows -F '#{window_index}'); do
|
||||
$OUT capture-pane -t ":$w" -p | grep -qF "(search) dual" || \
|
||||
fail "mode prompt not shown on client in outer window $w"
|
||||
done
|
||||
$IN send-keys Escape || exit 1
|
||||
settle
|
||||
|
||||
# --- Inner tmux is still alive and responsive. ---
|
||||
$IN display-message -p '#{version}' >/dev/null 2>&1 || fail "inner tmux died"
|
||||
|
||||
exit 0
|
||||
336
regress/prompt-mechanics.sh
Normal file
336
regress/prompt-mechanics.sh
Normal file
@@ -0,0 +1,336 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Exercise the prompt mechanics shared by all three host paths of the prompt
|
||||
# engine in prompt.c:
|
||||
#
|
||||
# status.c status_prompt_set - the status-line command prompt.
|
||||
# window.c window_pane_set_prompt - a prompt drawn over a pane (-P).
|
||||
# mode-tree.c mode_tree_set_prompt - search/filter prompts in tree modes.
|
||||
#
|
||||
# prompt-keys.sh covers the editing keys; this test covers that each path OPENS
|
||||
# and DRAWS in the right place and that the prompt flags select the right engine
|
||||
# behaviour: -1 (single), -N (numeric), -i (incremental), -k (key), -e
|
||||
# (backspace exit), -I (prefill), multi prompts, type-scoped history and command
|
||||
# completion.
|
||||
|
||||
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="$TEST_TMUX -Ltest -f/dev/null" # outer (host for the client)
|
||||
IN="$TEST_TMUX -Ltest2 -f/dev/null" # inner (under test)
|
||||
|
||||
$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" EXIT
|
||||
|
||||
fail() {
|
||||
echo "[FAIL] $1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Capture the outer pane: what the inner client rendered. Pane-area prompts
|
||||
# (over a pane or in a tree mode) and the status-line prompt all end up here.
|
||||
capture() {
|
||||
$OUT capture-pane -p
|
||||
}
|
||||
|
||||
# The inner status line is the last row of the outer capture.
|
||||
status_line() {
|
||||
capture | tail -1
|
||||
}
|
||||
|
||||
# The recovered buffer. Every prompt below accepts into the @r option so the
|
||||
# exact final string can be checked; reset it to a sentinel first so we can tell
|
||||
# "accept ran" from "prompt cancelled".
|
||||
got() {
|
||||
$IN show -gv @r
|
||||
}
|
||||
reset() {
|
||||
$IN set -g @r "SENTINEL" || exit 1
|
||||
}
|
||||
|
||||
# Settle for the key -> inner server -> inner client -> outer pane redraw round
|
||||
# trip, as in prompt-keys.sh.
|
||||
settle() {
|
||||
sleep 0.5
|
||||
}
|
||||
|
||||
# --- Inner session under test. ---------------------------------------------
|
||||
#
|
||||
# The window is created at -y23 so that, with a one-row bottom status, the pane
|
||||
# area (23) exactly fills the 24-row client: a pane prompt drawn on the pane's
|
||||
# bottom row lands on visible row 23, just above the status line on row 24. A
|
||||
# second, distinctively named window gives the tree something to filter.
|
||||
$IN new -d -x80 -y23 -n YAK "sh -c 'exec sleep 1000'" || exit 1
|
||||
$IN set -g status on || exit 1
|
||||
$IN set -g status-position bottom || exit 1
|
||||
$IN set -g status-keys emacs || exit 1
|
||||
$IN set -g window-size manual || exit 1
|
||||
$IN new-window -d -n ZEBRA "sh -c 'exec sleep 1000'" || exit 1
|
||||
|
||||
# One root-table key per path/flag. The accept template records the final
|
||||
# buffer in @r (or both buffers, for the multi-prompt case).
|
||||
$IN bind -n M-s command-prompt -p '(stat)' "set -g @r '%%'" || exit 1
|
||||
$IN bind -n M-p command-prompt -P -p '(pane)' "set -g @r '%%'" || exit 1
|
||||
$IN bind -n M-o command-prompt -P -1 -p '(one)' "set -g @r '%%'" || exit 1
|
||||
$IN bind -n M-n command-prompt -P -N -I 5 -p '(num)' "set -g @r '%%'" || exit 1
|
||||
$IN bind -n M-i command-prompt -P -i -p '(inc)' "set -g @r '%%'" || exit 1
|
||||
$IN bind -n M-k command-prompt -P -k -p '(key)' "set -g @r '%%'" || exit 1
|
||||
$IN bind -n M-e command-prompt -P -e -p '(bs)' "set -g @r '%%'" || exit 1
|
||||
$IN bind -n M-j command-prompt -P -I hello -p '(pre)' "set -g @r '%%'" || exit 1
|
||||
$IN bind -n M-m command-prompt -p 'first,second' "set -g @r '%1/%2'" || exit 1
|
||||
$IN bind -n M-c command-prompt -p '(cmd)' "set -g @r '%%'" || exit 1
|
||||
$IN bind -n M-h command-prompt -T search -p '(srch)' "set -g @r '%%'" || exit 1
|
||||
|
||||
# --- Outer session: attach the inner one inside its pane. -------------------
|
||||
$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
|
||||
|
||||
# ===========================================================================
|
||||
# 1. Each path opens and draws in the right place.
|
||||
# ===========================================================================
|
||||
|
||||
# --- 1a. status.c: drawn on the status line (the last row). ---
|
||||
reset
|
||||
$OUT send-keys M-s || exit 1
|
||||
settle
|
||||
status_line | grep -qF '(stat)' || \
|
||||
fail "status-line prompt not on the status line (got '$(status_line)')"
|
||||
$OUT send-keys -l "go" || exit 1
|
||||
$OUT send-keys Enter || exit 1
|
||||
settle
|
||||
[ "$(got)" = "go" ] || fail "status-line accept recovered '$(got)', wanted 'go'"
|
||||
|
||||
# --- 1b. window.c: drawn over the pane, not on the status line. ---
|
||||
reset
|
||||
$OUT send-keys M-p || exit 1
|
||||
settle
|
||||
capture | grep -qF '(pane)' || fail "pane prompt not drawn in the pane"
|
||||
status_line | grep -qF '(pane)' && \
|
||||
fail "pane prompt drawn on the status line, not over the pane"
|
||||
$OUT send-keys -l "deep" || exit 1
|
||||
$OUT send-keys Enter || exit 1
|
||||
settle
|
||||
[ "$(got)" = "deep" ] || fail "pane prompt accept recovered '$(got)', wanted 'deep'"
|
||||
|
||||
# --- 1c. mode-tree.c: search prompt drawn in the pane. ---
|
||||
$IN choose-tree || exit 1
|
||||
settle
|
||||
[ "$($IN display-message -p '#{pane_mode}')" = "tree-mode" ] || \
|
||||
fail "choose-tree did not enter tree-mode"
|
||||
$IN send-keys C-s || exit 1
|
||||
settle
|
||||
capture | grep -qF '(search)' || fail "mode-tree search prompt not drawn in the pane"
|
||||
status_line | grep -qF '(search)' && \
|
||||
fail "mode-tree search prompt drawn on the status line"
|
||||
$IN send-keys Escape || exit 1
|
||||
settle
|
||||
|
||||
# --- 1d. mode-tree.c: filter prompt opens, applies, prefills, and clears. ---
|
||||
$IN send-keys f || exit 1
|
||||
settle
|
||||
capture | grep -qF '(filter)' || fail "mode-tree filter prompt not drawn"
|
||||
$IN send-keys -l "ZEBRA" || exit 1
|
||||
$IN send-keys Enter || exit 1
|
||||
settle
|
||||
capture | grep -q 'filter: active' || fail "accepting the filter did not apply it"
|
||||
# Reopening the filter prompt prefills it with the current filter.
|
||||
$IN send-keys f || exit 1
|
||||
settle
|
||||
capture | grep -qF '(filter) ZEBRA' || \
|
||||
fail "filter prompt not prefilled with the current filter"
|
||||
$IN send-keys Escape || exit 1
|
||||
settle
|
||||
# 'c' clears the filter.
|
||||
$IN send-keys c || exit 1
|
||||
settle
|
||||
capture | grep -q 'filter: active' && fail "'c' did not clear the filter"
|
||||
$IN send-keys q || exit 1
|
||||
settle
|
||||
|
||||
# ===========================================================================
|
||||
# 2. Flags select the right engine behaviour.
|
||||
# ===========================================================================
|
||||
|
||||
# --- 2a. -1 (PROMPT_SINGLE): one keystroke closes and accepts that char. ---
|
||||
reset
|
||||
$OUT send-keys M-o || exit 1
|
||||
settle
|
||||
capture | grep -qF '(one)' || fail "single prompt did not open"
|
||||
$OUT send-keys -l "q" || exit 1
|
||||
settle
|
||||
[ "$(got)" = "q" ] || fail "single prompt recovered '$(got)', wanted 'q'"
|
||||
capture | grep -qF '(one)' && fail "single prompt stayed open after one key"
|
||||
|
||||
# --- 2b. -N (PROMPT_NUMERIC): prefilled, digits append, non-digit closes. ---
|
||||
reset
|
||||
$OUT send-keys M-n || exit 1
|
||||
settle
|
||||
capture | grep -qF '(num) 5' || fail "numeric prompt not prefilled with 5"
|
||||
$OUT send-keys -l "7" || exit 1 # 57
|
||||
$OUT send-keys Enter || exit 1 # Enter is a non-digit: close
|
||||
settle
|
||||
[ "$(got)" = "57" ] || fail "numeric accept recovered '$(got)', wanted '57'"
|
||||
# A non-digit key closes the prompt with the existing buffer, dropping the key.
|
||||
reset
|
||||
$OUT send-keys M-n || exit 1
|
||||
settle
|
||||
$OUT send-keys -l "x" || exit 1
|
||||
settle
|
||||
[ "$(got)" = "5" ] || fail "numeric non-digit close recovered '$(got)', wanted '5'"
|
||||
capture | grep -qF '(num)' && fail "numeric prompt stayed open after a non-digit"
|
||||
|
||||
# --- 2c. -i (PROMPT_INCREMENTAL): callback fires on every edit, stays open. ---
|
||||
# The incremental code path prefixes the buffer with '=' (or +/-), so the '='
|
||||
# proves the value came through the incremental callback, not a plain accept.
|
||||
reset
|
||||
$OUT send-keys M-i || exit 1
|
||||
settle
|
||||
[ "$(got)" = "=" ] || fail "incremental prompt did not fire on open (got '$(got)')"
|
||||
$OUT send-keys -l "a" || exit 1
|
||||
settle
|
||||
[ "$(got)" = "=a" ] || fail "incremental did not fire after 'a' (got '$(got)')"
|
||||
$OUT send-keys -l "b" || exit 1
|
||||
settle
|
||||
[ "$(got)" = "=ab" ] || fail "incremental did not fire after 'b' (got '$(got)')"
|
||||
capture | grep -qF '(inc)' || fail "incremental prompt closed during editing"
|
||||
$OUT send-keys Escape || exit 1
|
||||
settle
|
||||
capture | grep -qF '(inc)' && fail "Escape did not close the incremental prompt"
|
||||
|
||||
# --- 2d. -k (PROMPT_KEY): the next key closes and delivers its name. ---
|
||||
reset
|
||||
$OUT send-keys M-k || exit 1
|
||||
settle
|
||||
capture | grep -qF '(key)' || fail "key prompt did not open"
|
||||
$OUT send-keys -l "z" || exit 1
|
||||
settle
|
||||
[ "$(got)" = "z" ] || fail "key prompt recovered '$(got)', wanted 'z'"
|
||||
capture | grep -qF '(key)' && fail "key prompt stayed open after a key"
|
||||
|
||||
# --- 2e. -e (PROMPT_BSPACE_EXIT): backspace on empty cancels (no accept). ---
|
||||
reset
|
||||
$OUT send-keys M-e || exit 1
|
||||
settle
|
||||
$OUT send-keys BSpace || exit 1
|
||||
settle
|
||||
[ "$(got)" = "SENTINEL" ] || fail "backspace-exit ran the accept action (got '$(got)')"
|
||||
capture | grep -qF '(bs)' && fail "backspace on empty did not close the prompt"
|
||||
|
||||
# --- 2f. -I (prefill): prompt opens with the given buffer. ---
|
||||
reset
|
||||
$OUT send-keys M-j || exit 1
|
||||
settle
|
||||
capture | grep -qF '(pre) hello' || fail "prefill not shown (got '$(capture | grep -F '(pre)')')"
|
||||
$OUT send-keys Enter || exit 1
|
||||
settle
|
||||
[ "$(got)" = "hello" ] || fail "prefill accept recovered '$(got)', wanted 'hello'"
|
||||
|
||||
# --- 2g. Multi prompt: accept advances to the next, both are delivered. ---
|
||||
reset
|
||||
$OUT send-keys M-m || exit 1
|
||||
settle
|
||||
capture | grep -qF 'first' || fail "first of multi prompt not shown"
|
||||
$OUT send-keys -l "X" || exit 1
|
||||
$OUT send-keys Enter || exit 1
|
||||
settle
|
||||
capture | grep -qF 'second' || fail "multi prompt did not advance to the second"
|
||||
$OUT send-keys -l "Y" || exit 1
|
||||
$OUT send-keys Enter || exit 1
|
||||
settle
|
||||
[ "$(got)" = "X/Y" ] || fail "multi prompt recovered '$(got)', wanted 'X/Y'"
|
||||
|
||||
# ===========================================================================
|
||||
# 3. Type-scoped history and command completion.
|
||||
# ===========================================================================
|
||||
|
||||
# --- 3a. Command history is recalled with Up; search history is separate. ---
|
||||
reset
|
||||
$OUT send-keys M-c || exit 1
|
||||
settle
|
||||
$OUT send-keys -l "alpha" || exit 1
|
||||
$OUT send-keys Enter || exit 1
|
||||
settle
|
||||
[ "$(got)" = "alpha" ] || fail "command prompt accept recovered '$(got)'"
|
||||
# Reopen the command prompt: Up recalls the command-type entry.
|
||||
$OUT send-keys M-c || exit 1
|
||||
settle
|
||||
$OUT send-keys Up || exit 1
|
||||
settle
|
||||
capture | grep -qF '(cmd) alpha' || fail "Up did not recall command history"
|
||||
$OUT send-keys Escape || exit 1
|
||||
settle
|
||||
# A search-type prompt must NOT recall the command-type entry (separate rings).
|
||||
$OUT send-keys M-h || exit 1
|
||||
settle
|
||||
$OUT send-keys Up || exit 1
|
||||
settle
|
||||
capture | grep -qF 'alpha' && fail "search prompt recalled command-type history"
|
||||
$OUT send-keys Escape || exit 1
|
||||
settle
|
||||
|
||||
# --- 3b. Tab completion works in a command prompt (command type only). ---
|
||||
$OUT send-keys M-c || exit 1
|
||||
settle
|
||||
$OUT send-keys -l "new-w" || exit 1
|
||||
$OUT send-keys Tab || exit 1
|
||||
settle
|
||||
status_line | grep -qF 'new-window' || \
|
||||
fail "Tab did not complete new-w to new-window (got '$(status_line)')"
|
||||
$OUT send-keys Escape || exit 1
|
||||
settle
|
||||
# A search-type prompt does not complete commands: Tab is literal / inert.
|
||||
$OUT send-keys M-h || exit 1
|
||||
settle
|
||||
$OUT send-keys -l "new-w" || exit 1
|
||||
$OUT send-keys Tab || exit 1
|
||||
settle
|
||||
status_line | grep -qF 'new-window' && fail "search prompt completed a command"
|
||||
$OUT send-keys Escape || exit 1
|
||||
settle
|
||||
|
||||
# ===========================================================================
|
||||
# 4. Robustness: re-entrancy guard and Escape, then liveness.
|
||||
# ===========================================================================
|
||||
|
||||
# --- 4a. A second prompt while one is open is refused (status path). ---
|
||||
client=$($IN list-clients -F '#{client_name}' | head -1)
|
||||
reset
|
||||
$OUT send-keys M-s || exit 1
|
||||
settle
|
||||
$OUT send-keys -l "AAA" || exit 1
|
||||
settle
|
||||
$IN command-prompt -t"$client" -p '(re)' "set -g @r 'REENTERED'" 2>/dev/null
|
||||
settle
|
||||
capture | grep -qF '(re)' && fail "a second status prompt opened over the first"
|
||||
status_line | grep -qF '(stat) AAA' || fail "first status prompt was disturbed"
|
||||
$OUT send-keys Escape || exit 1
|
||||
settle
|
||||
[ "$(got)" = "SENTINEL" ] || fail "Escape ran the status prompt accept action"
|
||||
|
||||
# --- 4b. A second pane prompt while one is open is refused (pane path). ---
|
||||
reset
|
||||
$OUT send-keys M-p || exit 1
|
||||
settle
|
||||
$OUT send-keys -l "BBB" || exit 1
|
||||
settle
|
||||
$IN command-prompt -P -t"$client" -p '(re)' "set -g @r 'REENTERED'" 2>/dev/null
|
||||
settle
|
||||
capture | grep -qF '(re)' && fail "a second pane prompt opened over the first"
|
||||
capture | grep -qF '(pane) BBB' || fail "first pane prompt was disturbed"
|
||||
$OUT send-keys Escape || exit 1
|
||||
settle
|
||||
[ "$(got)" = "SENTINEL" ] || fail "Escape ran the pane prompt accept action"
|
||||
|
||||
# --- 4c. Inner tmux survived every path and flag. ---
|
||||
$IN display-message -p '#{version}' >/dev/null 2>&1 || fail "inner tmux died"
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user