tests: Bluetooth: CAP commander volume_change unit tests

Adds unit tests for the CAP Commander volume_change
procedure.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
Emil Gydesen 2023-11-23 12:32:59 +01:00 committed by Anas Nashif
commit 6e52f384c8
8 changed files with 299 additions and 34 deletions

View file

@ -253,14 +253,13 @@ static void cap_commander_vcp_vol_set_cb(struct bt_vcp_vol_ctlr *vol_ctlr, int e
proc_param = &active_proc->proc_param.commander[active_proc->proc_done_cnt];
conn = proc_param->conn;
active_proc->proc_initiated_cnt++;
err = bt_vcp_vol_ctlr_set_vol(bt_vcp_vol_ctlr_get_by_conn(conn),
proc_param->change_volume.volume);
if (err != 0) {
LOG_DBG("Failed to set volume for conn %p: %d", (void *)conn, err);
bt_cap_common_abort_proc(conn, err);
cap_commander_unicast_audio_proc_complete();
} else {
active_proc->proc_initiated_cnt++;
}
} else {
cap_commander_unicast_audio_proc_complete();
@ -322,6 +321,7 @@ int bt_cap_commander_change_volume(const struct bt_cap_commander_change_volume_p
proc_param = &active_proc->proc_param.commander[0];
conn = proc_param->conn;
active_proc->proc_initiated_cnt++;
err = bt_vcp_vol_ctlr_set_vol(bt_vcp_vol_ctlr_get_by_conn(conn),
proc_param->change_volume.volume);
if (err != 0) {
@ -329,8 +329,6 @@ int bt_cap_commander_change_volume(const struct bt_cap_commander_change_volume_p
return -ENOEXEC;
}
active_proc->proc_initiated_cnt++;
return 0;
}

View file

@ -552,6 +552,7 @@ static int cap_initiator_unicast_audio_configure(
codec_cfg = &proc_param->start.codec_cfg;
conn = proc_param->start.conn;
ep = proc_param->start.ep;
active_proc->proc_initiated_cnt++;
/* Since BAP operations may require a write long or a read long on the notification,
* we cannot assume that we can do multiple streams at once, thus do it one at a time.
@ -562,8 +563,6 @@ static int cap_initiator_unicast_audio_configure(
LOG_DBG("Failed to config stream %p: %d", proc_param->stream, err);
bt_cap_common_clear_active_proc();
} else {
active_proc->proc_initiated_cnt++;
}
return err;
@ -646,6 +645,7 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream)
ep = proc_param->start.ep;
codec_cfg = &proc_param->start.codec_cfg;
bap_stream = &next_cap_stream->bap_stream;
active_proc->proc_initiated_cnt++;
/* Since BAP operations may require a write long or a read long on the notification,
* we cannot assume that we can do multiple streams at once, thus do it one at a
@ -659,8 +659,6 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream)
bt_cap_common_abort_proc(conn, err);
cap_initiator_unicast_audio_proc_complete();
} else {
active_proc->proc_initiated_cnt++;
}
return;
@ -712,6 +710,8 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream)
break;
}
active_proc->proc_initiated_cnt++;
err = bt_bap_stream_qos(conns[i], unicast_group);
if (err != 0) {
LOG_DBG("Failed to set stream QoS for conn %p and group %p: %d",
@ -728,8 +728,6 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream)
return;
}
active_proc->proc_initiated_cnt++;
}
}
@ -773,6 +771,7 @@ void bt_cap_initiator_qos_configured(struct bt_cap_stream *cap_stream)
proc_param = &active_proc->proc_param.initiator[0];
next_cap_stream = proc_param->stream;
bap_stream = &next_cap_stream->bap_stream;
active_proc->proc_initiated_cnt++;
/* Since BAP operations may require a write long or a read long on the notification, we
* cannot assume that we can do multiple streams at once, thus do it one at a time.
@ -785,8 +784,6 @@ void bt_cap_initiator_qos_configured(struct bt_cap_stream *cap_stream)
bt_cap_common_abort_proc(bap_stream->conn, err);
cap_initiator_unicast_audio_proc_complete();
} else {
active_proc->proc_initiated_cnt++;
}
}
@ -825,6 +822,8 @@ void bt_cap_initiator_enabled(struct bt_cap_stream *cap_stream)
active_proc->proc_param.initiator[active_proc->proc_done_cnt].stream;
struct bt_bap_stream *next_bap_stream = &next_cap_stream->bap_stream;
active_proc->proc_initiated_cnt++;
/* Since BAP operations may require a write long or a read long on the notification,
* we cannot assume that we can do multiple streams at once, thus do it one at a
* time.
@ -838,8 +837,6 @@ void bt_cap_initiator_enabled(struct bt_cap_stream *cap_stream)
bt_cap_common_abort_proc(next_bap_stream->conn, err);
cap_initiator_unicast_audio_proc_complete();
} else {
active_proc->proc_initiated_cnt++;
}
return;
@ -1011,14 +1008,13 @@ int bt_cap_initiator_unicast_audio_update(const struct bt_cap_unicast_audio_upda
bap_stream = &proc_param->stream->bap_stream;
meta_len = proc_param->meta_update.meta_len;
meta = proc_param->meta_update.meta;
active_proc->proc_initiated_cnt++;
err = bt_bap_stream_metadata(bap_stream, meta, meta_len);
if (err != 0) {
LOG_DBG("Failed to update metadata for stream %p: %d", proc_param->stream, err);
bt_cap_common_clear_active_proc();
} else {
active_proc->proc_initiated_cnt++;
}
return err;
@ -1079,6 +1075,7 @@ void bt_cap_initiator_metadata_updated(struct bt_cap_stream *cap_stream)
meta = proc_param->meta_update.meta;
next_cap_stream = proc_param->stream;
bap_stream = &next_cap_stream->bap_stream;
active_proc->proc_initiated_cnt++;
/* Since BAP operations may require a write long or a read long on the notification,
* we cannot assume that we can do multiple streams at once, thus do it one at a
@ -1094,8 +1091,6 @@ void bt_cap_initiator_metadata_updated(struct bt_cap_stream *cap_stream)
bt_cap_common_abort_proc(bap_stream->conn, err);
cap_initiator_unicast_audio_proc_complete();
} else {
active_proc->proc_initiated_cnt++;
}
return;
@ -1169,14 +1164,13 @@ int bt_cap_initiator_unicast_audio_stop(struct bt_bap_unicast_group *unicast_gro
*/
proc_param = &active_proc->proc_param.initiator[0];
bap_stream = &proc_param->stream->bap_stream;
active_proc->proc_initiated_cnt++;
err = bt_bap_stream_release(bap_stream);
if (err != 0) {
LOG_DBG("Failed to stop bap_stream %p: %d", proc_param->stream, err);
bt_cap_common_clear_active_proc();
} else {
active_proc->proc_initiated_cnt++;
}
return err;
@ -1215,6 +1209,7 @@ void bt_cap_initiator_released(struct bt_cap_stream *cap_stream)
struct bt_bap_stream *bap_stream = &next_cap_stream->bap_stream;
int err;
active_proc->proc_initiated_cnt++;
/* Since BAP operations may require a write long or a read long on the notification,
* we cannot assume that we can do multiple streams at once, thus do it one at a
* time.
@ -1227,8 +1222,6 @@ void bt_cap_initiator_released(struct bt_cap_stream *cap_stream)
bt_cap_common_abort_proc(bap_stream->conn, err);
cap_initiator_unicast_audio_proc_complete();
} else {
active_proc->proc_initiated_cnt++;
}
} else {
cap_initiator_unicast_audio_proc_complete();

View file

@ -0,0 +1,10 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
void mock_bt_csip_init(void);
void mock_bt_csip_cleanup(void);
void mock_bt_vcp_init(void);
void mock_bt_vcp_cleanup(void);

View file

@ -2,6 +2,7 @@ CONFIG_ZTEST=y
CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_MAX_CONN=2
CONFIG_BT_AUDIO=y
# Requirements for CAP commander

View file

@ -9,29 +9,35 @@
#include <stdlib.h>
#include <zephyr/bluetooth/audio/cap.h>
#include <zephyr/bluetooth/audio/vcp.h>
#include <zephyr/fff.h>
#include "bluetooth.h"
#include "cap_commander.h"
#include "conn.h"
#include "expects_util.h"
#include "cap_mocks.h"
DEFINE_FFF_GLOBALS;
static void mock_init_rule_before(const struct ztest_unit_test *test, void *fixture)
{
mock_cap_commander_init();
mock_bt_csip_init();
mock_bt_vcp_init();
}
static void mock_destroy_rule_after(const struct ztest_unit_test *test, void *fixture)
{
mock_cap_commander_cleanup();
mock_bt_csip_cleanup();
mock_bt_vcp_cleanup();
}
ZTEST_RULE(mock_rule, mock_init_rule_before, mock_destroy_rule_after);
struct cap_commander_test_suite_fixture {
struct bt_conn conn;
struct bt_conn conns[CONFIG_BT_MAX_CONN];
};
static void test_conn_init(struct bt_conn *conn)
@ -47,7 +53,9 @@ static void test_conn_init(struct bt_conn *conn)
static void cap_commander_test_suite_fixture_init(struct cap_commander_test_suite_fixture *fixture)
{
test_conn_init(&fixture->conn);
for (size_t i = 0; i < ARRAY_SIZE(fixture->conns); i++) {
test_conn_init(&fixture->conns[i]);
}
}
static void *cap_commander_test_suite_setup(void)
@ -68,7 +76,13 @@ static void cap_commander_test_suite_before(void *f)
static void cap_commander_test_suite_after(void *f)
{
struct cap_commander_test_suite_fixture *fixture = f;
bt_cap_commander_unregister_cb(&mock_cap_commander_cb);
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_suite_teardown(void *f)
@ -147,10 +161,12 @@ ZTEST_F(cap_commander_test_suite, test_commander_discover)
err = bt_cap_commander_register_cb(&mock_cap_commander_cb);
zassert_equal(0, err, "Unexpected return value %d", err);
err = bt_cap_commander_discover(&fixture->conn);
zassert_equal(0, err, "Unexpected return value %d", err);
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);
}
zexpect_call_count("bt_cap_commander_cb.discovery_complete", 1,
zexpect_call_count("bt_cap_commander_cb.discovery_complete", ARRAY_SIZE(fixture->conns),
mock_cap_commander_discovery_complete_cb_fake.call_count);
}
@ -164,3 +180,216 @@ ZTEST_F(cap_commander_test_suite, test_commander_discover_inval_param_null)
err = bt_cap_commander_discover(NULL);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
}
ZTEST_F(cap_commander_test_suite, test_commander_change_volume)
{
union bt_cap_set_member members[ARRAY_SIZE(fixture->conns)];
const struct bt_cap_commander_change_volume_param param = {
.type = BT_CAP_SET_TYPE_AD_HOC,
.members = members,
.count = ARRAY_SIZE(fixture->conns),
.volume = 177,
};
int err;
for (size_t i = 0U; i < ARRAY_SIZE(members); i++) {
members[i].member = &fixture->conns[i];
}
err = bt_cap_commander_register_cb(&mock_cap_commander_cb);
zassert_equal(0, err, "Unexpected return value %d", err);
for (size_t i = 0; i < ARRAY_SIZE(fixture->conns); i++) {
struct bt_vcp_vol_ctlr *vol_ctlr; /* We don't care about this */
err = bt_cap_commander_discover(&fixture->conns[i]);
zassert_equal(0, err, "Unexpected return value %d", err);
err = bt_vcp_vol_ctlr_discover(&fixture->conns[i], &vol_ctlr);
zassert_equal(0, err, "Unexpected return value %d", err);
}
err = bt_cap_commander_change_volume(&param);
zassert_equal(0, err, "Unexpected return value %d", err);
zexpect_call_count("bt_cap_commander_cb.volume_changed", 1,
mock_cap_commander_volume_changed_cb_fake.call_count);
}
ZTEST_F(cap_commander_test_suite, test_commander_change_volume_double)
{
union bt_cap_set_member members[ARRAY_SIZE(fixture->conns)];
const struct bt_cap_commander_change_volume_param param = {
.type = BT_CAP_SET_TYPE_AD_HOC,
.members = members,
.count = ARRAY_SIZE(fixture->conns),
.volume = 177,
};
int err;
for (size_t i = 0U; i < ARRAY_SIZE(members); i++) {
members[i].member = &fixture->conns[i];
}
err = bt_cap_commander_register_cb(&mock_cap_commander_cb);
zassert_equal(0, err, "Unexpected return value %d", err);
for (size_t i = 0; i < ARRAY_SIZE(fixture->conns); i++) {
struct bt_vcp_vol_ctlr *vol_ctlr; /* We don't care about this */
err = bt_cap_commander_discover(&fixture->conns[i]);
zassert_equal(0, err, "Unexpected return value %d", err);
err = bt_vcp_vol_ctlr_discover(&fixture->conns[i], &vol_ctlr);
zassert_equal(0, err, "Unexpected return value %d", err);
}
err = bt_cap_commander_change_volume(&param);
zassert_equal(0, err, "Unexpected return value %d", err);
zexpect_call_count("bt_cap_commander_cb.volume_changed", 1,
mock_cap_commander_volume_changed_cb_fake.call_count);
/* Verify that it still works as expected if we set the same value twice */
err = bt_cap_commander_change_volume(&param);
zassert_equal(0, err, "Unexpected return value %d", err);
zexpect_call_count("bt_cap_commander_cb.volume_changed", 2,
mock_cap_commander_volume_changed_cb_fake.call_count);
}
ZTEST_F(cap_commander_test_suite, test_commander_change_volume_inval_param_null)
{
int err;
err = bt_cap_commander_change_volume(NULL);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
}
ZTEST_F(cap_commander_test_suite, test_commander_change_volume_inval_param_null_members)
{
const struct bt_cap_commander_change_volume_param param = {
.type = BT_CAP_SET_TYPE_AD_HOC,
.members = NULL,
.count = ARRAY_SIZE(fixture->conns),
.volume = 177,
};
int err;
err = bt_cap_commander_change_volume(&param);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
}
ZTEST_F(cap_commander_test_suite, test_commander_change_volume_inval_param_null_member)
{
union bt_cap_set_member members[ARRAY_SIZE(fixture->conns)];
const struct bt_cap_commander_change_volume_param param = {
.type = BT_CAP_SET_TYPE_AD_HOC,
.members = members,
.count = ARRAY_SIZE(fixture->conns),
.volume = 177,
};
int err;
for (size_t i = 0U; i < ARRAY_SIZE(members) - 1; i++) {
members[i].member = &fixture->conns[i];
}
members[ARRAY_SIZE(members) - 1].member = NULL;
err = bt_cap_commander_change_volume(&param);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
}
ZTEST_F(cap_commander_test_suite, test_commander_change_volume_inval_missing_cas)
{
union bt_cap_set_member members[ARRAY_SIZE(fixture->conns)];
const struct bt_cap_commander_change_volume_param param = {
.type = BT_CAP_SET_TYPE_AD_HOC,
.members = members,
.count = ARRAY_SIZE(fixture->conns),
.volume = 177,
};
int err;
for (size_t i = 0U; i < ARRAY_SIZE(members); i++) {
members[i].member = &fixture->conns[i];
}
err = bt_cap_commander_register_cb(&mock_cap_commander_cb);
zassert_equal(0, err, "Unexpected return value %d", err);
for (size_t i = 0; i < ARRAY_SIZE(fixture->conns); i++) {
struct bt_vcp_vol_ctlr *vol_ctlr; /* We don't care about this */
err = bt_vcp_vol_ctlr_discover(&fixture->conns[i], &vol_ctlr);
zassert_equal(0, err, "Unexpected return value %d", err);
}
err = bt_cap_commander_change_volume(&param);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
}
ZTEST_F(cap_commander_test_suite, test_commander_change_volume_inval_missing_vcs)
{
union bt_cap_set_member members[ARRAY_SIZE(fixture->conns)];
const struct bt_cap_commander_change_volume_param param = {
.type = BT_CAP_SET_TYPE_AD_HOC,
.members = members,
.count = ARRAY_SIZE(fixture->conns),
.volume = 177,
};
int err;
for (size_t i = 0U; i < ARRAY_SIZE(members); i++) {
members[i].member = &fixture->conns[i];
}
err = bt_cap_commander_register_cb(&mock_cap_commander_cb);
zassert_equal(0, err, "Unexpected return value %d", err);
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);
}
err = bt_cap_commander_change_volume(&param);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
}
ZTEST_F(cap_commander_test_suite, test_commander_change_volume_inval_param_zero_count)
{
union bt_cap_set_member members[ARRAY_SIZE(fixture->conns)];
const struct bt_cap_commander_change_volume_param param = {
.type = BT_CAP_SET_TYPE_AD_HOC,
.members = members,
.count = 0U,
.volume = 177,
};
int err;
for (size_t i = 0U; i < ARRAY_SIZE(members); i++) {
members[i].member = &fixture->conns[i];
}
err = bt_cap_commander_change_volume(&param);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
}
ZTEST_F(cap_commander_test_suite, test_commander_change_volume_inval_param_inval_count)
{
union bt_cap_set_member members[ARRAY_SIZE(fixture->conns)];
const struct bt_cap_commander_change_volume_param param = {
.type = BT_CAP_SET_TYPE_AD_HOC,
.members = members,
.count = CONFIG_BT_MAX_CONN + 1,
.volume = 177,
};
int err;
for (size_t i = 0U; i < ARRAY_SIZE(members); i++) {
members[i].member = &fixture->conns[i];
}
err = bt_cap_commander_change_volume(&param);
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
}

View file

@ -34,12 +34,38 @@ int bt_vcp_vol_ctlr_conn_get(const struct bt_vcp_vol_ctlr *vol_ctlr, struct bt_c
int bt_vcp_vol_ctlr_set_vol(struct bt_vcp_vol_ctlr *vol_ctlr, uint8_t volume)
{
if (vcp_cb->vol_set != NULL) {
if (vcp_cb != NULL && vcp_cb->vol_set != NULL) {
vcp_cb->vol_set(vol_ctlr, 0);
}
return 0;
}
int bt_vcp_vol_ctlr_discover(struct bt_conn *conn, struct bt_vcp_vol_ctlr **vol_ctlr)
{
for (size_t i = 0; i < ARRAY_SIZE(vol_ctlrs); i++) {
if (vol_ctlrs[i].conn == NULL) {
vol_ctlrs[i].conn = conn;
*vol_ctlr = &vol_ctlrs[i];
return 0;
}
}
return -ENOMEM;
}
int bt_vcp_vol_ctlr_cb_register(struct bt_vcp_vol_ctlr_cb *cb)
{
vcp_cb = cb;
return 0;
}
void mock_bt_vcp_init(void)
{
memset(vol_ctlrs, 0, sizeof(vol_ctlrs));
}
void mock_bt_vcp_cleanup(void)
{
}

View file

@ -10,12 +10,13 @@
#include <zephyr/fff.h>
#include <zephyr/bluetooth/audio/cap.h>
extern struct bt_cap_commander_cb mock_cap_commander_cb;
extern const struct bt_cap_commander_cb mock_cap_commander_cb;
void mock_cap_commander_init(void);
void mock_cap_commander_cleanup(void);
DECLARE_FAKE_VOID_FUNC(mock_cap_commander_discovery_complete_cb, struct bt_conn *, int,
const struct bt_csip_set_coordinator_csis_inst *);
DECLARE_FAKE_VOID_FUNC(mock_cap_commander_volume_changed_cb, struct bt_conn *, int);
#endif /* MOCKS_CAP_COMMANDER_H_ */

View file

@ -9,18 +9,25 @@
#include "cap_commander.h"
/* List of fakes used by this unit tester */
#define FFF_FAKES_LIST(FAKE) FAKE(mock_cap_commander_discovery_complete_cb)
struct bt_cap_commander_cb mock_cap_commander_cb;
#define FFF_FAKES_LIST(FAKE) \
FAKE(mock_cap_commander_discovery_complete_cb) \
FAKE(mock_cap_commander_volume_changed_cb)
DEFINE_FAKE_VOID_FUNC(mock_cap_commander_discovery_complete_cb, struct bt_conn *, int,
const struct bt_csip_set_coordinator_csis_inst *);
DEFINE_FAKE_VOID_FUNC(mock_cap_commander_volume_changed_cb, struct bt_conn *, int);
const struct bt_cap_commander_cb mock_cap_commander_cb = {
.discovery_complete = mock_cap_commander_discovery_complete_cb,
#if defined(CONFIG_BT_VCP_VOL_CTLR)
.volume_changed = mock_cap_commander_volume_changed_cb,
#endif /* CONFIG_BT_VCP_VOL_CTLR */
};
void mock_cap_commander_init(void)
{
FFF_FAKES_LIST(RESET_FAKE);
mock_cap_commander_cb.discovery_complete = mock_cap_commander_discovery_complete_cb;
}
void mock_cap_commander_cleanup(void)