@@ -2908,11 +2908,352 @@ static void test_port(void)
29082908 TEST_ASSERT_EQUAL (local_uid , rx .p2p_port .topic_hash );
29092909 TEST_ASSERT_EQUAL (UDPARD_REORDERING_WINDOW_UNORDERED , rx .p2p_port .reordering_window );
29102910
2911- // TODO continue: init two ports, one ORDERED, one STATELESS. Feed frames from different remote nodes into each.
2912- // Verify the callbacks: on message, on ack, on collision.
2913- // Feed bad frames and verify they are rejected with no memory leaks.
2914- (void )mem_payload ; // TODO: Will be used when test is completed
2915- (void )del_payload ; // TODO: Will be used when test is completed
2911+ // Initialize two ports: one ORDERED, one STATELESS.
2912+ udpard_rx_port_t port_ordered ;
2913+ const uint64_t topic_hash_ordered = 0x1234567890ABCDEFULL ;
2914+ TEST_ASSERT (udpard_rx_port_new (& port_ordered , topic_hash_ordered , 1000 , 10 * KILO , rx_mem ));
2915+
2916+ udpard_rx_port_t port_stateless ;
2917+ const uint64_t topic_hash_stateless = 0xFEDCBA0987654321ULL ;
2918+ TEST_ASSERT (
2919+ udpard_rx_port_new (& port_stateless , topic_hash_stateless , 500 , UDPARD_REORDERING_WINDOW_STATELESS , rx_mem ));
2920+
2921+ udpard_us_t now = 0 ;
2922+
2923+ // Test 1: Send a valid single-frame transfer to the ORDERED port.
2924+ {
2925+ const uint64_t remote_uid = 0xAABBCCDDEEFF0011ULL ;
2926+ const uint64_t transfer_id = 100 ;
2927+ const char * payload_str = "Hello World" ;
2928+ const size_t payload_len = strlen (payload_str ) + 1 ; // include null terminator
2929+ meta_t meta = { .priority = udpard_prio_nominal ,
2930+ .flag_ack = true,
2931+ .transfer_payload_size = (uint32_t )payload_len ,
2932+ .transfer_id = transfer_id ,
2933+ .sender_uid = remote_uid ,
2934+ .topic_hash = topic_hash_ordered };
2935+ const rx_frame_t frame = make_frame (meta , mem_payload , payload_str , 0 , payload_len );
2936+
2937+ // Serialize the frame into a datagram.
2938+ byte_t dgram [HEADER_SIZE_BYTES + payload_len ];
2939+ header_serialize (dgram , meta , 0 , 0 , frame .base .crc );
2940+ memcpy (dgram + HEADER_SIZE_BYTES , payload_str , payload_len );
2941+ mem_free (mem_payload , frame .base .origin .size , frame .base .origin .data );
2942+
2943+ // Allocate payload for the push.
2944+ void * push_payload = mem_payload .alloc (mem_payload .user , sizeof (dgram ));
2945+ memcpy (push_payload , dgram , sizeof (dgram ));
2946+
2947+ now += 1000 ;
2948+ TEST_ASSERT (udpard_rx_port_push (& rx ,
2949+ & port_ordered ,
2950+ now ,
2951+ (udpard_udpip_ep_t ){ .ip = 0x0A000001 , .port = 0x1234 },
2952+ (udpard_bytes_mut_t ){ .data = push_payload , .size = sizeof (dgram ) },
2953+ del_payload ,
2954+ 0 ));
2955+
2956+ // Verify the callback was invoked.
2957+ TEST_ASSERT_EQUAL (1 , cb_result .message .count );
2958+ TEST_ASSERT_EQUAL (transfer_id , cb_result .message .history [0 ].transfer_id );
2959+ TEST_ASSERT_EQUAL (remote_uid , cb_result .message .history [0 ].remote .uid );
2960+ TEST_ASSERT_EQUAL (payload_len , cb_result .message .history [0 ].payload_size_stored );
2961+ TEST_ASSERT (transfer_payload_verify (& cb_result .message .history [0 ], payload_len , payload_str , payload_len ));
2962+
2963+ // Verify ACK was mandated.
2964+ TEST_ASSERT_EQUAL (1 , cb_result .ack_mandate .count );
2965+ TEST_ASSERT_EQUAL (transfer_id , cb_result .ack_mandate .am .transfer_id );
2966+
2967+ // Clean up.
2968+ udpard_fragment_free_all (cb_result .message .history [0 ].payload_head , mem_frag );
2969+ cb_result .message .count = 0 ;
2970+ cb_result .ack_mandate .count = 0 ;
2971+ }
2972+
2973+ // Test 2: Send a valid single-frame transfer to the STATELESS port.
2974+ {
2975+ const uint64_t remote_uid = 0x1122334455667788ULL ;
2976+ const uint64_t transfer_id = 200 ;
2977+ const char * payload_str = "Stateless" ;
2978+ const size_t payload_len = strlen (payload_str ) + 1 ;
2979+ meta_t meta = { .priority = udpard_prio_high ,
2980+ .flag_ack = false,
2981+ .transfer_payload_size = (uint32_t )payload_len ,
2982+ .transfer_id = transfer_id ,
2983+ .sender_uid = remote_uid ,
2984+ .topic_hash = topic_hash_stateless };
2985+ const rx_frame_t frame = make_frame (meta , mem_payload , payload_str , 0 , payload_len );
2986+
2987+ byte_t dgram [HEADER_SIZE_BYTES + payload_len ];
2988+ header_serialize (dgram , meta , 0 , 0 , frame .base .crc );
2989+ memcpy (dgram + HEADER_SIZE_BYTES , payload_str , payload_len );
2990+ mem_free (mem_payload , frame .base .origin .size , frame .base .origin .data );
2991+
2992+ void * push_payload = mem_payload .alloc (mem_payload .user , sizeof (dgram ));
2993+ memcpy (push_payload , dgram , sizeof (dgram ));
2994+
2995+ now += 1000 ;
2996+ TEST_ASSERT (udpard_rx_port_push (& rx ,
2997+ & port_stateless ,
2998+ now ,
2999+ (udpard_udpip_ep_t ){ .ip = 0x0B000001 , .port = 0x5678 },
3000+ (udpard_bytes_mut_t ){ .data = push_payload , .size = sizeof (dgram ) },
3001+ del_payload ,
3002+ 1 ));
3003+
3004+ TEST_ASSERT_EQUAL (1 , cb_result .message .count );
3005+ TEST_ASSERT_EQUAL (transfer_id , cb_result .message .history [0 ].transfer_id );
3006+ TEST_ASSERT_EQUAL (remote_uid , cb_result .message .history [0 ].remote .uid );
3007+ TEST_ASSERT_EQUAL (payload_len , cb_result .message .history [0 ].payload_size_stored );
3008+ TEST_ASSERT (transfer_payload_verify (& cb_result .message .history [0 ], payload_len , payload_str , payload_len ));
3009+
3010+ // No ACK for stateless mode without flag_ack.
3011+ TEST_ASSERT_EQUAL (0 , cb_result .ack_mandate .count );
3012+
3013+ udpard_fragment_free_all (cb_result .message .history [0 ].payload_head , mem_frag );
3014+ cb_result .message .count = 0 ;
3015+ }
3016+
3017+ // Test 3: Send a multi-frame transfer to the ORDERED port.
3018+ {
3019+ const uint64_t remote_uid = 0xAABBCCDDEEFF0011ULL ;
3020+ const uint64_t transfer_id = 101 ;
3021+ const char * full_payload = "0123456789ABCDEFGHIJ" ;
3022+ const size_t payload_len = 20 ;
3023+ meta_t meta = { .priority = udpard_prio_nominal ,
3024+ .flag_ack = true,
3025+ .transfer_payload_size = (uint32_t )payload_len ,
3026+ .transfer_id = transfer_id ,
3027+ .sender_uid = remote_uid ,
3028+ .topic_hash = topic_hash_ordered };
3029+
3030+ // Frame 1: offset 0, 10 bytes.
3031+ {
3032+ const rx_frame_t frame = make_frame (meta , mem_payload , full_payload , 0 , 10 );
3033+ byte_t dgram [HEADER_SIZE_BYTES + 10 ];
3034+ header_serialize (dgram , meta , 0 , 0 , frame .base .crc );
3035+ memcpy (dgram + HEADER_SIZE_BYTES , full_payload , 10 );
3036+ mem_free (mem_payload , frame .base .origin .size , frame .base .origin .data );
3037+
3038+ void * push_payload = mem_payload .alloc (mem_payload .user , sizeof (dgram ));
3039+ memcpy (push_payload , dgram , sizeof (dgram ));
3040+
3041+ now += 1000 ;
3042+ TEST_ASSERT (udpard_rx_port_push (& rx ,
3043+ & port_ordered ,
3044+ now ,
3045+ (udpard_udpip_ep_t ){ .ip = 0x0A000001 , .port = 0x1234 },
3046+ (udpard_bytes_mut_t ){ .data = push_payload , .size = sizeof (dgram ) },
3047+ del_payload ,
3048+ 0 ));
3049+ }
3050+
3051+ // Frame 2: offset 10, 10 bytes.
3052+ {
3053+ const rx_frame_t frame = make_frame (meta , mem_payload , full_payload , 10 , 10 );
3054+ byte_t dgram [HEADER_SIZE_BYTES + 10 ];
3055+ header_serialize (dgram , meta , 1 , 10 , frame .base .crc );
3056+ memcpy (dgram + HEADER_SIZE_BYTES , full_payload + 10 , 10 );
3057+ mem_free (mem_payload , frame .base .origin .size , frame .base .origin .data );
3058+
3059+ void * push_payload = mem_payload .alloc (mem_payload .user , sizeof (dgram ));
3060+ memcpy (push_payload , dgram , sizeof (dgram ));
3061+
3062+ now += 1000 ;
3063+ TEST_ASSERT (udpard_rx_port_push (& rx ,
3064+ & port_ordered ,
3065+ now ,
3066+ (udpard_udpip_ep_t ){ .ip = 0x0A000001 , .port = 0x1234 },
3067+ (udpard_bytes_mut_t ){ .data = push_payload , .size = sizeof (dgram ) },
3068+ del_payload ,
3069+ 0 ));
3070+ }
3071+
3072+ // Verify the transfer was received.
3073+ TEST_ASSERT_EQUAL (1 , cb_result .message .count );
3074+ TEST_ASSERT_EQUAL (transfer_id , cb_result .message .history [0 ].transfer_id );
3075+ TEST_ASSERT_EQUAL (payload_len , cb_result .message .history [0 ].payload_size_stored );
3076+ TEST_ASSERT (transfer_payload_verify (& cb_result .message .history [0 ], payload_len , full_payload , payload_len ));
3077+
3078+ TEST_ASSERT_EQUAL (1 , cb_result .ack_mandate .count );
3079+
3080+ udpard_fragment_free_all (cb_result .message .history [0 ].payload_head , mem_frag );
3081+ cb_result .message .count = 0 ;
3082+ cb_result .ack_mandate .count = 0 ;
3083+ }
3084+
3085+ // Test 4: Send a frame with wrong topic hash (collision).
3086+ {
3087+ const uint64_t remote_uid = 0x9988776655443322ULL ;
3088+ const uint64_t transfer_id = 300 ;
3089+ const char * payload_str = "Collision" ;
3090+ const size_t payload_len = strlen (payload_str ) + 1 ;
3091+ const uint64_t wrong_hash = topic_hash_ordered + 1 ; // Different hash
3092+ meta_t meta = { .priority = udpard_prio_nominal ,
3093+ .flag_ack = false,
3094+ .transfer_payload_size = (uint32_t )payload_len ,
3095+ .transfer_id = transfer_id ,
3096+ .sender_uid = remote_uid ,
3097+ .topic_hash = wrong_hash };
3098+ const rx_frame_t frame = make_frame (meta , mem_payload , payload_str , 0 , payload_len );
3099+
3100+ byte_t dgram [HEADER_SIZE_BYTES + payload_len ];
3101+ header_serialize (dgram , meta , 0 , 0 , frame .base .crc );
3102+ memcpy (dgram + HEADER_SIZE_BYTES , payload_str , payload_len );
3103+ mem_free (mem_payload , frame .base .origin .size , frame .base .origin .data );
3104+
3105+ void * push_payload = mem_payload .alloc (mem_payload .user , sizeof (dgram ));
3106+ memcpy (push_payload , dgram , sizeof (dgram ));
3107+
3108+ now += 1000 ;
3109+ TEST_ASSERT (udpard_rx_port_push (& rx ,
3110+ & port_ordered ,
3111+ now ,
3112+ (udpard_udpip_ep_t ){ .ip = 0x0C000001 , .port = 0x9999 },
3113+ (udpard_bytes_mut_t ){ .data = push_payload , .size = sizeof (dgram ) },
3114+ del_payload ,
3115+ 2 ));
3116+
3117+ // Verify collision callback was invoked.
3118+ TEST_ASSERT_EQUAL (1 , cb_result .collision .count );
3119+ TEST_ASSERT_EQUAL (remote_uid , cb_result .collision .remote .uid );
3120+
3121+ // No message should have been received.
3122+ TEST_ASSERT_EQUAL (0 , cb_result .message .count );
3123+
3124+ cb_result .collision .count = 0 ;
3125+ }
3126+
3127+ // Test 5: Send a malformed frame (bad CRC in header).
3128+ {
3129+ const uint64_t errors_before = rx .errors_frame_malformed ;
3130+ byte_t bad_dgram [HEADER_SIZE_BYTES + 10 ];
3131+ memset (bad_dgram , 0xAA , sizeof (bad_dgram )); // Garbage data
3132+
3133+ void * push_payload = mem_payload .alloc (mem_payload .user , sizeof (bad_dgram ));
3134+ memcpy (push_payload , bad_dgram , sizeof (bad_dgram ));
3135+
3136+ now += 1000 ;
3137+ TEST_ASSERT (udpard_rx_port_push (& rx ,
3138+ & port_ordered ,
3139+ now ,
3140+ (udpard_udpip_ep_t ){ .ip = 0x0D000001 , .port = 0xAAAA },
3141+ (udpard_bytes_mut_t ){ .data = push_payload , .size = sizeof (bad_dgram ) },
3142+ del_payload ,
3143+ 0 ));
3144+
3145+ // Verify error counter was incremented.
3146+ TEST_ASSERT_EQUAL (errors_before + 1 , rx .errors_frame_malformed );
3147+
3148+ // No callbacks should have been invoked.
3149+ TEST_ASSERT_EQUAL (0 , cb_result .message .count );
3150+ TEST_ASSERT_EQUAL (0 , cb_result .collision .count );
3151+ TEST_ASSERT_EQUAL (0 , cb_result .ack_mandate .count );
3152+ }
3153+
3154+ // Test 6: Send a multi-frame transfer to STATELESS port (should be rejected).
3155+ {
3156+ const uint64_t errors_before = rx .errors_transfer_malformed ;
3157+ const uint64_t remote_uid = 0x1122334455667788ULL ;
3158+ const uint64_t transfer_id = 201 ;
3159+ const char * payload_str = "MultiFrameStateless" ;
3160+ const size_t payload_len = strlen (payload_str ) + 1 ;
3161+ meta_t meta = { .priority = udpard_prio_high ,
3162+ .flag_ack = false,
3163+ .transfer_payload_size = (uint32_t )payload_len ,
3164+ .transfer_id = transfer_id ,
3165+ .sender_uid = remote_uid ,
3166+ .topic_hash = topic_hash_stateless };
3167+
3168+ // Send only the first frame (offset 0, partial payload).
3169+ const rx_frame_t frame = make_frame (meta , mem_payload , payload_str , 0 , 10 );
3170+ byte_t dgram [HEADER_SIZE_BYTES + 10 ];
3171+ header_serialize (dgram , meta , 0 , 0 , frame .base .crc );
3172+ memcpy (dgram + HEADER_SIZE_BYTES , payload_str , 10 );
3173+ mem_free (mem_payload , frame .base .origin .size , frame .base .origin .data );
3174+
3175+ void * push_payload = mem_payload .alloc (mem_payload .user , sizeof (dgram ));
3176+ memcpy (push_payload , dgram , sizeof (dgram ));
3177+
3178+ now += 1000 ;
3179+ TEST_ASSERT (udpard_rx_port_push (& rx ,
3180+ & port_stateless ,
3181+ now ,
3182+ (udpard_udpip_ep_t ){ .ip = 0x0B000001 , .port = 0x5678 },
3183+ (udpard_bytes_mut_t ){ .data = push_payload , .size = sizeof (dgram ) },
3184+ del_payload ,
3185+ 1 ));
3186+
3187+ // STATELESS mode rejects multi-frame transfers.
3188+ TEST_ASSERT_EQUAL (errors_before + 1 , rx .errors_transfer_malformed );
3189+ TEST_ASSERT_EQUAL (0 , cb_result .message .count );
3190+ }
3191+
3192+ // Test 7: Verify invalid API calls return false.
3193+ {
3194+ void * dummy_payload = mem_payload .alloc (mem_payload .user , 100 );
3195+ memset (dummy_payload , 0 , 100 );
3196+ // Null rx pointer.
3197+ TEST_ASSERT_FALSE (udpard_rx_port_push (NULL ,
3198+ & port_ordered ,
3199+ now ,
3200+ (udpard_udpip_ep_t ){ .ip = 0x01020304 , .port = 1234 },
3201+ (udpard_bytes_mut_t ){ .data = dummy_payload , .size = 100 },
3202+ del_payload ,
3203+ 0 ));
3204+ // Null port pointer.
3205+ TEST_ASSERT_FALSE (udpard_rx_port_push (& rx ,
3206+ NULL ,
3207+ now ,
3208+ (udpard_udpip_ep_t ){ .ip = 0x01020304 , .port = 1234 },
3209+ (udpard_bytes_mut_t ){ .data = dummy_payload , .size = 100 },
3210+ del_payload ,
3211+ 0 ));
3212+ // Invalid endpoint (ip = 0).
3213+ TEST_ASSERT_FALSE (udpard_rx_port_push (& rx ,
3214+ & port_ordered ,
3215+ now ,
3216+ (udpard_udpip_ep_t ){ .ip = 0 , .port = 1234 },
3217+ (udpard_bytes_mut_t ){ .data = dummy_payload , .size = 100 },
3218+ del_payload ,
3219+ 0 ));
3220+ // Invalid endpoint (port = 0).
3221+ TEST_ASSERT_FALSE (udpard_rx_port_push (& rx ,
3222+ & port_ordered ,
3223+ now ,
3224+ (udpard_udpip_ep_t ){ .ip = 0x01020304 , .port = 0 },
3225+ (udpard_bytes_mut_t ){ .data = dummy_payload , .size = 100 },
3226+ del_payload ,
3227+ 0 ));
3228+ // Null datagram payload.
3229+ TEST_ASSERT_FALSE (udpard_rx_port_push (& rx ,
3230+ & port_ordered ,
3231+ now ,
3232+ (udpard_udpip_ep_t ){ .ip = 0x01020304 , .port = 1234 },
3233+ (udpard_bytes_mut_t ){ .data = NULL , .size = 100 },
3234+ del_payload ,
3235+ 0 ));
3236+ // Invalid interface index.
3237+ TEST_ASSERT_FALSE (udpard_rx_port_push (& rx ,
3238+ & port_ordered ,
3239+ now ,
3240+ (udpard_udpip_ep_t ){ .ip = 0x01020304 , .port = 1234 },
3241+ (udpard_bytes_mut_t ){ .data = dummy_payload , .size = 100 },
3242+ del_payload ,
3243+ UDPARD_NETWORK_INTERFACE_COUNT_MAX ));
3244+ // Free the dummy payload since all calls failed.
3245+ mem_free (mem_payload , 100 , dummy_payload );
3246+ }
3247+
3248+ // Cleanup.
3249+ udpard_rx_port_free (& rx , & port_ordered );
3250+ udpard_rx_port_free (& rx , & port_stateless );
3251+ udpard_rx_free (& rx );
3252+
3253+ // Verify no memory leaks.
3254+ TEST_ASSERT_EQUAL_size_t (0 , alloc_frag .allocated_fragments );
3255+ TEST_ASSERT_EQUAL_size_t (0 , alloc_session .allocated_fragments );
3256+ TEST_ASSERT_EQUAL_size_t (0 , alloc_payload .allocated_fragments );
29163257}
29173258
29183259void setUp (void ) {}
0 commit comments