Files
Panama/tests/quickshell/wallpaper-service-contract
T

248 lines
9.7 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
service="$repo_dir/config/dot/quickshell/services/Wallpaper.qml"
scanner="$repo_dir/config/dot/quickshell/scripts/panama-wallpaper-scan"
fixture="$(mktemp -d /tmp/panama-wallpaper-service.XXXXXX)"
config_home="$fixture/config"
state_home="$fixture/state"
config_path="$config_home/quickshell"
harness="$config_path/wallpaper-service-harness.qml"
test_bin="$fixture/bin"
command_log="$fixture/commands.log"
active_state="$fixture/active.json"
control="$fixture/control"
shell_log="$fixture/quickshell.log"
harness_pid=""
quoted_home="$fixture/home's"
quoted_wallpaper="$quoted_home/Pictures/Wallpapers/quoted.jpg"
bulk_wallpapers="$fixture/bulk-wallpapers"
bulk_segment="wallpaper-root-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
fail() {
printf 'wallpaper service contract: %s\n' "$1" >&2
[[ -s "$shell_log" ]] && sed -n '1,180p' "$shell_log" >&2
exit 1
}
for needle in 'property var activeByOutput' 'function applyPolicy' 'function setSingle'; do
rg -Fq "$needle" "$service" || fail "Wallpaper service is missing $needle"
done
instances_for_harness() {
qs list --all 2>/dev/null | awk -v expected="$harness" '
/^Instance / { pid = "" }
/^[[:space:]]*Process ID:/ { pid = $3 }
/^[[:space:]]*Config path:/ {
path = $0
sub(/^[[:space:]]*Config path: /, "", path)
if (path == expected && pid ~ /^[0-9]+$/) print pid
}
'
}
cleanup() {
if [[ "$harness_pid" =~ ^[0-9]+$ ]] && kill -0 "$harness_pid" 2>/dev/null; then
kill "$harness_pid" 2>/dev/null || true
fi
rm -rf "$fixture"
}
trap cleanup EXIT
for _ in $(seq 1 15); do
bulk_wallpapers="$bulk_wallpapers/$bulk_segment"
done
mkdir -p "$config_home/panama" "$state_home" "$test_bin" \
"$quoted_home/Pictures/Wallpapers" "$bulk_wallpapers"
cp -a "$repo_dir/config/dot/quickshell" "$config_path"
printf 'quoted wallpaper\n' >"$quoted_wallpaper"
for index in $(seq 1 401); do
wallpaper="$bulk_wallpapers/wallpaper-$index.jpg"
printf 'bulk wallpaper %s\n' "$index" >"$wallpaper"
touch -d "@$((1700000000 + index))" "$wallpaper"
done
if ! bulk_scan="$("$scanner" "$bulk_wallpapers")"; then
fail 'a normal scan with more than 60 images exited non-zero'
fi
expected_bulk_scan="$(for index in $(seq 401 -1 342); do
printf '%s\n' "$bulk_wallpapers/wallpaper-$index.jpg"
done)"
[[ "$bulk_scan" == "$expected_bulk_scan" ]] \
|| fail "over-cap scan did not emit exactly the newest 60 images: $bulk_scan"
cat >"$config_home/panama/settings.json" <<'JSON'
{
"displays": {
"DP-2": {"mode":"[email protected]","scale":1.5,"transform":0,"x":0,"y":0,"primary":true},
"HDMI-A-1": {"mode":"[email protected]","scale":1,"transform":0,"x":3000,"y":0,"primary":false}
}
}
JSON
printf '%s\n' '{}' >"$active_state"
: >"$command_log"
: >"$control"
cat >"$test_bin/hyprctl" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf '%s' 'hyprctl' >>"$PANAMA_WALLPAPER_COMMAND_LOG"
printf ' %s' "$@" >>"$PANAMA_WALLPAPER_COMMAND_LOG"
printf '\n' >>"$PANAMA_WALLPAPER_COMMAND_LOG"
if [[ "${1:-}" == "hyprpaper" && "${2:-}" == "wallpaper" ]]; then
request="${3:-}"
output="${request%%,*}"
path="${request#*,}"
if [[ "$(<"$PANAMA_WALLPAPER_CONTROL")" == fail-second && "$output" == HDMI-A-1 ]]; then
exit 7
fi
next="$(jq -c --arg output "$output" --arg path "$path" '. + {($output):$path}' \
"$PANAMA_WALLPAPER_ACTIVE_STATE")"
printf '%s\n' "$next" >"$PANAMA_WALLPAPER_ACTIVE_STATE"
exit 0
fi
if [[ "${1:-}" == "hyprpaper" && "${2:-}" == "listactive" ]]; then
if [[ "$(<"$PANAMA_WALLPAPER_CONTROL")" == wrong ]]; then
printf 'DP-2: /images/wrong.jpg\nHDMI-A-1: /images/b.jpg\n'
exit 0
fi
jq -r 'to_entries[] | "\(.key): \(.value)"' "$PANAMA_WALLPAPER_ACTIVE_STATE"
exit 0
fi
exit 91
EOF
chmod +x "$test_bin/hyprctl"
qs_for_harness() {
env_args=(
XDG_CONFIG_HOME="$config_home"
XDG_STATE_HOME="$state_home"
PATH="$test_bin:$PATH"
HOME="$quoted_home"
PANAMA_WALLPAPER_COMMAND_LOG="$command_log"
PANAMA_WALLPAPER_ACTIVE_STATE="$active_state"
PANAMA_WALLPAPER_CONTROL="$control"
)
if [[ "$harness_pid" =~ ^[0-9]+$ && "${1:-}" == "ipc" ]]; then
env "${env_args[@]}" qs -p "$harness" ipc --pid "$harness_pid" "${@:2}"
else
env "${env_args[@]}" qs -p "$harness" "$@"
fi
}
wait_idle() {
local status=""
for _ in $(seq 1 80); do
status="$(qs_for_harness ipc call wallpaper-service-test status)"
[[ "$(jq -r '.busy' <<<"$status")" == false ]] && { printf '%s' "$status"; return; }
sleep 0.1
done
fail "wallpaper transaction did not settle: $status"
}
wait_for_scan() {
local status=""
for _ in $(seq 1 80); do
status="$(qs_for_harness ipc call wallpaper-service-test status)"
jq -e --arg path "$quoted_wallpaper" \
'.scanning == false and (.available | index($path) != null)' \
<<<"$status" >/dev/null && return
sleep 0.1
done
fail "argument-safe wallpaper scan did not discover the quoted HOME image: $status"
}
qs_for_harness --daemonize >"$shell_log" 2>&1 || fail 'isolated service harness did not launch'
for _ in $(seq 1 60); do
harness_pid="$(instances_for_harness | head -1)"
if [[ "$harness_pid" =~ ^[0-9]+$ ]] \
&& qs_for_harness ipc show 2>/dev/null | rg -q '^target wallpaper-service-test$'; then
break
fi
sleep 0.1
done
[[ "$harness_pid" =~ ^[0-9]+$ ]] || fail 'isolated service harness process did not start'
wait_for_scan
wait_idle >/dev/null
: >"$command_log"
printf '%s\n' '{}' >"$active_state"
qs_for_harness ipc call wallpaper-service-test applyPerMonitor >/dev/null
success="$(wait_idle)"
expected_calls=$'hyprctl hyprpaper wallpaper DP-2,/images/a.jpg\nhyprctl hyprpaper wallpaper HDMI-A-1,/images/b.jpg\nhyprctl hyprpaper listactive'
[[ "$(<"$command_log")" == "$expected_calls" ]] || fail "transaction argv/order was wrong: $(<"$command_log")"
jq -e '.lastError == "" and .mode == "per-monitor" and .path == "/images/a.jpg"
and .assignments == {"HDMI-A-1":"/images/b.jpg"}
and .activeByOutput == {"DP-2":"/images/a.jpg","HDMI-A-1":"/images/b.jpg"}' \
<<<"$success" >/dev/null || fail "verified policy was not persisted: $success"
printf 'wrong\n' >"$control"
: >"$command_log"
qs_for_harness ipc call wallpaper-service-test applySingle /images/c.jpg >/dev/null
wrong="$(wait_idle)"
jq -e '.path == "/images/a.jpg" and .mode == "per-monitor"
and .lastError == "Hyprpaper did not confirm that background."' \
<<<"$wrong" >/dev/null || fail "wrong readback persisted an unverified policy: $wrong"
printf 'fail-second\n' >"$control"
: >"$command_log"
qs_for_harness ipc call wallpaper-service-test applyPerMonitor >/dev/null
failed="$(wait_idle)"
jq -e '.path == "/images/a.jpg" and .mode == "per-monitor"
and .lastError == "Hyprpaper did not apply that background."' \
<<<"$failed" >/dev/null || fail "partial output failure changed policy: $failed"
[[ "$(tail -1 "$command_log")" == 'hyprctl hyprpaper wallpaper HDMI-A-1,/images/b.jpg' ]] \
|| fail 'transaction continued after the failed output'
printf '\n' >"$control"
printf '%s\n' '{}' >"$active_state"
: >"$command_log"
qs_for_harness ipc call wallpaper-service-test seedSlideshow \
' ["/images/a.jpg","/images/b.jpg","/images/c.jpg"]' false >/dev/null
seeded="$(qs_for_harness ipc call wallpaper-service-test status)"
jq -e '.slideshowTimerRunning == true and .slideshowPath == "/images/a.jpg"' \
<<<"$seeded" >/dev/null || fail "multi-image slideshow did not arm: $seeded"
qs_for_harness ipc call wallpaper-service-test advanceSlideshow >/dev/null
advanced="$(wait_idle)"
jq -e '.mode == "slideshow" and .path == "/images/a.jpg"
and .slideshowPath == "/images/b.jpg"
and .activeByOutput == {"DP-2":"/images/b.jpg","HDMI-A-1":"/images/b.jpg"}' \
<<<"$advanced" >/dev/null || fail "automatic advance persisted policy or lost runtime state: $advanced"
printf 'wrong\n' >"$control"
qs_for_harness ipc call wallpaper-service-test advanceSlideshow >/dev/null
automatic_failure="$(wait_idle)"
jq -e '.path == "/images/a.jpg" and .slideshowPath == "/images/b.jpg"
and .slideshowTimerRunning == true
and .lastError == "Hyprpaper did not confirm that background."' \
<<<"$automatic_failure" >/dev/null \
|| fail "failed automatic advance changed runtime policy or stopped normal scheduling: $automatic_failure"
printf '\n' >"$control"
qs_for_harness ipc call wallpaper-service-test seedSlideshow ' ["/images/a.jpg"]' false >/dev/null
single_item="$(qs_for_harness ipc call wallpaper-service-test status)"
jq -e '.slideshowTimerRunning == false' <<<"$single_item" >/dev/null \
|| fail "one-item slideshow armed a repeating timer: $single_item"
qs_for_harness ipc call wallpaper-service-test applySingle /images/a.jpg >/dev/null
wait_idle >/dev/null
printf '%s\n' '{}' >"$active_state"
: >"$command_log"
qs_for_harness ipc call wallpaper-service-test rapidOutputs >/dev/null
sleep 0.6
hotplug="$(wait_idle)"
[[ "$(rg -c '^hyprctl hyprpaper listactive$' "$command_log")" -eq 1 ]] \
|| fail "rapid output changes did not coalesce: $(<"$command_log")"
jq -e '.lastError == "" and .activeByOutput == {"DP-2":"/images/a.jpg","HDMI-A-1":"/images/a.jpg"}' \
<<<"$hotplug" >/dev/null || fail "hotplug reapply was not verified: $hotplug"
if rg -n 'ReferenceError|TypeError|Binding loop|Unable to assign|Cannot assign' "$shell_log"; then
fail 'service harness emitted a QML runtime warning'
fi
printf 'wallpaper service contract: PASS\n'