|
| 1 | +#!/bin/bash |
| 2 | +# Shared launcher for UI smoke tests. |
| 3 | +# Usage: launch.sh <sim-config-ini> |
| 4 | +# |
| 5 | +# Spawns linuxcnc -r <ini> under xvfb-run, then runs the common driver |
| 6 | +# script against it via NML. Captures stdout/stderr to per-test files. |
| 7 | +# |
| 8 | +# Skip vs fail (BsAtHome / hdiethelm review, PR #3999): xvfb-run absence |
| 9 | +# is handled by the per-test skip files (skip-if-missing.sh), which |
| 10 | +# runtests gates on before invoking test.sh. CI is expected to have all |
| 11 | +# required deps; if any python module the GUI needs is missing the test |
| 12 | +# should fail loudly rather than silently skip. Per-GUI deps are |
| 13 | +# declared in debian/control under !nocheck, so apt-get build-dep |
| 14 | +# installs them on CI. |
| 15 | + |
| 16 | +set -u |
| 17 | + |
| 18 | +CONFIG_INI="$1" |
| 19 | +TEST_DIR="${TEST_DIR:-$(cd "$(dirname "$0")" && pwd)}" |
| 20 | +LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 21 | + |
| 22 | +cd "$TEST_DIR" || exit 1 |
| 23 | +rm -f ui-smoke.out ui-smoke.err linuxcnc.pid |
| 24 | + |
| 25 | +# Pre-launch cleanup: a previous ui-smoke test in the same job may |
| 26 | +# have left a daemon listening on the NML TCP port or HAL shared |
| 27 | +# memory still attached. Run the shared cleanup once before we start. |
| 28 | +bash "$LIB_DIR/cleanup-runtime.sh" |
| 29 | + |
| 30 | +# Launch linuxcnc inside xvfb-run. The outer timeout is a safety net |
| 31 | +# so a wedged GUI cannot hang CI. |
| 32 | +LINUXCNC_TIMEOUT=240 |
| 33 | +DRIVER_TIMEOUT=90 |
| 34 | + |
| 35 | +# Force software OpenGL (Mesa llvmpipe). CI runners have no GPU and |
| 36 | +# Qt/GL widgets segfault under hardware GL with no display. The Qt- |
| 37 | +# specific knobs cover qtdragon's QtQuick + RHI paths. |
| 38 | +export LIBGL_ALWAYS_SOFTWARE=1 |
| 39 | +export GALLIUM_DRIVER=llvmpipe |
| 40 | +export QT_QUICK_BACKEND=software |
| 41 | +export QSG_RHI_BACKEND=software |
| 42 | +export QT_OPENGL=software |
| 43 | + |
| 44 | +# Export the per-invocation values so the inner bash -c receives them |
| 45 | +# as proper env vars (avoids embedding paths into the inner script |
| 46 | +# via quoting, which breaks on apostrophes / spaces). |
| 47 | +export CONFIG_INI LIB_DIR DRIVER_TIMEOUT |
| 48 | + |
| 49 | +# Single quotes around the inner script are intentional: CONFIG_INI, |
| 50 | +# LIB_DIR and DRIVER_TIMEOUT are expanded by the inner bash (which sees |
| 51 | +# them via the exported env), not by the outer shell. |
| 52 | +# shellcheck disable=SC2016 |
| 53 | +xvfb-run -a --server-args="-screen 0 1024x768x24" \ |
| 54 | + timeout "$LINUXCNC_TIMEOUT" \ |
| 55 | + bash -c ' |
| 56 | + # Run linuxcnc in its own process group so we can signal the |
| 57 | + # whole group cleanly (linuxcnc forks task, motion, GUI, halrun). |
| 58 | + # setsid makes the child a session leader, so its PID equals |
| 59 | + # the PGID and we can group-signal via "kill -- -$PID". |
| 60 | + setsid linuxcnc -r "$CONFIG_INI" >linuxcnc.out 2>linuxcnc.err & |
| 61 | + LINUXCNC_PID=$! |
| 62 | + echo "$LINUXCNC_PID" >linuxcnc.pid |
| 63 | +
|
| 64 | + # The driver polls NML readiness itself (BsAtHome review: |
| 65 | + # avoid real-clock waits where status polling will do). |
| 66 | + timeout "$DRIVER_TIMEOUT" python3 "$LIB_DIR/drive.py" >ui-smoke.out 2>ui-smoke.err |
| 67 | + DRIVE_RC=$? |
| 68 | +
|
| 69 | + # Clean shutdown: GUI-specific quit first (lets linuxcnc end |
| 70 | + # its own SIGTERM trap run Cleanup which unloads halrun and |
| 71 | + # reaps shared memory). axis-remote works only for axis but is |
| 72 | + # harmless otherwise. Then group-SIGTERM so the trap runs |
| 73 | + # in-process. Wait up to 60s for Cleanup to finish before |
| 74 | + # falling back to SIGKILL + cleanup-runtime.sh. |
| 75 | + if command -v axis-remote >/dev/null 2>&1; then |
| 76 | + axis-remote --quit 2>/dev/null || true |
| 77 | + fi |
| 78 | +
|
| 79 | + kill -TERM -- -"$LINUXCNC_PID" 2>/dev/null || true |
| 80 | + for _ in $(seq 60); do |
| 81 | + kill -0 "$LINUXCNC_PID" 2>/dev/null || break |
| 82 | + sleep 1 |
| 83 | + done |
| 84 | + if kill -0 "$LINUXCNC_PID" 2>/dev/null; then |
| 85 | + echo "WARN: linuxcnc did not exit on SIGTERM, escalating to KILL" >&2 |
| 86 | + kill -KILL -- -"$LINUXCNC_PID" 2>/dev/null || true |
| 87 | + sleep 2 |
| 88 | + bash "$LIB_DIR/cleanup-runtime.sh" |
| 89 | + fi |
| 90 | +
|
| 91 | + exit "$DRIVE_RC" |
| 92 | + ' |
| 93 | +RC=$? |
| 94 | + |
| 95 | +# Surface logs so checkresult and CI artifact upload can see them. |
| 96 | +echo "=== linuxcnc.out ===" |
| 97 | +[ -f linuxcnc.out ] && cat linuxcnc.out |
| 98 | +echo "=== linuxcnc.err ===" |
| 99 | +[ -f linuxcnc.err ] && cat linuxcnc.err |
| 100 | +echo "=== ui-smoke.out ===" |
| 101 | +[ -f ui-smoke.out ] && cat ui-smoke.out |
| 102 | +echo "=== ui-smoke.err ===" |
| 103 | +[ -f ui-smoke.err ] && cat ui-smoke.err |
| 104 | + |
| 105 | +exit "$RC" |
0 commit comments