#include "Unity/src/unity.h" #include "message_builder.h" // Stellt sicher, dass deine Header-Datei message_parser.h die Definitionen für build_message, StartByte, EscapeByte, EndByte, MAX_MESSAGE_PAYLOAD_LENGTH und Fehlercodes enthält #include #include // Für memcpy, memset // --- Globale Konstanten (Annahmen aus vorheriger Konversation) --- // Wenn diese in message_parser.h nicht definiert sind, musst du sie hier // definieren: #define StartByte 0xAA #define EscapeByte 0xBB #define EndByte // 0xCC // #define MAX_MESSAGE_PAYLOAD_LENGTH 250 // Beispiel: Max. Payload-Länge // Fehlercodes für den Builder (sollten mit deiner build_message Implementierung // übereinstimmen) #define BUILD_ERROR_BUFFER_TOO_SMALL_INITIAL_CHECK -1 #define // BUILD_ERROR_BUFFER_OVERFLOW -2 #define PayloadBiggerThenBuffer -3 // --- UNITY SETUP/TEARDOWN --- void setUp(void) { // Nichts Besonderes für den Builder-Test zu resetten } void tearDown(void) {} // optional static bool needs_stuffing_byte(uint8_t byte) { return (byte == StartByte || byte == EscapeByte || byte == EndByte); } // --- 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 } // --- Hilfsfunktion zum Vergleichen von Hex-Arrays und Debug-Ausgabe --- void TEST_ASSERT_EQUAL_UINT8_ARRAY_HEX(const uint8_t *expected, const uint8_t *actual, size_t len) { char expected_str[len * 3 + 1]; char actual_str[len * 3 + 1]; int offset_exp = 0; int offset_act = 0; for (size_t i = 0; i < len; ++i) { offset_exp += snprintf(expected_str + offset_exp, sizeof(expected_str) - offset_exp, "%02X ", expected[i]); offset_act += snprintf(actual_str + offset_act, sizeof(actual_str) - offset_act, "%02X ", actual[i]); } // Stelle sicher, dass die Strings nullterminiert sind, falls der Puffer genau // gefüllt wurde if (offset_exp > 0) expected_str[offset_exp - 1] = '\0'; else expected_str[0] = '\0'; // Remove last space, null-terminate if (offset_act > 0) actual_str[offset_act - 1] = '\0'; else actual_str[0] = '\0'; printf("\n"); // Neue Zeile für bessere Lesbarkeit printf(" Expected: %s\n", expected_str); printf(" Actual: %s\n", actual_str); TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, len); } // --- TESTFÄLLE FÜR build_message --- // Test 1: Gültige Nachricht ohne Escaping void test_builder_1_basic_message_no_escaping(void) { uint8_t msgid = 0x01; uint8_t payload[] = {0x10, 0x20, 0x30, 0x40}; size_t payload_len = sizeof(payload); uint8_t output_buffer[64]; // Ausreichend großer Puffer size_t output_buffer_size = sizeof(output_buffer); // Erwarteter Checksummen-Wert uint8_t expected_checksum = calculate_payload_checksum_byte(msgid, payload, payload_len); // Erwartete fertige Nachricht uint8_t expected_message[] = { StartByte, msgid, 0x10, 0x20, 0x30, 0x40, // Payload expected_checksum, EndByte}; size_t expected_len = sizeof(expected_message); int actual_len = build_message(msgid, payload, payload_len, output_buffer, output_buffer_size); TEST_ASSERT_EQUAL_INT(expected_len, actual_len); TEST_ASSERT_EQUAL_UINT8_ARRAY_HEX(expected_message, output_buffer, actual_len); } // Test 2: Gültige Nachricht mit leerem Payload void test_builder_2_empty_payload(void) { uint8_t msgid = 0x02; uint8_t payload[] = {}; size_t payload_len = sizeof(payload); uint8_t output_buffer[64]; size_t output_buffer_size = sizeof(output_buffer); uint8_t expected_checksum = calculate_payload_checksum_byte(msgid, payload, payload_len); uint8_t expected_message[] = {StartByte, msgid, expected_checksum, EndByte}; size_t expected_len = sizeof(expected_message); int actual_len = build_message(msgid, payload, payload_len, output_buffer, output_buffer_size); TEST_ASSERT_EQUAL_INT(expected_len, actual_len); TEST_ASSERT_EQUAL_UINT8_ARRAY_HEX(expected_message, output_buffer, actual_len); } // Test 3: MSGID muss escapet werden (StartByte als MSGID) void test_builder_3_escaped_msgid(void) { uint8_t msgid = StartByte; // MSGID = 0xAA uint8_t payload[] = {0x11, 0x22}; size_t payload_len = sizeof(payload); uint8_t output_buffer[64]; size_t output_buffer_size = sizeof(output_buffer); uint8_t expected_checksum = calculate_payload_checksum_byte(msgid, payload, payload_len); uint8_t expected_message[] = {StartByte, EscapeByte, StartByte, // Escaped MSGID 0x11, 0x22, // Payload expected_checksum, EndByte}; size_t expected_len = sizeof(expected_message); int actual_len = build_message(msgid, payload, payload_len, output_buffer, output_buffer_size); TEST_ASSERT_EQUAL_INT(expected_len, actual_len); TEST_ASSERT_EQUAL_UINT8_ARRAY_HEX(expected_message, output_buffer, actual_len); } // Test 4: Payload-Byte muss escapet werden (EndByte im Payload) void test_builder_4_escaped_payload_byte(void) { uint8_t msgid = 0x04; uint8_t payload[] = {0x01, EndByte, 0x03}; // EndByte = 0xCC im Payload size_t payload_len = sizeof(payload); uint8_t output_buffer[64]; size_t output_buffer_size = sizeof(output_buffer); uint8_t expected_checksum = calculate_payload_checksum_byte(msgid, payload, payload_len); uint8_t expected_message[] = {StartByte, msgid, 0x01, EscapeByte, EndByte, // Escaped payload byte 0x03, expected_checksum, EndByte}; size_t expected_len = sizeof(expected_message); int actual_len = build_message(msgid, payload, payload_len, output_buffer, output_buffer_size); TEST_ASSERT_EQUAL_INT(expected_len, actual_len); TEST_ASSERT_EQUAL_UINT8_ARRAY_HEX(expected_message, output_buffer, actual_len); } // Test 5: Checksummen-Byte muss escapet werden (EscapeByte als Checksumme) void test_builder_5_escaped_checksum_byte(void) { uint8_t msgid = 0x05; // Payload so wählen, dass msgid ^ payload[0] = EscapeByte (0xBB) uint8_t payload[] = {msgid ^ EscapeByte}; // Payload ist 0x05 ^ 0xBB = 0xBE size_t payload_len = sizeof(payload); uint8_t output_buffer[64]; size_t output_buffer_size = sizeof(output_buffer); uint8_t expected_checksum = calculate_payload_checksum_byte(msgid, payload, payload_len); TEST_ASSERT_EQUAL_UINT8(EscapeByte, expected_checksum); // Sanity check uint8_t expected_message[] = {StartByte, msgid, payload[0], EscapeByte, expected_checksum, // Escaped checksum byte EndByte}; size_t expected_len = sizeof(expected_message); int actual_len = build_message(msgid, payload, payload_len, output_buffer, output_buffer_size); TEST_ASSERT_EQUAL_UINT8_ARRAY_HEX(expected_message, output_buffer, actual_len); TEST_ASSERT_EQUAL_INT(expected_len, actual_len); } // Test 6: Mehrere Escapings in einer Nachricht void test_builder_6_multiple_escapings(void) { uint8_t msgid = StartByte; // 0xAA uint8_t payload[] = {EscapeByte, 0x01, EndByte, StartByte, 0x02}; size_t payload_len = sizeof(payload); uint8_t output_buffer[64]; size_t output_buffer_size = sizeof(output_buffer); uint8_t expected_checksum = calculate_payload_checksum_byte(msgid, payload, payload_len); // Angenommen, die berechnete Checksumme selbst ist KEIN Steuerzeichen, // sonst müsste sie auch escapet werden. // Wenn doch, würde expected_message noch ein EscapeByte vor der Checksumme // bekommen. uint8_t expected_message[] = { StartByte, EscapeByte, StartByte, // Escaped MSGID EscapeByte, EscapeByte, // Escaped payload[0] 0x01, EscapeByte, EndByte, // Escaped payload[2] EscapeByte, StartByte, // Escaped payload[3] 0x02, expected_checksum, EndByte}; size_t expected_len = sizeof(expected_message); int actual_len = build_message(msgid, payload, payload_len, output_buffer, output_buffer_size); TEST_ASSERT_EQUAL_INT(expected_len, actual_len); TEST_ASSERT_EQUAL_UINT8_ARRAY_HEX(expected_message, output_buffer, actual_len); } // Test 7: Puffer zu klein - initiale Prüfung (Payload ist zu lang für den // Puffer) void test_builder_7_buffer_too_small_initial(void) { uint8_t msgid = 0x07; // Payload, das auch im besten Fall (ohne Stuffing) nicht in einen // 10-Byte-Puffer passt. Minimale Länge wäre 1+1+5+1+1 = 9 Bytes (Start, ID, // Payload, CRC, End) uint8_t payload[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; size_t payload_len = sizeof(payload); // 8 Bytes uint8_t output_buffer[10]; // Ein Puffer, der zu klein ist für 8 Payload-Bytes // + Rahmung + CRC size_t output_buffer_size = sizeof(output_buffer); // 10 Bytes int actual_len = build_message(msgid, payload, payload_len, output_buffer, output_buffer_size); // Die Berechnung für PayloadBiggerThenBuffer sollte hier greifen. // payload_len * 2 + 5 (worst case) = 8*2+5 = 21. 21 ist > 10. TEST_ASSERT_EQUAL_INT(PayloadBiggerThenBuffer, actual_len); } // Test 8: Puffer zu klein - Überlauf beim Schreiben (knapper Puffer, z.B. bei // Stuffing) void test_builder_8_buffer_overflow_during_build(void) { uint8_t msgid = 0x08; // Payload so wählen, dass Stuffing stattfindet uint8_t payload[] = {0x01, StartByte, 0x02}; // StartByte im Payload size_t payload_len = sizeof(payload); // 3 Bytes // Minimaler Puffer für diese Nachricht OHNE Stuffing: // Start (1) + MSGID (1) + Payload (3) + CRC (1) + End (1) = 7 Bytes // Mit Stuffing für payload[1] (StartByte) wird es: // 1 (Start) + 1 (MSGID) + 1 (0x01) + 2 (Escape+StartByte) + 1 (0x02) + 1 // (CRC) + 1 (End) = 8 Bytes uint8_t output_buffer[7]; // Puffer ist 1 Byte zu klein für die gestuffte // Nachricht size_t output_buffer_size = sizeof(output_buffer); int actual_len = build_message(msgid, payload, payload_len, output_buffer, output_buffer_size); TEST_ASSERT_EQUAL_INT(BufferOverFlow, actual_len); } // Test 9: Puffer genau groß genug (Randfall) void test_builder_9_buffer_just_enough(void) { uint8_t msgid = 0x09; uint8_t payload[] = {0x01, 0x02, 0x03, 0x04}; // 4 Bytes size_t payload_len = sizeof(payload); uint8_t expected_checksum = calculate_payload_checksum_byte(msgid, payload, payload_len); uint8_t expected_message[] = { StartByte, msgid, 0x01, 0x02, 0x03, 0x04, expected_checksum, EndByte}; size_t expected_len = sizeof(expected_message); // 1 + 1 + 4 + 1 + 1 = 8 Bytes uint8_t output_buffer[expected_len]; // Puffer GENAU der erwarteten Größe size_t output_buffer_size = sizeof(output_buffer); int actual_len = build_message(msgid, payload, payload_len, output_buffer, output_buffer_size); TEST_ASSERT_EQUAL_INT(expected_len, actual_len); TEST_ASSERT_EQUAL_UINT8_ARRAY_HEX(expected_message, output_buffer, actual_len); } // Test 10: Maximale Payload-Länge ohne Stuffing void test_builder_10_max_payload_no_stuffing(void) { uint8_t msgid = 0x10; uint8_t payload[MAX_MESSAGE_PAYLOAD_LENGTH]; for (size_t i = 0; i < MAX_MESSAGE_PAYLOAD_LENGTH; ++i) { // Sicherstellen, dass keine Steuerzeichen dabei sind payload[i] = (uint8_t)(i % 0xF0 + 0x01); // Vermeidet 0xAA, 0xBB, 0xCC } size_t payload_len = MAX_MESSAGE_PAYLOAD_LENGTH; // Geschätzte maximale Puffergröße für worst-case (alle Bytes gestuffed) // 1 (Start) + 1 (MSGID) + MAX_PAYLOAD_LEN*2 (Payload worst-case) + 1 (CRC) + // 1 (End) // + 2 (potential stuffing for MSGID/CRC) = 2*MAX_MESSAGE_PAYLOAD_LENGTH + 5 // Aber da wir hier KEIN Stuffing haben, ist es einfacher: 1 + 1 + // MAX_PAYLOAD_LEN + 1 + 1 size_t max_buffer_needed = 1 + 1 + MAX_MESSAGE_PAYLOAD_LENGTH + 1 + 1; uint8_t output_buffer[max_buffer_needed + 10]; // Etwas Puffer extra size_t output_buffer_size = sizeof(output_buffer); uint8_t expected_checksum = calculate_payload_checksum_byte(msgid, payload, payload_len); // Erstelle erwartete Nachricht manuell oder dynamisch uint8_t expected_message[max_buffer_needed]; size_t exp_idx = 0; expected_message[exp_idx++] = StartByte; expected_message[exp_idx++] = msgid; memcpy(&expected_message[exp_idx], payload, payload_len); exp_idx += payload_len; expected_message[exp_idx++] = expected_checksum; expected_message[exp_idx++] = EndByte; size_t expected_len = exp_idx; int actual_len = build_message(msgid, payload, payload_len, output_buffer, output_buffer_size); TEST_ASSERT_EQUAL_INT(expected_len, actual_len); TEST_ASSERT_EQUAL_UINT8_ARRAY_HEX(expected_message, output_buffer, actual_len); } // Test 11: Maximale Payload-Länge mit Stuffing (Worst-Case Szenario) void test_builder_11_max_payload_with_stuffing(void) { uint8_t msgid = StartByte; // MSGID muss gestuffed werden uint8_t payload[MAX_MESSAGE_PAYLOAD_LENGTH]; for (size_t i = 0; i < MAX_MESSAGE_PAYLOAD_LENGTH; ++i) { // Alle Bytes sind Steuerzeichen, müssen gestuffed werden payload[i] = (i % 3 == 0) ? StartByte : ((i % 3 == 1) ? EscapeByte : EndByte); } size_t payload_len = MAX_MESSAGE_PAYLOAD_LENGTH; // Worst-Case Puffergröße: // 1 (Start) + 2 (Escaped MSGID) + MAX_PAYLOAD_LEN * 2 (Escaped Payload) + 2 // (Escaped CRC) + 1 (End) size_t max_buffer_needed_worst_case = 1 + 2 + (MAX_MESSAGE_PAYLOAD_LENGTH * 2) + 2 + 1; uint8_t output_buffer[max_buffer_needed_worst_case + 10]; // Etwas Puffer extra size_t output_buffer_size = sizeof(output_buffer); // Build the expected message manually to account for all stuffing uint8_t expected_message[max_buffer_needed_worst_case]; size_t exp_idx = 0; // StartByte expected_message[exp_idx++] = StartByte; // MSGID (escaped) expected_message[exp_idx++] = EscapeByte; expected_message[exp_idx++] = msgid; // Payload (all bytes escaped) for (size_t i = 0; i < payload_len; ++i) { expected_message[exp_idx++] = EscapeByte; expected_message[exp_idx++] = payload[i]; } // Checksumme (kann auch escapet sein) uint8_t expected_checksum = calculate_payload_checksum_byte(msgid, payload, payload_len); if (needs_stuffing_byte(expected_checksum)) { expected_message[exp_idx++] = EscapeByte; } expected_message[exp_idx++] = expected_checksum; // EndByte expected_message[exp_idx++] = EndByte; size_t expected_len = exp_idx; int actual_len = build_message(msgid, payload, payload_len, output_buffer, output_buffer_size); TEST_ASSERT_EQUAL_INT(expected_len, actual_len); TEST_ASSERT_EQUAL_UINT8_ARRAY_HEX(expected_message, output_buffer, actual_len); } // --- MAIN TEST RUNNER --- int main(void) { UNITY_BEGIN(); // Run Builder Tests RUN_TEST(test_builder_1_basic_message_no_escaping); RUN_TEST(test_builder_2_empty_payload); RUN_TEST(test_builder_3_escaped_msgid); RUN_TEST(test_builder_4_escaped_payload_byte); RUN_TEST(test_builder_5_escaped_checksum_byte); RUN_TEST(test_builder_6_multiple_escapings); RUN_TEST(test_builder_7_buffer_too_small_initial); RUN_TEST(test_builder_8_buffer_overflow_during_build); RUN_TEST(test_builder_9_buffer_just_enough); RUN_TEST(test_builder_10_max_payload_no_stuffing); RUN_TEST(test_builder_11_max_payload_with_stuffing); return UNITY_END(); }