mirror of
https://github.com/tmux/tmux.git
synced 2026-07-03 10:12:31 +00:00
Format tests.
This commit is contained in:
@@ -43,6 +43,29 @@ test_format()
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# test_expand $format $expected
|
||||||
|
#
|
||||||
|
# Expand $format in a plain format_expand context (list-windows -F on the
|
||||||
|
# single-window "tf" session) rather than the format_expand_time context of
|
||||||
|
# display-message. This matters for t/f: display-message runs the whole format
|
||||||
|
# through strftime(3), so a strftime specifier there must be doubled (%%H); in a
|
||||||
|
# format_expand context a single specifier (%H) is applied directly to the
|
||||||
|
# variable's time.
|
||||||
|
test_expand()
|
||||||
|
{
|
||||||
|
fmt="$1"
|
||||||
|
exp="$2"
|
||||||
|
|
||||||
|
out=$($TMUX list-windows -t tf -F "$fmt")
|
||||||
|
|
||||||
|
if [ "$out" != "$exp" ]; then
|
||||||
|
echo "Format test failed for '$fmt'."
|
||||||
|
echo "Expected: '$exp'"
|
||||||
|
echo "But got '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# assert_alive
|
# assert_alive
|
||||||
#
|
#
|
||||||
# Check that the server is still responding (used after operations that could
|
# Check that the server is still responding (used after operations that could
|
||||||
@@ -58,15 +81,22 @@ assert_alive()
|
|||||||
$TMUX kill-server 2>/dev/null
|
$TMUX kill-server 2>/dev/null
|
||||||
$TMUX new-session -d -s main -x 80 -y 24 || exit 1
|
$TMUX new-session -d -s main -x 80 -y 24 || exit 1
|
||||||
|
|
||||||
|
# Single-window session used by test_expand for format_expand-context tests.
|
||||||
|
$TMUX new-session -d -s tf || exit 1
|
||||||
|
|
||||||
# User options used as inputs. Modifiers operate on variable names, so plain
|
# User options used as inputs. Modifiers operate on variable names, so plain
|
||||||
# literals must be provided via options (or a nested #{l:...}).
|
# literals must be provided via options (or a nested #{l:...}). They are set
|
||||||
$TMUX set @s 'abcdefghij' || exit 1
|
# globally (-g) so they are visible from every session, including the "tf"
|
||||||
$TMUX set @path '/usr/local/bin/foo' || exit 1
|
# session used by test_expand.
|
||||||
$TMUX set @name 'window-name' || exit 1
|
$TMUX set -g @s 'abcdefghij' || exit 1
|
||||||
$TMUX set @greek 'αβγ' || exit 1 # 6 bytes, 3 columns wide
|
$TMUX set -g @path '/usr/local/bin/foo' || exit 1
|
||||||
$TMUX set @cjk '中文' || exit 1 # 6 bytes, 4 columns wide
|
$TMUX set -g @name 'window-name' || exit 1
|
||||||
$TMUX set @host 'myhost' || exit 1
|
$TMUX set -g @greek 'αβγ' || exit 1 # 6 bytes, 3 columns wide
|
||||||
$TMUX set @ts '1000000000' || exit 1 # 2001-09-09 01:46:40 UTC
|
$TMUX set -g @cjk '中文' || exit 1 # 6 bytes, 4 columns wide
|
||||||
|
$TMUX set -g @host 'myhost' || exit 1
|
||||||
|
$TMUX set -g @ts '1000000000' || exit 1 # 2001-09-09 01:46:40 UTC
|
||||||
|
$TMUX set -g @sp 'a b$c' || exit 1 # shell-special characters for q:
|
||||||
|
$TMUX set -g @hash 'a#b' || exit 1 # a "#" for q/e:
|
||||||
|
|
||||||
|
|
||||||
# --- Comparisons and matching --------------------------------------------
|
# --- Comparisons and matching --------------------------------------------
|
||||||
@@ -84,6 +114,15 @@ test_format "#{m/r:^[0-9]+\$,12a45}" "0"
|
|||||||
# m/ri: regular expression, ignore case.
|
# m/ri: regular expression, ignore case.
|
||||||
test_format "#{m/ri:^ab+\$,ABBB}" "1"
|
test_format "#{m/ri:^ab+\$,ABBB}" "1"
|
||||||
test_format "#{m/ri:^ab+\$,ACCC}" "0"
|
test_format "#{m/ri:^ab+\$,ACCC}" "0"
|
||||||
|
# m/z: fuzzy match, returns a boolean.
|
||||||
|
test_format "#{m/z:foo,foobar}" "1"
|
||||||
|
test_format "#{m/z:xyz,foobar}" "0"
|
||||||
|
# m/p: fuzzy match, returns the matched (0-based) column positions.
|
||||||
|
test_format "#{m/p:ac,abc}" "0,2"
|
||||||
|
test_format "#{m/p:xyz,abc}" ""
|
||||||
|
# Fuzzy match against empty text.
|
||||||
|
test_format "#{m/p:x,}" ""
|
||||||
|
test_format "#{m/z:x,}" "0"
|
||||||
|
|
||||||
# String comparisons.
|
# String comparisons.
|
||||||
test_format "#{==:#{@host},myhost}" "1"
|
test_format "#{==:#{@host},myhost}" "1"
|
||||||
@@ -107,6 +146,29 @@ test_format "#{!!:0}" "0"
|
|||||||
test_format "#{!!:non-empty}" "1"
|
test_format "#{!!:non-empty}" "1"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Quoting (q) ---------------------------------------------------------
|
||||||
|
|
||||||
|
# q: escapes shell special characters with a backslash.
|
||||||
|
test_format "#{q:@sp}" 'a\ b\$c'
|
||||||
|
# q/e and q/h escape "#" for the format/style parser by doubling it.
|
||||||
|
test_format "#{q/e:@hash}" 'a##b'
|
||||||
|
test_format "#{q/h:@hash}" 'a##b'
|
||||||
|
# q/a quotes the value as a single shell argument.
|
||||||
|
test_format "#{q/a:@sp}" '"a b\$c"'
|
||||||
|
|
||||||
|
|
||||||
|
# --- Name existence (N) --------------------------------------------------
|
||||||
|
|
||||||
|
# N/w is true if a window with the (expanded) name exists in the session, N/s
|
||||||
|
# if a session with that name exists. The default (no argument) is /w.
|
||||||
|
$TMUX rename-window -t main:0 knownwin
|
||||||
|
test_format "#{N/s:main}" "1"
|
||||||
|
test_format "#{N/s:nosuchsession}" "0"
|
||||||
|
test_format "#{N/w:knownwin}" "1" "main:"
|
||||||
|
test_format "#{N/w:nosuchwindow}" "0" "main:"
|
||||||
|
test_format "#{N:nosuchwindow}" "0" "main:"
|
||||||
|
|
||||||
|
|
||||||
# --- Numeric operations (e) ----------------------------------------------
|
# --- Numeric operations (e) ----------------------------------------------
|
||||||
|
|
||||||
# Integer operators.
|
# Integer operators.
|
||||||
@@ -145,9 +207,14 @@ assert_alive "division by zero"
|
|||||||
# a: numeric value to its ASCII character.
|
# a: numeric value to its ASCII character.
|
||||||
test_format "#{a:98}" "b"
|
test_format "#{a:98}" "b"
|
||||||
test_format "#{a:65}" "A"
|
test_format "#{a:65}" "A"
|
||||||
|
# a: out-of-range or non-numeric input yields an empty string.
|
||||||
|
test_format "#{a:200}" ""
|
||||||
|
test_format "#{a:notanumber}" ""
|
||||||
# R: repeat first argument second-argument times.
|
# R: repeat first argument second-argument times.
|
||||||
test_format "#{R:a,3}" "aaa"
|
test_format "#{R:a,3}" "aaa"
|
||||||
test_format "#{R:ab,2}" "abab"
|
test_format "#{R:ab,2}" "abab"
|
||||||
|
# A long repeat exercises output-buffer growth during expansion.
|
||||||
|
test_format "#{n:#{R:x,300}}" "300"
|
||||||
|
|
||||||
|
|
||||||
# --- Width, padding and truncation ---------------------------------------
|
# --- Width, padding and truncation ---------------------------------------
|
||||||
@@ -155,15 +222,23 @@ test_format "#{R:ab,2}" "abab"
|
|||||||
# =N truncates from the start, =-N from the end.
|
# =N truncates from the start, =-N from the end.
|
||||||
test_format "#{=5:@s}" "abcde"
|
test_format "#{=5:@s}" "abcde"
|
||||||
test_format "#{=-5:@s}" "fghij"
|
test_format "#{=-5:@s}" "fghij"
|
||||||
|
# = with no width, or a non-numeric width, does not truncate.
|
||||||
|
test_format "#{=:@s}" "abcdefghij"
|
||||||
|
test_format "#{=/x:@s}" "abcdefghij"
|
||||||
# A marker is appended/prepended only when trimming actually occurs.
|
# A marker is appended/prepended only when trimming actually occurs.
|
||||||
test_format "#{=/5/...:@s}" "abcde..."
|
test_format "#{=/5/...:@s}" "abcde..."
|
||||||
test_format "#{=/5/...:@name}" "windo..."
|
test_format "#{=/5/...:@name}" "windo..."
|
||||||
test_format "#{=/20/...:@s}" "abcdefghij"
|
test_format "#{=/20/...:@s}" "abcdefghij"
|
||||||
# Truncation is display-width (UTF-8) aware.
|
# Truncation is display-width (UTF-8) aware: a wide (2-column) character is only
|
||||||
|
# included if it fits entirely within the limit.
|
||||||
test_format "#{=3:@greek}" "αβγ"
|
test_format "#{=3:@greek}" "αβγ"
|
||||||
test_format "#{=2:@greek}" "αβ"
|
test_format "#{=2:@greek}" "αβ"
|
||||||
test_format "#{=2:@cjk}" "中"
|
test_format "#{=2:@cjk}" "中"
|
||||||
test_format "#{=1:@cjk}" ""
|
test_format "#{=1:@cjk}" ""
|
||||||
|
# Markers with wide characters: the marker is added when trimming occurs, and a
|
||||||
|
# limit that splits a wide character drops it entirely.
|
||||||
|
test_format "#{=/2/x:@cjk}" "中x"
|
||||||
|
test_format "#{=/1/x:@cjk}" "x"
|
||||||
|
|
||||||
# p pads to a width: a positive width left-aligns (pads on the right), a
|
# p pads to a width: a positive width left-aligns (pads on the right), a
|
||||||
# negative width right-aligns (pads on the left).
|
# negative width right-aligns (pads on the left).
|
||||||
@@ -171,6 +246,12 @@ test_format "#{p12:@name}" "window-name "
|
|||||||
test_format "#{p-12:@name}" " window-name"
|
test_format "#{p-12:@name}" " window-name"
|
||||||
# No padding once the value already meets the width.
|
# No padding once the value already meets the width.
|
||||||
test_format "#{p3:@name}" "window-name"
|
test_format "#{p3:@name}" "window-name"
|
||||||
|
# p with no width does nothing.
|
||||||
|
test_format "#{p:@name}" "window-name"
|
||||||
|
# Padding is display-width aware: @cjk is 4 columns wide, so p6/p-6 add two
|
||||||
|
# spaces (not four).
|
||||||
|
test_format "#{p6:@cjk}" "中文 "
|
||||||
|
test_format "#{p-6:@cjk}" " 中文"
|
||||||
|
|
||||||
# n is byte length, w is display width.
|
# n is byte length, w is display width.
|
||||||
test_format "#{n:@s}" "10"
|
test_format "#{n:@s}" "10"
|
||||||
@@ -199,34 +280,40 @@ if [ -z "$($TMUX display-message -p '#{t/r:@ts}')" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# t/f: custom strftime format applied to the variable's time. The % specifiers
|
# t/f: custom strftime format applied to the variable's time. Tested in a
|
||||||
# are doubled because display-message also expands the format through strftime;
|
# format_expand context (list-windows -F), where a single strftime specifier is
|
||||||
# the colon inside the format is escaped as '#:'.
|
# applied directly. (In display-message, which additionally expands the format
|
||||||
test_format "#{t/f/%%Y:@ts}" "2001"
|
# through strftime, these would need to be doubled - %%Y etc.) The colon in the
|
||||||
test_format "#{t/f/%%Y-%%m-%%d:@ts}" "2001-09-09"
|
# format is escaped as '#:' because it is otherwise the modifier separator.
|
||||||
test_format "#{t/f/%%H#:%%M#:%%S:@ts}" "01:46:40"
|
test_expand "#{t/f/%Y:@ts}" "2001"
|
||||||
|
test_expand "#{t/f/%Y-%m-%d:@ts}" "2001-09-09"
|
||||||
|
test_expand "#{t/f/%H#:%M#:%S:@ts}" "01:46:40"
|
||||||
|
# An escaped comma in the custom format is unescaped before strftime.
|
||||||
|
test_expand "#{t/f/%Y#,end:@ts}" "2001,end"
|
||||||
|
|
||||||
|
# T: expands its argument and then runs the result through strftime with the
|
||||||
|
# current time. A value with no strftime specifier is returned unchanged.
|
||||||
|
test_format "#{T:@ts}" "1000000000"
|
||||||
|
|
||||||
# --- Loops (S, W, P) -----------------------------------------------------
|
# t/p (pretty) and t/r (relative) format times by age relative to now, with a
|
||||||
|
# different branch per age band. Build options a known number of seconds in the
|
||||||
# Windows in the session, iterated in index order.
|
# past and check each yields a non-empty result (the exact text depends on the
|
||||||
$TMUX set -g automatic-rename off
|
# wall clock, so only non-emptiness is asserted).
|
||||||
$TMUX rename-window -t main:0 w0
|
now=$(date +%s)
|
||||||
$TMUX new-window -t main: -n w1
|
for age in 30 300 4000 90000 200000 3000000 40000000; do
|
||||||
$TMUX new-window -t main: -n w2
|
$TMUX set -g @age "$((now - age))"
|
||||||
test_format "#{W:#{window_index}}" "012" "main:"
|
if [ -z "$($TMUX display-message -p '#{t/r:@age}')" ]; then
|
||||||
test_format "#{W:[#{window_name}]}" "[w0][w1][w2]" "main:"
|
echo "Empty #{t/r:@age} for age ${age}s"
|
||||||
|
exit 1
|
||||||
# Panes: iteration order depends on layout, so assert a per-item constant to
|
fi
|
||||||
# check the count/iteration only.
|
if [ -z "$($TMUX display-message -p '#{t/p:@age}')" ]; then
|
||||||
$TMUX split-window -t main:0 -d
|
echo "Empty #{t/p:@age} for age ${age}s"
|
||||||
$TMUX split-window -t main:0 -d
|
exit 1
|
||||||
test_format "#{P:x}" "xxx" "main:0"
|
fi
|
||||||
|
done
|
||||||
# Sessions: assert a per-session constant (order independent).
|
# A time in the future has no relative form.
|
||||||
$TMUX new-session -d -s alpha
|
$TMUX set -g @future "$((now + 100000))"
|
||||||
$TMUX new-session -d -s beta
|
test_format "#{t/r:@future}" ""
|
||||||
test_format "#{S:s}" "sss"
|
|
||||||
|
|
||||||
|
|
||||||
# --- Content search (C) --------------------------------------------------
|
# --- Content search (C) --------------------------------------------------
|
||||||
@@ -254,6 +341,10 @@ test_format "#{c:#7f7f7f}" "7f7f7f"
|
|||||||
test_format "#{c/f:red}" "${ESC}[31m"
|
test_format "#{c/f:red}" "${ESC}[31m"
|
||||||
test_format "#{c/b:red}" "${ESC}[41m"
|
test_format "#{c/b:red}" "${ESC}[41m"
|
||||||
test_format "#{c/b:colour4}" "${ESC}[48;5;4m"
|
test_format "#{c/b:colour4}" "${ESC}[48;5;4m"
|
||||||
|
# "none" gives a reset; an unknown colour gives an empty string.
|
||||||
|
test_format "#{c/f:none}" "${ESC}[0m"
|
||||||
|
test_format "#{c:notacolour}" ""
|
||||||
|
test_format "#{c/f:notacolour}" ""
|
||||||
|
|
||||||
|
|
||||||
# --- Nesting and limits --------------------------------------------------
|
# --- Nesting and limits --------------------------------------------------
|
||||||
@@ -267,9 +358,206 @@ test_format "#{n:#{b:@path}}" "3"
|
|||||||
# Nested l: literal expanded then truncated.
|
# Nested l: literal expanded then truncated.
|
||||||
test_format "#{=5:#{l:abcdefghij}}" "abcde"
|
test_format "#{=5:#{l:abcdefghij}}" "abcde"
|
||||||
|
|
||||||
|
# Deeper nesting: basename -> pad to 10 -> truncate to 5.
|
||||||
|
test_format "#{=5:#{p10:#{b:@path}}}" "foo "
|
||||||
|
# A substitution applied to a nested basename.
|
||||||
|
test_format "#{s/o/O/:#{b:@path}}" "fOO"
|
||||||
|
|
||||||
# Unbounded self-recursion must hit the loop limit rather than crash.
|
# Unbounded self-recursion must hit the loop limit rather than crash.
|
||||||
$TMUX set @rec '#{E:@rec}'
|
$TMUX set @rec '#{E:@rec}'
|
||||||
$TMUX display-message -p '#{E:@rec}' >/dev/null 2>&1
|
$TMUX display-message -p '#{E:@rec}' >/dev/null 2>&1
|
||||||
assert_alive "recursive expansion"
|
assert_alive "recursive expansion"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Missing, malformed and limit inputs ---------------------------------
|
||||||
|
|
||||||
|
# An undefined variable expands to empty; modifiers on it behave sensibly.
|
||||||
|
test_format "#{@undefined}" ""
|
||||||
|
test_format "#{=5:@undefined}" ""
|
||||||
|
test_format "#{b:@undefined}" ""
|
||||||
|
test_format "#{n:@undefined}" "0"
|
||||||
|
|
||||||
|
# Malformed numeric expressions expand to empty rather than erroring out.
|
||||||
|
test_format "#{e|+|:notanumber,2}" "" # invalid left operand
|
||||||
|
test_format "#{e|+|:2,notanumber}" "" # invalid right operand
|
||||||
|
test_format "#{e|badop|:1,2}" "" # unknown operator
|
||||||
|
test_format "#{e|+|f|x:1,2}" "" # invalid precision
|
||||||
|
test_format "#{e|+|:1}" "" # too few operands
|
||||||
|
test_format "#{e|+|f|2|extra:1,2}" "" # too many arguments (limit is 3)
|
||||||
|
|
||||||
|
# Repeat with a non-numeric or zero count yields an empty string.
|
||||||
|
test_format "#{R:a,notanumber}" ""
|
||||||
|
test_format "#{R:a,0}" ""
|
||||||
|
|
||||||
|
# Comparisons with too few arguments expand to empty.
|
||||||
|
test_format "#{==:a}" ""
|
||||||
|
test_format "#{<:a}" ""
|
||||||
|
|
||||||
|
# A substitution with fewer than two arguments is a no-op.
|
||||||
|
test_format "#{s/onlyone:@s}" "abcdefghij"
|
||||||
|
|
||||||
|
# A non-numeric width for = or p is treated as no width (no change).
|
||||||
|
test_format "#{=/x:@s}" "abcdefghij"
|
||||||
|
test_format "#{p/x:@s}" "abcdefghij"
|
||||||
|
|
||||||
|
# The I (client terminal) modifier with no attached client is empty; this also
|
||||||
|
# exercises its argument parsing (/c termcap, /f feature, default). The
|
||||||
|
# non-empty terminal cases are covered with a real client in format-variables.sh.
|
||||||
|
test_format "#{I/c:RGB}" ""
|
||||||
|
test_format "#{I/f:overline}" ""
|
||||||
|
test_format "#{I:x}" ""
|
||||||
|
|
||||||
|
|
||||||
|
# --- Escaping inside modifiers -------------------------------------------
|
||||||
|
|
||||||
|
# A "," or "#" inside a modifier argument is escaped with "#".
|
||||||
|
test_format "#{s/#,/-/:#{l:a,b,c}}" "a-b-c" # escaped comma in the pattern
|
||||||
|
test_format "#{=/3/#,:@s}" "abc," # escaped comma in the marker
|
||||||
|
# The truncation marker is itself expanded as a format.
|
||||||
|
test_format "#{=/3/#{l:>}:@s}" "abc>"
|
||||||
|
|
||||||
|
# Substitution flags: a third argument of "i" is case-insensitive; an invalid
|
||||||
|
# regular expression leaves the text unchanged.
|
||||||
|
test_format "#{s/A/X/i:@s}" "Xbcdefghij"
|
||||||
|
test_format "#{s/[/X/:@s}" "abcdefghij"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Unicode in modifier arguments ---------------------------------------
|
||||||
|
|
||||||
|
# Wide (CJK) and emoji text: matching, substitution, repeat and markers all
|
||||||
|
# operate on characters, and n/w report bytes/columns.
|
||||||
|
$TMUX set -g @emoji '😀😀' || exit 1 # 8 bytes, 4 columns
|
||||||
|
test_format "#{m:*中*,#{@cjk}}" "1"
|
||||||
|
test_format "#{s/文/X/:@cjk}" "中X"
|
||||||
|
test_format "#{R:中,3}" "中中中"
|
||||||
|
test_format "#{=/1/中:@s}" "a中"
|
||||||
|
test_format "#{w:@emoji}" "4"
|
||||||
|
test_format "#{n:@emoji}" "8"
|
||||||
|
test_format "#{=2:@emoji}" "😀"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Server messages (show-messages) -------------------------------------
|
||||||
|
|
||||||
|
# show-messages formats each logged message (this exercises the message-time
|
||||||
|
# formatting path); just check the server survives producing it.
|
||||||
|
$TMUX show-messages >/dev/null 2>&1
|
||||||
|
assert_alive "show-messages"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Verbose expansion (logging) -----------------------------------------
|
||||||
|
|
||||||
|
# display-message -v turns on format logging, so re-expanding a representative
|
||||||
|
# set of formats with -v exercises the logging code paths. Only survival is
|
||||||
|
# checked; the log text itself is not asserted.
|
||||||
|
for f in \
|
||||||
|
'#{=3:@s}' \
|
||||||
|
'#{e|+|:2,3}' \
|
||||||
|
'#{e|*|f|2:2.5,2}' \
|
||||||
|
'#{m:*a*,abc}' \
|
||||||
|
'#{<:3,5}' \
|
||||||
|
'#{s/a/X/:@s}' \
|
||||||
|
'#{b:@path}' \
|
||||||
|
'#{t:@ts}' \
|
||||||
|
'#{p6:@name}' \
|
||||||
|
'#{=3:#{b:@path}}'; do
|
||||||
|
$TMUX display-message -v -p "$f" >/dev/null 2>&1
|
||||||
|
done
|
||||||
|
assert_alive "verbose expansion"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Loops and sorting (S, W, P, L) --------------------------------------
|
||||||
|
#
|
||||||
|
# These need a fully controlled server so the set of sessions, windows and
|
||||||
|
# panes (and their order) is known, so start from a clean server. This must be
|
||||||
|
# the last section as it discards the setup above.
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
|
||||||
|
# Sessions, created in this order, so session ids (and hence creation order)
|
||||||
|
# are zeta=$0, alpha=$1, mike=$2.
|
||||||
|
$TMUX new-session -d -s zeta -x 80 -y 24 || exit 1
|
||||||
|
$TMUX new-session -d -s alpha || exit 1
|
||||||
|
$TMUX new-session -d -s mike || exit 1
|
||||||
|
$TMUX set -g automatic-rename off
|
||||||
|
|
||||||
|
# S loops over every session. The default order is by session id (SORT_INDEX),
|
||||||
|
# /i is the same, /n is by name, and the r suffix reverses.
|
||||||
|
test_format "#{S:#{session_name} }" "zeta alpha mike "
|
||||||
|
test_format "#{S/i:#{session_name} }" "zeta alpha mike "
|
||||||
|
test_format "#{S/n:#{session_name} }" "alpha mike zeta "
|
||||||
|
test_format "#{S/nr:#{session_name} }" "zeta mike alpha "
|
||||||
|
test_format "#{S/ir:#{session_name} }" "mike alpha zeta "
|
||||||
|
# /t sorts by activity time; the exact order is timing-dependent, so just check
|
||||||
|
# every session is still iterated (this exercises the activity-sort branch).
|
||||||
|
test_format "#{S/t:x}" "xxx"
|
||||||
|
# An unrecognised sort letter falls back to the default order; /r on its own
|
||||||
|
# reverses that default (this covers the fall-through branch).
|
||||||
|
test_format "#{S/r:#{session_name} }" "mike alpha zeta "
|
||||||
|
|
||||||
|
# Windows in session zeta: window 0 renamed charlie, then alpha at 1, bravo at
|
||||||
|
# 2. The default order is by index (SORT_ORDER), /n is by name, r reverses.
|
||||||
|
$TMUX rename-window -t zeta:0 charlie
|
||||||
|
$TMUX new-window -d -t zeta:1 -n alpha
|
||||||
|
$TMUX new-window -d -t zeta:2 -n bravo
|
||||||
|
test_format "#{W:#{window_name} }" "charlie alpha bravo " "zeta:"
|
||||||
|
test_format "#{W/n:#{window_name} }" "alpha bravo charlie " "zeta:"
|
||||||
|
test_format "#{W/nr:#{window_name} }" "charlie bravo alpha " "zeta:"
|
||||||
|
test_format "#{W/ir:#{window_index}}" "210" "zeta:"
|
||||||
|
# /i (by index) and /t (by activity); /i matches the default order here.
|
||||||
|
test_format "#{W/i:#{window_name} }" "charlie alpha bravo " "zeta:"
|
||||||
|
test_format "#{W/t:x}" "xxx" "zeta:"
|
||||||
|
# An unrecognised sort letter falls back to the default order; /r reverses it.
|
||||||
|
test_format "#{W/r:#{window_name} }" "bravo alpha charlie " "zeta:"
|
||||||
|
|
||||||
|
# Panes in window zeta:charlie. Splitting the active (newest) pane each time
|
||||||
|
# makes pane index match creation order (0,1,2 left to right). The default
|
||||||
|
# order is by creation (SORT_CREATION), r reverses.
|
||||||
|
$TMUX split-window -h -t zeta:charlie
|
||||||
|
$TMUX split-window -h -t zeta:charlie
|
||||||
|
test_format "#{P:#{pane_index}}" "012" "zeta:charlie"
|
||||||
|
test_format "#{P/r:#{pane_index}}" "210" "zeta:charlie"
|
||||||
|
# A pane-sort argument is accepted; for panes only the r (reverse) suffix has an
|
||||||
|
# effect, so these all keep the count and exercise the argument branch.
|
||||||
|
test_format "#{P/i:x}" "xxx" "zeta:charlie"
|
||||||
|
test_format "#{P/n:x}" "xxx" "zeta:charlie"
|
||||||
|
test_format "#{P/t:x}" "xxx" "zeta:charlie"
|
||||||
|
|
||||||
|
# Verbose expansion of the loops, to exercise their logging paths.
|
||||||
|
$TMUX display-message -v -p "#{S:#{session_name}}" >/dev/null 2>&1
|
||||||
|
$TMUX display-message -v -t zeta: -p "#{W:#{window_name}}" >/dev/null 2>&1
|
||||||
|
$TMUX display-message -v -t zeta:charlie -p "#{P:#{pane_index}}" >/dev/null 2>&1
|
||||||
|
assert_alive "verbose loop expansion"
|
||||||
|
|
||||||
|
# L loops over attached clients. Attach two control-mode clients, each held
|
||||||
|
# open by a background process keeping a FIFO's write end open.
|
||||||
|
FIFO1="${TMPDIR:-/tmp}/fmt-l-$$-1"
|
||||||
|
FIFO2="${TMPDIR:-/tmp}/fmt-l-$$-2"
|
||||||
|
rm -f "$FIFO1" "$FIFO2"
|
||||||
|
mkfifo "$FIFO1" "$FIFO2" || exit 1
|
||||||
|
# Hold the write ends open so the control clients stay attached.
|
||||||
|
sleep 30 >"$FIFO1" &
|
||||||
|
HOLD1=$!
|
||||||
|
sleep 30 >"$FIFO2" &
|
||||||
|
HOLD2=$!
|
||||||
|
$TMUX -C attach -t zeta <"$FIFO1" >/dev/null 2>&1 &
|
||||||
|
CC1=$!
|
||||||
|
$TMUX -C attach -t alpha <"$FIFO2" >/dev/null 2>&1 &
|
||||||
|
CC2=$!
|
||||||
|
sleep 1
|
||||||
|
# Two clients attached: L emits one item per client.
|
||||||
|
test_format "#{L:x}" "xx"
|
||||||
|
# The client sort orders (default, index, name, activity, reversed) are all
|
||||||
|
# accepted; assert only the count so the test does not depend on client names or
|
||||||
|
# timing.
|
||||||
|
test_format "#{L/i:x}" "xx"
|
||||||
|
test_format "#{L/n:x}" "xx"
|
||||||
|
test_format "#{L/t:x}" "xx"
|
||||||
|
test_format "#{L/nr:x}" "xx"
|
||||||
|
test_format "#{L/r:x}" "xx"
|
||||||
|
# Now detach one and confirm the count drops to one.
|
||||||
|
kill $HOLD2 2>/dev/null
|
||||||
|
sleep 1
|
||||||
|
test_format "#{L:x}" "x"
|
||||||
|
kill $HOLD1 $CC1 $CC2 2>/dev/null
|
||||||
|
rm -f "$FIFO1" "$FIFO2"
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
143
regress/format-mouse.sh
Normal file
143
regress/format-mouse.sh
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Tests of the mouse format variables (mouse_x, mouse_y, mouse_word,
|
||||||
|
# mouse_line, ...). These are only populated while a mouse key binding is being
|
||||||
|
# dispatched, so the test drives a real mouse event:
|
||||||
|
#
|
||||||
|
# - an inner client is attached inside a pane of a second ("outer") tmux
|
||||||
|
# server, giving the inner server a genuine terminal;
|
||||||
|
# - mouse mode is on and a MouseDown1Pane binding records the mouse format
|
||||||
|
# variables into an option;
|
||||||
|
# - an SGR mouse sequence is written to the outer pane, so the inner client
|
||||||
|
# receives it as a real mouse click.
|
||||||
|
#
|
||||||
|
# This exercises the mouse callbacks and the grid word/line lookup code that
|
||||||
|
# display-message cannot otherwise reach.
|
||||||
|
|
||||||
|
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 >/dev/null 2>&1
|
||||||
|
$TMUX2 kill-server >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$1"
|
||||||
|
cleanup
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# click COL ROW
|
||||||
|
#
|
||||||
|
# Write an SGR mouse press then release (button 0) at 1-based COL/ROW to the
|
||||||
|
# outer pane holding the inner client.
|
||||||
|
click()
|
||||||
|
{
|
||||||
|
col="$1"
|
||||||
|
row="$2"
|
||||||
|
seq=$(printf '\033[<0;%s;%sM\033[<0;%s;%sm' "$col" "$row" "$col" "$row")
|
||||||
|
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||||
|
sleep 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
|
||||||
|
# Inner session with a single pane running cat, so its content is exactly what
|
||||||
|
# we send it.
|
||||||
|
$TMUX new-session -d -s cov -x 80 -y 24 'cat' || exit 1
|
||||||
|
$TMUX set -g mouse on
|
||||||
|
sleep 1
|
||||||
|
$TMUX send-keys -t cov:0.0 'alpha beta gamma' Enter
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Record every pane mouse variable when the pane is clicked.
|
||||||
|
$TMUX bind -n MouseDown1Pane run-shell \
|
||||||
|
"$TMUX set -g @m 'x=#{mouse_x} y=#{mouse_y} word=#{mouse_word} line=#{mouse_line} pane=#{mouse_pane} hl=[#{mouse_hyperlink}]'"
|
||||||
|
|
||||||
|
# Attach a real client inside an outer tmux pane. Clicks all target the first
|
||||||
|
# row, which lines up with the inner client regardless of the outer status line.
|
||||||
|
$TMUX2 new-session -d -x 80 -y 24 "$TMUX attach -t cov" || exit 1
|
||||||
|
sleep 1
|
||||||
|
OUTER=$($TMUX2 list-panes -F '#{pane_id}' | head -1)
|
||||||
|
[ -n "$OUTER" ] || fail "No outer pane."
|
||||||
|
|
||||||
|
# Click column 3, row 1: over the first word ("alpha") of the first line.
|
||||||
|
click 3 1
|
||||||
|
|
||||||
|
M=$($TMUX show -gv @m 2>/dev/null)
|
||||||
|
[ -n "$M" ] || fail "Mouse binding did not fire (no @m)."
|
||||||
|
|
||||||
|
# mouse_x is 0-based column (SGR column 3 -> x 2); mouse_y is 0-based row 0.
|
||||||
|
case "$M" in
|
||||||
|
*"x=2 "*) ;;
|
||||||
|
*) fail "Unexpected mouse_x in: $M" ;;
|
||||||
|
esac
|
||||||
|
case "$M" in
|
||||||
|
*"y=0 "*) ;;
|
||||||
|
*) fail "Unexpected mouse_y in: $M" ;;
|
||||||
|
esac
|
||||||
|
# mouse_word is the word under the cursor, mouse_line the whole line.
|
||||||
|
case "$M" in
|
||||||
|
*"word=alpha "*) ;;
|
||||||
|
*) fail "Unexpected mouse_word in: $M" ;;
|
||||||
|
esac
|
||||||
|
case "$M" in
|
||||||
|
*"line=alpha beta gamma "*) ;;
|
||||||
|
*) fail "Unexpected mouse_line in: $M" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# A click in a different column selects a different word.
|
||||||
|
click 8 1
|
||||||
|
M=$($TMUX show -gv @m 2>/dev/null)
|
||||||
|
case "$M" in
|
||||||
|
*"word=beta "*) ;;
|
||||||
|
*) fail "Unexpected mouse_word for second click in: $M" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# The same variables have a separate path when the pane is in a mode (the word
|
||||||
|
# and line come from the mode, not the live grid). A binding in the copy-mode
|
||||||
|
# key table fires while copy mode is active.
|
||||||
|
$TMUX bind -T copy-mode MouseDown1Pane run-shell \
|
||||||
|
"$TMUX set -g @cm 'x=#{mouse_x} word=#{mouse_word} line=#{mouse_line}'"
|
||||||
|
$TMUX copy-mode -t cov:0.0
|
||||||
|
sleep 1
|
||||||
|
click 8 1
|
||||||
|
CM=$($TMUX show -gv @cm 2>/dev/null)
|
||||||
|
case "$CM" in
|
||||||
|
*"word=beta"*) ;;
|
||||||
|
*) fail "Unexpected copy-mode mouse_word in: $CM" ;;
|
||||||
|
esac
|
||||||
|
$TMUX send-keys -t cov:0.0 -X cancel
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Hyperlinks: a new window whose pane emits an OSC 8 hyperlink over the text
|
||||||
|
# "LINKED". Clicking it reports the target URL via mouse_hyperlink (this drives
|
||||||
|
# the grid hyperlink lookup). The emitter is written to a small script to keep
|
||||||
|
# the escape sequence readable.
|
||||||
|
LINKSH="${TMPDIR:-/tmp}/fmt-mouse-link-$$.sh"
|
||||||
|
cat >"$LINKSH" <<'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
printf '\033]8;;http://example.com\033\\LINKED\033]8;;\033\\\n'
|
||||||
|
exec cat
|
||||||
|
EOF
|
||||||
|
chmod +x "$LINKSH"
|
||||||
|
$TMUX neww -t cov: -n link "$LINKSH"
|
||||||
|
sleep 1
|
||||||
|
$TMUX select-window -t cov:link
|
||||||
|
sleep 1
|
||||||
|
click 3 1
|
||||||
|
M=$($TMUX show -gv @m 2>/dev/null)
|
||||||
|
rm -f "$LINKSH"
|
||||||
|
case "$M" in
|
||||||
|
*"hl=[http://example.com]"*) ;;
|
||||||
|
*) fail "Unexpected mouse_hyperlink in: $M" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
exit 0
|
||||||
388
regress/format-variables.sh
Normal file
388
regress/format-variables.sh
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Tests that every format variable listed in tmux(1) (the format_table in
|
||||||
|
# format.c) can be expanded without crashing the server, and checks the value
|
||||||
|
# of a stable subset.
|
||||||
|
#
|
||||||
|
# The main point is coverage and crash-safety: each variable is expanded in a
|
||||||
|
# rich context - a real attached client (from a nested tmux), a control-mode
|
||||||
|
# client, a grouped session, two windows with a bell alert, a window with two
|
||||||
|
# panes running cat, a paste buffer and options - so the per-variable callbacks
|
||||||
|
# actually run. format-modifiers.sh covers the modifier machinery; this covers
|
||||||
|
# the variable callbacks.
|
||||||
|
|
||||||
|
PATH=/bin:/usr/bin
|
||||||
|
TERM=screen
|
||||||
|
|
||||||
|
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||||
|
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||||
|
# A second server on its own socket provides a real terminal (an inner client
|
||||||
|
# attached inside one of its panes) so client terminal variables are populated.
|
||||||
|
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||||
|
|
||||||
|
# Every variable name in format_table[]. Kept as a plain word list so it can be
|
||||||
|
# iterated with normal shell word splitting.
|
||||||
|
NAMES="
|
||||||
|
active_window_index
|
||||||
|
alternate_on
|
||||||
|
alternate_saved_x
|
||||||
|
alternate_saved_y
|
||||||
|
bracket_paste_flag
|
||||||
|
buffer_created
|
||||||
|
buffer_full
|
||||||
|
buffer_mode_format
|
||||||
|
buffer_name
|
||||||
|
buffer_sample
|
||||||
|
buffer_size
|
||||||
|
client_activity
|
||||||
|
client_cell_height
|
||||||
|
client_cell_width
|
||||||
|
client_colours
|
||||||
|
client_control_mode
|
||||||
|
client_created
|
||||||
|
client_discarded
|
||||||
|
client_flags
|
||||||
|
client_height
|
||||||
|
client_key_table
|
||||||
|
client_last_session
|
||||||
|
client_mode_format
|
||||||
|
client_name
|
||||||
|
client_pid
|
||||||
|
client_prefix
|
||||||
|
client_readonly
|
||||||
|
client_session
|
||||||
|
client_termfeatures
|
||||||
|
client_termname
|
||||||
|
client_termtype
|
||||||
|
client_theme
|
||||||
|
client_tty
|
||||||
|
client_uid
|
||||||
|
client_user
|
||||||
|
client_utf8
|
||||||
|
client_width
|
||||||
|
client_written
|
||||||
|
config_files
|
||||||
|
cursor_blinking
|
||||||
|
cursor_character
|
||||||
|
cursor_colour
|
||||||
|
cursor_flag
|
||||||
|
cursor_shape
|
||||||
|
cursor_very_visible
|
||||||
|
cursor_x
|
||||||
|
cursor_y
|
||||||
|
history_all_bytes
|
||||||
|
history_bytes
|
||||||
|
history_limit
|
||||||
|
history_size
|
||||||
|
host
|
||||||
|
host_short
|
||||||
|
insert_flag
|
||||||
|
keypad_cursor_flag
|
||||||
|
keypad_flag
|
||||||
|
last_window_index
|
||||||
|
mouse_all_flag
|
||||||
|
mouse_any_flag
|
||||||
|
mouse_button_flag
|
||||||
|
mouse_hyperlink
|
||||||
|
mouse_line
|
||||||
|
mouse_pane
|
||||||
|
mouse_sgr_flag
|
||||||
|
mouse_standard_flag
|
||||||
|
mouse_status_line
|
||||||
|
mouse_status_range
|
||||||
|
mouse_utf8_flag
|
||||||
|
mouse_word
|
||||||
|
mouse_x
|
||||||
|
mouse_y
|
||||||
|
next_session_id
|
||||||
|
origin_flag
|
||||||
|
pane_active
|
||||||
|
pane_at_bottom
|
||||||
|
pane_at_left
|
||||||
|
pane_at_right
|
||||||
|
pane_at_top
|
||||||
|
pane_bg
|
||||||
|
pane_bottom
|
||||||
|
pane_current_command
|
||||||
|
pane_current_path
|
||||||
|
pane_dead
|
||||||
|
pane_dead_signal
|
||||||
|
pane_dead_status
|
||||||
|
pane_dead_time
|
||||||
|
pane_fg
|
||||||
|
pane_flags
|
||||||
|
pane_floating_flag
|
||||||
|
pane_format
|
||||||
|
pane_height
|
||||||
|
pane_id
|
||||||
|
pane_in_mode
|
||||||
|
pane_index
|
||||||
|
pane_input_off
|
||||||
|
pane_key_mode
|
||||||
|
pane_last
|
||||||
|
pane_left
|
||||||
|
pane_marked
|
||||||
|
pane_marked_set
|
||||||
|
pane_mode
|
||||||
|
pane_path
|
||||||
|
pane_pb_progress
|
||||||
|
pane_pb_state
|
||||||
|
pane_pid
|
||||||
|
pane_pipe
|
||||||
|
pane_pipe_pid
|
||||||
|
pane_right
|
||||||
|
pane_search_string
|
||||||
|
pane_start_command
|
||||||
|
pane_start_path
|
||||||
|
pane_synchronized
|
||||||
|
pane_tabs
|
||||||
|
pane_title
|
||||||
|
pane_top
|
||||||
|
pane_tty
|
||||||
|
pane_unseen_changes
|
||||||
|
pane_width
|
||||||
|
pane_x
|
||||||
|
pane_y
|
||||||
|
pane_z
|
||||||
|
pane_zoomed_flag
|
||||||
|
pid
|
||||||
|
scroll_region_lower
|
||||||
|
scroll_region_upper
|
||||||
|
server_sessions
|
||||||
|
session_active
|
||||||
|
session_activity
|
||||||
|
session_activity_flag
|
||||||
|
session_alert
|
||||||
|
session_alerts
|
||||||
|
session_attached
|
||||||
|
session_attached_list
|
||||||
|
session_bell_flag
|
||||||
|
session_created
|
||||||
|
session_format
|
||||||
|
session_group
|
||||||
|
session_group_attached
|
||||||
|
session_group_attached_list
|
||||||
|
session_group_list
|
||||||
|
session_group_many_attached
|
||||||
|
session_group_size
|
||||||
|
session_grouped
|
||||||
|
session_id
|
||||||
|
session_last_attached
|
||||||
|
session_many_attached
|
||||||
|
session_marked
|
||||||
|
session_name
|
||||||
|
session_path
|
||||||
|
session_silence_flag
|
||||||
|
session_stack
|
||||||
|
session_windows
|
||||||
|
sixel_support
|
||||||
|
socket_path
|
||||||
|
start_time
|
||||||
|
synchronized_output_flag
|
||||||
|
tree_mode_format
|
||||||
|
uid
|
||||||
|
user
|
||||||
|
version
|
||||||
|
window_active
|
||||||
|
window_active_clients
|
||||||
|
window_active_clients_list
|
||||||
|
window_active_sessions
|
||||||
|
window_active_sessions_list
|
||||||
|
window_activity
|
||||||
|
window_activity_flag
|
||||||
|
window_bell_flag
|
||||||
|
window_bigger
|
||||||
|
window_cell_height
|
||||||
|
window_cell_width
|
||||||
|
window_end_flag
|
||||||
|
window_flags
|
||||||
|
window_format
|
||||||
|
window_height
|
||||||
|
window_id
|
||||||
|
window_index
|
||||||
|
window_last_flag
|
||||||
|
window_layout
|
||||||
|
window_linked
|
||||||
|
window_linked_sessions
|
||||||
|
window_linked_sessions_list
|
||||||
|
window_marked_flag
|
||||||
|
window_name
|
||||||
|
window_offset_x
|
||||||
|
window_offset_y
|
||||||
|
window_panes
|
||||||
|
window_raw_flags
|
||||||
|
window_silence_flag
|
||||||
|
window_stack_index
|
||||||
|
window_start_flag
|
||||||
|
window_visible_layout
|
||||||
|
window_width
|
||||||
|
window_zoomed_flag
|
||||||
|
wrap_flag
|
||||||
|
"
|
||||||
|
|
||||||
|
# test_var $name $expected [$extra_args...]
|
||||||
|
#
|
||||||
|
# Expand a single #{name} and compare against $expected. Any extra arguments
|
||||||
|
# are passed straight to display-message (e.g. -c or -t).
|
||||||
|
test_var()
|
||||||
|
{
|
||||||
|
name="$1"
|
||||||
|
exp="$2"
|
||||||
|
shift 2
|
||||||
|
|
||||||
|
out=$($TMUX display-message "$@" -p "#{$name}")
|
||||||
|
if [ "$out" != "$exp" ]; then
|
||||||
|
echo "Variable test failed for '#{$name}'."
|
||||||
|
echo "Expected: '$exp'"
|
||||||
|
echo "But got '$out'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_alive()
|
||||||
|
{
|
||||||
|
if [ "$($TMUX display-message -p alive)" != "alive" ]; then
|
||||||
|
echo "Server did not survive: $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
FIFO="${TMPDIR:-/tmp}/fmt-vars-$$"
|
||||||
|
HOLD=""
|
||||||
|
CC=""
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
[ -n "$HOLD" ] && kill $HOLD 2>/dev/null
|
||||||
|
[ -n "$CC" ] && kill $CC 2>/dev/null
|
||||||
|
rm -f "$FIFO"
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX2 kill-server 2>/dev/null
|
||||||
|
}
|
||||||
|
fail()
|
||||||
|
{
|
||||||
|
echo "$1"
|
||||||
|
cleanup
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$TMUX kill-server 2>/dev/null
|
||||||
|
$TMUX2 kill-server 2>/dev/null
|
||||||
|
|
||||||
|
# A session "cov" with a window "win0" holding two panes running cat, plus a
|
||||||
|
# second window, an option and a paste buffer.
|
||||||
|
$TMUX new-session -d -s cov -x 80 -y 24 -n win0 'cat' || exit 1
|
||||||
|
$TMUX set -g automatic-rename off
|
||||||
|
$TMUX set -g monitor-bell on
|
||||||
|
$TMUX set -g monitor-activity on
|
||||||
|
$TMUX split-window -t cov:win0 -d 'cat' || exit 1
|
||||||
|
$TMUX new-window -d -t cov:1 -n win1 'cat' || exit 1
|
||||||
|
$TMUX set -g @opt 'optionvalue' || exit 1
|
||||||
|
$TMUX set-buffer -b buf0 'somebuffer' || exit 1
|
||||||
|
|
||||||
|
# A second session grouped with cov, so the session_group_* variables have real
|
||||||
|
# data to report.
|
||||||
|
$TMUX new-session -d -s cov2 -t cov || exit 1
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
$TMUX send-keys -t cov:win0.0 'some pane content' Enter
|
||||||
|
# Ring the bell in the non-current window so a bell alert is raised on the
|
||||||
|
# session (this populates session_alert/session_alerts and window_bell_flag).
|
||||||
|
$TMUX send-keys -t cov:win1.0 C-g
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Attach a control-mode client, held open by a background process keeping the
|
||||||
|
# write end of a FIFO open, so client_* variables have a client to read.
|
||||||
|
rm -f "$FIFO"
|
||||||
|
mkfifo "$FIFO" || exit 1
|
||||||
|
sleep 30 >"$FIFO" &
|
||||||
|
HOLD=$!
|
||||||
|
$TMUX -C attach -t cov <"$FIFO" >/dev/null 2>&1 &
|
||||||
|
CC=$!
|
||||||
|
|
||||||
|
# Attach a real client too: an inner tmux running inside a pane of the second
|
||||||
|
# server gets a genuine terminal, which populates the terminal-dependent client
|
||||||
|
# variables (client_termname, cursor_shape, the I modifier, ...).
|
||||||
|
$TMUX2 new-session -d -x 90 -y 30 "$TMUX attach -t cov" || exit 1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# The real (terminal) client, identified by not being in control mode.
|
||||||
|
RC=$($TMUX list-clients -F '#{client_control_mode} #{client_name}' |
|
||||||
|
awk '$1==0 { print $2; exit }')
|
||||||
|
# The control client.
|
||||||
|
CLIENT=$($TMUX list-clients -F '#{client_control_mode} #{client_name}' |
|
||||||
|
awk '$1==1 { print $2; exit }')
|
||||||
|
[ -n "$RC" ] || fail "No real client attached."
|
||||||
|
[ -n "$CLIENT" ] || fail "No control client attached."
|
||||||
|
|
||||||
|
# Expand every variable at once, with the real terminal client and a target
|
||||||
|
# pane in context, and confirm the server survives. This runs every callback.
|
||||||
|
FMT=""
|
||||||
|
for n in $NAMES; do
|
||||||
|
FMT="$FMT#{$n}"
|
||||||
|
done
|
||||||
|
$TMUX display-message -c "$RC" -t cov:win0.0 -p "$FMT" >/dev/null 2>&1
|
||||||
|
assert_alive "expanding all variables together"
|
||||||
|
|
||||||
|
# Expand each variable on its own too, so a crash can be pinned to one name.
|
||||||
|
for n in $NAMES; do
|
||||||
|
$TMUX display-message -c "$RC" -t cov:win0.0 -p "#{$n}" >/dev/null 2>&1
|
||||||
|
assert_alive "expanding #{$n}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Deterministic checks on stable variables (targeting pane 0 of window 0).
|
||||||
|
TGT="cov:win0.0"
|
||||||
|
test_var session_name "cov" -t "$TGT"
|
||||||
|
test_var window_name "win0" -t "$TGT"
|
||||||
|
test_var window_index "0" -t "$TGT"
|
||||||
|
test_var window_panes "2" -t "$TGT"
|
||||||
|
test_var session_windows "2" -t "$TGT"
|
||||||
|
test_var pane_index "0" -t "$TGT"
|
||||||
|
test_var pane_in_mode "0" -t "$TGT"
|
||||||
|
test_var pane_at_top "1" -t "$TGT"
|
||||||
|
test_var pane_at_left "1" -t "$TGT"
|
||||||
|
test_var last_window_index "1" -t "$TGT"
|
||||||
|
test_var pid "$($TMUX display-message -p '#{pid}')" -t "$TGT"
|
||||||
|
|
||||||
|
# The grouped session is reported as such.
|
||||||
|
test_var session_grouped "1" -t "cov:"
|
||||||
|
test_var session_group_size "2" -t "cov:"
|
||||||
|
|
||||||
|
# list-buffers -F formats each paste buffer (this fills in the paste-buffer
|
||||||
|
# format defaults).
|
||||||
|
if [ "$($TMUX list-buffers -F '#{buffer_name}=#{buffer_sample}')" != \
|
||||||
|
"buf0=somebuffer" ]; then
|
||||||
|
fail "Unexpected list-buffers format output."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Version reported by the variable matches tmux -V.
|
||||||
|
VER=$($TMUX -V | sed 's/^tmux //')
|
||||||
|
test_var version "$VER" -t "$TGT"
|
||||||
|
|
||||||
|
# Client variables from each kind of client.
|
||||||
|
test_var client_name "$CLIENT" -c "$CLIENT"
|
||||||
|
test_var client_control_mode "1" -c "$CLIENT"
|
||||||
|
test_var client_control_mode "0" -c "$RC"
|
||||||
|
test_var socket_path "$($TMUX display-message -p '#{socket_path}')" -c "$CLIENT"
|
||||||
|
# The real client has a terminal, so termcap/feature/environ queries work.
|
||||||
|
test_var "I/e:TERM" "$($TMUX display-message -c "$RC" -p '#{client_termname}')" \
|
||||||
|
-c "$RC"
|
||||||
|
# Termcap and feature queries against a real terminal return a boolean.
|
||||||
|
case "$($TMUX display-message -c "$RC" -p '#{I/c:colors}')" in
|
||||||
|
0|1) ;;
|
||||||
|
*) fail "Unexpected #{I/c:colors} for real client." ;;
|
||||||
|
esac
|
||||||
|
case "$($TMUX display-message -c "$RC" -p '#{I/f:256}')" in
|
||||||
|
0|1) ;;
|
||||||
|
*) fail "Unexpected #{I/f:256} for real client." ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Time variables through the pretty and relative modifiers: start_time is the
|
||||||
|
# recent server start, exercising the "last 24 hours" and "just now" paths.
|
||||||
|
[ -n "$($TMUX display-message -p '#{t/p:start_time}')" ] ||
|
||||||
|
fail "Empty #{t/p:start_time}."
|
||||||
|
[ -n "$($TMUX display-message -p '#{t/r:start_time}')" ] ||
|
||||||
|
fail "Empty #{t/r:start_time}."
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
exit 0
|
||||||
Reference in New Issue
Block a user