diff --git a/doc/develop/test/bsim.rst b/doc/develop/test/bsim.rst index 596d2911fdf..d2f98295dc7 100644 --- a/doc/develop/test/bsim.rst +++ b/doc/develop/test/bsim.rst @@ -132,6 +132,12 @@ and then directly run one of the tests: Conventions =========== +Test code +--------- + +See the :zephyr_file:`Bluetooth sample test ` for conventions that apply to test +code. + Build scripts ------------- diff --git a/tests/bsim/bluetooth/host/compile.sh b/tests/bsim/bluetooth/host/compile.sh index de3fd1cd940..cc0d49ea360 100755 --- a/tests/bsim/bluetooth/host/compile.sh +++ b/tests/bsim/bluetooth/host/compile.sh @@ -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 diff --git a/tests/bsim/bluetooth/host/misc/sample_test/CMakeLists.txt b/tests/bsim/bluetooth/host/misc/sample_test/CMakeLists.txt new file mode 100644 index 00000000000..8ae3a088a42 --- /dev/null +++ b/tests/bsim/bluetooth/host/misc/sample_test/CMakeLists.txt @@ -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 +) diff --git a/tests/bsim/bluetooth/host/misc/sample_test/Kconfig b/tests/bsim/bluetooth/host/misc/sample_test/Kconfig new file mode 100644 index 00000000000..d1289e2c121 --- /dev/null +++ b/tests/bsim/bluetooth/host/misc/sample_test/Kconfig @@ -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" diff --git a/tests/bsim/bluetooth/host/misc/sample_test/README.rst b/tests/bsim/bluetooth/host/misc/sample_test/README.rst new file mode 100644 index 00000000000..a14244e9a98 --- /dev/null +++ b/tests/bsim/bluetooth/host/misc/sample_test/README.rst @@ -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 ` 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 `. +#. ``test_scripts/run.sh`` +#. ``CMakeLists.txt`` +#. ``src/dut.c`` and ``src/peer.c`` +#. ``src/main.c`` + +.. _BabbleSim: + https://BabbleSim.github.io diff --git a/tests/bsim/bluetooth/host/misc/sample_test/prj.conf b/tests/bsim/bluetooth/host/misc/sample_test/prj.conf new file mode 100644 index 00000000000..19d767d93ef --- /dev/null +++ b/tests/bsim/bluetooth/host/misc/sample_test/prj.conf @@ -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 diff --git a/tests/bsim/bluetooth/host/misc/sample_test/src/data.h b/tests/bsim/bluetooth/host/misc/sample_test/src/data.h new file mode 100644 index 00000000000..48feee06c49 --- /dev/null +++ b/tests/bsim/bluetooth/host/misc/sample_test/src/data.h @@ -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 + +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_ */ diff --git a/tests/bsim/bluetooth/host/misc/sample_test/src/dut.c b/tests/bsim/bluetooth/host/misc/sample_test/src/dut.c new file mode 100644 index 00000000000..ea6d62f3ded --- /dev/null +++ b/tests/bsim/bluetooth/host/misc/sample_test/src/dut.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#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"); +} diff --git a/tests/bsim/bluetooth/host/misc/sample_test/src/main.c b/tests/bsim/bluetooth/host/misc/sample_test/src/main.c new file mode 100644 index 00000000000..278cdc2090e --- /dev/null +++ b/tests/bsim/bluetooth/host/misc/sample_test/src/main.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#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; +} diff --git a/tests/bsim/bluetooth/host/misc/sample_test/src/peer.c b/tests/bsim/bluetooth/host/misc/sample_test/src/peer.c new file mode 100644 index 00000000000..475043c0d3a --- /dev/null +++ b/tests/bsim/bluetooth/host/misc/sample_test/src/peer.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#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"); +} diff --git a/tests/bsim/bluetooth/host/misc/sample_test/test_scripts/_compile.sh b/tests/bsim/bluetooth/host/misc/sample_test/test_scripts/_compile.sh new file mode 100755 index 00000000000..e717a4b2bbe --- /dev/null +++ b/tests/bsim/bluetooth/host/misc/sample_test/test_scripts/_compile.sh @@ -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 diff --git a/tests/bsim/bluetooth/host/misc/sample_test/test_scripts/run.sh b/tests/bsim/bluetooth/host/misc/sample_test/test_scripts/run.sh new file mode 100755 index 00000000000..431d7c2064e --- /dev/null +++ b/tests/bsim/bluetooth/host/misc/sample_test/test_scripts/run.sh @@ -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