diff --git a/regress/customize-mode.sh b/regress/customize-mode.sh new file mode 100644 index 000000000..89ebc6a42 --- /dev/null +++ b/regress/customize-mode.sh @@ -0,0 +1,746 @@ +#!/bin/sh + +# Exercise customize-mode-specific mutations. mode-mutation.sh covers stale +# mode-tree data while customize-mode is open; this covers the actions unique to +# customize mode: adding, setting, editing, resetting, renaming array keys and +# unsetting options, hooks, environment variables and key bindings. + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMP=$(mktemp -d) || exit 1 +TMUX_TMPDIR="$TMP" +export TMUX_TMPDIR + +TMUX="$TEST_TMUX -LtestA$$ -f/dev/null" +OUT="$TEST_TMUX -LtestB$$ -f/dev/null" +FORMAT='CM #{?is_option,O,#{?is_environment,E,K}} #{option_name}#{environment_name}#{key}=#{option_value}#{environment_value}' + +cleanup_servers() +{ + $TMUX kill-server 2>/dev/null + $OUT kill-server 2>/dev/null + sleep 0.5 +} + +cleanup() +{ + cleanup_servers + rm -rf "$TMP" +} +trap cleanup EXIT + +fail() +{ + echo "$1" >&2 + cleanup + exit 1 +} + +capture() +{ + $OUT capture-pane -p -t out:0 2>/dev/null +} + +settle() +{ + sleep 0.3 +} + +wait_clients() +{ + i=0 + while [ "$i" -lt 50 ]; do + c=$($TMUX list-clients -F x 2>/dev/null | grep -c x) + [ "$c" -eq "$1" ] && return 0 + sleep 0.2 + i=$((i + 1)) + done + fail "expected $1 clients, have $c" +} + +wait_mode() +{ + want=$1 + + i=0 + while [ "$i" -lt 50 ]; do + got=$($TMUX display-message -p -t cm:0 '#{pane_in_mode}' \ + 2>/dev/null) + [ "$got" = "$want" ] && return 0 + sleep 0.2 + i=$((i + 1)) + done + fail "pane mode state is $got, expected $want" +} + +assert_alive() +{ + $TMUX display-message -p 'alive' >/dev/null 2>&1 || + fail "$1: server exited" +} + +start_client() +{ + cleanup_servers + $TMUX new-session -d -s cm -n main -x 80 -y 24 'cat' || + fail "new-session failed" + $OUT new-session -d -s out -x 80 -y 24 "$TMUX attach -t cm" || + fail "outer client failed" + wait_clients 1 +} + +open_customize() +{ + if [ "$#" -eq 0 ]; then + $TMUX customize-mode -t cm:0 -F "$FORMAT" || + fail "customize-mode failed" + else + $TMUX customize-mode -t cm:0 -F "$FORMAT" -f "$1" || + fail "customize-mode failed" + fi + wait_mode 1 + settle +} + +send() +{ + $TMUX send-keys -t cm:0 "$@" || fail "send-keys failed: $*" +} + +send_literal() +{ + $TMUX send-keys -t cm:0 -l "$1" || fail "send literal failed: $1" +} + +repeat_key() +{ + key=$1 + count=$2 + + i=0 + while [ "$i" -lt "$count" ]; do + send "$key" + i=$((i + 1)) + done +} + +replace_prompt() +{ + send C-u + send_literal "$1" + send Enter + settle +} + +assert_equals() +{ + got=$1 + want=$2 + what=$3 + + [ "$got" = "$want" ] || + fail "$what: got '$got', expected '$want'" +} + +assert_empty() +{ + got=$1 + what=$2 + + [ -z "$got" ] || fail "$what: got '$got', expected empty" +} + +assert_contains() +{ + text=$1 + needle=$2 + what=$3 + + echo "$text" | grep -F -q "$needle" || + fail "$what: missing '$needle' in '$text'" +} + +assert_not_contains() +{ + text=$1 + needle=$2 + what=$3 + + echo "$text" | grep -F -q "$needle" && + fail "$what: found '$needle' in '$text'" + return 0 +} + +test_user_option() +{ + start_client + + open_customize + send Enter + settle + send_literal "cm_add added" + send Enter + settle + assert_equals "$($TMUX show-options -sqv @cm_add)" "added" \ + "added server user option" + + send q + wait_mode 0 + open_customize '#{==:#{option_name},@cm_add}' + send Right j Enter + settle + replace_prompt "changed" + assert_equals "$($TMUX show-options -sqv @cm_add)" "changed" \ + "set user option" + + send u y + settle + assert_empty "$($TMUX show-options -sqv @cm_add)" "unset user option" + send q + wait_mode 0 +} + +test_reset_window_pane_option() +{ + start_client + + $TMUX set-option -g window-style fg=red || + fail "set global window-style failed" + $TMUX set-window-option -t cm:0 window-style fg=green || + fail "set window window-style failed" + $TMUX set-option -p -t cm:0.0 window-style fg=blue || + fail "set pane window-style failed" + + open_customize '#{==:#{option_name},window-style}' + send j j Right j + settle + send d y + settle + + assert_equals "$($TMUX show-option -gqv window-style)" "default" \ + "reset global window-style" + assert_empty "$($TMUX show-options -wqv -t cm:0 window-style)" \ + "reset window window-style" + assert_empty "$($TMUX show-option -p -t cm:0.0 -qv window-style)" \ + "reset pane window-style" + send q + wait_mode 0 +} + +test_environment() +{ + start_client + + open_customize + repeat_key j 6 + send Enter + settle + send_literal "CM_ENV=one" + send Enter + settle + assert_equals "$($TMUX show-environment -t cm CM_ENV)" \ + "CM_ENV=one" "add session environment" + + send q + wait_mode 0 + open_customize '#{==:#{environment_name},CM_ENV}' + repeat_key j 6 + send Right j Enter + settle + replace_prompt "two" + assert_equals "$($TMUX show-environment -t cm CM_ENV)" \ + "CM_ENV=two" "set session environment" + + send S + settle + replace_prompt "global" + assert_equals "$($TMUX show-environment -g CM_ENV)" \ + "CM_ENV=global" "set global environment" + + send u y + settle + $TMUX show-environment -t cm CM_ENV >/dev/null 2>&1 && + fail "unset session environment: variable still exists" + send q + wait_mode 0 +} + +test_user_hook() +{ + start_client + + open_customize + repeat_key j 3 + send Enter + settle + send_literal "cm_hook display-message hook" + send Enter + settle + assert_equals "$($TMUX show-option -qv @cm_hook)" \ + "display-message hook" "add user hook" + + send q + wait_mode 0 + open_customize '#{==:#{option_name},@cm_hook}' + repeat_key j 3 + send Right j + settle + assert_contains "$(capture)" "@cm_hook" "user hook shown as hook" + + send u y + settle + assert_empty "$($TMUX show-option -qv @cm_hook)" "unset user hook" + send q + wait_mode 0 +} + +test_hook_array() +{ + start_client + + $TMUX set-hook -g 'after-new-session[0]' 'display-message old' || + fail "set hook array failed" + + open_customize '#{==:#{option_name},after-new-session}' + repeat_key j 3 + send Right j Right j + settle + + send a + settle + replace_prompt "1" + hooks=$($TMUX show-hooks -g after-new-session) + assert_contains "$hooks" "after-new-session[1] display-message old" \ + "renamed hook array key" + assert_not_contains "$hooks" "after-new-session[0]" \ + "old hook array key removed" + + send Enter + settle + replace_prompt "display-message new" + hooks=$($TMUX show-hooks -g after-new-session) + assert_contains "$hooks" "after-new-session[1] display-message new" \ + "set hook array value" + + send u y + settle + hooks=$($TMUX show-hooks -g after-new-session) + assert_not_contains "$hooks" "after-new-session[1]" \ + "unset hook array item" + send q + wait_mode 0 +} + +test_changed_only() +{ + start_client + + $TMUX set-option -s @cm_changed yes || + fail "set changed server option failed" + $TMUX bind-key -T prefix User1 display-message changed-only || + fail "set changed key failed" + + open_customize + send C + settle + send M-+ + settle + screen=$(capture) + assert_contains "$screen" "@cm_changed" "changed-only user option" + assert_contains "$screen" "User1" "changed-only key" + assert_not_contains "$screen" "Global Environment" \ + "changed-only hides environment" + send q + wait_mode 0 +} + +test_reset_key() +{ + start_client + + orig=$($TMUX list-keys -T prefix -F '#{key_command}' c) || + fail "default c binding not found" + $TMUX bind-key -T prefix c display-message changed || + fail "change c binding failed" + + open_customize '#{==:#{key},c}' + repeat_key j 10 + send Right j d y + settle + after=$($TMUX list-keys -T prefix -F '#{key_command}' c) || + fail "reset c binding not found" + assert_equals "$after" "$orig" "reset key binding" + send q + wait_mode 0 +} + +test_tagged_unset() +{ + start_client + + $TMUX set-option -s @cm_tag_a one || fail "set tag option a failed" + $TMUX set-option -s @cm_tag_b two || fail "set tag option b failed" + + open_customize '#{m/r:^@cm_tag_,#{option_name}}' + send Right j t j t U y + settle + assert_empty "$($TMUX show-options -sqv @cm_tag_a)" \ + "tagged unset option a" + assert_empty "$($TMUX show-options -sqv @cm_tag_b)" \ + "tagged unset option b" + send q + wait_mode 0 +} + +test_key_binding() +{ + start_client + + open_customize + repeat_key j 10 + send Enter + settle + send_literal "User0 display-message before" + send Enter + settle + keys=$($TMUX list-keys -T prefix User0 2>/dev/null) || + fail "added key binding not found" + assert_contains "$keys" "display-message before" "add key binding" + + send q + wait_mode 0 + open_customize '#{==:#{key},User0}' + repeat_key j 10 + send Right j Right j + settle + + send s + settle + replace_prompt "display-message after" + keys=$($TMUX list-keys -T prefix User0 2>/dev/null) || + fail "edited key binding not found" + assert_contains "$keys" "display-message after" "set key command" + + send j s + settle + replace_prompt "custom note" + note=$($TMUX list-keys -T prefix -F '#{key_note}' User0) + assert_equals "$note" "custom note" "set key note" + + send j s + settle + keys=$($TMUX list-keys -T prefix User0 2>/dev/null) || + fail "repeat key binding not found" + assert_contains "$keys" "bind-key -r" "toggle key repeat" + + send u y + settle + $TMUX list-keys -T prefix User0 >/dev/null 2>&1 && + fail "unset key binding: binding still exists" + send q + wait_mode 0 +} + +test_editor() +{ + start_client + + editor="$TMP/editor.sh" + editor_value="$TMP/editor-value" + { + echo '#!/bin/sh' + printf 'cat "%s" > "$1"\n' "$editor_value" + } > "$editor" || fail "write editor helper failed" + chmod +x "$editor" || fail "chmod editor helper failed" + + $TMUX set-option -g editor "$editor" || fail "set editor failed" + $TMUX set-option -g @cm_edit old || fail "set edit option failed" + + printf 'edited' > "$editor_value" || fail "write editor value failed" + open_customize '#{==:#{option_name},@cm_edit}' + send j Right j e + + i=0 + while [ "$i" -lt 50 ]; do + value=$($TMUX show-option -gqv @cm_edit) + [ "$value" = "edited" ] && break + sleep 0.2 + i=$((i + 1)) + done + assert_equals "$value" "edited" "edit option in editor" + send q + wait_mode 0 + + $TMUX bind-key -T prefix User2 display-message old || + fail "set editor key failed" + printf 'display-message edited' > "$editor_value" || + fail "write editor key command failed" + open_customize '#{==:#{key},User2}' + repeat_key j 10 + send Right j Right j e + i=0 + while [ "$i" -lt 50 ]; do + value=$($TMUX list-keys -T prefix -F '#{key_command}' User2) + [ "$value" = "display-message edited" ] && break + sleep 0.2 + i=$((i + 1)) + done + assert_equals "$value" "display-message edited" \ + "edit key command in editor" + + printf 'edited note' > "$editor_value" || + fail "write editor key note failed" + send j e + i=0 + while [ "$i" -lt 50 ]; do + value=$($TMUX list-keys -T prefix -F '#{key_note}' User2) + [ "$value" = "edited note" ] && break + sleep 0.2 + i=$((i + 1)) + done + assert_equals "$value" "edited note" "edit key note in editor" + send q + wait_mode 0 + + $TMUX set-environment -t cm CM_EDIT_ENV old || + fail "set editor environment failed" + printf 'env-edited' > "$editor_value" || + fail "write editor environment failed" + open_customize '#{==:#{environment_name},CM_EDIT_ENV}' + repeat_key j 6 + send Right j e + i=0 + while [ "$i" -lt 50 ]; do + value=$($TMUX show-environment -t cm CM_EDIT_ENV) + [ "$value" = "CM_EDIT_ENV=env-edited" ] && break + sleep 0.2 + i=$((i + 1)) + done + assert_equals "$value" "CM_EDIT_ENV=env-edited" \ + "edit environment in editor" + send q + wait_mode 0 +} + +test_stale_current_items() +{ + start_client + + $TMUX set-option -s @cm_stale_option old || + fail "set stale option failed" + open_customize '#{==:#{option_name},@cm_stale_option}' + send Right j + settle + $TMUX set-option -su @cm_stale_option || + fail "external unset stale option failed" + send Enter + send u y + send d y + settle + assert_alive "stale option current item" + send q + wait_mode 0 + + $TMUX set-environment -t cm CM_STALE_ENV old || + fail "set stale environment failed" + open_customize '#{==:#{environment_name},CM_STALE_ENV}' + repeat_key j 6 + send Right j + settle + $TMUX set-environment -t cm -u CM_STALE_ENV || + fail "external unset stale environment failed" + send Enter + send S + send u y + settle + assert_alive "stale environment current item" + send q + wait_mode 0 + + $TMUX bind-key -T prefix User3 display-message stale || + fail "set stale key failed" + open_customize '#{==:#{key},User3}' + repeat_key j 10 + send Right j + settle + $TMUX unbind-key -T prefix User3 || + fail "external unbind stale key failed" + send s + send d y + send u y + settle + assert_alive "stale key current item" + send q + wait_mode 0 +} + +test_prompt_invalidation() +{ + start_client + + $TMUX set-option -s @cm_prompt_stale old || + fail "set prompt stale option failed" + open_customize '#{==:#{option_name},@cm_prompt_stale}' + send Right j Enter + settle + $TMUX set-option -su @cm_prompt_stale || + fail "external unset prompt option failed" + replace_prompt "new" + assert_empty "$($TMUX show-options -sqv @cm_prompt_stale)" \ + "prompt stale option should not be recreated" + assert_alive "stale option prompt" + send q + wait_mode 0 + + $TMUX bind-key -T prefix User4 display-message old || + fail "set prompt stale key failed" + open_customize '#{==:#{key},User4}' + repeat_key j 10 + send Right j Right j s + settle + $TMUX unbind-key -T prefix User4 || + fail "external unbind prompt key failed" + replace_prompt "display-message new" + $TMUX list-keys -T prefix User4 >/dev/null 2>&1 && + fail "prompt stale key should stay unbound" + assert_alive "stale key prompt" + send q + wait_mode 0 + + $TMUX set-hook -g 'after-new-session[0]' 'display-message old' || + fail "set prompt stale hook array failed" + $TMUX set-hook -g 'after-new-session[1]' 'display-message taken' || + fail "set prompt stale hook array target failed" + open_customize '#{==:#{option_name},after-new-session}' + repeat_key j 3 + send Right j Right j a + settle + replace_prompt "1" + hooks=$($TMUX show-hooks -g after-new-session) + assert_contains "$hooks" "after-new-session[0] display-message old" \ + "array rename to existing key keeps old key" + assert_contains "$hooks" "after-new-session[1] display-message taken" \ + "array rename to existing key keeps target key" + assert_alive "existing array key prompt" + send q + wait_mode 0 +} + +test_editor_invalidation() +{ + start_client + + editor="$TMP/slow-editor.sh" + { + echo '#!/bin/sh' + echo 'sleep 1' + echo 'printf edited > "$1"' + } > "$editor" || fail "write slow editor helper failed" + chmod +x "$editor" || fail "chmod slow editor helper failed" + $TMUX set-option -g editor "$editor" || fail "set slow editor failed" + + $TMUX set-option -g @cm_editor_stale old || + fail "set editor stale option failed" + open_customize '#{==:#{option_name},@cm_editor_stale}' + send j Right j e + settle + $TMUX set-option -gu @cm_editor_stale || + fail "external unset editor option failed" + sleep 2 + assert_empty "$($TMUX show-option -gqv @cm_editor_stale)" \ + "editor stale option should not be recreated" + assert_alive "stale option editor" + send q + wait_mode 0 + + $TMUX bind-key -T prefix User5 display-message old || + fail "set editor stale key failed" + open_customize '#{==:#{key},User5}' + repeat_key j 10 + send Right j Right j e + settle + $TMUX unbind-key -T prefix User5 || + fail "external unbind editor key failed" + sleep 2 + $TMUX list-keys -T prefix User5 >/dev/null 2>&1 && + fail "editor stale key should stay unbound" + assert_alive "stale key editor" + send q + wait_mode 0 + + $TMUX set-environment -t cm CM_EDITOR_STALE old || + fail "set editor stale environment failed" + open_customize '#{==:#{environment_name},CM_EDITOR_STALE}' + repeat_key j 6 + send Right j e + settle + $TMUX set-environment -t cm -u CM_EDITOR_STALE || + fail "external unset editor environment failed" + sleep 2 + assert_equals "$($TMUX show-environment -t cm CM_EDITOR_STALE)" \ + "CM_EDITOR_STALE=edited" "editor stale environment is reset" + assert_alive "stale environment editor" + send q + wait_mode 0 +} + +test_invalid_prompts() +{ + start_client + + open_customize + send Enter + settle + send_literal "bad value" + send Enter + settle + assert_empty "$($TMUX show-options -sqv bad)" "invalid user option name" + assert_alive "invalid user option prompt" + send q + wait_mode 0 + + open_customize + repeat_key j 10 + send Enter + settle + send_literal "NoSuchKey display-message invalid" + send Enter + settle + assert_alive "invalid key prompt" + send q + wait_mode 0 + + $TMUX bind-key -T prefix User6 display-message valid || + fail "set invalid command key failed" + open_customize '#{==:#{key},User6}' + repeat_key j 10 + send Right j Right j s + settle + replace_prompt "not-a-command -Z" + keys=$($TMUX list-keys -T prefix -F '#{key_command}' User6) + assert_equals "$keys" "display-message valid" \ + "invalid key command keeps old command" + assert_alive "invalid key command prompt" + send q + wait_mode 0 +} + +cleanup_servers +test_user_option +test_reset_window_pane_option +test_environment +test_user_hook +test_hook_array +test_changed_only +test_reset_key +test_tagged_unset +test_key_binding +test_editor +test_stale_current_items +test_prompt_invalidation +test_editor_invalidation +test_invalid_prompts +cleanup +exit 0