From b676519c58d907bfcd5b41b9a5c1436a4c895cd7 Mon Sep 17 00:00:00 2001 From: Sum Abiut Date: Sun, 17 May 2026 01:15:39 +1100 Subject: [PATCH] test: add headless launch + --doctor smoke tests for AppImage artifact (#592) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .github/workflows/test-artifacts.yml | 3 +- tests/test-artifact-appimage.sh | 85 ++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) mode change 100644 => 100755 tests/test-artifact-appimage.sh diff --git a/.github/workflows/test-artifacts.yml b/.github/workflows/test-artifacts.yml index af40ed0..a5ed1b4 100644 --- a/.github/workflows/test-artifacts.yml +++ b/.github/workflows/test-artifacts.yml @@ -44,7 +44,8 @@ jobs: if: matrix.format != 'rpm' run: | 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 run: | diff --git a/tests/test-artifact-appimage.sh b/tests/test-artifact-appimage.sh old mode 100644 new mode 100755 index 9e8a907..9166780 --- a/tests/test-artifact-appimage.sh +++ b/tests/test-artifact-appimage.sh @@ -96,6 +96,91 @@ assert_contains "$appdir/AppRun" 'build_electron_args' \ resources_dir="$appdir/usr/lib/node_modules/electron/dist/resources" 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 --- rm -rf "$extract_dir"