test: add headless launch + --doctor smoke tests for AppImage artifact (#592)

The AppImage artifact test only validated package structure (extraction,
AppDir layout, asar contents) — runtime regressions like frame-fix-wrapper
syntax errors, bad asar patches, or Electron startup crashes silently
passed CI. The .deb path already ran `--doctor` as a smoke check; the
AppImage path now has parity plus a 10s headless launch under Xvfb.

`setsid` + `kill -- -PGID` is load-bearing: xvfb-run's EXIT trap leaks
Xvfb on signal kill, so running the whole stack in its own process group
lets the teardown reap xvfb-run, Xvfb, dbus, AppRun, electron, and zygote
children together. `procps` (for pkill), `dbus-x11`, and `xvfb` added to
the CI apt line.

The headless probe catches main-process startup failures only — GPU /
renderer-process crashes like #583 leave the main process alive and pass
this check; that scope disclaimer is inlined at test-artifact-appimage.sh
lines 114-117 so future contributors don't try to claim #583 coverage by
switching Xvfb off.

Co-authored-by: Sum Abiut <sabiut@users.noreply.github.com>
This commit is contained in:
Sum Abiut
2026-05-17 01:15:39 +11:00
committed by GitHub
parent 4b2b1d3390
commit b676519c58
2 changed files with 87 additions and 1 deletions

View File

@@ -44,7 +44,8 @@ jobs:
if: matrix.format != 'rpm' if: matrix.format != 'rpm'
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y file libfuse2 nodejs npm sudo apt-get install -y file libfuse2 nodejs npm \
xvfb dbus-x11 procps
- name: Run artifact tests - name: Run artifact tests
run: | run: |

85
tests/test-artifact-appimage.sh Normal file → Executable file
View File

@@ -96,6 +96,91 @@ assert_contains "$appdir/AppRun" 'build_electron_args' \
resources_dir="$appdir/usr/lib/node_modules/electron/dist/resources" resources_dir="$appdir/usr/lib/node_modules/electron/dist/resources"
validate_app_contents "$resources_dir" "${component_id}.desktop" validate_app_contents "$resources_dir" "${component_id}.desktop"
# --- Doctor smoke test ---
# Some --doctor checks fail in CI (no display, etc.); we only care that
# the script itself didn't crash via signal or exec failure (>=127).
doctor_exit=0
"$appimage_file" --doctor >/dev/null 2>&1 || doctor_exit=$?
if [[ $doctor_exit -lt 127 ]]; then
pass "--doctor runs without crashing (exit: $doctor_exit)"
else
fail "--doctor crashed (exit: $doctor_exit)"
fi
# --- Headless launch smoke test ---
# Catches startup-only regressions (asar/frame-fix-wrapper syntax errors)
# that pure structure checks miss.
#
# Scope: main-process startup failures only. GPU/renderer-process
# crashes (e.g. #583-class) leave the main process alive and pass
# this check — Xvfb has no GPU, so Electron falls back to SwiftShader
# and the GPU-crash path isn't exercised here.
if command -v xvfb-run &>/dev/null \
&& command -v dbus-run-session &>/dev/null \
&& command -v setsid &>/dev/null; then
# XDG_CACHE_HOME redirect so the test owns the launcher log.
cache_root=$(mktemp -d)
export XDG_CACHE_HOME="$cache_root"
launcher_log="$cache_root/claude-desktop-debian/launcher.log"
# setsid puts xvfb-run + Xvfb + dbus + AppRun + electron in a fresh
# process group; xvfb-run's EXIT trap alone leaves Xvfb behind on
# TERM, so we need kill -- -PGID below.
# AppRun redirects electron's stdout/stderr into launcher_log;
# xvfb_log captures xvfb-run's own stderr.
xvfb_log=$(mktemp)
setsid xvfb-run -a -s '-screen 0 1280x720x24' \
dbus-run-session -- "$appimage_file" \
>"$xvfb_log" 2>&1 &
launch_pid=$!
# Safety net: covers Ctrl-C, CI timeout, or any earlier `exit` so we
# never leak Xvfb/electron between launch and the explicit kill below.
trap '
kill -KILL -- "-$launch_pid" 2>/dev/null
pkill -KILL -f "$appimage_file" 2>/dev/null
rm -rf "$cache_root" "$xvfb_log"
' EXIT INT TERM
# CI is slow; 10s is the floor for Electron startup.
sleep 10
if kill -0 "$launch_pid" 2>/dev/null; then
pass "AppImage stays alive under Xvfb for 10s"
else
wait "$launch_pid" 2>/dev/null
exit_code=$?
fail "AppImage exited within 10s (exit: $exit_code)"
if [[ -f $launcher_log ]]; then
echo '--- launcher.log (last 40 lines) ---' >&2
tail -40 "$launcher_log" >&2
echo '------------------------------------' >&2
fi
if [[ -s $xvfb_log ]]; then
echo '--- xvfb-run stderr (last 20 lines) ---' >&2
tail -20 "$xvfb_log" >&2
echo '---------------------------------------' >&2
fi
fi
# Negative PID targets the process group.
kill -TERM -- "-$launch_pid" 2>/dev/null || true
sleep 1
kill -KILL -- "-$launch_pid" 2>/dev/null || true
wait "$launch_pid" 2>/dev/null || true
# Sweep any electron child that escaped the group (e.g. zygote).
pkill -KILL -f "$appimage_file" 2>/dev/null || true
rm -rf "$cache_root" "$xvfb_log"
unset XDG_CACHE_HOME
else
# Match the codebase convention (test-artifact-common.sh
# validate_app_contents): tool absence is a skip, not a failure.
# Loud failure on missing tools belongs at the workflow layer.
pass "Skipping launch smoke test (xvfb-run/dbus-run-session/setsid missing)"
fi
# --- Cleanup --- # --- Cleanup ---
rm -rf "$extract_dir" rm -rf "$extract_dir"