testsAndMisc/linux_configuration/tests/test_music_parallelism.sh
Krzysztof kuhy Rudnicki d889c8e655
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Python tests / test (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Split music_parallelism.sh into an entry script and three libs
412 lines -> 135, with the nine functions in lib/music_detect.sh (133),
lib/music_monitor.sh (91) and lib/music_status.sh (85).

find_music_services and kill_music_services stay together: the first
writes MUSIC_FOUND_PROCESS/MUSIC_FOUND_WINDOW and the second reads them,
so splitting between them would thread mutable state across files.

The test suite caught what the static checks could not. The harness
copies the script into a temp worktree to stub common.sh, and copied
exactly one file -- so after the split every source line failed and the
script died before any behaviour under test ran. Teaching it to copy
lib/ too is a harness fix under decision 8: no assertion changed, the
same five checks run, exit 0 before and after.

bash -n also earned its keep: the first splice dropped notify_user's
closing brace, and shellcheck reported that only as SC1094 "parsing of
sourced file failed" against the entry.

One rename, not a suppression: monitor_loop's local `music_services`
became `active_services` after shellcheck flagged SC2153 against the
MUSIC_SERVICES config array now visible in the same file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-16 21:50:34 +02:00

177 lines
5.6 KiB
Bash
Executable File

#!/bin/bash
# Regression tests for the music parallelism daemon's polling cadence.
set -euo pipefail
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
REPO_DIR=$(cd -- "$SCRIPT_DIR/.." && pwd)
TARGET_SCRIPT="$REPO_DIR/scripts/periodic_background/digital_wellbeing/music_parallelism.sh"
fail() {
printf 'FAIL: %s\n' "$1" >&2
exit 1
}
assert_equals() {
local expected="$1"
local actual="$2"
local context="$3"
if [[ "$expected" != "$actual" ]]; then
fail "$context (expected: '$expected', actual: '$actual')"
fi
}
TMP_DIR=$(mktemp -d)
cleanup() {
rm -rf "$TMP_DIR"
}
trap cleanup EXIT
WORKTREE="$TMP_DIR/worktree"
BIN_DIR="$TMP_DIR/bin"
# Mirror the script's real position inside the worktree instead of hardcoding
# it. music_parallelism.sh sources its library as $SCRIPT_DIR/../../lib/common.sh,
# so the copy must sit at the same depth below the repo root or that path
# resolves outside the worktree. When it did, the script silently fell back to
# the real /usr/local/lib/common.sh and the stub below was never used — the
# tests then measured the live system and failed as if the daemon were wrong.
# Deriving the path keeps this correct across future reorganisations.
TARGET_REL="${TARGET_SCRIPT#"$REPO_DIR"/}"
if [[ "$TARGET_REL" == "$TARGET_SCRIPT" ]]; then
fail "TARGET_SCRIPT ($TARGET_SCRIPT) is not below REPO_DIR ($REPO_DIR)"
fi
WORKTREE_SCRIPT="$WORKTREE/$TARGET_REL"
# The library lives at <repo>/scripts/lib/common.sh; keep it relative to the
# same root rather than assuming how deep the script itself is nested.
WORKTREE_LIB="$WORKTREE/scripts/lib/common.sh"
mkdir -p "$(dirname "$WORKTREE_SCRIPT")" "$(dirname "$WORKTREE_LIB")" "$BIN_DIR"
cp "$TARGET_SCRIPT" "$WORKTREE_SCRIPT"
# The script sources its own libs from $SCRIPT_DIR/lib/. They must travel into
# the worktree beside it, or every source line fails and the script exits before
# any behaviour under test runs.
cp -r "$(dirname "$TARGET_SCRIPT")/lib" "$(dirname "$WORKTREE_SCRIPT")/lib"
# Fail loudly if the copy cannot reach the stub, rather than letting the script
# fall through to the real library and produce a misleading behavioural failure.
STUB_FROM_SCRIPT="$(dirname "$WORKTREE_SCRIPT")/../../lib/common.sh"
if [[ "$(realpath -m "$STUB_FROM_SCRIPT")" != "$(realpath -m "$WORKTREE_LIB")" ]]; then
fail "worktree layout drift: script at $WORKTREE_SCRIPT resolves ../../lib/common.sh to $(realpath -m "$STUB_FROM_SCRIPT"), not the stub at $WORKTREE_LIB"
fi
cat >"$WORKTREE_LIB" <<'EOF'
#!/bin/bash
# Records that the stub — not the real installed common.sh — was sourced.
if [[ -n ${MUSIC_PARALLELISM_STUB_MARKER:-} ]]; then
printf 'sourced\n' >"$MUSIC_PARALLELISM_STUB_MARKER"
fi
FOCUS_APPS_WINDOWS=("Mock Focus App")
FOCUS_APPS_PROCESSES=("mock-focus-proc")
is_focus_app_running() {
if [[ ${MOCK_FOCUS_ACTIVE:-0} -eq 1 ]]; then
printf '%s\n' "Mock Focus App"
return 0
fi
return 1
}
get_timestamp() {
printf '%s\n' "${MOCK_TIMESTAMP:-1000}"
}
log_message() {
:
}
EOF
chmod +x "$WORKTREE_LIB"
create_fake_proc_process() {
local proc_root="$1"
local pid="$2"
local name="$3"
mkdir -p "$proc_root/$pid"
printf '%s\n' "$name" >"$proc_root/$pid/comm"
}
run_case() {
local expected_wait="$1"
local focus_active="$2"
local music_proc_name="${3:-}"
local mode="${4:-instant}"
local wait_log="$TMP_DIR/wait.log"
local proc_root="$TMP_DIR/proc"
local stub_marker="$TMP_DIR/stub.marker"
: >"$wait_log"
rm -f "$stub_marker"
rm -rf "$proc_root"
mkdir -p "$proc_root"
if [[ -n $music_proc_name ]]; then
create_fake_proc_process "$proc_root" 4242 "$music_proc_name"
fi
PATH="$BIN_DIR:$PATH" \
MUSIC_PARALLELISM_TEST_WAIT_LOG="$wait_log" \
MUSIC_PARALLELISM_TEST_EXIT_AFTER_WAIT=1 \
MUSIC_PARALLELISM_STUB_MARKER="$stub_marker" \
XDOTOOL_LOG="${XDOTOOL_LOG:-}" \
PROC_ROOT="$proc_root" \
MOCK_FOCUS_ACTIVE="$focus_active" \
bash "$WORKTREE_SCRIPT" "$mode" \
>/dev/null 2>&1 || true
# Without this the script can source the real /usr/local/lib/common.sh and
# still "pass" or fail for reasons that have nothing to do with the daemon.
[[ -f "$stub_marker" ]] \
|| fail 'stubbed common.sh was not sourced — the script used a different library, so this run measured the live system rather than the mock'
assert_equals "$expected_wait" "$(<"$wait_log")" "music_parallelism.sh should pick the expected wait interval"
}
printf 'Checking stable-focus backoff uses the slower interval...\n'
run_case 15 1
printf 'Checking conflict handling uses the faster retry interval...\n'
run_case 5 1 spotify
printf 'Checking idle mode uses the idle interval...\n'
run_case 30 0
printf 'Checking conflict path avoids duplicate xdotool searches...\n'
xdotool_log="$TMP_DIR/xdotool.log"
: >"$xdotool_log"
XDOTOOL_LOG="$xdotool_log"
cat >"$BIN_DIR/xdotool" <<'EOF'
#!/bin/bash
printf '%s\n' "$1" >> "${XDOTOOL_LOG:?}"
if [[ ${1:-} == search ]]; then
exit 1
fi
if [[ ${1:-} == windowclose ]]; then
exit 0
fi
exit 0
EOF
chmod +x "$BIN_DIR/xdotool"
run_case 5 1 spotify
search_calls=$(grep -c '^search$' "$xdotool_log" 2>/dev/null || true)
assert_equals '1' "$search_calls" 'music_parallelism.sh should avoid duplicate xdotool search calls when process-only music is detected'
printf 'Checking monitor loop also avoids duplicate xdotool searches...\n'
: >"$xdotool_log"
run_case 15 1 spotify monitor
monitor_search_calls=$(grep -c '^search$' "$xdotool_log" 2>/dev/null || true)
assert_equals '1' "$monitor_search_calls" 'music_parallelism.sh monitor loop should avoid duplicate xdotool search calls when process-only music is detected'
printf 'music_parallelism.sh regression checks passed.\n'