tests: Bluetooth: Add example bsim test
This test is intended to teach the conventions and best practices to a potential contributor that wants to add a test, for say a new feature. It is liberally commented on purpose, so new contributors don't have to spend two weeks understanding the intricacies of the simulator, the bsim test framework, the native builds, backchannels, etc.. Signed-off-by: Jonathan Rico <jonathan.rico@nordicsemi.no>
This commit is contained in:
parent
389192a94d
commit
f818cbd8cc
|
@ -132,6 +132,12 @@ and then directly run one of the tests:
|
|||
Conventions
|
||||
===========
|
||||
|
||||
Test code
|
||||
---------
|
||||
|
||||
See the :zephyr_file:`Bluetooth sample test <tests/bsim/bluetooth/host/misc/sample_test/README.rst>` for conventions that apply to test
|
||||
code.
|
||||
|
||||
Build scripts
|
||||
-------------
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ app=tests/bsim/bluetooth/host/misc/conn_stress/central compile
|
|||
app=tests/bsim/bluetooth/host/misc/conn_stress/peripheral compile
|
||||
app=tests/bsim/bluetooth/host/misc/hfc compile
|
||||
app=tests/bsim/bluetooth/host/misc/unregister_conn_cb compile
|
||||
app=tests/bsim/bluetooth/host/misc/sample_test compile
|
||||
|
||||
app=tests/bsim/bluetooth/host/privacy/central compile
|
||||
app=tests/bsim/bluetooth/host/privacy/peripheral compile
|
||||
|
|
49
tests/bsim/bluetooth/host/misc/sample_test/CMakeLists.txt
Normal file
49
tests/bsim/bluetooth/host/misc/sample_test/CMakeLists.txt
Normal file
|
@ -0,0 +1,49 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
|
||||
# You can name the project however you like. Having a unique name is encouraged.
|
||||
project(sample_test)
|
||||
|
||||
# This contains a variety of helper functions that abstract away common tasks,
|
||||
# like scanning, setting up a connection, querying the peer for a given
|
||||
# characteristic, etc..
|
||||
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/common/testlib testlib)
|
||||
target_link_libraries(app PRIVATE testlib)
|
||||
|
||||
# This contains babblesim-specific helpers, e.g. device synchronization.
|
||||
add_subdirectory(${ZEPHYR_BASE}/tests/bsim/babblekit babblekit)
|
||||
target_link_libraries(app PRIVATE babblekit)
|
||||
|
||||
zephyr_include_directories(
|
||||
${BSIM_COMPONENTS_PATH}/libUtilv1/src/
|
||||
${BSIM_COMPONENTS_PATH}/libPhyComv1/src/
|
||||
)
|
||||
|
||||
# List every source file in the test application. Do not use GLOB.
|
||||
#
|
||||
# It is a good idea to have one file per test role / entry point.
|
||||
#
|
||||
# Try to keep test procedures readable, that is minimizing the amount of
|
||||
# boilerplate. Use the `testlib` for anything that is not the object of the test
|
||||
# or that you don't need tight control over. For example, setting up a
|
||||
# connection between two devices.
|
||||
#
|
||||
# As a general rule of thumb: All functions that use `TEST_ASSERT()` are part of
|
||||
# the test and should be in the same file as the test entry point. Any other
|
||||
# "helper" functions can be isolated in their own file, minimizing visual
|
||||
# clutter and cognitive overhead for future readers.
|
||||
#
|
||||
# Common data can live in a header included by multiple roles (e.g. service or
|
||||
# characteristic UUIDs, test data that should be verified by both parties,
|
||||
# etc..).
|
||||
#
|
||||
# Ideally, main.c should only set up the test framework, and map the entry
|
||||
# points to the test identifiers.
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/dut.c
|
||||
src/peer.c
|
||||
)
|
18
tests/bsim/bluetooth/host/misc/sample_test/Kconfig
Normal file
18
tests/bsim/bluetooth/host/misc/sample_test/Kconfig
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Kconfig options for the test
|
||||
#
|
||||
# Only used as single point for log level configuration.
|
||||
# Can be extended with any new kconfig, really.
|
||||
#
|
||||
# Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menu "Test configuration"
|
||||
|
||||
module = APP
|
||||
module-str = app
|
||||
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
endmenu
|
||||
|
||||
source "Kconfig.zephyr"
|
34
tests/bsim/bluetooth/host/misc/sample_test/README.rst
Normal file
34
tests/bsim/bluetooth/host/misc/sample_test/README.rst
Normal file
|
@ -0,0 +1,34 @@
|
|||
.. _bluetooth_bsim_test_sample:
|
||||
|
||||
Bluetooth: Example BabbleSim test
|
||||
#################################
|
||||
|
||||
Abstract
|
||||
********
|
||||
|
||||
This test's purpose is to serve as template for implementing a new BabbleSim Bluetooth test.
|
||||
|
||||
BabbleSim_ is :ref:`integrated in zephyr <bsim>` and used for testing the Bluetooth stack.
|
||||
The tests are implemented in ``tests/bsim/bluetooth``.
|
||||
They can only be run on Linux.
|
||||
|
||||
This sample test uses the ``testlib`` (:zephyr_file:`tests/bluetooth/common/testlib/CMakeLists.txt`)
|
||||
test library, in order to de-duplicate code that is not relevant to the test in question. Things
|
||||
like setting up a connection, getting the GATT handle of a characteristic, etc..
|
||||
|
||||
Please don't use the ``bs_`` prefix in files or identifiers. It's meant to
|
||||
namespace the babblesim simulator code.
|
||||
|
||||
Reading guide
|
||||
*************
|
||||
|
||||
Read in order:
|
||||
|
||||
1. The :ref:`Bsim test documentation <bsim>`.
|
||||
#. ``test_scripts/run.sh``
|
||||
#. ``CMakeLists.txt``
|
||||
#. ``src/dut.c`` and ``src/peer.c``
|
||||
#. ``src/main.c``
|
||||
|
||||
.. _BabbleSim:
|
||||
https://BabbleSim.github.io
|
56
tests/bsim/bluetooth/host/misc/sample_test/prj.conf
Normal file
56
tests/bsim/bluetooth/host/misc/sample_test/prj.conf
Normal file
|
@ -0,0 +1,56 @@
|
|||
# Select only the config options that are necessary for the test.
|
||||
# I.e. don't just copy this config and its comments into your new test.
|
||||
#
|
||||
# If the test is a stress or robustness test, it is also a good idea to set the
|
||||
# stack resources (e.g. number of buffers, roles) to the lowest value possible.
|
||||
|
||||
CONFIG_BT=y
|
||||
CONFIG_BT_DEVICE_NAME="sample-test"
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
|
||||
# Dependency of testlib/adv and testlib/scan.
|
||||
CONFIG_BT_EXT_ADV=y
|
||||
|
||||
CONFIG_BT_GATT_CLIENT=y
|
||||
CONFIG_BT_GATT_AUTO_DISCOVER_CCC=y
|
||||
|
||||
# This is the object of the test. Commenting it out should make the test fail.
|
||||
CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION=n
|
||||
|
||||
# Always enable asserts in tests, they're virtually free on native targets.
|
||||
CONFIG_ASSERT=y
|
||||
|
||||
# The same applies for logs.
|
||||
# Only enable the INFO level though, as a contributor that isn't familiar with
|
||||
# the test will have a hard time understanding what the problem is if it's
|
||||
# buried in thousands of lines of debug logs.
|
||||
CONFIG_LOG=y
|
||||
|
||||
# Those two options together add the thread name in every log print, very useful
|
||||
# for debugging if you expect the same functions to be called from different
|
||||
# threads.
|
||||
CONFIG_THREAD_NAME=y
|
||||
CONFIG_LOG_THREAD_ID_PREFIX=y
|
||||
|
||||
# BT_TESTING provides additional hooks in the stack to inspect or modify state.
|
||||
# It is not strictly necessary, leave it disabled if you don't need it.
|
||||
# CONFIG_BT_TESTING=y
|
||||
|
||||
# Will call `raise(SIGTRAP)` on fatal error.
|
||||
# If a debugger is connected to the app, it will automatically be stopped.
|
||||
# Makes retrieving an exception stacktrace very easy.
|
||||
CONFIG_ARCH_POSIX_TRAP_ON_FATAL=y
|
||||
|
||||
# It's OK to leave useful debug options commented out, with a short comment
|
||||
# explaining why they might be useful. That way, someone trying to debug your
|
||||
# test will get a headstart.
|
||||
# CONFIG_APP_LOG_LEVEL_DBG=y
|
||||
# CONFIG_BT_CONN_LOG_LEVEL_DBG=y
|
||||
# CONFIG_BT_ATT_LOG_LEVEL_DBG=y
|
||||
# CONFIG_BT_GATT_LOG_LEVEL_DBG=y
|
||||
|
||||
# For this particular test, the LOG_INF printed on `bt_enable()` are just noise.
|
||||
# They might matter for other tests though. They are printed by the
|
||||
# "bt_hci_core" domain, and HCI_CORE doesn't use LOG_INF very much anyways.
|
||||
CONFIG_BT_HCI_CORE_LOG_LEVEL_WRN=y
|
26
tests/bsim/bluetooth/host/misc/sample_test/src/data.h
Normal file
26
tests/bsim/bluetooth/host/misc/sample_test/src/data.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_TESTS_BSIM_BLUETOOTH_HOST_MISC_SAMPLE_TEST_SRC_DATA_H_
|
||||
#define ZEPHYR_TESTS_BSIM_BLUETOOTH_HOST_MISC_SAMPLE_TEST_SRC_DATA_H_
|
||||
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
|
||||
static uint8_t payload_1[] = {0xab, 0xcd};
|
||||
static uint8_t payload_2[] = {0x13, 0x37};
|
||||
|
||||
/* Both payloads are assumed to be the same size in order to simplify the test
|
||||
* procedure.
|
||||
*/
|
||||
BUILD_ASSERT(sizeof(payload_1) == sizeof(payload_2),
|
||||
"Both payloads should be of equal length");
|
||||
|
||||
#define test_service_uuid \
|
||||
BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0xf0debc9a, 0x7856, 0x3412, 0x7856, 0x341278563412))
|
||||
#define test_characteristic_uuid \
|
||||
BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0xf2debc9a, 0x7856, 0x3412, 0x7856, 0x341278563412))
|
||||
|
||||
#endif /* ZEPHYR_TESTS_BSIM_BLUETOOTH_HOST_MISC_SAMPLE_TEST_SRC_DATA_H_ */
|
172
tests/bsim/bluetooth/host/misc/sample_test/src/dut.c
Normal file
172
tests/bsim/bluetooth/host/misc/sample_test/src/dut.c
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "testlib/conn.h"
|
||||
#include "testlib/scan.h"
|
||||
|
||||
#include "babblekit/flags.h"
|
||||
#include "babblekit/sync.h"
|
||||
#include "babblekit/testcase.h"
|
||||
|
||||
/* local includes */
|
||||
#include "data.h"
|
||||
|
||||
LOG_MODULE_REGISTER(dut, CONFIG_APP_LOG_LEVEL);
|
||||
|
||||
static DEFINE_FLAG(is_subscribed);
|
||||
|
||||
static void ccc_changed(const struct bt_gatt_attr *attr, uint16_t value)
|
||||
{
|
||||
/* assume we only get it for the `test_gatt_service` */
|
||||
if (value != 0) {
|
||||
SET_FLAG(is_subscribed);
|
||||
} else {
|
||||
UNSET_FLAG(is_subscribed);
|
||||
}
|
||||
}
|
||||
|
||||
BT_GATT_SERVICE_DEFINE(test_gatt_service, BT_GATT_PRIMARY_SERVICE(test_service_uuid),
|
||||
BT_GATT_CHARACTERISTIC(test_characteristic_uuid,
|
||||
(BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE |
|
||||
BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE),
|
||||
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, NULL, NULL,
|
||||
NULL),
|
||||
BT_GATT_CCC(ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),);
|
||||
|
||||
/* This is the entrypoint for the DUT.
|
||||
*
|
||||
* This is executed by the `bst_test` framework provided by the zephyr bsim
|
||||
* boards. The framework selects which "main" function to run as entrypoint
|
||||
* depending on the `-testid=` command-line parameter passed to the zephyr
|
||||
* executable.
|
||||
*
|
||||
* In our case, the `testid` is set to "dut" and `entrypoint_dut()` is mapped to
|
||||
* the "dut" ID in `entrypoints[]`.
|
||||
*
|
||||
* In our case we only have two entrypoints, as we only have a single test
|
||||
* involving two devices (so 1x 2 entrypoints). One can define more test cases
|
||||
* with different entrypoints and map them to different test ID strings in
|
||||
* `entrypoints[]`
|
||||
*/
|
||||
void entrypoint_dut(void)
|
||||
{
|
||||
/* Please leave a comment indicating what the test is supposed to test,
|
||||
* and what is the pass verdict. A nice place is at the beginning of
|
||||
* each test entry point. Something like the following:
|
||||
*/
|
||||
|
||||
/* Test purpose:
|
||||
*
|
||||
* Verifies that we are able to send a notification to the peer when
|
||||
* `CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION` is disabled and the peer has
|
||||
* unsubscribed from the characteristic in question.
|
||||
*
|
||||
* Two devices:
|
||||
* - `dut`: tries to send the notification
|
||||
* - `peer`: will receive the notification
|
||||
*
|
||||
* Procedure:
|
||||
* - [dut] establish connection to `peer`
|
||||
* - [peer] discover GATT and subscribe to the test characteristic
|
||||
* - [dut] send notification #1
|
||||
* - [peer] wait for notification
|
||||
* - [peer] unsubscribe
|
||||
* - [dut] send notification #2
|
||||
* - [peer] and [dut] pass test
|
||||
*
|
||||
* [verdict]
|
||||
* - peer receives notifications #1 and #2
|
||||
*/
|
||||
int err;
|
||||
bt_addr_le_t peer = {};
|
||||
struct bt_conn *conn = NULL;
|
||||
const struct bt_gatt_attr *attr;
|
||||
uint8_t data[10];
|
||||
|
||||
/* Mark test as in progress. */
|
||||
TEST_START("dut");
|
||||
|
||||
/* Initialize device sync library */
|
||||
bk_sync_init();
|
||||
|
||||
/* Initialize Bluetooth */
|
||||
err = bt_enable(NULL);
|
||||
TEST_ASSERT(err == 0, "Can't enable Bluetooth (err %d)", err);
|
||||
|
||||
LOG_DBG("Bluetooth initialized");
|
||||
|
||||
/* Find the address of the peer. In our case, both devices are actually
|
||||
* the same executable (with the same config) but executed with
|
||||
* different arguments. We can then just use CONFIG_BT_DEVICE_NAME which
|
||||
* contains our device name in string form.
|
||||
*/
|
||||
err = bt_testlib_scan_find_name(&peer, CONFIG_BT_DEVICE_NAME);
|
||||
TEST_ASSERT(!err, "Failed to start scan (err %d)", err);
|
||||
|
||||
/* Create a connection using that address */
|
||||
err = bt_testlib_connect(&peer, &conn);
|
||||
TEST_ASSERT(!err, "Failed to initiate connection (err %d)", err);
|
||||
|
||||
LOG_DBG("Connected");
|
||||
|
||||
LOG_INF("Wait until peer subscribes");
|
||||
UNSET_FLAG(is_subscribed);
|
||||
WAIT_FOR_FLAG(is_subscribed);
|
||||
|
||||
/* Prepare data for notifications
|
||||
* attrs[0] is our service declaration
|
||||
* attrs[1] is our characteristic declaration
|
||||
* attrs[2] is our characteristic value
|
||||
*
|
||||
* We store a pointer for the characteristic value as that is the
|
||||
* value we want to notify later.
|
||||
*
|
||||
* We could alternatively use `bt_gatt_notify_uuid()`.
|
||||
*/
|
||||
attr = &test_gatt_service.attrs[2];
|
||||
|
||||
LOG_INF("Send notification #1");
|
||||
LOG_HEXDUMP_DBG(data, sizeof(data), "Notification payload");
|
||||
|
||||
err = bt_gatt_notify(conn, attr, payload_1, sizeof(payload_1));
|
||||
TEST_ASSERT(!err, "Failed to send notification: err %d", err);
|
||||
|
||||
LOG_INF("Wait until peer unsubscribes");
|
||||
WAIT_FOR_FLAG_UNSET(is_subscribed);
|
||||
|
||||
LOG_INF("Send notification #2");
|
||||
err = bt_gatt_notify(conn, attr, payload_2, sizeof(payload_2));
|
||||
TEST_ASSERT(!err, "Failed to send notification: err %d", err);
|
||||
|
||||
/* We won't be using `conn` anymore */
|
||||
bt_conn_unref(conn);
|
||||
|
||||
/* Wait until the peer has received notification #2.
|
||||
*
|
||||
* This is not strictly necessary, but serves as an example on how to
|
||||
* use the backchannel-based synchronization mechanism between devices
|
||||
* in a simulation.
|
||||
*/
|
||||
bk_sync_wait();
|
||||
|
||||
/* Wait for the acknowledge of the DUT. If a device that uses
|
||||
* backchannels exits prematurely (ie before the other side has read the
|
||||
* message it sent), we are in undefined behavior territory.
|
||||
*
|
||||
* The simulation will continue running for its specified length.
|
||||
*
|
||||
* If you don't need backchannels, using `TEST_PASS_AND_EXIT()` is
|
||||
* better as it will make the simulation exit prematurely, saving
|
||||
* computing resources (CI compute time is not free).
|
||||
*/
|
||||
TEST_PASS("dut");
|
||||
}
|
62
tests/bsim/bluetooth/host/misc/sample_test/src/main.c
Normal file
62
tests/bsim/bluetooth/host/misc/sample_test/src/main.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include "bs_tracing.h"
|
||||
#include "bstests.h"
|
||||
#include "babblekit/testcase.h"
|
||||
|
||||
extern void entrypoint_dut(void);
|
||||
extern void entrypoint_peer(void);
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
|
||||
static void test_end_cb(void)
|
||||
{
|
||||
/* This callback will fire right before the executable returns
|
||||
*
|
||||
* You can use it to print test or system state that would be of use for
|
||||
* debugging why the test fails.
|
||||
* Here we just print a dummy string for demonstration purposes.
|
||||
*
|
||||
* Can also be used to trigger a `k_oops` which will halt the image if
|
||||
* running under a debugger, if `CONFIG_ARCH_POSIX_TRAP_ON_FATAL=y`.
|
||||
*/
|
||||
static const char demo_state[] = "My interesting state";
|
||||
|
||||
if (bst_result != Passed) {
|
||||
TEST_PRINT("Test has not passed. State: %s", demo_state);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct bst_test_instance entrypoints[] = {
|
||||
{
|
||||
.test_id = "dut",
|
||||
.test_delete_f = test_end_cb,
|
||||
.test_main_f = entrypoint_dut,
|
||||
},
|
||||
{
|
||||
.test_id = "peer",
|
||||
.test_delete_f = test_end_cb,
|
||||
.test_main_f = entrypoint_peer,
|
||||
},
|
||||
BSTEST_END_MARKER,
|
||||
};
|
||||
|
||||
static struct bst_test_list *install(struct bst_test_list *tests)
|
||||
{
|
||||
return bst_add_tests(tests, entrypoints);
|
||||
};
|
||||
|
||||
bst_test_install_t test_installers[] = {install, NULL};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
bst_main();
|
||||
|
||||
return 0;
|
||||
}
|
200
tests/bsim/bluetooth/host/misc/sample_test/src/peer.c
Normal file
200
tests/bsim/bluetooth/host/misc/sample_test/src/peer.c
Normal file
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/att.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "testlib/adv.h"
|
||||
#include "testlib/att_read.h"
|
||||
#include "testlib/att_write.h"
|
||||
#include "testlib/conn.h"
|
||||
|
||||
#include "babblekit/flags.h"
|
||||
#include "babblekit/sync.h"
|
||||
#include "babblekit/testcase.h"
|
||||
|
||||
/* local includes */
|
||||
#include "data.h"
|
||||
|
||||
LOG_MODULE_REGISTER(peer, CONFIG_APP_LOG_LEVEL);
|
||||
|
||||
static DEFINE_FLAG(is_subscribed);
|
||||
static DEFINE_FLAG(got_notification_1);
|
||||
static DEFINE_FLAG(got_notification_2);
|
||||
|
||||
int find_characteristic(struct bt_conn *conn,
|
||||
const struct bt_uuid *svc,
|
||||
const struct bt_uuid *chrc,
|
||||
uint16_t *chrc_value_handle)
|
||||
{
|
||||
uint16_t svc_handle;
|
||||
uint16_t svc_end_handle;
|
||||
uint16_t chrc_end_handle;
|
||||
int err;
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
err = bt_testlib_gatt_discover_primary(&svc_handle, &svc_end_handle, conn, svc,
|
||||
BT_ATT_FIRST_ATTRIBUTE_HANDLE,
|
||||
BT_ATT_LAST_ATTRIBUTE_HANDLE);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to discover service: %d", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
LOG_DBG("svc_handle: %u, svc_end_handle: %u", svc_handle, svc_end_handle);
|
||||
|
||||
err = bt_testlib_gatt_discover_characteristic(chrc_value_handle, &chrc_end_handle,
|
||||
NULL, conn, chrc, (svc_handle + 1),
|
||||
svc_end_handle);
|
||||
if (err != 0) {
|
||||
LOG_ERR("Failed to get value handle: %d", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
LOG_DBG("chrc_value_handle: %u, chrc_end_handle: %u", *chrc_value_handle, chrc_end_handle);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static uint8_t received_notification(struct bt_conn *conn,
|
||||
struct bt_gatt_subscribe_params *params,
|
||||
const void *data,
|
||||
uint16_t length)
|
||||
{
|
||||
if (length) {
|
||||
LOG_INF("RX notification");
|
||||
LOG_HEXDUMP_DBG(data, length, "payload");
|
||||
|
||||
TEST_ASSERT(length == sizeof(payload_1), "Unexpected length: %d", length);
|
||||
|
||||
if (!memcmp(payload_1, data, length)) {
|
||||
SET_FLAG(got_notification_1);
|
||||
} else if (!memcmp(payload_2, data, length)) {
|
||||
SET_FLAG(got_notification_2);
|
||||
}
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static void sub_cb(struct bt_conn *conn,
|
||||
uint8_t err,
|
||||
struct bt_gatt_subscribe_params *params)
|
||||
{
|
||||
TEST_ASSERT(!err, "Subscribe failed (err %d)", err);
|
||||
|
||||
TEST_ASSERT(params, "params is NULL");
|
||||
TEST_ASSERT(params->value, "Host shouldn't know we have unsubscribed");
|
||||
|
||||
LOG_DBG("Subscribed to handle 0x%04x", params->value_handle);
|
||||
SET_FLAG(is_subscribed);
|
||||
}
|
||||
|
||||
/* Subscription parameters have the same lifetime as a subscription.
|
||||
* That is the backing struct should stay valid until a call to
|
||||
* `bt_gatt_unsubscribe()` is made. Hence the `static`.
|
||||
*/
|
||||
static struct bt_gatt_subscribe_params sub_params;
|
||||
|
||||
/* This is "working memory" used by the `CONFIG_BT_GATT_AUTO_DISCOVER_CCC`
|
||||
* feature. It also has to stay valid until the end of the async call.
|
||||
*/
|
||||
static struct bt_gatt_discover_params ccc_disc_params;
|
||||
|
||||
static void subscribe(struct bt_conn *conn,
|
||||
uint16_t handle,
|
||||
bt_gatt_notify_func_t cb)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Subscribe to notifications */
|
||||
sub_params.notify = cb;
|
||||
sub_params.subscribe = sub_cb;
|
||||
sub_params.value = BT_GATT_CCC_NOTIFY;
|
||||
sub_params.value_handle = handle;
|
||||
|
||||
/* Set-up auto-discovery of the CCC handle */
|
||||
sub_params.ccc_handle = 0;
|
||||
sub_params.disc_params = &ccc_disc_params;
|
||||
sub_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
|
||||
|
||||
err = bt_gatt_subscribe(conn, &sub_params);
|
||||
TEST_ASSERT(!err, "Subscribe failed (err %d)", err);
|
||||
|
||||
WAIT_FOR_FLAG(is_subscribed);
|
||||
}
|
||||
|
||||
static void unsubscribe_but_not_really(struct bt_conn *conn, uint16_t handle)
|
||||
{
|
||||
/* Here we do something slightly different:
|
||||
*
|
||||
* Since we want to still be able to receive the notification, we don't
|
||||
* actually want to unsubscribe. We only want to make the server *think*
|
||||
* we have unsubscribed in order to test that
|
||||
* CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION works properly.
|
||||
*
|
||||
* So we just write a 0 to the CCC handle, that should do the trick.
|
||||
*/
|
||||
uint8_t data[1] = {0};
|
||||
|
||||
int err = bt_testlib_att_write(conn, BT_ATT_CHAN_OPT_NONE,
|
||||
sub_params.ccc_handle, data, sizeof(data));
|
||||
|
||||
TEST_ASSERT(!err, "Unsubscribe failed: err %d", err);
|
||||
}
|
||||
|
||||
/* Read the comments on `entrypoint_dut()` first. */
|
||||
void entrypoint_peer(void)
|
||||
{
|
||||
int err;
|
||||
struct bt_conn *conn;
|
||||
uint16_t handle;
|
||||
|
||||
/* Mark test as in progress. */
|
||||
TEST_START("peer");
|
||||
|
||||
/* Initialize device sync library */
|
||||
bk_sync_init();
|
||||
|
||||
/* Initialize Bluetooth */
|
||||
err = bt_enable(NULL);
|
||||
TEST_ASSERT(err == 0, "Can't enable Bluetooth (err %d)", err);
|
||||
|
||||
LOG_DBG("Bluetooth initialized");
|
||||
|
||||
err = bt_testlib_adv_conn(&conn, BT_ID_DEFAULT,
|
||||
(BT_LE_ADV_OPT_USE_NAME | BT_LE_ADV_OPT_FORCE_NAME_IN_AD));
|
||||
TEST_ASSERT(!err, "Failed to start connectable advertising (err %d)", err);
|
||||
|
||||
LOG_DBG("Discover test characteristic");
|
||||
err = find_characteristic(conn, test_service_uuid, test_characteristic_uuid, &handle);
|
||||
TEST_ASSERT(!err, "Failed to find characteristic: %d", err);
|
||||
|
||||
LOG_DBG("Subscribe to test characteristic: handle 0x%04x", handle);
|
||||
subscribe(conn, handle, received_notification);
|
||||
|
||||
WAIT_FOR_FLAG(got_notification_1);
|
||||
|
||||
LOG_DBG("Unsubscribe from test characteristic: handle 0x%04x", handle);
|
||||
unsubscribe_but_not_really(conn, handle);
|
||||
|
||||
WAIT_FOR_FLAG(got_notification_2);
|
||||
bk_sync_send();
|
||||
|
||||
/* Disconnect and destroy connection object */
|
||||
LOG_DBG("Disconnect");
|
||||
err = bt_testlib_disconnect(&conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
|
||||
TEST_ASSERT(!err, "Failed to disconnect (err %d)", err);
|
||||
|
||||
TEST_PASS("peer");
|
||||
}
|
13
tests/bsim/bluetooth/host/misc/sample_test/test_scripts/_compile.sh
Executable file
13
tests/bsim/bluetooth/host/misc/sample_test/test_scripts/_compile.sh
Executable file
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2023 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
set -eu
|
||||
: "${ZEPHYR_BASE:?ZEPHYR_BASE must be defined}"
|
||||
|
||||
INCR_BUILD=1
|
||||
|
||||
source ${ZEPHYR_BASE}/tests/bsim/compile.source
|
||||
|
||||
app="$(guess_test_relpath)" compile
|
||||
|
||||
wait_for_background_jobs
|
78
tests/bsim/bluetooth/host/misc/sample_test/test_scripts/run.sh
Executable file
78
tests/bsim/bluetooth/host/misc/sample_test/test_scripts/run.sh
Executable file
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2024 Nordic Semiconductor
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set -eu
|
||||
|
||||
# Provides common functions for bsim tests.
|
||||
# Mainly `Execute`, and `wait_for_background_jobs`.
|
||||
source ${ZEPHYR_BASE}/tests/bsim/sh_common.source
|
||||
|
||||
# Helper variable. Expands to "tests_bsim_bluetooth_host_misc_sample_test".
|
||||
test_name="$(guess_test_long_name)"
|
||||
|
||||
# Use a unique simulation id per test script. It is a necessity as the CI runner
|
||||
# will run all the test scripts in parallel. If multiple simulations share the
|
||||
# same ID, they will step on each other's toes in unpredictable ways.
|
||||
simulation_id=${test_name}
|
||||
|
||||
# This controls the verbosity of the simulator. It goes up to 9 (not 11, sorry)
|
||||
# and is useful for figuring out what is happening on the PHYsical layer (e.g.
|
||||
# device 1 starts listening at T=100us) among other things.
|
||||
# `2` is the default value if not specified.
|
||||
verbosity_level=2
|
||||
|
||||
# This sets the watchdog timeout for the `Execute` function.
|
||||
#
|
||||
# Although all simulations are started with a bounded end (the `sim_length`
|
||||
# option), something very wrong can still happen and this additional time-out
|
||||
# will ensure all executables started by the current script are killed.
|
||||
#
|
||||
# It measures wall-clock time, not simulated time. E.g. a test that simulates 5
|
||||
# hours might actually complete (have a runtime of) 10 seconds.
|
||||
#
|
||||
# The default is set in `sh_common.source`.
|
||||
# Guidelines to set this value:
|
||||
# - Do not set it to a value lower or equal to the default.
|
||||
# - If the test takes over 5 seconds of runtime, set `EXECUTE_TIMEOUT` to at
|
||||
# least 5 times the run-time on your machine.
|
||||
EXECUTE_TIMEOUT=120
|
||||
|
||||
# Set simulation length, in microseconds. The PHY will run for this amount of
|
||||
# simulated time, unless both devices exit.
|
||||
#
|
||||
# If you are not early-exiting the devices (e.g. using `TEST_PASS_AND_EXIT()`),
|
||||
# please run the test once and set the simulation time in the same ballpark. No
|
||||
# need to simulate hours of runtime if the test finishes in 10 seconds.
|
||||
#
|
||||
SIM_LEN_US=$((2 * 1000 * 1000))
|
||||
|
||||
# This is the final path of the test executable.
|
||||
#
|
||||
# Some tests may have different executable images for each device in the test.
|
||||
#
|
||||
# In our case, both test cases are compiled in the same image, and the right one
|
||||
# will be run depending on what arguments we give the executable.
|
||||
test_exe="${BSIM_OUT_PATH}/bin/bs_${BOARD_TS}_${test_name}_prj_conf"
|
||||
|
||||
# BabbleSim will by default search for its shared libraries assuming it is
|
||||
# running in the bin/ directory. Test results will also be placed in
|
||||
# `${BSIM_OUT_PATH}/results` if not specified.
|
||||
cd ${BSIM_OUT_PATH}/bin
|
||||
|
||||
# Instantiate all devices in the simulation.
|
||||
# The `testid` parameter is used to run the right role or procedure (here "dut" vs "tester").
|
||||
Execute "${test_exe}" -v=${verbosity_level} -s=${simulation_id} -d=0 -rs=420 -testid=dut
|
||||
Execute "${test_exe}" -v=${verbosity_level} -s=${simulation_id} -d=1 -rs=69 -testid=peer
|
||||
|
||||
# Start the PHY. Double-check the `-D` parameter: it has to match the number of
|
||||
# devices started in the lines above.
|
||||
#
|
||||
# Also set a maximum simulation length. If the devices have not set a special
|
||||
# variable indicating they have passed before the simulation runs out of time,
|
||||
# the test will be reported as "in progress (not passed)".
|
||||
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} -D=2 -sim_length=${SIM_LEN_US} $@
|
||||
|
||||
# Wait until all executables started above have returned.
|
||||
# The exit code returned will be != 0 if any of them have failed.
|
||||
wait_for_background_jobs
|
Loading…
Reference in a new issue