diff --git a/Runner/config/pkg_command_map.conf b/Runner/config/pkg_command_map.conf index a7313dad..d373050a 100755 --- a/Runner/config/pkg_command_map.conf +++ b/Runner/config/pkg_command_map.conf @@ -65,7 +65,7 @@ apt:efivar=efivar # endpoint/profile integration validated as part of the Ubuntu BT stack. # --------------------------------------------------------------------------- -ubuntu:package-set:bluetooth=bluez pipewire wireplumber libspa-0.2-bluetooth alsa-utils +ubuntu:package-set:bluetooth=bluez pipewire wireplumber libspa-0.2-bluetooth alsa-utils rfkill expect # --------------------------------------------------------------------------- # Weston compositor and example-client mappings. @@ -292,8 +292,11 @@ debian:package-set:graphics=kgsl-dkms adreno-common libgbm-msm1 adreno-gles1 adr # Upstream Debian Mesa/freedreno userspace required by the default KMSCube run. debian:package-set:graphics-base=libegl-mesa0 libgl1-mesa-dri libgbm1 libegl1 libgles2 mesa-utils -# TODO on ubuntu -ubuntu:package-set:graphics=kgsl-dkms adreno-common adreno-gles1 adreno-gles2 adreno-egl1 adreno-vulkan-icd adreno-opencl-icd adreno-opencl-dev +# Ubuntu uses the same Mesa/freedreno base userspace as Debian. +ubuntu:package-set:graphics-base=libegl-mesa0 libgl1-mesa-dri libgbm1 libegl1 libgles2 mesa-utils + +# Qualcomm proprietary graphics overlay package set for Ubuntu. +ubuntu:package-set:graphics=kgsl-dkms adreno-common libgbm-msm adreno-gles1 adreno-gles2 adreno-egl1 adreno-vulkan-icd adreno-opencl-icd adreno-opencl-dev # --------------------------------------------------------------------------- # Audio validation package recovery. diff --git a/Runner/suites/Multimedia/Graphics/KMSCube/run.sh b/Runner/suites/Multimedia/Graphics/KMSCube/run.sh index a85c204f..2f35ba04 100755 --- a/Runner/suites/Multimedia/Graphics/KMSCube/run.sh +++ b/Runner/suites/Multimedia/Graphics/KMSCube/run.sh @@ -69,7 +69,7 @@ GPU_BOOT_ARTIFACTS_CHANGED=0 GPU_MODULE="msm_kgsl" GPU_OVERLAY_DEVICE="/dev/kgsl-3d0" -GPU_OVERLAY_GBM_PACKAGE="${GPU_OVERLAY_GBM_PACKAGE:-libgbm-msm1}" +GPU_OVERLAY_GBM_PACKAGE="${GPU_OVERLAY_GBM_PACKAGE:-}" DISPLAY_MANAGER_SERVICE="${DISPLAY_MANAGER_SERVICE:-display-manager.service}" DISPLAY_MANAGER_STATE_FILE="/tmp/qcom-testkit-${TESTNAME}-display-manager.$$.state" @@ -114,6 +114,17 @@ fi [ -n "$OS_ID" ] || OS_ID="unknown" +if [ -z "$GPU_OVERLAY_GBM_PACKAGE" ]; then + case "$OS_ID" in + ubuntu) + GPU_OVERLAY_GBM_PACKAGE="libgbm-msm" + ;; + *) + GPU_OVERLAY_GBM_PACKAGE="libgbm-msm1" + ;; + esac +fi + case "$OS_ID" in debian|ubuntu|centos|rhel|fedora) DISTRO_GPU_HANDLING_SUPPORTED=1 diff --git a/Runner/suites/Multimedia/Graphics/weston-simple-egl/README_weston-simple-egl.md b/Runner/suites/Multimedia/Graphics/weston-simple-egl/README_weston-simple-egl.md index 0e661c99..c4f5d6ba 100755 --- a/Runner/suites/Multimedia/Graphics/weston-simple-egl/README_weston-simple-egl.md +++ b/Runner/suites/Multimedia/Graphics/weston-simple-egl/README_weston-simple-egl.md @@ -1,9 +1,12 @@ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. -SPDX-License-Identifier: BSD-3-Clause# weston-simple-egl GraphicsTest Scripts for Qualcomm Linux based platform (Yocto) +SPDX-License-Identifier: BSD-3-Clause + +# weston-simple-egl Graphics Test + # Overview -Graphics scripts automates the validation of Graphics OpenGL ES 2.0 capabilities on the Qualcomm RB3 Gen2 platform running a Yocto-based Linux system. It utilizes Weston-Simple-EGL test app which is publicly available at https://github.com/krh/weston +This suite validates OpenGL ES 2.0 through the `weston-simple-egl` Wayland client on Qualcomm Linux platforms. It supports image-provided Weston on Yocto and active desktop Wayland sessions on Debian-family distributions. ## Features @@ -13,11 +16,27 @@ Graphics scripts automates the validation of Graphics OpenGL ES 2.0 capabilities ## Prerequisites -Ensure the following components are present in the target Yocto build: +Yocto images must provide the required client and Weston runtime. On Ubuntu and Debian, the shared package provider ensures the mapped Weston and graphics packages. - `weston-simple-egl` (Binary Available in /usr/bin) be default - Write access to root filesystem (for environment setup) +## Desktop distribution modes + +- `./run.sh --base` selects the upstream MSM/freedreno stack and ensures the OS-specific Mesa package set. +- `./run.sh --overlay` selects the Qualcomm KGSL/Adreno stack and ensures the OS-specific overlay package set. Package or DKMS changes can require a reboot before validation continues. +- Debian uses `libgbm-msm1`, while Ubuntu uses `libgbm-msm`. Set `GPU_OVERLAY_GBM_PACKAGE` only when an explicit override is required. +- On Ubuntu, the test reuses an active GNOME Wayland session when Weston is not running. A root-launched test executes the client as the Wayland socket owner without stopping or restarting GDM. +- GDM can throttle an unfocused greeter client. That path validates compositor connectivity and EGL startup while recording, but not performance-gating, any FPS samples. + +Examples: + +```sh +./run.sh --base +./run.sh --overlay +./run.sh --auto +``` + ## Directory Structure ``` diff --git a/Runner/suites/Multimedia/Graphics/weston-simple-egl/run.sh b/Runner/suites/Multimedia/Graphics/weston-simple-egl/run.sh index c3914c27..f53e4d4a 100755 --- a/Runner/suites/Multimedia/Graphics/weston-simple-egl/run.sh +++ b/Runner/suites/Multimedia/Graphics/weston-simple-egl/run.sh @@ -81,7 +81,7 @@ ALLOW_RELAUNCH="${ALLOW_RELAUNCH:-0}" GPU_MODULE="${GPU_MODULE:-msm_kgsl}" GPU_OVERLAY_DEVICE="${GPU_OVERLAY_DEVICE:-/dev/kgsl-3d0}" -GPU_OVERLAY_GBM_PACKAGE="${GPU_OVERLAY_GBM_PACKAGE:-libgbm-msm1}" +GPU_OVERLAY_GBM_PACKAGE="${GPU_OVERLAY_GBM_PACKAGE:-}" OS_ID="unknown" DISTRO_GPU_HANDLING_SUPPORTED=0 @@ -287,6 +287,17 @@ fi [ -n "$OS_ID" ] || OS_ID="unknown" +if [ -z "$GPU_OVERLAY_GBM_PACKAGE" ]; then + case "$OS_ID" in + ubuntu) + GPU_OVERLAY_GBM_PACKAGE="libgbm-msm" + ;; + *) + GPU_OVERLAY_GBM_PACKAGE="libgbm-msm1" + ;; + esac +fi + case "$OS_ID" in debian|ubuntu|centos|rhel|fedora) DISTRO_GPU_HANDLING_SUPPORTED=1 @@ -442,17 +453,19 @@ fi case "$OS_ID" in debian|ubuntu|centos|rhel|fedora) - if ! command -v weston_prepare_runtime >/dev/null 2>&1; then + if [ "$OS_ID" = "ubuntu" ] && + command -v display_adopt_gnome_wayland_session >/dev/null 2>&1 && + display_adopt_gnome_wayland_session; then + log_info "Using the active GNOME Wayland session for onscreen EGL validation" + elif ! command -v weston_prepare_runtime >/dev/null 2>&1; then log_fail "$TESTNAME FAIL - weston_prepare_runtime helper is unavailable" echo "$TESTNAME FAIL" >"$RES_FILE" exit 0 - fi - - if ! weston_prepare_runtime \ - "$TESTNAME" \ - "$WAIT_SECS" \ - client \ - "$ALLOW_RELAUNCH"; then + elif ! weston_prepare_runtime \ + "$TESTNAME" \ + "$WAIT_SECS" \ + client \ + "$ALLOW_RELAUNCH"; then log_fail "$TESTNAME FAIL - no usable managed Weston runtime is available for onscreen EGL clients" echo "$TESTNAME FAIL" >"$RES_FILE" exit 0 @@ -501,6 +514,15 @@ if ! display_resolve_test_fps_gate_policy \ exit 0 fi +if [ "${DISPLAY_RUNTIME_MODEL:-}" = "desktop-gnome-session" ]; then + # GDM intentionally throttles non-interactive greeter clients, so their + # compositor frame cadence is not a display-refresh or GPU-performance + # measurement. The client connection and EGL startup remain validated. + DISPLAY_TEST_FPS_POLICY="desktop-session-connectivity" + export DISPLAY_TEST_FPS_POLICY + log_info "FPS policy, desktop-session-connectivity, GNOME greeter throttling is not performance evidence" +fi + if [ "${DISPLAY_TEST_FPS_POLICY:-shared}" = "desktop-functional-cap" ]; then log_info "FPS policy, desktop-functional-cap" log_info "Detected output refresh, ${DISPLAY_TEST_FPS_REFRESH:-unknown}Hz" @@ -567,8 +589,9 @@ fi log_info "Using client binary, $BIN" log_info "Wayland socket, ${DISPLAY_WAYLAND_SOCKET:-}" -log_info "XDG_RUNTIME_DIR, ${XDG_RUNTIME_DIR:-}" -log_info "WAYLAND_DISPLAY, ${WAYLAND_DISPLAY:-}" +log_info "Wayland session user, ${DISPLAY_WAYLAND_SESSION_USER:-current-user}" +log_info "XDG_RUNTIME_DIR, ${DISPLAY_WAYLAND_SESSION_RUNTIME_DIR:-${XDG_RUNTIME_DIR:-}}" +log_info "WAYLAND_DISPLAY, $(basename "${DISPLAY_WAYLAND_SOCKET:-${WAYLAND_DISPLAY:-}}")" log_info "Client mode, compositor-synchronized weston-simple-egl" # Retain the existing environment on Yocto. Upstream weston-simple-egl prints @@ -592,9 +615,32 @@ rc=0 if command -v run_with_timeout >/dev/null 2>&1; then log_info "Using run_with_timeout" - if command -v stdbuf >/dev/null 2>&1; then + if [ -n "${DISPLAY_WAYLAND_SESSION_USER:-}" ] && + command -v timeout >/dev/null 2>&1; then + log_info "Using session-local timeout to reap the desktop Wayland client" + + if command -v stdbuf >/dev/null 2>&1; then + display_run_in_wayland_session \ + timeout \ + "$DURATION" \ + stdbuf \ + -oL \ + -eL \ + "$BIN" >>"$RUN_LOG" 2>&1 + rc=$? + else + log_warn "stdbuf is unavailable, running the client without line buffering" + + display_run_in_wayland_session \ + timeout \ + "$DURATION" \ + "$BIN" >>"$RUN_LOG" 2>&1 + rc=$? + fi + elif command -v stdbuf >/dev/null 2>&1; then run_with_timeout \ "$DURATION" \ + display_run_in_wayland_session \ stdbuf \ -oL \ -eL \ @@ -605,6 +651,7 @@ if command -v run_with_timeout >/dev/null 2>&1; then run_with_timeout \ "$DURATION" \ + display_run_in_wayland_session \ "$BIN" >>"$RUN_LOG" 2>&1 rc=$? fi @@ -624,7 +671,7 @@ else [ -n "$duration_secs" ] || duration_secs=30 [ -n "$stop_grace_secs" ] || stop_grace_secs=3 - "$BIN" >>"$RUN_LOG" 2>&1 & + display_run_in_wayland_session "$BIN" >>"$RUN_LOG" 2>&1 & APP_PID=$! run_elapsed=0 @@ -713,6 +760,8 @@ fi if [ "${DISPLAY_TEST_FPS_POLICY:-shared}" = "desktop-functional-cap" ]; then log_info "Result summary, rc=${rc} elapsed=${elapsed}s fps=${fps_for_summary} mode=desktop-functional refresh=${DISPLAY_TEST_FPS_REFRESH:-unknown}Hz target=${DISPLAY_TEST_FPS_EXPECTED:-unknown} min_ok=${DISPLAY_TEST_FPS_MIN_OK:-unknown} graphics=${DISPLAY_BUILD_FLAVOUR} source=client-synchronized" +elif [ "${DISPLAY_TEST_FPS_POLICY:-shared}" = "desktop-session-connectivity" ]; then + log_info "Result summary, rc=${rc} elapsed=${elapsed}s fps=${fps_for_summary} mode=desktop-session-connectivity graphics=${DISPLAY_BUILD_FLAVOUR} source=client-synchronized" elif [ "${DISPLAY_FPS_MODE:-}" = "detected" ]; then log_info "Result summary, rc=${rc} elapsed=${elapsed}s fps=${fps_for_summary} mode=${DISPLAY_FPS_MODE} refresh=${DISPLAY_FPS_DETECTED_HZ}Hz expected=${DISPLAY_FPS_EXPECTED} min_ok=${DISPLAY_FPS_MIN_OK} graphics=${DISPLAY_BUILD_FLAVOUR} source=client-synchronized" else @@ -724,6 +773,12 @@ final="PASS" case "$rc" in 0|143) ;; + 124) + if [ "${DISPLAY_RUNTIME_MODEL:-}" != "desktop-gnome-session" ]; then + log_fail "$TESTNAME execution timed out unexpectedly, rc=$rc runtime=${DISPLAY_RUNTIME_MODEL:-unknown}" + final="FAIL" + fi + ;; *) log_fail "$TESTNAME execution failed, rc=$rc" final="FAIL" diff --git a/Runner/suites/Multimedia/Graphics/weston-simple-shm/run.sh b/Runner/suites/Multimedia/Graphics/weston-simple-shm/run.sh index cde61a18..b33363cb 100755 --- a/Runner/suites/Multimedia/Graphics/weston-simple-shm/run.sh +++ b/Runner/suites/Multimedia/Graphics/weston-simple-shm/run.sh @@ -53,7 +53,7 @@ ALLOW_RELAUNCH="${ALLOW_RELAUNCH:-0}" REQUESTED_GRAPHICS_MODE="default" GPU_MODULE="${GPU_MODULE:-msm_kgsl}" GPU_OVERLAY_DEVICE="${GPU_OVERLAY_DEVICE:-/dev/kgsl-3d0}" -GPU_OVERLAY_GBM_PACKAGE="${GPU_OVERLAY_GBM_PACKAGE:-libgbm-msm1}" +GPU_OVERLAY_GBM_PACKAGE="${GPU_OVERLAY_GBM_PACKAGE:-}" CLIENT_PID="" while [ "$#" -gt 0 ]; do @@ -153,6 +153,17 @@ elif [ -r /etc/os-release ]; then fi [ -n "$OS_ID" ] || OS_ID="unknown" +if [ -z "$GPU_OVERLAY_GBM_PACKAGE" ]; then + case "$OS_ID" in + ubuntu) + GPU_OVERLAY_GBM_PACKAGE="libgbm-msm" + ;; + *) + GPU_OVERLAY_GBM_PACKAGE="libgbm-msm1" + ;; + esac +fi + DISTRO_GPU_HANDLING_SUPPORTED=0 case "$OS_ID" in debian|ubuntu|centos|rhel|fedora) diff --git a/Runner/utils/lib_bluetooth.sh b/Runner/utils/lib_bluetooth.sh index 48f3acd4..a80d6c58 100755 --- a/Runner/utils/lib_bluetooth.sh +++ b/Runner/utils/lib_bluetooth.sh @@ -124,8 +124,51 @@ bt_ubuntu_user_systemctl() { return 1 } +# Ensure the Debian-family Bluetooth control tools required by the shared +# power and scan helpers are installed before a suite starts. +bt_prepare_debian_bluetooth_tools() { + bt_pdbt_os_id="$1" + + case "$bt_pdbt_os_id" in + debian|ubuntu) + ;; + *) + return 0 + ;; + esac + + if [ "$(id -u 2>/dev/null || echo 1)" -ne 0 ]; then + log_fail "Debian-family Bluetooth tool preparation must run as root" + return 1 + fi + + if ! command -v pkg_ensure_command >/dev/null 2>&1 && + [ -n "${TOOLS:-}" ] && + [ -r "$TOOLS/lib_pkg_provider.sh" ]; then + # shellcheck disable=SC1090,SC1091 + . "$TOOLS/lib_pkg_provider.sh" + fi + + if ! command -v pkg_ensure_command >/dev/null 2>&1; then + log_fail "Bluetooth command package helper is unavailable" + return 1 + fi + + # rfkill is used for bounded controller recovery. expect provides the PTY + # needed for the same bluetoothctl interaction as a manual session. + for bt_pdbt_command in rfkill expect; do + if ! pkg_ensure_command "$bt_pdbt_command"; then + log_fail "Failed to prepare required Bluetooth command: $bt_pdbt_command" + return 1 + fi + done + + return 0 +} + # Prepare the Ubuntu Bluetooth stack used by all current BT tests. -# Debian, Yocto, and other distributions return immediately without changes. +# Debian receives the shared control-tool preparation and no Ubuntu-specific +# service or desktop changes. Yocto and other distributions return unchanged. bt_prepare_ubuntu_stack() { if command -v pkg_detect_os_id >/dev/null 2>&1; then bt_pus_os_id="$(pkg_detect_os_id 2>/dev/null || echo unknown)" @@ -136,6 +179,10 @@ bt_prepare_ubuntu_stack() { )" fi + if ! bt_prepare_debian_bluetooth_tools "$bt_pus_os_id"; then + return 1 + fi + if [ "$bt_pus_os_id" != "ubuntu" ]; then return 0 fi @@ -1100,6 +1147,14 @@ bt_scan_devices() { fi done + if [ "${BT_SCAN_INTERACTIVE_FALLBACK:-1}" = "1" ] && \ + command -v expect >/dev/null 2>&1; then + log_warn "bt_scan_devices: trying expect PTY fallback" + if bt_scan_devices_expect "$mac_id"; then + return 0 + fi + fi + if [ "${BT_SCAN_INTERACTIVE_FALLBACK:-1}" = "1" ]; then if bt_scan_devices_interactive_fallback "$adapter" "$scan_window" "$mac_id_up"; then return 0 @@ -1751,6 +1806,111 @@ btctl_script() { } | bluetoothctl 2>/dev/null } +# Request a Bluetooth controller power transition while keeping bluetoothctl +# connected long enough for BlueZ to complete its asynchronous operation. +# Args: +bt_request_power() { + # Keep the adapter argument for API compatibility. The original helper + # deliberately used BlueZ's default controller, which is required by some + # minimal/Yocto bluetoothctl implementations. + want="${2:-}" + settle_seconds="${BT_POWER_REQUEST_WAIT:-5}" + + case "$want" in + on|off) + ;; + *) + return 1 + ;; + esac + + case "$settle_seconds" in + ""|*[!0-9]*) + settle_seconds=5 + ;; + esac + + if [ "$settle_seconds" -lt 1 ] 2>/dev/null; then + settle_seconds=1 + fi + + request_timeout=$((settle_seconds + 8)) + + # bluetoothctl changes behaviour when stdin is a pipe on some BlueZ + # releases. Use a PTY when expect is available, matching the manual + # `bluetoothctl` flow and waiting for BlueZ to acknowledge the command. + if command -v expect >/dev/null 2>&1; then + BT_POWER_REQUEST_MODE="$want" \ + BT_POWER_REQUEST_SETTLE="$settle_seconds" \ + expect <<'__BT_POWER__' + set timeout 15 + set mode $env(BT_POWER_REQUEST_MODE) + set settle $env(BT_POWER_REQUEST_SETTLE) + + proc wait_prompt {} { + expect { + -re {(\r\n|\n)?(\[.*\][#>]|[#>])} { + return 1 + } + timeout { + return 0 + } + eof { + return 0 + } + } + } + + spawn bluetoothctl + if {![wait_prompt]} { + exit 1 + } + + send "power $mode\r" + expect { + -re {Changing power (on|off) succeeded} {} + -re {(Failed|failed|not available|No default controller)} { + exit 1 + } + timeout { + exit 1 + } + eof { + exit 1 + } + } + + after [expr {$settle * 1000}] + send "show\r" + if {![wait_prompt]} { + exit 1 + } + + send "quit\r" + expect { + eof {} + timeout {} + } +__BT_POWER__ + return $? + fi + + if command -v run_with_timeout >/dev/null 2>&1; then + { + printf 'power %s\n' "$want" + sleep "$settle_seconds" + printf 'quit\n' + } | run_with_timeout "$request_timeout" bluetoothctl 2>/dev/null + return $? + fi + + { + printf 'power %s\n' "$want" + sleep "$settle_seconds" + printf 'quit\n' + } | bluetoothctl 2>/dev/null +} + # bt_set_scan on|off [adapter] # NOTE: # - We deliberately ignore adapter here and rely on BlueZ's default controller, @@ -2773,28 +2933,81 @@ btgetpower() { )" fi + # Newer BlueZ versions complete `show` synchronously when invoked with + # arguments. Prefer that form before the interactive fallback below. The + # fallback is retained for minimal images where non-interactive output is + # incomplete. if [ -n "$mac" ]; then + if command -v run_with_timeout >/dev/null 2>&1; then + out="$( + run_with_timeout 3 bluetoothctl show "$mac" 2>/dev/null \ + | sanitize_bt_output || true + )" + else + out="$(bluetoothctl show "$mac" 2>/dev/null | sanitize_bt_output || true)" + fi + + state="$(printf '%s\n' "$out" \ + | awk -F':[[:space:]]*' ' + /^[[:space:]]*Powered:/ { + v = tolower($2); + gsub(/\r/, "", v); + gsub(/[[:space:]]+/, "", v); + print v; + exit + } + ')" + fi + + # The default controller form is the most portable query on Debian and + # Ubuntu. It also covers BlueZ releases that do not accept an address as + # the argument to `show`. + if [ -z "$state" ]; then + if command -v run_with_timeout >/dev/null 2>&1; then + out="$( + run_with_timeout 3 bluetoothctl show 2>/dev/null \ + | sanitize_bt_output || true + )" + else + out="$(bluetoothctl show 2>/dev/null | sanitize_bt_output || true)" + fi + + state="$(printf '%s\n' "$out" \ + | awk -F':[[:space:]]*' ' + /^[[:space:]]*Powered:/ { + v = tolower($2); + gsub(/\r/, "", v); + gsub(/[[:space:]]+/, "", v); + print v; + exit + } + ')" + fi + + if [ -z "$state" ] && [ -n "$mac" ]; then out="$( btctl_script "show $mac" "quit" 2>/dev/null \ | sanitize_bt_output || true )" - else + elif [ -z "$state" ]; then out="$( btctl_script "show" "quit" 2>/dev/null \ | sanitize_bt_output || true )" fi - state="$(printf '%s\n' "$out" \ - | awk -F':[[:space:]]*' ' - /^[[:space:]]*Powered:/ { - v = tolower($2); - gsub(/\r/, "", v); - gsub(/[[:space:]]+/, "", v); - print v; - exit - } - ')" + if [ -z "$state" ]; then + state="$(printf '%s\n' "$out" \ + | awk -F':[[:space:]]*' ' + /^[[:space:]]*Powered:/ { + v = tolower($2); + gsub(/\r/, "", v); + gsub(/[[:space:]]+/, "", v); + print v; + exit + } + ')" + fi # Fallback: try default controller if adapter-specific attempt didn’t yield Powered: if [ -z "$state" ]; then @@ -2855,37 +3068,71 @@ btpower() { fi log_info "btpower: requesting '$want' on $dev (current=$cur_state)" - - # Drive bluetoothctl interactively (works on ramdisk where non-interactive list/show may be empty) - # Do NOT use "select hci0" (it can say "Controller hci0 not available" even when controller exists). - btctl_script "power $want" "quit" >/dev/null 2>&1 || true - - i=0 - max_tries=10 + + request_attempts="${BT_POWER_REQUEST_ATTEMPTS:-3}" + verify_attempts="${BT_POWER_VERIFY_ATTEMPTS:-10}" + retry_delay="${BT_POWER_REQUEST_RETRY_DELAY:-2}" + + case "$request_attempts" in + ""|*[!0-9]*) request_attempts=3 ;; + esac + case "$verify_attempts" in + ""|*[!0-9]*) verify_attempts=10 ;; + esac + case "$retry_delay" in + ""|*[!0-9]*) retry_delay=2 ;; + esac + + if [ "$request_attempts" -lt 1 ] 2>/dev/null; then + request_attempts=1 + fi + if [ "$verify_attempts" -lt 1 ] 2>/dev/null; then + verify_attempts=1 + fi + + log_info "btpower: request attempts=$request_attempts verify attempts=$verify_attempts retry delay=${retry_delay}s" + if command -v expect >/dev/null 2>&1; then + log_info "btpower: using an expect PTY for interactive bluetoothctl power requests" + else + log_warn "btpower: expect is unavailable, using the portable stdin-pipe fallback" + fi + + request_attempt=1 state="" pstate="" - - while [ "$i" -lt "$max_tries" ]; do - # Read Powered via btgetpower (must be interactive-based implementation) - state="$(btgetpower "$dev" 2>/dev/null || true)" - - if [ "$want" = "on" ] && [ "$state" = "yes" ]; then - log_info "btpower: $dev Powered=yes after request." - return 0 - fi - - if [ "$want" = "off" ] && [ "$state" = "no" ]; then - log_info "btpower: $dev Powered=no after request." - return 0 + + while [ "$request_attempt" -le "$request_attempts" ]; do + # Drive bluetoothctl interactively against BlueZ's default controller, + # which matches the established minimal/ramdisk flow. Its exit status + # is informational because BlueZ completes power changes asynchronously. + if ! bt_request_power "$dev" "$want" >/dev/null 2>&1; then + log_warn "btpower: bluetoothctl request attempt $request_attempt/$request_attempts exited without confirmation" fi - - # If Powered line is not available yet, try to parse PowerState as an informational fallback - # (Some stacks lag on Powered; PowerState can show transitions like off-enabling/on-disabling.) + + verify_attempt=1 + while [ "$verify_attempt" -le "$verify_attempts" ]; do + state="$(btgetpower "$dev" 2>/dev/null || true)" + + if [ "$want" = "on" ] && [ "$state" = "yes" ]; then + log_info "btpower: $dev Powered=yes after request attempt $request_attempt." + return 0 + fi + + if [ "$want" = "off" ] && [ "$state" = "no" ]; then + log_info "btpower: $dev Powered=no after request attempt $request_attempt." + return 0 + fi + + sleep 1 + verify_attempt=$((verify_attempt + 1)) + done + + # Keep PowerState as diagnostic evidence only. A PASS always requires + # the stable Powered=yes/no value above. out="$( btctl_script "show" "quit" 2>/dev/null \ | sanitize_bt_output || true )" - pstate="$(printf '%s\n' "$out" \ | awk -F':[[:space:]]*' ' /^[[:space:]]*PowerState:/ { @@ -2895,11 +3142,13 @@ btpower() { print v; exit }')" - - # If Powered was empty but PowerState suggests we reached a stable end state, - # keep waiting a little more for Powered to update (do not treat pstate as PASS alone). - sleep 1 - i=$((i + 1)) + + if [ "$request_attempt" -lt "$request_attempts" ]; then + log_warn "btpower: request attempt $request_attempt/$request_attempts did not reach $want, retrying in ${retry_delay}s (Powered=${state:-unknown}, PowerState=${pstate:-unknown})" + sleep "$retry_delay" + fi + + request_attempt=$((request_attempt + 1)) done if [ -z "$state" ]; then diff --git a/Runner/utils/lib_display.sh b/Runner/utils/lib_display.sh index 62860fd9..47b22f51 100755 --- a/Runner/utils/lib_display.sh +++ b/Runner/utils/lib_display.sh @@ -656,6 +656,114 @@ discover_wayland_socket_anywhere() { return 1 } +# Adopt the live GNOME Wayland session without changing the desktop compositor. +# The selected socket must be owned by a running gnome-shell user so later +# clients can be executed with the same uid and runtime directory. +display_adopt_gnome_wayland_session() { + dagws_pid="" + dagws_user="" + dagws_uid="" + dagws_runtime_dir="" + dagws_socket="" + dagws_socket_uid="" + dagws_home="" + + DISPLAY_WAYLAND_SESSION_USER="" + DISPLAY_WAYLAND_SESSION_HOME="" + DISPLAY_WAYLAND_SESSION_RUNTIME_DIR="" + DISPLAY_WAYLAND_SOCKET="" + DISPLAY_RUNTIME_MODEL="unknown" + export DISPLAY_WAYLAND_SESSION_USER + export DISPLAY_WAYLAND_SESSION_HOME + export DISPLAY_WAYLAND_SESSION_RUNTIME_DIR + export DISPLAY_WAYLAND_SOCKET + export DISPLAY_RUNTIME_MODEL + + command -v pgrep >/dev/null 2>&1 || return 1 + command -v ps >/dev/null 2>&1 || return 1 + command -v id >/dev/null 2>&1 || return 1 + + for dagws_pid in $(pgrep -x gnome-shell 2>/dev/null); do + dagws_user="$(ps -o user= -p "$dagws_pid" 2>/dev/null | awk 'NR == 1 { print $1 }')" + [ -n "$dagws_user" ] || continue + + dagws_uid="$(id -u "$dagws_user" 2>/dev/null || true)" + [ -n "$dagws_uid" ] || continue + + dagws_runtime_dir="/run/user/$dagws_uid" + [ -d "$dagws_runtime_dir" ] || continue + + for dagws_socket in "$dagws_runtime_dir"/wayland-*; do + [ -S "$dagws_socket" ] || continue + + dagws_socket_uid="$(stat -c '%u' "$dagws_socket" 2>/dev/null || true)" + [ "$dagws_socket_uid" = "$dagws_uid" ] || continue + + dagws_home="$(getent passwd "$dagws_user" 2>/dev/null | awk -F: 'NR == 1 { print $6 }')" + [ -n "$dagws_home" ] || dagws_home="/" + + DISPLAY_WAYLAND_SESSION_USER="$dagws_user" + DISPLAY_WAYLAND_SESSION_HOME="$dagws_home" + DISPLAY_WAYLAND_SESSION_RUNTIME_DIR="$dagws_runtime_dir" + DISPLAY_WAYLAND_SOCKET="$dagws_socket" + DISPLAY_RUNTIME_MODEL="desktop-gnome-session" + export DISPLAY_WAYLAND_SESSION_USER + export DISPLAY_WAYLAND_SESSION_HOME + export DISPLAY_WAYLAND_SESSION_RUNTIME_DIR + export DISPLAY_WAYLAND_SOCKET + export DISPLAY_RUNTIME_MODEL + + log_info "Adopted GNOME Wayland session, user=$dagws_user socket=$dagws_socket" + return 0 + done + done + + return 1 +} + +# Run a command in the user context that owns an adopted GNOME Wayland socket. +# When no GNOME session was adopted, preserve the caller's existing execution. +display_run_in_wayland_session() { + drws_user="${DISPLAY_WAYLAND_SESSION_USER:-}" + drws_home="${DISPLAY_WAYLAND_SESSION_HOME:-/}" + drws_runtime_dir="${DISPLAY_WAYLAND_SESSION_RUNTIME_DIR:-}" + drws_socket="${DISPLAY_WAYLAND_SOCKET:-}" + + if [ -z "$drws_user" ] || + [ -z "$drws_runtime_dir" ] || + [ -z "$drws_socket" ]; then + "$@" + return $? + fi + + if [ "$(id -un 2>/dev/null || true)" = "$drws_user" ]; then + env \ + HOME="$drws_home" \ + XDG_RUNTIME_DIR="$drws_runtime_dir" \ + WAYLAND_DISPLAY="$(basename "$drws_socket")" \ + DBUS_SESSION_BUS_ADDRESS="unix:path=$drws_runtime_dir/bus" \ + "$@" + return $? + fi + + if [ "$(id -u 2>/dev/null || echo 1)" -ne 0 ]; then + log_error "Cannot access the adopted GNOME Wayland session as user=$drws_user without root" + return 1 + fi + + if ! command -v runuser >/dev/null 2>&1; then + log_error "runuser is unavailable, cannot access the adopted GNOME Wayland session" + return 1 + fi + + runuser -u "$drws_user" -- env \ + HOME="$drws_home" \ + XDG_RUNTIME_DIR="$drws_runtime_dir" \ + WAYLAND_DISPLAY="$(basename "$drws_socket")" \ + DBUS_SESSION_BUS_ADDRESS="unix:path=$drws_runtime_dir/bus" \ + "$@" +} + adopt_wayland_env_from_socket() { sock="$1" [ -n "$sock" ] || return 1 @@ -5450,11 +5558,20 @@ display_prepare_desktop_graphics_stack() { dpdgs_mode="${2:-auto}" dpdgs_gpu_module="${3:-msm_kgsl}" dpdgs_gpu_device="${4:-/dev/kgsl-3d0}" - dpdgs_gbm_package="${5:-libgbm-msm1}" + dpdgs_gbm_package="${5:-}" dpdgs_rc=0 dpdgs_package_changed=0 dpdgs_boot_changed=0 + if [ -z "$dpdgs_gbm_package" ]; then + if command -v pkg_detect_os_id >/dev/null 2>&1 && + [ "$(pkg_detect_os_id 2>/dev/null || true)" = "ubuntu" ]; then + dpdgs_gbm_package="libgbm-msm" + else + dpdgs_gbm_package="libgbm-msm1" + fi + fi + case "$dpdgs_mode" in auto) log_info "Desktop graphics mode is auto, preserving the currently installed and booted stack" @@ -5865,6 +5982,16 @@ display_apply_test_fps_gate_policy() { ;; esac + if [ "${DISPLAY_TEST_FPS_POLICY:-shared}" = "desktop-session-connectivity" ]; then + if [ "$datfgp_count" -eq 0 ]; then + log_warn "No FPS samples were produced by the desktop session, compositor connectivity was validated" + else + log_info "Recording desktop-session FPS samples without performance gating, samples=$datfgp_count avg=$datfgp_avg" + fi + + return 0 + fi + if [ "${DISPLAY_TEST_FPS_POLICY:-shared}" != "desktop-functional-cap" ]; then if command -v display_fps_gate_avg >/dev/null 2>&1; then display_fps_gate_avg "$datfgp_avg" "$datfgp_count" @@ -5912,4 +6039,3 @@ display_apply_test_fps_gate_policy() { log_info "Desktop functional FPS gate passed, avg=$datfgp_avg (~$datfgp_rounded) >= ${DISPLAY_TEST_FPS_MIN_OK:-1} (target=${DISPLAY_TEST_FPS_EXPECTED:-unknown}, output=${DISPLAY_TEST_FPS_REFRESH:-unknown}Hz)" return 0 } -