Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Install tmux
run: brew install tmux
- name: Test with system Bash 3.2
env:
PATH: /usr/bin:/bin:/usr/sbin:/sbin
PATH: /usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin:/usr/local/bin
run: |
/bin/bash --version
/bin/bash -n nowplaying.tmux scripts/*.sh
Expand Down
3 changes: 0 additions & 3 deletions nowplaying.tmux
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ else
exit 1
fi

# Default options (only set if not already defined)
set_nowplaying_default_options

# Create the interpolation function
nowplaying_interpolation() {
local string="$1"
Expand Down
33 changes: 6 additions & 27 deletions scripts/helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ get_tmux_option() {
local option="$1"
local default_value="$2"
local option_value
option_value="$(tmux show-option -qv "$option")"
if [ -z "$option_value" ]; then
option_value="$(tmux show-option -gqv "$option")"
fi
if [ -z "$option_value" ]; then
echo "$default_value"

if option_value="$(tmux show-option -v "$option" 2>/dev/null)"; then
printf '%s\n' "$option_value"
elif option_value="$(tmux show-option -gv "$option" 2>/dev/null)"; then
printf '%s\n' "$option_value"
else
echo "$option_value"
printf '%s\n' "$default_value"
fi
}

Expand All @@ -34,26 +33,6 @@ get_nowplaying_default_option() {
esac
}

# Set every nowplaying tmux option default without overriding user values.
set_nowplaying_default_options() {
local option
for option in \
"@nowplaying_playing_icon" \
"@nowplaying_paused_icon" \
"@nowplaying_stopped_icon" \
"@nowplaying_scrolling_enabled" \
"@nowplaying_scrollable_threshold" \
"@nowplaying_scroll_speed" \
"@nowplaying_scroll_padding" \
"@nowplaying_auto_interval" \
"@nowplaying_playing_interval"
do
if [ -z "$(tmux show-option -gqv "$option")" ]; then
tmux set-option -g "$option" "$(get_nowplaying_default_option "$option")"
fi
done
}

# Get a nowplaying tmux option with its built-in default.
get_nowplaying_option() {
local option="$1"
Expand Down
158 changes: 142 additions & 16 deletions scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@ set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "${TMP_DIR}"' EXIT
REAL_TMUX=""
TMUX_TEST_SERVER=""

cleanup() {
if [[ -n "$REAL_TMUX" && -n "$TMUX_TEST_SERVER" ]]; then
"$REAL_TMUX" -L "$TMUX_TEST_SERVER" kill-server 2>/dev/null || true
fi
rm -rf "$TMP_DIR"
}
trap cleanup EXIT

fail() {
printf 'not ok - %s\n' "$1" >&2
Expand All @@ -28,19 +37,32 @@ write_tmux_mock() {
#!/usr/bin/env bash
if [[ "$1" == "show-option" ]]; then
option="${@: -1}"
case "$option" in
@nowplaying_playing_icon) printf '♪ ' ;;
@nowplaying_paused_icon) printf '⏸ ' ;;
@nowplaying_stopped_icon) printf '⏹ ' ;;
@nowplaying_scrolling_enabled) printf 'no' ;;
@nowplaying_scrollable_threshold) printf '50' ;;
@nowplaying_scroll_speed) printf '1' ;;
@nowplaying_scroll_padding) printf ' ' ;;
@nowplaying_auto_interval) printf 'no' ;;
@nowplaying_playing_interval) printf '1' ;;
@bad_integer) printf 'abc' ;;
@high_integer) printf '99' ;;
@low_integer) printf '0' ;;
scope="local"
if [[ " $* " == *" -gv "* || " $* " == *" -gqv "* ]]; then
scope="global"
fi

case "$scope:$option" in
global:@nowplaying_playing_icon) printf '♪ ' ;;
global:@nowplaying_paused_icon) printf '⏸ ' ;;
global:@nowplaying_stopped_icon) printf '⏹ ' ;;
global:@nowplaying_scrolling_enabled) printf 'no' ;;
global:@nowplaying_scrollable_threshold) printf '50' ;;
global:@nowplaying_scroll_speed) printf '1' ;;
global:@nowplaying_scroll_padding) printf ' ' ;;
global:@nowplaying_auto_interval) printf 'no' ;;
global:@nowplaying_playing_interval) printf '1' ;;
global:@bad_integer) printf 'abc' ;;
global:@empty_integer) printf '' ;;
global:@high_integer) printf '99' ;;
global:@low_integer) printf '0' ;;
global:@test_global_empty) printf '' ;;
global:@test_local_empty) printf 'global value' ;;
local:@test_local_empty) printf '' ;;
global:@test_precedence) printf 'global value' ;;
local:@test_precedence) printf 'local value' ;;
global:@test_trailing_spaces) printf 'value ' ;;
*) exit 1 ;;
esac
elif [[ "$1 $2" == "set-option -g" || "$1 $2" == "set-option -gq" ]]; then
:
Expand Down Expand Up @@ -127,13 +149,115 @@ run_with_mocks() {
PATH="${TMP_DIR}:${PATH}" "$@"
}

resolve_with_mock() {
PATH="${TMP_DIR}:${PATH}" bash -c 'source "$1"; get_tmux_option "$2" "$3"' \
_ "${ROOT_DIR}/scripts/helpers.sh" "$1" "$2"
}

run_real_tmux_tests() {
local real_bin="${TMP_DIR}/real-tmux-bin"
local session="nowplaying-test"
local actual
local global_options

REAL_TMUX="$(command -v tmux || true)"
if [[ -z "$REAL_TMUX" ]]; then
fail "tmux is required for isolated integration tests"
fi

TMUX_TEST_SERVER="nowplaying-test-$$-${RANDOM}"
"$REAL_TMUX" -L "$TMUX_TEST_SERVER" -f /dev/null new-session -d -s "$session"

mkdir -p "$real_bin"
export NOWPLAYING_TEST_REAL_TMUX="$REAL_TMUX"
export NOWPLAYING_TEST_SERVER="$TMUX_TEST_SERVER"
export NOWPLAYING_TEST_SESSION="$session"
cat > "${real_bin}/tmux" <<'WRAPPER'
#!/usr/bin/env bash
if [[ "$1" == "show-option" || "$1" == "set-option" ]]; then
command="$1"
shift
exec "$NOWPLAYING_TEST_REAL_TMUX" -L "$NOWPLAYING_TEST_SERVER" "$command" -t "$NOWPLAYING_TEST_SESSION" "$@"
fi
exec "$NOWPLAYING_TEST_REAL_TMUX" -L "$NOWPLAYING_TEST_SERVER" "$@"
WRAPPER
chmod +x "${real_bin}/tmux"

real_resolve() {
PATH="${real_bin}:${PATH}" bash -c 'source "$1"; get_tmux_option "$2" "$3"' \
_ "${ROOT_DIR}/scripts/helpers.sh" "$1" "$2"
}
real_nowplaying_option() {
PATH="${real_bin}:${PATH}" bash -c 'source "$1"; get_nowplaying_option "$2"' \
_ "${ROOT_DIR}/scripts/helpers.sh" "$1"
}

assert_eq "fallback" "$(real_resolve @test fallback)" "real tmux absent option uses fallback"

"$REAL_TMUX" -L "$TMUX_TEST_SERVER" set-option -g @test ""
assert_eq "" "$(real_resolve @test fallback)" "real tmux empty global option wins"
if ! "$REAL_TMUX" -L "$TMUX_TEST_SERVER" show-option -g @test >/dev/null; then
fail "real tmux empty global option remains present"
fi
printf 'ok - real tmux empty global option remains present\n'

"$REAL_TMUX" -L "$TMUX_TEST_SERVER" set-option -g @test "global value"
assert_eq "global value" "$(real_resolve @test fallback)" "real tmux global option resolves exactly"

"$REAL_TMUX" -L "$TMUX_TEST_SERVER" set-option -t "$session" @test "local value"
assert_eq "local value" "$(real_resolve @test fallback)" "real tmux local option wins"

"$REAL_TMUX" -L "$TMUX_TEST_SERVER" set-option -t "$session" @test ""
assert_eq "" "$(real_resolve @test fallback)" "real tmux empty local option wins over global"

"$REAL_TMUX" -L "$TMUX_TEST_SERVER" set-option -g -u @nowplaying_playing_icon 2>/dev/null || true
"$REAL_TMUX" -L "$TMUX_TEST_SERVER" set-option -t "$session" -u @nowplaying_playing_icon 2>/dev/null || true
assert_eq "♪ " "$(real_nowplaying_option @nowplaying_playing_icon)" "real tmux playing icon fallback preserves trailing space"

"$REAL_TMUX" -L "$TMUX_TEST_SERVER" set-option -g @nowplaying_playing_icon ""
assert_eq "" "$(real_nowplaying_option @nowplaying_playing_icon)" "real tmux empty playing icon wins"

"$REAL_TMUX" -L "$TMUX_TEST_SERVER" set-option -g @nowplaying_scroll_padding ""
assert_eq "" "$(real_nowplaying_option @nowplaying_scroll_padding)" "real tmux empty scroll padding wins"

"$REAL_TMUX" -L "$TMUX_TEST_SERVER" set-option -g -u @nowplaying_playing_icon
"$REAL_TMUX" -L "$TMUX_TEST_SERVER" set-option -g -u @nowplaying_scroll_padding
"$REAL_TMUX" -L "$TMUX_TEST_SERVER" set-option -g status-right 'before#{nowplaying}after'
PATH="${real_bin}:${PATH}" bash "${ROOT_DIR}/nowplaying.tmux"

global_options="$("$REAL_TMUX" -L "$TMUX_TEST_SERVER" show-options -g)"
if [[ "$global_options" == *"@nowplaying_"* ]]; then
fail "plugin load created nowplaying default options"
fi
printf 'ok - plugin load does not create nowplaying default options\n'

actual="$("$REAL_TMUX" -L "$TMUX_TEST_SERVER" show-option -gqv status-right)"
assert_eq "before#(${ROOT_DIR}/scripts/nowplaying.sh)after" "$actual" "status interpolation remains active"

"$REAL_TMUX" -L "$TMUX_TEST_SERVER" set-option -g @nowplaying_playing_icon ""
"$REAL_TMUX" -L "$TMUX_TEST_SERVER" set-option -g @nowplaying_scroll_padding "pad "
PATH="${real_bin}:${PATH}" bash "${ROOT_DIR}/nowplaying.tmux"

if ! "$REAL_TMUX" -L "$TMUX_TEST_SERVER" show-option -g @nowplaying_playing_icon >/dev/null; then
fail "plugin load removed empty override"
fi
assert_eq "" "$("$REAL_TMUX" -L "$TMUX_TEST_SERVER" show-option -gqv @nowplaying_playing_icon)" "plugin load preserves empty override"
assert_eq "pad " "$("$REAL_TMUX" -L "$TMUX_TEST_SERVER" show-option -gqv @nowplaying_scroll_padding)" "plugin load preserves non-empty override exactly"
}

bash -n "${ROOT_DIR}/nowplaying.tmux" "${ROOT_DIR}"/scripts/*.sh
printf 'ok - bash syntax\n'

write_tmux_mock

helper_output="$(PATH="${TMP_DIR}:${PATH}" bash -c 'source "$1"; printf "%s %s %s\n" "$(get_tmux_integer_option @bad_integer 50 4)" "$(get_tmux_integer_option @low_integer 50 4)" "$(get_tmux_integer_option @high_integer 1 1 10)"' _ "${ROOT_DIR}/scripts/helpers.sh")"
assert_eq "50 4 10" "$helper_output" "integer option validation"
assert_eq "fallback" "$(resolve_with_mock @test_absent fallback)" "mock absent option uses fallback"
assert_eq "" "$(resolve_with_mock @test_global_empty fallback)" "mock empty global option wins"
assert_eq "" "$(resolve_with_mock @test_local_empty fallback)" "mock empty local option wins over global"
assert_eq "local value" "$(resolve_with_mock @test_precedence fallback)" "mock local option wins over global"
assert_eq "value " "$(resolve_with_mock @test_trailing_spaces fallback)" "mock option preserves trailing spaces"

helper_output="$(PATH="${TMP_DIR}:${PATH}" bash -c 'source "$1"; printf "%s %s %s %s\n" "$(get_tmux_integer_option @bad_integer 50 4)" "$(get_tmux_integer_option @empty_integer 50 4)" "$(get_tmux_integer_option @low_integer 50 4)" "$(get_tmux_integer_option @high_integer 1 1 10)"' _ "${ROOT_DIR}/scripts/helpers.sh")"
assert_eq "50 50 4 10" "$helper_output" "integer option validation"

parse_output="$(PATH="${TMP_DIR}:${PATH}" bash -c 'source "$1"; parse_nowplaying_adapter_output $'"'"'Paused\tArtist\tTitle'"'"'' _ "${ROOT_DIR}/scripts/helpers.sh")"
assert_eq $'Paused\tArtist - Title' "$parse_output" "adapter output parsing"
Expand All @@ -153,4 +277,6 @@ assert_eq "⏹ Done - Song" "$(run_with_mocks "${ROOT_DIR}/scripts/nowplaying.sh
write_playerctl_mock empty
assert_eq "" "$(run_with_mocks "${ROOT_DIR}/scripts/nowplaying.sh")" "main renders empty output without metadata"

run_real_tmux_tests

printf 'all tests passed\n'
Loading