#!/usr/bin/env bash # Start one native, headless Sway session and run the complete Rust E2E matrix. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" RUNTIME_DIR="$(mktemp -d)" SWAY_CONFIG="$(mktemp)" SESSION_KIND="${CUA_E2E_WAYLAND_SESSION:-sway}" COMPOSITOR_LOG="${REPO_ROOT}/artifacts/cua-driver/linux/${SESSION_KIND}.log" ATSPI_LOG="${REPO_ROOT}/artifacts/cua-driver/linux/at-spi-bus.log" COMPOSITOR_PID="" DBUS_PID="" ATSPI_PID="" cleanup() { if [[ -n "${COMPOSITOR_PID}" ]]; then kill "${COMPOSITOR_PID}" 2>/dev/null || true wait "${COMPOSITOR_PID}" 2>/dev/null || true fi if [[ -n "${DBUS_PID}" ]]; then kill "${DBUS_PID}" 2>/dev/null || true fi if [[ -n "${ATSPI_PID}" ]]; then kill "${ATSPI_PID}" 2>/dev/null || true wait "${ATSPI_PID}" 2>/dev/null || true fi rm -f "${SWAY_CONFIG}" } trap cleanup EXIT mkdir -p "$(dirname "${COMPOSITOR_LOG}")" chmod 700 "${RUNTIME_DIR}" cat > "${SWAY_CONFIG}" <<'EOF' xwayland disable output HEADLESS-1 mode 1920x1080 seat seat0 fallback true focus_follows_mouse no default_border none default_floating_border none for_window [title="^CuaTestHarness"] floating enable, resize set 940 780, move position 0 0 for_window [title="CuaTestHarness Sentinel"] fullscreen enable EOF unset DISPLAY unset WAYLAND_DISPLAY export XDG_RUNTIME_DIR="${RUNTIME_DIR}" export XDG_SESSION_TYPE=wayland export XDG_CURRENT_DESKTOP=sway export XDG_SESSION_DESKTOP=sway export WLR_BACKENDS=headless export WLR_RENDERER=pixman export WLR_RENDERER_ALLOW_SOFTWARE=1 export WLR_LIBINPUT_NO_DEVICES=1 export WLR_HEADLESS_OUTPUTS=1 export CUA_DRIVER_RS_ENABLE_WAYLAND=1 if [[ "${SESSION_KIND}" == cua-compositor ]]; then export CUA_E2E_COMPOSITOR=cua-compositor-nested export CUA_E2E_INPUT_BACKENDS=atspi,cua-compositor-inject export CUA_E2E_HARNESS_FILTER=electron export CUA_INJECT_SOCKET="${XDG_RUNTIME_DIR}/cua-inject.sock" else export CUA_E2E_COMPOSITOR=sway export CUA_E2E_INPUT_BACKENDS=atspi,wlr-virtual-pointer fi export CUA_WAYLAND_RECORDING_OUTPUT=HEADLESS-1 export ELECTRON_OZONE_PLATFORM_HINT=wayland export GDK_BACKEND=wayland export QT_QPA_PLATFORM=wayland # WebKitGTK's accelerated compositor requires a DRM render node. The canonical # hosted lane has none, so keep its best-effort software settings explicit; # native-Wayland WebKit coverage runs on the representative GNOME/KDE VMs. export WEBKIT_DISABLE_COMPOSITING_MODE=1 export WEBKIT_DISABLE_DMABUF_RENDERER=1 # This isolated CI session uses a private runtime directory and no user home # namespaces. Modern WebKitGTK ignores WEBKIT_FORCE_SANDBOX; use its explicit # test-only escape hatch so the WebProcess can publish its AT-SPI subtree. export WEBKIT_DISABLE_SANDBOX_THIS_IS_DANGEROUS=1 export NO_AT_BRIDGE=0 export ACCESSIBILITY_ENABLED=1 if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then dbus_daemon="$(command -v dbus-daemon)" dbus_prefix="$(dirname "$(dirname "$(readlink -f "${dbus_daemon}")")")" dbus_config="${dbus_prefix}/share/dbus-1/session.conf" if [[ ! -f "${dbus_config}" ]]; then echo "DBus session config is missing: ${dbus_config}" >&2 exit 1 fi dbus_info="$(dbus-daemon --config-file="${dbus_config}" --fork --print-address=1 --print-pid=1)" export DBUS_SESSION_BUS_ADDRESS="$(sed -n '1p' <<< "${dbus_info}")" DBUS_PID="$(sed -n '2p' <<< "${dbus_info}")" fi # A private session bus does not activate the desktop accessibility stack by # itself. Start the repo-pinned AT-SPI launcher, then require org.a11y.Bus to # answer before any fixture or driver process inherits this session. ATSPI_LAUNCHER="${CUA_AT_SPI_BUS_LAUNCHER:-$(command -v at-spi-bus-launcher || true)}" if [[ ! -x "${ATSPI_LAUNCHER}" ]]; then echo "AT-SPI bus launcher is unavailable: ${ATSPI_LAUNCHER:-}" >&2 exit 1 fi "${ATSPI_LAUNCHER}" --launch-immediately > "${ATSPI_LOG}" 2>&1 & ATSPI_PID=$! deadline=$((SECONDS + 15)) while ((SECONDS < deadline)); do if ! kill -0 "${ATSPI_PID}" 2>/dev/null; then echo "AT-SPI bus launcher exited before org.a11y.Bus became ready" >&2 cat "${ATSPI_LOG}" >&2 exit 1 fi if gdbus call --session \ --dest org.a11y.Bus \ --object-path /org/a11y/bus \ --method org.a11y.Bus.GetAddress >/dev/null 2>&1; then break fi sleep 0.2 done if ! gdbus call --session \ --dest org.a11y.Bus \ --object-path /org/a11y/bus \ --method org.a11y.Bus.GetAddress >/dev/null 2>&1; then echo "org.a11y.Bus did not become ready within 15 seconds" >&2 cat "${ATSPI_LOG}" >&2 exit 1 fi # The bus launcher and the registry daemon are separate services. Resolve the # private accessibility bus and require the registry to activate on it before # any toolkit inherits this session. a11y_address="$( gdbus call --session \ --dest org.a11y.Bus \ --object-path /org/a11y/bus \ --method org.a11y.Bus.GetAddress \ | sed -e "s/^('//" -e "s/',)$//" )" export AT_SPI_BUS_ADDRESS="${a11y_address}" if [[ -z "${a11y_address}" ]] || ! gdbus call \ --address "${a11y_address}" \ --dest org.a11y.atspi.Registry \ --object-path /org/a11y/atspi/accessible/root \ --method org.freedesktop.DBus.Properties.Get \ org.a11y.atspi.Accessible ChildCount >/dev/null 2>&1; then echo "AT-SPI registry did not activate on the accessibility bus" >&2 cat "${ATSPI_LOG}" >&2 exit 1 fi if ! gdbus call --session \ --dest org.a11y.Bus \ --object-path /org/a11y/bus \ --method org.freedesktop.DBus.Properties.Set \ org.a11y.Status IsEnabled '' >/dev/null 2>&1; then echo "Could not enable accessibility on org.a11y.Bus" >&2 cat "${ATSPI_LOG}" >&2 exit 1 fi if [[ "${SESSION_KIND}" == cua-compositor ]]; then command -v cua-compositor >/dev/null || { echo "cua-compositor is required for the nested injection lane" >&2 exit 1 } cua-compositor > "${COMPOSITOR_LOG}" 2>&1 & else sway --unsupported-gpu --config "${SWAY_CONFIG}" > "${COMPOSITOR_LOG}" 2>&1 & fi COMPOSITOR_PID=$! deadline=$((SECONDS + 20)) while ((SECONDS < deadline)); do if ! kill -0 "${COMPOSITOR_PID}" 2>/dev/null; then echo "${SESSION_KIND} exited before its Wayland socket became ready" >&2 cat "${COMPOSITOR_LOG}" >&2 exit 1 fi socket="$(find "${XDG_RUNTIME_DIR}" -maxdepth 1 -type s -name 'wayland-*' -print -quit)" if [[ -n "${socket}" ]]; then export WAYLAND_DISPLAY="$(basename "${socket}")" break fi sleep 0.2 done if [[ -z "${WAYLAND_DISPLAY:-}" ]]; then echo "${SESSION_KIND} did not create a Wayland socket within 20 seconds" >&2 cat "${COMPOSITOR_LOG}" >&2 exit 1 fi if [[ "${SESSION_KIND}" == cua-compositor ]]; then deadline=$((SECONDS + 10)) while [[ ! -S "${CUA_INJECT_SOCKET}" ]] && ((SECONDS < deadline)); do sleep 0.2 done if [[ ! -S "${CUA_INJECT_SOCKET}" ]]; then echo "cua-compositor did not expose its injection socket" >&2 cat "${COMPOSITOR_LOG}" >&2 exit 1 fi else export SWAYSOCK="$(find "${XDG_RUNTIME_DIR}" -maxdepth 1 -type s -name 'sway-ipc.*.sock' -print -quit)" if [[ -z "${SWAYSOCK}" ]]; then echo "Sway did not expose its IPC socket" >&2 cat "${COMPOSITOR_LOG}" >&2 exit 1 fi fi echo "Native Wayland E2E session: ${SESSION_KIND} on ${WAYLAND_DISPLAY}" set +e "${SCRIPT_DIR}/run-rust-e2e.sh" "$@" status=$? set -e if [[ "${status}" != 0 && "${SESSION_KIND}" == sway ]]; then swaymsg -t get_tree > "${REPO_ROOT}/artifacts/cua-driver/linux/sway-tree.json" 2>/dev/null || true fi exit "${status}"