chore: import upstream snapshot with attribution
CI: cua-driver distro-compat matrix / debian:12 (glibc 2.36) (push) Has been cancelled
CI: SPDX Headers / Check SPDX headers (warn-only) (push) Has been cancelled
CD: Docs MCP Server / build (linux/amd64) (push) Has been cancelled
CD: Docs MCP Server / build (linux/arm64) (push) Has been cancelled
CD: Docs MCP Server / merge (push) Has been cancelled
CI: cua-driver distro-compat matrix / Resolve release version (push) Has been cancelled
CI: cua-driver distro-compat matrix / fedora:41 (glibc 2.40) (push) Has been cancelled
CI: cua-driver distro-compat matrix / rockylinux:9 (glibc 2.34) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:22.04 (glibc 2.35) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:24.04 (glibc 2.39) (push) Has been cancelled
CI: cua-driver distro-compat matrix / Distro compat summary (push) Has been cancelled
CI: Rust Linux unit / Rust Linux unit and compile (push) Has been cancelled
CI: Rust Windows unit / Rust Windows unit and compile (push) Has been cancelled
CI: Nix Linux Rust source / Nix / compositor build (push) Has been cancelled
CI: Nix Linux Rust source / Nix / driver package (push) Has been cancelled
CI: Nix Linux Rust source / Nix / Rust unit tests (push) Has been cancelled
CI: cua-driver distro-compat matrix / debian:12 (glibc 2.36) (push) Has been cancelled
CI: SPDX Headers / Check SPDX headers (warn-only) (push) Has been cancelled
CD: Docs MCP Server / build (linux/amd64) (push) Has been cancelled
CD: Docs MCP Server / build (linux/arm64) (push) Has been cancelled
CD: Docs MCP Server / merge (push) Has been cancelled
CI: cua-driver distro-compat matrix / Resolve release version (push) Has been cancelled
CI: cua-driver distro-compat matrix / fedora:41 (glibc 2.40) (push) Has been cancelled
CI: cua-driver distro-compat matrix / rockylinux:9 (glibc 2.34) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:22.04 (glibc 2.35) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:24.04 (glibc 2.39) (push) Has been cancelled
CI: cua-driver distro-compat matrix / Distro compat summary (push) Has been cancelled
CI: Rust Linux unit / Rust Linux unit and compile (push) Has been cancelled
CI: Rust Windows unit / Rust Windows unit and compile (push) Has been cancelled
CI: Nix Linux Rust source / Nix / compositor build (push) Has been cancelled
CI: Nix Linux Rust source / Nix / driver package (push) Has been cancelled
CI: Nix Linux Rust source / Nix / Rust unit tests (push) Has been cancelled
This commit is contained in:
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
# Validate the canonical Rust matrix in a representative maintainer desktop.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ENVIRONMENT="${1:-}"
|
||||
if [[ $# -gt 0 ]]; then
|
||||
shift
|
||||
fi
|
||||
|
||||
case "${ENVIRONMENT}" in
|
||||
gnome)
|
||||
[[ "${XDG_SESSION_TYPE:-}" == wayland ]] || {
|
||||
echo "GNOME validation requires an active Wayland user session" >&2
|
||||
exit 2
|
||||
}
|
||||
[[ "${XDG_CURRENT_DESKTOP:-}" == *GNOME* ]] || {
|
||||
echo "XDG_CURRENT_DESKTOP does not identify GNOME: ${XDG_CURRENT_DESKTOP:-<unset>}" >&2
|
||||
exit 2
|
||||
}
|
||||
gdbus call --session \
|
||||
--dest org.cua.WinRects \
|
||||
--object-path /org/cua/WinRects \
|
||||
--method org.cua.WinRects.GetRects >/dev/null || {
|
||||
echo "The GNOME WinRects helper is not available in this user session" >&2
|
||||
exit 2
|
||||
}
|
||||
export CUA_E2E_COMPOSITOR=gnome-mutter
|
||||
export CUA_E2E_INPUT_BACKENDS=atspi,libei-portal
|
||||
export CUA_DRIVER_RS_ENABLE_WAYLAND=1
|
||||
;;
|
||||
kde)
|
||||
[[ "${XDG_SESSION_TYPE:-}" == wayland ]] || {
|
||||
echo "KDE validation requires an active Wayland user session" >&2
|
||||
exit 2
|
||||
}
|
||||
[[ "${XDG_CURRENT_DESKTOP:-}" == *KDE* ]] || {
|
||||
echo "XDG_CURRENT_DESKTOP does not identify KDE: ${XDG_CURRENT_DESKTOP:-<unset>}" >&2
|
||||
exit 2
|
||||
}
|
||||
kwin_version="$(kwin_wayland --version 2>/dev/null | sed -n 's/^kwin \([0-9][0-9]*\).*/\1/p')"
|
||||
[[ "${kwin_version:-0}" -ge 6 ]] || {
|
||||
echo "Representative KDE validation requires Plasma/KWin 6; found ${kwin_version:-unknown}" >&2
|
||||
exit 2
|
||||
}
|
||||
export CUA_E2E_COMPOSITOR=kwin
|
||||
export CUA_E2E_INPUT_BACKENDS=atspi,libei-portal
|
||||
export CUA_DRIVER_RS_ENABLE_WAYLAND=1
|
||||
;;
|
||||
xorg)
|
||||
[[ -n "${DISPLAY:-}" && "${XDG_SESSION_TYPE:-x11}" != wayland ]] || {
|
||||
echo "Real-Xorg validation requires DISPLAY in a non-Wayland session" >&2
|
||||
exit 2
|
||||
}
|
||||
export CUA_E2E_COMPOSITOR=real-xorg
|
||||
export CUA_E2E_INPUT_BACKENDS=atspi,xsend-event,xtest,mpx-uinput
|
||||
;;
|
||||
*)
|
||||
echo "Usage: run-rust-e2e-desktop.sh {gnome|kde|xorg} [--no-build]" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "${CUA_E2E_HARNESS_FILTER:-}" == *tauri* && ! -e /dev/dri/renderD128 ]]; then
|
||||
echo "Tauri/WebKitGTK validation requires a representative DRM render node" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
exec "${SCRIPT_DIR}/run-rust-e2e.sh" "$@"
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run the canonical Rust matrix inside the nested cua-compositor environment.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
export CUA_E2E_WAYLAND_SESSION=cua-compositor
|
||||
exec "${SCRIPT_DIR}/run-rust-e2e-wayland.sh" "$@"
|
||||
Executable
+219
@@ -0,0 +1,219 @@
|
||||
#!/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:-<not found>}" >&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 '<true>' >/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}"
|
||||
Executable
+231
@@ -0,0 +1,231 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run the canonical Rust desktop matrix on a Linux user session.
|
||||
# Scenario definitions and assertions live in the Rust integration test.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
|
||||
DRIVER_ROOT="${REPO_ROOT}/libs/cua-driver"
|
||||
RUST_ROOT="${DRIVER_ROOT}/rust"
|
||||
BUILD_FIXTURES=1
|
||||
SUITE="${CUA_E2E_INTERNAL_LANE:-all}"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: run-rust-e2e.sh [--no-build]
|
||||
|
||||
The caller must provide a real or virtual Linux desktop session. For a
|
||||
headless session, wrap this command in xvfb-run and dbus-run-session.
|
||||
The contributor-facing command always runs the complete matrix.
|
||||
EOF
|
||||
}
|
||||
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
--no-build) BUILD_FIXTURES=0 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "unknown argument: $1" >&2; usage >&2; exit 2 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
case "$SUITE" in
|
||||
shared|native|capture|all) ;;
|
||||
*) echo "unsupported internal lane: $SUITE" >&2; exit 2 ;;
|
||||
esac
|
||||
|
||||
ARTIFACT_DIR="${REPO_ROOT}/artifacts/cua-driver/linux"
|
||||
mkdir -p "${ARTIFACT_DIR}"
|
||||
RECORDING_ROOT="${ARTIFACT_DIR}/recordings"
|
||||
rm -rf "${RECORDING_ROOT}"
|
||||
mkdir -p "${RECORDING_ROOT}"
|
||||
DECLARATIONS_FILE="${ARTIFACT_DIR}/cases.jsonl"
|
||||
ENVIRONMENT_FILE="${ARTIFACT_DIR}/environment.jsonl"
|
||||
RESULTS_FILE="${ARTIFACT_DIR}/results.jsonl"
|
||||
SUMMARY_FILE="${ARTIFACT_DIR}/summary.md"
|
||||
: > "${DECLARATIONS_FILE}"
|
||||
: > "${ENVIRONMENT_FILE}"
|
||||
: > "${RESULTS_FILE}"
|
||||
rm -f "${SUMMARY_FILE}"
|
||||
export CUA_E2E_DECLARATIONS_FILE="${DECLARATIONS_FILE}"
|
||||
export CUA_E2E_ENVIRONMENT_FILE="${ENVIRONMENT_FILE}"
|
||||
export CUA_E2E_RESULTS_FILE="${RESULTS_FILE}"
|
||||
export CUA_E2E_RECORDINGS_ROOT="${RECORDING_ROOT}"
|
||||
export CUA_TEST_WORKSPACE_ROOT="${RUST_ROOT}"
|
||||
export CUA_TEST_DRIVER_BIN="${RUST_ROOT}/target/release/cua-driver"
|
||||
export CUA_TEST_APPS_ROOT="${RUST_ROOT}/test-apps"
|
||||
export CUA_TEST_REQUIRE_FIXTURES=1
|
||||
export CUA_TEST_DRIVER_STDERR=1
|
||||
export RUST_BACKTRACE="${RUST_BACKTRACE:-1}"
|
||||
if [[ -z "${CUA_E2E_SOURCE_SHA:-}" && -f "${REPO_ROOT}/.cua-e2e-source-sha" ]]; then
|
||||
export CUA_E2E_SOURCE_SHA="$(tr -d '[:space:]' < "${REPO_ROOT}/.cua-e2e-source-sha")"
|
||||
export CUA_E2E_SOURCE_MARKER="${REPO_ROOT}/.cua-e2e-source-sha"
|
||||
fi
|
||||
if [[ -n "${WAYLAND_DISPLAY:-}" && -z "${DISPLAY:-}" ]]; then
|
||||
export GDK_BACKEND="${GDK_BACKEND:-wayland}"
|
||||
export CUA_E2E_COMPOSITOR="${CUA_E2E_COMPOSITOR:-wayland-unknown}"
|
||||
export CUA_E2E_INPUT_BACKENDS="${CUA_E2E_INPUT_BACKENDS:-atspi}"
|
||||
else
|
||||
export CUA_E2E_COMPOSITOR="${CUA_E2E_COMPOSITOR:-openbox-x11}"
|
||||
export CUA_E2E_INPUT_BACKENDS="${CUA_E2E_INPUT_BACKENDS:-atspi,xsend-event,xtest}"
|
||||
fi
|
||||
if [[ "${SUITE}" == shared || "${SUITE}" == all ]]; then
|
||||
export CUA_ATSPI_DEBUG=1
|
||||
fi
|
||||
|
||||
if [[ -n "${WAYLAND_DISPLAY:-}" && -z "${DISPLAY:-}" ]]; then
|
||||
command -v wf-recorder >/dev/null || { echo "wf-recorder is required for native Wayland E2E videos" >&2; exit 1; }
|
||||
command -v grim >/dev/null || { echo "grim is required for native Wayland capture fallback" >&2; exit 1; }
|
||||
command -v wtype >/dev/null || { echo "wtype is required for native Wayland keyboard input" >&2; exit 1; }
|
||||
else
|
||||
command -v ffmpeg >/dev/null || { echo "ffmpeg is required for X11 E2E trajectory videos" >&2; exit 1; }
|
||||
fi
|
||||
command -v ffprobe >/dev/null || { echo "ffprobe is required for E2E trajectory validation" >&2; exit 1; }
|
||||
command -v jq >/dev/null || { echo "jq is required for E2E ownership validation" >&2; exit 1; }
|
||||
|
||||
if [[ "${BUILD_FIXTURES}" == 1 ]]; then
|
||||
cargo build --release -p cua-driver --manifest-path "${RUST_ROOT}/Cargo.toml"
|
||||
case "${SUITE}" in
|
||||
shared) FIXTURE_TARGETS="${CUA_E2E_HARNESS_FILTER:-electron,tauri}" ;;
|
||||
native|capture) FIXTURE_TARGETS="electron,gtk3" ;;
|
||||
*) FIXTURE_TARGETS="${CUA_E2E_HARNESS_FILTER:-electron,tauri},gtk3" ;;
|
||||
esac
|
||||
bash "${DRIVER_ROOT}/tests/fixtures/build/linux.sh" --only "${FIXTURE_TARGETS}"
|
||||
fi
|
||||
|
||||
if [[ ! -x "${CUA_TEST_DRIVER_BIN}" ]]; then
|
||||
echo "driver binary not found: ${CUA_TEST_DRIVER_BIN}" >&2
|
||||
exit 1
|
||||
fi
|
||||
required_fixtures=()
|
||||
required_fixtures+=("${CUA_TEST_APPS_ROOT}/harness-electron/CuaTestHarness.Electron")
|
||||
if [[ ("${SUITE}" == shared || "${SUITE}" == all) \
|
||||
&& ",${CUA_E2E_HARNESS_FILTER:-electron,tauri}," == *,tauri,* ]]; then
|
||||
required_fixtures+=(
|
||||
"${CUA_TEST_APPS_ROOT}/harness-tauri/CuaTestHarness.Tauri"
|
||||
)
|
||||
fi
|
||||
if [[ "${SUITE}" == native || "${SUITE}" == all ]]; then
|
||||
required_fixtures+=("${CUA_TEST_APPS_ROOT}/harness-gtk3/CuaTestHarness.Gtk3")
|
||||
fi
|
||||
for fixture in "${required_fixtures[@]}"; do
|
||||
if [[ ! -x "${fixture}" ]]; then
|
||||
echo "Required fixture was not built: ${fixture}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
FAILURE_COUNT=0
|
||||
|
||||
run_report() {
|
||||
(cd "${RUST_ROOT}" && cargo run -p cua-driver-testkit --bin cua-e2e-report -- \
|
||||
--declarations "${DECLARATIONS_FILE}" \
|
||||
--environment "${ENVIRONMENT_FILE}" \
|
||||
--results "${RESULTS_FILE}" \
|
||||
--artifact-root "${ARTIFACT_DIR}" \
|
||||
--require-video \
|
||||
--output "${SUMMARY_FILE}")
|
||||
}
|
||||
|
||||
echo "[PREFLIGHT] Linux desktop, fixture, AX, capture, and video"
|
||||
set +e
|
||||
(cd "${RUST_ROOT}" && cargo test -p cua-driver --test e2e_environment_preflight_test -- \
|
||||
--ignored --exact canonical_e2e_environment_is_ready --nocapture --test-threads=1) \
|
||||
2>&1 | tee "${ARTIFACT_DIR}/environment-preflight.log"
|
||||
PREFLIGHT_EXIT=${PIPESTATUS[0]}
|
||||
set -e
|
||||
if [[ "${PREFLIGHT_EXIT}" != 0 ]]; then
|
||||
set +e
|
||||
run_report
|
||||
set -e
|
||||
echo "Linux E2E environment preflight failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run_test() {
|
||||
local name="$1"
|
||||
shift
|
||||
echo "[RUN] ${name}"
|
||||
set +e
|
||||
(cd "${RUST_ROOT}" && "$@") 2>&1 | tee "${ARTIFACT_DIR}/${name}.log"
|
||||
local exit_code=${PIPESTATUS[0]}
|
||||
set -e
|
||||
if [[ "${exit_code}" != 0 ]]; then
|
||||
FAILURE_COUNT=$((FAILURE_COUNT + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ "${SUITE}" == shared || "${SUITE}" == all ]]; then
|
||||
run_test shared-behavior-matrix \
|
||||
cargo test -p cua-driver --test cross_platform_behavior_test -- \
|
||||
--ignored --exact shared_web_action_matrix_is_state_verified \
|
||||
--nocapture --test-threads=1
|
||||
fi
|
||||
|
||||
if [[ "${SUITE}" == native || "${SUITE}" == all ]]; then
|
||||
run_test gtk3-native-harness \
|
||||
cargo test -p cua-driver --test harness_gtk3_test -- \
|
||||
--ignored --nocapture --test-threads=1
|
||||
fi
|
||||
|
||||
if [[ "${SUITE}" == capture || "${SUITE}" == all ]]; then
|
||||
run_test capture-contract \
|
||||
cargo test -p cua-driver --test capture_contract_test -- \
|
||||
--ignored --nocapture --test-threads=1
|
||||
run_test desktop-scope \
|
||||
cargo test -p cua-driver --test desktop_scope_linux_test -- \
|
||||
--ignored --nocapture --test-threads=1
|
||||
fi
|
||||
|
||||
video_count=0
|
||||
while IFS= read -r -d '' video; do
|
||||
video_count=$((video_count + 1))
|
||||
if ! ffprobe -v error -show_entries format=duration \
|
||||
-of default=noprint_wrappers=1:nokey=1 "${video}" >/dev/null; then
|
||||
echo "[VIDEO FAIL] Unplayable trajectory: ${video}" >&2
|
||||
FAILURE_COUNT=$((FAILURE_COUNT + 1))
|
||||
else
|
||||
echo "[VIDEO PASS] ${video}"
|
||||
fi
|
||||
done < <(find "${RECORDING_ROOT}" -type f -name recording.mp4 -print0)
|
||||
|
||||
OWNED_VIDEOS="$(mktemp)"
|
||||
jq -r 'select(.evidence.video != null) | .evidence.video' "${RESULTS_FILE}" > "${OWNED_VIDEOS}"
|
||||
while IFS= read -r -d '' video; do
|
||||
relative="${video#${ARTIFACT_DIR}/}"
|
||||
if [[ "${relative}" == recordings/environment-preflight-*/recording.mp4 ]]; then
|
||||
continue
|
||||
fi
|
||||
if ! grep -Fxq -- "${relative}" "${OWNED_VIDEOS}"; then
|
||||
echo "[VIDEO FAIL] Orphan trajectory has no typed result row: ${relative}" >&2
|
||||
FAILURE_COUNT=$((FAILURE_COUNT + 1))
|
||||
fi
|
||||
done < <(find "${RECORDING_ROOT}" -type f -name recording.mp4 -print0)
|
||||
rm -f "${OWNED_VIDEOS}"
|
||||
|
||||
while IFS= read -r -d '' recording_error; do
|
||||
echo "[VIDEO FAIL] ${recording_error}" >&2
|
||||
cat "${recording_error}" >&2
|
||||
FAILURE_COUNT=$((FAILURE_COUNT + 1))
|
||||
done < <(find "${RECORDING_ROOT}" -type f -name recording-error.txt -print0)
|
||||
|
||||
if [[ "${video_count}" == 0 ]]; then
|
||||
echo "[VIDEO FAIL] No E2E trajectory videos were produced" >&2
|
||||
FAILURE_COUNT=$((FAILURE_COUNT + 1))
|
||||
fi
|
||||
|
||||
set +e
|
||||
run_report
|
||||
REPORT_EXIT=$?
|
||||
set -e
|
||||
if [[ "${REPORT_EXIT}" != 0 ]]; then
|
||||
echo "Linux E2E result validation failed" >&2
|
||||
FAILURE_COUNT=$((FAILURE_COUNT + 1))
|
||||
fi
|
||||
|
||||
if [[ "${FAILURE_COUNT}" != 0 ]]; then
|
||||
echo "Linux Rust e2e suite had ${FAILURE_COUNT} failing lane(s)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Linux Rust e2e suite completed: ${SUITE}"
|
||||
Reference in New Issue
Block a user