Skip to content

Commit 6830a7c

Browse files
Rewrite test_rx_session_scan for new return type and add rx_slot_write_payload sub-cases
rx_session_scan now returns size_t (in-progress slot count) instead of canard_us_t (latest timestamp). Tests updated to check return value and ses.timestamp separately. tid_timeout raised to 40*MEGA to dominate later(RX_SESSION_TIMEOUT, tid_timeout). Added sub-cases 8-11 covering full slot population, multi-slot in-progress counting, fresh payload survival, and zero-extent destroy. Added write_payload sub-cases 15-16 for empty continuation frames and start re-detection after empty first frame. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
1 parent 16ab987 commit 6830a7c

File tree

1 file changed

+177
-26
lines changed

1 file changed

+177
-26
lines changed

tests/src/test_intrusive_rx.c

Lines changed: 177 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
752752
static 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

Comments
 (0)