Skip to content

Commit 6a2277b

Browse files
committed
test: add LR-snode regression for ZW_Abort_SendRequest
`ZW_Abort_SendRequest(uint8_t)` was silently masking LR node IDs to their low byte, so any in-flight `REQ_WAITING` session for a node with id >= 256 is never aborted and instead drains via the `request_timeout` ctimer. The caller (`rd_remove_node`) already passes a `nodeid_t`, so the bug is purely at the parameter-type boundary. Using `#ifdef UNIT_TEST` seam to for sake of simplicity. Relates-to: ZGW-3461 Signed-off-by: Laudin Molina Troconis <laudin.molinatroconis@silabs.com>
1 parent e92d3f7 commit 6a2277b

4 files changed

Lines changed: 161 additions & 0 deletions

File tree

src/transport/ZW_SendRequest.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,3 +192,33 @@ ZW_SendRequest_init()
192192
memb_init(&reqs);
193193
list_init(reqs_list);
194194
}
195+
196+
#ifdef UNIT_TEST
197+
/* Test seams.
198+
* Inject sessions into the file-static reqs_list
199+
* without also mocking the contiki list/memb machinery, so we expose two
200+
* narrow helpers used only by tests. */
201+
void *ZW_SendRequest_inject_waiting(nodeid_t snode,
202+
ZW_SendRequst_Callback_t callback,
203+
void *user)
204+
{
205+
send_request_state_t *s = memb_alloc(&reqs);
206+
if (!s) return NULL;
207+
s->state = REQ_WAITING;
208+
s->param.snode = snode;
209+
s->callback = callback;
210+
s->user = user;
211+
s->timeout = 0;
212+
s->round_trip_start = 0;
213+
list_add(reqs_list, s);
214+
return s;
215+
}
216+
217+
int ZW_SendRequest_pending_count(void)
218+
{
219+
int count = 0;
220+
send_request_state_t *s;
221+
for (s = list_head(reqs_list); s; s = list_item_next(s)) count++;
222+
return count;
223+
}
224+
#endif /* UNIT_TEST */

test/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ add_subdirectory(multicast_tlv)
6363
6464
add_subdirectory(test_multicast_auto)
6565
66+
add_subdirectory(zw_abort_sendrequest)
67+
6668
add_subdirectory(CC_fw_upd)
6769
6870
add_executable(eeprom-printer ${CMAKE_SOURCE_DIR}/test/eeprom-printer.c)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
add_definitions(-DUNIT_TEST)
2+
3+
add_unity_test(NAME test_zw_abort_sendrequest
4+
FILES
5+
${CMAKE_CURRENT_SOURCE_DIR}/test_zw_abort_sendrequest.c
6+
${CMAKE_SOURCE_DIR}/src/transport/ZW_SendRequest.c
7+
${CMAKE_SOURCE_DIR}/contiki/core/lib/list.c
8+
${CMAKE_SOURCE_DIR}/contiki/core/lib/memb.c
9+
)
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/* Copyright Silicon Laboratories Inc.
2+
*
3+
*/
4+
5+
#include <string.h>
6+
#include <stdint.h>
7+
8+
#include "contiki/core/sys/ctimer.h"
9+
#include "contiki/core/sys/clock.h"
10+
11+
#include "unity.h"
12+
13+
#include "ZW_SendRequest.h"
14+
#include "ZW_SendDataAppl.h"
15+
#include "ZW_transport_api.h"
16+
17+
/*
18+
* UNIT_TEST seams provided by ZW_SendRequest.c
19+
*/
20+
void *ZW_SendRequest_inject_waiting(nodeid_t snode,
21+
ZW_SendRequst_Callback_t callback,
22+
void *user);
23+
int ZW_SendRequest_pending_count(void);
24+
25+
void ctimer_set(struct ctimer *c, clock_time_t t, void (*f)(void *), void *ptr)
26+
{ (void)c; (void)t; (void)f; (void)ptr; }
27+
void ctimer_stop(struct ctimer *c) { (void)c; }
28+
clock_time_t clock_time(void) { return 0; }
29+
30+
/* ZW_Abort_SendRequest does not call ZW_SendDataAppl, ts_param_make_reply,
31+
* or ts_param_cmp. The symbols must still resolve at link time because
32+
* ZW_SendRequest() and SendRequest_ApplicationCommandHandler() (in the
33+
* same translation unit) reference them. */
34+
uint8_t ZW_SendDataAppl(ts_param_t *p, const void *pData, uint16_t dataLength,
35+
ZW_SendDataAppl_Callback_t callback, void *user)
36+
{
37+
(void)p; (void)pData; (void)dataLength; (void)callback; (void)user;
38+
return 0;
39+
}
40+
41+
void ts_param_make_reply(ts_param_t *dst, const ts_param_t *src)
42+
{
43+
(void)dst; (void)src;
44+
}
45+
46+
uint8_t ts_param_cmp(ts_param_t *a1, const ts_param_t *a2)
47+
{
48+
(void)a1; (void)a2; return 0;
49+
}
50+
51+
/* ------------------------------------------------------------------ */
52+
/* Test bookkeeping */
53+
/* ------------------------------------------------------------------ */
54+
typedef struct {
55+
int called;
56+
uint8_t status;
57+
void *user;
58+
} cb_record_t;
59+
60+
static int test_callback(BYTE txStatus, BYTE rxStatus,
61+
ZW_APPLICATION_TX_BUFFER *pCmd, WORD cmdLength,
62+
void *user)
63+
{
64+
(void)rxStatus; (void)pCmd; (void)cmdLength;
65+
cb_record_t *r = (cb_record_t *)user;
66+
r->called++;
67+
r->status = txStatus;
68+
r->user = user;
69+
return 0;
70+
}
71+
72+
static cb_record_t cb_lr;
73+
static cb_record_t cb_classic;
74+
75+
void setUp(void)
76+
{
77+
memset(&cb_lr, 0, sizeof(cb_lr));
78+
memset(&cb_classic, 0, sizeof(cb_classic));
79+
ZW_SendRequest_init();
80+
}
81+
82+
void tearDown(void) {}
83+
84+
/*
85+
* LR snode (257) must match an abort issued with the same nodeid_t value.
86+
* Pre-fix: uint8_t param truncates 257 -> 1 -> no match -> no callback,
87+
* list size stays at 2.
88+
* Post-fix: nodeid_t param matches, the LR session's callback fires with
89+
* TRANSMIT_COMPLETE_FAIL, and the list shrinks to 1.
90+
*/
91+
void test_abort_matches_lr_snode_without_truncation(void)
92+
{
93+
nodeid_t lr_node = 257;
94+
nodeid_t classic_node = 42;
95+
96+
void *s_lr = ZW_SendRequest_inject_waiting(lr_node, test_callback, &cb_lr);
97+
void *s_classic = ZW_SendRequest_inject_waiting(classic_node, test_callback, &cb_classic);
98+
99+
TEST_ASSERT_NOT_NULL_MESSAGE(s_lr, "memb_alloc for LR session failed");
100+
101+
TEST_ASSERT_NOT_NULL_MESSAGE(s_classic, "memb_alloc for classic session failed");
102+
103+
TEST_ASSERT_EQUAL_INT_MESSAGE(2, ZW_SendRequest_pending_count(),
104+
"Pre-condition: two sessions queued");
105+
106+
ZW_Abort_SendRequest(lr_node);
107+
108+
TEST_ASSERT_EQUAL_INT_MESSAGE(1, cb_lr.called,
109+
"LR (snode=257) session must be aborted exactly once. "
110+
"Pre-fix: uint8_t truncates 257 -> 1, no match, callback never fires.");
111+
112+
TEST_ASSERT_EQUAL_UINT8_MESSAGE(TRANSMIT_COMPLETE_FAIL, cb_lr.status,
113+
"Aborted session callback must receive TRANSMIT_COMPLETE_FAIL");
114+
115+
TEST_ASSERT_EQUAL_INT_MESSAGE(0, cb_classic.called,
116+
"Classic (snode=42) session must NOT be aborted by ZW_Abort_SendRequest(257)");
117+
118+
TEST_ASSERT_EQUAL_INT_MESSAGE(1, ZW_SendRequest_pending_count(),
119+
"Exactly one session must remain in reqs_list after the LR abort");
120+
}

0 commit comments

Comments
 (0)