Files
linuxcncAllWasm/tests/ui-smoke/_lib/qtdragon-prepare.sh
2026-08-20 06:17:39 +08:00

80 lines
3.4 KiB
Bash

#!/bin/bash
# Sourced by the qtdragon ui-smoke tests (smoke and quit) to build a
# config qtvcp can actually run under CI. Sets QTDRAGON_INI to the
# patched ini path and exports the headless env; the caller then execs
# run-gui.sh or quit-launch.sh with "$QTDRAGON_INI". Must be sourced
# with LIB_DIR already set.
#
# qtdragon's qtvcp logger writes its log file (path from INI [DISPLAY]
# LOG_FILE) into the config directory. CI mounts the workspace read-
# only for the runtime user, so a relative LOG_FILE like 'qtdragon.log'
# resolves to a path qtvcp cannot create, hal_bridge then exits, and
# linuxcnc tears down before our driver can do anything. Mirror the
# config dir to a writable tmp location and patch LOG_FILE to be
# rooted at $HOME so the log lands in a directory we can write to.
#
# Force the Qt offscreen platform plugin. qtvcp under xvfb + xcb on
# Ubuntu 24.04 segfaults during widget construction (no backtrace);
# Debian containers in the same CI matrix do not. Offscreen renders
# entirely in memory, no X server needed (xvfb-run still wraps the
# call so the rest of scripts/linuxcnc's X-display assumptions hold).
# scripts/linuxcnc itself forces QT_QPA_PLATFORM=xcb unless
# LINUXCNC_OPENGL_PLATFORM is set to something other than glx, so we
# pin both env vars.
#
# qtdragon embeds a QWebEngineView (Chromium). Under offscreen + xvfb
# with no GPU and no user namespaces in the CI runner sandbox,
# QtWebEngine browser-process init segfaults even with --no-sandbox
# --single-process --disable-gpu (Chromium logs "Sandboxing disabled
# by user." then crashes inside the same qtvcp PID). Rather than keep
# tuning Chromium flags for a widget the smoke test never touches,
# we shim qtpy.QtWebEngineWidgets to raise ImportError; web_widget.py
# already has a fallback path that swaps the QWebEngineView for a
# plain QWidget when the import fails (its "fail safe - mostly for
# designer" branch). No Chromium spawn = no crash.
: "${LIB_DIR:?qtdragon-prepare.sh must be sourced with LIB_DIR set}"
SRC_DIR="$(cd "$LIB_DIR/../../../configs/sim/qtdragon/qtdragon_xyz" && pwd)"
WORK_DIR="$(mktemp -d -t ui-smoke-qtdragon.XXXXXX)"
trap 'rm -rf "$WORK_DIR"' EXIT
cp -r "$SRC_DIR/." "$WORK_DIR/"
sed -i 's|^LOG_FILE = qtdragon\.log$|LOG_FILE = ~/qtdragon.log|' \
"$WORK_DIR/qtdragon_metric.ini"
export LINUXCNC_OPENGL_PLATFORM=offscreen
export QT_QPA_PLATFORM=offscreen
# sitecustomize.py is auto-imported by Python from any sys.path entry
# at interpreter startup. Drop a meta_path finder that blocks the
# qtpy.QtWebEngineWidgets import so WebWidget falls back to QWidget.
SHIM_DIR="$WORK_DIR/_pyshim"
mkdir -p "$SHIM_DIR"
cat >"$SHIM_DIR/sitecustomize.py" <<'PY'
import sys
from importlib.abc import MetaPathFinder, Loader
from importlib.util import spec_from_loader
_BLOCK = {'qtpy.QtWebEngineWidgets', 'PyQt5.QtWebEngineWidgets'}
class _BlockLoader(Loader):
def create_module(self, spec):
raise ImportError('QtWebEngineWidgets blocked for ui-smoke CI')
def exec_module(self, module):
pass
class _BlockFinder(MetaPathFinder):
def find_spec(self, name, path, target=None):
if name in _BLOCK:
return spec_from_loader(name, _BlockLoader())
return None
sys.meta_path.insert(0, _BlockFinder())
PY
export PYTHONPATH="$SHIM_DIR${PYTHONPATH:+:$PYTHONPATH}"
# Consumed by the sourcing test.sh, which execs the launcher with it.
# shellcheck disable=SC2034
QTDRAGON_INI="$WORK_DIR/qtdragon_metric.ini"