esp_alox/tests/test_parser.c

453 lines
17 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "Unity/src/unity.h"
#include "message_parser.h" // Stellt sicher, dass deine Header-Datei message_parser.h korrekt ist
#include <stdint.h>
#include <string.h> // 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();
}