Files
omarchycn/test/shell.d/compositor-guard-test.sh
T
0fa3170504 Skip shell tests when the compositor can't be reached (#6749)
* Skip shell tests when the compositor can't be reached, not just when WAYLAND_DISPLAY is unset

A set variable only proves the environment was inherited. Sandboxes pass it
through while blocking $XDG_RUNTIME_DIR, so Quickshell cleared the guard and
aborted inside QGuiApplication, leaving two core dumps per launch instead of a
clean skip. Probe the socket and, when there's a signature to ask with,
Hyprland itself. Disable core dumps on the way through for the compositor that
dies mid-run, which no probe can catch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Retry the compositor query before calling it dead

Hyprland can miss a query while it reconfigures outputs, and one miss was enough
to skip a whole file's runtime coverage. Retry the way omarchy-launch-shell
does. Only a leftover socket reaches the query at all, so the ordinary skip
still returns immediately.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 16:49:15 +02:00

102 lines
3.8 KiB
Bash

#!/bin/bash
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
require_command python3
test_dir=$(mktemp -d)
trap 'rm -rf "$test_dir"' EXIT
runtime_dir="$test_dir/run"
stub_bin="$test_dir/bin"
mkdir -p "$runtime_dir" "$stub_bin"
# A bound AF_UNIX socket outlives the process that bound it, which is exactly the
# corpse a dead compositor leaves behind.
python3 -c 'import socket, sys; socket.socket(socket.AF_UNIX).bind(sys.argv[1])' "$runtime_dir/wayland-1"
attempts_log="$test_dir/hyprctl-attempts"
stub_hyprctl() {
: >"$attempts_log"
cat >"$stub_bin/hyprctl" <<STUB
#!/bin/bash
echo asked >>"$attempts_log"
exit $1
STUB
chmod +x "$stub_bin/hyprctl"
}
# Answers only from the second query on, the way Hyprland behaves while it is
# busy reconfiguring outputs.
stub_flaky_hyprctl() {
: >"$attempts_log"
cat >"$stub_bin/hyprctl" <<STUB
#!/bin/bash
echo asked >>"$attempts_log"
(( \$(grep -c asked "$attempts_log") > 1 ))
STUB
chmod +x "$stub_bin/hyprctl"
}
attempts() {
grep -c asked "$attempts_log" || true
}
# The guard exits the shell it runs in, so run it in a child and report back what
# it did: the skip line, or the core limit it left behind for Quickshell.
run_guard() {
env "$@" PATH="$stub_bin:$PATH" bash -c '
source "$1/base-test.sh"
ulimit -c unlimited 2>/dev/null || true
require_compositor "sample runtime test"
printf "launched with core limit %s\n" "$(ulimit -c)"
' bash "$SHELL_TEST_DIR" 2>&1 || printf 'guard exited %s\n' "$?"
}
skipped="ok - no Wayland compositor; skipping sample runtime test"
output=$(run_guard -u WAYLAND_DISPLAY XDG_RUNTIME_DIR="$runtime_dir")
[[ $output == "$skipped" ]] || fail "guard skips without a display" "$output"
pass "guard skips without a display"
# The sandbox shape from the bug report: the variable comes through, the runtime
# directory does not.
output=$(run_guard WAYLAND_DISPLAY=wayland-1 XDG_RUNTIME_DIR="$test_dir/blocked")
[[ $output == "$skipped" ]] || fail "guard skips when the socket is unreachable" "$output"
pass "guard skips when the socket is unreachable"
# Hyprland can miss a single query mid-reconfigure, so only a compositor that
# stays silent counts as gone.
stub_hyprctl 1
output=$(run_guard WAYLAND_DISPLAY=wayland-1 XDG_RUNTIME_DIR="$runtime_dir" HYPRLAND_INSTANCE_SIGNATURE=test)
[[ $output == "$skipped" ]] || fail "guard skips when the compositor stopped answering" "$output"
pass "guard skips when the compositor stopped answering"
[[ $(attempts) == 3 ]] || fail "guard retries a silent compositor before giving up" "asked $(attempts) times"
pass "guard retries a silent compositor before giving up"
stub_flaky_hyprctl
output=$(run_guard WAYLAND_DISPLAY=wayland-1 XDG_RUNTIME_DIR="$runtime_dir" HYPRLAND_INSTANCE_SIGNATURE=test)
[[ $output == "launched with core limit 0" ]] || fail "guard rides out a missed query" "$output"
pass "guard rides out a missed query"
# Without a signature there is nothing to ask, and a live socket is all the
# evidence there is: run rather than skip real coverage.
stub_hyprctl 1
output=$(run_guard -u HYPRLAND_INSTANCE_SIGNATURE WAYLAND_DISPLAY=wayland-1 XDG_RUNTIME_DIR="$runtime_dir")
[[ $output == "launched with core limit 0" ]] || fail "guard runs when hyprctl cannot be asked" "$output"
pass "guard runs when hyprctl cannot be asked"
[[ $(attempts) == 0 ]] || fail "guard leaves hyprctl alone without a signature" "asked $(attempts) times"
pass "guard leaves hyprctl alone without a signature"
# Quickshell aborts if the compositor disappears mid-run, so the tests it is
# about to launch must not be able to dump core.
stub_hyprctl 0
output=$(run_guard WAYLAND_DISPLAY=wayland-1 XDG_RUNTIME_DIR="$runtime_dir" HYPRLAND_INSTANCE_SIGNATURE=test)
[[ $output == "launched with core limit 0" ]] || fail "guard runs with core dumps disabled" "$output"
pass "guard runs with core dumps disabled"