Bluetooth: Audio: implement the distribute broadcast code procedure

Implemenation of the distribute broadcast code CAP procedure, as well
as unittesting

Signed-off-by: Andries Kruithof <andries.kruithof@nordicsemi.no>
This commit is contained in:
Andries Kruithof 2024-10-31 13:36:10 +01:00 committed by Henrik Brix Andersen
commit bb3bab7a34
10 changed files with 500 additions and 2 deletions

View file

@ -19,4 +19,5 @@ target_sources(testbinary
src/test_vcp.c
src/test_micp.c
src/test_broadcast_reception.c
src/test_distribute_broadcast_code.c
)

View file

@ -25,5 +25,6 @@ DECLARE_FAKE_VOID_FUNC(mock_cap_commander_microphone_mute_changed_cb, struct bt_
DECLARE_FAKE_VOID_FUNC(mock_cap_commander_microphone_gain_changed_cb, struct bt_conn *, int);
DECLARE_FAKE_VOID_FUNC(mock_cap_commander_broadcast_reception_start_cb, struct bt_conn *, int);
DECLARE_FAKE_VOID_FUNC(mock_cap_commander_broadcast_reception_stop_cb, struct bt_conn *, int);
DECLARE_FAKE_VOID_FUNC(mock_cap_commander_distribute_broadcast_code_cb, struct bt_conn *, int);
#endif /* MOCKS_CAP_COMMANDER_H_ */

View file

@ -6,6 +6,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
#define BROADCAST_CODE "BroadcastCode"
#define RANDOM_SRC_ID 0x55
void test_mocks_init(void);
void test_mocks_cleanup(void);

View file

@ -0,0 +1,257 @@
/* test_distribute_broadcast_code.c - unit test for distribute broadcast code */
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <zephyr/bluetooth/audio/cap.h>
#include <zephyr/fff.h>
#include "bluetooth.h"
#include "cap_commander.h"
#include "conn.h"
#include "expects_util.h"
#include "cap_mocks.h"
#include "test_common.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_distribute_broadcast_code, CONFIG_BT_CAP_COMMANDER_LOG_LEVEL);
#define FFF_GLOBALS
struct cap_commander_test_distribute_broadcast_code_fixture {
struct bt_conn conns[CONFIG_BT_MAX_CONN];
struct bt_bap_bass_subgroup subgroups[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS];
struct bt_cap_commander_broadcast_reception_start_member_param
start_member_params[CONFIG_BT_MAX_CONN];
struct bt_cap_commander_broadcast_reception_start_param start_param;
struct bt_cap_commander_distribute_broadcast_code_member_param
broadcast_code_member_params[CONFIG_BT_MAX_CONN];
struct bt_cap_commander_distribute_broadcast_code_param distribute_broadcast_code_param;
struct bt_bap_broadcast_assistant_cb broadcast_assistant_cb;
};
static void cap_commander_test_distribute_broadcast_code_fixture_init(
struct cap_commander_test_distribute_broadcast_code_fixture *fixture)
{
for (size_t i = 0; i < ARRAY_SIZE(fixture->conns); i++) {
test_conn_init(&fixture->conns[i]);
fixture->conns[i].index = i;
}
fixture->distribute_broadcast_code_param.type = BT_CAP_SET_TYPE_AD_HOC;
fixture->distribute_broadcast_code_param.param = fixture->broadcast_code_member_params;
fixture->distribute_broadcast_code_param.count =
ARRAY_SIZE(fixture->broadcast_code_member_params);
memcpy(fixture->distribute_broadcast_code_param.broadcast_code, BROADCAST_CODE,
sizeof(BROADCAST_CODE));
for (size_t i = 0; i < ARRAY_SIZE(fixture->broadcast_code_member_params); i++) {
fixture->broadcast_code_member_params[i].member.member = &fixture->conns[i];
fixture->broadcast_code_member_params[i].src_id = RANDOM_SRC_ID;
}
}
static void *cap_commander_test_distribute_broadcast_code_setup(void)
{
struct cap_commander_test_distribute_broadcast_code_fixture *fixture;
fixture = malloc(sizeof(*fixture));
zassert_not_null(fixture);
return fixture;
}
static void cap_commander_test_distribute_broadcast_code_before(void *f)
{
int err;
struct cap_commander_test_distribute_broadcast_code_fixture *fixture = f;
memset(f, 0, sizeof(struct cap_commander_test_distribute_broadcast_code_fixture));
cap_commander_test_distribute_broadcast_code_fixture_init(fixture);
for (size_t i = 0; i < ARRAY_SIZE(fixture->conns); i++) {
err = bt_cap_commander_discover(&fixture->conns[i]);
zassert_equal(0, err, "Unexpected return value %d", err);
}
}
static void cap_commander_test_distribute_broadcast_code_after(void *f)
{
struct cap_commander_test_distribute_broadcast_code_fixture *fixture = f;
bt_cap_commander_unregister_cb(&mock_cap_commander_cb);
bt_bap_broadcast_assistant_unregister_cb(&fixture->broadcast_assistant_cb);
/* We need to cleanup since the CAP commander remembers state */
bt_cap_commander_cancel();
for (size_t i = 0; i < ARRAY_SIZE(fixture->conns); i++) {
mock_bt_conn_disconnected(&fixture->conns[i], BT_HCI_ERR_REMOTE_USER_TERM_CONN);
}
}
static void cap_commander_test_distribute_broadcast_code_teardown(void *f)
{
free(f);
}
static void test_distribute_broadcast_code(
struct bt_cap_commander_distribute_broadcast_code_param *distribute_broadcast_code_param)
{
int err;
err = bt_cap_commander_distribute_broadcast_code(distribute_broadcast_code_param);
zassert_equal(0, err, "Unexpected return value %d", err);
zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 1,
mock_cap_commander_distribute_broadcast_code_cb_fake.call_count);
zassert_equal_ptr(NULL,
mock_cap_commander_distribute_broadcast_code_cb_fake.arg0_history[0]);
zassert_equal(0, mock_cap_commander_distribute_broadcast_code_cb_fake.arg1_history[0]);
}
ZTEST_SUITE(cap_commander_test_distribute_broadcast_code, NULL,
cap_commander_test_distribute_broadcast_code_setup,
cap_commander_test_distribute_broadcast_code_before,
cap_commander_test_distribute_broadcast_code_after,
cap_commander_test_distribute_broadcast_code_teardown);
ZTEST_F(cap_commander_test_distribute_broadcast_code, test_commander_distribute_broadcast_code)
{
int err;
err = bt_cap_commander_register_cb(&mock_cap_commander_cb);
zassert_equal(0, err, "Unexpected return value %d", err);
test_distribute_broadcast_code(&fixture->distribute_broadcast_code_param);
}
ZTEST_F(cap_commander_test_distribute_broadcast_code,
test_commander_reception_distribute_broadcast_code_double)
{
int err;
err = bt_cap_commander_register_cb(&mock_cap_commander_cb);
zassert_equal(0, err, "Unexpected return value %d", err);
test_distribute_broadcast_code(&fixture->distribute_broadcast_code_param);
/*
* We can not use test_distribute_broadcast_code because of the check on how often the
* callback function is called
*/
err = bt_cap_commander_distribute_broadcast_code(&fixture->distribute_broadcast_code_param);
zassert_equal(0, err, "Unexpected return value %d", err);
zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 2,
mock_cap_commander_distribute_broadcast_code_cb_fake.call_count);
zassert_equal_ptr(NULL,
mock_cap_commander_distribute_broadcast_code_cb_fake.arg0_history[1]);
zassert_equal(0, mock_cap_commander_distribute_broadcast_code_cb_fake.arg1_history[1]);
}
ZTEST_F(cap_commander_test_distribute_broadcast_code,
test_commander_reception_distribute_broadcast_code_param_null)
{
int err;
err = bt_cap_commander_distribute_broadcast_code(NULL);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 0,
mock_cap_commander_distribute_broadcast_code_cb_fake.call_count);
}
ZTEST_F(cap_commander_test_distribute_broadcast_code,
test_commander_distribute_broadcast_code_param_zero_count)
{
int err;
fixture->distribute_broadcast_code_param.count = 0;
err = bt_cap_commander_distribute_broadcast_code(&fixture->distribute_broadcast_code_param);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 0,
mock_cap_commander_distribute_broadcast_code_cb_fake.call_count);
}
ZTEST_F(cap_commander_test_distribute_broadcast_code,
test_commander_distribute_broadcast_code_param_high_count)
{
int err;
fixture->distribute_broadcast_code_param.count = CONFIG_BT_MAX_CONN + 1;
err = bt_cap_commander_distribute_broadcast_code(&fixture->distribute_broadcast_code_param);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
zexpect_call_count("bt_cap_commander_cb.broadcast_distribute_broadcast_code", 0,
mock_cap_commander_distribute_broadcast_code_cb_fake.call_count);
}
ZTEST_F(cap_commander_test_distribute_broadcast_code,
test_commander_distribute_broadcast_code_inval_param_null_param)
{
int err;
fixture->distribute_broadcast_code_param.type = BT_CAP_SET_TYPE_AD_HOC;
fixture->distribute_broadcast_code_param.param = NULL;
fixture->distribute_broadcast_code_param.count = ARRAY_SIZE(fixture->conns);
err = bt_cap_commander_distribute_broadcast_code(&fixture->distribute_broadcast_code_param);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 0,
mock_cap_commander_distribute_broadcast_code_cb_fake.call_count);
}
ZTEST_F(cap_commander_test_distribute_broadcast_code,
test_commander_distribute_broadcast_code_inval_null_member)
{
int err;
fixture->distribute_broadcast_code_param.param[0].member.member = NULL;
err = bt_cap_commander_distribute_broadcast_code(&fixture->distribute_broadcast_code_param);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 0,
mock_cap_commander_distribute_broadcast_code_cb_fake.call_count);
}
ZTEST_F(cap_commander_test_distribute_broadcast_code,
test_commander_distribute_broadcast_code_inval_missing_cas)
{
int err;
fixture->distribute_broadcast_code_param.type = BT_CAP_SET_TYPE_CSIP;
err = bt_cap_commander_distribute_broadcast_code(&fixture->distribute_broadcast_code_param);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 0,
mock_cap_commander_distribute_broadcast_code_cb_fake.call_count);
}
ZTEST_F(cap_commander_test_distribute_broadcast_code,
test_commander_distribute_broadcast_code_double_conn)
{
int err;
for (size_t i = 0; i < ARRAY_SIZE(fixture->broadcast_code_member_params); i++) {
fixture->broadcast_code_member_params[i].member.member = &fixture->conns[0];
}
err = bt_cap_commander_distribute_broadcast_code(&fixture->distribute_broadcast_code_param);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 0,
mock_cap_commander_distribute_broadcast_code_cb_fake.call_count);
}

View file

@ -5,6 +5,7 @@
*/
#include "zephyr/bluetooth/audio/bap.h"
#include "test_common.h"
static sys_slist_t broadcast_assistant_cbs = SYS_SLIST_STATIC_INIT(&broadcast_assistant_cbs);
@ -176,3 +177,29 @@ int bt_bap_broadcast_assistant_rem_src(struct bt_conn *conn, uint8_t src_id)
return 0;
}
int bt_bap_broadcast_assistant_set_broadcast_code(
struct bt_conn *conn, uint8_t src_id,
const uint8_t broadcast_code[BT_ISO_BROADCAST_CODE_SIZE])
{
struct bap_broadcast_assistant_instance *inst;
struct bt_bap_broadcast_assistant_cb *listener, *next;
int err;
zassert_not_null(conn, "conn is NULL");
inst = inst_by_conn(conn);
zassert_equal(src_id, RANDOM_SRC_ID, "Invalid src_id");
err = strncmp((const char *)broadcast_code, BROADCAST_CODE, sizeof(BROADCAST_CODE));
zassert_equal(0, err, "Unexpected broadcast code");
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&broadcast_assistant_cbs, listener, next, _node) {
if (listener->broadcast_code != NULL) {
listener->broadcast_code(conn, 0);
}
}
return 0;
}

View file

@ -17,7 +17,8 @@
FAKE(mock_cap_commander_microphone_mute_changed_cb) \
FAKE(mock_cap_commander_microphone_gain_changed_cb) \
FAKE(mock_cap_commander_broadcast_reception_start_cb) \
FAKE(mock_cap_commander_broadcast_reception_stop_cb)
FAKE(mock_cap_commander_broadcast_reception_stop_cb) \
FAKE(mock_cap_commander_distribute_broadcast_code_cb)
DEFINE_FAKE_VOID_FUNC(mock_cap_commander_discovery_complete_cb, struct bt_conn *, int,
const struct bt_csip_set_coordinator_set_member *,
@ -30,6 +31,7 @@ DEFINE_FAKE_VOID_FUNC(mock_cap_commander_microphone_mute_changed_cb, struct bt_c
DEFINE_FAKE_VOID_FUNC(mock_cap_commander_microphone_gain_changed_cb, struct bt_conn *, int);
DEFINE_FAKE_VOID_FUNC(mock_cap_commander_broadcast_reception_start_cb, struct bt_conn *, int);
DEFINE_FAKE_VOID_FUNC(mock_cap_commander_broadcast_reception_stop_cb, struct bt_conn *, int);
DEFINE_FAKE_VOID_FUNC(mock_cap_commander_distribute_broadcast_code_cb, struct bt_conn *, int);
const struct bt_cap_commander_cb mock_cap_commander_cb = {
.discovery_complete = mock_cap_commander_discovery_complete_cb,
@ -49,6 +51,7 @@ const struct bt_cap_commander_cb mock_cap_commander_cb = {
#if defined(CONFIG_BT_BAP_BROADCAST_ASSISTANT)
.broadcast_reception_start = mock_cap_commander_broadcast_reception_start_cb,
.broadcast_reception_stop = mock_cap_commander_broadcast_reception_stop_cb,
.distribute_broadcast_code = mock_cap_commander_distribute_broadcast_code_cb,
#endif /* CONFIG_BT_BAP_BROADCAST_ASSISTANT */
};