#include "Unity/src/unity.h" #include "message_parser.h" // Stellt sicher, dass deine Header-Datei message_parser.h korrekt ist #include #include // Für memcpy // Globale Variablen für Callback-Überprüfung static uint8_t received_msgid = 0xFF; static uint8_t received_payload[MAX_TOTAL_CONTENT_LENGTH]; // Muss groß genug sein static size_t received_payload_len = 0; static enum ParserError received_error = NoError; static bool message_received_flag = false; static bool message_fail_flag = false; // Mock-Implementierungen für die Callbacks void mock_on_message_received(uint8_t msgid, const uint8_t *payload, size_t payload_len) { received_msgid = msgid; received_payload_len = payload_len; // Sicherstellen, dass der Puffer nicht überläuft memcpy(received_payload, payload, (payload_len < MAX_TOTAL_CONTENT_LENGTH) ? payload_len : MAX_TOTAL_CONTENT_LENGTH); message_received_flag = true; } void mock_on_message_fail(uint8_t msgid, const uint8_t *payload, size_t payload_len, enum ParserError error) { received_msgid = msgid; // Auch bei Fehlern kann die ID relevant sein received_payload_len = payload_len; // Auch hier, um sicherzustellen, dass wir den Zustand des Puffers beim Fehler // sehen können memcpy(received_payload, payload, (payload_len < MAX_TOTAL_CONTENT_LENGTH) ? payload_len : MAX_TOTAL_CONTENT_LENGTH); received_error = error; message_fail_flag = true; } // --- UNITY SETUP/TEARDOWN --- void setUp(void) { // Reset der globalen Variablen vor jedem Test received_msgid = 0xFF; received_payload_len = 0; received_error = NoError; message_received_flag = false; message_fail_flag = false; memset(received_payload, 0, MAX_TOTAL_CONTENT_LENGTH); // Registrierung der Mock-Callbacks (muss vor den Tests erfolgen) register_message_callback(mock_on_message_received); register_message_fail_callback(mock_on_message_fail); } void tearDown(void) {} // optional // --- Hilfsfunktion zur Checksummenberechnung (für Tests) --- // Berechnet die Checksumme für MSGID + Payload + Checksummen-Byte // Ergibt 0x00, wenn die gesamte Kette XORiert wird uint8_t calculate_test_checksum_final(uint8_t msgid, const uint8_t *payload, size_t payload_len, uint8_t actual_checksum_byte) { uint8_t cs = msgid; for (size_t i = 0; i < payload_len; ++i) { cs ^= payload[i]; } cs ^= actual_checksum_byte; // Das gesendete Checksummen-Byte wird auch XORiert return cs; } // Hilfsfunktion zur Berechnung des *zu sendenden* Checksummen-Bytes // Dies ist der Wert, der im Frame an der Checksummen-Position steht, // sodass die finale XOR-Summe der Nutzdaten (MSGID + Payload + dieses Byte) // 0x00 ergibt. uint8_t calculate_payload_checksum_byte(uint8_t msgid, const uint8_t *payload, size_t payload_len) { uint8_t cs = msgid; for (size_t i = 0; i < payload_len; ++i) { cs ^= payload[i]; } return cs; // Dies ist der Wert, der gesendet werden muss, damit die finale // XOR-Summe 0x00 wird } // Test 1: Gültige Nachricht mit Payload void test_1_valid_message_parses_correctly(void) { struct MessageReceive mr = InitMessageReceive(); uint8_t msgid = 0x01; uint8_t payload[] = {0x01, 0x02, 0x03}; size_t payload_len = sizeof(payload); // Berechne das Checksummen-Byte, das gesendet werden muss uint8_t checksum_byte_to_send = calculate_payload_checksum_byte(msgid, payload, payload_len); uint8_t full_message[] = { StartByte, // 0xAA msgid, // 0x01 0x01, 0x02, 0x03, // Payload checksum_byte_to_send, // Das Checksummen-Byte EndByte // 0xCC }; for (uint8_t i = 0; i < sizeof(full_message); ++i) { parse_byte(&mr, full_message[i]); } TEST_ASSERT_TRUE(message_received_flag); TEST_ASSERT_FALSE(message_fail_flag); TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); TEST_ASSERT_EQUAL_UINT8(payload_len, received_payload_len); // Payload-Länge TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, received_payload, payload_len); TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); TEST_ASSERT_EQUAL_UINT8(NoError, mr.error); } // Test 2: Ungültige Checksumme – Fehler gemeldet und Zustand zurückgesetzt void test_2_invalid_checksum_resets_state(void) { struct MessageReceive mr = InitMessageReceive(); uint8_t msgid = 0x02; uint8_t payload[] = {0x10, 0x20}; size_t payload_len = sizeof(payload); uint8_t wrong_checksum_byte = 0x01; // Absichtlich falsche Checksumme uint8_t full_message[] = {StartByte, msgid, 0x10, 0x20, wrong_checksum_byte, // Falsches Checksummen-Byte EndByte}; for (uint8_t i = 0; i < sizeof(full_message); ++i) { parse_byte(&mr, full_message[i]); } TEST_ASSERT_FALSE(message_received_flag); TEST_ASSERT_TRUE(message_fail_flag); TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); // received_payload_len sollte die Länge der Daten sein, die bis zum Fehler // empfangen wurden, d.h., Payload-Länge + das falsche Checksummen-Byte. TEST_ASSERT_EQUAL_UINT8(payload_len + 1, received_payload_len); TEST_ASSERT_EQUAL_UINT8(WrongCheckSum, received_error); TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); TEST_ASSERT_EQUAL_UINT8(WrongCheckSum, mr.error); } // Test 3: Gültige Nachricht ohne Payload (Länge 0) void test_3_zero_length_message(void) { struct MessageReceive mr = InitMessageReceive(); uint8_t msgid = 0x03; uint8_t payload[] = {}; // Leerer Payload size_t payload_len = sizeof(payload); uint8_t checksum_byte_to_send = calculate_payload_checksum_byte(msgid, payload, payload_len); uint8_t full_message[] = { StartByte, msgid, checksum_byte_to_send, // Checksummen-Byte (hier gleich MSGID, da Payload // leer) EndByte}; for (uint8_t i = 0; i < sizeof(full_message); ++i) { parse_byte(&mr, full_message[i]); } TEST_ASSERT_TRUE(message_received_flag); TEST_ASSERT_FALSE(message_fail_flag); TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); TEST_ASSERT_EQUAL_UINT8(0, received_payload_len); // Payload-Länge ist 0 TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); TEST_ASSERT_EQUAL_UINT8(NoError, mr.error); } // Test 4: Escapete MSGID (0xAA im MSGID-Feld) void test_4_escaped_message_id(void) { struct MessageReceive mr = InitMessageReceive(); uint8_t msgid = 0xAA; // MSGID ist ein Steuerzeichen, muss escapet werden uint8_t payload[] = {0x42}; size_t payload_len = sizeof(payload); uint8_t checksum_byte_to_send = calculate_payload_checksum_byte(msgid, payload, payload_len); uint8_t full_message[] = {StartByte, EscapeByte, msgid, // Escapete MSGID (0xBB 0xAA) 0x42, // Payload checksum_byte_to_send, EndByte}; for (uint8_t i = 0; i < sizeof(full_message); ++i) { parse_byte(&mr, full_message[i]); } TEST_ASSERT_TRUE(message_received_flag); TEST_ASSERT_FALSE(message_fail_flag); TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); TEST_ASSERT_EQUAL_UINT8(payload_len, received_payload_len); TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, received_payload, payload_len); TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); TEST_ASSERT_EQUAL_UINT8(NoError, mr.error); } // Test 5: Escapetes Payload-Byte (z.B. ein StartByte im Payload) void test_5_escaped_payload_byte(void) { struct MessageReceive mr = InitMessageReceive(); uint8_t msgid = 0x05; uint8_t payload[] = { 0x11, StartByte}; // StartByte (0xAA) im Payload, muss escapet werden size_t payload_len = sizeof(payload); uint8_t checksum_byte_to_send = calculate_payload_checksum_byte(msgid, payload, payload_len); uint8_t full_message[] = { StartByte, msgid, 0x11, EscapeByte, StartByte, // Escaptes StartByte (0xBB 0xAA) im Payload checksum_byte_to_send, EndByte}; for (uint8_t i = 0; i < sizeof(full_message); ++i) { parse_byte(&mr, full_message[i]); } TEST_ASSERT_TRUE(message_received_flag); TEST_ASSERT_FALSE(message_fail_flag); TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); TEST_ASSERT_EQUAL_UINT8(payload_len, received_payload_len); TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, received_payload, payload_len); TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); TEST_ASSERT_EQUAL_UINT8(NoError, mr.error); } // Test 6: Escapetes Checksummen-Byte (z.B. ein EndByte als Checksumme) void test_6_escaped_checksum_byte(void) { struct MessageReceive mr = InitMessageReceive(); uint8_t msgid = 0x01; uint8_t payload[] = { 0xCD}; // payload[0] ^ msgid = 0xCD ^ 0x01 = 0xCC (EndByte) size_t payload_len = sizeof(payload); uint8_t checksum_byte_to_send = calculate_payload_checksum_byte( msgid, payload, payload_len); // Dies ist 0xCC uint8_t full_message[] = { StartByte, msgid, payload[0], // Payload EscapeByte, checksum_byte_to_send, // Escaptes 0xCC als Checksummen-Byte (0xBB 0xCC) EndByte}; for (uint8_t i = 0; i < sizeof(full_message); ++i) { parse_byte(&mr, full_message[i]); } TEST_ASSERT_TRUE(message_received_flag); TEST_ASSERT_FALSE(message_fail_flag); TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); TEST_ASSERT_EQUAL_UINT8(payload_len, received_payload_len); TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, received_payload, payload_len); TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); TEST_ASSERT_EQUAL_UINT8(NoError, mr.error); } // Test 7: Nachricht zu lang (Pufferüberlauf) void test_7_message_too_long(void) { struct MessageReceive mr = InitMessageReceive(); // mr.max_total_content_length wird auf // MAX_TOTAL_CONTENT_LENGTH gesetzt uint8_t msgid = 0x07; // Dieser Payload ist absichtlich 1 Byte zu lang für // MAX_MESSAGE_PAYLOAD_LENGTH. D.h., der gesamte Inhalt für mr.message[] // (Payload + Checksumme) ist MAX_TOTAL_CONTENT_LENGTH + 1 Bytes lang. Dadurch // wird der Puffer definitiv überlaufen. uint8_t oversized_data_for_buffer[MAX_TOTAL_CONTENT_LENGTH + 1]; // Ein Byte zu viel für mr.message[] for (size_t i = 0; i < sizeof(oversized_data_for_buffer); ++i) { oversized_data_for_buffer[i] = (uint8_t)(i + 1); // Beliebige Daten } // Wir brauchen eine korrekte Checksumme, auch wenn die Nachricht zu lang ist, // da der Sender diese theoretisch senden würde. // Die Berechnung erfolgt über die tatsächlich gesendeten Payload-Daten (die // zu lang sind). uint8_t checksum_byte_for_oversized_msg = calculate_payload_checksum_byte( msgid, oversized_data_for_buffer, MAX_MESSAGE_PAYLOAD_LENGTH + 1); // Simuliere den Versand der Nachricht Byte für Byte parse_byte(&mr, StartByte); // mr.state = GetMessageType parse_byte(&mr, msgid); // mr.state = InPayload, mr.messageid = 0x07 // Sende MAX_TOTAL_CONTENT_LENGTH Bytes, die den Puffer `mr.message` // vollständig füllen. Index läuft von 0 bis MAX_TOTAL_CONTENT_LENGTH-1. for (size_t i = 0; i < MAX_TOTAL_CONTENT_LENGTH; ++i) { // Hier schicken wir die ersten MAX_TOTAL_CONTENT_LENGTH Bytes des // übergroßen Payloads (inklusive dem eigentlichen Checksummen-Byte an // Position MAX_MESSAGE_PAYLOAD_LENGTH). Das wird den Puffer anfüllen, aber // noch keinen Overflow melden. parse_byte(&mr, oversized_data_for_buffer[i]); } // An diesem Punkt sollte mr.index = MAX_TOTAL_CONTENT_LENGTH sein. // Der Puffer `mr.message` ist jetzt voll. TEST_ASSERT_EQUAL_UINT8(MAX_TOTAL_CONTENT_LENGTH, mr.index); // Der Puffer ist genau gefüllt. TEST_ASSERT_EQUAL_UINT8(InPayload, mr.state); // Noch im Payload-Zustand. // Das nächste Byte (das letzte Byte von oversized_data_for_buffer) // wird den Overflow auslösen, da mr->index dann mr->max_total_content_length // überschreitet. parse_byte(&mr, oversized_data_for_buffer[MAX_TOTAL_CONTENT_LENGTH]); TEST_ASSERT_FALSE(message_received_flag); TEST_ASSERT_TRUE(message_fail_flag); TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); // MSGID ist korrekt gesetzt // received_payload_len sollte die max. Puffergröße sein, bis der Fehler // auftrat TEST_ASSERT_EQUAL_UINT8(MAX_TOTAL_CONTENT_LENGTH, received_payload_len); TEST_ASSERT_EQUAL_UINT8(MessageToLong, received_error); TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); TEST_ASSERT_EQUAL_UINT8(MessageToLong, mr.error); } // Test 8: Unerwartetes StartByte mitten im Frame void test_8_unexpected_start_byte_in_payload(void) { struct MessageReceive mr = InitMessageReceive(); uint8_t msgid = 0x08; uint8_t full_message[] = { StartByte, msgid, 0x10, // Teil des Payloads StartByte, // Unerwartetes StartByte mitten im Payload 0x20, // Dies würde danach kommen 0x00, // Dummy-Checksumme EndByte // Dummy-EndByte }; parse_byte(&mr, full_message[0]); // StartByte parse_byte(&mr, full_message[1]); // MSGID parse_byte(&mr, full_message[2]); // Payload 0x10 // Hier kommt das unerwartete StartByte, sollte den Fehler auslösen und // resetten parse_byte(&mr, full_message[3]); TEST_ASSERT_FALSE(message_received_flag); TEST_ASSERT_TRUE(message_fail_flag); TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); // received_payload_len sollte die Länge der Daten sein, die vor dem Fehler // empfangen wurden. TEST_ASSERT_EQUAL_UINT8(1, received_payload_len); // Nur 0x10 empfangen TEST_ASSERT_EQUAL_UINT8_ARRAY(((uint8_t[]){0x10}), received_payload, 1); TEST_ASSERT_EQUAL_UINT8(UnexpectedCommandByte, received_error); TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); TEST_ASSERT_EQUAL_UINT8(UnexpectedCommandByte, mr.error); } // Test 9: Unerwartetes EndByte an der Position der MSGID void test_9_unexpected_end_byte_at_msgid(void) { struct MessageReceive mr = InitMessageReceive(); uint8_t full_message[] = {StartByte, EndByte, // EndByte anstelle von MSGID 0x01, 0x02, 0x03, 0x04, EndByte}; parse_byte(&mr, full_message[0]); // StartByte parse_byte(&mr, full_message[1]); // EndByte anstelle MSGID TEST_ASSERT_FALSE(message_received_flag); TEST_ASSERT_TRUE(message_fail_flag); TEST_ASSERT_EQUAL_UINT8( 0x00, received_msgid); // MSGID ist noch 0, da keine empfangen TEST_ASSERT_EQUAL_UINT8(0, received_payload_len); // Noch kein Payload TEST_ASSERT_EQUAL_UINT8(UnexpectedCommandByte, received_error); TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); TEST_ASSERT_EQUAL_UINT8(UnexpectedCommandByte, mr.error); } // Test 10: Kein StartByte zu Beginn der Sequenz (Parser sollte ignorieren) void test_10_no_startbyte_at_beginning_ignored(void) { struct MessageReceive mr = InitMessageReceive(); uint8_t msg[] = {0x01, 0x02, 0x03, 0x04}; // Beginnt nicht mit StartByte for (uint8_t i = 0; i < sizeof(msg); ++i) { parse_byte(&mr, msg[i]); } TEST_ASSERT_FALSE(message_received_flag); TEST_ASSERT_FALSE( message_fail_flag); // Sollte keinen Fehler melden, nur ignorieren TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); // Sollte im Wartezustand bleiben TEST_ASSERT_EQUAL_UINT8(0, mr.index); // Index sollte 0 bleiben TEST_ASSERT_EQUAL_UINT8(NoError, mr.error); // Kein Fehler gemeldet } // Test 11: Ungültige Länge (z.B. EndByte kommt zu früh, ohne Checksumme) // Angenommen, das Protokoll erwartet immer mindestens MSGID + Checksumme. // Ein leeres Payload ist OK (MSGID + Checksumme + EndByte), aber nur MSGID + // EndByte ist Fehler. void test_11_frame_too_short_no_checksum(void) { struct MessageReceive mr = InitMessageReceive(); uint8_t msgid = 0x09; uint8_t full_message[] = { StartByte, msgid, EndByte // Kein Payload, keine Checksumme }; for (uint8_t i = 0; i < sizeof(full_message); ++i) { parse_byte(&mr, full_message[i]); } TEST_ASSERT_FALSE(message_received_flag); TEST_ASSERT_TRUE(message_fail_flag); TEST_ASSERT_EQUAL_UINT8(msgid, received_msgid); // MSGID ist bekannt TEST_ASSERT_EQUAL_UINT8(0, received_payload_len); // Payload-Puffer ist leer TEST_ASSERT_EQUAL_UINT8(WrongCheckSum, received_error); // Checksumme kann nicht 0x00 sein TEST_ASSERT_EQUAL_UINT8(WaitingForStartByte, mr.state); TEST_ASSERT_EQUAL_UINT8(WrongCheckSum, mr.error); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_1_valid_message_parses_correctly); RUN_TEST(test_2_invalid_checksum_resets_state); RUN_TEST(test_3_zero_length_message); RUN_TEST(test_4_escaped_message_id); RUN_TEST(test_5_escaped_payload_byte); RUN_TEST(test_6_escaped_checksum_byte); RUN_TEST(test_7_message_too_long); RUN_TEST(test_8_unexpected_start_byte_in_payload); RUN_TEST(test_9_unexpected_end_byte_at_msgid); RUN_TEST(test_10_no_startbyte_at_beginning_ignored); RUN_TEST(test_11_frame_too_short_no_checksum); return UNITY_END(); }