Files
aaddrick--claude-desktop-de…/tests/launcher-disable-gpu.bats
T
wehub-resource-sync 3e076d4dd9
Deploy Worker / deploy (push) Failing after 1s
Shellcheck / Check shell scripts (push) Failing after 1s
CI / Build Packages (amd64 - appimage) (push) Has been skipped
CI / Build Packages (amd64 - deb) (push) Has been skipped
CI / Build Packages (amd64 - rpm) (push) Has been skipped
CI / Build Packages (arm64 - appimage) (push) Has been skipped
CI / Build Packages (arm64 - deb) (push) Has been skipped
CI / Test Flags Parsing (push) Failing after 1s
BATS Tests / BATS unit tests (push) Failing after 1s
CI / Build Packages (arm64 - rpm) (push) Has been skipped
CI / Test Build Artifacts (amd64) (push) Has been skipped
CI / Test Build Artifacts (arm64) (push) Has been skipped
CI / Update APT Repository (push) Has been cancelled
CI / Mirror Official .deb to Release (push) Has been cancelled
CI / Update DNF Repository (push) Has been cancelled
CI / Update AUR Package (push) Has been cancelled
CI / Create Release (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:29:21 +08:00

279 lines
8.0 KiB
Bash

#!/usr/bin/env bats
#
# launcher-disable-gpu.bats
# Tests for the CLAUDE_DISABLE_GPU env var handling in
# build_electron_args (scripts/launcher-common.sh). The var is an
# opt-in workaround for the Chromium GPU process FATAL exhaustion
# tracked in #583. CLAUDE_DISABLE_GPU=1 adds --disable-gpu and
# --disable-software-rasterizer; co-occurrence with XRDP must not
# stack duplicate flags.
#
SCRIPT_DIR="$(cd "$(dirname "${BATS_TEST_FILENAME}")" && pwd)"
LAUNCHER_COMMON="${SCRIPT_DIR}/../scripts/launcher-common.sh"
setup() {
TEST_TMP=$(mktemp -d)
export TEST_TMP
# loginctl shim — same pattern as launcher-xrdp-detection.bats.
# Defaults to a non-XRDP session so CLAUDE_DISABLE_GPU is the
# only signal in play unless a test overrides MOCK_LOGINCTL_TYPE.
mkdir -p "$TEST_TMP/bin"
cat > "$TEST_TMP/bin/loginctl" <<'SHIM'
#!/usr/bin/env bash
printf '%s\n' "${MOCK_LOGINCTL_TYPE:-x11}"
SHIM
chmod +x "$TEST_TMP/bin/loginctl"
export PATH="$TEST_TMP/bin:$PATH"
log_file="$TEST_TMP/launcher.log"
: > "$log_file"
unset CLAUDE_DISABLE_GPU
unset XRDP_SESSION
unset XDG_SESSION_ID
unset MOCK_LOGINCTL_TYPE
# shellcheck disable=SC1090
source "$LAUNCHER_COMMON"
is_wayland=false
use_x11_on_wayland=true
}
teardown() {
if [[ -n ${TEST_TMP:-} && -d $TEST_TMP ]]; then
rm -rf "$TEST_TMP"
fi
}
args_contain() {
local needle="$1"
local arg
for arg in "${electron_args[@]}"; do
[[ $arg == "$needle" ]] && return 0
done
return 1
}
args_count() {
local needle="$1"
local arg count=0
for arg in "${electron_args[@]}"; do
[[ $arg == "$needle" ]] && ((count++))
done
printf '%d' "$count"
}
# =============================================================================
# CLAUDE_DISABLE_GPU=1 — flags must be added
# =============================================================================
@test "disable-gpu: CLAUDE_DISABLE_GPU=1 adds flags + logs message" {
export CLAUDE_DISABLE_GPU=1
build_electron_args deb
args_contain '--disable-gpu'
args_contain '--disable-software-rasterizer'
grep -q 'CLAUDE_DISABLE_GPU=1' "$log_file"
}
# =============================================================================
# Co-occurrence with XRDP — no duplicate flags
# =============================================================================
@test "disable-gpu: with XRDP_SESSION, flags added exactly once (no dup)" {
export CLAUDE_DISABLE_GPU=1
export XRDP_SESSION=1
export XDG_SESSION_ID=5
export MOCK_LOGINCTL_TYPE=xrdp
build_electron_args deb
[[ "$(args_count '--disable-gpu')" -eq 1 ]]
[[ "$(args_count '--disable-software-rasterizer')" -eq 1 ]]
# Both signals should still log (independent diagnostic value),
# but only one set of flags should reach electron_args.
grep -q 'XRDP session detected' "$log_file"
grep -q 'CLAUDE_DISABLE_GPU=1' "$log_file"
}
# =============================================================================
# Off-states — flags must NOT be added
# =============================================================================
@test "disable-gpu: unset — flags NOT added" {
build_electron_args deb
run args_contain '--disable-gpu'
[[ "$status" -ne 0 ]]
run args_contain '--disable-software-rasterizer'
[[ "$status" -ne 0 ]]
}
@test "disable-gpu: empty string — flags NOT added" {
export CLAUDE_DISABLE_GPU=''
build_electron_args deb
run args_contain '--disable-gpu'
[[ "$status" -ne 0 ]]
}
@test "disable-gpu: =0 — flags NOT added (only literal '1' opts in)" {
export CLAUDE_DISABLE_GPU=0
build_electron_args deb
run args_contain '--disable-gpu'
[[ "$status" -ne 0 ]]
}
@test "disable-gpu: =true — flags NOT added (no boolean aliases)" {
# Documents the strict equality check. If we ever add aliases,
# update this test to match. Strict-only matches the existing
# CLAUDE_USE_WAYLAND pattern.
export CLAUDE_DISABLE_GPU=true
build_electron_args deb
run args_contain '--disable-gpu'
[[ "$status" -ne 0 ]]
}
@test "disable-gpu: prior GPU fatal auto-disables on next launch" {
cat > "$log_file" <<'LOG'
--- Claude Desktop Launcher Start ---
GPU process launch failed: error_code=1002
GPU process isn't usable. Goodbye.
--- Claude Desktop Launcher Start ---
LOG
build_electron_args deb
args_contain '--disable-gpu'
args_contain '--disable-software-rasterizer'
grep -q 'Previous launch hit GPU process FATAL' "$log_file"
}
@test "disable-gpu: recovery stays sticky on launch N+2 (no oscillation)" {
# A recovered launch runs with --disable-gpu and writes no GPU
# output, so the crash signature alone would re-enable GPU on
# launch N+2 (crash/work/crash forever). The launcher's own
# "disabling GPU" marker in the penultimate section must keep
# recovery tripped.
cat > "$log_file" <<'LOG'
--- Claude Desktop Launcher Start ---
GPU process launch failed: error_code=1002
GPU process isn't usable. Goodbye.
--- Claude Desktop Launcher Start ---
Previous launch hit GPU process FATAL - disabling GPU
--- Claude Desktop Launcher Start ---
LOG
build_electron_args deb
args_contain '--disable-gpu'
args_contain '--disable-software-rasterizer'
}
@test "disable-gpu: NixOS launcher header sections are detected" {
# nix/claude-desktop.nix writes "Launcher Start (NixOS)" headers;
# the section regex must match them or recovery silently no-ops
# on Nix.
cat > "$log_file" <<'LOG'
--- Claude Desktop Launcher Start (NixOS) ---
GPU process launch failed: error_code=1002
GPU process isn't usable. Goodbye.
--- Claude Desktop Launcher Start (NixOS) ---
LOG
build_electron_args deb
args_contain '--disable-gpu'
args_contain '--disable-software-rasterizer'
grep -q 'Previous launch hit GPU process FATAL' "$log_file"
}
@test "disable-gpu: CLAUDE_DISABLE_GPU=0 suppresses auto fallback" {
cat > "$log_file" <<'LOG'
--- Claude Desktop Launcher Start ---
GPU process launch failed: error_code=1002
GPU process isn't usable. Goodbye.
--- Claude Desktop Launcher Start ---
LOG
export CLAUDE_DISABLE_GPU=0
build_electron_args deb
run args_contain '--disable-gpu'
[[ "$status" -ne 0 ]]
}
# =============================================================================
# Bounded scan on a large launcher.log (#747)
# =============================================================================
@test "disable-gpu: large single-section log scans without O(n^2) hang" {
# One ~12 MB section with no header markers at all. The O(n^2)
# string-accumulating awk took minutes-to-hours on input this size;
# the single-pass rewrite completes in well under the 5s ceiling.
{
echo '--- Claude Desktop Launcher Start ---'
awk 'BEGIN {
for (i = 0; i < 300000; i++)
print "chromium gpu spam " i
}'
echo '--- Claude Desktop Launcher Start ---'
} > "$log_file"
run timeout 5 bash -c \
"log_file='$log_file'; source '$LAUNCHER_COMMON'; \
_previous_launch_hit_gpu_fatal"
# 124 = timeout killed it (the O(n^2) hang); anything else means
# the scan returned in time.
[[ "$status" -ne 124 ]]
}
@test "disable-gpu: large penultimate section keeps sticky recovery marker" {
# The sticky marker is written near the TOP of a section by
# build_electron_args. A tail-bounded scan would drop it and
# silently re-enable GPU (crash/work/crash oscillation), so this
# locks in that the rewrite reads the whole section instead.
{
echo '--- Claude Desktop Launcher Start ---'
echo 'Previous launch hit GPU process FATAL - disabling GPU'
awk 'BEGIN {
for (i = 0; i < 150000; i++)
print "electron debug noise " i
}'
echo '--- Claude Desktop Launcher Start ---'
} > "$log_file"
build_electron_args deb
args_contain '--disable-gpu'
args_contain '--disable-software-rasterizer'
}
@test "disable-gpu: crash signature deep in a large penultimate section is still detected" {
{
echo '--- Claude Desktop Launcher Start ---'
awk 'BEGIN {
for (i = 0; i < 150000; i++)
print "electron debug noise " i
}'
echo 'GPU process launch failed: error_code=1002'
echo "GPU process isn't usable. Goodbye."
echo '--- Claude Desktop Launcher Start ---'
} > "$log_file"
build_electron_args deb
args_contain '--disable-gpu'
args_contain '--disable-software-rasterizer'
}