Skip to content

Commit

Permalink
tests: Bluetooth: Add BT Tester GAP smoke test
Browse files Browse the repository at this point in the history
Add a babblesim test of the BT Tester doing a GAP smoke test
connecting 2 BT testers using BTP.

The purpose of this is to further increase the test coverage
of the BT Tester in CI, as it is only being built, and runtime
errors are typically not caught.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
  • Loading branch information
Thalley committed Mar 3, 2025
1 parent 95c1c85 commit f55cd0b
Show file tree
Hide file tree
Showing 19 changed files with 627 additions and 3 deletions.
7 changes: 7 additions & 0 deletions tests/bluetooth/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ LIST(APPEND QEMU_EXTRA_FLAGS -serial unix:/tmp/bt-stack-tester)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(tester)

if(CONFIG_SOC_SERIES_BSIM_NRFXX)
zephyr_include_directories(
${BSIM_COMPONENTS_PATH}/libUtilv1/src/
${BSIM_COMPONENTS_PATH}/libPhyComv1/src/
)
endif() # CONFIG_SOC_SERIES_BSIM_NRFXX

zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth)
zephyr_library_include_directories(${ZEPHYR_BASE}/subsys/bluetooth/mesh)
zephyr_library_include_directories(${ZEPHYR_BASE}/subsys/bluetooth/host)
Expand Down
13 changes: 13 additions & 0 deletions tests/bluetooth/tester/boards/nrf52_bsim.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# CONFIG_TEST enforces minimal logging, which we don't want
CONFIG_TEST=n

CONFIG_ASSERT=y
CONFIG_THREAD_NAME=y

CONFIG_LOG=y
CONFIG_LOG_BACKEND_SHOW_COLOR=y

CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_BTTESTER_LOG_LEVEL_DBG=y

CONFIG_UART_PIPE=n
13 changes: 13 additions & 0 deletions tests/bluetooth/tester/boards/nrf5340bsim_nrf5340_cpuapp.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# CONFIG_TEST enforces minimal logging, which we don't want
CONFIG_TEST=n

CONFIG_ASSERT=y
CONFIG_THREAD_NAME=y

CONFIG_LOG=y
CONFIG_LOG_BACKEND_SHOW_COLOR=y

CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_BTTESTER_LOG_LEVEL_DBG=y

CONFIG_UART_PIPE=n
13 changes: 13 additions & 0 deletions tests/bluetooth/tester/src/btp.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,19 @@ static void uart_send(const uint8_t *data, size_t len)
{
uart_pipe_send(data, len);
}
#elif defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
extern void btp_register_tester(uint8_t *buffer, size_t len, uart_pipe_recv_cb cb);
extern void btp_send_to_bsim(const uint8_t *data, size_t len);

static void uart_init(uint8_t *data)
{
btp_register_tester(data, BTP_MTU, recv_cb);
}

static void uart_send(const uint8_t *data, size_t len)
{
btp_send_to_bsim(data, len);
}
#else /* !CONFIG_UART_PIPE */
static uint8_t *recv_buf;
static size_t recv_off;
Expand Down
2 changes: 2 additions & 0 deletions tests/bluetooth/tester/src/btp/btp.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@

#define BTP_STATUS_VAL(err) (err) ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS

#define BTP_EVENT_OPCODE 0x80

/* TODO indicate delay response, should be removed when all commands are
* converted to cmd+status+ev pattern
*/
Expand Down
12 changes: 9 additions & 3 deletions tests/bluetooth/tester/src/btp_gap.c
Original file line number Diff line number Diff line change
Expand Up @@ -723,8 +723,9 @@ static uint8_t start_advertising(const void *cmd, uint16_t cmd_len,
* type.
*/
if ((cmd_len < sizeof(*cp)) ||
(cmd_len != sizeof(*cp) + cp->adv_data_len + cp->scan_rsp_len +
sizeof(duration) + sizeof(own_addr_type))) {
(cmd_len != sizeof(*cp) + cp->adv_data_len + cp->scan_rsp_len + sizeof(duration) +
sizeof(own_addr_type))) {
LOG_DBG("Invalid command length: %u", cmd_len);
return BTP_STATUS_FAILED;
}

Expand Down Expand Up @@ -759,6 +760,7 @@ static uint8_t start_advertising(const void *cmd, uint16_t cmd_len,

err = tester_gap_create_adv_instance(&param, own_addr_type, ad, adv_len, sd, sd_len, NULL);
if (err != 0) {
LOG_DBG("Failed to create adv instance: %d", err);
return BTP_STATUS_FAILED;
}

Expand All @@ -771,7 +773,7 @@ static uint8_t start_advertising(const void *cmd, uint16_t cmd_len,

/* BTP API don't allow to set empty scan response data. */
if (err < 0) {
LOG_ERR("Failed to start advertising");
LOG_ERR("Failed to start advertising: %d");

return BTP_STATUS_FAILED;
}
Expand Down Expand Up @@ -892,6 +894,10 @@ static void store_adv(const bt_addr_le_t *addr, int8_t rssi,
static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t evtype,
struct net_buf_simple *buf_ad)
{
char addr_str[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));

Check warning on line 898 in tests/bluetooth/tester/src/btp_gap.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

LINE_SPACING

tests/bluetooth/tester/src/btp_gap.c:898 Missing a blank line after declarations
LOG_ERR("Found remote device %s", addr_str);

/* if General/Limited Discovery - parse Advertising data to get flags */
if (!(discovery_flags & BTP_GAP_DISCOVERY_FLAG_LE_OBSERVE) &&
(evtype != BT_GAP_ADV_TYPE_SCAN_RSP)) {
Expand Down
13 changes: 13 additions & 0 deletions tests/bluetooth/tester/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@

/*
* Copyright (c) 2015-2016 Intel Corporation
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/autoconf.h>

#include <zephyr/kernel.h>
#include <zephyr/sys/util_macro.h>
#include <zephyr/types.h>
#include <zephyr/toolchain.h>

#if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
#include "bstests.h"
#endif /* CONFIG_SOC_SERIES_BSIM_NRFXX */

#include <zephyr/logging/log.h>
#define LOG_MODULE_NAME bttester_main
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
Expand All @@ -19,5 +27,10 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
int main(void)
{
tester_init();

#if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
bst_main();
#endif /* CONFIG_SOC_SERIES_BSIM_NRFXX */

return 0;
}
29 changes: 29 additions & 0 deletions tests/bsim/bluetooth/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(tester_bsim)

# Add the BSIM tester
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/tester ${CMAKE_BINARY_DIR}/tester_build)

# This contains babblesim-specific helpers, e.g. device synchronization.
add_subdirectory(${ZEPHYR_BASE}/tests/bsim/babblekit babblekit)
target_link_libraries(app PRIVATE babblekit)

# Add the testlib
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/common/testlib testlib)
target_link_libraries(app PRIVATE testlib)

zephyr_include_directories(
${BSIM_COMPONENTS_PATH}/libUtilv1/src/
${BSIM_COMPONENTS_PATH}/libPhyComv1/src/
)

target_sources(app PRIVATE
src/btp.c
src/gap_central.c
src/gap_peripheral.c
src/test_main.c
)
4 changes: 4 additions & 0 deletions tests/bsim/bluetooth/tester/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) 2024-2025 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

source "${ZEPHYR_BASE}/tests/bluetooth/tester/Kconfig"
10 changes: 10 additions & 0 deletions tests/bsim/bluetooth/tester/Kconfig.sysbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) 2023-2025 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

source "${ZEPHYR_BASE}/tests/bluetooth/tester/Kconfig.sysbuild"

config NATIVE_SIMULATOR_PRIMARY_MCU_INDEX
int
# Let's pass the test arguments to the application MCU test
# otherwise by default they would have gone to the net core.
default 0 if $(BOARD_TARGET_STRING) = "NRF5340BSIM_NRF5340_CPUAPP"
2 changes: 2 additions & 0 deletions tests/bsim/bluetooth/tester/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Please build using the sample configuration file:
# ${ZEPHYR_BASE}/tests/bluetooth/tester/prj.conf
176 changes: 176 additions & 0 deletions tests/bsim/bluetooth/tester/src/btp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stddef.h>
#include <string.h>

#include <zephyr/drivers/uart_pipe.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/net_buf.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys_clock.h>

#include "babblekit/testcase.h"

#include "btp/btp.h"

LOG_MODULE_REGISTER(bsim_btp, CONFIG_BTTESTER_LOG_LEVEL);

static uint8_t *btp_buffer;
static size_t btp_buffer_len;
static uart_pipe_recv_cb btp_cb;

K_FIFO_DEFINE(btp_rsp_fifo);
NET_BUF_POOL_FIXED_DEFINE(btp_rsp_pool, 1, BTP_MTU, 0, NULL);
K_FIFO_DEFINE(btp_evt_fifo);
NET_BUF_POOL_FIXED_DEFINE(btp_evt_pool, 100, BTP_MTU, 0, NULL);

static void wait_for_response(const struct btp_hdr *cmd_hdr)
{
const struct btp_hdr *rsp_hdr;
struct net_buf *buf;

buf = k_fifo_get(&btp_rsp_fifo, K_SECONDS(1));
TEST_ASSERT(buf != NULL);

rsp_hdr = (struct btp_hdr *)buf->data;
TEST_ASSERT(rsp_hdr->len <= BTP_MTU, "len %u > %d", rsp_hdr->len, BTP_MTU);

LOG_DBG("rsp service %u and opcode %u len %u", cmd_hdr->service, cmd_hdr->opcode,
rsp_hdr->len);

TEST_ASSERT(rsp_hdr->service == cmd_hdr->service && rsp_hdr->opcode == cmd_hdr->opcode);

net_buf_unref(buf);
}

/* BTP communication is inspired from `uart_pipe_rx` to achieve a similar API */
void btp_send_to_tester(const uint8_t *data, size_t len)
{
const struct btp_hdr *cmd_hdr;
size_t offset = len;

TEST_ASSERT(data != NULL);
TEST_ASSERT(btp_buffer != NULL);
TEST_ASSERT(len <= btp_buffer_len, "len %zu > %zu", len, btp_buffer_len);
TEST_ASSERT(len >= sizeof(*cmd_hdr), "len %zu <= %zu", len, sizeof(*cmd_hdr));

memcpy(btp_buffer, data, len);

cmd_hdr = (const struct btp_hdr *)data;
LOG_DBG("cmd service %u and opcode %u", cmd_hdr->service, cmd_hdr->opcode);
btp_buffer = btp_cb(btp_buffer, &offset);
TEST_ASSERT(offset == 0U);

wait_for_response(cmd_hdr);
}

void btp_register_tester(uint8_t *buffer, size_t len, uart_pipe_recv_cb cb)
{
TEST_ASSERT(cb != NULL);
TEST_ASSERT(buffer != NULL);
TEST_ASSERT(len > sizeof(struct btp_hdr), "len %zu", len);
TEST_ASSERT(len <= BTP_MTU, "len %zu > %d", len, BTP_MTU);

btp_buffer = buffer;
btp_buffer_len = len;
btp_cb = cb;

LOG_INF("btp registered with %p %zu %p", buffer, len, cb);
}

void btp_send_to_bsim(const uint8_t *data, size_t len)
{
static struct net_buf *buf;
bool full_evt;

TEST_ASSERT(len >= 0);
TEST_ASSERT(data != NULL);
TEST_ASSERT(len <= BTP_MTU - sizeof(struct btp_hdr), "len %zu < %d", len, BTP_MTU);
TEST_ASSERT(buf != NULL || (buf == NULL && len == sizeof(struct btp_hdr)), "len %zu buf %p",
len, buf);

/* tester_send_with_index always sends the header first, and then any additional information
* afterwards. We thus need to reassemble the full event.
*/
if (buf == NULL) {
const struct btp_hdr *hdr = (const struct btp_hdr *)data;

LOG_DBG("hdr service %u and opcode %u", hdr->service, hdr->opcode);
TEST_ASSERT(hdr->opcode != BTP_STATUS, "hdr service %u", hdr->service);

if (hdr->opcode < BTP_EVENT_OPCODE) {
buf = net_buf_alloc(&btp_rsp_pool, K_NO_WAIT);
TEST_ASSERT(buf != NULL);
} else {
buf = net_buf_alloc(&btp_evt_pool, K_NO_WAIT);
if (buf == NULL) {
/* Discard the oldest event */
buf = k_fifo_get(&btp_evt_fifo, K_NO_WAIT);
TEST_ASSERT(buf != NULL);
net_buf_unref(buf);

buf = net_buf_alloc(&btp_evt_pool, K_NO_WAIT);
TEST_ASSERT(buf != NULL);
}
}

full_evt = hdr->len == 0; /* we are not awaiting additional data if hdr->len == 0*/
} else {
/* If buf != NULL then we have received the hdr and this is the additional data for
* the event/response. We always assume that we receive the rest here
*/
const struct btp_hdr *hdr = (const struct btp_hdr *)buf->data;

TEST_ASSERT(buf != NULL && len == hdr->len, "len %zu hdr len %u", len, hdr->len);
full_evt = true;
}

net_buf_add_mem(buf, data, len);

if (full_evt) {
const struct btp_hdr *hdr = (const struct btp_hdr *)buf->data;

if (hdr->opcode < BTP_EVENT_OPCODE) {
k_fifo_put(&btp_rsp_fifo, buf);
} else {
k_fifo_put(&btp_evt_fifo, buf);
}

buf = NULL;
}
}

void btp_wait_for_evt(uint8_t service, uint8_t opcode, struct net_buf **out_buf)
{
LOG_DBG("Waiting for evt with service %u and opcode %u", service, opcode);

while (true) {
const struct btp_hdr *hdr;
struct net_buf *buf;

buf = k_fifo_get(&btp_evt_fifo, K_FOREVER);
TEST_ASSERT(buf != NULL);

LOG_HEXDUMP_DBG(buf->data, buf->len, "evt");
hdr = net_buf_pull_mem(buf, sizeof(struct btp_hdr));

/* TODO: Verify length of event based on the service and opcode */
if (hdr->service == service && hdr->opcode == opcode) {
if (out_buf != NULL) {
/* If the caller provides an out_buf, they are responsible for
* unref'ing it*/

Check warning on line 166 in tests/bsim/bluetooth/tester/src/btp.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

BLOCK_COMMENT_STYLE

tests/bsim/bluetooth/tester/src/btp.c:166 Block comments use a trailing */ on a separate line
*out_buf = net_buf_ref(buf);
}

net_buf_unref(buf);
return;
}

net_buf_unref(buf);
}
}
Loading

0 comments on commit f55cd0b

Please sign in to comment.