From e0f3524e3d9cb742303359af7e6acabfdace210a Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Wed, 29 Nov 2023 11:27:00 +0100 Subject: [PATCH] tests: Bluetooth: CAP Commander Change Volume Offset unittests Adds unittests for the CAP COmmander Change Volume Offset procedure. Signed-off-by: Emil Gydesen --- include/zephyr/bluetooth/audio/vocs.h | 2 + .../audio/cap_commander/include/cap_mocks.h | 2 + tests/bluetooth/audio/cap_commander/prj.conf | 2 + .../bluetooth/audio/cap_commander/src/main.c | 261 ++++++++++++++++++ .../audio/cap_commander/uut/CMakeLists.txt | 1 + .../bluetooth/audio/cap_commander/uut/csip.c | 2 +- tests/bluetooth/audio/cap_commander/uut/vcp.c | 42 ++- .../bluetooth/audio/cap_commander/uut/vocs.c | 76 +++++ .../audio/mocks/include/cap_commander.h | 1 + .../bluetooth/audio/mocks/src/cap_commander.c | 7 +- 10 files changed, 393 insertions(+), 3 deletions(-) create mode 100644 tests/bluetooth/audio/cap_commander/uut/vocs.c diff --git a/include/zephyr/bluetooth/audio/vocs.h b/include/zephyr/bluetooth/audio/vocs.h index eee5c43533a5..d34e6bcd16e9 100644 --- a/include/zephyr/bluetooth/audio/vocs.h +++ b/include/zephyr/bluetooth/audio/vocs.h @@ -29,6 +29,8 @@ #include #include +#include + #ifdef __cplusplus extern "C" { #endif diff --git a/tests/bluetooth/audio/cap_commander/include/cap_mocks.h b/tests/bluetooth/audio/cap_commander/include/cap_mocks.h index 5c7751bf37a7..a907ab2b42c7 100644 --- a/tests/bluetooth/audio/cap_commander/include/cap_mocks.h +++ b/tests/bluetooth/audio/cap_commander/include/cap_mocks.h @@ -8,3 +8,5 @@ void mock_bt_csip_init(void); void mock_bt_csip_cleanup(void); void mock_bt_vcp_init(void); void mock_bt_vcp_cleanup(void); +void mock_bt_vocs_init(void); +void mock_bt_vocs_cleanup(void); diff --git a/tests/bluetooth/audio/cap_commander/prj.conf b/tests/bluetooth/audio/cap_commander/prj.conf index fd805ec06a1e..247cffed78a3 100644 --- a/tests/bluetooth/audio/cap_commander/prj.conf +++ b/tests/bluetooth/audio/cap_commander/prj.conf @@ -7,6 +7,8 @@ CONFIG_BT_AUDIO=y # Requirements for CAP commander CONFIG_BT_VCP_VOL_CTLR=y +CONFIG_BT_VOCS_CLIENT_MAX_INSTANCE_COUNT=1 +CONFIG_BT_VCP_VOL_CTLR_MAX_VOCS_INST=1 CONFIG_BT_CSIP_SET_COORDINATOR=y CONFIG_BT_CAP_COMMANDER=y diff --git a/tests/bluetooth/audio/cap_commander/src/main.c b/tests/bluetooth/audio/cap_commander/src/main.c index 1cabbce134c6..d4709f688512 100644 --- a/tests/bluetooth/audio/cap_commander/src/main.c +++ b/tests/bluetooth/audio/cap_commander/src/main.c @@ -25,6 +25,7 @@ static void mock_init_rule_before(const struct ztest_unit_test *test, void *fixt mock_cap_commander_init(); mock_bt_csip_init(); mock_bt_vcp_init(); + mock_bt_vocs_init(); } static void mock_destroy_rule_after(const struct ztest_unit_test *test, void *fixture) @@ -32,6 +33,7 @@ static void mock_destroy_rule_after(const struct ztest_unit_test *test, void *fi mock_cap_commander_cleanup(); mock_bt_csip_cleanup(); mock_bt_vcp_cleanup(); + mock_bt_vocs_cleanup(); } ZTEST_RULE(mock_rule, mock_init_rule_before, mock_destroy_rule_after); @@ -393,3 +395,262 @@ ZTEST_F(cap_commander_test_suite, test_commander_change_volume_inval_param_inval err = bt_cap_commander_change_volume(¶m); zassert_equal(-EINVAL, err, "Unexpected return value %d", err); } + +ZTEST_F(cap_commander_test_suite, test_commander_change_volume_offset) +{ + struct bt_cap_commander_change_volume_offset_member_param + member_params[ARRAY_SIZE(fixture->conns)]; + const struct bt_cap_commander_change_volume_offset_param param = { + .type = BT_CAP_SET_TYPE_AD_HOC, + .param = member_params, + .count = ARRAY_SIZE(member_params), + }; + int err; + + for (size_t i = 0U; i < ARRAY_SIZE(member_params); i++) { + member_params[i].member.member = &fixture->conns[i]; + member_params[i].offset = 100 + 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_offset(¶m); + zassert_equal(0, err, "Unexpected return value %d", err); + + zexpect_call_count("bt_cap_commander_cb.volume_offset_changed", 1, + mock_cap_commander_volume_offset_changed_cb_fake.call_count); +} + +ZTEST_F(cap_commander_test_suite, test_commander_change_volume_offset_double) +{ + struct bt_cap_commander_change_volume_offset_member_param + member_params[ARRAY_SIZE(fixture->conns)]; + const struct bt_cap_commander_change_volume_offset_param param = { + .type = BT_CAP_SET_TYPE_AD_HOC, + .param = member_params, + .count = ARRAY_SIZE(member_params), + }; + int err; + + for (size_t i = 0U; i < ARRAY_SIZE(member_params); i++) { + member_params[i].member.member = &fixture->conns[i]; + member_params[i].offset = 100 + 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_offset(¶m); + zassert_equal(0, err, "Unexpected return value %d", err); + + zexpect_call_count("bt_cap_commander_cb.volume_offset_changed", 1, + mock_cap_commander_volume_offset_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_offset(¶m); + zassert_equal(0, err, "Unexpected return value %d", err); + + zexpect_call_count("bt_cap_commander_cb.volume_offset_changed", 2, + mock_cap_commander_volume_offset_changed_cb_fake.call_count); +} + +ZTEST_F(cap_commander_test_suite, test_commander_change_volume_offset_inval_param_null) +{ + int err; + + err = bt_cap_commander_change_volume_offset(NULL); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); +} + +ZTEST_F(cap_commander_test_suite, test_commander_change_volume_offset_inval_param_null_param) +{ + const struct bt_cap_commander_change_volume_offset_param param = { + .type = BT_CAP_SET_TYPE_AD_HOC, + .param = NULL, + .count = ARRAY_SIZE(fixture->conns), + }; + int err; + + err = bt_cap_commander_change_volume_offset(¶m); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); +} + +ZTEST_F(cap_commander_test_suite, test_commander_change_volume_offset_inval_param_null_member) +{ + struct bt_cap_commander_change_volume_offset_member_param + member_params[ARRAY_SIZE(fixture->conns)]; + const struct bt_cap_commander_change_volume_offset_param param = { + .type = BT_CAP_SET_TYPE_AD_HOC, + .param = member_params, + .count = ARRAY_SIZE(member_params), + }; + int err; + + for (size_t i = 0U; i < ARRAY_SIZE(member_params); i++) { + member_params[i].member.member = &fixture->conns[i]; + member_params[i].offset = 100 + i; + } + member_params[ARRAY_SIZE(member_params) - 1].member.member = NULL; + + err = bt_cap_commander_change_volume_offset(¶m); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); +} + +ZTEST_F(cap_commander_test_suite, test_commander_change_volume_offset_inval_missing_cas) +{ + struct bt_cap_commander_change_volume_offset_member_param + member_params[ARRAY_SIZE(fixture->conns)]; + const struct bt_cap_commander_change_volume_offset_param param = { + .type = BT_CAP_SET_TYPE_AD_HOC, + .param = member_params, + .count = ARRAY_SIZE(member_params), + }; + int err; + + for (size_t i = 0U; i < ARRAY_SIZE(member_params); i++) { + member_params[i].member.member = &fixture->conns[i]; + member_params[i].offset = 100 + 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_offset(¶m); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); +} + +ZTEST_F(cap_commander_test_suite, test_commander_change_volume_offset_inval_missing_vocs) +{ + struct bt_cap_commander_change_volume_offset_member_param + member_params[ARRAY_SIZE(fixture->conns)]; + const struct bt_cap_commander_change_volume_offset_param param = { + .type = BT_CAP_SET_TYPE_AD_HOC, + .param = member_params, + .count = ARRAY_SIZE(member_params), + }; + int err; + + for (size_t i = 0U; i < ARRAY_SIZE(member_params); i++) { + member_params[i].member.member = &fixture->conns[i]; + member_params[i].offset = 100 + 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_offset(¶m); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); +} + +ZTEST_F(cap_commander_test_suite, test_commander_change_volume_offset_inval_param_zero_count) +{ + struct bt_cap_commander_change_volume_offset_member_param + member_params[ARRAY_SIZE(fixture->conns)]; + const struct bt_cap_commander_change_volume_offset_param param = { + .type = BT_CAP_SET_TYPE_AD_HOC, + .param = member_params, + .count = 0U, + }; + int err; + + for (size_t i = 0U; i < ARRAY_SIZE(member_params); i++) { + member_params[i].member.member = &fixture->conns[i]; + member_params[i].offset = 100 + i; + } + + err = bt_cap_commander_change_volume_offset(¶m); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); +} + +ZTEST_F(cap_commander_test_suite, test_commander_change_volume_offset_inval_param_inval_count) +{ + struct bt_cap_commander_change_volume_offset_member_param + member_params[ARRAY_SIZE(fixture->conns)]; + const struct bt_cap_commander_change_volume_offset_param param = { + .type = BT_CAP_SET_TYPE_AD_HOC, + .param = member_params, + .count = CONFIG_BT_MAX_CONN + 1, + }; + int err; + + for (size_t i = 0U; i < ARRAY_SIZE(member_params); i++) { + member_params[i].member.member = &fixture->conns[i]; + member_params[i].offset = 100 + i; + } + + err = bt_cap_commander_change_volume_offset(¶m); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); +} + +ZTEST_F(cap_commander_test_suite, test_commander_change_volume_offset_inval_param_inval_offset_max) +{ + struct bt_cap_commander_change_volume_offset_member_param + member_params[ARRAY_SIZE(fixture->conns)]; + const struct bt_cap_commander_change_volume_offset_param param = { + .type = BT_CAP_SET_TYPE_AD_HOC, + .param = member_params, + .count = ARRAY_SIZE(member_params), + }; + int err; + + for (size_t i = 0U; i < ARRAY_SIZE(member_params); i++) { + member_params[i].member.member = &fixture->conns[i]; + member_params[i].offset = BT_VOCS_MAX_OFFSET + 1; + } + + err = bt_cap_commander_change_volume_offset(¶m); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); +} + +ZTEST_F(cap_commander_test_suite, test_commander_change_volume_offset_inval_param_inval_offset_min) +{ + struct bt_cap_commander_change_volume_offset_member_param + member_params[ARRAY_SIZE(fixture->conns)]; + const struct bt_cap_commander_change_volume_offset_param param = { + .type = BT_CAP_SET_TYPE_AD_HOC, + .param = member_params, + .count = ARRAY_SIZE(member_params), + }; + int err; + + for (size_t i = 0U; i < ARRAY_SIZE(member_params); i++) { + member_params[i].member.member = &fixture->conns[i]; + member_params[i].offset = BT_VOCS_MIN_OFFSET - 1; + } + + err = bt_cap_commander_change_volume_offset(¶m); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); +} diff --git a/tests/bluetooth/audio/cap_commander/uut/CMakeLists.txt b/tests/bluetooth/audio/cap_commander/uut/CMakeLists.txt index 8a0774a3182e..d91bfbcbe7a9 100644 --- a/tests/bluetooth/audio/cap_commander/uut/CMakeLists.txt +++ b/tests/bluetooth/audio/cap_commander/uut/CMakeLists.txt @@ -13,6 +13,7 @@ add_library(uut STATIC ${ZEPHYR_BASE}/subsys/net/buf_simple.c csip.c vcp.c + vocs.c ) add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/audio/mocks mocks) diff --git a/tests/bluetooth/audio/cap_commander/uut/csip.c b/tests/bluetooth/audio/cap_commander/uut/csip.c index 348b1ac9146d..c6e910e8c0be 100644 --- a/tests/bluetooth/audio/cap_commander/uut/csip.c +++ b/tests/bluetooth/audio/cap_commander/uut/csip.c @@ -54,9 +54,9 @@ int bt_csip_set_coordinator_discover(struct bt_conn *conn) void mock_bt_csip_init(void) { - csip_cb = NULL; } void mock_bt_csip_cleanup(void) { + csip_cb = NULL; } diff --git a/tests/bluetooth/audio/cap_commander/uut/vcp.c b/tests/bluetooth/audio/cap_commander/uut/vcp.c index b6dc66afde5b..c3390c21f2d0 100644 --- a/tests/bluetooth/audio/cap_commander/uut/vcp.c +++ b/tests/bluetooth/audio/cap_commander/uut/vcp.c @@ -12,6 +12,8 @@ static struct bt_vcp_vol_ctlr_cb *vcp_cb; static struct bt_vcp_vol_ctlr { struct bt_conn *conn; + struct bt_vocs *vocs[CONFIG_BT_VCP_VOL_CTLR_MAX_VOCS_INST]; + struct bt_aics *aics[CONFIG_BT_VCP_VOL_CTLR_MAX_AICS_INST]; } vol_ctlrs[CONFIG_BT_MAX_CONN]; struct bt_vcp_vol_ctlr *bt_vcp_vol_ctlr_get_by_conn(const struct bt_conn *conn) @@ -45,8 +47,17 @@ int bt_vcp_vol_ctlr_discover(struct bt_conn *conn, struct bt_vcp_vol_ctlr **vol_ { for (size_t i = 0; i < ARRAY_SIZE(vol_ctlrs); i++) { if (vol_ctlrs[i].conn == NULL) { + for (size_t j = 0U; j < ARRAY_SIZE(vol_ctlrs[i].vocs); j++) { + const int err = bt_vocs_discover(conn, vol_ctlrs[i].vocs[j], NULL); + + if (err != 0) { + return err; + } + } + vol_ctlrs[i].conn = conn; *vol_ctlr = &vol_ctlrs[i]; + return 0; } } @@ -58,14 +69,43 @@ int bt_vcp_vol_ctlr_cb_register(struct bt_vcp_vol_ctlr_cb *cb) { vcp_cb = cb; + if (IS_ENABLED(CONFIG_BT_VCP_VOL_CTLR_VOCS)) { + for (size_t i = 0U; i < ARRAY_SIZE(vol_ctlrs); i++) { + for (size_t j = 0U; j < ARRAY_SIZE(vol_ctlrs[i].vocs); j++) { + bt_vocs_client_cb_register(vol_ctlrs[i].vocs[j], &cb->vocs_cb); + } + } + } + + return 0; +} + +int bt_vcp_vol_ctlr_included_get(struct bt_vcp_vol_ctlr *vol_ctlr, struct bt_vcp_included *included) +{ + included->vocs_cnt = ARRAY_SIZE(vol_ctlr->vocs); + included->vocs = vol_ctlr->vocs; + + included->aics_cnt = ARRAY_SIZE(vol_ctlr->aics); + included->aics = vol_ctlr->aics; + return 0; } void mock_bt_vcp_init(void) { - memset(vol_ctlrs, 0, sizeof(vol_ctlrs)); + if (IS_ENABLED(CONFIG_BT_VCP_VOL_CTLR_VOCS)) { + for (size_t i = 0U; i < ARRAY_SIZE(vol_ctlrs); i++) { + for (size_t j = 0U; j < ARRAY_SIZE(vol_ctlrs[i].vocs); j++) { + vol_ctlrs[i].vocs[j] = bt_vocs_client_free_instance_get(); + + __ASSERT(vol_ctlrs[i].vocs[j], + "Could not allocate VOCS client instance"); + } + } + } } void mock_bt_vcp_cleanup(void) { + memset(vol_ctlrs, 0, sizeof(vol_ctlrs)); } diff --git a/tests/bluetooth/audio/cap_commander/uut/vocs.c b/tests/bluetooth/audio/cap_commander/uut/vocs.c new file mode 100644 index 000000000000..075bd69b8259 --- /dev/null +++ b/tests/bluetooth/audio/cap_commander/uut/vocs.c @@ -0,0 +1,76 @@ +/* csip.c - CAP Commander specific VOCS mocks */ + +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +static struct bt_vocs { + bool active; + struct bt_conn *conn; + struct bt_vocs_cb *cb; +} vocs_clients[CONFIG_BT_MAX_CONN * CONFIG_BT_VOCS_CLIENT_MAX_INSTANCE_COUNT]; + +int bt_vocs_client_conn_get(const struct bt_vocs *vocs, struct bt_conn **conn) +{ + *conn = vocs->conn; + + return 0; +} + +int bt_vocs_state_set(struct bt_vocs *vocs, int16_t offset) +{ + if (vocs != NULL && vocs->cb != NULL && vocs->cb->set_offset != NULL) { + vocs->cb->set_offset(vocs, 0); + } + + return 0; +} + +void bt_vocs_client_cb_register(struct bt_vocs *vocs, struct bt_vocs_cb *cb) +{ + vocs->cb = cb; +} + +struct bt_vocs *bt_vocs_client_free_instance_get(void) +{ + for (size_t i = 0U; i < ARRAY_SIZE(vocs_clients); i++) { + if (!vocs_clients[i].active) { + vocs_clients[i].active = true; + + return &vocs_clients[i]; + } + } + + return NULL; +} + +int bt_vocs_discover(struct bt_conn *conn, struct bt_vocs *vocs, + const struct bt_vocs_discover_param *param) +{ + vocs->conn = conn; + + if (vocs != NULL && vocs->cb != NULL && vocs->cb->discover != NULL) { + vocs->cb->discover(vocs, 0); + } + + return 0; +} + +void mock_bt_vocs_init(void) +{ +} + +void mock_bt_vocs_cleanup(void) +{ + /* Reset everything but the callbacks, as they will not be registered again between each + * test + */ + for (size_t i = 0U; i < ARRAY_SIZE(vocs_clients); i++) { + vocs_clients[i].active = false; + vocs_clients[i].conn = NULL; + } +} diff --git a/tests/bluetooth/audio/mocks/include/cap_commander.h b/tests/bluetooth/audio/mocks/include/cap_commander.h index 86be0119671d..73fab99c2b44 100644 --- a/tests/bluetooth/audio/mocks/include/cap_commander.h +++ b/tests/bluetooth/audio/mocks/include/cap_commander.h @@ -18,5 +18,6 @@ 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); +DECLARE_FAKE_VOID_FUNC(mock_cap_commander_volume_offset_changed_cb, struct bt_conn *, int); #endif /* MOCKS_CAP_COMMANDER_H_ */ diff --git a/tests/bluetooth/audio/mocks/src/cap_commander.c b/tests/bluetooth/audio/mocks/src/cap_commander.c index de0e309ecbd9..c4ba4b522c4b 100644 --- a/tests/bluetooth/audio/mocks/src/cap_commander.c +++ b/tests/bluetooth/audio/mocks/src/cap_commander.c @@ -11,17 +11,22 @@ /* List of fakes used by this unit tester */ #define FFF_FAKES_LIST(FAKE) \ FAKE(mock_cap_commander_discovery_complete_cb) \ - FAKE(mock_cap_commander_volume_changed_cb) + FAKE(mock_cap_commander_volume_changed_cb) \ + FAKE(mock_cap_commander_volume_offset_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); +DEFINE_FAKE_VOID_FUNC(mock_cap_commander_volume_offset_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, +#if defined(CONFIG_BT_VCP_VOL_CTLR_VOCS) + .volume_offset_changed = mock_cap_commander_volume_offset_changed_cb, +#endif /* CONFIG_BT_VCP_VOL_CTLR */ #endif /* CONFIG_BT_VCP_VOL_CTLR */ };