From 5cd43626ef66b9854625d860f3b68c0c47e362d3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 17 Jul 2026 09:29:40 +0100 Subject: [PATCH] cmd_template_replace test. --- regress/cmd-template-replace.sh | 181 ++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 regress/cmd-template-replace.sh diff --git a/regress/cmd-template-replace.sh b/regress/cmd-template-replace.sh new file mode 100644 index 000000000..00730abc3 --- /dev/null +++ b/regress/cmd-template-replace.sh @@ -0,0 +1,181 @@ +#!/bin/sh + +# Exercise cmd_template_replace through command-prompt, which passes prompt +# input as argv to args_make_commands. This covers the quoting modes, indexed +# replacements, and cases where replacements are intentionally not made. + +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) +TMP=$(mktemp -d) || exit 1 +TMUX_TMPDIR="$TMP" +export TMUX_TMPDIR + +OUT="$TEST_TMUX -LtestA$$ -f/dev/null" +IN="$TEST_TMUX -LtestB$$ -f/dev/null" + +cleanup() +{ + $OUT kill-server 2>/dev/null + $IN kill-server 2>/dev/null + rm -rf "$TMP" +} +trap cleanup EXIT + +fail() +{ + echo "[FAIL] $1" + exit 1 +} + +settle() +{ + sleep 0.5 +} + +wait_option() +{ + option=$1 + expected=$2 + i=0 + + while [ "$i" -lt 30 ]; do + value=$($IN 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'" +} + +accept_prompt() +{ + key=$1 + value=$2 + + $OUT send-keys "$key" || exit 1 + settle + $OUT send-keys -l "$value" || exit 1 + $OUT send-keys Enter || exit 1 + settle +} + +accept_two_prompts() +{ + key=$1 + first=$2 + second=$3 + + $OUT send-keys "$key" || exit 1 + settle + $OUT send-keys -l "$first" || exit 1 + $OUT send-keys Enter || exit 1 + settle + $OUT send-keys -l "$second" || exit 1 + $OUT send-keys Enter || exit 1 + settle +} + +reset_options() +{ + for option in \ + @r @first @second @plain @raw_tail @one @two @two_again \ + @double_index @marker; do + $IN set -g "$option" SENTINEL || exit 1 + done + $IN set -g @marker unchanged || exit 1 +} + +$IN new -d -x80 -y24 "sh -c 'exec sleep 1000'" || exit 1 +$IN set -g status on || exit 1 +$IN set -g status-keys emacs || exit 1 +$IN set -g window-size manual || exit 1 + +$IN bind -n M-s command-prompt -p '(single)' "set -g @r '%%'" || + exit 1 +$IN bind -n M-f command-prompt -p '(first)' \ + "set -g @first '%%' ; set -g @second '%%'" || + exit 1 +$IN bind -n M-d command-prompt -p '(double)' 'set -g @r "%%%"' || + exit 1 +$IN bind -n M-r command-prompt -p '(raw)' "set -g @r %1" || + exit 1 +$IN bind -n M-a command-prompt -p '(all)' \ + "set -g @first %1 ; set -g @second %1" || + exit 1 +$IN bind -n M-m command-prompt -p 'one,two' \ + "set -g @one %1 ; set -g @two %2 ; set -g @two_again %2" || + exit 1 +$IN bind -n M-i command-prompt -p 'one,two' 'set -g @double_index "%2%"' || + exit 1 +$IN bind -n M-n command-prompt -p '(none)' \ + "set -g @plain no-template-markers" || + exit 1 + +$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 + +reset_options + +# %% is for templates already inside single quotes. A single quote in the +# replacement must stay data and must not close the surrounding quotes. +payload="can't ; set -g @marker changed ; done" +accept_prompt M-s "$payload" +wait_option @r "$payload" +wait_option @marker unchanged + +# Only the first %% is replaced. +reset_options +payload="first'value" +accept_prompt M-f "$payload" +wait_option @first "$payload" +wait_option @second %% + +# %%% keeps the previous double-quote escaping behaviour. +reset_options +payload='a"$;~\z' +accept_prompt M-d "$payload" +wait_option @r "$payload" + +# %1 is intentionally left raw as an escape hatch. +reset_options +$IN set -g @raw_tail unchanged || exit 1 +accept_prompt M-r 'raw ; set -g @raw_tail yes' +wait_option @r raw +wait_option @raw_tail yes + +# All instances of %1 are replaced. +reset_options +accept_prompt M-a repeat +wait_option @first repeat +wait_option @second repeat + +# %1 and %2 select different prompt values, and all instances of %2 are +# replaced. +reset_options +accept_two_prompts M-m one two +wait_option @one one +wait_option @two two +wait_option @two_again two + +# %idx% uses double-quote escaping for indexed replacements. +reset_options +payload='b"$;~\y' +accept_two_prompts M-i ignored "$payload" +wait_option @double_index "$payload" + +# A template without replacement markers is left alone. +reset_options +accept_prompt M-n 'unused ; set -g @marker changed' +wait_option @plain no-template-markers +wait_option @marker unchanged + +exit 0