bluetooth: host: Unit tests for bt_le_cs_step_data_parse

Add unit tests for bt_le_cs_step_data_parse.
Also exit early (without calling the function pointer) if the next
step would seem to read out of bounds.

Signed-off-by: Olivier Lesage <olivier.lesage@nordicsemi.no>
This commit is contained in:
Olivier Lesage 2024-10-24 13:46:00 +02:00 committed by Alberto Escolar
commit 635d03b7e0
12 changed files with 367 additions and 0 deletions

View file

@ -0,0 +1,25 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(bt_step_data_parse)
include_directories(BEFORE
${ZEPHYR_BASE}/tests/bluetooth/host/cs/mocks
)
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/host host_mocks)
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/host/cs/mocks mocks)
target_link_libraries(testbinary PRIVATE mocks host_mocks)
target_sources(testbinary
PRIVATE
src/main.c
${ZEPHYR_BASE}/subsys/bluetooth/host/cs.c
${ZEPHYR_BASE}/lib/net_buf/buf_simple.c
${ZEPHYR_BASE}/subsys/logging/log_minimal.c
)

View file

@ -0,0 +1,10 @@
CONFIG_ZTEST=y
CONFIG_BT=y
CONFIG_BT_HCI=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_CHANNEL_SOUNDING=y
CONFIG_ASSERT=y
CONFIG_ASSERT_LEVEL=2
CONFIG_ASSERT_VERBOSE=y
CONFIG_ASSERT_ON_ERRORS=y
CONFIG_NET_BUF=y

View file

@ -0,0 +1,171 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mocks/conn.h"
#include "mocks/hci_core.h"
#include "mocks/net_buf.h"
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/cs.h>
#include <zephyr/fff.h>
DEFINE_FFF_GLOBALS;
FAKE_VALUE_FUNC(bool, bt_le_cs_step_data_parse_func, struct bt_le_cs_subevent_step *, void *);
static void fff_reset_rule_before(const struct ztest_unit_test *test, void *fixture)
{
RESET_FAKE(bt_le_cs_step_data_parse_func);
CONN_FFF_FAKES_LIST(RESET_FAKE);
}
ZTEST_RULE(fff_reset_rule, fff_reset_rule_before, NULL);
ZTEST_SUITE(bt_le_cs_step_data_parse, NULL, NULL, NULL, NULL, NULL);
/*
* Test empty data buffer
*
* Constraints:
* - buffer len set to 0
*
* Expected behaviour:
* - Callback function is not called
*/
ZTEST(bt_le_cs_step_data_parse, test_parsing_empty_buf)
{
struct net_buf_simple *buf = NET_BUF_SIMPLE(0);
bt_le_cs_step_data_parse(buf, bt_le_cs_step_data_parse_func, NULL);
zassert_equal(bt_le_cs_step_data_parse_func_fake.call_count, 0);
}
/*
* Test malformed step data
*
* Constraints:
* - step data with a step length going out of bounds
*
* Expected behaviour:
* - Callback function is called once
*/
ZTEST(bt_le_cs_step_data_parse, test_parsing_invalid_length)
{
struct net_buf_simple buf;
uint8_t data[] = {
0x00, 0x01, 0x01, 0x00, /* mode 0 */
0x03, 0x20, 0x03, 0x00, 0x11, /* mode 3 step with bad length */
};
bt_le_cs_step_data_parse_func_fake.return_val = true;
net_buf_simple_init_with_data(&buf, data, ARRAY_SIZE(data));
bt_le_cs_step_data_parse(&buf, bt_le_cs_step_data_parse_func, NULL);
zassert_equal(1, bt_le_cs_step_data_parse_func_fake.call_count, "called %d",
bt_le_cs_step_data_parse_func_fake.call_count);
}
/*
* Test parsing stopped
*
* Constraints:
* - Data contains valid step data
* - Callback function returns false to stop parsing
*
* Expected behaviour:
* - Once parsing is stopped, the callback is not called anymore
*/
ZTEST(bt_le_cs_step_data_parse, test_parsing_stopped)
{
struct net_buf_simple buf;
uint8_t data[] = {
0x00, 0x05, 0x01, 0x00, /* mode 0 */
0x01, 0x10, 0x02, 0x00, 0x11, /* mode 1 */
0x02, 0x11, 0x02, 0x00, 0x11, /* mode 2 */
};
bt_le_cs_step_data_parse_func_fake.return_val = false;
net_buf_simple_init_with_data(&buf, data, ARRAY_SIZE(data));
bt_le_cs_step_data_parse(&buf, bt_le_cs_step_data_parse_func, NULL);
zassert_equal(1, bt_le_cs_step_data_parse_func_fake.call_count, "called %d",
bt_le_cs_step_data_parse_func_fake.call_count);
}
struct custom_user_data {
const uint8_t *data;
size_t len;
};
static bool bt_le_cs_step_data_parse_func_custom_fake(struct bt_le_cs_subevent_step *step,
void *user_data)
{
struct custom_user_data *ud = user_data;
/* mode check */
zassert_true(ud->len-- > 0);
zassert_equal(step->mode, *ud->data);
ud->data++;
/* channel check */
zassert_true(ud->len-- > 0);
zassert_equal(step->channel, *ud->data);
ud->data++;
/* step data length check */
zassert_true(ud->len-- > 0);
zassert_equal(step->data_len, *ud->data);
ud->data++;
/* value check */
zassert_true(ud->len >= step->data_len);
zassert_mem_equal(step->data, ud->data, step->data_len);
ud->data += step->data_len;
ud->len -= step->data_len;
return true;
}
/*
* Test parsing successfully
*
* Constraints:
* - Data contains valid step data
* - Callback function returns false to stop parsing
*
* Expected behaviour:
* - Data passed to the callback match the expected data
*/
ZTEST(bt_le_cs_step_data_parse, test_parsing_success)
{
struct net_buf_simple buf;
uint8_t data[] = {
0x00, 0x05, 0x01, 0x00, /* mode 0 */
0x03, 0x11, 0x01, 0x11, /* mode 3 */
0x02, 0x12, 0x02, 0x00, 0x11, /* mode 2 */
0x03, 0x13, 0x01, 0x11, /* mode 3 */
0x02, 0x14, 0x02, 0x00, 0x11, /* mode 2 */
};
struct custom_user_data user_data = {
.data = data,
.len = ARRAY_SIZE(data),
};
bt_le_cs_step_data_parse_func_fake.custom_fake = bt_le_cs_step_data_parse_func_custom_fake;
net_buf_simple_init_with_data(&buf, data, ARRAY_SIZE(data));
bt_le_cs_step_data_parse(&buf, bt_le_cs_step_data_parse_func, &user_data);
zassert_equal(5, bt_le_cs_step_data_parse_func_fake.call_count, "called %d",
bt_le_cs_step_data_parse_func_fake.call_count);
}

View file

@ -0,0 +1,7 @@
common:
tags:
- bluetooth
- host
tests:
bluetooth.host.cs.bt_le_cs_step_data_parse:
type: unit

View file

@ -0,0 +1,17 @@
# SPDX-License-Identifier: Apache-2.0
add_library(mocks STATIC
conn.c
hci_core.c
net_buf.c
)
target_include_directories(mocks PUBLIC
..
${ZEPHYR_BASE}/subsys/bluetooth
${ZEPHYR_BASE}/subsys/bluetooth/host
${ZEPHYR_BASE}/tests/bluetooth/host
${ZEPHYR_BASE}/tests/bluetooth/host/cs/mocks
)
target_link_libraries(mocks PRIVATE test_interface)

View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mocks/conn.h"
#include <zephyr/kernel.h>
DEFINE_FAKE_VOID_FUNC(bt_conn_unref, struct bt_conn *);
DEFINE_FAKE_VALUE_FUNC(struct bt_conn *, bt_conn_lookup_handle, uint16_t, enum bt_conn_type);
DEFINE_FAKE_VOID_FUNC(notify_remote_cs_capabilities, struct bt_conn *,
struct bt_conn_le_cs_capabilities);
DEFINE_FAKE_VOID_FUNC(notify_remote_cs_fae_table, struct bt_conn *, struct bt_conn_le_cs_fae_table);
DEFINE_FAKE_VOID_FUNC(notify_cs_config_created, struct bt_conn *, struct bt_conn_le_cs_config *);
DEFINE_FAKE_VOID_FUNC(notify_cs_config_removed, struct bt_conn *, uint8_t);
DEFINE_FAKE_VOID_FUNC(notify_cs_subevent_result, struct bt_conn *,
struct bt_conn_le_cs_subevent_result *);
DEFINE_FAKE_VOID_FUNC(notify_cs_security_enable_available, struct bt_conn *);
DEFINE_FAKE_VOID_FUNC(notify_cs_procedure_enable_available, struct bt_conn *,
struct bt_conn_le_cs_procedure_enable_complete *);

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/conn.h>
#include <zephyr/fff.h>
#include <zephyr/kernel.h>
#include <host/conn_internal.h>
/* List of fakes used by this unit tester */
#define CONN_FFF_FAKES_LIST(FAKE) \
FAKE(bt_conn_unref) \
FAKE(bt_conn_lookup_handle) \
FAKE(notify_remote_cs_capabilities) \
FAKE(notify_cs_config_created) \
FAKE(notify_cs_config_removed) \
FAKE(notify_cs_subevent_result) \
FAKE(notify_cs_security_enable_available) \
FAKE(notify_cs_procedure_enable_available) \
FAKE(notify_remote_cs_fae_table)
DECLARE_FAKE_VOID_FUNC(bt_conn_unref, struct bt_conn *);
DECLARE_FAKE_VALUE_FUNC(struct bt_conn *, bt_conn_lookup_handle, uint16_t, enum bt_conn_type);
DECLARE_FAKE_VOID_FUNC(notify_remote_cs_capabilities, struct bt_conn *,
struct bt_conn_le_cs_capabilities);
DECLARE_FAKE_VOID_FUNC(notify_remote_cs_fae_table, struct bt_conn *,
struct bt_conn_le_cs_fae_table);
DECLARE_FAKE_VOID_FUNC(notify_cs_config_created, struct bt_conn *, struct bt_conn_le_cs_config *);
DECLARE_FAKE_VOID_FUNC(notify_cs_config_removed, struct bt_conn *, uint8_t);
DECLARE_FAKE_VOID_FUNC(notify_cs_subevent_result, struct bt_conn *,
struct bt_conn_le_cs_subevent_result *);
DECLARE_FAKE_VOID_FUNC(notify_cs_security_enable_available, struct bt_conn *);
DECLARE_FAKE_VOID_FUNC(notify_cs_procedure_enable_available, struct bt_conn *,
struct bt_conn_le_cs_procedure_enable_complete *);

View file

@ -0,0 +1,19 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mocks/hci_core.h"
#include <zephyr/bluetooth/hci.h>
#include <zephyr/kernel.h>
#include <host/hci_core.h>
struct bt_dev bt_dev = {
.manufacturer = 0x1234,
};
DEFINE_FAKE_VALUE_FUNC(struct net_buf *, bt_hci_cmd_create, uint16_t, uint8_t);
DEFINE_FAKE_VALUE_FUNC(int, bt_hci_cmd_send_sync, uint16_t, struct net_buf *, struct net_buf **);

View file

@ -0,0 +1,16 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/fff.h>
#include <zephyr/kernel.h>
/* List of fakes used by this unit tester */
#define HCI_CORE_FFF_FAKES_LIST(FAKE) \
FAKE(bt_hci_cmd_create) \
FAKE(bt_hci_cmd_send_sync)
DECLARE_FAKE_VALUE_FUNC(struct net_buf *, bt_hci_cmd_create, uint16_t, uint8_t);
DECLARE_FAKE_VALUE_FUNC(int, bt_hci_cmd_send_sync, uint16_t, struct net_buf *, struct net_buf **);

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/net_buf.h>
#include "mocks/net_buf.h"
const struct net_buf_data_cb net_buf_fixed_cb;
DEFINE_FAKE_VOID_FUNC(net_buf_unref, struct net_buf *);
DEFINE_FAKE_VOID_FUNC(net_buf_reset, struct net_buf *);
DEFINE_FAKE_VOID_FUNC(net_buf_slist_put, sys_slist_t *, struct net_buf *);
DEFINE_FAKE_VALUE_FUNC(struct net_buf *, net_buf_alloc_fixed, struct net_buf_pool *, k_timeout_t);

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/fff.h>
#include <zephyr/kernel.h>
#include <zephyr/net_buf.h>
/* List of fakes used by this unit tester */
#define NET_BUF_FFF_FAKES_LIST(FAKE) \
FAKE(net_buf_unref) \
FAKE(net_buf_reset) \
FAKE(net_buf_slist_put) \
FAKE(net_buf_alloc_fixed)
DECLARE_FAKE_VOID_FUNC(net_buf_unref, struct net_buf *);
DECLARE_FAKE_VOID_FUNC(net_buf_reset, struct net_buf *);
DECLARE_FAKE_VOID_FUNC(net_buf_slist_put, sys_slist_t *, struct net_buf *);
DECLARE_FAKE_VALUE_FUNC(struct net_buf *, net_buf_alloc_fixed, struct net_buf_pool *, k_timeout_t);