@@ -41,7 +41,8 @@ static bool eject_stub(udpard_tx_t* const tx, udpard_tx_ejection_t* const ejecti
4141 return true;
4242}
4343
44- static const udpard_tx_vtable_t tx_vtable = { .eject = eject_stub };
44+ static const udpard_tx_vtable_t tx_vtable = { .eject = eject_stub };
45+ static const udpard_tx_vtable_t tx_vtable_null_eject = { .eject = NULL };
4546
4647// RX callback stub used for constructor checks.
4748static void on_message_stub (udpard_rx_t * const rx , udpard_rx_port_t * const port , const udpard_rx_transfer_t transfer )
@@ -51,7 +52,9 @@ static void on_message_stub(udpard_rx_t* const rx, udpard_rx_port_t* const port,
5152 (void )transfer ;
5253}
5354
54- static const udpard_rx_port_vtable_t rx_vtable = { .on_message = on_message_stub };
55+ static const udpard_rx_port_vtable_t rx_vtable = { .on_message = on_message_stub };
56+ static const udpard_rx_port_vtable_t rx_vtable_null_cb = { .on_message = NULL };
57+ static const udpard_deleter_vtable_t del_vtable_null_free = { .free = NULL };
5558
5659static void test_misc_guards (void )
5760{
@@ -82,6 +85,16 @@ static void test_tx_new_guards(void)
8285 TEST_ASSERT_FALSE (udpard_tx_new (NULL , 1U , 1U , 1U , mem_ok , & tx_vtable ));
8386 TEST_ASSERT_FALSE (udpard_tx_new (& tx , 0U , 1U , 1U , mem_ok , & tx_vtable ));
8487 TEST_ASSERT_FALSE (udpard_tx_new (& tx , 1U , 1U , 1U , mem_ok , NULL ));
88+ TEST_ASSERT_FALSE (udpard_tx_new (& tx , 1U , 1U , 1U , mem_ok , & tx_vtable_null_eject ));
89+
90+ // Reject invalid payload memory resources.
91+ const udpard_mem_vtable_t bad_alloc = { .base = { .free = free_heap }, .alloc = NULL };
92+ const udpard_tx_mem_resources_t mem_bad_payload = {
93+ .transfer = make_mem (transfer_pool ),
94+ .payload = { make_mem (payload_pool ), { .vtable = & bad_alloc , .context = NULL }, make_mem (payload_pool ) },
95+ };
96+ TEST_ASSERT_FALSE (udpard_tx_new (& tx , 1U , 1U , 1U , mem_bad_payload , & tx_vtable ));
97+
8598 TEST_ASSERT_TRUE (udpard_tx_new (& tx , 1U , 1U , 4U , mem_ok , & tx_vtable ));
8699 udpard_tx_free (& tx );
87100}
@@ -99,15 +112,27 @@ static void test_tx_push_guards(void)
99112 TEST_ASSERT_TRUE (udpard_tx_new (& tx , 1U , 1U , 4U , mem_ok , & tx_vtable ));
100113
101114 // Validate argument checks for subject push.
102- const udpard_bytes_scattered_t empty_payload = make_scattered ("" , 0U );
115+ const udpard_bytes_scattered_t empty_payload = make_scattered ("" , 0U );
116+ const udpard_bytes_scattered_t empty_payload_null = { .bytes = { .size = 0U , .data = NULL }, .next = NULL };
117+ const udpard_bytes_scattered_t invalid_payload_ptr = { .bytes = { .size = 1U , .data = NULL }, .next = NULL };
103118 TEST_ASSERT_FALSE (
104119 udpard_tx_push (NULL , 0 , 1 , 1U , udpard_prio_fast , 1U , udpard_make_subject_endpoint (1U ), empty_payload , NULL ));
105120 TEST_ASSERT_FALSE (
106121 udpard_tx_push (& tx , 2 , 1 , 1U , udpard_prio_fast , 1U , udpard_make_subject_endpoint (1U ), empty_payload , NULL ));
122+ TEST_ASSERT_FALSE (
123+ udpard_tx_push (& tx , -1 , 1 , 1U , udpard_prio_fast , 1U , udpard_make_subject_endpoint (1U ), empty_payload , NULL ));
124+ tx .local_uid = 0U ;
125+ TEST_ASSERT_FALSE (
126+ udpard_tx_push (& tx , 0 , 1 , 1U , udpard_prio_fast , 1U , udpard_make_subject_endpoint (1U ), empty_payload , NULL ));
127+ tx .local_uid = 1U ;
107128 TEST_ASSERT_FALSE (
108129 udpard_tx_push (& tx , 0 , 1 , 0U , udpard_prio_fast , 1U , udpard_make_subject_endpoint (1U ), empty_payload , NULL ));
109130 TEST_ASSERT_FALSE (udpard_tx_push (
110131 & tx , 0 , 1 , 1U , udpard_prio_fast , 1U , (udpard_udpip_ep_t ){ .ip = 0U , .port = 0U }, empty_payload , NULL ));
132+ TEST_ASSERT_FALSE (
133+ udpard_tx_push (& tx , 0 , 1 , 1U , udpard_prio_fast , 1U , udpard_make_subject_endpoint (1U ), invalid_payload_ptr , NULL ));
134+ TEST_ASSERT_TRUE (
135+ udpard_tx_push (& tx , 0 , 1 , 1U , udpard_prio_fast , 1U , udpard_make_subject_endpoint (1U ), empty_payload_null , NULL ));
111136 TEST_ASSERT_TRUE (
112137 udpard_tx_push (& tx , 0 , 1 , 1U , udpard_prio_fast , 1U , udpard_make_subject_endpoint (1U ), empty_payload , NULL ));
113138 udpard_tx_free (& tx );
@@ -126,17 +151,85 @@ static void test_tx_push_unicast_guards(void)
126151 TEST_ASSERT_TRUE (udpard_tx_new (& tx , 2U , 2U , 4U , mem_ok , & tx_vtable ));
127152
128153 // Validate argument checks for unicast push.
129- const udpard_bytes_scattered_t empty_payload = make_scattered ("" , 0U );
154+ const udpard_bytes_scattered_t empty_payload = make_scattered ("" , 0U );
155+ const udpard_bytes_scattered_t empty_payload_null = { .bytes = { .size = 0U , .data = NULL }, .next = NULL };
156+ const udpard_bytes_scattered_t invalid_payload_ptr = { .bytes = { .size = 1U , .data = NULL }, .next = NULL };
130157 udpard_udpip_ep_t endpoints [UDPARD_IFACE_COUNT_MAX ] = { 0 };
131158 endpoints [0 ] = (udpard_udpip_ep_t ){ .ip = 0x0A000001U , .port = 9000U };
132159 TEST_ASSERT_FALSE (udpard_tx_push_unicast (NULL , 0 , 1 , udpard_prio_nominal , endpoints , empty_payload , NULL ));
133160 TEST_ASSERT_FALSE (udpard_tx_push_unicast (& tx , 2 , 1 , udpard_prio_nominal , endpoints , empty_payload , NULL ));
161+ TEST_ASSERT_FALSE (udpard_tx_push_unicast (& tx , -1 , 1 , udpard_prio_nominal , endpoints , empty_payload , NULL ));
162+ tx .local_uid = 0U ;
163+ TEST_ASSERT_FALSE (udpard_tx_push_unicast (& tx , 0 , 1 , udpard_prio_nominal , endpoints , empty_payload , NULL ));
164+ tx .local_uid = 2U ;
165+ TEST_ASSERT_FALSE (udpard_tx_push_unicast (& tx , 0 , 1 , udpard_prio_nominal , endpoints , invalid_payload_ptr , NULL ));
166+ TEST_ASSERT_TRUE (udpard_tx_push_unicast (& tx , 0 , 1 , udpard_prio_nominal , endpoints , empty_payload_null , NULL ));
134167 TEST_ASSERT_TRUE (udpard_tx_push_unicast (& tx , 0 , 1 , udpard_prio_nominal , endpoints , empty_payload , NULL ));
135168 endpoints [0 ] = (udpard_udpip_ep_t ){ .ip = 0U , .port = 0U };
136169 TEST_ASSERT_FALSE (udpard_tx_push_unicast (& tx , 0 , 1 , udpard_prio_nominal , endpoints , empty_payload , NULL ));
137170 udpard_tx_free (& tx );
138171}
139172
173+ static void test_tx_poll_and_free_guards (void )
174+ {
175+ // Validate no-op guard behavior for poll/free helpers.
176+ static byte_t transfer_pool [1024 ];
177+ static byte_t payload_pool [1024 ];
178+ const udpard_tx_mem_resources_t mem_ok = {
179+ .transfer = make_mem (transfer_pool ),
180+ .payload = { make_mem (payload_pool ), make_mem (payload_pool ), make_mem (payload_pool ) },
181+ };
182+ udpard_tx_t tx = { 0 };
183+ TEST_ASSERT_TRUE (udpard_tx_new (& tx , 10U , 11U , 4U , mem_ok , & tx_vtable ));
184+ udpard_tx_poll (NULL , 0 , UDPARD_IFACE_BITMAP_ALL );
185+ udpard_tx_poll (& tx , -1 , UDPARD_IFACE_BITMAP_ALL );
186+ TEST_ASSERT_EQUAL_UINT16 (0U , udpard_tx_pending_ifaces (NULL ));
187+ udpard_tx_free (& tx );
188+ udpard_tx_free (NULL );
189+ }
190+
191+ static void test_rx_port_ctor_guards (void )
192+ {
193+ // Reject invalid memory resources and callback descriptors.
194+ static byte_t session_pool [1024 ];
195+ static byte_t fragment_pool [1024 ];
196+ const udpard_rx_mem_resources_t rx_mem_ok = {
197+ .session = make_mem (session_pool ),
198+ .slot = make_mem (session_pool ),
199+ .fragment = make_mem (fragment_pool ),
200+ };
201+ const udpard_mem_vtable_t bad_alloc = { .base = { .free = free_heap }, .alloc = NULL };
202+ const udpard_mem_vtable_t bad_free = { .base = { .free = NULL }, .alloc = alloc_heap };
203+ const udpard_rx_mem_resources_t rx_mem_bad_session = {
204+ .session = { .vtable = & bad_alloc , .context = NULL },
205+ .slot = make_mem (session_pool ),
206+ .fragment = make_mem (fragment_pool ),
207+ };
208+ const udpard_rx_mem_resources_t rx_mem_bad_slot = {
209+ .session = make_mem (session_pool ),
210+ .slot = { .vtable = & bad_alloc , .context = NULL },
211+ .fragment = make_mem (fragment_pool ),
212+ };
213+ const udpard_rx_mem_resources_t rx_mem_bad_fragment = {
214+ .session = make_mem (session_pool ),
215+ .slot = make_mem (session_pool ),
216+ .fragment = { .vtable = & bad_free , .context = NULL },
217+ };
218+
219+ udpard_rx_port_t port = { 0 };
220+ TEST_ASSERT_FALSE (udpard_rx_port_new (NULL , 1U , rx_mem_ok , & rx_vtable ));
221+ TEST_ASSERT_FALSE (udpard_rx_port_new (& port , 1U , rx_mem_bad_session , & rx_vtable ));
222+ TEST_ASSERT_FALSE (udpard_rx_port_new (& port , 1U , rx_mem_bad_slot , & rx_vtable ));
223+ TEST_ASSERT_FALSE (udpard_rx_port_new (& port , 1U , rx_mem_bad_fragment , & rx_vtable ));
224+ TEST_ASSERT_FALSE (udpard_rx_port_new (& port , 1U , rx_mem_ok , NULL ));
225+ TEST_ASSERT_FALSE (udpard_rx_port_new (& port , 1U , rx_mem_ok , & rx_vtable_null_cb ));
226+ TEST_ASSERT_TRUE (udpard_rx_port_new (& port , 1U , rx_mem_ok , & rx_vtable ));
227+
228+ // Wrapper constructors should fail consistently when base validation fails.
229+ TEST_ASSERT_FALSE (udpard_rx_port_new_stateless (& port , 1U , rx_mem_bad_session , & rx_vtable ));
230+ TEST_ASSERT_FALSE (udpard_rx_port_new_unicast (& port , 1U , rx_mem_bad_session , & rx_vtable ));
231+ }
232+
140233static void test_rx_port_push_guards (void )
141234{
142235 // Prepare RX and port.
@@ -163,9 +256,14 @@ static void test_rx_port_push_guards(void)
163256 (void )header_serialize (datagram , meta , 0U , crc_full (0U , NULL ));
164257 const udpard_bytes_mut_t payload = { .size = sizeof (datagram ), .data = datagram };
165258 const udpard_deleter_t del = { .vtable = & del_vtable , .context = NULL };
259+ const udpard_deleter_t del_bad = { .vtable = & del_vtable_null_free , .context = NULL };
166260 TEST_ASSERT_FALSE (
167261 udpard_rx_port_push (NULL , & port , 0 , (udpard_udpip_ep_t ){ .ip = 1U , .port = 1U }, payload , del , 0U ));
168262 TEST_ASSERT_FALSE (udpard_rx_port_push (& rx , NULL , 0 , (udpard_udpip_ep_t ){ .ip = 1U , .port = 1U }, payload , del , 0U ));
263+ TEST_ASSERT_FALSE (
264+ udpard_rx_port_push (& rx , & port , -1 , (udpard_udpip_ep_t ){ .ip = 1U , .port = 1U }, payload , del , 0U ));
265+ TEST_ASSERT_FALSE (
266+ udpard_rx_port_push (& rx , & port , 0 , (udpard_udpip_ep_t ){ .ip = 0U , .port = 1U }, payload , del , 0U ));
169267 TEST_ASSERT_FALSE (
170268 udpard_rx_port_push (& rx , & port , 0 , (udpard_udpip_ep_t ){ .ip = 1U , .port = 1U }, payload , del , 99U ));
171269 TEST_ASSERT_FALSE (udpard_rx_port_push (& rx ,
@@ -175,8 +273,15 @@ static void test_rx_port_push_guards(void)
175273 (udpard_bytes_mut_t ){ .size = 1U , .data = NULL },
176274 del ,
177275 0U ));
276+ TEST_ASSERT_FALSE (udpard_rx_port_push (
277+ & rx , & port , 0 , (udpard_udpip_ep_t ){ .ip = 1U , .port = 1U }, payload , (udpard_deleter_t ){ 0 }, 0U ));
278+ TEST_ASSERT_FALSE (
279+ udpard_rx_port_push (& rx , & port , 0 , (udpard_udpip_ep_t ){ .ip = 1U , .port = 1U }, payload , del_bad , 0U ));
178280 TEST_ASSERT_TRUE (udpard_rx_port_push (& rx , & port , 0 , (udpard_udpip_ep_t ){ .ip = 1U , .port = 1U }, payload , del , 0U ));
179281
282+ // Free should ignore null arguments.
283+ udpard_rx_port_free (NULL , & port );
284+ udpard_rx_port_free (& rx , NULL );
180285 udpard_rx_port_free (& rx , & port );
181286}
182287
@@ -190,6 +295,8 @@ int main(void)
190295 RUN_TEST (test_tx_new_guards );
191296 RUN_TEST (test_tx_push_guards );
192297 RUN_TEST (test_tx_push_unicast_guards );
298+ RUN_TEST (test_tx_poll_and_free_guards );
299+ RUN_TEST (test_rx_port_ctor_guards );
193300 RUN_TEST (test_rx_port_push_guards );
194301 return UNITY_END ();
195302}
0 commit comments