Skip to content

Commit a117e08

Browse files
committed
tests: unit: add tests for bm_scheduler
Add unit tests. Signed-off-by: Emanuele Di Santo <emdi@nordicsemi.no>
1 parent 58e70ef commit a117e08

4 files changed

Lines changed: 250 additions & 0 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#
2+
# Copyright (c) 2026 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(bm_scheduler)
12+
13+
# Generate and add test file
14+
test_runner_generate(src/unity_test.c)
15+
target_sources(app PRIVATE src/unity_test.c)
16+
17+
# The unit under test (bm_scheduler.c) is compiled when CONFIG_BM_SCHEDULER=y
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CONFIG_UNITY=y
2+
3+
CONFIG_BM_SCHEDULER=y
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
/*
2+
* Copyright (c) 2026 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
#include <unity.h>
7+
#include <errno.h>
8+
#include <stdint.h>
9+
#include <string.h>
10+
11+
#include <bm/bm_scheduler.h>
12+
13+
#define MAX_RECORDED_CALLS 16
14+
15+
struct call_record {
16+
bm_scheduler_fn_t handler;
17+
size_t len;
18+
uint8_t data[64];
19+
};
20+
21+
static struct call_record call_log[MAX_RECORDED_CALLS];
22+
static size_t call_count;
23+
24+
static void record_call(bm_scheduler_fn_t handler, void *evt, size_t len)
25+
{
26+
if (call_count >= MAX_RECORDED_CALLS) {
27+
return;
28+
}
29+
30+
call_log[call_count].handler = handler;
31+
call_log[call_count].len = len;
32+
if (len > 0 && len <= sizeof(call_log[call_count].data)) {
33+
memcpy(call_log[call_count].data, evt, len);
34+
}
35+
call_count++;
36+
}
37+
38+
static void handler_a(void *evt, size_t len)
39+
{
40+
record_call(handler_a, evt, len);
41+
}
42+
43+
static void handler_b(void *evt, size_t len)
44+
{
45+
record_call(handler_b, evt, len);
46+
}
47+
48+
void test_bm_scheduler_defer_efault(void)
49+
{
50+
int err;
51+
uint8_t data = 0;
52+
53+
err = bm_scheduler_defer(NULL, NULL, 0);
54+
TEST_ASSERT_EQUAL(-EFAULT, err);
55+
56+
err = bm_scheduler_defer(NULL, &data, sizeof(data));
57+
TEST_ASSERT_EQUAL(-EFAULT, err);
58+
}
59+
60+
void test_bm_scheduler_defer_einval(void)
61+
{
62+
int err;
63+
uint8_t data = 0;
64+
65+
/* data != NULL but len == 0 */
66+
err = bm_scheduler_defer(handler_a, &data, 0);
67+
TEST_ASSERT_EQUAL(-EINVAL, err);
68+
69+
/* data == NULL but len != 0 */
70+
err = bm_scheduler_defer(handler_a, NULL, sizeof(data));
71+
TEST_ASSERT_EQUAL(-EINVAL, err);
72+
}
73+
74+
void test_bm_scheduler_defer_enomem(void)
75+
{
76+
int err;
77+
uint8_t big_data[CONFIG_BM_SCHEDULER_BUF_SIZE + 1] = {0};
78+
79+
/* Requesting more than the entire heap is guaranteed to fail. */
80+
err = bm_scheduler_defer(handler_a, big_data, sizeof(big_data));
81+
TEST_ASSERT_EQUAL(-ENOMEM, err);
82+
}
83+
84+
void test_bm_scheduler_defer_no_data(void)
85+
{
86+
int err;
87+
88+
/* NULL data with 0 length is a valid combination. */
89+
err = bm_scheduler_defer(handler_a, NULL, 0);
90+
TEST_ASSERT_EQUAL(0, err);
91+
92+
/* The deferred event must be invoked when processing runs. */
93+
(void)bm_scheduler_process();
94+
95+
TEST_ASSERT_EQUAL(1, call_count);
96+
TEST_ASSERT_EQUAL_PTR(handler_a, call_log[0].handler);
97+
TEST_ASSERT_EQUAL(0, call_log[0].len);
98+
}
99+
100+
void test_bm_scheduler_defer(void)
101+
{
102+
int err;
103+
uint8_t payload[] = {0xDE, 0xAD, 0xBE, 0xEF};
104+
105+
err = bm_scheduler_defer(handler_a, payload, sizeof(payload));
106+
TEST_ASSERT_EQUAL(0, err);
107+
108+
/* Defer must not invoke the handler synchronously. */
109+
TEST_ASSERT_EQUAL(0, call_count);
110+
111+
(void)bm_scheduler_process();
112+
113+
TEST_ASSERT_EQUAL(1, call_count);
114+
TEST_ASSERT_EQUAL_PTR(handler_a, call_log[0].handler);
115+
TEST_ASSERT_EQUAL(sizeof(payload), call_log[0].len);
116+
TEST_ASSERT_EQUAL_MEMORY(payload, call_log[0].data, sizeof(payload));
117+
}
118+
119+
void test_bm_scheduler_process(void)
120+
{
121+
int err;
122+
123+
/* Processing with no deferred events is a no-op that returns success. */
124+
err = bm_scheduler_process();
125+
TEST_ASSERT_EQUAL(0, err);
126+
TEST_ASSERT_EQUAL(0, call_count);
127+
}
128+
129+
void test_bm_scheduler_process_dispatches_in_order(void)
130+
{
131+
int err;
132+
uint8_t payload_a[] = {1, 2, 3};
133+
uint8_t payload_b[] = {0xAA, 0xBB};
134+
135+
/* Defer two events with distinct handlers and payloads, so we can
136+
* verify both that each event is routed to its own handler and that
137+
* events are dispatched in FIFO order.
138+
*/
139+
err = bm_scheduler_defer(handler_a, payload_a, sizeof(payload_a));
140+
TEST_ASSERT_EQUAL(0, err);
141+
142+
err = bm_scheduler_defer(handler_b, payload_b, sizeof(payload_b));
143+
TEST_ASSERT_EQUAL(0, err);
144+
145+
TEST_ASSERT_EQUAL(0, call_count);
146+
147+
err = bm_scheduler_process();
148+
TEST_ASSERT_EQUAL(0, err);
149+
150+
TEST_ASSERT_EQUAL(2, call_count);
151+
152+
TEST_ASSERT_EQUAL_PTR(handler_a, call_log[0].handler);
153+
TEST_ASSERT_EQUAL(sizeof(payload_a), call_log[0].len);
154+
TEST_ASSERT_EQUAL_MEMORY(payload_a, call_log[0].data, sizeof(payload_a));
155+
156+
TEST_ASSERT_EQUAL_PTR(handler_b, call_log[1].handler);
157+
TEST_ASSERT_EQUAL(sizeof(payload_b), call_log[1].len);
158+
TEST_ASSERT_EQUAL_MEMORY(payload_b, call_log[1].data, sizeof(payload_b));
159+
}
160+
161+
void test_bm_scheduler_process_data_is_copied(void)
162+
{
163+
int err;
164+
uint8_t payload[] = {0x55, 0x66, 0x77, 0x88};
165+
166+
err = bm_scheduler_defer(handler_a, payload, sizeof(payload));
167+
TEST_ASSERT_EQUAL(0, err);
168+
169+
/* Mutate the source buffer after deferring. The scheduler must have
170+
* captured a snapshot of the data, so the handler should see the
171+
* original bytes, not the mutated ones.
172+
*/
173+
memset(payload, 0, sizeof(payload));
174+
175+
(void)bm_scheduler_process();
176+
177+
TEST_ASSERT_EQUAL(1, call_count);
178+
TEST_ASSERT_EQUAL(sizeof(payload), call_log[0].len);
179+
180+
const uint8_t expected[] = {0x55, 0x66, 0x77, 0x88};
181+
182+
TEST_ASSERT_EQUAL_MEMORY(expected, call_log[0].data, sizeof(expected));
183+
}
184+
185+
void test_bm_scheduler_process_releases_memory(void)
186+
{
187+
int err;
188+
uint8_t payload[64] = {0};
189+
190+
/* Fill the heap until allocation fails, then drain the queue. After
191+
* processing, the heap must be free again so a new defer succeeds.
192+
*/
193+
do {
194+
err = bm_scheduler_defer(handler_a, payload, sizeof(payload));
195+
} while (err == 0);
196+
TEST_ASSERT_EQUAL(-ENOMEM, err);
197+
198+
err = bm_scheduler_process();
199+
TEST_ASSERT_EQUAL(0, err);
200+
201+
/* The heap must be reusable after processing. */
202+
err = bm_scheduler_defer(handler_a, payload, sizeof(payload));
203+
TEST_ASSERT_EQUAL(0, err);
204+
}
205+
206+
void setUp(void)
207+
{
208+
/* Drain any deferred events that may have leaked from a previous
209+
* test, so that each test starts from a clean state.
210+
*/
211+
(void)bm_scheduler_process();
212+
213+
call_count = 0;
214+
memset(call_log, 0, sizeof(call_log));
215+
}
216+
217+
void tearDown(void)
218+
{
219+
}
220+
221+
extern int unity_main(void);
222+
223+
int main(void)
224+
{
225+
return unity_main();
226+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
tests:
2+
lib.bm_scheduler:
3+
platform_allow: native_sim
4+
tags: unittest

0 commit comments

Comments
 (0)