@@ -700,6 +700,201 @@ static void test_rx_parse_anonymous_multi_frame_reject(void)
700700 }
701701}
702702
703+ // =====================================================================================================================
704+ // Test 19: One-byte frame (tail byte only). SOT+EOT single-frame tail 0xE0 → v1 parse, payload.size=0.
705+ static void test_rx_parse_one_byte_tail_only (void )
706+ {
707+ frame_t v0 ;
708+ frame_t v1 ;
709+ // v1.1 message CAN ID: prio=0, subject=0, src=0, bit7=1. CAN ID = 0x00000080.
710+ // Tail byte 0xE0 = SOT|EOT|toggle=1|TID=0 → v1 single-frame.
711+ {
712+ const byte_t d [] = { 0xE0 };
713+ const canard_bytes_t pl = { sizeof (d ), d };
714+ const byte_t ret = rx_parse (0x00000080UL , pl , & v0 , & v1 );
715+ TEST_ASSERT_EQUAL_UINT8 (2 , ret ); // v1 accepted
716+ TEST_ASSERT_EQUAL_INT (canard_kind_1v1_message , v1 .kind );
717+ TEST_ASSERT_EQUAL_size_t (0 , v1 .payload .size );
718+ TEST_ASSERT_EQUAL_PTR (d , v1 .payload .data );
719+ TEST_ASSERT_TRUE (v1 .start );
720+ TEST_ASSERT_TRUE (v1 .end );
721+ TEST_ASSERT_TRUE (v1 .toggle );
722+ TEST_ASSERT_EQUAL_UINT8 (0 , v1 .transfer_id );
723+ }
724+ // v0 single-frame: tail 0xC0 = SOT|EOT|toggle=0|TID=0 → v0 only.
725+ // CAN ID 0x00002A01: message, bit7=0. v0 message src=1 (non-anonymous), type_id=42.
726+ {
727+ const byte_t d [] = { 0xC0 };
728+ const canard_bytes_t pl = { sizeof (d ), d };
729+ const byte_t ret = rx_parse (0x00002A01UL , pl , & v0 , & v1 );
730+ TEST_ASSERT_EQUAL_UINT8 (1 , ret ); // v0 only
731+ TEST_ASSERT_EQUAL_INT (canard_kind_0v1_message , v0 .kind );
732+ TEST_ASSERT_EQUAL_size_t (0 , v0 .payload .size );
733+ TEST_ASSERT_EQUAL_PTR (d , v0 .payload .data );
734+ TEST_ASSERT_TRUE (v0 .start );
735+ TEST_ASSERT_TRUE (v0 .end );
736+ TEST_ASSERT_FALSE (v0 .toggle );
737+ TEST_ASSERT_EQUAL_UINT8 (0 , v0 .transfer_id );
738+ }
739+ }
740+
741+ // =====================================================================================================================
742+ // Test 20: 64-byte CAN FD frame with v1.1 message CAN ID. Tail byte at position 63.
743+ static void test_rx_parse_max_fd_frame (void )
744+ {
745+ frame_t v0 ;
746+ frame_t v1 ;
747+ // v1.1 message CAN ID: prio=2, subject=5000, src=10, bit7=1. CAN ID = (2<<26)|(5000<<8)|(1<<7)|10 = 0x0813888A.
748+ byte_t d [64 ];
749+ memset (d , 0xBB , sizeof (d ));
750+ d [63 ] = 0xEF ; // v1 single-frame tail: SOT|EOT|toggle=1|TID=15
751+ const canard_bytes_t pl = { sizeof (d ), d };
752+ const byte_t ret = rx_parse (0x0813888AUL , pl , & v0 , & v1 );
753+ TEST_ASSERT_EQUAL_UINT8 (2 , ret ); // v1 only (toggle=1 excludes v0 on start)
754+ TEST_ASSERT_EQUAL_INT (canard_kind_1v1_message , v1 .kind );
755+ TEST_ASSERT_EQUAL_size_t (63 , v1 .payload .size );
756+ TEST_ASSERT_EQUAL_PTR (d , v1 .payload .data );
757+ TEST_ASSERT_EQUAL_UINT8 (15 , v1 .transfer_id );
758+ TEST_ASSERT_EQUAL_HEX8 (10 , v1 .src );
759+ TEST_ASSERT_EQUAL_UINT32 (5000 , v1 .port_id );
760+ TEST_ASSERT_EQUAL_INT (canard_prio_fast , v1 .priority );
761+ }
762+
763+ // =====================================================================================================================
764+ // Test 21: v1.0 service with src==dst (self-addressing). Must be rejected.
765+ static void test_rx_parse_v1_service_self_addressing (void )
766+ {
767+ frame_t v0 ;
768+ frame_t v1 ;
769+ // v1.0 service request: prio=0, svc_id=1, dst=42, src=42. Self-addressing → rejected.
770+ // CAN ID: (0<<26)|(1<<25)|(1<<24)|(1<<14)|(42<<7)|42 = 0x0300552A
771+ {
772+ const byte_t d [] = { 0xE0 }; // v1 single-frame tail: SOT|EOT|toggle=1|TID=0
773+ const canard_bytes_t pl = { sizeof (d ), d };
774+ const byte_t ret = rx_parse (0x0300552AUL , pl , & v0 , & v1 );
775+ // v1 rejected due to src==dst. v0: SOT=1 toggle=1 → v0 excluded. Return = 0.
776+ TEST_ASSERT_EQUAL_UINT8 (0 , ret );
777+ }
778+ // v1.0 service response: prio=4, svc_id=100, dst=10, src=10.
779+ // CAN ID: (4<<26)|(1<<25)|(0<<24)|(100<<14)|(10<<7)|10 = 0x1219050A
780+ {
781+ const byte_t d [] = { 0xE3 }; // v1 single, tid=3, SOT|EOT|toggle=1
782+ const canard_bytes_t pl = { sizeof (d ), d };
783+ const byte_t ret = rx_parse (0x1219050AUL , pl , & v0 , & v1 );
784+ // v1 rejected (src==dst=10). v0: SOT=1 toggle=1 → excluded. Return=0.
785+ TEST_ASSERT_EQUAL_UINT8 (0 , ret );
786+ }
787+ }
788+
789+ // =====================================================================================================================
790+ // Test 22: v0 service with src==dst (self-addressing). Must be rejected.
791+ static void test_rx_parse_v0_service_self_addressing (void )
792+ {
793+ frame_t v0 ;
794+ frame_t v1 ;
795+ // v0 service request: prio=4, type_id=0x37, dst=11, src=11. Self-addressing.
796+ // CAN ID: (((4<<2)|3)<<24)|(0x37<<16)|(1<<15)|(11<<8)|(1<<7)|11 = 0x13378B8B
797+ {
798+ const byte_t d [] = { 0xC0 }; // v0 single: SOT=1, EOT=1, toggle=0, TID=0
799+ const canard_bytes_t pl = { sizeof (d ), d };
800+ const byte_t ret = rx_parse (0x13378B8BUL , pl , & v0 , & v1 );
801+ // v0 rejected (src==dst). v1: SOT=1 toggle=0 → v1 excluded. Return=0.
802+ TEST_ASSERT_EQUAL_UINT8 (0 , ret );
803+ }
804+ }
805+
806+ // =====================================================================================================================
807+ // Test 23: v0 service with src=0. Must be rejected (node-ID 0 reserved for anonymous).
808+ static void test_rx_parse_v0_service_zero_src (void )
809+ {
810+ frame_t v0 ;
811+ frame_t v1 ;
812+ // v0 service request: prio=4, type_id=0x37, dst=24, src=0.
813+ // CAN ID: (((4<<2)|3)<<24)|(0x37<<16)|(1<<15)|(24<<8)|(1<<7)|0 = 0x13379880
814+ {
815+ const byte_t d [] = { 0xC0 }; // v0 single
816+ const canard_bytes_t pl = { sizeof (d ), d };
817+ const byte_t ret = rx_parse (0x13379880UL , pl , & v0 , & v1 );
818+ // v0 rejected (src=0). v1: start && !toggle → excluded. Return=0.
819+ TEST_ASSERT_EQUAL_UINT8 (0 , ret );
820+ }
821+ }
822+
823+ // =====================================================================================================================
824+ // Test 24: v0 service with dst=0. Must be rejected (node-ID 0 reserved for anonymous).
825+ static void test_rx_parse_v0_service_zero_dst (void )
826+ {
827+ frame_t v0 ;
828+ frame_t v1 ;
829+ // v0 service request: prio=4, type_id=0x37, dst=0, src=11.
830+ // CAN ID: (((4<<2)|3)<<24)|(0x37<<16)|(1<<15)|(0<<8)|(1<<7)|11 = 0x1337808B
831+ {
832+ const byte_t d [] = { 0xC0 }; // v0 single
833+ const canard_bytes_t pl = { sizeof (d ), d };
834+ const byte_t ret = rx_parse (0x1337808BUL , pl , & v0 , & v1 );
835+ // v0 rejected (dst=0). v1: start && !toggle → excluded. Return=0.
836+ TEST_ASSERT_EQUAL_UINT8 (0 , ret );
837+ }
838+ }
839+
840+ // =====================================================================================================================
841+ // Test 25: Middle frame (SOT=0, EOT=0) with only 1 byte (tail only). Payload=0. Rejected by payload_ok.
842+ static void test_rx_parse_non_start_non_end_empty (void )
843+ {
844+ frame_t v0 ;
845+ frame_t v1 ;
846+ // CAN ID valid for both versions: message, bit7=0. 0x00002A01.
847+ // Tail: SOT=0 EOT=0 toggle=0 TID=5 → 0x05. One byte total.
848+ {
849+ const byte_t d [] = { 0x05 };
850+ const canard_bytes_t pl = { sizeof (d ), d };
851+ const byte_t ret = rx_parse (0x00002A01UL , pl , & v0 , & v1 );
852+ // payload_raw.size=1. payload.size=0.
853+ // payload_ok = (end || (1 >= 8)) && ((start && end) || (0 > 0))
854+ // = (false || false) && (false || false)
855+ // = false
856+ // Both rejected.
857+ TEST_ASSERT_EQUAL_UINT8 (0 , ret );
858+ }
859+ }
860+
861+ // =====================================================================================================================
862+ // Test 26: Continuation frame (non-last) with only 6 bytes. Under full MTU (8). Rejected.
863+ static void test_rx_parse_non_last_short_frame (void )
864+ {
865+ frame_t v0 ;
866+ frame_t v1 ;
867+ // CAN ID valid for both versions: message, bit7=0. 0x00002A01.
868+ // Tail: SOT=0 EOT=0 toggle=0 TID=3 → 0x03. 6 bytes total, 5 payload.
869+ {
870+ const byte_t d [] = { 0xAA , 0xBB , 0xCC , 0xDD , 0xEE , 0x03 };
871+ const canard_bytes_t pl = { sizeof (d ), d };
872+ const byte_t ret = rx_parse (0x00002A01UL , pl , & v0 , & v1 );
873+ // payload_raw.size=6. payload.size=5.
874+ // payload_ok = (end || (6 >= 8)) && ((start && end) || (5 > 0))
875+ // = (false || false) && (false || true)
876+ // = false
877+ // Both rejected.
878+ TEST_ASSERT_EQUAL_UINT8 (0 , ret );
879+ }
880+ // Same but with 7 bytes: still under MTU.
881+ {
882+ const byte_t d [] = { 0xAA , 0xBB , 0xCC , 0xDD , 0xEE , 0xFF , 0x03 };
883+ const canard_bytes_t pl = { sizeof (d ), d };
884+ const byte_t ret = rx_parse (0x00002A01UL , pl , & v0 , & v1 );
885+ // payload_raw.size=7 < 8 → first clause false. Rejected.
886+ TEST_ASSERT_EQUAL_UINT8 (0 , ret );
887+ }
888+ // With exactly 8 bytes: at MTU. Accepted.
889+ {
890+ const byte_t d [] = { 0xAA , 0xBB , 0xCC , 0xDD , 0xEE , 0xFF , 0x11 , 0x03 };
891+ const canard_bytes_t pl = { sizeof (d ), d };
892+ const byte_t ret = rx_parse (0x00002A01UL , pl , & v0 , & v1 );
893+ // payload_raw.size=8 >= 8 → OK. payload.size=7 > 0 → OK. Both versions attempted.
894+ TEST_ASSERT_EQUAL_UINT8 (3 , ret );
895+ }
896+ }
897+
703898// =====================================================================================================================
704899
705900int main (void )
@@ -724,5 +919,13 @@ int main(void)
724919 RUN_TEST (test_rx_parse_non_first_dual_output );
725920 RUN_TEST (test_rx_parse_payload_validation );
726921 RUN_TEST (test_rx_parse_anonymous_multi_frame_reject );
922+ RUN_TEST (test_rx_parse_one_byte_tail_only );
923+ RUN_TEST (test_rx_parse_max_fd_frame );
924+ RUN_TEST (test_rx_parse_v1_service_self_addressing );
925+ RUN_TEST (test_rx_parse_v0_service_self_addressing );
926+ RUN_TEST (test_rx_parse_v0_service_zero_src );
927+ RUN_TEST (test_rx_parse_v0_service_zero_dst );
928+ RUN_TEST (test_rx_parse_non_start_non_end_empty );
929+ RUN_TEST (test_rx_parse_non_last_short_frame );
727930 return UNITY_END ();
728931}
0 commit comments