tests: Bluetooth: Add common helpers for bsim

This code can be found (kinda) duplicated all over
`tests/bsim/bluetooth`.

Put it in a common place.

Refactoring the current tests will be done in a future commit.

Signed-off-by: Jonathan Rico <jonathan.rico@nordicsemi.no>
This commit is contained in:
Jonathan Rico 2024-04-03 14:35:36 +02:00 committed by Anas Nashif
parent 936598ddf1
commit 389192a94d
5 changed files with 296 additions and 0 deletions

View file

@ -0,0 +1,19 @@
# Copyright (c) 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
# Helpers that can be used on bsim targets but don't have other
# dependencies (e.g. on Bluetooth, etc).
add_library(babblekit)
target_link_libraries(babblekit PUBLIC
kernel
zephyr_interface
)
target_include_directories(babblekit PUBLIC
include
)
target_sources(babblekit PRIVATE
src/sync.c
)

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* Provides a way to set/clear and block on binary flags.
*
* These flags are often used to wait until the test has gotten in a particular
* state, e.g. a connection is established or a message has been successfully
* sent.
*
* These macros can only be called from Zephyr threads. They can't be called
* from e.g. a bs_tests callback.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/sys/atomic.h>
/* Declare a flag that has been defined in another file */
#define DECLARE_FLAG(flag) extern atomic_t flag
/* Define a new binary flag.
* Declare them static if defining flags with the same name in multiple files.
*
* @param flag Name of the flag
*/
#define DEFINE_FLAG(flag) atomic_t flag = (atomic_t) false
#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t) true)
#define UNSET_FLAG(flag) (void)atomic_set(&flag, (atomic_t) false)
#define IS_FLAG_SET(flag) (atomic_get(&flag) != false)
/* Block until `flag` is equal to `val` */
#define WAIT_FOR_VAL(var, val) \
while (atomic_get(&var) != val) { \
(void)k_sleep(K_MSEC(1)); \
}
/* Block until `flag` is true */
#define WAIT_FOR_FLAG(flag) \
while (!(bool)atomic_get(&flag)) { \
(void)k_sleep(K_MSEC(1)); \
}
/* Block until `flag` is false */
#define WAIT_FOR_FLAG_UNSET(flag) \
while ((bool)atomic_get(&flag)) { \
(void)k_sleep(K_MSEC(1)); \
}
/* Block until `flag` is true and set it to false */
#define TAKE_FLAG(flag) \
while (!(bool)atomic_cas(&flag, true, false)) { \
(void)k_sleep(K_MSEC(1)); \
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*
* This file provides a synchronization mechanism between devices, for the
* simple use-case when there are only two devices in the simulation.
*/
/*
* @brief Initialize the sync library
*
* This initializes a simple synchronization library based on bsim backchannels.
*
* Calling `bk_sync_wait()` on device A will make it block until
* `bk_sync_send()` is called on device B.
*
* @note Only works between two devices in a simulation, with IDs 0 and 1.
*
* @retval 0 Sync channel operational
* @retval -1 Failed to open sync channel
*
*/
int bk_sync_init(void);
/*
* @brief Send a synchronization packet
*
* @note Only works between two devices in a simulation, with IDs 0 and 1.
*
*/
void bk_sync_send(void);
/*
* @brief Wait for a synchronization packet
*
* This blocks until the other device has called `bk_sync_send()`.
*
* @note Only works between two devices in a simulation, with IDs 0 and 1.
*
*/
void bk_sync_wait(void);

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bs_tracing.h"
#include "bs_types.h"
#include "bstests.h"
extern enum bst_result_t bst_result;
/*
* @brief Mark the test as in progress
*
* Call this at the start of the test entry point.
*
* @param ... format-string and arguments to print to console
*
*/
#define TEST_START(msg, ...) \
do { \
bst_result = In_progress; \
bs_trace_info_time(2, "Test start: " msg "\n", ##__VA_ARGS__); \
} while (0)
/*
* @brief Fail the test and exit
*
* Printf-like function that also terminates the device with an error code.
*
* @param ... format-string and arguments to print to console
*
*/
#define TEST_FAIL(msg, ...) \
do { \
bst_result = Failed; \
bs_trace_error_time_line(msg "\n", ##__VA_ARGS__); \
} while (0)
/*
* @brief Mark the currently-running test as "Passed"
*
* Mark the test as "passed". The execution continues after that point.
*
* @note Use this if you use backchannels (testlib/bsim/sync.h).
*
* After calling this, the executable will eventually return 0 when it exits.
* Unless `TEST_FAIL` is called (e.g. in a callback).
*
* @param ... format-string and arguments to print to console
*
*/
#define TEST_PASS(msg, ...) \
do { \
bst_result = Passed; \
bs_trace_info_time(2, "Test end: " msg "\n", ##__VA_ARGS__); \
} while (0)
/*
* @brief Mark test case as passed and end execution
*
* Mark the role / test-case as "Passed" and return 0.
*
* @note DO NOT use this if you use backchannels (testlib/bsim/sync.h).
*
* @note This macro only ends execution for the current executable, not all
* executables in a simulation.
*
* @param ... format-string and arguments to print to console
*
*/
#define TEST_PASS_AND_EXIT(msg, ...) \
do { \
bst_result = Passed; \
bs_trace_info_time(2, "Test end: " msg "\n", ##__VA_ARGS__); \
bs_trace_silent_exit(0); \
} while (0)
/*
* @brief Assert `expr` is true
*
* Assert that `expr` is true. If assertion is false, print a printf-like
* message to the console and fail the test. I.e. return non-zero.
*
* @note This is different than `sys/__assert.h`.
*
* @param message String to print to console
*
*/
#define TEST_ASSERT(expr, ...) \
do { \
if (!(expr)) { \
TEST_FAIL(__VA_ARGS__); \
} \
} while (0)
/*
* @brief Print a value. Lower-level than `printk` or `LOG_xx`.
*
* Print a message to console.
*
* This can be safely called at any time in the execution of the device.
* Use it to print when the logging subsystem is not available, e.g. early
* startup or shutdown.
*
* @param ... format-string and arguments to print to console
*
*/
#define TEST_PRINT(msg, ...) \
bs_trace_print(BS_TRACE_INFO, __FILE__, __LINE__, 0, BS_TRACE_AUTOTIME, 0, \
msg "\n", ##__VA_ARGS__)

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/sys/util.h>
#include "argparse.h"
#include "bs_types.h"
#include "bs_tracing.h"
#include "time_machine.h"
#include "bs_pc_backchannel.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(sync, CONFIG_LOG_DEFAULT_LEVEL);
#define CHANNEL_ID 0
#define MSG_SIZE 1
int bk_sync_init(void)
{
uint device_number = get_device_nbr();
uint peer_number = device_number ^ 1;
uint device_numbers[] = { peer_number };
uint channel_numbers[] = { CHANNEL_ID };
uint *ch;
ch = bs_open_back_channel(device_number, device_numbers, channel_numbers,
ARRAY_SIZE(channel_numbers));
if (!ch) {
return -1;
}
LOG_DBG("Sync initialized");
return 0;
}
void bk_sync_send(void)
{
uint8_t sync_msg[MSG_SIZE] = { get_device_nbr() };
LOG_DBG("Sending sync");
bs_bc_send_msg(CHANNEL_ID, sync_msg, ARRAY_SIZE(sync_msg));
}
void bk_sync_wait(void)
{
uint8_t sync_msg[MSG_SIZE];
LOG_DBG("Waiting for sync");
while (true) {
if (bs_bc_is_msg_received(CHANNEL_ID) > 0) {
bs_bc_receive_msg(CHANNEL_ID, sync_msg, ARRAY_SIZE(sync_msg));
if (sync_msg[0] != get_device_nbr()) {
/* Received a message from another device, exit */
break;
}
}
k_sleep(K_MSEC(1));
}
LOG_DBG("Sync received");
}