Files
omarchy/test/shell.d/theme-intro-switch-test.sh
T
David Heinemeier HanssonandClaude Opus 5.5 1ac31e55ae Never leave the desktop without a wallpaper after a theme intro (#15000)
A theme switch that plays an intro gave the shell the new still only when
the intro failed. The shell kept the old theme's path, which the theme swap
deletes, and loaded it again when OWE handed the desktop back, leaving it
empty. The new still is now handed over either way.

The shell also re-reads the background link whenever OWE hands the desktop
back or a wallpaper fails to load, falling back to the theme's first
background when the link itself is broken.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:42:00 +09:00

212 lines
9.7 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
test_tmp=$(mktemp -d)
trap 'rm -rf "$test_tmp"' EXIT
home="$test_tmp/home"
state="$home/.local/state/omarchy/current"
themes="$home/.config/omarchy/themes"
commands="$test_tmp/bin"
log="$test_tmp/commands"
mkdir -p "$state" "$commands" "$home/.local/state/omarchy/theme-backgrounds"
for theme in alpha beta; do
mkdir -p "$themes/$theme/backgrounds/intros"
cp "$ROOT/themes/tokyo-night/colors.toml" "$themes/$theme/colors.toml"
printf 'still\n' >"$themes/$theme/backgrounds/1-sky.webp"
printf 'still\n' >"$themes/$theme/backgrounds/2-road.webp"
printf 'intro\n' >"$themes/$theme/backgrounds/intros/2-road.mp4"
done
cp -r "$themes/alpha" "$state/theme"
printf 'alpha\n' >"$state/theme.name"
ln -s "$state/theme/backgrounds/1-sky.webp" "$state/background"
printf '%s\n' "$state/theme/backgrounds/2-road.webp" >"$home/.local/state/omarchy/theme-backgrounds/beta"
printf 'already-consumed\n' >"$home/.local/state/omarchy/background-intro.session-id"
cat >"$commands/noop" <<'STUB'
#!/bin/bash
exit 0
STUB
chmod +x "$commands/noop"
# Post-theme retints run through bash -lc; this fixture only exercises selection and presentation.
for command in omarchy-theme-set-templates omarchy-hook omarchy-theme-set-herdr-machines omarchy-theme-bg-cache; do
ln -s noop "$commands/$command"
done
cat >"$commands/bash" <<'STUB'
#!/bin/bash
if [[ $1 == "-lc" && $2 == "omarchy-restart-hyprctl" ]]; then
omarchy-restart-hyprctl
fi
STUB
cat >"$commands/omarchy-restart-hyprctl" <<'STUB'
#!/bin/bash
echo "hypr-reload" >>"$TEST_LOG"
STUB
cat >"$commands/hyprctl" <<'STUB'
#!/bin/bash
printf '{"bool":%s}\n' "${TEST_ANIMATIONS:-true}"
STUB
cat >"$commands/omarchy-shell" <<'STUB'
#!/bin/bash
printf 'shell: %s\n' "$*" >>"$TEST_LOG"
if [[ -n ${TEST_GATE_DIR:-} ]]; then
if [[ $2 == "themeIntroStatus" && ! -f $TEST_GATE_DIR/fade-ready ]]; then
if [[ ! -f $TEST_GATE_DIR/fade-waited ]]; then
touch "$TEST_GATE_DIR/fade-waited"
echo "pending"
else
touch "$TEST_GATE_DIR/fade-ready"
echo "ready"
fi
elif [[ $2 == "themeIntroCoverStatus" && ! -f $TEST_GATE_DIR/cover-ready ]]; then
if [[ ! -f $TEST_GATE_DIR/fade-ready || $(<"$TEST_STATE/theme.name") != "$TEST_OLD_THEME" ]]; then
touch "$TEST_GATE_DIR/premature-swap"
fi
if [[ ! -f $TEST_GATE_DIR/cover-waited ]]; then
touch "$TEST_GATE_DIR/cover-waited"
echo "loading"
else
touch "$TEST_GATE_DIR/cover-ready"
echo "ready"
fi
else
echo "ready"
fi
fi
STUB
cat >"$commands/owe" <<'STUB'
#!/bin/bash
if [[ $1 == intro && -n ${TEST_RELEASE:-} ]]; then
printf '%s\n' "$$" >"$TEST_INTRO_PID"
fi
printf 'owe: %s\n' "$*" >>"$TEST_LOG"
[[ ${TEST_OWE_FAIL:-false} != true ]] || exit 1
if [[ $1 == "render" ]]; then
[[ ${TEST_LIVE_FRAME:-false} == "true" ]] || exit 1
snapshot=$(jq -r '.path' <<<"$2")
printf 'live video frame\n' >"$snapshot"
elif [[ $1 == "intro-prepare" && ${TEST_OWE_NO_PREPARE:-false} == "true" ]]; then
exit 1
fi
if [[ $1 == intro && -n ${TEST_RELEASE:-} ]]; then
for attempt in {1..100}; do
[[ ! -f $TEST_RELEASE ]] || exit 0
sleep 0.05
done
exit 1
fi
STUB
chmod +x "$commands/bash" "$commands/omarchy-restart-hyprctl" "$commands/hyprctl" "$commands/omarchy-shell" "$commands/owe"
set_theme() {
: >"$log"
HOME="$home" OMARCHY_PATH="$ROOT" PATH="$commands:$ROOT/bin:$PATH" XDG_RUNTIME_DIR="$test_tmp" TEST_LOG="$log" \
/bin/bash "$ROOT/bin/omarchy-theme-set" "$1" >"$test_tmp/stdout" 2>"$test_tmp/stderr" || fail "theme selection completes" "$(cat "$test_tmp/stderr")"
}
wait_command() {
for attempt in {1..100}; do
grep -q "$1" "$log" && return 0
sleep 0.02
done
fail "theme selection reaches $1" "$(cat "$log")"
}
set_theme beta
wait_command '^owe: intro '
grep -Fxq "owe: intro --start first-frame --refresh $state/theme/backgrounds/intros/2-road.mp4" "$log" || fail "a theme switch plays the remembered background's matching prepared intro"
[[ $(readlink "$state/background") == "$state/theme/backgrounds/2-road.webp" ]] || fail "the remembered background is selected"
! grep -qE '^shell: background (prepare|themeTransition)' "$log" || fail "an intro replaces the normal still transition"
[[ $(<"$home/.local/state/omarchy/background-intro.session-id") == already-consumed ]] || fail "a theme switch leaves login consumption unchanged"
# The shell holds the old theme's still, whose path the swap deleted, and loads
# it again when OWE hands the desktop back, so it gets the new one either way.
wait_command "^shell: background setInstant $state/theme/backgrounds/2-road.webp\$"
pass "switching themes plays the matching intro for the remembered background"
set_theme beta
[[ $(readlink "$state/background") == "$state/theme/backgrounds/1-sky.webp" ]] || fail "reselecting the current theme cycles its background"
grep -q '^shell: background themeTransition ' "$log" || fail "same-theme cycling uses the normal still transition"
! grep -q '^owe: intro ' "$log" || fail "same-theme cycling does not play an intro"
OMARCHY_THEME_SKIP_BACKGROUND=1 set_theme alpha
! grep -q '^owe: intro ' "$log" || fail "theme refresh does not play an intro"
pass "same-theme cycling and refresh keep the normal background behavior"
printf '%s\n' "$state/theme/backgrounds/2-road.webp" >"$home/.local/state/omarchy/theme-backgrounds/beta"
mkdir -p "$home/.local/state/omarchy/toggles"
touch "$home/.local/state/omarchy/toggles/background-intros-off"
set_theme beta
grep -q '^shell: background themeTransition ' "$log" || fail "disabled intros use the normal still transition"
! grep -q '^owe: intro ' "$log" || fail "disabled intros do not play on theme selection"
rm "$home/.local/state/omarchy/toggles/background-intros-off"
printf '%s\n' "$state/theme/backgrounds/2-road.webp" >"$home/.local/state/omarchy/theme-backgrounds/alpha"
TEST_ANIMATIONS=false set_theme alpha
grep -q '^shell: background themeTransition ' "$log" || fail "disabled animations use the normal still transition"
! grep -q '^owe: intro ' "$log" || fail "disabled animations do not play an intro"
OMARCHY_THEME_HEADLESS=1 set_theme beta
! grep -q '^owe: intro ' "$log" || fail "headless theme setup does not play an intro"
pass "theme intros respect the global toggle, disabled animations, and headless setup"
# Alpha's remembered image must have a clip for these presentation checks.
printf '%s\n' "$state/theme/backgrounds/2-road.webp" >"$home/.local/state/omarchy/theme-backgrounds/alpha"
TEST_OWE_FAIL=true set_theme alpha
wait_command '^shell: background setInstant '
wait_command '^hypr-reload$'
! grep -q '^owe: intro ' "$log" || fail "an unavailable renderer falls back to the still"
pass "unavailable OWE falls back to the selected still image"
printf '%s\n' "$state/theme/backgrounds/2-road.webp" >"$home/.local/state/omarchy/theme-backgrounds/beta"
release="$test_tmp/release"
TEST_LIVE_FRAME=true TEST_INTRO_PID="$test_tmp/intro.pid" TEST_RELEASE="$release" set_theme beta
wait_command '^owe: intro '
cover=$(awk '/^shell: shell prepareThemeIntro / { print $4; exit }' "$log")
[[ -f $cover && $(<"$cover") == "live video frame" ]] || fail "an interrupted video supplies the outgoing cover instead of its final still"
kill -0 "$(<"$test_tmp/intro.pid")" || fail "the fake renderer is still playing during the lock check"
timeout 1 flock "$test_tmp/omarchy-theme-set.lock" true || fail "theme playback does not retain the theme selection lock"
touch "$release"
pass "interrupted playback uses its current frame and releases the theme selection lock"
TEST_OWE_NO_PREPARE=true set_theme alpha
wait_command '^owe: intro '
grep -Fxq "owe: intro --start first-frame $state/theme/backgrounds/intros/2-road.mp4" "$log" || fail "older OWE uses the ordinary intro path"
pass "older OWE keeps the ordinary intro startup path"
gates="$test_tmp/gates"
mkdir "$gates"
TEST_GATE_DIR="$gates" TEST_STATE="$state" TEST_OLD_THEME=alpha set_theme beta
wait_command '^owe: intro '
[[ -f $gates/fade-waited && -f $gates/cover-waited && -f $gates/cover-ready ]] || fail "a rapid selection waits for the opening fade and replacement cover"
[[ ! -f $gates/premature-swap ]] || fail "the outgoing fade and cover settle before replacing the active theme"
pass "rapid switches present the outgoing video cover before replacing its still"
# Earlier switches finish in the background, so the next one waits for them,
# or a late reload of theirs reads as one during its intro.
for attempt in {1..250}; do
pgrep -f "^/bin/bash $ROOT/bin/omarchy-theme-set (alpha|beta)\$" >/dev/null || break
sleep 0.02
done
release="$test_tmp/reload-release"
TEST_INTRO_PID="$test_tmp/intro.pid" TEST_RELEASE="$release" set_theme alpha
wait_command '^owe: intro '
! grep -q '^hypr-reload$' "$log" || fail "the compositor is not reloaded during intro playback"
touch "$release"
wait_command '^hypr-reload$'
(( $(grep -c '^hypr-reload$' "$log") == 1 )) || fail "the compositor reloads once after playback"
pass "theme intros defer the compositor reload until playback finishes"
release="$test_tmp/superseded-release"
TEST_INTRO_PID="$test_tmp/intro.pid" TEST_RELEASE="$release" set_theme beta
wait_command '^owe: intro '
cover=$(awk '/^shell: shell prepareThemeIntro / { print $4; exit }' "$log")
! grep -q '^hypr-reload$' "$log" || fail "the held intro has not reloaded the compositor"
OMARCHY_THEME_SKIP_BACKGROUND=1 set_theme alpha
grep -q '^hypr-reload$' "$log" || fail "a theme refresh reloads the compositor immediately"
touch "$release"
for attempt in {1..100}; do
[[ -f $cover ]] || break
sleep 0.02
done
[[ ! -f $cover ]] || fail "the superseded intro finishes cleanup"
(( $(grep -c '^hypr-reload$' "$log") == 1 )) || fail "the superseded intro cannot reload during a newer theme"
pass "superseded intros do not reload the compositor"