tests: Bluetooth: audio: Add initial ASCS unit tests

This adds initial ASCS unit tests.
Failing test cases have been conditionally skipped with Zephyr issue
number.

Signed-off-by: Mariusz Skamra <mariusz.skamra@codecoup.pl>
This commit is contained in:
Mariusz Skamra 2023-03-17 08:09:31 +01:00 committed by Carles Cufí
parent 269290d614
commit b176c51785
5 changed files with 808 additions and 0 deletions

View file

@ -0,0 +1,15 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
project(bluetooth_ascs)
find_package(Zephyr COMPONENTS unittest HINTS $ENV{ZEPHYR_BASE})
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/audio/ascs/uut uut)
target_link_libraries(testbinary PRIVATE uut)
target_sources(testbinary
PRIVATE
src/main.c
)

View file

@ -0,0 +1,24 @@
CONFIG_ZTEST=y
CONFIG_ZTEST_NEW_API=y
CONFIG_BT=y
CONFIG_BT_MAX_CONN=1
CONFIG_BT_ISO_MAX_CHAN=1
CONFIG_BT_AUDIO=y
CONFIG_BT_ASCS=y
CONFIG_BT_ASCS_ASE_SNK_COUNT=2
CONFIG_BT_ASCS_ASE_SRC_COUNT=2
CONFIG_BT_ASCS_MAX_ACTIVE_ASES=1
CONFIG_BT_BAP_UNICAST_SERVER=y
CONFIG_BT_ISO_MAX_CHAN=2
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_ASCS_LOG_LEVEL_DBG=y
CONFIG_BT_BAP_ISO_LOG_LEVEL_DBG=y
CONFIG_BT_BAP_STREAM_LOG_LEVEL_DBG=y
CONFIG_BT_BAP_UNICAST_SERVER_LOG_LEVEL_DBG=y
CONFIG_ASSERT=y
CONFIG_ASSERT_LEVEL=2
CONFIG_ASSERT_VERBOSE=y
CONFIG_ASSERT_ON_ERRORS=y

View file

@ -0,0 +1,738 @@
/* main.c - Application main entry point */
/*
* Copyright (c) 2023 Codecoup
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include <zephyr/fff.h>
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/audio/pacs.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/sys/util_macro.h>
#include "assert.h"
#include "ascs_help_utils.h"
#include "bap_unicast_server.h"
#include "bap_unicast_server_expects.h"
#include "bap_stream.h"
#include "bap_stream_expects.h"
#include "conn.h"
#include "gatt.h"
#include "gatt_expects.h"
#include "iso.h"
#include "mock_kernel.h"
#include "pacs.h"
DEFINE_FFF_GLOBALS;
struct attr_found_data {
const struct bt_gatt_attr *attr;
uint16_t found_cnt;
};
static uint8_t attr_found_func(const struct bt_gatt_attr *attr, uint16_t handle,
void *user_data)
{
struct attr_found_data *data = user_data;
data->attr = attr;
data->found_cnt++;
return BT_GATT_ITER_CONTINUE;
}
static const struct bt_gatt_attr *ascs_get_attr(const struct bt_uuid *uuid, uint16_t nth)
{
struct attr_found_data data = {
.attr = NULL,
.found_cnt = 0,
};
if (nth == 0) {
return NULL;
}
bt_gatt_foreach_attr_type(0x0001, 0xFFFF, uuid, NULL, nth, attr_found_func, &data);
if (data.found_cnt != nth) {
return NULL;
}
return data.attr;
}
static void mock_init_rule_before(const struct ztest_unit_test *test, void *fixture)
{
mock_bap_unicast_server_init();
ISO_FFF_FAKES_LIST(RESET_FAKE);
KERNEL_FFF_FAKES_LIST(RESET_FAKE);
PACS_FFF_FAKES_LIST(RESET_FAKE);
mock_bap_stream_init();
mock_bt_gatt_init();
}
static void mock_destroy_rule_after(const struct ztest_unit_test *test, void *fixture)
{
mock_bap_unicast_server_cleanup();
mock_bap_stream_cleanup();
mock_bt_gatt_cleanup();
}
ZTEST_RULE(mock_rule, mock_init_rule_before, mock_destroy_rule_after);
ZTEST_SUITE(ascs_attrs_test_suite, NULL, NULL, NULL, NULL, NULL);
ZTEST(ascs_attrs_test_suite, test_has_sink_ase_chrc)
{
Z_TEST_SKIP_IFNDEF(CONFIG_BT_ASCS_ASE_SNK);
zassert_not_null(ascs_get_attr(BT_UUID_ASCS_ASE_SNK, 1));
zassert_not_null(ascs_get_attr(BT_UUID_ASCS_ASE_SNK, CONFIG_BT_ASCS_ASE_SNK_COUNT));
}
ZTEST(ascs_attrs_test_suite, test_has_source_ase_chrc)
{
Z_TEST_SKIP_IFNDEF(CONFIG_BT_ASCS_ASE_SRC);
zassert_not_null(ascs_get_attr(BT_UUID_ASCS_ASE_SRC, 1));
zassert_not_null(ascs_get_attr(BT_UUID_ASCS_ASE_SRC, CONFIG_BT_ASCS_ASE_SRC_COUNT));
}
ZTEST(ascs_attrs_test_suite, test_has_single_control_point_chrc)
{
zassert_not_null(ascs_get_attr(BT_UUID_ASCS_ASE_CP, 1));
zassert_is_null(ascs_get_attr(BT_UUID_ASCS_ASE_CP, 2));
}
static struct bt_codec lc3_codec =
BT_CODEC_LC3(BT_CODEC_LC3_FREQ_ANY, BT_CODEC_LC3_DURATION_10,
BT_CODEC_LC3_CHAN_COUNT_SUPPORT(1), 40u, 120u, 1u,
(BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL | BT_AUDIO_CONTEXT_TYPE_MEDIA));
static const struct bt_codec_qos_pref qos_pref = BT_CODEC_QOS_PREF(true, BT_GAP_LE_PHY_2M,
0x02, 10, 40000, 40000,
40000, 40000);
static void pacs_cap_foreach_custom_fake(enum bt_audio_dir dir, bt_pacs_cap_foreach_func_t func,
void *user_data)
{
static const struct bt_pacs_cap cap[] = {
{
&lc3_codec,
},
};
for (size_t i = 0; i < ARRAY_SIZE(cap); i++) {
if (func(&cap[i], user_data) == false) {
break;
}
}
}
struct ascs_ase_control_test_suite_fixture {
struct bt_conn conn;
struct bt_bap_stream stream;
const struct bt_gatt_attr *ase_cp;
const struct bt_gatt_attr *ase_snk;
};
static void conn_init(struct bt_conn *conn)
{
memset(conn, 0, sizeof(*conn));
conn->index = 0;
conn->info.type = BT_CONN_TYPE_LE;
conn->info.role = BT_CONN_ROLE_PERIPHERAL;
conn->info.state = BT_CONN_STATE_CONNECTED;
conn->info.security.level = BT_SECURITY_L3;
conn->info.security.enc_key_size = BT_ENC_KEY_SIZE_MAX;
conn->info.security.flags = BT_SECURITY_FLAG_OOB | BT_SECURITY_FLAG_SC;
}
static void *ascs_ase_control_test_suite_setup(void)
{
struct ascs_ase_control_test_suite_fixture *fixture;
fixture = malloc(sizeof(*fixture));
zassert_not_null(fixture);
conn_init(&fixture->conn);
fixture->ase_cp = ascs_get_attr(BT_UUID_ASCS_ASE_CP, 1);
fixture->ase_snk = ascs_get_attr(BT_UUID_ASCS_ASE_SNK, 1);
bt_bap_unicast_server_register_cb(&mock_bap_unicast_server_cb);
return fixture;
}
static void ascs_ase_control_test_suite_before(void *f)
{
struct ascs_ase_control_test_suite_fixture *fixture = f;
ARG_UNUSED(fixture);
bt_pacs_cap_foreach_fake.custom_fake = pacs_cap_foreach_custom_fake;
}
static void ascs_ase_control_test_suite_after(void *f)
{
ascs_cleanup();
}
static void ascs_ase_control_test_suite_teardown(void *f)
{
struct ascs_ase_control_test_suite_fixture *fixture = f;
bt_bap_unicast_server_unregister_cb(&mock_bap_unicast_server_cb);
free(fixture);
}
ZTEST_SUITE(ascs_ase_control_test_suite, NULL, ascs_ase_control_test_suite_setup,
ascs_ase_control_test_suite_before, ascs_ase_control_test_suite_after,
ascs_ase_control_test_suite_teardown);
ZTEST_F(ascs_ase_control_test_suite, test_sink_ase_read_state_idle)
{
uint8_t data_expected[] = {
0x01, /* ASE_ID */
0x00, /* ASE_State = Idle */
};
ssize_t ret;
Z_TEST_SKIP_IFNDEF(CONFIG_BT_ASCS_ASE_SNK);
ret = fixture->ase_snk->read(&fixture->conn, fixture->ase_snk, NULL, 0, 0);
zassert_false(ret < 0, "attr->read returned unexpected (err 0x%02x)",
BT_GATT_ERR(ret));
expect_bt_gatt_attr_read_called_once(&fixture->conn, fixture->ase_snk, EMPTY,
EMPTY, 0x0000, data_expected, sizeof(data_expected));
}
ZTEST_F(ascs_ase_control_test_suite, test_sink_ase_control_operation_zero_length_write)
{
uint8_t buf[] = {};
ssize_t ret;
ret = fixture->ase_cp->write(&fixture->conn, fixture->ase_cp, (void *)buf, 0, 0, 0);
zassert_true(ret < 0, "ase_cp_attr->write returned unexpected (err 0x%02x)",
BT_GATT_ERR(ret));
}
static void test_unsupported_opcode(struct ascs_ase_control_test_suite_fixture *fixture,
uint8_t opcode)
{
uint8_t buf[] = {
opcode, /* Opcode */
0x01, /* Number_of_ASEs */
0x01, /* ASE_ID[0] */
};
uint8_t data_expected[] = {
opcode, /* Opcode */
0xFF, /* Number_of_ASEs */
0x00, /* ASE_ID[0] */
0x01, /* Response_Code[0] = Unsupported Opcode */
0x00, /* Reason[0] */
};
fixture->ase_cp->write(&fixture->conn, fixture->ase_cp, (void *)buf, sizeof(buf), 0, 0);
expect_bt_gatt_notify_cb_called_once(&fixture->conn, BT_UUID_ASCS_ASE_CP,
fixture->ase_cp, data_expected, sizeof(data_expected));
}
ZTEST_F(ascs_ase_control_test_suite, test_unsupported_opcode_0x00)
{
test_unsupported_opcode(fixture, 0x00);
}
ZTEST_F(ascs_ase_control_test_suite, test_unsupported_opcode_rfu)
{
test_unsupported_opcode(fixture, 0x09);
}
static void test_codec_configure_invalid_length(struct ascs_ase_control_test_suite_fixture *fixture,
const uint8_t *buf, size_t len)
{
const uint8_t data_expected[] = {
0x01, /* Opcode */
0xFF, /* Number_of_ASEs */
0x00, /* ASE_ID[0] */
0x02, /* Response_Code[0] = Invalid Length */
0x00, /* Reason[0] */
};
fixture->ase_cp->write(&fixture->conn, fixture->ase_cp, buf, len, 0, 0);
expect_bt_gatt_notify_cb_called_once(&fixture->conn, BT_UUID_ASCS_ASE_CP,
fixture->ase_cp, data_expected, sizeof(data_expected));
}
/*
* Test correctly formatted ASE Control Point 'Invalid Length' notification is sent
*
* ASCS_v1.0; 5 ASE Control operations
* "A client-initiated ASE Control operation shall be defined as an invalid length operation
* if the Number_of_ASEs parameter value is less than 1"
*
* Constraints:
* - Number_of_ASEs is set to 0
* - Config Codec operation parameter array is valid
*
* Expected behaviour:
* - "If the Response_Code value is 0x01 or 0x02, Number_of_ASEs shall be set to 0xFF."
* - ASE Control Point notification is correctly formatted
*/
ZTEST_F(ascs_ase_control_test_suite, test_codec_configure_invalid_length_number_of_ases_0x00)
{
const uint8_t buf[] = {
0x01, /* Opcode = Config Codec */
0x00, /* Number_of_ASEs */
0x01, /* ASE_ID[0] */
0x01, /* Target_Latency[0] = Target low latency */
0x02, /* Target_PHY[0] = LE 2M PHY */
0x06, /* Codec_ID[0].Coding_Format = LC3 */
0x00, 0x00, /* Codec_ID[0].Company_ID */
0x00, 0x00, /* Codec_ID[0].Vendor_Specific_Codec_ID */
0x00, /* Codec_Specific_Configuration_Length[0] */
};
test_codec_configure_invalid_length(fixture, buf, sizeof(buf));
}
/*
* Test correctly formatted ASE Control Point 'Invalid Length' notification is sent
*
* ASCS_v1.0; 5 ASE Control operations
* "A client-initiated ASE Control operation shall be defined as an invalid length operation(...)
* if the Number_of_ASEs parameter value does not match the number of parameter arrays written by
* the client"
*
* Constraints:
* - Number_of_ASEs is set to 1
* - Config Codec operation parameter arrays != Number_of_ASEs and is set to 2
*
* Expected behaviour:
* - "If the Response_Code value is 0x01 or 0x02, Number_of_ASEs shall be set to 0xFF."
* - ASE Control Point notification is correctly formatted
*/
ZTEST_F(ascs_ase_control_test_suite, test_codec_configure_invalid_length_too_many_parameter_arrays)
{
const uint8_t buf[] = {
0x01, /* Opcode = Config Codec */
0x01, /* Number_of_ASEs */
0x01, /* ASE_ID[0] */
0x01, /* Target_Latency[0] = Target low latency */
0x02, /* Target_PHY[0] = LE 2M PHY */
0x06, /* Codec_ID[0].Coding_Format = LC3 */
0x00, 0x00, /* Codec_ID[0].Company_ID */
0x00, 0x00, /* Codec_ID[0].Vendor_Specific_Codec_ID */
0x00, /* Codec_Specific_Configuration_Length[0] */
0x02, /* ASE_ID[1] */
0x01, /* Target_Latency[1] = Target low latency */
0x02, /* Target_PHY[1] = LE 2M PHY */
0x06, /* Codec_ID[1].Coding_Format = LC3 */
0x00, 0x00, /* Codec_ID[1].Company_ID */
0x00, 0x00, /* Codec_ID[1].Vendor_Specific_Codec_ID */
0x00, /* Codec_Specific_Configuration_Length[1] */
};
test_codec_configure_invalid_length(fixture, buf, sizeof(buf));
}
/*
* Test correctly formatted ASE Control Point 'Invalid Length' notification is sent
*
* ASCS_v1.0; 5 ASE Control operations
* "A client-initiated ASE Control operation shall be defined as an invalid length operation
* if the total length of all parameters written by the client is not equal to the total length
* of all fixed parameters plus the length of any variable length parameters for that operation"
*
* Constraints:
* - Number_of_ASEs is set to 1
* - Config Codec operation parameter arrays == Number_of_ASEs
* - Codec_Specific_Configuration_Length[i] > sizeof(Codec_Specific_Configuration[i])
*
* Expected behaviour:
* - "If the Response_Code value is 0x01 or 0x02, Number_of_ASEs shall be set to 0xFF."
* - ASE Control Point notification is correctly formatted
*/
ZTEST_F(ascs_ase_control_test_suite,
test_codec_configure_invalid_length_codec_specific_configuration_too_short)
{
uint8_t buf[] = {
0x01, /* Opcode = Config Codec */
0x01, /* Number_of_ASEs */
0x01, /* ASE_ID[0] */
0x01, /* Target_Latency[0] = Target low latency */
0x02, /* Target_PHY[0] = LE 2M PHY */
0x06, /* Codec_ID[0].Coding_Format = LC3 */
0x00, 0x00, /* Codec_ID[0].Company_ID */
0x00, 0x00, /* Codec_ID[0].Vendor_Specific_Codec_ID */
0x05, /* Codec_Specific_Configuration_Length[0] */
0x00, 0x00, /* Codec_Specific_Configuration[0] */
0x00, 0x00,
};
test_codec_configure_invalid_length(fixture, buf, sizeof(buf));
}
/*
* Test correctly formatted ASE Control Point 'Invalid Length' notification is sent
*
* ASCS_v1.0; 5 ASE Control operations
* "A client-initiated ASE Control operation shall be defined as an invalid length operation
* if the total length of all parameters written by the client is not equal to the total length
* of all fixed parameters plus the length of any variable length parameters for that operation"
*
* Constraints:
* - Number_of_ASEs is set to 1
* - Config Codec operation parameter arrays == Number_of_ASEs
* - Codec_Specific_Configuration_Length[i] < sizeof(Codec_Specific_Configuration[i])
*
* Expected behaviour:
* - "If the Response_Code value is 0x01 or 0x02, Number_of_ASEs shall be set to 0xFF."
* - ASE Control Point notification is correctly formatted
*/
ZTEST_F(ascs_ase_control_test_suite,
test_codec_configure_invalid_length_codec_specific_configuration_too_long)
{
uint8_t buf[] = {
0x01, /* Opcode = Config Codec */
0x01, /* Number_of_ASEs */
0x01, /* ASE_ID[0] */
0x01, /* Target_Latency[0] = Target low latency */
0x02, /* Target_PHY[0] = LE 2M PHY */
0x06, /* Codec_ID[0].Coding_Format = LC3 */
0x00, 0x00, /* Codec_ID[0].Company_ID */
0x00, 0x00, /* Codec_ID[0].Vendor_Specific_Codec_ID */
0x05, /* Codec_Specific_Configuration_Length[0] */
0x00, 0x00, /* Codec_Specific_Configuration[0] */
0x00, 0x00,
0x00, 0x00,
};
test_codec_configure_invalid_length(fixture, buf, sizeof(buf));
}
/*
* Test correctly formatted ASE Control Point 'Invalid ASE_ID' notification is sent
*
* Constraints:
* - Number_of_ASEs is set to 1
* - Requested ASE_ID is not present on the server.
*
* Expected behaviour:
* - Correctly formatted ASE Control Point notification is sent with Invalid ASE_ID response code.
*/
static void test_codec_configure_invalid_ase_id(struct ascs_ase_control_test_suite_fixture *fixture,
uint8_t ase_id)
{
const uint8_t buf[] = {
0x01, /* Opcode = Config Codec */
0x01, /* Number_of_ASEs */
ase_id, /* ASE_ID[0] */
0x01, /* Target_Latency[0] = Target low latency */
0x02, /* Target_PHY[0] = LE 2M PHY */
0x06, /* Codec_ID[0].Coding_Format = LC3 */
0x00, 0x00, /* Codec_ID[0].Company_ID */
0x00, 0x00, /* Codec_ID[0].Vendor_Specific_Codec_ID */
0x00, /* Codec_Specific_Configuration_Length[0] */
};
const uint8_t data_expected[] = {
0x01, /* Opcode */
0x01, /* Number_of_ASEs */
ase_id, /* ASE_ID[0] */
0x03, /* Response_Code[0] = Invalid ASE_ID */
0x00, /* Reason[0] */
};
fixture->ase_cp->write(&fixture->conn, fixture->ase_cp, buf, sizeof(buf), 0, 0);
expect_bt_gatt_notify_cb_called_once(&fixture->conn, BT_UUID_ASCS_ASE_CP,
fixture->ase_cp, data_expected, sizeof(data_expected));
}
ZTEST_F(ascs_ase_control_test_suite, test_codec_configure_invalid_ase_id_0x00)
{
test_codec_configure_invalid_ase_id(fixture, 0x00);
}
ZTEST_F(ascs_ase_control_test_suite, test_codec_configure_invalid_ase_id_unavailable)
{
test_codec_configure_invalid_ase_id(fixture, (CONFIG_BT_ASCS_ASE_SNK_COUNT +
CONFIG_BT_ASCS_ASE_SRC_COUNT + 1));
}
static void test_codec_configure_target_latency_out_of_range(
struct ascs_ase_control_test_suite_fixture *fixture, uint8_t target_latency)
{
const uint8_t buf[] = {
0x01, /* Opcode = Config Codec */
0x01, /* Number_of_ASEs */
0x01, /* ASE_ID[0] */
target_latency, /* Target_Latency[0] */
0x02, /* Target_PHY[0] = LE 2M PHY */
0x06, /* Codec_ID[0].Coding_Format = LC3 */
0x00, 0x00, /* Codec_ID[0].Company_ID */
0x00, 0x00, /* Codec_ID[0].Vendor_Specific_Codec_ID */
0x00, /* Codec_Specific_Configuration_Length[0] */
};
const uint8_t data_expected[] = {
0x01, /* Opcode */
0x01, /* Number_of_ASEs */
0x01, /* ASE_ID[0] */
0x00, /* Response_Code[0] = Success */
0x00, /* Reason[0] */
};
fixture->ase_cp->write(&fixture->conn, fixture->ase_cp, buf, sizeof(buf), 0, 0);
expect_bt_gatt_notify_cb_called_once(&fixture->conn, BT_UUID_ASCS_ASE_CP,
fixture->ase_cp, data_expected, sizeof(data_expected));
}
ZTEST_F(ascs_ase_control_test_suite, test_codec_configure_target_latency_out_of_range_0x00)
{
/* TODO: Remove once resolved */
Z_TEST_SKIP_IFNDEF(BUG_55794);
test_codec_configure_target_latency_out_of_range(fixture, 0x00);
}
ZTEST_F(ascs_ase_control_test_suite, test_codec_configure_target_latency_out_of_range_0x04)
{
/* TODO: Remove once resolved */
Z_TEST_SKIP_IFNDEF(BUG_55794);
test_codec_configure_target_latency_out_of_range(fixture, 0x04);
}
static void test_codec_configure_target_phy_out_of_range(
struct ascs_ase_control_test_suite_fixture *fixture, uint8_t target_phy)
{
const uint8_t buf[] = {
0x01, /* Opcode = Config Codec */
0x01, /* Number_of_ASEs */
0x01, /* ASE_ID[0] */
0x01, /* Target_Latency[0] */
target_phy, /* Target_PHY[0] */
0x06, /* Codec_ID[0].Coding_Format = LC3 */
0x00, 0x00, /* Codec_ID[0].Company_ID */
0x00, 0x00, /* Codec_ID[0].Vendor_Specific_Codec_ID */
0x00, /* Codec_Specific_Configuration_Length[0] */
};
const uint8_t data_expected[] = {
0x01, /* Opcode */
0x01, /* Number_of_ASEs */
0x01, /* ASE_ID[0] */
0x00, /* Response_Code[0] = Success */
0x00, /* Reason[0] */
};
fixture->ase_cp->write(&fixture->conn, fixture->ase_cp, buf, sizeof(buf), 0, 0);
expect_bt_gatt_notify_cb_called_once(&fixture->conn, BT_UUID_ASCS_ASE_CP,
fixture->ase_cp, data_expected, sizeof(data_expected));
}
ZTEST_F(ascs_ase_control_test_suite, test_codec_configure_target_phy_out_of_range_0x00)
{
/* TODO: Remove once resolved */
Z_TEST_SKIP_IFNDEF(BUG_55794);
test_codec_configure_target_phy_out_of_range(fixture, 0x00);
}
ZTEST_F(ascs_ase_control_test_suite, test_codec_configure_target_phy_out_of_range_0x04)
{
/* TODO: Remove once resolved */
Z_TEST_SKIP_IFNDEF(BUG_55794);
test_codec_configure_target_phy_out_of_range(fixture, 0x04);
}
static struct bt_bap_stream *stream_allocated;
int unicast_server_cb_config_custom_fake(struct bt_conn *conn, const struct bt_bap_ep *ep,
enum bt_audio_dir dir, const struct bt_codec *codec,
struct bt_bap_stream **stream,
struct bt_codec_qos_pref *const pref)
{
*stream = stream_allocated;
*pref = qos_pref;
bt_bap_stream_cb_register(*stream, &mock_bap_stream_ops);
return 0;
}
static void ase_cp_write_codec_config(const struct bt_gatt_attr *attr, struct bt_conn *conn,
struct bt_bap_stream *stream)
{
const uint8_t buf[] = {
0x01, /* Opcode = Config Codec */
0x01, /* Number_of_ASEs */
0x01, /* ASE_ID[0] */
0x01, /* Target_Latency[0] = Target low latency */
0x02, /* Target_PHY[0] = LE 2M PHY */
0x06, /* Codec_ID[0].Coding_Format = LC3 */
0x00, 0x00, /* Codec_ID[0].Company_ID */
0x00, 0x00, /* Codec_ID[0].Vendor_Specific_Codec_ID */
0x00, /* Codec_Specific_Configuration_Length[0] */
};
ssize_t ret;
stream_allocated = stream;
mock_bap_unicast_server_cb_config_fake.custom_fake = unicast_server_cb_config_custom_fake;
ret = attr->write(conn, attr, (void *)buf, sizeof(buf), 0, 0);
zassert_false(ret < 0, "cp_attr->write returned unexpected (err 0x%02x)", BT_GATT_ERR(ret));
stream_allocated = NULL;
}
ZTEST_F(ascs_ase_control_test_suite, test_sink_ase_control_codec_configure_from_idle)
{
Z_TEST_SKIP_IFNDEF(CONFIG_BT_ASCS_ASE_SNK);
ase_cp_write_codec_config(fixture->ase_cp, &fixture->conn, &fixture->stream);
expect_bt_bap_unicast_server_cb_config_called_once(&fixture->conn, EMPTY, BT_AUDIO_DIR_SINK,
EMPTY);
expect_bt_bap_stream_ops_configured_called_once(&fixture->stream, EMPTY);
}
static void ase_cp_write_config_qos(const struct bt_gatt_attr *attr, struct bt_conn *conn,
struct bt_bap_stream *stream)
{
const uint8_t buf[] = {
0x02, /* Opcode = Config QoS */
0x01, /* Number_of_ASEs */
0x01, /* ASE_ID[0] */
0x01, /* CIG_ID[0] */
0x01, /* CIS_ID[0] */
0xFF, 0x00, 0x00, /* SDU_Interval[0] */
0x00, /* Framing[0] */
0x02, /* PHY[0] */
0x64, 0x00, /* Max_SDU[0] */
0x02, /* Retransmission_Number[0] */
0x0A, 0x00, /* Max_Transport_Latency[0] */
0x40, 0x9C, 0x00, /* Presentation_Delay[0] */
};
ssize_t ret;
ret = attr->write(conn, attr, (void *)buf, sizeof(buf), 0, 0);
zassert_false(ret < 0, "attr->write returned unexpected (err 0x%02x)", BT_GATT_ERR(ret));
}
ZTEST_F(ascs_ase_control_test_suite, test_sink_ase_control_qos_configure_from_codec_configured)
{
Z_TEST_SKIP_IFNDEF(CONFIG_BT_ASCS_ASE_SNK);
/* Preamble */
ase_cp_write_codec_config(fixture->ase_cp, &fixture->conn, &fixture->stream);
ase_cp_write_config_qos(fixture->ase_cp, &fixture->conn, &fixture->stream);
expect_bt_bap_unicast_server_cb_qos_called_once(&fixture->stream, EMPTY);
expect_bt_bap_stream_ops_qos_set_called_once(&fixture->stream);
}
static void ase_cp_write_enable(const struct bt_gatt_attr *attr, struct bt_conn *conn,
struct bt_bap_stream *stream)
{
const uint8_t buf[] = {
0x03, /* Opcode = Enable */
0x01, /* Number_of_ASEs */
0x01, /* ASE_ID[0] */
0x00, /* Metadata_Length[0] */
};
ssize_t ret;
ret = attr->write(conn, attr, (void *)buf, sizeof(buf), 0, 0);
zassert_false(ret < 0, "attr->write returned unexpected (err 0x%02x)", BT_GATT_ERR(ret));
}
ZTEST_F(ascs_ase_control_test_suite, test_sink_ase_control_enable_from_qos_configured)
{
Z_TEST_SKIP_IFNDEF(CONFIG_BT_ASCS_ASE_SNK);
/* Preamble */
ase_cp_write_codec_config(fixture->ase_cp, &fixture->conn, &fixture->stream);
ase_cp_write_config_qos(fixture->ase_cp, &fixture->conn, &fixture->stream);
ase_cp_write_enable(fixture->ase_cp, &fixture->conn, &fixture->stream);
expect_bt_bap_unicast_server_cb_enable_called_once(&fixture->stream, EMPTY, EMPTY);
expect_bt_bap_stream_ops_enabled_called_once(&fixture->stream);
}
static void ase_cp_write_disable(const struct bt_gatt_attr *attr, struct bt_conn *conn,
struct bt_bap_stream *stream)
{
const uint8_t buf[] = {
0x05, /* Opcode = Disable */
0x01, /* Number_of_ASEs */
0x01, /* ASE_ID[0] */
};
ssize_t ret;
ret = attr->write(conn, attr, (void *)buf, sizeof(buf), 0, 0);
zassert_false(ret < 0, "attr->write returned unexpected (err 0x%02x)", BT_GATT_ERR(ret));
}
ZTEST_F(ascs_ase_control_test_suite, test_sink_ase_control_disable_from_enabling)
{
Z_TEST_SKIP_IFNDEF(CONFIG_BT_ASCS_ASE_SNK);
/* Preamble */
ase_cp_write_codec_config(fixture->ase_cp, &fixture->conn, &fixture->stream);
ase_cp_write_config_qos(fixture->ase_cp, &fixture->conn, &fixture->stream);
ase_cp_write_enable(fixture->ase_cp, &fixture->conn, &fixture->stream);
/* Reset the bap_stream_ops.qos_set callback that is expected to be called again */
mock_bap_stream_qos_set_cb_reset();
ase_cp_write_disable(fixture->ase_cp, &fixture->conn, &fixture->stream);
expect_bt_bap_unicast_server_cb_disable_called_once(&fixture->stream);
expect_bt_bap_stream_ops_qos_set_called_once(&fixture->stream);
}
static void ase_cp_write_release(const struct bt_gatt_attr *attr, struct bt_conn *conn,
struct bt_bap_stream *stream)
{
const uint8_t buf[] = {
0x08, /* Opcode = Disable */
0x01, /* Number_of_ASEs */
0x01, /* ASE_ID[0] */
};
ssize_t ret;
ret = attr->write(conn, attr, (void *)buf, sizeof(buf), 0, 0);
zassert_false(ret < 0, "attr->write returned unexpected (err 0x%02x)", BT_GATT_ERR(ret));
}
ZTEST_F(ascs_ase_control_test_suite, test_sink_ase_control_release_from_qos_configured)
{
Z_TEST_SKIP_IFNDEF(CONFIG_BT_ASCS_ASE_SNK);
/* Preamble */
ase_cp_write_codec_config(fixture->ase_cp, &fixture->conn, &fixture->stream);
ase_cp_write_config_qos(fixture->ase_cp, &fixture->conn, &fixture->stream);
ase_cp_write_enable(fixture->ase_cp, &fixture->conn, &fixture->stream);
ase_cp_write_disable(fixture->ase_cp, &fixture->conn, &fixture->stream);
/* QoS configured state */
ase_cp_write_release(fixture->ase_cp, &fixture->conn, &fixture->stream);
expect_bt_bap_unicast_server_cb_release_called_once(&fixture->stream);
expect_bt_bap_stream_ops_released_called_once(&fixture->stream);
}

View file

@ -0,0 +1,5 @@
common:
tags: bluetooth bluetooth_audio
tests:
bluetooth.audio.ascs.test:
type: unit

View file

@ -0,0 +1,26 @@
#
# Copyright (c) 2022 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
# CMakeLists.txt file for creating of uut library.
#
add_library(uut STATIC
${ZEPHYR_BASE}/subsys/bluetooth/audio/ascs.c
${ZEPHYR_BASE}/subsys/bluetooth/audio/audio.c
${ZEPHYR_BASE}/subsys/bluetooth/audio/bap_iso.c
${ZEPHYR_BASE}/subsys/bluetooth/audio/bap_stream.c
${ZEPHYR_BASE}/subsys/bluetooth/audio/bap_unicast_server.c
${ZEPHYR_BASE}/subsys/bluetooth/common/bt_str.c
${ZEPHYR_BASE}/subsys/bluetooth/host/data.c
${ZEPHYR_BASE}/subsys/bluetooth/host/uuid.c
${ZEPHYR_BASE}/subsys/logging/log_minimal.c
${ZEPHYR_BASE}/subsys/net/buf_simple.c
)
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/audio/mocks mocks)
target_link_libraries(uut PUBLIC test_interface mocks)
target_compile_options(uut PRIVATE -std=c11 -include ztest.h)