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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0900855a28
commit
0fa3170504
@@ -4,10 +4,7 @@ set -euo pipefail
|
|||||||
|
|
||||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||||
|
|
||||||
if [[ -z ${WAYLAND_DISPLAY:-} ]]; then
|
require_compositor "bar icon geometry test"
|
||||||
pass "no Wayland compositor; skipping bar icon geometry test"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v quickshell >/dev/null 2>&1; then
|
if ! command -v quickshell >/dev/null 2>&1; then
|
||||||
pass "quickshell not installed; skipping bar icon geometry test"
|
pass "quickshell not installed; skipping bar icon geometry test"
|
||||||
|
|||||||
@@ -18,10 +18,7 @@ cleanup() {
|
|||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
if [[ -z ${WAYLAND_DISPLAY:-} ]]; then
|
require_compositor "bar widget contract test"
|
||||||
pass "no Wayland compositor; skipping bar widget contract test"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v quickshell >/dev/null 2>&1; then
|
if ! command -v quickshell >/dev/null 2>&1; then
|
||||||
pass "quickshell not installed; skipping bar widget contract test"
|
pass "quickshell not installed; skipping bar widget contract test"
|
||||||
|
|||||||
@@ -29,6 +29,53 @@ require_command() {
|
|||||||
command -v "$command" >/dev/null || fail "required command is available: $command"
|
command -v "$command" >/dev/null || fail "required command is available: $command"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# WAYLAND_DISPLAY proves the variable was inherited, not that the compositor
|
||||||
|
# answers. Sandboxes pass the environment through while blocking
|
||||||
|
# $XDG_RUNTIME_DIR, so Quickshell clears a bare variable check and then aborts
|
||||||
|
# inside QGuiApplication, before any QML loads: a core dump per launch where a
|
||||||
|
# skip belonged. Probe the socket, then Hyprland itself, since a compositor that
|
||||||
|
# died mid-session can leave its socket behind.
|
||||||
|
compositor_reachable() {
|
||||||
|
local socket=${WAYLAND_DISPLAY:-}
|
||||||
|
|
||||||
|
[[ -n $socket ]] || return 1
|
||||||
|
[[ $socket == /* ]] || socket=${XDG_RUNTIME_DIR:-}/$socket
|
||||||
|
[[ -S $socket ]] || return 1
|
||||||
|
|
||||||
|
# A compositor that died can leave its socket behind, so ask Hyprland whether
|
||||||
|
# it is still answering. Only when it can be asked: hyprctl needs
|
||||||
|
# HYPRLAND_INSTANCE_SIGNATURE, and treating a missing signature as a dead
|
||||||
|
# compositor would skip tests that would have run fine.
|
||||||
|
[[ -n ${HYPRLAND_INSTANCE_SIGNATURE:-} ]] || return 0
|
||||||
|
|
||||||
|
# Hyprland can miss a query while it reconfigures outputs, and one miss is not
|
||||||
|
# a dead compositor; retry the way omarchy-launch-shell does rather than
|
||||||
|
# discard a whole file's runtime coverage. Only a leftover socket gets this
|
||||||
|
# far, so the waiting is rare.
|
||||||
|
local attempt
|
||||||
|
for attempt in 1 2 3; do
|
||||||
|
hyprctl -j monitors >/dev/null 2>&1 && return 0
|
||||||
|
(( attempt < 3 )) && sleep 0.5
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
require_compositor() {
|
||||||
|
local description="$1"
|
||||||
|
|
||||||
|
if compositor_reachable; then
|
||||||
|
# No probe outruns a compositor that dies mid-run, and Quickshell leaves
|
||||||
|
# through qFatal() when its connection drops. Keep that abort from writing a
|
||||||
|
# core; the test still fails, just without the debris.
|
||||||
|
ulimit -c 0 2>/dev/null || true
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
pass "no Wayland compositor; skipping $description"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
run_node_test() {
|
run_node_test() {
|
||||||
require_command node
|
require_command node
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,7 @@ assert(
|
|||||||
)
|
)
|
||||||
JS
|
JS
|
||||||
|
|
||||||
if [[ -z ${WAYLAND_DISPLAY:-} ]]; then
|
require_compositor "Button hover geometry runtime test"
|
||||||
pass "no Wayland compositor; skipping Button hover geometry runtime test"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v quickshell >/dev/null 2>&1; then
|
if ! command -v quickshell >/dev/null 2>&1; then
|
||||||
pass "quickshell not installed; skipping Button hover geometry runtime test"
|
pass "quickshell not installed; skipping Button hover geometry runtime test"
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
#!/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"
|
||||||
@@ -18,10 +18,7 @@ cleanup() {
|
|||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
if [[ -z ${WAYLAND_DISPLAY:-} ]]; then
|
require_compositor "QML contract test"
|
||||||
pass "no Wayland compositor; skipping QML contract test"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v quickshell >/dev/null 2>&1; then
|
if ! command -v quickshell >/dev/null 2>&1; then
|
||||||
pass "quickshell not installed; skipping QML contract test"
|
pass "quickshell not installed; skipping QML contract test"
|
||||||
|
|||||||
@@ -18,10 +18,7 @@ cleanup() {
|
|||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
if [[ -z ${WAYLAND_DISPLAY:-} ]]; then
|
require_compositor "lock fingerprint indicator test"
|
||||||
pass "no Wayland compositor; skipping lock fingerprint indicator test"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v quickshell >/dev/null 2>&1; then
|
if ! command -v quickshell >/dev/null 2>&1; then
|
||||||
pass "quickshell not installed; skipping lock fingerprint indicator test"
|
pass "quickshell not installed; skipping lock fingerprint indicator test"
|
||||||
|
|||||||
@@ -18,10 +18,7 @@ cleanup() {
|
|||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
if [[ -z ${WAYLAND_DISPLAY:-} ]]; then
|
require_compositor "lock password overflow test"
|
||||||
pass "no Wayland compositor; skipping lock password overflow test"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v quickshell >/dev/null 2>&1; then
|
if ! command -v quickshell >/dev/null 2>&1; then
|
||||||
pass "quickshell not installed; skipping lock password overflow test"
|
pass "quickshell not installed; skipping lock password overflow test"
|
||||||
|
|||||||
@@ -18,10 +18,7 @@ cleanup() {
|
|||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
if [[ -z ${WAYLAND_DISPLAY:-} ]]; then
|
require_compositor "manifest entrypoint load test"
|
||||||
pass "no Wayland compositor; skipping manifest entrypoint load test"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v quickshell >/dev/null 2>&1; then
|
if ! command -v quickshell >/dev/null 2>&1; then
|
||||||
pass "quickshell not installed; skipping manifest entrypoint load test"
|
pass "quickshell not installed; skipping manifest entrypoint load test"
|
||||||
|
|||||||
@@ -18,10 +18,7 @@ cleanup() {
|
|||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
if [[ -z ${WAYLAND_DISPLAY:-} ]]; then
|
require_compositor "plugin registry contract test"
|
||||||
pass "no Wayland compositor; skipping plugin registry contract test"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v quickshell >/dev/null 2>&1; then
|
if ! command -v quickshell >/dev/null 2>&1; then
|
||||||
pass "quickshell not installed; skipping plugin registry contract test"
|
pass "quickshell not installed; skipping plugin registry contract test"
|
||||||
|
|||||||
@@ -51,10 +51,7 @@ assert(
|
|||||||
)
|
)
|
||||||
JS
|
JS
|
||||||
|
|
||||||
if [[ -z ${WAYLAND_DISPLAY:-} ]]; then
|
require_compositor "pointer movement gate runtime test"
|
||||||
pass "no Wayland compositor; skipping pointer movement gate runtime test"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v quickshell >/dev/null 2>&1; then
|
if ! command -v quickshell >/dev/null 2>&1; then
|
||||||
pass "quickshell not installed; skipping pointer movement gate runtime test"
|
pass "quickshell not installed; skipping pointer movement gate runtime test"
|
||||||
|
|||||||
@@ -17,10 +17,7 @@ cleanup() {
|
|||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
if [[ -z ${WAYLAND_DISPLAY:-} ]]; then
|
require_compositor "shell runtime smoke test"
|
||||||
pass "no Wayland compositor; skipping shell runtime smoke test"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v quickshell >/dev/null 2>&1; then
|
if ! command -v quickshell >/dev/null 2>&1; then
|
||||||
pass "quickshell not installed; skipping shell runtime smoke test"
|
pass "quickshell not installed; skipping shell runtime smoke test"
|
||||||
|
|||||||
@@ -18,10 +18,7 @@ cleanup() {
|
|||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
if [[ -z ${WAYLAND_DISPLAY:-} ]]; then
|
require_compositor "screenshot sanity test"
|
||||||
pass "no Wayland compositor; skipping screenshot sanity test"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v quickshell >/dev/null 2>&1; then
|
if ! command -v quickshell >/dev/null 2>&1; then
|
||||||
pass "quickshell not installed; skipping screenshot sanity test"
|
pass "quickshell not installed; skipping screenshot sanity test"
|
||||||
|
|||||||
@@ -22,10 +22,7 @@ cleanup() {
|
|||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
if [[ -z ${WAYLAND_DISPLAY:-} ]]; then
|
require_compositor "tray menu activation test"
|
||||||
pass "no Wayland compositor; skipping tray menu activation test"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v quickshell >/dev/null 2>&1; then
|
if ! command -v quickshell >/dev/null 2>&1; then
|
||||||
pass "quickshell not installed; skipping tray menu activation test"
|
pass "quickshell not installed; skipping tray menu activation test"
|
||||||
|
|||||||
Reference in New Issue
Block a user