@@ -199,7 +199,7 @@ void test_out_of_order_multiframe_reassembly()
199199 TEST_ASSERT_TRUE (udpard_rx_port_new (&port, 4096U , rx_mem, &rx_vtable));
200200
201201 // Send a payload that spans multiple frames.
202- std::vector<std::uint8_t > payload (280U );
202+ std::vector<std::uint8_t > payload (600U );
203203 for (std::size_t i = 0 ; i < payload.size (); i++) {
204204 payload[i] = static_cast <std::uint8_t >(i ^ 0x5AU );
205205 }
@@ -214,7 +214,7 @@ void test_out_of_order_multiframe_reassembly()
214214 make_scattered (payload.data (), payload.size ()),
215215 nullptr ));
216216 udpard_tx_poll (&tx, 1001 , UDPARD_IFACE_BITMAP_ALL);
217- TEST_ASSERT_TRUE (! frames.empty () );
217+ TEST_ASSERT_TRUE (frames.size () > 1U );
218218
219219 // Deliver frames in reverse order to exercise out-of-order reassembly.
220220 std::reverse (frames.begin (), frames.end ());
@@ -243,6 +243,155 @@ void test_out_of_order_multiframe_reassembly()
243243 instrumented_allocator_reset (&rx_alloc_fragment);
244244}
245245
246+ void test_stateless_single_frame_acceptance ()
247+ {
248+ seed_prng ();
249+
250+ // Configure TX and RX.
251+ instrumented_allocator_t tx_alloc_transfer{};
252+ instrumented_allocator_t tx_alloc_payload{};
253+ instrumented_allocator_t rx_alloc_session{};
254+ instrumented_allocator_t rx_alloc_fragment{};
255+ instrumented_allocator_new (&tx_alloc_transfer);
256+ instrumented_allocator_new (&tx_alloc_payload);
257+ instrumented_allocator_new (&rx_alloc_session);
258+ instrumented_allocator_new (&rx_alloc_fragment);
259+
260+ udpard_tx_t tx{};
261+ std::vector<CapturedFrame> frames;
262+ TEST_ASSERT_TRUE (udpard_tx_new (
263+ &tx, 0x1234123412341234ULL , 777U , 8U , make_tx_mem (tx_alloc_transfer, tx_alloc_payload), &tx_vtable));
264+ tx.mtu [0 ] = 128U ;
265+ tx.mtu [1 ] = 128U ;
266+ tx.mtu [2 ] = 128U ;
267+ tx.user = &frames;
268+
269+ const auto rx_mem = make_rx_mem (rx_alloc_session, rx_alloc_fragment);
270+ const udpard_deleter_t del = instrumented_allocator_make_deleter (&rx_alloc_fragment);
271+ udpard_rx_t rx{};
272+ udpard_rx_port_t port{};
273+ RxState state{};
274+ udpard_rx_new (&rx);
275+ rx.user = &state;
276+ TEST_ASSERT_TRUE (udpard_rx_port_new_stateless (&port, 1U , rx_mem, &rx_vtable));
277+
278+ // Send and deliver one single-frame transfer.
279+ const std::vector<std::uint8_t > payload{ 0x10U , 0x20U , 0x30U , 0x40U };
280+ TEST_ASSERT_TRUE (udpard_tx_push (&tx,
281+ 100U ,
282+ 10000U ,
283+ 1U ,
284+ udpard_prio_nominal,
285+ 88U ,
286+ udpard_make_subject_endpoint (66U ),
287+ make_scattered (payload.data (), payload.size ()),
288+ nullptr ));
289+ udpard_tx_poll (&tx, 101U , UDPARD_IFACE_BITMAP_ALL);
290+ TEST_ASSERT_EQUAL_size_t (1U , frames.size ());
291+
292+ deliver (frames.front (), rx_mem.fragment , del, &rx, &port, 200U );
293+ udpard_rx_poll (&rx, 201U );
294+ TEST_ASSERT_EQUAL_size_t (1U , state.count );
295+ TEST_ASSERT_EQUAL_size_t (payload.size (), state.payload .size ());
296+ TEST_ASSERT_EQUAL_MEMORY (payload.data (), state.payload .data (), payload.size ());
297+ TEST_ASSERT_EQUAL_UINT64 (0U , rx.errors_transfer_malformed );
298+
299+ // Release all resources.
300+ udpard_rx_port_free (&rx, &port);
301+ udpard_tx_free (&tx);
302+ TEST_ASSERT_EQUAL_size_t (0U , tx_alloc_transfer.allocated_fragments );
303+ TEST_ASSERT_EQUAL_size_t (0U , tx_alloc_payload.allocated_fragments );
304+ TEST_ASSERT_EQUAL_size_t (0U , rx_alloc_session.allocated_fragments );
305+ TEST_ASSERT_EQUAL_size_t (0U , rx_alloc_fragment.allocated_fragments );
306+ instrumented_allocator_reset (&tx_alloc_transfer);
307+ instrumented_allocator_reset (&tx_alloc_payload);
308+ instrumented_allocator_reset (&rx_alloc_session);
309+ instrumented_allocator_reset (&rx_alloc_fragment);
310+ }
311+
312+ void test_stateless_multiframe_first_frame_handling (const std::size_t extent, const bool expect_accept)
313+ {
314+ seed_prng ();
315+
316+ // Configure TX and RX.
317+ instrumented_allocator_t tx_alloc_transfer{};
318+ instrumented_allocator_t tx_alloc_payload{};
319+ instrumented_allocator_t rx_alloc_session{};
320+ instrumented_allocator_t rx_alloc_fragment{};
321+ instrumented_allocator_new (&tx_alloc_transfer);
322+ instrumented_allocator_new (&tx_alloc_payload);
323+ instrumented_allocator_new (&rx_alloc_session);
324+ instrumented_allocator_new (&rx_alloc_fragment);
325+
326+ udpard_tx_t tx{};
327+ std::vector<CapturedFrame> frames;
328+ TEST_ASSERT_TRUE (udpard_tx_new (
329+ &tx, 0x5555666677778888ULL , 999U , 16U , make_tx_mem (tx_alloc_transfer, tx_alloc_payload), &tx_vtable));
330+ tx.mtu [0 ] = 128U ;
331+ tx.mtu [1 ] = 128U ;
332+ tx.mtu [2 ] = 128U ;
333+ tx.user = &frames;
334+
335+ const auto rx_mem = make_rx_mem (rx_alloc_session, rx_alloc_fragment);
336+ const udpard_deleter_t del = instrumented_allocator_make_deleter (&rx_alloc_fragment);
337+ udpard_rx_t rx{};
338+ udpard_rx_port_t port{};
339+ RxState state{};
340+ udpard_rx_new (&rx);
341+ rx.user = &state;
342+ TEST_ASSERT_TRUE (udpard_rx_port_new_stateless (&port, extent, rx_mem, &rx_vtable));
343+
344+ // Emit a transfer that is guaranteed to span multiple frames.
345+ std::vector<std::uint8_t > payload (600U );
346+ for (std::size_t i = 0 ; i < payload.size (); i++) {
347+ payload[i] = static_cast <std::uint8_t >(i);
348+ }
349+ TEST_ASSERT_TRUE (udpard_tx_push (&tx,
350+ 1000U ,
351+ 100000U ,
352+ 1U ,
353+ udpard_prio_nominal,
354+ 99U ,
355+ udpard_make_subject_endpoint (67U ),
356+ make_scattered (payload.data (), payload.size ()),
357+ nullptr ));
358+ udpard_tx_poll (&tx, 1001U , UDPARD_IFACE_BITMAP_ALL);
359+ TEST_ASSERT_TRUE (frames.size () > 1U );
360+
361+ // Deliver only the first frame. Stateless mode may accept it if the configured extent is already covered.
362+ deliver (frames.front (), rx_mem.fragment , del, &rx, &port, 2000U );
363+ udpard_rx_poll (&rx, 2001U );
364+ if (expect_accept) {
365+ TEST_ASSERT_EQUAL_size_t (1U , state.count );
366+ TEST_ASSERT_EQUAL_UINT64 (0U , rx.errors_transfer_malformed );
367+ TEST_ASSERT_EQUAL_size_t (payload.size (), state.payload_size_wire );
368+ TEST_ASSERT_GREATER_OR_EQUAL_size_t (std::min (extent, payload.size ()), state.payload .size ());
369+ TEST_ASSERT_LESS_THAN_size_t (payload.size (), state.payload .size ());
370+ TEST_ASSERT_EQUAL_MEMORY (payload.data (), state.payload .data (), state.payload .size ());
371+ } else {
372+ TEST_ASSERT_EQUAL_size_t (0U , state.count );
373+ TEST_ASSERT_EQUAL_UINT64 (1U , rx.errors_transfer_malformed );
374+ }
375+
376+ // Release all resources.
377+ udpard_rx_port_free (&rx, &port);
378+ udpard_tx_free (&tx);
379+ TEST_ASSERT_EQUAL_size_t (0U , tx_alloc_transfer.allocated_fragments );
380+ TEST_ASSERT_EQUAL_size_t (0U , tx_alloc_payload.allocated_fragments );
381+ TEST_ASSERT_EQUAL_size_t (0U , rx_alloc_session.allocated_fragments );
382+ TEST_ASSERT_EQUAL_size_t (0U , rx_alloc_fragment.allocated_fragments );
383+ instrumented_allocator_reset (&tx_alloc_transfer);
384+ instrumented_allocator_reset (&tx_alloc_payload);
385+ instrumented_allocator_reset (&rx_alloc_session);
386+ instrumented_allocator_reset (&rx_alloc_fragment);
387+ }
388+
389+ void test_stateless_multiframe_truncation_small_extent () { test_stateless_multiframe_first_frame_handling (10U , true ); }
390+
391+ void test_stateless_multiframe_truncation_zero_extent () { test_stateless_multiframe_first_frame_handling (0U , true ); }
392+
393+ void test_stateless_multiframe_rejection_large_extent () { test_stateless_multiframe_first_frame_handling (600U , false ); }
394+
246395} // namespace
247396
248397void setUp () {}
@@ -253,5 +402,9 @@ int main()
253402 UNITY_BEGIN ();
254403 RUN_TEST (test_zero_payload_transfer);
255404 RUN_TEST (test_out_of_order_multiframe_reassembly);
405+ RUN_TEST (test_stateless_single_frame_acceptance);
406+ RUN_TEST (test_stateless_multiframe_truncation_small_extent);
407+ RUN_TEST (test_stateless_multiframe_truncation_zero_extent);
408+ RUN_TEST (test_stateless_multiframe_rejection_large_extent);
256409 return UNITY_END ();
257410}
0 commit comments