Tests: Bluetooth: Mesh: BLOB cli push & pull trans resume

Add bsim test for resuming push & pull mode transfer on the BLOB client
after BLOB server has been suspended during initial attempt.

Signed-off-by: Anders Storrø <anders.storro@nordicsemi.no>
This commit is contained in:
Anders Storrø 2022-04-24 13:58:05 +02:00 committed by Carles Cufí
commit cb2bc394df
5 changed files with 328 additions and 1 deletions

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include "mesh_test.h"
#include "argparse.h"
#include <bs_pc_backchannel.h>
#define LOG_MODULE_NAME mesh_test
@ -487,3 +489,78 @@ void bt_mesh_test_ra_cb_setup(void (*cb)(uint8_t *, size_t))
{
ra_cb = cb;
}
void bt_mesh_test_sync_init(struct bt_mesh_test_sync_ctx *ctx)
{
ctx->chan_id = bs_open_back_channel(get_device_nbr(),
ctx->dev_nmbr,
ctx->chan_nmbr, ctx->cnt);
}
static bool wait_for_sync(uint32_t channel_id, int *wait, uint8_t msg_len)
{
int size;
while (true) {
size = bs_bc_is_msg_received(channel_id);
if (size < 0) {
FAIL("Sync channel error: %d", size);
} else if (size > 0) {
ASSERT_EQUAL(size, msg_len);
return true;
} else if (*wait <= 0) {
return false;
}
k_sleep(K_MSEC(100));
*wait -= 100;
}
}
bool bt_mesh_test_sync_multi(struct bt_mesh_test_sync_ctx *ctx, uint32_t channel, uint16_t wait_sec)
{
const uint32_t barrier_msg = 0xC0FFEE;
uint32_t recv_msg;
int wait = wait_sec * MSEC_PER_SEC;
for (int i = 0; i < ctx->cnt; i++) {
if (ctx->chan_nmbr[i] != channel) {
continue;
}
if (!wait_for_sync(ctx->chan_id[i], &wait, sizeof(barrier_msg))) {
return false;
}
bs_bc_receive_msg(ctx->chan_id[i], (uint8_t *)&recv_msg, sizeof(recv_msg));
ASSERT_EQUAL(barrier_msg, recv_msg);
}
for (int i = 0; i < ctx->cnt; i++) {
if (ctx->chan_nmbr[i] != channel) {
continue;
}
bs_bc_send_msg(ctx->chan_id[i], (uint8_t *)&barrier_msg, sizeof(barrier_msg));
}
return true;
}
bool bt_mesh_test_sync(uint32_t channel_id, uint16_t wait_sec)
{
const uint32_t barrier_msg = 0xC0FFEE;
uint32_t recv_msg;
int wait = wait_sec * MSEC_PER_SEC;
bs_bc_send_msg(channel_id, (uint8_t *)&barrier_msg, sizeof(barrier_msg));
if (!wait_for_sync(channel_id, &wait, sizeof(barrier_msg))) {
return false;
}
bs_bc_receive_msg(channel_id, (uint8_t *)&recv_msg, sizeof(recv_msg));
ASSERT_EQUAL(barrier_msg, recv_msg);
return true;
}

View file

@ -116,6 +116,13 @@ struct bt_mesh_test_msg {
struct bt_mesh_msg_ctx ctx;
};
struct bt_mesh_test_sync_ctx {
uint32_t *dev_nmbr;
uint32_t *chan_nmbr;
uint32_t *chan_id;
uint16_t cnt;
};
extern enum bst_result_t bst_result;
extern const struct bt_mesh_test_cfg *cfg;
extern struct bt_mesh_model *test_model;
@ -146,4 +153,10 @@ int bt_mesh_test_send_ra(uint16_t addr, uint8_t *data, size_t len,
const struct bt_mesh_send_cb *send_cb,
void *cb_data);
void bt_mesh_test_ra_cb_setup(void (*cb)(uint8_t *, size_t));
void bt_mesh_test_sync_init(struct bt_mesh_test_sync_ctx *ctx);
bool bt_mesh_test_sync_multi(struct bt_mesh_test_sync_ctx *ctx, uint32_t channel,
uint16_t wait_sec);
bool bt_mesh_test_sync(uint32_t channel_id, uint16_t wait_sec);
#endif /* ZEPHYR_TESTS_BLUETOOTH_BSIM_BT_BSIM_TEST_MESH_MESH_TEST_H_ */

View file

@ -6,6 +6,7 @@
#include "mesh_test.h"
#include "mesh/blob.h"
#include "argparse.h"
#include "mesh/adv.h"
#define LOG_MODULE_NAME test_blob
@ -15,6 +16,73 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_INF);
#define BLOB_GROUP_ADDR 0xc000
#define BLOB_CLI_ADDR 0x0001
#define MODEL_LIST(...) ((struct bt_mesh_model[]){ __VA_ARGS__ })
#define SYNC_CHAN 0
#define CLI_DEV 0
#define SRV1_DEV 1
static bool is_pull_mode;
static void test_args_parse(int argc, char *argv[])
{
bs_args_struct_t args_struct[] = {
{
.dest = &is_pull_mode,
.type = 'b',
.name = "{0, 1}",
.option = "use-pull-mode",
.descript = "Set transfer type to pull mode"
},
};
bs_args_parse_all_cmd_line(argc, argv, args_struct);
}
static int blob_io_open(const struct bt_mesh_blob_io *io,
const struct bt_mesh_blob_xfer *xfer,
enum bt_mesh_blob_io_mode mode)
{
return 0;
}
static struct k_sem first_block_wr_sem;
static uint16_t partial_block;
ATOMIC_DEFINE(block_bitfield, 8);
static int blob_chunk_wr(const struct bt_mesh_blob_io *io,
const struct bt_mesh_blob_xfer *xfer,
const struct bt_mesh_blob_block *block,
const struct bt_mesh_blob_chunk *chunk)
{
partial_block += chunk->size;
ASSERT_TRUE(partial_block <= block->size, "Received block is too large");
if (partial_block == block->size) {
partial_block = 0;
ASSERT_FALSE(atomic_test_and_set_bit(block_bitfield, block->number),
"Received duplicate block");
}
if (atomic_test_bit(block_bitfield, 0)) {
k_sem_give(&first_block_wr_sem);
}
return 0;
}
static int blob_chunk_rd(const struct bt_mesh_blob_io *io,
const struct bt_mesh_blob_xfer *xfer,
const struct bt_mesh_blob_block *block,
const struct bt_mesh_blob_chunk *chunk)
{
return 0;
}
static const struct bt_mesh_blob_io blob_io = {
.open = blob_io_open,
.rd = blob_chunk_rd,
.wr = blob_chunk_wr,
};
static uint8_t dev_key[16] = { 0xdd };
static uint8_t app_key[16] = { 0xaa };
@ -23,7 +91,8 @@ static struct bt_mesh_prov prov;
static struct {
struct bt_mesh_blob_cli_inputs inputs;
struct bt_mesh_blob_target targets[6];
struct bt_mesh_blob_target targets[5];
struct bt_mesh_blob_target_pull pull[5];
uint8_t target_count;
struct bt_mesh_blob_xfer xfer;
} blob_cli_xfer;
@ -91,21 +160,33 @@ static void blob_cli_lost_target(struct bt_mesh_blob_cli *b, struct bt_mesh_blob
}
}
static struct k_sem blob_cli_suspend_sem;
static void blob_cli_suspended(struct bt_mesh_blob_cli *b)
{
k_sem_give(&blob_cli_suspend_sem);
}
static struct k_sem blob_cli_end_sem;
static void blob_cli_end(struct bt_mesh_blob_cli *b, const struct bt_mesh_blob_xfer *xfer,
bool success)
{
k_sem_give(&blob_cli_end_sem);
}
static struct k_sem blob_srv_suspend_sem;
static void blob_srv_suspended(struct bt_mesh_blob_srv *b)
{
k_sem_give(&blob_srv_suspend_sem);
}
static struct k_sem blob_srv_end_sem;
static void blob_srv_end(struct bt_mesh_blob_srv *b, uint64_t id, bool success)
{
k_sem_give(&blob_srv_end_sem);
}
static int blob_srv_recover(struct bt_mesh_blob_srv *b, struct bt_mesh_blob_xfer *xfer,
@ -268,6 +349,7 @@ static void blob_cli_inputs_prepare(uint16_t group)
memset(&blob_cli_xfer.targets[i], 0, sizeof(struct bt_mesh_blob_target));
blob_cli_xfer.targets[i].addr = addr;
blob_cli_xfer.targets[i].pull = &blob_cli_xfer.pull[i];
sys_slist_append(&blob_cli_xfer.inputs.targets, &blob_cli_xfer.targets[i].n);
}
@ -800,10 +882,145 @@ static void test_cli_broadcast_unicast(void)
PASS();
}
static void test_cli_trans_resume(void)
{
int err;
struct bt_mesh_test_sync_ctx sync = {
.chan_nmbr = (uint32_t[]){ SYNC_CHAN },
.dev_nmbr = (uint32_t[]){ SRV1_DEV },
.cnt = 1
};
bt_mesh_test_sync_init(&sync);
tm_set_phy_max_resync_offset(100000);
bt_mesh_test_cfg_set(NULL, 800);
bt_mesh_device_setup(&prov, &cli_comp);
blob_cli_prov_and_conf(BLOB_CLI_ADDR);
(void)target_srv_add(BLOB_CLI_ADDR + 1, true);
k_sem_init(&blob_caps_sem, 0, 1);
k_sem_init(&lost_target_sem, 0, 1);
k_sem_init(&blob_cli_end_sem, 0, 1);
k_sem_init(&blob_cli_suspend_sem, 0, 1);
LOG_INF("Running transfer in %s", is_pull_mode ? "Pull mode" : "Push mode");
/** Test resumption of suspended BLOB transfer (Push).
* Client initiates transfer with two blocks. After
* first block completes the server will be suspended.
* At this point the client will attempt to resume the
* transfer.
*/
blob_cli_inputs_prepare(BLOB_GROUP_ADDR);
blob_cli_xfer.xfer.mode =
is_pull_mode ? BT_MESH_BLOB_XFER_MODE_PULL : BT_MESH_BLOB_XFER_MODE_PUSH;
blob_cli_xfer.xfer.size = CONFIG_BT_MESH_BLOB_BLOCK_SIZE_MIN * 2;
blob_cli_xfer.xfer.id = 1;
blob_cli_xfer.xfer.block_size_log = 12;
blob_cli_xfer.xfer.chunk_size = 377;
blob_cli_xfer.inputs.timeout_base = 10;
err = bt_mesh_blob_cli_send(&blob_cli, &blob_cli_xfer.inputs,
&blob_cli_xfer.xfer, &blob_io);
if (err) {
FAIL("BLOB send failed (err: %d)", err);
}
if (k_sem_take(&blob_cli_suspend_sem, K_SECONDS(500))) {
FAIL("Suspend CB did not trigger as expected for the cli");
}
if (k_sem_take(&lost_target_sem, K_NO_WAIT)) {
FAIL("Lost targets CB did not trigger for the target srv");
}
ASSERT_TRUE(blob_cli.state == BT_MESH_BLOB_CLI_STATE_SUSPENDED);
/* Sync with the server device to enable scanning again. */
ASSERT_TRUE(bt_mesh_test_sync(sync.chan_id[0], 3));
/* Initiate resumption of BLOB transfer */
err = bt_mesh_blob_cli_resume(&blob_cli);
if (err) {
FAIL("BLOB resume failed (err: %d)", err);
}
if (k_sem_take(&blob_cli_end_sem, K_SECONDS(180))) {
FAIL("End CB did not trigger as expected for the cli");
}
ASSERT_TRUE(blob_cli.state == BT_MESH_BLOB_CLI_STATE_NONE);
PASS();
}
static void test_srv_trans_resume(void)
{
struct bt_mesh_test_sync_ctx sync = {
.chan_nmbr = (uint32_t[]){ SYNC_CHAN },
.dev_nmbr = (uint32_t[]){ 0 },
.cnt = 1
};
bt_mesh_test_sync_init(&sync);
tm_set_phy_max_resync_offset(100000);
bt_mesh_test_cfg_set(NULL, 800);
bt_mesh_device_setup(&prov, &srv_comp);
blob_srv_prov_and_conf(own_addr_get());
k_sem_init(&first_block_wr_sem, 0, 1);
k_sem_init(&blob_srv_end_sem, 0, 1);
k_sem_init(&blob_srv_suspend_sem, 0, 1);
/** Wait for a first blob block to be received, then simulate radio
* disruption to cause suspension of the blob srv. Re-enable the radio
* as soon as the server is suspended and wait to receive the second
* block.
*/
bt_mesh_blob_srv_recv(&blob_srv, 1, &blob_io, 0, 1);
/* Let server receive a couple of chunks from second block before disruption */
for (int i = 0; i < 3; i++) {
if (k_sem_take(&first_block_wr_sem, K_SECONDS(180))) {
FAIL("Server did not receive the first BLOB block");
}
}
bt_mesh_scan_disable();
partial_block = 0;
if (k_sem_take(&blob_srv_suspend_sem, K_SECONDS(30))) {
FAIL("Suspend CB did not trigger as expected for the srv");
}
ASSERT_TRUE(blob_srv.phase == BT_MESH_BLOB_XFER_PHASE_SUSPENDED);
/* Wait for BLOB client to suspend */
ASSERT_TRUE(bt_mesh_test_sync(sync.chan_id[0], 400));
bt_mesh_scan_enable();
if (k_sem_take(&blob_srv_end_sem, K_SECONDS(180))) {
FAIL("End CB did not trigger as expected for the srv");
}
ASSERT_TRUE(blob_srv.phase == BT_MESH_BLOB_XFER_PHASE_COMPLETE);
/* Check that all blocks is received */
ASSERT_TRUE(atomic_test_bit(block_bitfield, 0));
ASSERT_TRUE(atomic_test_bit(block_bitfield, 1));
/* Check that a third block was not received */
ASSERT_FALSE(atomic_test_bit(block_bitfield, 3));
PASS();
}
#define TEST_CASE(role, name, description) \
{ \
.test_id = "blob_" #role "_" #name, \
.test_descr = description, \
.test_args_f = test_args_parse, \
.test_tick_f = bt_mesh_test_timeout, \
.test_main_f = test_##role##_##name, \
}
@ -817,9 +1034,11 @@ static const struct bst_test_instance test_blob[] = {
TEST_CASE(cli, broadcast_trans, "Test all broadcast transmission types"),
TEST_CASE(cli, broadcast_unicast_seq, "Test broadcast with unicast addr (Sequential)"),
TEST_CASE(cli, broadcast_unicast, "Test broadcast with unicast addr"),
TEST_CASE(cli, trans_resume, "Resume BLOB transfer after srv suspension (Default: Push)"),
TEST_CASE(srv, caps_standard, "Standard responsive blob server"),
TEST_CASE(srv, caps_no_rsp, "Non-responsive blob server"),
TEST_CASE(srv, trans_resume, "Self suspending server after first received block"),
BSTEST_END_MARKER
};

View file

@ -0,0 +1,9 @@
#!/usr/bin/env bash
# Copyright 2022 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh
# Test that BLOB Client can resume a suspended BLOB Transfer in Pull mode
conf=prj_mesh1d1_conf
RunTest blob_resume_pull blob_cli_trans_resume blob_srv_trans_resume -- -argstest use-pull-mode=1

View file

@ -0,0 +1,9 @@
#!/usr/bin/env bash
# Copyright 2022 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh
# Test that BLOB Client can resume a suspended BLOB Transfer in Push mode
conf=prj_mesh1d1_conf
RunTest blob_resume_push blob_cli_trans_resume blob_srv_trans_resume