@@ -748,67 +748,72 @@ static rx_slot_t* make_test_slot(const test_rx_context_t* const ctx,
748748}
749749
750750// =====================================================================================================================
751- // Test 19: rx_session_scan — purges stale slots and returns latest fresh timestamp .
751+ // Test 19: rx_session_scan — purges stale slots and returns count of in-progress slots remaining .
752752static void test_rx_session_scan (void )
753753{
754- // tid_timeout=1000, so deadline = now - 1000 = 5000 - 1000 = 4000 for all sub-cases.
754+ // tid_timeout=40*MEGA > RX_SESSION_TIMEOUT=30*MEGA, so deadline = now - tid_timeout.
755+ // With now = 40*MEGA + 4000: deadline = 4000. Stale if timestamp < 4000.
755756 test_rx_context_t ctx ;
756757 rx_session_t ses ;
757758
758- // 1) All slots NULL → BIG_BANG, no allocator activity .
759+ // 1) All slots NULL → return 0, ses.timestamp unchanged .
759760 {
760- test_rx_context_init (& ctx , 64 , 1000 );
761+ test_rx_context_init (& ctx , 64 , 40 * MEGA );
761762 memset (& ses , 0 , sizeof (ses ));
762763 ses .owner = & ctx .sub ;
763- TEST_ASSERT_EQUAL_INT64 (BIG_BANG , rx_session_scan (& ses , 5000 ));
764+ TEST_ASSERT_EQUAL_size_t (0 , rx_session_scan (& ses , 40 * MEGA + 4000 ));
765+ TEST_ASSERT_EQUAL_INT64 (0 , ses .timestamp );
764766 TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_slot .allocated_fragments );
765767 TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_payload .allocated_fragments );
766768 }
767- // 2) All slots fresh → returns max timestamp, no frees .
769+ // 2) All fresh, total_payload_size=0 → return 0 (no in-progress), ses.timestamp updated .
768770 {
769- test_rx_context_init (& ctx , 64 , 1000 );
771+ test_rx_context_init (& ctx , 64 , 40 * MEGA );
770772 memset (& ses , 0 , sizeof (ses ));
771773 ses .owner = & ctx .sub ;
772774 ses .slots [0 ] = make_test_slot (& ctx , 4500 , 0 , true);
773775 ses .slots [3 ] = make_test_slot (& ctx , 4800 , 0 , true);
774776 ses .slots [7 ] = make_test_slot (& ctx , 4001 , 0 , true);
775- TEST_ASSERT_EQUAL_INT64 (4800 , rx_session_scan (& ses , 5000 ));
777+ TEST_ASSERT_EQUAL_size_t (0 , rx_session_scan (& ses , 40 * MEGA + 4000 ));
778+ TEST_ASSERT_EQUAL_INT64 (4800 , ses .timestamp );
776779 TEST_ASSERT_EQUAL_UINT64 (0 , ctx .alloc_slot .count_free );
777- // Cleanup remaining fresh slots.
778780 rx_slot_destroy (& ctx .sub , ses .slots [0 ]);
779781 rx_slot_destroy (& ctx .sub , ses .slots [3 ]);
780782 rx_slot_destroy (& ctx .sub , ses .slots [7 ]);
781783 TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_slot .allocated_fragments );
782784 }
783- // 3) All slots stale → BIG_BANG , both destroyed.
785+ // 3) All stale → return 0 , both destroyed, ses.timestamp unchanged (no fresh slots) .
784786 {
785- test_rx_context_init (& ctx , 64 , 1000 );
787+ test_rx_context_init (& ctx , 64 , 40 * MEGA );
786788 memset (& ses , 0 , sizeof (ses ));
787789 ses .owner = & ctx .sub ;
788790 ses .slots [1 ] = make_test_slot (& ctx , 3999 , 0 , true);
789791 ses .slots [5 ] = make_test_slot (& ctx , 2000 , 0 , true);
790- TEST_ASSERT_EQUAL_INT64 ( BIG_BANG , rx_session_scan (& ses , 5000 ));
792+ TEST_ASSERT_EQUAL_size_t ( 0 , rx_session_scan (& ses , 40 * MEGA + 4000 ));
791793 TEST_ASSERT_NULL (ses .slots [1 ]);
792794 TEST_ASSERT_NULL (ses .slots [5 ]);
795+ TEST_ASSERT_EQUAL_INT64 (0 , ses .timestamp );
793796 TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_slot .allocated_fragments );
794797 TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_slot .allocated_bytes );
795798 }
796- // 4) Mix of fresh, stale, NULL.
799+ // 4) Mix of fresh (with in-progress) , stale, NULL.
797800 {
798- test_rx_context_init (& ctx , 64 , 1000 );
801+ test_rx_context_init (& ctx , 64 , 40 * MEGA );
799802 memset (& ses , 0 , sizeof (ses ));
800803 ses .owner = & ctx .sub ;
801- ses .slots [0 ] = make_test_slot (& ctx , 3999 , 0 , true); // stale
802- ses .slots [2 ] = make_test_slot (& ctx , 1000 , 0 , true); // stale
803- ses .slots [1 ] = make_test_slot (& ctx , 4500 , 0 , true); // fresh
804- ses .slots [4 ] = make_test_slot (& ctx , 4200 , 0 , true); // fresh
805- ses .slots [6 ] = make_test_slot (& ctx , 4000 , 0 , true); // exactly at deadline, NOT stale
806- TEST_ASSERT_EQUAL_INT64 (4500 , rx_session_scan (& ses , 5000 ));
804+ ses .slots [0 ] = make_test_slot (& ctx , 3999 , 0 , true); // stale
805+ ses .slots [2 ] = make_test_slot (& ctx , 1000 , 0 , true); // stale
806+ ses .slots [1 ] = make_test_slot (& ctx , 4500 , 0 , true); // fresh, in-progress
807+ ses .slots [1 ]-> total_payload_size = 10 ;
808+ ses .slots [4 ] = make_test_slot (& ctx , 4200 , 0 , true); // fresh, idle
809+ ses .slots [6 ] = make_test_slot (& ctx , 4000 , 0 , true); // boundary, NOT stale
810+ TEST_ASSERT_EQUAL_size_t (1 , rx_session_scan (& ses , 40 * MEGA + 4000 ));
807811 TEST_ASSERT_NULL (ses .slots [0 ]);
808812 TEST_ASSERT_NULL (ses .slots [2 ]);
809813 TEST_ASSERT_NOT_NULL (ses .slots [1 ]);
810814 TEST_ASSERT_NOT_NULL (ses .slots [4 ]);
811815 TEST_ASSERT_NOT_NULL (ses .slots [6 ]);
816+ TEST_ASSERT_EQUAL_INT64 (4500 , ses .timestamp );
812817 rx_slot_destroy (& ctx .sub , ses .slots [1 ]);
813818 rx_slot_destroy (& ctx .sub , ses .slots [4 ]);
814819 rx_slot_destroy (& ctx .sub , ses .slots [6 ]);
@@ -817,28 +822,30 @@ static void test_rx_session_scan(void)
817822 }
818823 // 5) Boundary: timestamp == deadline (4000). Condition is strict '<', so NOT stale.
819824 {
820- test_rx_context_init (& ctx , 64 , 1000 );
825+ test_rx_context_init (& ctx , 64 , 40 * MEGA );
821826 memset (& ses , 0 , sizeof (ses ));
822827 ses .owner = & ctx .sub ;
823828 ses .slots [0 ] = make_test_slot (& ctx , 4000 , 0 , true);
824- TEST_ASSERT_EQUAL_INT64 ( 4000 , rx_session_scan (& ses , 5000 ));
829+ TEST_ASSERT_EQUAL_size_t ( 0 , rx_session_scan (& ses , 40 * MEGA + 4000 ));
825830 TEST_ASSERT_NOT_NULL (ses .slots [0 ]);
831+ TEST_ASSERT_EQUAL_INT64 (4000 , ses .timestamp );
826832 rx_slot_destroy (& ctx .sub , ses .slots [0 ]);
827833 TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_slot .allocated_fragments );
828834 }
829835 // 6) Boundary: timestamp == deadline - 1 (3999). Stale and destroyed.
830836 {
831- test_rx_context_init (& ctx , 64 , 1000 );
837+ test_rx_context_init (& ctx , 64 , 40 * MEGA );
832838 memset (& ses , 0 , sizeof (ses ));
833839 ses .owner = & ctx .sub ;
834840 ses .slots [0 ] = make_test_slot (& ctx , 3999 , 0 , true);
835- TEST_ASSERT_EQUAL_INT64 ( BIG_BANG , rx_session_scan (& ses , 5000 ));
841+ TEST_ASSERT_EQUAL_size_t ( 0 , rx_session_scan (& ses , 40 * MEGA + 4000 ));
836842 TEST_ASSERT_NULL (ses .slots [0 ]);
843+ TEST_ASSERT_EQUAL_INT64 (0 , ses .timestamp );
837844 TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_slot .allocated_fragments );
838845 }
839846 // 7) single_frame affects free size — instrumented allocator validates correct sizes.
840847 {
841- test_rx_context_init (& ctx , 64 , 1000 );
848+ test_rx_context_init (& ctx , 64 , 40 * MEGA );
842849 memset (& ses , 0 , sizeof (ses ));
843850 ses .owner = & ctx .sub ;
844851 // Stale single-frame: payload freed with payload.size=42.
@@ -848,14 +855,100 @@ static void test_rx_session_scan(void)
848855 ses .slots [1 ] = make_test_slot (& ctx , 2000 , 64 , false);
849856 ses .slots [1 ]-> payload .size = 30 ; // written so far; irrelevant to free size
850857 ses .slots [1 ]-> total_payload_size = 30 ;
851- TEST_ASSERT_EQUAL_INT64 ( BIG_BANG , rx_session_scan (& ses , 5000 ));
858+ TEST_ASSERT_EQUAL_size_t ( 0 , rx_session_scan (& ses , 40 * MEGA + 4000 ));
852859 TEST_ASSERT_NULL (ses .slots [0 ]);
853860 TEST_ASSERT_NULL (ses .slots [1 ]);
861+ TEST_ASSERT_EQUAL_INT64 (0 , ses .timestamp );
854862 TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_slot .allocated_fragments );
855863 TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_slot .allocated_bytes );
856864 TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_payload .allocated_fragments );
857865 TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_payload .allocated_bytes );
858866 }
867+ // 8) All 8 slots populated — verifies FOREACH_SLOT covers all CANARD_PRIO_COUNT entries.
868+ {
869+ test_rx_context_init (& ctx , 64 , 40 * MEGA );
870+ memset (& ses , 0 , sizeof (ses ));
871+ ses .owner = & ctx .sub ;
872+ // 4 stale:
873+ ses .slots [0 ] = make_test_slot (& ctx , 3000 , 0 , true);
874+ ses .slots [2 ] = make_test_slot (& ctx , 3500 , 0 , true);
875+ ses .slots [4 ] = make_test_slot (& ctx , 2000 , 0 , true);
876+ ses .slots [6 ] = make_test_slot (& ctx , 3999 , 0 , true);
877+ // 4 fresh (slots[1] and slots[5] are in-progress):
878+ ses .slots [1 ] = make_test_slot (& ctx , 4500 , 0 , true);
879+ ses .slots [1 ]-> total_payload_size = 10 ;
880+ ses .slots [3 ] = make_test_slot (& ctx , 4001 , 0 , true);
881+ ses .slots [5 ] = make_test_slot (& ctx , 4999 , 0 , true);
882+ ses .slots [5 ]-> total_payload_size = 20 ;
883+ ses .slots [7 ] = make_test_slot (& ctx , 4000 , 0 , true);
884+ TEST_ASSERT_EQUAL_size_t (2 , rx_session_scan (& ses , 40 * MEGA + 4000 ));
885+ TEST_ASSERT_NULL (ses .slots [0 ]);
886+ TEST_ASSERT_NULL (ses .slots [2 ]);
887+ TEST_ASSERT_NULL (ses .slots [4 ]);
888+ TEST_ASSERT_NULL (ses .slots [6 ]);
889+ TEST_ASSERT_NOT_NULL (ses .slots [1 ]);
890+ TEST_ASSERT_NOT_NULL (ses .slots [3 ]);
891+ TEST_ASSERT_NOT_NULL (ses .slots [5 ]);
892+ TEST_ASSERT_NOT_NULL (ses .slots [7 ]);
893+ TEST_ASSERT_EQUAL_INT64 (4999 , ses .timestamp );
894+ rx_slot_destroy (& ctx .sub , ses .slots [1 ]);
895+ rx_slot_destroy (& ctx .sub , ses .slots [3 ]);
896+ rx_slot_destroy (& ctx .sub , ses .slots [5 ]);
897+ rx_slot_destroy (& ctx .sub , ses .slots [7 ]);
898+ TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_slot .allocated_fragments );
899+ }
900+ // 9) In-progress counting: multiple fresh slots with total_payload_size > 0.
901+ {
902+ test_rx_context_init (& ctx , 64 , 40 * MEGA );
903+ memset (& ses , 0 , sizeof (ses ));
904+ ses .owner = & ctx .sub ;
905+ ses .slots [0 ] = make_test_slot (& ctx , 5000 , 0 , true);
906+ ses .slots [0 ]-> total_payload_size = 10 ;
907+ ses .slots [3 ] = make_test_slot (& ctx , 6000 , 0 , true);
908+ ses .slots [3 ]-> total_payload_size = 20 ;
909+ ses .slots [5 ] = make_test_slot (& ctx , 7000 , 0 , true); // fresh, idle
910+ TEST_ASSERT_EQUAL_size_t (2 , rx_session_scan (& ses , 40 * MEGA + 4000 ));
911+ TEST_ASSERT_EQUAL_INT64 (7000 , ses .timestamp );
912+ rx_slot_destroy (& ctx .sub , ses .slots [0 ]);
913+ rx_slot_destroy (& ctx .sub , ses .slots [3 ]);
914+ rx_slot_destroy (& ctx .sub , ses .slots [5 ]);
915+ TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_slot .allocated_fragments );
916+ }
917+ // 10) Fresh slots WITH payload allocations survive intact.
918+ {
919+ test_rx_context_init (& ctx , 64 , 40 * MEGA );
920+ memset (& ses , 0 , sizeof (ses ));
921+ ses .owner = & ctx .sub ;
922+ // Fresh single-frame with 42-byte payload.
923+ ses .slots [0 ] = make_test_slot (& ctx , 5000 , 42 , true);
924+ ses .slots [0 ]-> total_payload_size = 42 ;
925+ // Fresh multi-frame with extent-sized payload, partially written.
926+ ses .slots [1 ] = make_test_slot (& ctx , 6000 , 64 , false);
927+ ses .slots [1 ]-> payload .size = 30 ;
928+ ses .slots [1 ]-> total_payload_size = 30 ;
929+ TEST_ASSERT_EQUAL_size_t (2 , rx_session_scan (& ses , 40 * MEGA + 4000 ));
930+ TEST_ASSERT_EQUAL_UINT64 (0 , ctx .alloc_payload .count_free );
931+ TEST_ASSERT_EQUAL_size_t (42 , ses .slots [0 ]-> payload .size );
932+ TEST_ASSERT_EQUAL_size_t (30 , ses .slots [1 ]-> payload .size );
933+ TEST_ASSERT_EQUAL_INT64 (6000 , ses .timestamp );
934+ rx_slot_destroy (& ctx .sub , ses .slots [0 ]);
935+ rx_slot_destroy (& ctx .sub , ses .slots [1 ]);
936+ TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_slot .allocated_fragments );
937+ TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_payload .allocated_fragments );
938+ }
939+ // 11) Zero-extent subscription: stale slot with NULL payload.data.
940+ // rx_slot_destroy → mem_free(rx_payload, 0, NULL) → skipped by NULL check in mem_free.
941+ {
942+ test_rx_context_init (& ctx , 0 , 40 * MEGA );
943+ memset (& ses , 0 , sizeof (ses ));
944+ ses .owner = & ctx .sub ;
945+ ses .slots [0 ] = make_test_slot (& ctx , 3999 , 0 , true);
946+ TEST_ASSERT_EQUAL_size_t (0 , rx_session_scan (& ses , 40 * MEGA + 4000 ));
947+ TEST_ASSERT_NULL (ses .slots [0 ]);
948+ TEST_ASSERT_EQUAL_INT64 (0 , ses .timestamp );
949+ TEST_ASSERT_EQUAL_UINT64 (0 , ctx .alloc_payload .count_free );
950+ TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_slot .allocated_fragments );
951+ }
859952}
860953
861954// =====================================================================================================================
@@ -1091,6 +1184,64 @@ static void test_rx_slot_write_payload(void)
10911184 mem_free (ctx .canard .mem .rx_payload , ctx .sub .extent , slot .payload .data );
10921185 TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_payload .allocated_fragments );
10931186 }
1187+ // 15) Empty continuation frame mid-stream — buffer and payload.size unchanged.
1188+ {
1189+ test_rx_context_init (& ctx , 64 , 1000 );
1190+ memset (& ses , 0 , sizeof (ses ));
1191+ ses .owner = & ctx .sub ;
1192+ memset (& slot , 0 , sizeof (slot ));
1193+ // Start frame: 8 bytes.
1194+ const byte_t d1 [] = { 0xA1 , 0xA2 , 0xA3 , 0xA4 , 0xA5 , 0xA6 , 0xA7 , 0xA8 };
1195+ const canard_bytes_t f1 = { 8 , d1 };
1196+ TEST_ASSERT_TRUE (rx_slot_write_payload (& ses , & slot , f1 , false));
1197+ TEST_ASSERT_EQUAL_size_t (8 , slot .payload .size );
1198+ TEST_ASSERT_EQUAL_size_t (8 , slot .total_payload_size );
1199+ // Empty continuation: 0 bytes. Early return, no state change.
1200+ const canard_bytes_t f2 = { 0 , NULL };
1201+ TEST_ASSERT_TRUE (rx_slot_write_payload (& ses , & slot , f2 , false));
1202+ TEST_ASSERT_EQUAL_size_t (8 , slot .payload .size );
1203+ TEST_ASSERT_EQUAL_size_t (8 , slot .total_payload_size );
1204+ // Last frame: 4 bytes appended.
1205+ const byte_t d3 [] = { 0xC1 , 0xC2 , 0xC3 , 0xC4 };
1206+ const canard_bytes_t f3 = { 4 , d3 };
1207+ TEST_ASSERT_TRUE (rx_slot_write_payload (& ses , & slot , f3 , true));
1208+ TEST_ASSERT_EQUAL_size_t (12 , slot .payload .size );
1209+ TEST_ASSERT_EQUAL_size_t (12 , slot .total_payload_size );
1210+ TEST_ASSERT_EQUAL_MEMORY (d1 , slot .payload .data , 8 );
1211+ TEST_ASSERT_EQUAL_MEMORY (d3 , (const byte_t * )slot .payload .data + 8 , 4 );
1212+ mem_free (ctx .canard .mem .rx_payload , ctx .sub .extent , slot .payload .data );
1213+ TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_payload .allocated_fragments );
1214+ }
1215+ // 16) Non-empty frame after empty first frame — start re-detection.
1216+ // An empty first frame leaves total_payload_size=0, so the next non-empty frame re-detects start=true.
1217+ {
1218+ test_rx_context_init (& ctx , 64 , 1000 );
1219+ memset (& ses , 0 , sizeof (ses ));
1220+ ses .owner = & ctx .sub ;
1221+ memset (& slot , 0 , sizeof (slot ));
1222+ // Empty first frame (not last). total_payload_size stays 0, no allocation.
1223+ const canard_bytes_t f0 = { 0 , NULL };
1224+ TEST_ASSERT_TRUE (rx_slot_write_payload (& ses , & slot , f0 , false));
1225+ TEST_ASSERT_EQUAL_size_t (0 , slot .total_payload_size );
1226+ TEST_ASSERT_NULL (slot .payload .data );
1227+ // Non-empty frame (not last). Re-detects start=true, allocates extent.
1228+ const byte_t d1 [] = { 0x11 , 0x22 , 0x33 , 0x44 , 0x55 , 0x66 };
1229+ const canard_bytes_t f1 = { 6 , d1 };
1230+ TEST_ASSERT_TRUE (rx_slot_write_payload (& ses , & slot , f1 , false));
1231+ TEST_ASSERT_EQUAL_size_t (6 , slot .payload .size );
1232+ TEST_ASSERT_EQUAL_size_t (6 , slot .total_payload_size );
1233+ TEST_ASSERT_NOT_NULL (slot .payload .data );
1234+ // Last frame: 4 bytes appended.
1235+ const byte_t d2 [] = { 0xAA , 0xBB , 0xCC , 0xDD };
1236+ const canard_bytes_t f2 = { 4 , d2 };
1237+ TEST_ASSERT_TRUE (rx_slot_write_payload (& ses , & slot , f2 , true));
1238+ TEST_ASSERT_EQUAL_size_t (10 , slot .payload .size );
1239+ TEST_ASSERT_EQUAL_size_t (10 , slot .total_payload_size );
1240+ TEST_ASSERT_EQUAL_MEMORY (d1 , slot .payload .data , 6 );
1241+ TEST_ASSERT_EQUAL_MEMORY (d2 , (const byte_t * )slot .payload .data + 6 , 4 );
1242+ mem_free (ctx .canard .mem .rx_payload , ctx .sub .extent , slot .payload .data );
1243+ TEST_ASSERT_EQUAL_size_t (0 , ctx .alloc_payload .allocated_fragments );
1244+ }
10941245}
10951246
10961247// =====================================================================================================================
0 commit comments