Skip to content

[BT][Test] Implement BT_SCAN_PAIR test with pairing retries, whitelist, and expect automation #99

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
# SPDX-License-Identifier: BSD-3-Clause-Clear
# Source init_env and functestlib.sh

# Robustly find and source init_env
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
INIT_ENV=""
SEARCH="$SCRIPT_DIR"
Expand All @@ -14,31 +14,32 @@ while [ "$SEARCH" != "/" ]; do
fi
SEARCH=$(dirname "$SEARCH")
done

if [ -z "$INIT_ENV" ]; then
echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2
exit 1
fi

# shellcheck disable=SC1090
. "$INIT_ENV"


if [ -z "$__INIT_ENV_LOADED" ]; then
# shellcheck disable=SC1090
. "$INIT_ENV"
fi
# shellcheck disable=SC1090,SC1091
. "$TOOLS/functestlib.sh"
TESTNAME="Bluetooth"

TESTNAME="BT_ON_FF"
test_path=$(find_test_case_by_name "$TESTNAME") || {
log_fail "$TESTNAME : Test directory not found."
echo "$TESTNAME FAIL" > "./$TESTNAME.res"
exit 1
}

cd "$test_path" || exit 1
res_file="./$TESTNAME.res"
rm -f "$res_file"
log_info "-----------------------------------------------------------------------------------------"
log_info "-------------------Starting $TESTNAME Testcase----------------------------"

log_info "------------------------------------------------------------"
log_info "Starting $TESTNAME Testcase"
log_info "Checking dependency: bluetoothctl"
check_dependencies bluetoothctl

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@

# Bluetooth BT_SCAN_PAIR Test

This test automates Bluetooth scanning and pairing for embedded Linux devices using BlueZ and bluetoothctl. It is designed for use in the [qcom-linux-testkit](https://github.com/qualcomm-linux/qcom-linux-testkit) test suite.

## Features

- Scans for Bluetooth devices
- Optionally pairs with a device by name or MAC address
- Retries pairing on failure, including handling for busy/temporarily unavailable devices
- Cleans up previous pairings for repeatable CI runs
- Accepts device name/MAC as argument, environment variable, or in `bt_device_list.txt`
- Generates summary and detailed logs (`scan.log`, `pair.log`, `found_devices.log`)

## Usage

```sh
./run.sh [DEVICE_NAME_OR_MAC] [WHITELIST]
```
- `DEVICE_NAME_OR_MAC` – (optional) Device name or MAC address to pair.
- Can also be set as `BT_NAME_ENV` or in `bt_device_list.txt`
- `WHITELIST` – (optional) Comma-separated MACs/names allowed for pairing.
- Can also be set as `BT_WHITELIST_ENV`

If no device name is given, only scanning is performed and the test passes if devices are found.

## Examples

```sh
./run.sh [BT_NAME] [WHITELIST]
```

- `BT_NAME` - Optional. Bluetooth name or MAC to search for.
- `WHITELIST` - Optional. Comma-separated names/MACs allowed for pairing.

- Scan for any device (no pairing):

```
./run.sh
```

- Scan and pair with a device named "MySpeaker":

```
./run.sh MySpeaker
```

- Scan and pair only if device MAC is in whitelist:

```
./run.sh MySpeaker 00:11:22:33:44:55,AnotherSpeaker
```

- Use environment variables:

```
export BT_NAME_ENV="MySpeaker"
export BT_WHITELIST_ENV="00:11:22:33:44:55"
./run.sh
```

- Device list file (first line is used):

```
echo "MySpeaker" > bt_device_list.txt
./run.sh
```

## Whitelist Usage

To ensure only known devices are considered during scan:

```sh
./run.sh JBL_Speaker "JBL_Speaker,12:34:56:78:9A:BC"
```

## Arguments & Variables

- Argument 1: Device name or MAC address (takes precedence)
- `BT_NAME_ENV`: Device name or MAC from the environment
- `bt_device_list.txt`: Fallback if argument or env is not set

## Example Summary Output

```
[INFO] 2025-06-23 10:00:00 - Starting BT_SCAN_PAIR Testcase
[INFO] 2025-06-23 10:00:02 - Unblocking and powering on Bluetooth
[INFO] 2025-06-23 10:00:05 - Devices found during scan:
Device 12:34:56:78:9A:BC SomeBTHeadset
[INFO] 2025-06-23 10:00:06 - Expected device 'SomeBTHeadset' found in scan
[PASS] 2025-06-23 10:00:08 - Pairing successful with 12:34:56:78:9A:BC
```

## Result

- PASS: Device paired (or scan-only with no target)
- FAIL: Device not found, not in whitelist, or pairing failed
- All paired devices are removed after the test

## Files Generated

- `BT_SCAN_PAIR.res`: Test PASS/FAIL/SKIP result
- `scan.log`: Output of Bluetooth device scan
- `found_devices.log`: List of discovered device names/MACs
- `pair.log`: Detailed pairing output and errors

## Troubleshooting

- Ensure `bluetoothctl`, `rfkill`, `expect`, and `hciconfig` are available.
- For headless automation, the remote device must be in pairing/discoverable mode.
- The script retries pairing if "busy" or "temporarily unavailable" errors are seen.
- Check `pair.log` and `scan.log` for detailed debug info if a failure occurs.

## Helper Functions (in functestlib.sh)

- `bt_scan_devices` – Scans and logs found BT devices
- `bt_pair_with_mac` – Attempts pairing via expect with retries
- `bt_in_whitelist` – Checks if MAC/name is in whitelist
- `bt_cleanup_paired_device` – Removes paired device by MAC

## Customization

- **Whitelist**: You can restrict scan to a whitelist of MAC addresses or names using environment variables or script customization.
- **Retries/Timeouts**: Retry and timeout values can be set in the script for more robust pairing.

## Integration with LAVA

In your LAVA job:

```yaml
deploy:
to: tftp
images:
bt_device_list.txt:
image: path/to/bt_device_list.txt
compression: none
```

Injects a per-DUT Bluetooth configuration.

## Dependencies

- `bluetoothctl`, `expect`, `rfkill`, `hciconfig`
- BlueZ stack running on embedded Linux

## License

SPDX-License-Identifier: BSD-3-Clause-Clear
Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
job_name: BT Scan and Pair Test
device_type: qcom-dut
priority: medium
timeouts:
job:
minutes: 10
action:
minutes: 5
visibility:
group: everyone

actions:
- deploy:
timeout:
minutes: 2
to: tftp
images:
bt_device_list.txt:
image: bt_device_list.txt
compression: none

- boot:
method: minimal

- test:
timeout:
minutes: 5
definitions:
- name: bt_scan_pair
from: inline
path: inline/bt_scan_pair.sh
inline:
- chmod +x ./run.sh
- ./run.sh

116 changes: 116 additions & 0 deletions Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#!/bin/sh

# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
# SPDX-License-Identifier: BSD-3-Clause-Clear

# Robustly find and source init_env
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
INIT_ENV=""
SEARCH="$SCRIPT_DIR"
while [ "$SEARCH" != "/" ]; do
if [ -f "$SEARCH/init_env" ]; then
INIT_ENV="$SEARCH/init_env"
break
fi
SEARCH=$(dirname "$SEARCH")
done

if [ -z "$INIT_ENV" ]; then
echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2
exit 1
fi

if [ -z "$__INIT_ENV_LOADED" ]; then
# shellcheck disable=SC1090
. "$INIT_ENV"
fi
# shellcheck disable=SC1090,SC1091
. "$TOOLS/functestlib.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 1
}
cd "$test_path" || exit 1
RES_FILE="./$TESTNAME.res"
rm -f "$RES_FILE"

log_info "------------------------------------------------------------"
log_info "Starting $TESTNAME Testcase"

BT_NAME=""
BT_MAC=""
WHITELIST=""
PAIR_RETRIES="${PAIR_RETRIES:-3}"

# Check bt_device_list.txt if present
if [ -f "./bt_device_list.txt" ]; then
BT_NAME=$(awk '!/^#/ && NF {print $2}' ./bt_device_list.txt | head -n1)
BT_MAC=$(awk '!/^#/ && NF {print $1}' ./bt_device_list.txt | head -n1)
fi

# Command-line override
[ -n "$1" ] && BT_NAME="$1"
[ -n "$2" ] && WHITELIST="$2"

check_dependencies bluetoothctl rfkill expect hciconfig || {
echo "$TESTNAME SKIP" > "$RES_FILE"
exit 0
}

cleanup_bt_test() {
[ -n "$BT_MAC" ] && bt_cleanup_paired_device "$BT_MAC"
killall -q bluetoothctl 2>/dev/null
}
trap cleanup_bt_test EXIT

rfkill unblock bluetooth
retry_command_bt "hciconfig hci0 up" "Bring up hci0" || {
log_fail "Failed to bring up hci0"
echo "$TESTNAME FAIL" > "$RES_FILE"
exit 1
}

log_info "Scanning for Bluetooth devices..."
bt_scan_devices
log_info "Devices found during scan:"
cat found_devices.log

if [ -z "$BT_NAME" ] && [ -z "$BT_MAC" ]; then
log_pass "No device specified. Scan-only mode."
echo "$TESTNAME PASS" > "$RES_FILE"
exit 0
fi

MATCH_FOUND=0
while IFS= read -r line; do
mac=$(echo "$line" | awk '{print $3}')
name=$(echo "$line" | cut -d' ' -f4-)
if { [ "$mac" = "$BT_MAC" ] || [ "$name" = "$BT_NAME" ]; } && \
{ bt_in_whitelist "$mac" "$WHITELIST" || bt_in_whitelist "$name" "$WHITELIST"; }; then
BT_MAC="$mac"
MATCH_FOUND=1
break
fi
done < found_devices.log

if [ "$MATCH_FOUND" -ne 1 ]; then
log_fail "Expected device not found or not in whitelist"
echo "$TESTNAME FAIL" > "$RES_FILE"
exit 1
fi

log_info "Attempting to pair with $BT_NAME ($BT_MAC)"
if bt_pair_with_mac "$BT_MAC" "$PAIR_RETRIES"; then
log_pass "Pairing successful"
echo "$TESTNAME PASS" > "$RES_FILE"
exit 0
else
log_fail "Pairing failed after $PAIR_RETRIES retries"
echo "$TESTNAME FAIL" > "$RES_FILE"
exit 1
fi

log_info "Completed $TESTNAME Testcase"
Loading
Loading