Skip to content

Commit 7d066e9

Browse files
MechanicV1anhmolt
andcommitted
tests: ble_hrs: unit tests for ble_hrs
Adding unit tests to increase coverage for ble_hrs library Signed-off-by: Nirmal Krishna <[email protected]> Co-authored-by: Andreas Moltumyr <[email protected]>
1 parent 60df6af commit 7d066e9

File tree

5 files changed

+338
-0
lines changed

5 files changed

+338
-0
lines changed

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
/tests/lib/ble_adv/ @nrfconnect/ncs-bm-test
9393
/tests/lib/bm_storage/ @nrfconnect/ncs-bm
9494
/tests/lib/bm_timer/ @nrfconnect/ncs-bm @nrfconnect/ncs-bm-test
95+
/tests/subsys/bluetooth/services/ble_hrs/ @nrfconnect/ncs-bm-test
9596

9697
# Zephyr module
9798
/zephyr/ @nrfconnect/ncs-co-build-system
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
cmake_minimum_required(VERSION 3.20.0)
8+
9+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
10+
11+
project(unit_test_ble_hrs)
12+
13+
set(SOFTDEVICE_VARIANT "s115")
14+
set(SOFTDEVICE_INCLUDE_DIR "${ZEPHYR_NRF_BM_MODULE_DIR}/components/softdevice/\
15+
${SOFTDEVICE_VARIANT}/${SOFTDEVICE_VARIANT}_API/include")
16+
17+
cmock_handle(${SOFTDEVICE_INCLUDE_DIR}/ble_gatts.h)
18+
19+
target_compile_definitions(app PRIVATE
20+
NRF54L15_XXAA
21+
SVCALL_AS_NORMAL_FUNCTION
22+
SUPPRESS_INLINE_IMPLEMENTATION
23+
CONFIG_NRF_SDH_BLE_TOTAL_LINK_COUNT=1
24+
CONFIG_BLE_HRS_MAX_BUFFERED_RR_INTERVALS=20
25+
CONFIG_NRF_SDH_BLE_GATT_MAX_MTU_SIZE=23
26+
)
27+
28+
target_include_directories(app PRIVATE
29+
${SOFTDEVICE_INCLUDE_DIR}
30+
${ZEPHYR_NRF_BM_MODULE_DIR}/include
31+
${ZEPHYR_HAL_NORDIC_MODULE_DIR}/nrfx/mdk
32+
)
33+
34+
# Generate and add test file
35+
test_runner_generate(src/unity_test.c)
36+
target_sources(app PRIVATE src/unity_test.c)
37+
38+
# Unit under test
39+
target_sources(app PRIVATE ${ZEPHYR_NRF_BM_MODULE_DIR}/subsys/bluetooth/services/ble_hrs/hrs.c)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
CONFIG_UNITY=y
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
#include <unity.h>
7+
8+
#include <errno.h>
9+
#include <stddef.h>
10+
#include <stdint.h>
11+
#include <string.h>
12+
13+
#include <bluetooth/services/ble_hrs.h>
14+
#include <bluetooth/services/uuid.h>
15+
16+
#include "cmock_ble_gatts.h"
17+
18+
void ble_hrs_evt_handler(struct ble_hrs *hrs, const struct ble_hrs_evt *evt)
19+
{
20+
}
21+
22+
uint32_t stub_sd_ble_gatts_hvx(
23+
uint16_t conn_handle, const ble_gatts_hvx_params_t *p_hvx_params, int cmock_num_calls)
24+
{
25+
if (p_hvx_params && p_hvx_params->p_len) {
26+
*(p_hvx_params->p_len) = 0;
27+
}
28+
return NRF_SUCCESS;
29+
}
30+
31+
uint32_t stub_sd_ble_gatts_characteristic_add(
32+
uint16_t service_handle,
33+
const ble_gatts_char_md_t *p_char_md,
34+
const ble_gatts_attr_t *p_attr_char_value,
35+
ble_gatts_char_handles_t *p_handles,
36+
int cmock_num_calls)
37+
{
38+
TEST_ASSERT_NOT_NULL(p_char_md);
39+
TEST_ASSERT_NOT_NULL(p_attr_char_value);
40+
TEST_ASSERT_NOT_NULL(p_handles);
41+
if (p_attr_char_value->p_uuid->uuid == BLE_UUID_HEART_RATE_MEASUREMENT_CHAR) {
42+
TEST_ASSERT_EQUAL(BLE_GATTS_VLOC_STACK, p_char_md->p_cccd_md->vloc);
43+
TEST_ASSERT_TRUE(p_char_md->char_props.notify);
44+
TEST_ASSERT_TRUE(p_attr_char_value->p_attr_md->vlen);
45+
TEST_ASSERT_GREATER_OR_EQUAL(0, p_attr_char_value->init_len);
46+
TEST_ASSERT_TRUE(p_attr_char_value->max_len);
47+
} else if (p_attr_char_value->p_uuid->uuid == BLE_UUID_BODY_SENSOR_LOCATION_CHAR) {
48+
TEST_ASSERT_TRUE(p_char_md->char_props.read);
49+
TEST_ASSERT_EQUAL(sizeof(uint8_t), p_attr_char_value->init_len);
50+
TEST_ASSERT_EQUAL(sizeof(uint8_t), p_attr_char_value->max_len);
51+
TEST_ASSERT_EQUAL(BLE_GATTS_VLOC_STACK, p_attr_char_value->p_attr_md->vloc);
52+
} else {
53+
TEST_FAIL_MESSAGE("Unexpected UUID in characteristic add stub");
54+
}
55+
TEST_ASSERT_EQUAL(BLE_UUID_TYPE_BLE, p_attr_char_value->p_uuid->type);
56+
TEST_ASSERT_NOT_NULL(p_attr_char_value->p_value);
57+
58+
return NRF_SUCCESS;
59+
}
60+
61+
void test_ble_hrs_rr_interval_add(void)
62+
{
63+
struct ble_hrs hrs = {
64+
.evt_handler = ble_hrs_evt_handler,
65+
.service_handle = 0,
66+
.conn_handle = BLE_CONN_HANDLE_INVALID,
67+
.rr_interval_count = 0,
68+
.max_hrm_len = 0,
69+
.is_sensor_contact_supported = false,
70+
.is_sensor_contact_detected = false
71+
};
72+
int err;
73+
74+
/* Initialize the heart rate service. */
75+
memset(&hrs, 0, sizeof(hrs));
76+
77+
/* Add RR interval measurement. */
78+
err = ble_hrs_rr_interval_add(&hrs, 100);
79+
TEST_ASSERT_EQUAL(0, err);
80+
TEST_ASSERT_EQUAL(1, hrs.rr_interval_count);
81+
TEST_ASSERT_EQUAL(100, hrs.rr_interval[0]);
82+
83+
/* Add another RR interval measurement. */
84+
err = ble_hrs_rr_interval_add(&hrs, 200);
85+
TEST_ASSERT_EQUAL(0, err);
86+
TEST_ASSERT_EQUAL(2, hrs.rr_interval_count);
87+
TEST_ASSERT_EQUAL(200, hrs.rr_interval[1]);
88+
89+
/* Add a third RR interval measurement. */
90+
err = ble_hrs_rr_interval_add(&hrs, 300);
91+
TEST_ASSERT_EQUAL(0, err);
92+
TEST_ASSERT_EQUAL(3, hrs.rr_interval_count);
93+
TEST_ASSERT_EQUAL(300, hrs.rr_interval[2]);
94+
}
95+
96+
void test_ble_hrs_rr_interval_add_efault(void)
97+
{
98+
/* Try to use null for hrs struct */
99+
int err = ble_hrs_rr_interval_add(NULL, 0);
100+
101+
TEST_ASSERT_EQUAL(-EFAULT, err);
102+
}
103+
104+
void test_ble_hrs_rr_interval_add_overflow(void)
105+
{
106+
struct ble_hrs hrs = {
107+
.max_hrm_len = CONFIG_BLE_HRS_MAX_BUFFERED_RR_INTERVALS * sizeof(uint16_t) + 1
108+
};
109+
uint16_t rr_interval_second = 0;
110+
int err;
111+
112+
/* Fill the buffer to max */
113+
for (int i = 0; i < CONFIG_BLE_HRS_MAX_BUFFERED_RR_INTERVALS; i++) {
114+
err = ble_hrs_rr_interval_add(&hrs, i + 1);
115+
TEST_ASSERT_EQUAL(0, err);
116+
}
117+
rr_interval_second = hrs.rr_interval[1];
118+
119+
/* Now adding one more should remove the first one and add new to last*/
120+
err = ble_hrs_rr_interval_add(&hrs, 999);
121+
122+
TEST_ASSERT_EQUAL(0, err);
123+
TEST_ASSERT_EQUAL(CONFIG_BLE_HRS_MAX_BUFFERED_RR_INTERVALS, hrs.rr_interval_count);
124+
TEST_ASSERT_EQUAL(999, hrs.rr_interval[CONFIG_BLE_HRS_MAX_BUFFERED_RR_INTERVALS - 1]);
125+
TEST_ASSERT_EQUAL(rr_interval_second, hrs.rr_interval[0]);
126+
}
127+
128+
void test_ble_hrs_rr_interval_buffer_is_full(void)
129+
{
130+
struct ble_hrs hrs = {0};
131+
int err;
132+
133+
/* Check if buffer is empty */
134+
TEST_ASSERT_FALSE(ble_hrs_rr_interval_buffer_is_full(&hrs));
135+
136+
/* Fill the buffer to max */
137+
for (int i = 0; i < CONFIG_BLE_HRS_MAX_BUFFERED_RR_INTERVALS; i++) {
138+
err = ble_hrs_rr_interval_add(&hrs, i + 1);
139+
TEST_ASSERT_EQUAL(0, err);
140+
}
141+
142+
/* Check if buffer is full */
143+
TEST_ASSERT_TRUE(ble_hrs_rr_interval_buffer_is_full(&hrs));
144+
}
145+
146+
void test_ble_hrs_sensor_contact_supported_set(void)
147+
{
148+
struct ble_hrs hrs = {0};
149+
int err;
150+
151+
hrs.conn_handle = BLE_CONN_HANDLE_INVALID;
152+
/* Set sensor contact supported to true */
153+
err = ble_hrs_sensor_contact_supported_set(&hrs, true);
154+
TEST_ASSERT_EQUAL(0, err);
155+
TEST_ASSERT_TRUE(hrs.is_sensor_contact_supported);
156+
}
157+
158+
void test_ble_hrs_sensor_contact_supported_set_eisconn(void)
159+
{
160+
struct ble_hrs hrs = {0};
161+
int err;
162+
163+
/* Try to set sensor contact supported while in connection
164+
* Simulate being in a connection
165+
*/
166+
hrs.conn_handle = 1;
167+
err = ble_hrs_sensor_contact_supported_set(&hrs, true);
168+
TEST_ASSERT_EQUAL(-EISCONN, err);
169+
}
170+
171+
void test_ble_hrs_sensor_contact_supported_set_efault(void)
172+
{
173+
int err;
174+
175+
/* Try to use null for hrs struct */
176+
err = ble_hrs_sensor_contact_supported_set(NULL, false);
177+
TEST_ASSERT_EQUAL(-EFAULT, err);
178+
}
179+
180+
void test_ble_hrs_sensor_contact_detected_update(void)
181+
{
182+
struct ble_hrs hrs = {0};
183+
int err;
184+
185+
/* Update sensor contact detected state */
186+
err = ble_hrs_sensor_contact_detected_update(&hrs, true);
187+
TEST_ASSERT_EQUAL(0, err);
188+
TEST_ASSERT_TRUE(hrs.is_sensor_contact_detected);
189+
190+
/* Try to use null for hrs struct */
191+
err = ble_hrs_sensor_contact_detected_update(NULL, true);
192+
TEST_ASSERT_EQUAL(-EFAULT, err);
193+
}
194+
195+
void test_ble_hrs_body_sensor_location_set(void)
196+
{
197+
struct ble_hrs hrs = {0};
198+
uint8_t body_sensor_location = BLE_HRS_BODY_SENSOR_LOCATION_FINGER;
199+
int err;
200+
201+
__cmock_sd_ble_gatts_value_set_ExpectAndReturn(
202+
hrs.conn_handle, hrs.bsl_handles.value_handle, NULL, NRF_SUCCESS);
203+
__cmock_sd_ble_gatts_value_set_IgnoreArg_p_value();
204+
err = ble_hrs_body_sensor_location_set(&hrs, body_sensor_location);
205+
TEST_ASSERT_EQUAL(0, err);
206+
207+
__cmock_sd_ble_gatts_value_set_ExpectAndReturn(
208+
hrs.conn_handle, hrs.bsl_handles.value_handle, NULL, NRF_ERROR_INVALID_ADDR);
209+
__cmock_sd_ble_gatts_value_set_IgnoreArg_p_value();
210+
err = ble_hrs_body_sensor_location_set(&hrs, body_sensor_location);
211+
TEST_ASSERT_EQUAL(-EINVAL, err);
212+
213+
/* Try to use null for hrs struct */
214+
err = ble_hrs_body_sensor_location_set(NULL, body_sensor_location);
215+
TEST_ASSERT_EQUAL(-EFAULT, err);
216+
}
217+
218+
void test_ble_hrs_heart_rate_measurement_send(void)
219+
{
220+
struct ble_hrs hrs = {
221+
.evt_handler = ble_hrs_evt_handler,
222+
.service_handle = 0,
223+
.conn_handle = BLE_CONN_HANDLE_INVALID,
224+
.rr_interval_count = 2,
225+
.max_hrm_len = 0,
226+
.is_sensor_contact_supported = true,
227+
.is_sensor_contact_detected = false
228+
};
229+
uint16_t heart_rate_measurement = 72;
230+
int err;
231+
232+
err = ble_hrs_heart_rate_measurement_send(NULL, heart_rate_measurement);
233+
TEST_ASSERT_EQUAL(-EFAULT, err);
234+
235+
__cmock_sd_ble_gatts_hvx_IgnoreAndReturn(BLE_ERROR_INVALID_CONN_HANDLE);
236+
err = ble_hrs_heart_rate_measurement_send(&hrs, heart_rate_measurement);
237+
TEST_ASSERT_EQUAL(-ENOTCONN, err);
238+
239+
__cmock_sd_ble_gatts_hvx_ExpectAndReturn(hrs.conn_handle, NULL, NRF_ERROR_INVALID_STATE);
240+
__cmock_sd_ble_gatts_hvx_IgnoreArg_p_hvx_params();
241+
err = ble_hrs_heart_rate_measurement_send(&hrs, heart_rate_measurement);
242+
TEST_ASSERT_EQUAL(-EPIPE, err);
243+
244+
__cmock_sd_ble_gatts_hvx_ExpectAndReturn(hrs.conn_handle, NULL, NRF_ERROR_BUSY);
245+
__cmock_sd_ble_gatts_hvx_IgnoreArg_p_hvx_params();
246+
err = ble_hrs_heart_rate_measurement_send(&hrs, heart_rate_measurement);
247+
TEST_ASSERT_EQUAL(-EINVAL, err);
248+
249+
/* Set up the stub to manipulate p_len */
250+
__cmock_sd_ble_gatts_hvx_Stub(stub_sd_ble_gatts_hvx);
251+
err = ble_hrs_heart_rate_measurement_send(&hrs, heart_rate_measurement);
252+
TEST_ASSERT_EQUAL(-EINVAL, err);
253+
}
254+
255+
void test_ble_hrs_init(void)
256+
{
257+
struct ble_hrs hrs = {0};
258+
struct ble_hrs_config hrs_config = {
259+
.evt_handler = ble_hrs_evt_handler,
260+
.is_sensor_contact_supported = true,
261+
.body_sensor_location = (uint8_t[]){ BLE_HRS_BODY_SENSOR_LOCATION_FINGER }
262+
};
263+
int err;
264+
265+
err = ble_hrs_init(NULL, NULL);
266+
TEST_ASSERT_EQUAL(-EFAULT, err);
267+
268+
__cmock_sd_ble_gatts_service_add_IgnoreAndReturn(NRF_ERROR_INVALID_ADDR);
269+
err = ble_hrs_init(&hrs, &hrs_config);
270+
TEST_ASSERT_EQUAL(-EINVAL, err);
271+
TEST_ASSERT_EQUAL_PTR(hrs_config.evt_handler, hrs.evt_handler);
272+
TEST_ASSERT_EQUAL(BLE_CONN_HANDLE_INVALID, hrs.conn_handle);
273+
TEST_ASSERT_EQUAL(0, hrs.rr_interval_count);
274+
TEST_ASSERT_EQUAL(hrs_config.is_sensor_contact_supported, hrs.is_sensor_contact_supported);
275+
TEST_ASSERT_FALSE(hrs.is_sensor_contact_detected);
276+
277+
__cmock_sd_ble_gatts_service_add_IgnoreAndReturn(NRF_SUCCESS);
278+
__cmock_sd_ble_gatts_characteristic_add_Stub(stub_sd_ble_gatts_characteristic_add);
279+
err = ble_hrs_init(&hrs, &hrs_config);
280+
TEST_ASSERT_EQUAL(0, err);
281+
}
282+
283+
extern int unity_main(void);
284+
285+
int main(void)
286+
{
287+
return unity_main();
288+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
tests:
2+
lib.ble_hrs:
3+
platform_allow: native_sim
4+
tags: unittest

0 commit comments

Comments
 (0)