mirror of
https://github.com/tmux/tmux.git
synced 2026-10-09 11:44:23 +00:00
289 lines
7.0 KiB
Bash
289 lines
7.0 KiB
Bash
#!/bin/sh
|
||
|
||
PATH=/bin:/usr/bin TERM=screen
|
||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||
TMP=$(mktemp)
|
||
EXP=$(mktemp)
|
||
trap 'rm -f "$TMP" "$EXP"; $TMUX kill-server 2>/dev/null' 0 1 15
|
||
|
||
WIDTHS="80 40 20 10 7 5 4 3 2 1 2 3 4 5 7 10 20 40 80"
|
||
HISTORY_LIMIT=220
|
||
HISTORY_BOUND=12000
|
||
JOINED_BOUND=500000
|
||
RAW_BOUND=5000000
|
||
|
||
record_fail()
|
||
{
|
||
echo "FAIL: $1"
|
||
exit_status=1
|
||
}
|
||
|
||
make_payload()
|
||
{
|
||
n=0
|
||
i=0
|
||
while [ "$i" -lt 16 ]; do
|
||
printf 'L%04d|ascii|abcdefghijklmnopqrstuvwxyz\n' "$n"; n=$((n + 1))
|
||
printf 'L%04d|wrap|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n' "$n"; n=$((n + 1))
|
||
printf 'L%04d|wide|AあB界C\n' "$n"; n=$((n + 1))
|
||
printf 'L%04d|wide-edge|abcdあZ\n' "$n"; n=$((n + 1))
|
||
printf 'L%04d|combining|é é é\n' "$n"; n=$((n + 1))
|
||
printf 'L%04d|orphan-combining|́A\n' "$n"; n=$((n + 1))
|
||
printf 'L%04d|variation|✔️ ⚠️ 🏝️\n' "$n"; n=$((n + 1))
|
||
printf 'L%04d|emoji|🙂🙂🙂\n' "$n"; n=$((n + 1))
|
||
printf 'L%04d|flag|🇬🇧 🇺🇸 🇯🇵\n' "$n"; n=$((n + 1))
|
||
printf 'L%04d|mixed|Aあé✔️B\n' "$n"; n=$((n + 1))
|
||
printf 'L%04d|style|plain \033[31mred\033[0m plain\n' "$n"; n=$((n + 1))
|
||
printf 'L%04d|hyperlink|\033]8;;https://example.com/%04d\033\\LINK\033]8;;\033\\\n' "$n" "$n"; n=$((n + 1))
|
||
printf 'L%04d|tabs|a\tb\tc\n' "$n"; n=$((n + 1))
|
||
printf 'L%04d|backspace|abc\bX\n' "$n"; n=$((n + 1))
|
||
printf 'carriage-return-%04d|abcdef\rL%04d|carriage-return|XY\n' "$n" "$n"; n=$((n + 1))
|
||
i=$((i + 1))
|
||
done
|
||
}
|
||
|
||
make_alternate_payload()
|
||
{
|
||
printf '\033[?1049h'
|
||
printf 'ALT000|wide|AあB界C\n'
|
||
printf 'ALT001|combining|é é é\n'
|
||
printf 'ALT002|style|plain \033[32mgreen\033[0m plain\n'
|
||
printf 'ALT003|emoji|🙂🙂🙂\n'
|
||
}
|
||
|
||
load_and_paste()
|
||
{
|
||
buffer=$1
|
||
shift
|
||
|
||
"$@" | $TMUX load-buffer -b "$buffer" - || exit 1
|
||
$TMUX paste-buffer -d -b "$buffer" -t stress:0.0 || exit 1
|
||
sleep 0.3
|
||
}
|
||
|
||
assert_alive()
|
||
{
|
||
alive=$($TMUX display-message -p -t stress: alive 2>&1) || {
|
||
record_fail "server died after $1"
|
||
return
|
||
}
|
||
[ "$alive" = alive ] || record_fail "server died after $1"
|
||
}
|
||
|
||
capture_joined()
|
||
{
|
||
$TMUX capture-pane -pNJ -t stress: -S - -E - >"$TMP"
|
||
}
|
||
|
||
assert_joined_sane()
|
||
{
|
||
label=$1
|
||
check_sentinels=${2:-yes}
|
||
|
||
capture_joined || {
|
||
record_fail "joined capture failed after $label"
|
||
return
|
||
}
|
||
|
||
bytes=$(wc -c <"$TMP")
|
||
if [ "$bytes" -gt "$JOINED_BOUND" ]; then
|
||
record_fail "joined capture too large after $label: $bytes"
|
||
fi
|
||
|
||
if grep -q "$(printf '\357\277\275')" "$TMP"; then
|
||
record_fail "replacement character in joined capture after $label"
|
||
fi
|
||
|
||
awk -v label="$label" '
|
||
{
|
||
line = $0
|
||
seen_on_line = 0
|
||
while (match(line, /L[0-9][0-9][0-9][0-9]\|/)) {
|
||
id = substr(line, RSTART + 1, 4) + 0
|
||
seen_on_line++
|
||
seen[id]++
|
||
if (id < last) {
|
||
printf("IDs out of order after %s: L%04d after L%04d\n",
|
||
label, id, last)
|
||
exit 1
|
||
}
|
||
if (seen[id] > 1) {
|
||
printf("duplicate ID after %s: L%04d\n", label, id)
|
||
exit 1
|
||
}
|
||
last = id
|
||
found = 1
|
||
line = substr(line, RSTART + RLENGTH)
|
||
}
|
||
if (seen_on_line > 1) {
|
||
printf("fused IDs after %s: %s\n", label, $0)
|
||
exit 1
|
||
}
|
||
}
|
||
END {
|
||
if (!found) {
|
||
printf("no line IDs after %s\n", label)
|
||
exit 1
|
||
}
|
||
}
|
||
' "$TMP" || {
|
||
cp "$TMP" "$EXP"
|
||
record_fail "joined structure failed after $label"
|
||
}
|
||
|
||
if [ "$check_sentinels" = yes ]; then
|
||
for expected in \
|
||
'L0234|mixed|' \
|
||
'L0236|hyperlink|' \
|
||
'L0239|carriage-return|XY'; do
|
||
if ! grep -Fq "$expected" "$TMP"; then
|
||
printf '%s\n' "$expected" >"$EXP"
|
||
record_fail "missing sentinel after $label"
|
||
fi
|
||
done
|
||
fi
|
||
}
|
||
|
||
assert_raw_sane()
|
||
{
|
||
label=$1
|
||
width=$2
|
||
|
||
$TMUX capture-pane -pR -t stress: >"$TMP" || {
|
||
record_fail "raw capture failed after $label"
|
||
return
|
||
}
|
||
|
||
bytes=$(wc -c <"$TMP")
|
||
if [ "$bytes" -gt "$RAW_BOUND" ]; then
|
||
record_fail "raw capture too large after $label: $bytes"
|
||
fi
|
||
|
||
awk -v label="$label" -v width="$width" '
|
||
/^C / {
|
||
cell = $0
|
||
sub(/^C /, "", cell)
|
||
split(cell, fields, " ")
|
||
split(fields[1], position, ",")
|
||
row = position[1] + 0
|
||
col = position[2] + 0
|
||
if (col >= width) {
|
||
printf("cell column outside pane after %s: %s\n", label, $0)
|
||
exit 1
|
||
}
|
||
|
||
data = $0
|
||
if (data !~ /data=\(/)
|
||
next
|
||
sub(/^.*data=\(/, "", data)
|
||
split(data, data_fields, ",")
|
||
cell_width = data_fields[1] + 0
|
||
padding = ($0 ~ /flags=PADDING/)
|
||
|
||
if (!padding && cell_width == 0) {
|
||
printf("zero-width non-padding cell after %s: %s\n", label, $0)
|
||
exit 1
|
||
}
|
||
if (padding && !(last_row == row && last_width > 1 && !last_padding)) {
|
||
printf("padding without preceding wide cell after %s: %s\n", label, $0)
|
||
exit 1
|
||
}
|
||
|
||
last_row = row
|
||
last_width = cell_width
|
||
last_padding = padding
|
||
}
|
||
' "$TMP" || {
|
||
cp "$TMP" "$EXP"
|
||
record_fail "raw structure failed after $label"
|
||
}
|
||
}
|
||
|
||
assert_history_sane()
|
||
{
|
||
label=$1
|
||
|
||
size=$($TMUX display-message -p -t stress: '#{history_size}') || {
|
||
record_fail "history-size failed after $label"
|
||
return
|
||
}
|
||
case "$size" in
|
||
''|*[!0-9]*)
|
||
record_fail "bad history size after $label: $size"
|
||
;;
|
||
*)
|
||
if [ "$size" -gt "$HISTORY_BOUND" ]; then
|
||
record_fail "history too large after $label: $size"
|
||
fi
|
||
;;
|
||
esac
|
||
}
|
||
|
||
assert_copy_mode_sane()
|
||
{
|
||
label=$1
|
||
|
||
$TMUX capture-pane -pM -t stress: >"$TMP" || {
|
||
record_fail "copy-mode capture failed after $label"
|
||
return
|
||
}
|
||
|
||
bytes=$(wc -c <"$TMP")
|
||
if [ "$bytes" -gt 20000 ]; then
|
||
record_fail "copy-mode capture too large after $label"
|
||
fi
|
||
if grep -q "$(printf '\357\277\275')" "$TMP"; then
|
||
record_fail "replacement character in copy-mode after $label"
|
||
fi
|
||
if ! grep -Eq 'L[0-9][0-9][0-9][0-9]\|' "$TMP"; then
|
||
record_fail "no line ID in copy-mode after $label"
|
||
fi
|
||
}
|
||
|
||
run_resize_checks()
|
||
{
|
||
for width in $WIDTHS; do
|
||
$TMUX resize-window -t stress: -x "$width" -y 8 || exit 1
|
||
sleep 0.1
|
||
assert_alive "resize to $width"
|
||
assert_joined_sane "resize to $width"
|
||
assert_raw_sane "resize to $width" "$width"
|
||
assert_history_sane "resize to $width"
|
||
done
|
||
}
|
||
|
||
$TMUX kill-server 2>/dev/null
|
||
sleep 0.1
|
||
$TMUX new-session -d -x 1 -y 1 -s test-setup "sleep 2" || exit 1
|
||
$TMUX set-option -g history-limit "$HISTORY_LIMIT" || exit 1
|
||
$TMUX new-session -d -x 80 -y 8 -s stress "stty -echo; cat" || exit 1
|
||
$TMUX kill-session -t test-setup
|
||
sleep 0.3
|
||
|
||
load_and_paste stress-data make_payload
|
||
run_resize_checks
|
||
|
||
$TMUX copy-mode -H -t stress: || exit 1
|
||
for cmd in history-top page-down halfpage-down halfpage-up page-up history-bottom; do
|
||
$TMUX send-keys -t stress: -X "$cmd" || exit 1
|
||
sleep 0.1
|
||
assert_copy_mode_sane "$cmd"
|
||
done
|
||
$TMUX send-keys -t stress: -X cancel || exit 1
|
||
|
||
load_and_paste stress-alt make_alternate_payload
|
||
for width in 10 4 1 4 10 80; do
|
||
$TMUX resize-window -t stress: -x "$width" -y 8 || exit 1
|
||
sleep 0.1
|
||
assert_alive "alternate resize to $width"
|
||
assert_raw_sane "alternate resize to $width" "$width"
|
||
done
|
||
printf '\033[?1049l' | $TMUX load-buffer -b stress-alt-exit - || exit 1
|
||
$TMUX paste-buffer -d -b stress-alt-exit -t stress:0.0 || exit 1
|
||
sleep 0.3
|
||
assert_joined_sane "alternate screen exit" no
|
||
|
||
$TMUX kill-server 2>/dev/null
|
||
exit $exit_status
|