tests: Bluetooth: CAP Commander Change Volume mute unittests

Adds unittests for the CAP COmmander Change Volume mute
procedure.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
diff --git a/tests/bluetooth/audio/cap_commander/src/main.c b/tests/bluetooth/audio/cap_commander/src/main.c
index d4709f6..f72d3d6 100644
--- a/tests/bluetooth/audio/cap_commander/src/main.c
+++ b/tests/bluetooth/audio/cap_commander/src/main.c
@@ -654,3 +654,216 @@
 	err = bt_cap_commander_change_volume_offset(&param);
 	zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
 }
+
+ZTEST_F(cap_commander_test_suite, test_commander_change_volume_mute_state)
+{
+	union bt_cap_set_member members[ARRAY_SIZE(fixture->conns)];
+	const struct bt_cap_commander_change_volume_mute_state_param param = {
+		.type = BT_CAP_SET_TYPE_AD_HOC,
+		.members = members,
+		.count = ARRAY_SIZE(fixture->conns),
+		.mute = true,
+	};
+	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_mute_state(&param);
+	zassert_equal(0, err, "Unexpected return value %d", err);
+
+	zexpect_call_count("bt_cap_commander_cb.volume_mute_changed", 1,
+			   mock_cap_commander_volume_mute_changed_cb_fake.call_count);
+}
+
+ZTEST_F(cap_commander_test_suite, test_commander_change_volume_mute_state_double)
+{
+	union bt_cap_set_member members[ARRAY_SIZE(fixture->conns)];
+	const struct bt_cap_commander_change_volume_mute_state_param param = {
+		.type = BT_CAP_SET_TYPE_AD_HOC,
+		.members = members,
+		.count = ARRAY_SIZE(fixture->conns),
+		.mute = true,
+	};
+	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_mute_state(&param);
+	zassert_equal(0, err, "Unexpected return value %d", err);
+
+	zexpect_call_count("bt_cap_commander_cb.volume_mute_changed", 1,
+			   mock_cap_commander_volume_mute_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_mute_state(&param);
+	zassert_equal(0, err, "Unexpected return value %d", err);
+
+	zexpect_call_count("bt_cap_commander_cb.volume_mute_changed", 2,
+			   mock_cap_commander_volume_mute_changed_cb_fake.call_count);
+}
+
+ZTEST_F(cap_commander_test_suite, test_commander_change_volume_mute_state_inval_param_null)
+{
+	int err;
+
+	err = bt_cap_commander_change_volume_mute_state(NULL);
+	zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
+}
+
+ZTEST_F(cap_commander_test_suite, test_commander_change_volume_mute_state_inval_param_null_members)
+{
+	const struct bt_cap_commander_change_volume_mute_state_param param = {
+		.type = BT_CAP_SET_TYPE_AD_HOC,
+		.members = NULL,
+		.count = ARRAY_SIZE(fixture->conns),
+		.mute = true,
+	};
+	int err;
+
+	err = bt_cap_commander_change_volume_mute_state(&param);
+	zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
+}
+
+ZTEST_F(cap_commander_test_suite, test_commander_change_volume_mute_state_inval_param_null_member)
+{
+	union bt_cap_set_member members[ARRAY_SIZE(fixture->conns)];
+	const struct bt_cap_commander_change_volume_mute_state_param param = {
+		.type = BT_CAP_SET_TYPE_AD_HOC,
+		.members = members,
+		.count = ARRAY_SIZE(fixture->conns),
+		.mute = true,
+	};
+	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_mute_state(&param);
+	zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
+}
+
+ZTEST_F(cap_commander_test_suite, test_commander_change_volume_mute_state_inval_missing_cas)
+{
+	union bt_cap_set_member members[ARRAY_SIZE(fixture->conns)];
+	const struct bt_cap_commander_change_volume_mute_state_param param = {
+		.type = BT_CAP_SET_TYPE_AD_HOC,
+		.members = members,
+		.count = ARRAY_SIZE(fixture->conns),
+		.mute = true,
+	};
+	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_mute_state(&param);
+	zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
+}
+
+ZTEST_F(cap_commander_test_suite, test_commander_change_volume_mute_state_inval_missing_vcs)
+{
+	union bt_cap_set_member members[ARRAY_SIZE(fixture->conns)];
+	const struct bt_cap_commander_change_volume_mute_state_param param = {
+		.type = BT_CAP_SET_TYPE_AD_HOC,
+		.members = members,
+		.count = ARRAY_SIZE(fixture->conns),
+		.mute = true,
+	};
+	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_mute_state(&param);
+	zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
+}
+
+ZTEST_F(cap_commander_test_suite, test_commander_change_volume_mute_state_inval_param_zero_count)
+{
+	union bt_cap_set_member members[ARRAY_SIZE(fixture->conns)];
+	const struct bt_cap_commander_change_volume_mute_state_param param = {
+		.type = BT_CAP_SET_TYPE_AD_HOC,
+		.members = members,
+		.count = 0U,
+		.mute = true,
+	};
+	int err;
+
+	for (size_t i = 0U; i < ARRAY_SIZE(members); i++) {
+		members[i].member = &fixture->conns[i];
+	}
+
+	err = bt_cap_commander_change_volume_mute_state(&param);
+	zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
+}
+
+ZTEST_F(cap_commander_test_suite, test_commander_change_volume_mute_state_inval_param_inval_count)
+{
+	union bt_cap_set_member members[ARRAY_SIZE(fixture->conns)];
+	const struct bt_cap_commander_change_volume_mute_state_param param = {
+		.type = BT_CAP_SET_TYPE_AD_HOC,
+		.members = members,
+		.count = CONFIG_BT_MAX_CONN + 1,
+		.mute = true,
+	};
+	int err;
+
+	for (size_t i = 0U; i < ARRAY_SIZE(members); i++) {
+		members[i].member = &fixture->conns[i];
+	}
+
+	err = bt_cap_commander_change_volume_mute_state(&param);
+	zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
+}
diff --git a/tests/bluetooth/audio/cap_commander/uut/vcp.c b/tests/bluetooth/audio/cap_commander/uut/vcp.c
index c3390c2..c47fd70 100644
--- a/tests/bluetooth/audio/cap_commander/uut/vcp.c
+++ b/tests/bluetooth/audio/cap_commander/uut/vcp.c
@@ -43,6 +43,24 @@
 	return 0;
 }
 
+int bt_vcp_vol_ctlr_mute(struct bt_vcp_vol_ctlr *vol_ctlr)
+{
+	if (vcp_cb != NULL && vcp_cb->mute != NULL) {
+		vcp_cb->mute(vol_ctlr, 0);
+	}
+
+	return 0;
+}
+
+int bt_vcp_vol_ctlr_unmute(struct bt_vcp_vol_ctlr *vol_ctlr)
+{
+	if (vcp_cb != NULL && vcp_cb->unmute != NULL) {
+		vcp_cb->unmute(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++) {
diff --git a/tests/bluetooth/audio/mocks/include/cap_commander.h b/tests/bluetooth/audio/mocks/include/cap_commander.h
index 73fab99..8ccd2ca 100644
--- a/tests/bluetooth/audio/mocks/include/cap_commander.h
+++ b/tests/bluetooth/audio/mocks/include/cap_commander.h
@@ -18,6 +18,7 @@
 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_mute_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 c4ba4b5..abf9245 100644
--- a/tests/bluetooth/audio/mocks/src/cap_commander.c
+++ b/tests/bluetooth/audio/mocks/src/cap_commander.c
@@ -12,18 +12,21 @@
 #define FFF_FAKES_LIST(FAKE)                                                                       \
 	FAKE(mock_cap_commander_discovery_complete_cb)                                             \
 	FAKE(mock_cap_commander_volume_changed_cb)                                                 \
+	FAKE(mock_cap_commander_volume_mute_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_mute_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,
+	.volume_mute_changed = mock_cap_commander_volume_mute_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 */