From 4fa3a903d44045ffe1f635fa35fbb16a21a913d9 Mon Sep 17 00:00:00 2001 From: Srikanth Muppandam Date: Wed, 26 Aug 2026 17:27:12 +0530 Subject: [PATCH 1/3] utils: harden Bluetooth runtime recovery Bluetooth controllers can appear before QCA firmware initialization has completed, leaving an all-zero BD address and causing the first CI run to fail while a rerun passes. Existing readiness checks accepted service and HCI presence alone, and nested bluetoothctl polling could consume most of the LAVA timeout. Require an active service and a valid non-zero controller address, add bounded rfkill and service recovery attempts, and prevent public-addr setup for invalid addresses. Route controller, scan, and power queries through bounded helpers so a stalled BlueZ command cannot exhaust the job timeout. Signed-off-by: Srikanth Muppandam --- Runner/utils/lib_bluetooth.sh | 315 ++++++++++++++++++++++++++-------- 1 file changed, 243 insertions(+), 72 deletions(-) diff --git a/Runner/utils/lib_bluetooth.sh b/Runner/utils/lib_bluetooth.sh index 117e0b3c..edd6218c 100755 --- a/Runner/utils/lib_bluetooth.sh +++ b/Runner/utils/lib_bluetooth.sh @@ -638,7 +638,7 @@ bt_scan_devices_interactive_fallback() { log_warn "bt_scan_devices: trying interactive bluetoothctl fallback on $adapter for ${scan_window}s" - if command -v timeout >/dev/null 2>&1; then + if command -v run_with_timeout >/dev/null 2>&1; then fallback_out="$( { printf 'select %s\n' "$adapter" @@ -652,7 +652,7 @@ bt_scan_devices_interactive_fallback() { printf 'devices\n' sleep 1 printf 'quit\n' - } | timeout "$total_timeout" bluetoothctl 2>&1 | sanitize_bt_output || true + } | run_with_timeout "$total_timeout" bluetoothctl 2>&1 | sanitize_bt_output || true )" else fallback_out="$( @@ -921,10 +921,10 @@ bt_scan_devices() { log_warn "bt_scan_devices: btpower($adapter on) did not report success; continuing" fi else - bluetoothctl power on >/dev/null 2>&1 || true + run_with_timeout 3 bluetoothctl power on >/dev/null 2>&1 || true fi - bluetoothctl select "$adapter" >/dev/null 2>&1 || true + run_with_timeout 3 bluetoothctl select "$adapter" >/dev/null 2>&1 || true attempt=1 found_lines="" @@ -934,9 +934,10 @@ bt_scan_devices() { live_out="$( { - bluetoothctl select "$adapter" 2>/dev/null || true - bluetoothctl power on 2>/dev/null || true - bluetoothctl --timeout "$scan_window" scan on 2>&1 || true + run_with_timeout 3 bluetoothctl select "$adapter" 2>/dev/null || true + run_with_timeout 3 bluetoothctl power on 2>/dev/null || true + run_with_timeout "$((scan_window + 5))" \ + bluetoothctl --timeout "$scan_window" scan on 2>&1 || true } | sanitize_bt_output )" @@ -945,7 +946,7 @@ bt_scan_devices() { log_warn "bt_scan_devices: bt_set_scan(off) reported failure after attempt $attempt" fi else - bluetoothctl scan off >/dev/null 2>&1 || true + run_with_timeout 5 bluetoothctl scan off >/dev/null 2>&1 || true fi if command -v bt_list_devices_raw >/dev/null 2>&1; then @@ -1728,7 +1729,7 @@ btctl_script() { BTINTERACTIVEMODELOGGED=1 fi - if command -v timeout >/dev/null 2>&1; then + if command -v run_with_timeout >/dev/null 2>&1; then { for line in "$@"; do [ -n "$line" ] || continue @@ -1736,7 +1737,7 @@ btctl_script() { sleep 0.2 done sleep 1 - } | timeout 6 bluetoothctl 2>/dev/null + } | run_with_timeout 6 bluetoothctl 2>/dev/null return $? fi @@ -2074,7 +2075,7 @@ btcontrollerpresent() { # ret: 0=controller visible, 1=not visible # This is just a clearer alias/wrapper if you prefer the name. bt_controller_visible() { - btcontrollerpresent + btcontrollervisible "${1:-}" } # Usage: btensurepublicaddr hci0 @@ -2089,10 +2090,15 @@ btensurepublicaddr() { dev="${1:-}" # Already visible: nothing to do. - if btcontrollerpresent || bt_controller_visible "$dev"; then + if btcontrollervisible "$dev"; then log_info "controller already visible via bluetoothctl, skip public-addr" return 0 fi + + if ! btbdok "$dev"; then + log_warn "Bluetooth adapter ${dev:-} has no valid BD address, public-addr cannot be applied" + return 2 + fi mac="$( btgetbdaddr "$dev" 2>/dev/null \ @@ -2114,10 +2120,20 @@ quit" >/dev/null 2>&1 || true # Poll for controller visibility (BlueZ can be async) i=0 - max_wait=15 # was 5; 15 is still small but avoids flakiness - while [ "$i" -lt "$max_wait" ]; do - if btcontrollerpresent || bt_controller_visible "$dev"; then - log_info "controller visible after public-addr $mac (waited ${i}s)" + max_wait="${BT_CONTROLLER_VISIBLE_WAIT:-15}" + + case "$max_wait" in + ''|*[!0-9]*) + max_wait=15 + ;; + esac + + max_attempts=$(((max_wait + 4) / 5)) + [ "$max_attempts" -gt 0 ] || max_attempts=1 + + while [ "$i" -lt "$max_attempts" ]; do + if btcontrollervisible "$dev"; then + log_info "controller visible after public-addr $mac" return 0 fi sleep 1 @@ -2157,7 +2173,9 @@ btcontrollervisible() { esac btctlrun() { - if command -v timeout >/dev/null 2>&1; then + if command -v run_with_timeout >/dev/null 2>&1; then + run_with_timeout 2 bluetoothctl "$@" 2>/dev/null || true + elif command -v timeout >/dev/null 2>&1; then timeout 2 bluetoothctl "$@" 2>/dev/null || true else bluetoothctl "$@" 2>/dev/null || true @@ -2197,7 +2215,7 @@ bt_ensure_controller_visible() { adapter="${1:-}" # Fast path: already visible - if btcontrollerpresent || bt_controller_visible "$adapter"; then + if btcontrollervisible "$adapter"; then return 0 fi @@ -2217,7 +2235,7 @@ bt_ensure_controller_visible() { fi # Final controller visibility check - if btcontrollerpresent || bt_controller_visible "$adapter"; then + if btcontrollervisible "$adapter"; then return 0 fi @@ -2495,7 +2513,11 @@ bt_pair_once() { # Get current Discovering state from bluetoothctl show. # Prints one of: yes | no | unknown bt_get_discovering() { - out="$(bluetoothctl show 2>/dev/null | sanitize_bt_output | tr -d '\r')" + out="$( + run_with_timeout 3 bluetoothctl show 2>/dev/null \ + | sanitize_bt_output \ + | tr -d '\r' + )" case "$out" in *"Discovering: yes"*) @@ -2535,7 +2557,10 @@ bt_wait_discovering() { # Raw devices output from bluetoothctl bt_list_devices_raw() { - out="$(bluetoothctl devices 2>/dev/null | sanitize_bt_output || true)" + out="$( + run_with_timeout 3 bluetoothctl devices 2>/dev/null \ + | sanitize_bt_output || true + )" if [ -z "$out" ]; then # If controller list is already known-flaky, mark fallback so btctl_script logs once. @@ -2731,19 +2756,13 @@ btgetpower() { if [ -n "$mac" ]; then out="$( - { - printf 'show %s\n' "$mac" - sleep 1 - printf 'quit\n' - } | bluetoothctl 2>/dev/null | sanitize_bt_output || true + btctl_script "show $mac" "quit" 2>/dev/null \ + | sanitize_bt_output || true )" else out="$( - { - printf 'show\n' - sleep 1 - printf 'quit\n' - } | bluetoothctl 2>/dev/null | sanitize_bt_output || true + btctl_script "show" "quit" 2>/dev/null \ + | sanitize_bt_output || true )" fi @@ -2761,11 +2780,8 @@ btgetpower() { # Fallback: try default controller if adapter-specific attempt didn’t yield Powered: if [ -z "$state" ]; then out="$( - { - printf 'show\n' - sleep 1 - printf 'quit\n' - } | bluetoothctl 2>/dev/null | sanitize_bt_output || true + btctl_script "show" "quit" 2>/dev/null \ + | sanitize_bt_output || true )" state="$(printf '%s\n' "$out" \ | awk -F':[[:space:]]*' ' @@ -2847,11 +2863,8 @@ btpower() { # 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.) out="$( - { - printf 'show\n' - sleep 1 - printf 'quit\n' - } | bluetoothctl 2>/dev/null | sanitize_bt_output || true + btctl_script "show" "quit" 2>/dev/null \ + | sanitize_bt_output || true )" pstate="$(printf '%s\n' "$out" \ @@ -2914,57 +2927,215 @@ btfwpresent() { } bt_wait_ready() { - max_wait="${1:-60}" - sleep_step="${2:-2}" - waited=0 - started_service=0 - - if [ -z "$max_wait" ]; then - max_wait=60 + bt_wr_max_wait="${1:-60}" + bt_wr_sleep_step="${2:-2}" + bt_wr_requested_adapter="${3:-${BT_ADAPTER:-}}" + bt_wr_waited=0 + bt_wr_started_service=0 + bt_wr_adapter="" + + BT_RUNTIME_READY_ADAPTER="" + + if [ -z "$bt_wr_max_wait" ]; then + bt_wr_max_wait=60 fi - if [ -z "$sleep_step" ]; then - sleep_step=2 + if [ -z "$bt_wr_sleep_step" ]; then + bt_wr_sleep_step=2 fi - - case "$max_wait" in + + case "$bt_wr_max_wait" in ''|*[!0-9]*) - max_wait=60 + bt_wr_max_wait=60 ;; esac - case "$sleep_step" in + case "$bt_wr_sleep_step" in ''|*[!0-9]*) - sleep_step=2 + bt_wr_sleep_step=2 ;; esac - - if [ "$max_wait" -le 0 ] 2>/dev/null; then - max_wait=60 + + if [ "$bt_wr_max_wait" -le 0 ] 2>/dev/null; then + bt_wr_max_wait=60 fi - if [ "$sleep_step" -le 0 ] 2>/dev/null; then - sleep_step=2 + if [ "$bt_wr_sleep_step" -le 0 ] 2>/dev/null; then + bt_wr_sleep_step=2 fi - - while [ "$waited" -lt "$max_wait" ]; do - if btsvcactive && bthcipresent; then - log_info "Bluetooth runtime became ready after ${waited}s." + + while [ "$bt_wr_waited" -le "$bt_wr_max_wait" ]; do + if [ -n "$bt_wr_requested_adapter" ]; then + bt_wr_adapter="$bt_wr_requested_adapter" + else + bt_wr_fallback_adapter="$(listhcis 2>/dev/null | sed -n '1p')" + bt_wr_adapter="$( + listhcis 2>/dev/null | + while IFS= read -r bt_wr_candidate; do + if btbdok "$bt_wr_candidate"; then + printf '%s\n' "$bt_wr_candidate" + break + fi + done + )" + + if [ -z "$bt_wr_adapter" ]; then + bt_wr_adapter="$bt_wr_fallback_adapter" + fi + fi + + if btsvcactive && + [ -n "$bt_wr_adapter" ] && + btbdok "$bt_wr_adapter"; then + BT_RUNTIME_READY_ADAPTER="$bt_wr_adapter" + log_info "Bluetooth runtime became ready after ${bt_wr_waited}s with usable adapter $bt_wr_adapter." return 0 fi - - if [ "$started_service" -eq 0 ]; then + + if [ "$bt_wr_started_service" -eq 0 ]; then if command -v systemctl >/dev/null 2>&1; then if ! btsvcactive; then log_info "Bluetooth service not active yet, attempting start." systemctl start bluetooth.service >/dev/null 2>&1 || true fi fi - started_service=1 + bt_wr_started_service=1 fi - - sleep "$sleep_step" - waited=$((waited + sleep_step)) + + if [ "$bt_wr_waited" -eq "$bt_wr_max_wait" ]; then + break + fi + + bt_wr_delay="$bt_wr_sleep_step" + bt_wr_remaining=$((bt_wr_max_wait - bt_wr_waited)) + if [ "$bt_wr_delay" -gt "$bt_wr_remaining" ]; then + bt_wr_delay="$bt_wr_remaining" + fi + + sleep "$bt_wr_delay" + bt_wr_waited=$((bt_wr_waited + bt_wr_delay)) done - - log_warn "Bluetooth runtime did not become ready within ${max_wait}s." + + if [ -n "$bt_wr_adapter" ]; then + log_warn "Bluetooth runtime did not become ready within ${bt_wr_max_wait}s, adapter $bt_wr_adapter has no valid BD address" + else + log_warn "Bluetooth runtime did not become ready within ${bt_wr_max_wait}s, no HCI adapter appeared" + fi + + return 1 +} + +# Perform one controlled recovery attempt for an incompletely initialized +# Bluetooth runtime. The controller is left unblocked and bluetooth.service is +# left running for subsequent tests. +# +# Usage: +# bt_recover_runtime [ADAPTER] +bt_recover_runtime() { + bt_rr_adapter="${1:-}" + bt_rr_action=0 + + log_warn "Attempting one controlled Bluetooth runtime recovery" + + if command -v rfkill >/dev/null 2>&1; then + log_info "Cycling the Bluetooth software rfkill state" + rfkill block bluetooth >/dev/null 2>&1 || true + sleep 2 + rfkill unblock bluetooth >/dev/null 2>&1 || true + sleep 2 + bt_rr_action=1 + elif command -v rfkillunblocksysfs >/dev/null 2>&1; then + log_info "Unblocking Bluetooth through sysfs" + rfkillunblocksysfs >/dev/null 2>&1 || true + bt_rr_action=1 + fi + + if command -v systemctl >/dev/null 2>&1; then + log_info "Restarting bluetooth.service after the recovery attempt" + if command -v run_with_timeout >/dev/null 2>&1; then + run_with_timeout 15 \ + systemctl restart bluetooth.service >/dev/null 2>&1 || true + else + systemctl restart bluetooth.service >/dev/null 2>&1 || true + fi + bt_rr_action=1 + fi + + if [ -n "$bt_rr_adapter" ] && command -v hciconfig >/dev/null 2>&1; then + hciconfig "$bt_rr_adapter" up >/dev/null 2>&1 || true + bt_rr_action=1 + fi + + if [ "$bt_rr_action" -eq 0 ]; then + log_warn "No supported Bluetooth recovery mechanism is available" + return 1 + fi + + return 0 +} + +# Ensure Bluetooth reaches a usable state, including bounded recovery retries. +# A usable runtime has an active service and an HCI adapter with a valid, +# non-zero BD address. BT_RUNTIME_RECOVERED and BT_RUNTIME_READY_ADAPTER are +# updated for callers that want to report whether recovery was required. +# +# Usage: +# bt_ensure_runtime_ready [ADAPTER] [INITIAL_WAIT] [RECOVERY_WAIT] [ATTEMPTS] +bt_ensure_runtime_ready() { + bt_err_adapter="${1:-${BT_ADAPTER:-}}" + bt_err_initial_wait="${2:-${BT_RUNTIME_READY_WAIT:-20}}" + bt_err_recovery_wait="${3:-${BT_RUNTIME_RECOVERY_WAIT:-40}}" + bt_err_attempts="${4:-${BT_RUNTIME_RECOVERY_ATTEMPTS:-2}}" + bt_err_attempt=1 + + # Exported result state for suite callers. + # shellcheck disable=SC2034 + BT_RUNTIME_RECOVERED=0 + # shellcheck disable=SC2034 + BT_RUNTIME_READY_ADAPTER="" + + for bt_err_value in \ + "$bt_err_initial_wait" \ + "$bt_err_recovery_wait" \ + "$bt_err_attempts" + do + case "$bt_err_value" in + ''|*[!0-9]*|0) + log_error "Bluetooth readiness waits and attempts must be positive integers" + return 2 + ;; + esac + done + + if bt_wait_ready "$bt_err_initial_wait" 2 "$bt_err_adapter"; then + return 0 + fi + + while [ "$bt_err_attempt" -le "$bt_err_attempts" ]; do + log_warn "Bluetooth recovery attempt $bt_err_attempt/$bt_err_attempts" + + bt_err_recovery_adapter="$bt_err_adapter" + if [ -z "$bt_err_recovery_adapter" ]; then + bt_err_recovery_adapter="$(listhcis 2>/dev/null | sed -n '1p')" + fi + + if bt_recover_runtime "$bt_err_recovery_adapter"; then + log_info "Waiting up to ${bt_err_recovery_wait}s after Bluetooth recovery" + + if bt_wait_ready \ + "$bt_err_recovery_wait" \ + 2 \ + "$bt_err_adapter"; then + # shellcheck disable=SC2034 + BT_RUNTIME_RECOVERED=1 + log_warn "Bluetooth runtime recovered on attempt $bt_err_attempt" + return 0 + fi + else + log_warn "Bluetooth recovery attempt $bt_err_attempt could not perform a recovery action" + fi + + bt_err_attempt=$((bt_err_attempt + 1)) + done + + log_error "Bluetooth runtime remained unusable after $bt_err_attempts controlled recovery attempts" return 1 } From 131d279ff61fd5e867c95d6e3d7f177ce41bc87e Mon Sep 17 00:00:00 2001 From: Srikanth Muppandam Date: Wed, 26 Aug 2026 17:27:23 +0530 Subject: [PATCH 2/3] bluetooth: recover firmware and power tests on first boot QCA firmware initialization can leave an HCI device present with an all-zero address during the first boot. BT_FW_KMD_Service and BT_ON_OFF previously continued from that incomplete state or skipped missing BlueZ controller visibility, producing failures that disappeared on rerun. Gate both suites on the shared bounded runtime recovery, retain the recovered adapter, and fail only after recovery is exhausted. Use the shared result API and expose recovery timing through the LAVA parameters for deterministic CI behavior. Signed-off-by: Srikanth Muppandam --- .../BT_FW_KMD_Service/BT_FW_KMD_Service.yaml | 7 +- .../Bluetooth/BT_FW_KMD_Service/run.sh | 207 +++++++++------- .../Bluetooth/BT_ON_OFF/BT_ON_OFF.yaml | 5 +- .../Connectivity/Bluetooth/BT_ON_OFF/run.sh | 225 ++++++++++-------- 4 files changed, 259 insertions(+), 185 deletions(-) diff --git a/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/BT_FW_KMD_Service.yaml b/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/BT_FW_KMD_Service.yaml index f7e3b89b..7ff6d296 100644 --- a/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/BT_FW_KMD_Service.yaml +++ b/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/BT_FW_KMD_Service.yaml @@ -7,10 +7,15 @@ metadata: scope: - functional +params: + BT_ADAPTER: "" + BT_RUNTIME_READY_WAIT: "20" + BT_RUNTIME_RECOVERY_WAIT: "40" + BT_RUNTIME_RECOVERY_ATTEMPTS: "2" + run: steps: - REPO_PATH=$PWD - cd Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/ - ./run.sh || true - $REPO_PATH/Runner/utils/send-to-lava.sh BT_FW_KMD_Service.res - diff --git a/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/run.sh b/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/run.sh index 334968a1..b96a83b9 100755 --- a/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/run.sh +++ b/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/run.sh @@ -4,7 +4,7 @@ # BT_FW_KMD_Service - Bluetooth FW + KMD + service + controller infra validation # Non-expect version, using lib_bluetooth.sh helpers. -# ---------- init_env + tools ---------- +# ---------- Repo env + helpers ---------- SCRIPT_DIR="$( cd "$(dirname "$0")" || exit 1 pwd @@ -25,12 +25,16 @@ if [ -z "$INIT_ENV" ]; then exit 1 fi +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. if [ -z "${__INIT_ENV_LOADED:-}" ]; then # shellcheck disable=SC1090 . "$INIT_ENV" __INIT_ENV_LOADED=1 fi +# shellcheck disable=SC1090 +. "$INIT_ENV" # shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" # shellcheck disable=SC1091 @@ -38,63 +42,111 @@ fi # ---------- CLI / env parameters ---------- BT_ADAPTER="${BT_ADAPTER-}" +BT_RUNTIME_READY_WAIT="${BT_RUNTIME_READY_WAIT:-20}" +BT_RUNTIME_RECOVERY_WAIT="${BT_RUNTIME_RECOVERY_WAIT:-40}" +BT_RUNTIME_RECOVERY_ATTEMPTS="${BT_RUNTIME_RECOVERY_ATTEMPTS:-2}" + +usage() { + cat <&2 + return 2 + fi + + BT_ADAPTER="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + log_error "Unknown argument: $1" + usage >&2 + return 2 + ;; + esac + done +} + +parse_args "$@" || exit $? + +for wait_value in \ + "$BT_RUNTIME_READY_WAIT" \ + "$BT_RUNTIME_RECOVERY_WAIT" \ + "$BT_RUNTIME_RECOVERY_ATTEMPTS" +do + if ! is_unsigned_number "$wait_value" || [ "$wait_value" -eq 0 ]; then + log_error "Bluetooth runtime wait values must be positive integers" + exit 2 + fi done -TESTNAME="BT_FW_KMD_Service" -testpath="$(find_test_case_by_name "$TESTNAME")" || { - log_fail "$TESTNAME : Test directory not found." - echo "$TESTNAME FAIL" > "./$TESTNAME.res" +testpath="$(find_test_case_by_name "BT_FW_KMD_Service")" || { + log_fail "BT_FW_KMD_Service FAIL - test directory not found" + printf '%s\n' "BT_FW_KMD_Service FAIL" >"./BT_FW_KMD_Service.res" exit 1 } cd "$testpath" || exit 1 +TESTNAME="BT_FW_KMD_Service" RES_FILE="./${TESTNAME}.res" -rm -f "$RES_FILE" +test_result_init "$TESTNAME" "$RES_FILE" || exit 1 -FAIL_COUNT=0 WARN_COUNT=0 -inc_fail() { FAIL_COUNT=$((FAIL_COUNT + 1)); } -inc_warn() { WARN_COUNT=$((WARN_COUNT + 1)); } - log_info "------------------------------------------------------------" log_info "Starting $TESTNAME" if ! bt_prepare_ubuntu_stack; then - log_fail "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 + test_result_finish "FAIL" "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" fi log_info "Checking dependencies: bluetoothctl hciconfig lsmod" if ! check_dependencies bluetoothctl hciconfig lsmod; then - echo "$TESTNAME SKIP" > "$RES_FILE" - exit 0 + test_result_finish "SKIP" "$TESTNAME SKIP - required Bluetooth tools are unavailable" fi # ---------- Bluetooth runtime check/ readiness ---------- -log_info "Waiting for Bluetooth runtime readiness..." -bt_wait_ready 60 2 || true +log_info "Waiting up to ${BT_RUNTIME_READY_WAIT}s for a usable Bluetooth runtime" +if bt_ensure_runtime_ready \ + "$BT_ADAPTER" \ + "$BT_RUNTIME_READY_WAIT" \ + "$BT_RUNTIME_RECOVERY_WAIT" \ + "$BT_RUNTIME_RECOVERY_ATTEMPTS"; then + if [ "$BT_RUNTIME_RECOVERED" -eq 1 ]; then + WARN_COUNT=$((WARN_COUNT + 1)) + test_result_record "PASS" "Bluetooth runtime recovered with a valid HCI adapter" + else + test_result_record "PASS" "Bluetooth runtime exposed an HCI adapter with a valid BD address" + fi +else + test_result_record "FAIL" "Bluetooth runtime remained unusable after bounded recovery attempts" +fi + +ADAPTER="$BT_RUNTIME_READY_ADAPTER" # ---------- Bluetooth service / daemon ---------- log_info "Checking if bluetoothd (or bluetooth.service) is running..." if btsvcactive; then - log_pass "Bluetooth service/daemon active." + test_result_record "PASS" "Bluetooth service/daemon active" else - log_fail "Bluetooth service/daemon NOT active." - inc_fail + test_result_record "FAIL" "Bluetooth service/daemon is not active" fi # ---------- DT node / compatible ---------- @@ -109,10 +161,11 @@ if dt_confirm_node_or_compatible_all \ "qcom,bluetooth" \ "pcie-m2-e-connector" then - log_pass "DT node/compatible for BT or an M.2 E-key connector is present." + test_result_record "PASS" "DT node/compatible for BT or an M.2 E-key connector is present" else - dt_runtime_adapter="" - if command -v bt_select_usable_adapter >/dev/null 2>&1; then + dt_runtime_adapter="$ADAPTER" + if [ -z "$dt_runtime_adapter" ] && \ + command -v bt_select_usable_adapter >/dev/null 2>&1; then dt_runtime_adapter="$(bt_select_usable_adapter 2>/dev/null || true)" fi @@ -121,15 +174,15 @@ else else log_warn "No static BT DT node or M.2 E-key connector found, deferring the verdict to the authoritative KMD, HCI, service, firmware, and controller checks below (GH#533)." fi - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) fi # ---------- Firmware presence ---------- if fw_dir="$(btfwpresent 2>/dev/null)"; then - log_pass "Firmware present in: $fw_dir" + test_result_record "PASS" "Firmware present in: $fw_dir" else log_warn "No BT firmware matching msbtfw*/msnv* or cmbtfw*/cmnv* found under standard firmware paths." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) fi # ----------------------------- @@ -161,15 +214,16 @@ if command -v btfwloaded >/dev/null 2>&1; then rc=$? case "$rc" in 0) - log_pass "Firmware load/setup appears completed (kernel log)." + test_result_record "PASS" "Firmware load/setup appears completed in the kernel log" ;; 2) log_warn "Firmware load/setup completed after retry, transient errors seen earlier (kernel log)." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) + test_result_record "PASS" "Firmware load/setup completed after a transient retry" ;; *) runtime_bt_ok=1 - fallback_adapter="$BT_ADAPTER" + fallback_adapter="$ADAPTER" if [ -z "$fallback_adapter" ] && findhcisysfs >/dev/null 2>&1; then fallback_adapter="$(findhcisysfs 2>/dev/null || true)" @@ -192,53 +246,52 @@ if command -v btfwloaded >/dev/null 2>&1; then if [ "$runtime_bt_ok" -eq 1 ]; then log_warn "No retained BT firmware-load signature found, but BT runtime state is healthy." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) else - log_fail "Firmware load/setup does NOT look clean and BT runtime state is also unhealthy." - inc_fail + test_result_record "FAIL" "Firmware load/setup is incomplete and Bluetooth runtime state is unhealthy" fi ;; esac else # No SKIP: continue test, just warn. log_warn "btfwloaded() helper not available, firmware-load kernel-log validation not performed." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) fi # ---------- Kernel modules / KMD ---------- if btkmdpresent; then - log_pass "Kernel BT driver stack present (bluetooth/hci_uart/btqca or built-in)." + test_result_record "PASS" "Kernel BT driver stack is present" else - log_fail "Kernel BT driver stack not detected (no bluetooth/hci_uart/btqca in sysfs/dmesg)." - inc_fail + test_result_record "FAIL" "Kernel BT driver stack was not detected" fi # ---------- HCI presence ---------- if bthcipresent; then - log_pass "HCI present in /sys/class/bluetooth." + test_result_record "PASS" "HCI is present in /sys/class/bluetooth" else - log_fail "No /sys/class/bluetooth/hci* found (HCI not up)." - inc_fail + test_result_record "FAIL" "No HCI adapter was found in /sys/class/bluetooth" fi # --- Bluetooth service / daemon check via btsvcactive() --- if btsvcactive; then - log_pass "Bluetooth service active (systemd bluetooth.service or bluetoothd)." + test_result_record "PASS" "Bluetooth service remains active" else log_warn "Bluetooth service is not active (bluetooth.service inactive and bluetoothd not running)." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) fi # ----------------------------- # Detect adapter (CLI/ENV > auto-detect) # ----------------------------- -if [ -n "$BT_ADAPTER" ]; then - ADAPTER="$BT_ADAPTER" - log_info "Using adapter from BT_ADAPTER/CLI: $ADAPTER" -elif findhcisysfs >/dev/null 2>&1; then - ADAPTER="$(findhcisysfs 2>/dev/null || true)" -else - ADAPTER="" +if [ -z "$ADAPTER" ]; then + if [ -n "$BT_ADAPTER" ]; then + ADAPTER="$BT_ADAPTER" + log_info "Using adapter from BT_ADAPTER/CLI: $ADAPTER" + elif findhcisysfs >/dev/null 2>&1; then + ADAPTER="$(findhcisysfs 2>/dev/null || true)" + else + ADAPTER="" + fi fi if [ -n "$ADAPTER" ]; then @@ -251,21 +304,19 @@ fi if [ -z "$ADAPTER" ]; then log_warn "No HCI adapter found." - - if [ "$FAIL_COUNT" -gt 0 ]; then - echo "$TESTNAME FAIL" > "$RES_FILE" + + if [ "$TEST_RESULT_FAIL_COUNT" -gt 0 ]; then + test_result_finish else - echo "$TESTNAME SKIP" > "$RES_FILE" + test_result_finish "SKIP" "$TESTNAME SKIP - no HCI adapter was found" fi - exit 0 fi # ---------- BD address sanity check ---------- if [ -n "$ADAPTER" ]; then if btbdok "$ADAPTER"; then - log_pass "BD address sane for $ADAPTER (not all zeros)." + test_result_record "PASS" "BD address is valid for $ADAPTER" else - log_fail "BD address invalid or all zeros for $ADAPTER." - inc_fail + test_result_record "FAIL" "BD address is invalid or all zeros for $ADAPTER" fi fi @@ -278,11 +329,11 @@ if [ -n "$ADAPTER" ]; then # For this infra test we treat this as WARN, not FAIL: # stack is otherwise OK (firmware, KMD, HCI, BD). log_warn "No controller in 'bluetoothctl list' (controller not fully instantiated)." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) fi else log_warn "Controller visibility not checked (no adapter determined)." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) fi # ---------- Optional: dump some useful diagnostics ---------- @@ -291,12 +342,15 @@ if command -v hciconfig >/dev/null 2>&1; then hciconfig -a || true else log_warn "hciconfig command not available." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) fi log_info "=== bluetoothctl list (controllers) ===" -out="$(bluetoothctl list 2>/dev/null | sanitize_bt_output || true)" +out="$( + run_with_timeout 3 bluetoothctl list 2>/dev/null \ + | sanitize_bt_output || true +)" if printf '%s\n' "$out" | grep -qi '^[[:space:]]*Controller[[:space:]]'; then # Non-interactive worked print what we got printf '%s\n' "$out" @@ -312,12 +366,5 @@ log_info "=== lsmod (subset: BT stack) ===" lsmod 2>/dev/null | grep -E '^(bluetooth|hci_uart|btqca|btbcm|rfkill|cfg80211)\b' || true # ---------- Final result ---------- -log_info "Completed with WARN=${WARN_COUNT}, FAIL=${FAIL_COUNT}" - -if [ "$FAIL_COUNT" -gt 0 ]; then - echo "$TESTNAME FAIL" > "$RES_FILE" -else - echo "$TESTNAME PASS" > "$RES_FILE" -fi - -exit 0 +log_info "Completed with WARN=${WARN_COUNT}, FAIL=${TEST_RESULT_FAIL_COUNT}" +test_result_finish diff --git a/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/BT_ON_OFF.yaml b/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/BT_ON_OFF.yaml index 48683d5e..146e23a0 100644 --- a/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/BT_ON_OFF.yaml +++ b/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/BT_ON_OFF.yaml @@ -13,10 +13,13 @@ params: BT_POWER_ON_ATTEMPTS: "2" BT_POWER_ON_RETRY_DELAY: "10" BT_RESTART_SERVICE_ON_RETRY: "1" + BT_RUNTIME_READY_WAIT: "20" + BT_RUNTIME_RECOVERY_WAIT: "40" + BT_RUNTIME_RECOVERY_ATTEMPTS: "2" run: steps: - REPO_PATH=$PWD - cd Runner/suites/Connectivity/Bluetooth/BT_ON_OFF - - ./run.sh --adapter "${BT_ADAPTER}" --power-cycle-delay "${BT_POWER_CYCLE_DELAY}" --power-on-attempts "${BT_POWER_ON_ATTEMPTS}" --power-on-retry-delay "${BT_POWER_ON_RETRY_DELAY}" --restart-service-on-retry "${BT_RESTART_SERVICE_ON_RETRY}" || true + - ./run.sh --power-cycle-delay "${BT_POWER_CYCLE_DELAY}" --power-on-attempts "${BT_POWER_ON_ATTEMPTS}" --power-on-retry-delay "${BT_POWER_ON_RETRY_DELAY}" --restart-service-on-retry "${BT_RESTART_SERVICE_ON_RETRY}" || true - $REPO_PATH/Runner/utils/send-to-lava.sh BT_ON_OFF.res diff --git a/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/run.sh b/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/run.sh index c2df8b9b..d1b80605 100755 --- a/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/run.sh +++ b/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/run.sh @@ -4,10 +4,10 @@ # SPDX-License-Identifier: BSD-3-Clause # BT_ON_OFF - Basic Bluetooth power toggle validation (non-expect version) -# Robustly find and source init_env +# ---------- Repo env + helpers ---------- SCRIPT_DIR="$( - cd "$(dirname "$0")" || exit 1 - pwd + cd "$(dirname "$0")" || exit 1 + pwd )" INIT_ENV="" SEARCH="$SCRIPT_DIR" @@ -26,12 +26,15 @@ if [ -z "$INIT_ENV" ]; then fi # Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. if [ -z "${__INIT_ENV_LOADED:-}" ]; then # shellcheck disable=SC1090 . "$INIT_ENV" __INIT_ENV_LOADED=1 fi +# shellcheck disable=SC1090 +. "$INIT_ENV" # shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" # shellcheck disable=SC1091 @@ -47,90 +50,126 @@ BT_POWER_CYCLE_DELAY="${BT_POWER_CYCLE_DELAY:-10}" BT_POWER_ON_ATTEMPTS="${BT_POWER_ON_ATTEMPTS:-2}" BT_POWER_ON_RETRY_DELAY="${BT_POWER_ON_RETRY_DELAY:-10}" BT_RESTART_SERVICE_ON_RETRY="${BT_RESTART_SERVICE_ON_RETRY:-1}" +BT_RUNTIME_READY_WAIT="${BT_RUNTIME_READY_WAIT:-20}" +BT_RUNTIME_RECOVERY_WAIT="${BT_RUNTIME_RECOVERY_WAIT:-40}" +BT_RUNTIME_RECOVERY_ATTEMPTS="${BT_RUNTIME_RECOVERY_ATTEMPTS:-2}" + +usage() { + cat <&2 + return 2 + fi + + case "$1" in + --adapter) + BT_ADAPTER="$2" + ;; + --power-cycle-delay) + BT_POWER_CYCLE_DELAY="$2" + ;; + --power-on-attempts) + BT_POWER_ON_ATTEMPTS="$2" + ;; + --power-on-retry-delay) + BT_POWER_ON_RETRY_DELAY="$2" + ;; + --restart-service-on-retry) + BT_RESTART_SERVICE_ON_RETRY="$2" + ;; + esac + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + log_error "Unknown argument: $1" + usage >&2 + return 2 + ;; + esac + done +} -case "$BT_POWER_ON_RETRY_DELAY" in - ''|*[!0-9]*) - BT_POWER_ON_RETRY_DELAY=10 - ;; -esac +parse_args "$@" || exit $? + +for positive_value in \ + "$BT_POWER_CYCLE_DELAY" \ + "$BT_POWER_ON_ATTEMPTS" \ + "$BT_POWER_ON_RETRY_DELAY" \ + "$BT_RUNTIME_READY_WAIT" \ + "$BT_RUNTIME_RECOVERY_WAIT" \ + "$BT_RUNTIME_RECOVERY_ATTEMPTS" +do + if ! is_unsigned_number "$positive_value" || [ "$positive_value" -eq 0 ]; then + log_error "Bluetooth retry counts and wait values must be positive integers" + exit 2 + fi +done case "$BT_RESTART_SERVICE_ON_RETRY" in 0|1) ;; *) - BT_RESTART_SERVICE_ON_RETRY=1 + log_error "--restart-service-on-retry must be 0 or 1" + exit 2 ;; esac -if [ "$BT_POWER_ON_ATTEMPTS" -lt 1 ] 2>/dev/null; then - BT_POWER_ON_ATTEMPTS=1 -fi - -TESTNAME="BT_ON_OFF" -testpath="$(find_test_case_by_name "$TESTNAME")" || { - log_fail "$TESTNAME : Test directory not found." - echo "$TESTNAME FAIL" > "./$TESTNAME.res" +testpath="$(find_test_case_by_name "BT_ON_OFF")" || { + log_fail "BT_ON_OFF FAIL - test directory not found" + printf '%s\n' "BT_ON_OFF FAIL" >./BT_ON_OFF.res exit 1 } cd "$testpath" || exit 1 -res_file="./$TESTNAME.res" -rm -f "$res_file" +TESTNAME="BT_ON_OFF" +RES_FILE="./$TESTNAME.res" +test_result_init "$TESTNAME" "$RES_FILE" || exit 1 log_info "------------------------------------------------------------" log_info "Starting $TESTNAME Testcase" log_info "Config: BT_POWER_CYCLE_DELAY=${BT_POWER_CYCLE_DELAY}s BT_POWER_ON_ATTEMPTS=$BT_POWER_ON_ATTEMPTS BT_POWER_ON_RETRY_DELAY=${BT_POWER_ON_RETRY_DELAY}s BT_RESTART_SERVICE_ON_RETRY=$BT_RESTART_SERVICE_ON_RETRY" if ! bt_prepare_ubuntu_stack; then - log_fail "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" fi log_info "Checking dependency: bluetoothctl" # Verify that all necessary dependencies are available. -check_dependencies bluetoothctl pgrep +if ! check_dependencies bluetoothctl pgrep; then + test_result_finish "SKIP" "$TESTNAME SKIP - required Bluetooth tools are unavailable" +fi + +log_info "Ensuring Bluetooth runtime readiness before power-cycle validation" +if ! bt_ensure_runtime_ready \ + "$BT_ADAPTER" \ + "$BT_RUNTIME_READY_WAIT" \ + "$BT_RUNTIME_RECOVERY_WAIT" \ + "$BT_RUNTIME_RECOVERY_ATTEMPTS"; then + test_result_finish "FAIL" "Bluetooth runtime remained unusable after bounded recovery attempts" +fi + +ADAPTER="$BT_RUNTIME_READY_ADAPTER" +test_result_record "PASS" "Bluetooth runtime is ready with usable adapter $ADAPTER" log_info "Checking if bluetoothd is running..." MAX_RETRIES=3 @@ -149,16 +188,15 @@ while [ "$retry" -lt "$MAX_RETRIES" ]; do done if [ "$retry" -eq "$MAX_RETRIES" ]; then - log_fail "Bluetooth daemon not detected after ${MAX_RETRIES} attempts." - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "Bluetooth daemon was not detected after ${MAX_RETRIES} attempts" fi +test_result_record "PASS" "Bluetooth daemon is running" + # ----------------------------- # Detect adapter with precedence: CLI/ENV > auto-detect # ----------------------------- if [ -n "$BT_ADAPTER" ]; then - ADAPTER="$BT_ADAPTER" log_info "Using adapter from BT_ADAPTER/CLI: $ADAPTER" if command -v bt_adapter_is_usable >/dev/null 2>&1; then @@ -169,14 +207,6 @@ if [ -n "$BT_ADAPTER" ]; then fi else bt_log_hci_candidates || true - - if command -v bt_select_usable_adapter >/dev/null 2>&1; then - ADAPTER="$(bt_select_usable_adapter 2>/dev/null || true)" - elif findhcisysfs >/dev/null 2>&1; then - ADAPTER="$(findhcisysfs 2>/dev/null || true)" - else - ADAPTER="" - fi fi if [ -n "$ADAPTER" ]; then @@ -187,17 +217,21 @@ if [ -n "$ADAPTER" ]; then fi fi +if [ -z "$ADAPTER" ]; then + test_result_finish "FAIL" "No usable Bluetooth HCI adapter was found after runtime recovery" +fi + # Warn/diag if non-interactive "bluetoothctl list" is empty. This is non-fatal. btwarniflistempty "$ADAPTER" || true # Ensure controller is visible to bluetoothctl, trying public-addr if needed. if ! bt_ensure_controller_visible "$ADAPTER"; then btloghcidiag "$ADAPTER" failure "$testpath" || true - log_warn "SKIP — no controller visible to bluetoothctl (HCI RAW/DOWN or attach incomplete)." - echo "$TESTNAME SKIP" > "$res_file" - exit 0 + test_result_finish "FAIL" "No controller is visible to bluetoothctl after runtime recovery" fi +test_result_record "PASS" "Bluetooth controller is visible to bluetoothctl" + # Read initial power state. initial_power="$(btgetpower "$ADAPTER" 2>/dev/null || true)" [ -z "$initial_power" ] && initial_power="unknown" @@ -206,22 +240,18 @@ log_info "Initial Powered = $initial_power" # ---- Power OFF test ---- log_info "Powering OFF..." if ! btpower "$ADAPTER" off; then - log_fail "btpower($ADAPTER, off) failed (command-level error)." btloghcidiag "$ADAPTER" failure "$testpath" || true - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "btpower($ADAPTER, off) failed at command level" fi after_off="$(btgetpower "$ADAPTER" 2>/dev/null || true)" [ -z "$after_off" ] && after_off="unknown" if [ "$after_off" = "no" ]; then - log_pass "Post-OFF verification: Powered=no (as expected)." + test_result_record "PASS" "Post-OFF verification reported Powered=no" else - log_fail "Post-OFF verification failed (Powered=$after_off)." btloghcidiag "$ADAPTER" failure "$testpath" || true - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "Post-OFF verification failed with Powered=$after_off" fi # ---- Power ON test ---- @@ -256,20 +286,12 @@ while [ "$on_attempt" -le "$BT_POWER_ON_ATTEMPTS" ]; do log_warn "Preparing controlled Power ON retry after ${BT_POWER_ON_RETRY_DELAY}s" sleep "$BT_POWER_ON_RETRY_DELAY" - if command -v rfkill >/dev/null 2>&1; then + if [ "$BT_RESTART_SERVICE_ON_RETRY" -eq 1 ] 2>/dev/null; then + bt_recover_runtime "$ADAPTER" || \ + log_warn "Controlled Bluetooth recovery did not complete before retry" + elif command -v rfkill >/dev/null 2>&1; then log_info "Running rfkill unblock bluetooth before retry" rfkill unblock bluetooth >/dev/null 2>&1 || true - elif command -v rfkillunblocksysfs >/dev/null 2>&1; then - log_info "Running rfkillunblocksysfs before retry" - rfkillunblocksysfs >/dev/null 2>&1 || true - else - log_warn "No rfkill unblock helper available before retry" - fi - - if [ "$BT_RESTART_SERVICE_ON_RETRY" -eq 1 ] 2>/dev/null && command -v systemctl >/dev/null 2>&1; then - log_info "Restarting bluetooth.service before retry" - systemctl restart bluetooth >/dev/null 2>&1 || log_warn "systemctl restart bluetooth failed" - sleep 3 fi if command -v bt_ensure_controller_visible >/dev/null 2>&1; then @@ -287,15 +309,12 @@ if [ "$on_success" -eq 1 ]; then btwarniflistempty "$ADAPTER" || true - log_pass "Post-ON verification: Powered=yes (as expected)." - echo "$TESTNAME PASS" > "$res_file" - exit 0 + test_result_record "PASS" "Post-ON verification reported Powered=yes" + test_result_finish fi after_on="$(btgetpower "$ADAPTER" 2>/dev/null || true)" [ -z "$after_on" ] && after_on="unknown" -log_fail "Post-ON verification failed after $BT_POWER_ON_ATTEMPTS attempt(s) (Powered=$after_on)." btloghcidiag "$ADAPTER" failure "$testpath" || true -echo "$TESTNAME FAIL" > "$res_file" -exit 0 +test_result_finish "FAIL" "Post-ON verification failed after $BT_POWER_ON_ATTEMPTS attempts with Powered=$after_on" From 696c8c8fcd3ddccf62472bd2b9d5f2e9b84ad3ca Mon Sep 17 00:00:00 2001 From: Srikanth Muppandam Date: Wed, 26 Aug 2026 17:27:34 +0530 Subject: [PATCH 3/3] bluetooth: stabilize scan and pairing tests The scan and pairing suites could start while the Bluetooth service and QCA controller were only partially initialized. A missing controller was treated as a skip in some paths, while repeated unbounded setup calls made first-attempt failures expensive. Require bounded runtime recovery before scanning or pairing, reuse the validated adapter, and keep result files machine-readable through the shared result API. Normalize argument handling and LAVA parameters so recovery and scan timing can be tuned consistently. Signed-off-by: Srikanth Muppandam --- .../Bluetooth/BT_SCAN/BT_SCAN.yaml | 13 +- .../Connectivity/Bluetooth/BT_SCAN/run.sh | 178 ++++++++----- .../Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR.yaml | 19 +- .../Bluetooth/BT_SCAN_PAIR/run.sh | 249 ++++++++++++------ Runner/utils/lib_bluetooth.sh | 39 ++- 5 files changed, 326 insertions(+), 172 deletions(-) diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN/BT_SCAN.yaml b/Runner/suites/Connectivity/Bluetooth/BT_SCAN/BT_SCAN.yaml index 36e15a03..1f8b7ad3 100644 --- a/Runner/suites/Connectivity/Bluetooth/BT_SCAN/BT_SCAN.yaml +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN/BT_SCAN.yaml @@ -7,10 +7,19 @@ metadata: scope: - functional +params: + BT_ADAPTER: "" + BT_TARGET_MAC: "" + BT_SCAN_SECONDS: "15" + BT_SCAN_RETRIES: "3" + BT_SCAN_RETRY_DELAY: "2" + BT_RUNTIME_READY_WAIT: "20" + BT_RUNTIME_RECOVERY_WAIT: "40" + BT_RUNTIME_RECOVERY_ATTEMPTS: "2" + run: steps: - REPO_PATH=$PWD - cd Runner/suites/Connectivity/Bluetooth/BT_SCAN/ - - ./run.sh || true + - ./run.sh --target-mac "${BT_TARGET_MAC}" --scan-seconds "${BT_SCAN_SECONDS}" --scan-retries "${BT_SCAN_RETRIES}" --scan-retry-delay "${BT_SCAN_RETRY_DELAY}" || true - $REPO_PATH/Runner/utils/send-to-lava.sh BT_SCAN.res - diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN/run.sh b/Runner/suites/Connectivity/Bluetooth/BT_SCAN/run.sh index d21b5b86..267de2ca 100755 --- a/Runner/suites/Connectivity/Bluetooth/BT_SCAN/run.sh +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN/run.sh @@ -24,12 +24,16 @@ if [ -z "$INIT_ENV" ]; then exit 1 fi +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. if [ -z "${__INIT_ENV_LOADED:-}" ]; then # shellcheck disable=SC1090 . "$INIT_ENV" __INIT_ENV_LOADED=1 fi +# shellcheck disable=SC1090 +. "$INIT_ENV" # shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" # shellcheck disable=SC1091 @@ -42,62 +46,116 @@ BT_TARGET_MAC="${BT_TARGET_MAC-}" BT_SCAN_SECONDS="${BT_SCAN_SECONDS:-15}" BT_SCAN_RETRIES="${BT_SCAN_RETRIES:-3}" BT_SCAN_RETRY_DELAY="${BT_SCAN_RETRY_DELAY:-2}" +BT_RUNTIME_READY_WAIT="${BT_RUNTIME_READY_WAIT:-20}" +BT_RUNTIME_RECOVERY_WAIT="${BT_RUNTIME_RECOVERY_WAIT:-40}" +BT_RUNTIME_RECOVERY_ATTEMPTS="${BT_RUNTIME_RECOVERY_ATTEMPTS:-2}" + +usage() { + cat <&2 + return 2 + fi + + case "$1" in + --adapter) + BT_ADAPTER="$2" + ;; + --target-mac) + BT_SCAN_TARGET_MAC="$2" + ;; + --scan-seconds) + BT_SCAN_SECONDS="$2" + ;; + --scan-retries) + BT_SCAN_RETRIES="$2" + ;; + --scan-retry-delay) + BT_SCAN_RETRY_DELAY="$2" + ;; + esac + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + log_error "Unknown argument: $1" + usage >&2 + return 2 + ;; + esac + done +} -while [ "$#" -gt 0 ]; do - case "$1" in - --adapter) - BT_ADAPTER="$2" - shift 2 - ;; - --target-mac) - BT_TARGET_MAC="$2" - shift 2 - ;; - --scan-seconds) - BT_SCAN_SECONDS="$2" - shift 2 - ;; - --scan-retries) - BT_SCAN_RETRIES="$2" - shift 2 - ;; - --scan-retry-delay) - BT_SCAN_RETRY_DELAY="$2" - shift 2 - ;; - *) - log_warn "Unknown argument ignored: $1" - shift 1 - ;; - esac +parse_args "$@" || exit $? + +for positive_value in \ + "$BT_SCAN_SECONDS" \ + "$BT_SCAN_RETRIES" \ + "$BT_SCAN_RETRY_DELAY" \ + "$BT_RUNTIME_READY_WAIT" \ + "$BT_RUNTIME_RECOVERY_WAIT" \ + "$BT_RUNTIME_RECOVERY_ATTEMPTS" +do + if ! is_unsigned_number "$positive_value" || [ "$positive_value" -eq 0 ]; then + log_error "Bluetooth scan retry counts and wait values must be positive integers" + exit 2 + fi done -TESTNAME="BT_SCAN" -testpath="$(find_test_case_by_name "$TESTNAME")" || { - log_fail "$TESTNAME : Test directory not found." - echo "$TESTNAME FAIL" > "./$TESTNAME.res" - exit 0 +testpath="$(find_test_case_by_name "BT_SCAN")" || { + log_fail "BT_SCAN FAIL - test directory not found" + printf '%s\n' "BT_SCAN FAIL" >./BT_SCAN.res + exit 1 } cd "$testpath" || exit 1 -res_file="./$TESTNAME.res" -rm -f "$res_file" +TESTNAME="BT_SCAN" +RES_FILE="./$TESTNAME.res" +test_result_init "$TESTNAME" "$RES_FILE" || exit 1 log_info "------------------------------------------------------------" log_info "Starting $TESTNAME Testcase" if ! bt_prepare_ubuntu_stack; then - log_fail "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" fi log_info "Checking dependencies: bluetoothctl pgrep" if ! check_dependencies bluetoothctl pgrep; then - echo "$TESTNAME SKIP" > "$res_file" - exit 0 + test_result_finish "SKIP" "$TESTNAME SKIP - required Bluetooth tools are unavailable" fi +log_info "Ensuring Bluetooth runtime readiness before scanning" +if ! bt_ensure_runtime_ready \ + "$BT_ADAPTER" \ + "$BT_RUNTIME_READY_WAIT" \ + "$BT_RUNTIME_RECOVERY_WAIT" \ + "$BT_RUNTIME_RECOVERY_ATTEMPTS"; then + test_result_finish "FAIL" "Bluetooth runtime remained unusable after bounded recovery attempts" +fi + +ADAPTER="$BT_RUNTIME_READY_ADAPTER" +test_result_record "PASS" "Bluetooth runtime is ready with usable adapter $ADAPTER" + # ----------------------------- # 1. Ensure bluetoothd is running # ----------------------------- @@ -117,21 +175,18 @@ while [ "$retry" -lt "$MAX_RETRIES" ]; do done if [ "$retry" -eq "$MAX_RETRIES" ]; then - log_fail "bluetoothd not detected after $MAX_RETRIES attempts." - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "bluetoothd was not detected after $MAX_RETRIES attempts" fi +test_result_record "PASS" "Bluetooth daemon is running" + # ----------------------------- # 2. Detect adapter (CLI/ENV > auto-detect) # ----------------------------- if [ -n "$BT_ADAPTER" ]; then - ADAPTER="$BT_ADAPTER" log_info "Using adapter from BT_ADAPTER/CLI: $ADAPTER" -elif findhcisysfs >/dev/null 2>&1; then - ADAPTER="$(findhcisysfs 2>/dev/null || true)" else - ADAPTER="" + log_info "Using adapter selected during runtime readiness: $ADAPTER" fi if [ -n "$ADAPTER" ]; then @@ -145,34 +200,30 @@ fi if [ -n "$ADAPTER" ]; then log_info "Using adapter: $ADAPTER" else - log_warn "No HCI adapter found; skipping test." - echo "$TESTNAME SKIP" > "$res_file" - exit 0 + test_result_finish "FAIL" "No usable HCI adapter was found after runtime recovery" fi # ----------------------------- # 3. Ensure controller is visible # ----------------------------- if ! bt_ensure_controller_visible "$ADAPTER"; then - log_warn "SKIP — controller not visible to bluetoothctl." - echo "$TESTNAME SKIP" > "$res_file" - exit 0 + test_result_finish "FAIL" "Controller is not visible to bluetoothctl after runtime recovery" fi +test_result_record "PASS" "Bluetooth controller is visible to bluetoothctl" + # ----------------------------- # 4. Ensure power is ON # ----------------------------- pw="$(btgetpower "$ADAPTER" 2>/dev/null || true)" if [ "$pw" = "yes" ]; then - log_pass "Power ON verified before scan." + test_result_record "PASS" "Power ON was verified before scanning" else log_info "Controller Power=$pw — enabling now..." if ! btpower "$ADAPTER" on; then - log_fail "Failed to power ON controller." - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "Failed to power ON the Bluetooth controller" fi - log_pass "Power ON successful." + test_result_record "PASS" "Bluetooth controller powered on successfully" fi # ----------------------------- @@ -210,9 +261,9 @@ if bt_scan_devices "$TARGET_MAC"; then log_info "Discovering state after scan attempts: $dstate_on" if [ -n "$TARGET_MAC" ]; then - log_pass "Target MAC $TARGET_MAC detected." + test_result_record "PASS" "Target MAC $TARGET_MAC was detected" else - log_pass "At least one Bluetooth device discovered." + test_result_record "PASS" "At least one Bluetooth device was discovered" fi else dstate_on="$(bt_get_discovering 2>/dev/null || true)" @@ -225,8 +276,7 @@ else log_fail "No Bluetooth devices discovered after scan attempts." fi - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "Bluetooth scan did not discover the required device" fi # ----------------------------- @@ -241,12 +291,10 @@ fi # Use lib helper to avoid repetitive log spam and handle 'unknown' cleanly. if bt_scan_poll_off 10 1; then # On minimal/ramdisk images bt_scan_poll_off may treat persistent 'unknown' as non-fatal. - log_pass "Scan OFF cleanup completed." + test_result_record "PASS" "Scan OFF cleanup completed" else # If you keep bt_scan_poll_off strict, this may still warn; not a test failure. log_warn "Scan OFF cleanup did not confirm Discovering=no (non-fatal)." fi -echo "$TESTNAME PASS" > "$res_file" -exit 0 - +test_result_finish diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR.yaml b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR.yaml index 53496de8..a88ca864 100644 --- a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR.yaml +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR.yaml @@ -8,16 +8,15 @@ metadata: - functional params: - # Max pairing attempts per device - - PAIR_RETRIES: 3 - # Max scan retries per device before pairing - - SCAN_ATTEMPTS: 2 - # Overrides direct MAC (same format as CLI argument) - - BT_MAC_ENV: "" - # Overrides device name - - BT_NAME_ENV: "" - # Comma-separated MACs or names allowed for pairing - - BT_WHITELIST_ENV: "" + PAIR_RETRIES: "3" + SCAN_ATTEMPTS: "2" + BT_MAC_ENV: "" + BT_NAME_ENV: "" + BT_WHITELIST_ENV: "" + BT_ADAPTER: "" + BT_RUNTIME_READY_WAIT: "20" + BT_RUNTIME_RECOVERY_WAIT: "40" + BT_RUNTIME_RECOVERY_ATTEMPTS: "2" run: steps: diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh index fc42d031..6292ca6a 100755 --- a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh @@ -1,8 +1,11 @@ #!/bin/sh # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause -# Robustly find and source init_env -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + pwd +)" INIT_ENV="" SEARCH="$SCRIPT_DIR" @@ -19,73 +22,149 @@ if [ -z "$INIT_ENV" ]; then exit 1 fi -if [ -z "$__INIT_ENV_LOADED" ]; then +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then # shellcheck disable=SC1090 . "$INIT_ENV" + __INIT_ENV_LOADED=1 fi -# shellcheck disable=SC1090,SC1091 +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" -# shellcheck disable=SC1090,SC1091 +# shellcheck disable=SC1091 . "$TOOLS/lib_bluetooth.sh" -TESTNAME="BT_SCAN_PAIR" -test_path=$(find_test_case_by_name "$TESTNAME") || { - log_fail "$TESTNAME : Test directory not found." - echo "$TESTNAME FAIL" > "./$TESTNAME.res" - exit 0 +test_path=$(find_test_case_by_name "BT_SCAN_PAIR") || { + log_fail "BT_SCAN_PAIR FAIL - test directory not found" + printf '%s\n' "BT_SCAN_PAIR FAIL" >./BT_SCAN_PAIR.res + exit 1 } if ! cd "$test_path"; then - log_fail "$TESTNAME : Failed to cd into test directory: $test_path" - echo "$TESTNAME FAIL" > "./$TESTNAME.res" - exit 0 + log_fail "BT_SCAN_PAIR FAIL - failed to enter test directory $test_path" + printf '%s\n' "BT_SCAN_PAIR FAIL" >./BT_SCAN_PAIR.res + exit 1 fi +TESTNAME="BT_SCAN_PAIR" RES_FILE="./$TESTNAME.res" -rm -f "$RES_FILE" +test_result_init "$TESTNAME" "$RES_FILE" || exit 1 log_info "------------------------------------------------------------" log_info "Starting $TESTNAME Testcase" -# Capture BT_MAC from environment (LAVA secret) *before* we touch BT_MAC locally -BT_ENV_MAC="" -if [ -n "${BT_MAC:-}" ]; then - BT_ENV_MAC="$BT_MAC" -fi - # Defaults PAIR_RETRIES="${PAIR_RETRIES:-3}" +BT_RUNTIME_READY_WAIT="${BT_RUNTIME_READY_WAIT:-20}" +BT_RUNTIME_RECOVERY_WAIT="${BT_RUNTIME_RECOVERY_WAIT:-40}" +BT_RUNTIME_RECOVERY_ATTEMPTS="${BT_RUNTIME_RECOVERY_ATTEMPTS:-2}" +BT_ADAPTER="${BT_ADAPTER:-}" -BT_NAME="" +BT_ENV_MAC="${BT_MAC_ENV:-${BT_MAC:-}}" +BT_NAME="${BT_NAME_ENV:-}" BT_MAC="" -WHITELIST="" +WHITELIST="${BT_WHITELIST_ENV:-}" -# ------------------------- -# CLI parsing -# ------------------------- -if [ -n "$1" ]; then - if echo "$1" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then - # CLI MAC has highest priority - BT_MAC="$1" +usage() { + cat <&2 + return 2 + fi + + case "$1" in + --adapter) + BT_ADAPTER="$2" + ;; + --target) + set_pair_target "$2" + ;; + --whitelist) + WHITELIST="$2" + ;; + esac + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + --*) + log_error "Unknown argument: $1" + usage >&2 + return 2 + ;; + *) + positional_count=$((positional_count + 1)) + case "$positional_count" in + 1) + set_pair_target "$1" + ;; + 2) + WHITELIST="$1" + ;; + *) + log_error "Too many positional arguments" + usage >&2 + return 2 + ;; + esac + shift + ;; + esac + done +} + +parse_args "$@" || exit $? + +for positive_value in \ + "$PAIR_RETRIES" \ + "$BT_RUNTIME_READY_WAIT" \ + "$BT_RUNTIME_RECOVERY_WAIT" \ + "$BT_RUNTIME_RECOVERY_ATTEMPTS" +do + if ! is_unsigned_number "$positive_value" || [ "$positive_value" -eq 0 ]; then + log_error "Bluetooth pairing retry counts and wait values must be positive integers" + exit 2 + fi +done # If BT_MAC not set by CLI, fall back to BT_ENV_MAC (LAVA export) -if [ -z "$BT_MAC" ] && [ -n "$BT_ENV_MAC" ] && \ - echo "$BT_ENV_MAC" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then +if [ -z "$BT_MAC" ] && [ -z "$BT_NAME" ] && [ -n "$BT_ENV_MAC" ] && \ + printf '%s\n' "$BT_ENV_MAC" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then BT_MAC="$BT_ENV_MAC" fi # Optionally: if BT_MAC still empty and whitelist itself is a MAC, treat it as BT_MAC -if [ -z "$BT_MAC" ] && [ -n "$WHITELIST" ] && \ - echo "$WHITELIST" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then +if [ -z "$BT_MAC" ] && [ -z "$BT_NAME" ] && [ -n "$WHITELIST" ] && \ + printf '%s\n' "$WHITELIST" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then BT_MAC="$WHITELIST" fi @@ -95,22 +174,28 @@ fi # Skip if no MAC/name and no list file if [ -z "$BT_MAC" ] && [ -z "$BT_NAME" ] && [ ! -f "./bt_device_list.txt" ]; then - log_warn "No BT_MAC/BT_NAME and no bt_device_list.txt found. Skipping test." - echo "$TESTNAME SKIP" > "$RES_FILE" - exit 0 + test_result_finish "SKIP" "No Bluetooth target and no bt_device_list.txt were provided" fi if ! bt_prepare_ubuntu_stack; then - log_fail "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 + test_result_finish "FAIL" "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" fi -check_dependencies bluetoothctl rfkill expect hciconfig || { - log_warn "Missing required tools; skipping $TESTNAME." - echo "$TESTNAME SKIP" > "$RES_FILE" - exit 0 -} +if ! check_dependencies bluetoothctl rfkill expect hciconfig; then + test_result_finish "SKIP" "$TESTNAME SKIP - required Bluetooth tools are unavailable" +fi + +log_info "Ensuring Bluetooth runtime readiness before pairing" +if ! bt_ensure_runtime_ready \ + "${BT_ADAPTER:-}" \ + "$BT_RUNTIME_READY_WAIT" \ + "$BT_RUNTIME_RECOVERY_WAIT" \ + "$BT_RUNTIME_RECOVERY_ATTEMPTS"; then + test_result_finish "FAIL" "Bluetooth runtime remained unusable after bounded recovery attempts" +fi + +BT_ADAPTER="$BT_RUNTIME_READY_ADAPTER" +test_result_record "PASS" "Bluetooth runtime is ready with usable adapter $BT_ADAPTER" # shellcheck disable=SC2317 # cleanup is invoked via trap cleanup_bt_test() { @@ -124,35 +209,34 @@ trap cleanup_bt_test EXIT rfkill unblock bluetooth 2>/dev/null || true # Detect adapter (no hardcoded hci0) -BT_ADAPTER="${BT_ADAPTER:-}" if [ -z "$BT_ADAPTER" ]; then - BT_ADAPTER="$(listhcis | head -n1)" + if command -v bt_select_usable_adapter >/dev/null 2>&1; then + BT_ADAPTER="$(bt_select_usable_adapter 2>/dev/null || true)" + else + BT_ADAPTER="$(listhcis | head -n1)" + fi fi if [ -z "$BT_ADAPTER" ]; then - log_fail "No Bluetooth HCI adapter found under /sys/class/bluetooth; cannot run $TESTNAME." - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 + test_result_finish "FAIL" "No usable Bluetooth HCI adapter was found after runtime recovery" fi log_info "Detected Bluetooth adapter: $BT_ADAPTER" # Ensure controller is visible to bluetoothctl (public-addr bootstrap) if bt_ensure_controller_visible "$BT_ADAPTER"; then - log_info "Bluetooth controller is visible to bluetoothctl after bt_ensure_controller_visible." + test_result_record "PASS" "Bluetooth controller is visible to bluetoothctl" else - log_fail "Bluetooth controller is not visible to bluetoothctl after bt_ensure_controller_visible." - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 + test_result_finish "FAIL" "Bluetooth controller is not visible to bluetoothctl after runtime recovery" fi -# Power on adapter via existing helper; do this regardless of HCICONFIG state. +# Power on adapter via the existing helper regardless of hciconfig state. if ! btpower "$BT_ADAPTER" on; then - log_fail "Failed to power on adapter $BT_ADAPTER via btpower (RF-kill/firmware issue?)." - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 + test_result_finish "FAIL" "Failed to power on adapter $BT_ADAPTER" fi +test_result_record "PASS" "Bluetooth adapter $BT_ADAPTER is powered on" + # Optional debug: show BD address and confirm firmware/driver presence bdaddr="$(btgetbdaddr "$BT_ADAPTER" 2>/dev/null || true)" [ -n "$bdaddr" ] && log_info "Adapter $BT_ADAPTER BD_ADDR=$bdaddr" @@ -162,7 +246,7 @@ if ! btkmdpresent; then fi if ! btfwpresent >/dev/null 2>&1; then - log_warn "No obvious Bluetooth firmware files found (btfwpresent); continuing anyway." + log_warn "No obvious Bluetooth firmware files found with btfwpresent, continuing anyway." fi # Remove any previously paired devices to start clean @@ -171,10 +255,9 @@ bt_remove_all_paired_devices # Helper: l2ping link verification verify_link() { mac="$1" - if bt_l2ping_check "$mac" "$RES_FILE"; then - log_pass "l2ping link check succeeded for $mac" - echo "$TESTNAME PASS" > "$RES_FILE" - exit 0 + if bt_l2ping_check "$mac" "./l2ping.log"; then + test_result_record "PASS" "l2ping link check succeeded for $mac" + test_result_finish else log_warn "l2ping link check failed for $mac" fi @@ -191,12 +274,12 @@ if [ -n "$BT_MAC" ]; then bt_cleanup_paired_device "$BT_MAC" if bt_pair_with_mac "$BT_MAC"; then - log_info "Pair succeeded; attempting post-pair connect to $BT_MAC" + log_info "Pair succeeded, attempting post-pair connect to $BT_MAC" if bt_post_pair_connect "$BT_MAC"; then log_pass "Post-pair connect succeeded for $BT_MAC" verify_link "$BT_MAC" else - log_warn "Post-pair connect failed; trying l2ping fallback for $BT_MAC" + log_warn "Post-pair connect failed, trying l2ping fallback for $BT_MAC" verify_link "$BT_MAC" bt_cleanup_paired_device "$BT_MAC" fi @@ -206,9 +289,7 @@ if [ -n "$BT_MAC" ]; then done log_warn "Exhausted direct pairing attempts for $BT_MAC" - log_fail "Direct pairing failed for ${BT_MAC:-$BT_NAME}" - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 + test_result_finish "FAIL" "Direct pairing failed for ${BT_MAC:-$BT_NAME}" fi # ------------------------- @@ -217,14 +298,16 @@ fi if [ -z "$BT_MAC" ] && [ -z "$BT_NAME" ] && [ -f "./bt_device_list.txt" ]; then # Skip if list is empty or only comments if ! grep -v -e '^[[:space:]]*#' -e '^[[:space:]]*$' bt_device_list.txt | grep -q .; then - log_warn "bt_device_list.txt is empty or only comments. Skipping test." - echo "$TESTNAME SKIP" > "$RES_FILE" - exit 0 + test_result_finish "SKIP" "bt_device_list.txt is empty or contains only comments" fi log_info "Using fallback device list in bt_device_list.txt" while IFS= read -r line || [ -n "$line" ]; do - case "$line" in ''|\#*) continue ;; esac + case "$line" in + ''|\#*) + continue + ;; + esac # split into MAC and NAME (simple space-separated) IFS=' ' read -r MAC NAME < "$RES_FILE" - exit 0 + test_result_finish "FAIL" "All fallback devices from bt_device_list.txt failed" fi # Should never reach here -log_fail "No execution path matched; exiting with FAIL." -echo "$TESTNAME FAIL" > "$RES_FILE" -exit 0 +test_result_finish "FAIL" "No Bluetooth pairing execution path matched" diff --git a/Runner/utils/lib_bluetooth.sh b/Runner/utils/lib_bluetooth.sh index edd6218c..48f3acd4 100755 --- a/Runner/utils/lib_bluetooth.sh +++ b/Runner/utils/lib_bluetooth.sh @@ -1799,6 +1799,7 @@ bt_set_scan() { off) timeout="${BT_SCAN_OFF_TIMEOUT:-5}" + discovering_state="" if command -v log_info >/dev/null 2>&1; then log_info "bt_set_scan(off): running 'bluetoothctl --timeout $timeout scan off'" @@ -1813,22 +1814,31 @@ bt_set_scan() { done fi - # Treat "already stopped" as success too + # Treat an explicitly confirmed stop as success. if printf '%s\n' "$out" | grep -q "Discovery stopped"; then sleep 1 return 0 fi if printf '%s\n' "$out" | grep -qi "Failed to stop discovery"; then - if command -v log_info >/dev/null 2>&1; then - log_info "bt_set_scan(off): BlueZ reported stop failure. Discovering may already be stopped, treating as non-fatal" + discovering_state="$(bt_is_discovering 2>/dev/null || true)" + [ -n "$discovering_state" ] || discovering_state="unknown" + + if [ "$discovering_state" = "no" ]; then + if command -v log_info >/dev/null 2>&1; then + log_info "bt_set_scan(off): discovery is already stopped, treating scan off as success" + fi + sleep 1 + return 0 fi - sleep 1 - return 0 fi # Fallback: interactive bluetoothctl (needed on minimal/ramdisk) if command -v log_warn >/dev/null 2>&1; then - log_warn "bt_set_scan(off): non-interactive scan off did not confirm stop; falling back to interactive bluetoothctl." + if [ -n "$discovering_state" ]; then + log_warn "bt_set_scan(off): BlueZ failed to stop discovery and state is $discovering_state, falling back to interactive bluetoothctl" + else + log_warn "bt_set_scan(off): non-interactive scan off did not confirm stop, falling back to interactive bluetoothctl" + fi fi out2="$(btctl_script "scan off" "quit" 2>/dev/null | sanitize_bt_output || true)" @@ -1837,11 +1847,20 @@ bt_set_scan() { return 0 fi if printf '%s\n' "$out2" | grep -qi "Failed to stop discovery"; then - if command -v log_info >/dev/null 2>&1; then - log_info "bt_set_scan(off): discovery already stopped, treating as success" + discovering_state="$(bt_is_discovering 2>/dev/null || true)" + [ -n "$discovering_state" ] || discovering_state="unknown" + + if [ "$discovering_state" = "no" ]; then + if command -v log_info >/dev/null 2>&1; then + log_info "bt_set_scan(off): discovery is already stopped, treating scan off as success" + fi + sleep 1 + return 0 + fi + + if command -v log_warn >/dev/null 2>&1; then + log_warn "bt_set_scan(off): interactive scan off failed and discovery state is $discovering_state" fi - sleep 1 - return 0 fi sleep 1