diff --git a/.build/build.options.json b/.build/build.options.json new file mode 100644 index 0000000000000000000000000000000000000000..f213921ea7bd753452aa74a75cd6ff29cf8820aa --- /dev/null +++ b/.build/build.options.json @@ -0,0 +1,12 @@ +{ + "additionalFiles": "..,..", + "builtInLibrariesFolders": "/Applications/Arduino.app/Contents/Java/libraries", + "builtInToolsFolders": "/Applications/Arduino.app/Contents/Java/tools-builder,/Applications/Arduino.app/Contents/Java/hardware/tools/avr,/Users/to/Library/Arduino15/packages", + "compiler.optimization_flags": "", + "customBuildProperties": "build.path=/Users/to/sciebo/TT_Arduino/2_projects/serialTable/.build,build.warn_data_percentage=75,runtime.tools.arduinoOTA.path=/Users/to/Library/Arduino15/packages/arduino/tools/arduinoOTA/1.3.0,runtime.tools.arduinoOTA-1.3.0.path=/Users/to/Library/Arduino15/packages/arduino/tools/arduinoOTA/1.3.0,runtime.tools.avr-gcc.path=/Users/to/Library/Arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7,runtime.tools.avr-gcc-7.3.0-atmel3.6.1-arduino7.path=/Users/to/Library/Arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7,runtime.tools.avrdude.path=/Users/to/Library/Arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17,runtime.tools.avrdude-6.3.0-arduino17.path=/Users/to/Library/Arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17", + "fqbn": "arduino:avr:mega:cpu=atmega2560", + "hardwareFolders": "/Applications/Arduino.app/Contents/Java/hardware,/Users/to/Library/Arduino15/packages", + "otherLibrariesFolders": "/Users/to/sciebo/TT_Arduino/libraries", + "runtime.ide.version": "10819", + "sketchLocation": "/Users/to/sciebo/TT_Arduino/2_projects/serialTable/serialTable.ino" +} \ No newline at end of file diff --git a/.build/core/core.a b/.build/core/core.a new file mode 100644 index 0000000000000000000000000000000000000000..99c9279bdecf1eb3ad99f7f5311871bc732536b1 Binary files /dev/null and b/.build/core/core.a differ diff --git a/.build/includes.cache b/.build/includes.cache new file mode 100644 index 0000000000000000000000000000000000000000..4b6f9d106d58bc17206fc08fa4719dafc4abee8b --- /dev/null +++ b/.build/includes.cache @@ -0,0 +1,77 @@ +[ + { + "Sourcefile": null, + "Include": "", + "Includepath": "/Users/to/Library/Arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino" + }, + { + "Sourcefile": null, + "Include": "", + "Includepath": "/Users/to/Library/Arduino15/packages/arduino/hardware/avr/1.8.6/variants/mega" + }, + { + "Sourcefile": "/Users/to/sciebo/TT_Arduino/2_projects/serialTable/.build/sketch/serialTable.ino.cpp", + "Include": "AUnit.h", + "Includepath": "/Users/to/sciebo/TT_Arduino/libraries/AUnit/src" + }, + { + "Sourcefile": "/Users/to/sciebo/TT_Arduino/2_projects/serialTable/.build/sketch/serialTable.ino.cpp", + "Include": "", + "Includepath": null + }, + { + "Sourcefile": "/Users/to/sciebo/TT_Arduino/libraries/AUnit/src/aunit/Assertion.cpp", + "Include": "", + "Includepath": null + }, + { + "Sourcefile": "/Users/to/sciebo/TT_Arduino/libraries/AUnit/src/aunit/Compare.cpp", + "Include": "", + "Includepath": null + }, + { + "Sourcefile": "/Users/to/sciebo/TT_Arduino/libraries/AUnit/src/aunit/FCString.cpp", + "Include": "", + "Includepath": null + }, + { + "Sourcefile": "/Users/to/sciebo/TT_Arduino/libraries/AUnit/src/aunit/MetaAssertion.cpp", + "Include": "", + "Includepath": null + }, + { + "Sourcefile": "/Users/to/sciebo/TT_Arduino/libraries/AUnit/src/aunit/Printer.cpp", + "Include": "", + "Includepath": null + }, + { + "Sourcefile": "/Users/to/sciebo/TT_Arduino/libraries/AUnit/src/aunit/Test.cpp", + "Include": "", + "Includepath": null + }, + { + "Sourcefile": "/Users/to/sciebo/TT_Arduino/libraries/AUnit/src/aunit/TestAgain.cpp", + "Include": "", + "Includepath": null + }, + { + "Sourcefile": "/Users/to/sciebo/TT_Arduino/libraries/AUnit/src/aunit/TestOnce.cpp", + "Include": "", + "Includepath": null + }, + { + "Sourcefile": "/Users/to/sciebo/TT_Arduino/libraries/AUnit/src/aunit/TestRunner.cpp", + "Include": "", + "Includepath": null + }, + { + "Sourcefile": "/Users/to/sciebo/TT_Arduino/libraries/AUnit/src/aunit/print64.cpp", + "Include": "", + "Includepath": null + }, + { + "Sourcefile": "/Users/to/sciebo/TT_Arduino/libraries/AUnit/src/aunit/string_util.cpp", + "Include": "", + "Includepath": null + } +] \ No newline at end of file diff --git a/.build/preproc/ctags_target_for_gcc_minus_e.cpp b/.build/preproc/ctags_target_for_gcc_minus_e.cpp new file mode 100644 index 0000000000000000000000000000000000000000..924d9e23a4649a66c587fc5ab346018e605291bc --- /dev/null +++ b/.build/preproc/ctags_target_for_gcc_minus_e.cpp @@ -0,0 +1,53 @@ +# 1 "/Users/to/sciebo/TT_Arduino/2_projects/serialTable/serialTable.ino" +# 2 "/Users/to/sciebo/TT_Arduino/2_projects/serialTable/serialTable.ino" 2 +# 3 "/Users/to/sciebo/TT_Arduino/2_projects/serialTable/serialTable.ino" 2 +# 4 "/Users/to/sciebo/TT_Arduino/2_projects/serialTable/serialTable.ino" 2 + +void setup() +{ + // Initialisieren der seriellen Verbindung + Serial.begin(115200); + + // Definieren der Kopfzeilen und Spaltenbreiten für 8 Spalten + const char *headers[] = {"Vorname", "Nachname", "Alter", "Stadt", "Beruf", "Unternehmen", "Hobby", "Bemerkung"}; + int columnWidths[] = {10, 15, 5, 10, 10, 15, 10, 20}; + + SerialTable table(headers, columnWidths, 8); + table.printHeader(); + + // Erstellen von 20 Zeilen mit unterschiedlichen Einträgen + char *rows[20][8] = { + {"Alice", "Wonderland", "30", "Köln", "Ingenieur", "FirmaX", "Schwimmen", "Keine"}, + {"Bob", "Builder\nJr", "35", "Berlin", "Architekt", "Architektur AG", "Bauen", "Sehr erfahren"}, + {"Charlie", "Brown", "28", "Hamburg", "Designer", "Die Design", "Zeichnen", "Mag Karikaturen"}, + {"David", "Tennant", "40", "München", "Schauspieler", "Freiberufler", "Lesen", "Doktor gewesen"}, + {"Emma", "Watson", "33", "Frankfurt", "Schauspielerin", "Independent", "Yoga", "Bekannt aus Filmen"}, + {"Frank", "Herbert", "45", "Stuttgart", "Autor", "Ein Verlag", "Schreiben", "Bücher geschrieben"}, + {"Grace", "Hopper", "50", "Berlin", "Programmiererin", "Tech Corp", "Informatik", "Pionierin"}, + {"Hugo", "Strange", "60", "Leipzig", "Psychiater", "Gesundheits GmbH", "Philosophie", "Ruhestand"}, + {"Irene", "Adler", "29", "Dresden", "Detektivin", "Privat", "Abenteuer", "Scharfsinnig"}, + {"John", "Doe", "49", "Bremen", "Buchhalter", "Kontorechner", "Radfahren", "Gewissenhaft"}, + {"Klara", "Kühn", "39", "Hannover", "Lehrerin", "Schule ABC", "Lesen", "Einfühlsam"}, + {"Lenny", "Small", "25", "Koblenz", "Musiker", "Orchester", "Gitarre", "Kreativ"}, + {"Mona", "Lisa", "33", "Siegen", "Malerin", "Kunsthaus", "Kunst", "Geheimnisvoll"}, + {"Nina", "Simone", "30", "Essen", "Sängerin", "Soul Musik", "Musik", "Talentiert"}, + {"Oliver", "Twist", "19", "Potsdam", "Student", "Uni XY", "Fußball", "Engagiert"}, + {"Pam", "Beesly", "32", "Wuppertal", "Sekretärin", "Dunder Mifflin", "Malen", "Freundlich"}, + {"Quinn", "Fabray", "26", "Magdeburg", "Designer", "Agentur XYZ", "Tanzen", "Kreativ"}, + {"Ron", "Swanson", "55", "Bielefeld", "Verwalter", "Rathaus", "Holzarbeiten", "Direkt"}, + {"Sophia", "Loren", "70", "Osnabrück", "Schauspielerin", "Independent", "Kochen", "Ikonisch"}, + {"Tom", "Hanson", "36", "Regensburg", "Polizist", "Polizei", "Jagen", "Schützend"}, + }; + + // Drucke die Zeilen + for (int i = 0; i < 20; ++i) + { + table.printRow(rows[i]); + } + +} + +void loop() +{ + //TestRunner::run(); +} diff --git a/.build/sketch/SerialTable.h b/.build/sketch/SerialTable.h new file mode 100644 index 0000000000000000000000000000000000000000..49057b62133c114faeb28f6d41379d1b99dac93e --- /dev/null +++ b/.build/sketch/SerialTable.h @@ -0,0 +1,354 @@ +#line 1 "/Users/to/sciebo/TT_Arduino/2_projects/serialTable/SerialTable.h" +#ifndef SERIALTABLE_H +#define SERIALTABLE_H + +#include <Arduino.h> + +class SerialTable +{ +public: + SerialTable(const char *headers[], const int columnWidths[], size_t size) + : headers(headers), columnWidths(columnWidths), size(size) {} + + // Diese Methode gibt den Header an die serielle Schnittstelle aus + void printHeader() + { + String header = buildHeader(); + Serial.print(header); + } + + String buildHeader() + { + return buildLine('-') + buildRow(const_cast<char **>(headers), '='); // Kopfzeile mit '=' + } + + void printRow(char **t_row) + { + String row = buildRow(t_row); + Serial.print(row); + } + + String buildRow(char **t_row, char line = '-') + { + String output; + char buffer[256]; + int maxLines = 1; // Maximale Anzahl Zeilen über alle Spalten + + // Erste Phase: Teile jede Zelle in Zeilen und finde maximale Zeilenzahl + for (size_t i = 0; i < size; ++i) + { + if (truncateText) + { + truncateToColumnWidth(t_row[i], columnWidths[i], buffer); + } + else + { + splitStringByLength(t_row[i], columnWidths[i], buffer); + } + + // Zähle Zeilen in dieser Zelle + int lineCount = 1; + for (size_t j = 0; j < displayLength(buffer); j++) + { + if (buffer[j] == '\n') + lineCount++; + } + if (lineCount > maxLines) + maxLines = lineCount; + } + + // Zweite Phase: Baue die Zeilen auf + for (int line = 0; line < maxLines; line++) + { + output += "|"; + + // Gehe durch jede Spalte + for (size_t col = 0; col < size; ++col) + { + if (truncateText) + { + truncateToColumnWidth(t_row[col], columnWidths[col], buffer); + } + else + { + splitStringByLength(t_row[col], columnWidths[col], buffer); + } + + // Finde die aktuelle Zeile für diese Spalte + char *currentLine = buffer; + int currentLineNum = 0; + char tempBuffer[256] = ""; + + while (currentLineNum < line) + { + char *newline = strchr(currentLine, '\n'); + if (newline == NULL) + { + currentLine = ""; + break; + } + currentLine = newline + 1; + currentLineNum++; + } + + // Extrahiere die Zeile bis zum nächsten \n oder Ende + int j = 0; + while (currentLine[j] && currentLine[j] != '\n') + { + tempBuffer[j] = currentLine[j]; + j++; + } + tempBuffer[j] = '\0'; + + // Berechne die tatsächliche Anzeigelänge + int printedLength = displayLength(tempBuffer); + + // Füge den Inhalt und die fehlenden Leerzeichen hinzu + output += tempBuffer; + int spacesToAdd = columnWidths[col] - printedLength; + for (int k = 0; k < spacesToAdd; k++) + { + output += " "; + } + output += "|"; + } + output += "\n"; + } + + // Füge die Trennlinie hinzu + output += buildLine(line); + return output; + } + + void setTruncateText(bool value) + { + truncateText = value; + } + +public: // private: + const char **headers; + const int *columnWidths; + size_t size; + bool truncateText = false; + + static int displayLength(const char *text) + { + int length = 0; + for (int i = 0; text[i] != '\0'; ++i) + { + if ((text[i] & 0xC0) != 0x80) + { // Überspringe UTF-8 Fortsetzungsbytes + length++; + } + } + return length; + } + + static void safeStringCopy(char *dest, const char *src, size_t bufferSize) + { + if (strlen(src) >= bufferSize) + { + strncpy(dest, src, bufferSize - 1); + dest[bufferSize - 1] = '\0'; // NULL-termination sichergestellt + } + else + { + strcpy(dest, src); + } + } + + static void replaceUmlauts(char *text) + { + // UTF-8 Codes für Umlaute (hex): ä(c3 a4) ö(c3 b6) ü(c3 bc) Ä(c3 84) Ö(c3 96) Ü(c3 9c) ß(c3 9f) + struct UmlautPair + { + unsigned char bytes[2]; // UTF-8 bytes + const char *replacement; + }; + + const UmlautPair pairs[] = { + {{0xc3, 0xa4}, "ae"}, // ä + {{0xc3, 0xb6}, "oe"}, // ö + {{0xc3, 0xbc}, "ue"}, // ü + {{0xc3, 0x84}, "Ae"}, // Ä + {{0xc3, 0x96}, "Oe"}, // Ö + {{0xc3, 0x9c}, "Ue"}, // Ü + {{0xc3, 0x9f}, "ss"} // ß + }; + const int numPairs = sizeof(pairs) / sizeof(pairs[0]); + + size_t maxOutputLength = strlen(text) * 2 + 1; + char *buffer = (char *)malloc(maxOutputLength); + if (!buffer) + return; + + int k = 0; // Position im Ausgabepuffer + int i = 0; // Position im Eingabetext + + while (text[i] != '\0') + { + bool replaced = false; + + // Prüfe auf UTF-8 Sequenz + if ((unsigned char)text[i] == 0xc3 && text[i + 1] != '\0') + { + for (int j = 0; j < numPairs; ++j) + { + if ((unsigned char)text[i] == pairs[j].bytes[0] && + (unsigned char)text[i + 1] == pairs[j].bytes[1]) + { + // Kopiere Ersetzung + const char *replacement = pairs[j].replacement; + for (int r = 0; replacement[r] != '\0' && k < maxOutputLength - 1; ++r) + { + buffer[k++] = replacement[r]; + } + replaced = true; + i += 2; // Überspringe beide UTF-8 Bytes + break; + } + } + } + + if (!replaced && k < maxOutputLength - 1) + { + buffer[k++] = text[i++]; + } + } + + buffer[k] = '\0'; + strcpy(text, buffer); + free(buffer); + } + + // Neue Version von splitStringByLength + static void splitStringByLength(const char *input, int L, char *output) + { + int len = strlen(input); + int outputIndex = 0; + int currentLength = 0; + + for (int i = 0; i < len; ++i) + { + // Kopiere das aktuelle Zeichen zu Ausgabe + unsigned char curr = input[i]; + output[outputIndex++] = curr; + + // UTF-8-Handling: Überspringe zusätzliche Bytes für ein Multibyte-Zeichen + if ((curr & 0x80) != 0) + { + // Wenn das höchste Bit gesetzt ist, haben wir ein Multibyte-Zeichen + while ((input[i + 1] & 0xC0) == 0x80) + { + output[outputIndex++] = input[++i]; + } + } + + currentLength++; + + // Wenn die maximale Länge erreicht ist und das nächste Zeichen kein newline ist + if (currentLength == L && i + 1 < len && input[i + 1] != '\n') + { + output[outputIndex++] = '\n'; + currentLength = 0; + } + } + output[outputIndex] = '\0'; // Beende den String + } + + void truncateToColumnWidth(const char *input, int width, char *output) + { + int len = strlen(input); + if (len > width) + { + width = (width > 3) ? width - 3 : width; + strncpy(output, input, width); + strcpy(output + width, "..."); + } + else + { + strcpy(output, input); + } + } + + size_t calculateMaxLines(char **t_row) + { + size_t maxLines = 0; + + for (size_t i = 0; i < size; ++i) + { + const char *text = t_row[i]; + size_t textLength = strlen(text); + size_t lines = 1; + + for (size_t j = 0; j < textLength; ++j) + { + if (text[j] == '\n') + { + ++lines; + } + } + + maxLines = max(maxLines, lines); + } + + return maxLines; + } + + const char *getRowSegment(const char *text, int width, size_t line, char *buffer) + { + size_t textLength = strlen(text); + size_t lineStart = 0; + size_t currentLine = 0; + buffer[0] = '\0'; + + for (size_t i = 0; i < textLength; ++i) + { + if (currentLine == line) + { + lineStart = i; + break; + } + if (text[i] == '\n' || (i > 0 && (i % width) == 0)) + { + ++currentLine; + } + } + + if (lineStart >= textLength || currentLine != line) + { + return buffer; + } + + size_t charCount = 0; + for (size_t i = lineStart; i < textLength && charCount < width; ++i) + { + if (text[i] == '\n') + { + break; + } + buffer[charCount++] = text[i]; + } + + buffer[charCount] = '\0'; + + return buffer; + } + + String buildLine(char lineChar) + { + String line; + for (size_t i = 0; i < size; ++i) + { + line += '+'; + for (int j = 0; j < columnWidths[i]; ++j) + { + line += lineChar; + } + } + line += "+\n"; + return line; + } +}; + +#endif // SERIALTABLE_H \ No newline at end of file diff --git a/.build/sketch/UnitTests.h b/.build/sketch/UnitTests.h new file mode 100644 index 0000000000000000000000000000000000000000..0a12d8ddeb70d4d08f61cff7b381dbc92e4e09c9 --- /dev/null +++ b/.build/sketch/UnitTests.h @@ -0,0 +1,343 @@ +#line 1 "/Users/to/sciebo/TT_Arduino/2_projects/serialTable/UnitTests.h" +#include <AUnit.h> +#include "SerialTable.h" +using namespace aunit; + +test(SplitStringNoNewlines) +{ + char input[] = "This is a test string"; + int length = 5; + char expectedOutput[] = "This \nis a \ntest \nstrin\ng"; + char actualOutput[256] = {0}; // Ausreichend großer Puffer + + SerialTable::splitStringByLength(input, length, actualOutput); + + // Überprüfung der Länge der Strings + assertEqual(strlen(expectedOutput), strlen(actualOutput)); +/* + // Zusätzliche Debug-Ausgabe, um Hex-Werte zu sehen + for (size_t i = 0; i < strlen(actualOutput); ++i) + { + Serial.print("Expected: "); + Serial.print((int)expectedOutput[i]); + Serial.print(", Actual: "); + Serial.println((int)actualOutput[i]); + } +*/ + assertEqual(String(expectedOutput), String(actualOutput)); +} + +// Einzelner Testfall 2: Eingabe mit existierenden Zeilenumbrüchen +test(SplitStringWithNewlines) +{ + char input[] = "This\nis\na\ntest"; + int length = 4; + char expectedOutput[] = "This\nis\na\ntest"; + char actualOutput[256] = {0}; + SerialTable::splitStringByLength(input, length, actualOutput); + + assertEqual(String(expectedOutput), String(actualOutput)); +} + +// Einzelner Testfall 3: Länge größer als Eingabetext +test(SplitStringLengthGreaterThanText) +{ + char input[] = "Short"; + int length = 10; + char expectedOutput[] = "Short"; + char actualOutput[256] = {0}; + SerialTable::splitStringByLength(input, length, actualOutput); + + assertEqual(String(expectedOutput), String(actualOutput)); +} + +// Einzelner Testfall 4: Genau passende Länge +test(SplitStringExactLength) +{ + char input[] = "Lengthy"; + int length = 7; + char expectedOutput[] = "Lengthy"; + char actualOutput[256] = {0}; + SerialTable::splitStringByLength(input, length, actualOutput); + assertEqual(String(expectedOutput), String(actualOutput)); +} + +// Einzelner Testfall 5: Länge von 1 +test(SplitStringLengthOfOne) +{ + char input[] = "One"; + int length = 1; + char expectedOutput[] = "O\nn\ne"; + char actualOutput[256] = {0}; + SerialTable::splitStringByLength(input, length, actualOutput); + assertEqual(String(expectedOutput), String(actualOutput)); +} + +// Einzelner Testfall 6: Leerer Eingabestring +test(SplitStringEmptyInput) +{ + char input[] = ""; + int length = 5; + char expectedOutput[] = ""; + char actualOutput[256] = {0}; + SerialTable::splitStringByLength(input, length, actualOutput); + assertEqual(String(expectedOutput), String(actualOutput)); +} + +// Erstelle eine Hilfsfunktion für replaceUmlauts-Tests +test(replaceUmlautsTest) +{ + char text1[] = "Köln"; + SerialTable::replaceUmlauts(text1); + assertEqual("Koeln", text1); + + char text2[] = "München"; + SerialTable::replaceUmlauts(text2); + assertEqual("Muenchen", text2); + + char text3[] = "Straße"; + SerialTable::replaceUmlauts(text3); + assertEqual("Strasse", text3); +} + +// Erstelle eine Hilfsfunktion für truncateTextTest +test(truncateTextTest) +{ + char buffer[20]; + SerialTable table(nullptr, nullptr, 0); + + // Fall: Text passt in die Spaltenbreite + strcpy(buffer, "Hello"); + table.truncateToColumnWidth(buffer, 10, buffer); + assertEqual("Hello", buffer); + + // Fall: Text wird abgeschnitten + strcpy(buffer, "HelloWorld!"); + table.truncateToColumnWidth(buffer, 5, buffer); + assertEqual("He...", buffer); +} + +test(buildHeaderTest1) +{ + // Definiere header mit char*-Zeigern + const char *headers[] = {"Name", "Age", "City"}; + const int columnWidths[] = {10, 5, 10}; + SerialTable table(headers, columnWidths, 3); + + // Verwende buildHeader, um den Header-String zu erhalten + String header = table.buildHeader(); + + // Gib den Header über Serial aus + Serial.print(header); + Serial.flush(); + + // Erwartete Ausgabe des Headers + String expectedHeader = + "+----------+-----+----------+\n" + "|Name |Age |City |\n" + "+==========+=====+==========+\n"; + + // Vergleiche den erzeugten Header mit dem erwarteten Header + assertEqual(expectedHeader, header); +} + +test(buildHeaderTest2) +{ + // Definiere header mit char*-Zeigern + const char *headers[] = {"NameName Name", "Age Age Age", "CityCityCity"}; + const int columnWidths[] = {10, 5, 10}; + SerialTable table(headers, columnWidths, 3); + + // Verwende buildHeader, um den Header-String zu erhalten + String header = table.buildHeader(); + + // Gib den Header über Serial aus + Serial.print(header); + Serial.flush(); + + // Erwartete Ausgabe des Headers + String expectedHeader = + "+----------+-----+----------+\n" + "|NameName N|Age A|CityCityCi|\n" + "|ame |ge Ag|ty |\n" + "| |e | |\n" + "+==========+=====+==========+\n"; + + // Vergleiche den erzeugten Header mit dem erwarteten Header + assertEqual(expectedHeader, header); +} + +test(buildHeaderTest3) +{ + // Definiere header mit char*-Zeigern + const char *headers[] = {"ä123", "ö123", "ü123"}; + const int columnWidths[] = {4, 4, 4}; + SerialTable table(headers, columnWidths, 3); + + // Verwende buildHeader, um den Header-String zu erhalten + String header = table.buildHeader(); + + // Gib den Header über Serial aus + Serial.print(header); + Serial.flush(); + + // Erwartete Ausgabe des Headers + String expectedHeader = + "+----+----+----+\n" + "|ä123|ö123|ü123|\n" + "+====+====+====+\n"; + + // Vergleiche den erzeugten Header mit dem erwarteten Header + assertEqual(expectedHeader, header); +} + +test(printHeaderTest) +{ + // Verwende char*-Array statt String-Array + const char *headers[] = {"Name", "Age", "City"}; + const int columnWidths[] = {10, 5, 10}; + SerialTable table(headers, columnWidths, 3); + + table.printHeader(); + Serial.flush(); + + // Placeholder assertion, Verwendung eines Serial Monitor Tools empfohlen + assertTrue(true); +} + +test(buildRowTest) +{ + const char *headers[] = {"Name", "Age", "City"}; + const int columnWidths[] = {10, 5, 10}; + SerialTable table(headers, columnWidths, 3); + + char *rows[] = {"Alice", "30", "Wonderland"}; + + // Verwende buildRow, um den Zeilen-String zu bekommen + String rowOutput = table.buildRow(rows); + + // Gib die Zeile über Serial aus + Serial.print(rowOutput); + Serial.flush(); + + // Erwartete Ausgabe der Zeile + String expectedRowOutput = + "|Alice |30 |Wonderland|\n" + "+----------+-----+----------+\n"; + + // Vergleiche den generierten Zeilen-String mit dem erwarteten Zeilen-String + assertEqual(expectedRowOutput, rowOutput); +} + +test(printRowWithMultipleLinesTest) +{ + const char *headers[] = {"Name", "Age", "City"}; + const int columnWidths[] = {10, 5, 5}; + SerialTable table(headers, columnWidths, 3); + + char *rows[] = {"Alice", "30", "Wonderland"}; + + // Verwende buildRow, um den Zeilen-String zu bekommen + String rowOutput = table.buildRow(rows); + + // Gib die Zeile über Serial aus + Serial.print(rowOutput); + Serial.flush(); + + // Erwartete Ausgabe der Zeile + String expectedRowOutput = + "|Alice |30 |Wonde|\n" + "| | |rland|\n" + "+----------+-----+-----+\n"; + + // Vergleiche den generierten Zeilen-String mit dem erwarteten Zeilen-String + assertEqual(expectedRowOutput, rowOutput); +} + +test(printRowWithLongTextTest) +{ + const char *headers[] = {"Name", "Age", "City"}; + const int columnWidths[] = {10, 5, 10}; + SerialTable table(headers, columnWidths, 3); + + char *rows[] = {"A very long name", "12345", "A very long city name"}; + + // Verwende buildRow, um den Zeilen-String zu bekommen + String rowOutput = table.buildRow(rows); + + // Gib die Zeile über Serial aus + Serial.print(rowOutput); + Serial.flush(); + + // Erwartete Ausgabe der Zeile + String expectedRowOutput = + "|A very lon|12345|A very lon|\n" + "|g name | |g city nam|\n" + "| | |e |\n" + "+----------+-----+----------+\n"; + + // Vergleiche den generierten Zeilen-String mit dem erwarteten Zeilen-String + assertEqual(expectedRowOutput, rowOutput); +} + +test(printEmptyRowTest) +{ + const char *headers[] = {"Name", "Age", "City"}; + const int columnWidths[] = {10, 5, 10}; + SerialTable table(headers, columnWidths, 3); + + char *rows[] = {"", "", ""}; + + // Verwende buildRow, um den Zeilen-String zu bekommen + String rowOutput = table.buildRow(rows); + + // Gib die Zeile über Serial aus + Serial.print(rowOutput); + Serial.flush(); + + // Erwartete Ausgabe der Zeile + String expectedRowOutput = + "| | | |\n" + "+----------+-----+----------+\n"; + + // Vergleiche den generierten Zeilen-String mit dem erwarteten Zeilen-String + assertEqual(expectedRowOutput, rowOutput); +} + +test(DisplayLengthBasic) { + assertEqual(5, SerialTable::displayLength("Hello")); +} + +test(DisplayLengthWithSpace) { + assertEqual(11, SerialTable::displayLength("Hello World")); +} + +test(DisplayLengthWithUmlauts) { + // "Müller" in UTF-8 hat eine Länge von 6 Zeichen (ä zählt als 1 Zeichen) + assertEqual(6, SerialTable::displayLength("Müller")); +} + +test(DisplayLengthOnlyUmlauts) { + // "äöü" in UTF-8 sollten jeweils als ein Zeichen zählen + assertEqual(3, SerialTable::displayLength("äöü")); +} + +test(DisplayLengthUTF8Emojis) { + // "😀😃😄" Emojis können mehrere Bytes haben, prüfen ob jedes nur als ein Zeichen gezählt wird + assertEqual(3, SerialTable::displayLength("😀😃😄")); +} + +test(DisplayLengthWithNewline) { + // "Hello\nWorld" hat eine Länge von 11 Zeichen, da \n als einzelnes Zeichen zählt + assertEqual(11, SerialTable::displayLength("Hello\nWorld")); +} + +test(DisplayLengthEmptyString) { + // Ein leerer String sollte eine Länge von 0 haben + assertEqual(0, SerialTable::displayLength("")); +} + +test(DisplayLengthSpaces) { + // " " hat eine Länge von 3 Leerzeichen + assertEqual(3, SerialTable::displayLength(" ")); +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d17daf97dd08da406728b4f2812eddf287596e88 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +# Allgemeine VSCode-Einstellungen +.vscode/ +*.code-workspace + +# ESP32 spezifische Dateien +build/ +*.elf +*.bin +*.map +*.hex +*.o +sdkconfig +sdkconfig.old +partition_table.csv +partition_table.bin + +# Temporäre Dateien +*.log +*.tmp +*.bak +*.swp +*.swo +*.DS_Store +*.pyc +__pycache__/ +*.d +*.lst + +# Arduino Core (falls verwendet) +*.ino.* +*.cpp~ +*.h~ + +# Debugging und Flashing +flash_project_args +build.log +flash.log + +# PlattformIO (falls genutzt) +.pio/ +.env +.clangd/ +ipch/ +project_config.json + +# Betriebssystem-spezifische Dateien +Thumbs.db +Desktop.ini + +# Andere IDEs (falls versehentlich hinzugefügt) +.idea/ +*.iml diff --git a/SerialTable.h b/SerialTable.h new file mode 100644 index 0000000000000000000000000000000000000000..27731954972edfdee428fc4f1042a2a00c1318b1 --- /dev/null +++ b/SerialTable.h @@ -0,0 +1,353 @@ +#ifndef SERIALTABLE_H +#define SERIALTABLE_H + +#include <Arduino.h> + +class SerialTable +{ +public: + SerialTable(const char *headers[], const int columnWidths[], size_t size) + : headers(headers), columnWidths(columnWidths), size(size) {} + + // Diese Methode gibt den Header an die serielle Schnittstelle aus + void printHeader() + { + String header = buildHeader(); + Serial.print(header); + } + + String buildHeader() + { + return buildLine('-') + buildRow(const_cast<char **>(headers), '='); // Kopfzeile mit '=' + } + + void printRow(char **t_row) + { + String row = buildRow(t_row); + Serial.print(row); + } + + String buildRow(char **t_row, char line = '-') + { + String output; + char buffer[256]; + int maxLines = 1; // Maximale Anzahl Zeilen über alle Spalten + + // Erste Phase: Teile jede Zelle in Zeilen und finde maximale Zeilenzahl + for (size_t i = 0; i < size; ++i) + { + if (truncateText) + { + truncateToColumnWidth(t_row[i], columnWidths[i], buffer); + } + else + { + splitStringByLength(t_row[i], columnWidths[i], buffer); + } + + // Zähle Zeilen in dieser Zelle + int lineCount = 1; + for (size_t j = 0; j < displayLength(buffer); j++) + { + if (buffer[j] == '\n') + lineCount++; + } + if (lineCount > maxLines) + maxLines = lineCount; + } + + // Zweite Phase: Baue die Zeilen auf + for (int line = 0; line < maxLines; line++) + { + output += "|"; + + // Gehe durch jede Spalte + for (size_t col = 0; col < size; ++col) + { + if (truncateText) + { + truncateToColumnWidth(t_row[col], columnWidths[col], buffer); + } + else + { + splitStringByLength(t_row[col], columnWidths[col], buffer); + } + + // Finde die aktuelle Zeile für diese Spalte + char *currentLine = buffer; + int currentLineNum = 0; + char tempBuffer[256] = ""; + + while (currentLineNum < line) + { + char *newline = strchr(currentLine, '\n'); + if (newline == NULL) + { + currentLine = ""; + break; + } + currentLine = newline + 1; + currentLineNum++; + } + + // Extrahiere die Zeile bis zum nächsten \n oder Ende + int j = 0; + while (currentLine[j] && currentLine[j] != '\n') + { + tempBuffer[j] = currentLine[j]; + j++; + } + tempBuffer[j] = '\0'; + + // Berechne die tatsächliche Anzeigelänge + int printedLength = displayLength(tempBuffer); + + // Füge den Inhalt und die fehlenden Leerzeichen hinzu + output += tempBuffer; + int spacesToAdd = columnWidths[col] - printedLength; + for (int k = 0; k < spacesToAdd; k++) + { + output += " "; + } + output += "|"; + } + output += "\n"; + } + + // Füge die Trennlinie hinzu + output += buildLine(line); + return output; + } + + void setTruncateText(bool value) + { + truncateText = value; + } + +public: // private: + const char **headers; + const int *columnWidths; + size_t size; + bool truncateText = false; + + static int displayLength(const char *text) + { + int length = 0; + for (int i = 0; text[i] != '\0'; ++i) + { + if ((text[i] & 0xC0) != 0x80) + { // Überspringe UTF-8 Fortsetzungsbytes + length++; + } + } + return length; + } + + static void safeStringCopy(char *dest, const char *src, size_t bufferSize) + { + if (strlen(src) >= bufferSize) + { + strncpy(dest, src, bufferSize - 1); + dest[bufferSize - 1] = '\0'; // NULL-termination sichergestellt + } + else + { + strcpy(dest, src); + } + } + + static void replaceUmlauts(char *text) + { + // UTF-8 Codes für Umlaute (hex): ä(c3 a4) ö(c3 b6) ü(c3 bc) Ä(c3 84) Ö(c3 96) Ü(c3 9c) ß(c3 9f) + struct UmlautPair + { + unsigned char bytes[2]; // UTF-8 bytes + const char *replacement; + }; + + const UmlautPair pairs[] = { + {{0xc3, 0xa4}, "ae"}, // ä + {{0xc3, 0xb6}, "oe"}, // ö + {{0xc3, 0xbc}, "ue"}, // ü + {{0xc3, 0x84}, "Ae"}, // Ä + {{0xc3, 0x96}, "Oe"}, // Ö + {{0xc3, 0x9c}, "Ue"}, // Ü + {{0xc3, 0x9f}, "ss"} // ß + }; + const int numPairs = sizeof(pairs) / sizeof(pairs[0]); + + size_t maxOutputLength = strlen(text) * 2 + 1; + char *buffer = (char *)malloc(maxOutputLength); + if (!buffer) + return; + + int k = 0; // Position im Ausgabepuffer + int i = 0; // Position im Eingabetext + + while (text[i] != '\0') + { + bool replaced = false; + + // Prüfe auf UTF-8 Sequenz + if ((unsigned char)text[i] == 0xc3 && text[i + 1] != '\0') + { + for (int j = 0; j < numPairs; ++j) + { + if ((unsigned char)text[i] == pairs[j].bytes[0] && + (unsigned char)text[i + 1] == pairs[j].bytes[1]) + { + // Kopiere Ersetzung + const char *replacement = pairs[j].replacement; + for (int r = 0; replacement[r] != '\0' && k < maxOutputLength - 1; ++r) + { + buffer[k++] = replacement[r]; + } + replaced = true; + i += 2; // Überspringe beide UTF-8 Bytes + break; + } + } + } + + if (!replaced && k < maxOutputLength - 1) + { + buffer[k++] = text[i++]; + } + } + + buffer[k] = '\0'; + strcpy(text, buffer); + free(buffer); + } + + // Neue Version von splitStringByLength + static void splitStringByLength(const char *input, int L, char *output) + { + int len = strlen(input); + int outputIndex = 0; + int currentLength = 0; + + for (int i = 0; i < len; ++i) + { + // Kopiere das aktuelle Zeichen zu Ausgabe + unsigned char curr = input[i]; + output[outputIndex++] = curr; + + // UTF-8-Handling: Überspringe zusätzliche Bytes für ein Multibyte-Zeichen + if ((curr & 0x80) != 0) + { + // Wenn das höchste Bit gesetzt ist, haben wir ein Multibyte-Zeichen + while ((input[i + 1] & 0xC0) == 0x80) + { + output[outputIndex++] = input[++i]; + } + } + + currentLength++; + + // Wenn die maximale Länge erreicht ist und das nächste Zeichen kein newline ist + if (currentLength == L && i + 1 < len && input[i + 1] != '\n') + { + output[outputIndex++] = '\n'; + currentLength = 0; + } + } + output[outputIndex] = '\0'; // Beende den String + } + + void truncateToColumnWidth(const char *input, int width, char *output) + { + int len = strlen(input); + if (len > width) + { + width = (width > 3) ? width - 3 : width; + strncpy(output, input, width); + strcpy(output + width, "..."); + } + else + { + strcpy(output, input); + } + } + + size_t calculateMaxLines(char **t_row) + { + size_t maxLines = 0; + + for (size_t i = 0; i < size; ++i) + { + const char *text = t_row[i]; + size_t textLength = strlen(text); + size_t lines = 1; + + for (size_t j = 0; j < textLength; ++j) + { + if (text[j] == '\n') + { + ++lines; + } + } + + maxLines = max(maxLines, lines); + } + + return maxLines; + } + + const char *getRowSegment(const char *text, int width, size_t line, char *buffer) + { + size_t textLength = strlen(text); + size_t lineStart = 0; + size_t currentLine = 0; + buffer[0] = '\0'; + + for (size_t i = 0; i < textLength; ++i) + { + if (currentLine == line) + { + lineStart = i; + break; + } + if (text[i] == '\n' || (i > 0 && (i % width) == 0)) + { + ++currentLine; + } + } + + if (lineStart >= textLength || currentLine != line) + { + return buffer; + } + + size_t charCount = 0; + for (size_t i = lineStart; i < textLength && charCount < width; ++i) + { + if (text[i] == '\n') + { + break; + } + buffer[charCount++] = text[i]; + } + + buffer[charCount] = '\0'; + + return buffer; + } + + String buildLine(char lineChar) + { + String line; + for (size_t i = 0; i < size; ++i) + { + line += '+'; + for (int j = 0; j < columnWidths[i]; ++j) + { + line += lineChar; + } + } + line += "+\n"; + return line; + } +}; + +#endif // SERIALTABLE_H \ No newline at end of file diff --git a/UnitTests.h b/UnitTests.h new file mode 100644 index 0000000000000000000000000000000000000000..8d67725577d202525c4eefd8a92f2ce52abb0c4b --- /dev/null +++ b/UnitTests.h @@ -0,0 +1,342 @@ +#include <AUnit.h> +#include "SerialTable.h" +using namespace aunit; + +test(SplitStringNoNewlines) +{ + char input[] = "This is a test string"; + int length = 5; + char expectedOutput[] = "This \nis a \ntest \nstrin\ng"; + char actualOutput[256] = {0}; // Ausreichend großer Puffer + + SerialTable::splitStringByLength(input, length, actualOutput); + + // Überprüfung der Länge der Strings + assertEqual(strlen(expectedOutput), strlen(actualOutput)); +/* + // Zusätzliche Debug-Ausgabe, um Hex-Werte zu sehen + for (size_t i = 0; i < strlen(actualOutput); ++i) + { + Serial.print("Expected: "); + Serial.print((int)expectedOutput[i]); + Serial.print(", Actual: "); + Serial.println((int)actualOutput[i]); + } +*/ + assertEqual(String(expectedOutput), String(actualOutput)); +} + +// Einzelner Testfall 2: Eingabe mit existierenden Zeilenumbrüchen +test(SplitStringWithNewlines) +{ + char input[] = "This\nis\na\ntest"; + int length = 4; + char expectedOutput[] = "This\nis\na\ntest"; + char actualOutput[256] = {0}; + SerialTable::splitStringByLength(input, length, actualOutput); + + assertEqual(String(expectedOutput), String(actualOutput)); +} + +// Einzelner Testfall 3: Länge größer als Eingabetext +test(SplitStringLengthGreaterThanText) +{ + char input[] = "Short"; + int length = 10; + char expectedOutput[] = "Short"; + char actualOutput[256] = {0}; + SerialTable::splitStringByLength(input, length, actualOutput); + + assertEqual(String(expectedOutput), String(actualOutput)); +} + +// Einzelner Testfall 4: Genau passende Länge +test(SplitStringExactLength) +{ + char input[] = "Lengthy"; + int length = 7; + char expectedOutput[] = "Lengthy"; + char actualOutput[256] = {0}; + SerialTable::splitStringByLength(input, length, actualOutput); + assertEqual(String(expectedOutput), String(actualOutput)); +} + +// Einzelner Testfall 5: Länge von 1 +test(SplitStringLengthOfOne) +{ + char input[] = "One"; + int length = 1; + char expectedOutput[] = "O\nn\ne"; + char actualOutput[256] = {0}; + SerialTable::splitStringByLength(input, length, actualOutput); + assertEqual(String(expectedOutput), String(actualOutput)); +} + +// Einzelner Testfall 6: Leerer Eingabestring +test(SplitStringEmptyInput) +{ + char input[] = ""; + int length = 5; + char expectedOutput[] = ""; + char actualOutput[256] = {0}; + SerialTable::splitStringByLength(input, length, actualOutput); + assertEqual(String(expectedOutput), String(actualOutput)); +} + +// Erstelle eine Hilfsfunktion für replaceUmlauts-Tests +test(replaceUmlautsTest) +{ + char text1[] = "Köln"; + SerialTable::replaceUmlauts(text1); + assertEqual("Koeln", text1); + + char text2[] = "München"; + SerialTable::replaceUmlauts(text2); + assertEqual("Muenchen", text2); + + char text3[] = "Straße"; + SerialTable::replaceUmlauts(text3); + assertEqual("Strasse", text3); +} + +// Erstelle eine Hilfsfunktion für truncateTextTest +test(truncateTextTest) +{ + char buffer[20]; + SerialTable table(nullptr, nullptr, 0); + + // Fall: Text passt in die Spaltenbreite + strcpy(buffer, "Hello"); + table.truncateToColumnWidth(buffer, 10, buffer); + assertEqual("Hello", buffer); + + // Fall: Text wird abgeschnitten + strcpy(buffer, "HelloWorld!"); + table.truncateToColumnWidth(buffer, 5, buffer); + assertEqual("He...", buffer); +} + +test(buildHeaderTest1) +{ + // Definiere header mit char*-Zeigern + const char *headers[] = {"Name", "Age", "City"}; + const int columnWidths[] = {10, 5, 10}; + SerialTable table(headers, columnWidths, 3); + + // Verwende buildHeader, um den Header-String zu erhalten + String header = table.buildHeader(); + + // Gib den Header über Serial aus + Serial.print(header); + Serial.flush(); + + // Erwartete Ausgabe des Headers + String expectedHeader = + "+----------+-----+----------+\n" + "|Name |Age |City |\n" + "+==========+=====+==========+\n"; + + // Vergleiche den erzeugten Header mit dem erwarteten Header + assertEqual(expectedHeader, header); +} + +test(buildHeaderTest2) +{ + // Definiere header mit char*-Zeigern + const char *headers[] = {"NameName Name", "Age Age Age", "CityCityCity"}; + const int columnWidths[] = {10, 5, 10}; + SerialTable table(headers, columnWidths, 3); + + // Verwende buildHeader, um den Header-String zu erhalten + String header = table.buildHeader(); + + // Gib den Header über Serial aus + Serial.print(header); + Serial.flush(); + + // Erwartete Ausgabe des Headers + String expectedHeader = + "+----------+-----+----------+\n" + "|NameName N|Age A|CityCityCi|\n" + "|ame |ge Ag|ty |\n" + "| |e | |\n" + "+==========+=====+==========+\n"; + + // Vergleiche den erzeugten Header mit dem erwarteten Header + assertEqual(expectedHeader, header); +} + +test(buildHeaderTest3) +{ + // Definiere header mit char*-Zeigern + const char *headers[] = {"ä123", "ö123", "ü123"}; + const int columnWidths[] = {4, 4, 4}; + SerialTable table(headers, columnWidths, 3); + + // Verwende buildHeader, um den Header-String zu erhalten + String header = table.buildHeader(); + + // Gib den Header über Serial aus + Serial.print(header); + Serial.flush(); + + // Erwartete Ausgabe des Headers + String expectedHeader = + "+----+----+----+\n" + "|ä123|ö123|ü123|\n" + "+====+====+====+\n"; + + // Vergleiche den erzeugten Header mit dem erwarteten Header + assertEqual(expectedHeader, header); +} + +test(printHeaderTest) +{ + // Verwende char*-Array statt String-Array + const char *headers[] = {"Name", "Age", "City"}; + const int columnWidths[] = {10, 5, 10}; + SerialTable table(headers, columnWidths, 3); + + table.printHeader(); + Serial.flush(); + + // Placeholder assertion, Verwendung eines Serial Monitor Tools empfohlen + assertTrue(true); +} + +test(buildRowTest) +{ + const char *headers[] = {"Name", "Age", "City"}; + const int columnWidths[] = {10, 5, 10}; + SerialTable table(headers, columnWidths, 3); + + char *rows[] = {"Alice", "30", "Wonderland"}; + + // Verwende buildRow, um den Zeilen-String zu bekommen + String rowOutput = table.buildRow(rows); + + // Gib die Zeile über Serial aus + Serial.print(rowOutput); + Serial.flush(); + + // Erwartete Ausgabe der Zeile + String expectedRowOutput = + "|Alice |30 |Wonderland|\n" + "+----------+-----+----------+\n"; + + // Vergleiche den generierten Zeilen-String mit dem erwarteten Zeilen-String + assertEqual(expectedRowOutput, rowOutput); +} + +test(printRowWithMultipleLinesTest) +{ + const char *headers[] = {"Name", "Age", "City"}; + const int columnWidths[] = {10, 5, 5}; + SerialTable table(headers, columnWidths, 3); + + char *rows[] = {"Alice", "30", "Wonderland"}; + + // Verwende buildRow, um den Zeilen-String zu bekommen + String rowOutput = table.buildRow(rows); + + // Gib die Zeile über Serial aus + Serial.print(rowOutput); + Serial.flush(); + + // Erwartete Ausgabe der Zeile + String expectedRowOutput = + "|Alice |30 |Wonde|\n" + "| | |rland|\n" + "+----------+-----+-----+\n"; + + // Vergleiche den generierten Zeilen-String mit dem erwarteten Zeilen-String + assertEqual(expectedRowOutput, rowOutput); +} + +test(printRowWithLongTextTest) +{ + const char *headers[] = {"Name", "Age", "City"}; + const int columnWidths[] = {10, 5, 10}; + SerialTable table(headers, columnWidths, 3); + + char *rows[] = {"A very long name", "12345", "A very long city name"}; + + // Verwende buildRow, um den Zeilen-String zu bekommen + String rowOutput = table.buildRow(rows); + + // Gib die Zeile über Serial aus + Serial.print(rowOutput); + Serial.flush(); + + // Erwartete Ausgabe der Zeile + String expectedRowOutput = + "|A very lon|12345|A very lon|\n" + "|g name | |g city nam|\n" + "| | |e |\n" + "+----------+-----+----------+\n"; + + // Vergleiche den generierten Zeilen-String mit dem erwarteten Zeilen-String + assertEqual(expectedRowOutput, rowOutput); +} + +test(printEmptyRowTest) +{ + const char *headers[] = {"Name", "Age", "City"}; + const int columnWidths[] = {10, 5, 10}; + SerialTable table(headers, columnWidths, 3); + + char *rows[] = {"", "", ""}; + + // Verwende buildRow, um den Zeilen-String zu bekommen + String rowOutput = table.buildRow(rows); + + // Gib die Zeile über Serial aus + Serial.print(rowOutput); + Serial.flush(); + + // Erwartete Ausgabe der Zeile + String expectedRowOutput = + "| | | |\n" + "+----------+-----+----------+\n"; + + // Vergleiche den generierten Zeilen-String mit dem erwarteten Zeilen-String + assertEqual(expectedRowOutput, rowOutput); +} + +test(DisplayLengthBasic) { + assertEqual(5, SerialTable::displayLength("Hello")); +} + +test(DisplayLengthWithSpace) { + assertEqual(11, SerialTable::displayLength("Hello World")); +} + +test(DisplayLengthWithUmlauts) { + // "Müller" in UTF-8 hat eine Länge von 6 Zeichen (ä zählt als 1 Zeichen) + assertEqual(6, SerialTable::displayLength("Müller")); +} + +test(DisplayLengthOnlyUmlauts) { + // "äöü" in UTF-8 sollten jeweils als ein Zeichen zählen + assertEqual(3, SerialTable::displayLength("äöü")); +} + +test(DisplayLengthUTF8Emojis) { + // "😀😃😄" Emojis können mehrere Bytes haben, prüfen ob jedes nur als ein Zeichen gezählt wird + assertEqual(3, SerialTable::displayLength("😀😃😄")); +} + +test(DisplayLengthWithNewline) { + // "Hello\nWorld" hat eine Länge von 11 Zeichen, da \n als einzelnes Zeichen zählt + assertEqual(11, SerialTable::displayLength("Hello\nWorld")); +} + +test(DisplayLengthEmptyString) { + // Ein leerer String sollte eine Länge von 0 haben + assertEqual(0, SerialTable::displayLength("")); +} + +test(DisplayLengthSpaces) { + // " " hat eine Länge von 3 Leerzeichen + assertEqual(3, SerialTable::displayLength(" ")); +} diff --git a/output.txt b/output.txt new file mode 100644 index 0000000000000000000000000000000000000000..d9207f8797def018532d7b7f9a40c9e68fc9d020 --- /dev/null +++ b/output.txt @@ -0,0 +1,26 @@ ++---------------+--------------------+----------+ +|Header 1 |Header 2 |Header 3 | ++===============+====================+==========+ +|Text | |Text | ++---------------+--------------------+----------+ +|Text |Text | | ++---------------+--------------------+----------+ +| |Text |Text1 | +|Text | |Text2 | ++---------------+--------------------+----------+ + +|Alice |30 |Wonde| +| | |rland| ++----------+-----+-----+ + +|Alice |30 |Wonderland| ++----------+-----+-----+ + + +|Alice |30 |Wonde| +| | |rland| ++----------+-----+-----+ + +|Alice |30 |Wonde +rland| ++----------+-----+-----+ diff --git a/serialTable.ino b/serialTable.ino new file mode 100644 index 0000000000000000000000000000000000000000..ac42ae46fb8c1ee4de7a9a161dabb0117dbc58ad --- /dev/null +++ b/serialTable.ino @@ -0,0 +1,52 @@ +#include <Arduino.h> +#include "UnitTests.h" +#include "SerialTable.h" + +void setup() +{ + // Initialisieren der seriellen Verbindung + Serial.begin(115200); + + // Definieren der Kopfzeilen und Spaltenbreiten für 8 Spalten + const char *headers[] = {"Vorname", "Nachname", "Alter", "Stadt", "Beruf", "Unternehmen", "Hobby", "Bemerkung"}; + int columnWidths[] = {10, 15, 5, 10, 10, 15, 10, 20}; + + SerialTable table(headers, columnWidths, 8); + table.printHeader(); + + // Erstellen von 20 Zeilen mit unterschiedlichen Einträgen + char *rows[20][8] = { + {"Alice", "Wonderland", "30", "Köln", "Ingenieur", "FirmaX", "Schwimmen", "Keine"}, + {"Bob", "Builder\nJr", "35", "Berlin", "Architekt", "Architektur AG", "Bauen", "Sehr erfahren"}, + {"Charlie", "Brown", "28", "Hamburg", "Designer", "Die Design", "Zeichnen", "Mag Karikaturen"}, + {"David", "Tennant", "40", "München", "Schauspieler", "Freiberufler", "Lesen", "Doktor gewesen"}, + {"Emma", "Watson", "33", "Frankfurt", "Schauspielerin", "Independent", "Yoga", "Bekannt aus Filmen"}, + {"Frank", "Herbert", "45", "Stuttgart", "Autor", "Ein Verlag", "Schreiben", "Bücher geschrieben"}, + {"Grace", "Hopper", "50", "Berlin", "Programmiererin", "Tech Corp", "Informatik", "Pionierin"}, + {"Hugo", "Strange", "60", "Leipzig", "Psychiater", "Gesundheits GmbH", "Philosophie", "Ruhestand"}, + {"Irene", "Adler", "29", "Dresden", "Detektivin", "Privat", "Abenteuer", "Scharfsinnig"}, + {"John", "Doe", "49", "Bremen", "Buchhalter", "Kontorechner", "Radfahren", "Gewissenhaft"}, + {"Klara", "Kühn", "39", "Hannover", "Lehrerin", "Schule ABC", "Lesen", "Einfühlsam"}, + {"Lenny", "Small", "25", "Koblenz", "Musiker", "Orchester", "Gitarre", "Kreativ"}, + {"Mona", "Lisa", "33", "Siegen", "Malerin", "Kunsthaus", "Kunst", "Geheimnisvoll"}, + {"Nina", "Simone", "30", "Essen", "Sängerin", "Soul Musik", "Musik", "Talentiert"}, + {"Oliver", "Twist", "19", "Potsdam", "Student", "Uni XY", "Fußball", "Engagiert"}, + {"Pam", "Beesly", "32", "Wuppertal", "Sekretärin", "Dunder Mifflin", "Malen", "Freundlich"}, + {"Quinn", "Fabray", "26", "Magdeburg", "Designer", "Agentur XYZ", "Tanzen", "Kreativ"}, + {"Ron", "Swanson", "55", "Bielefeld", "Verwalter", "Rathaus", "Holzarbeiten", "Direkt"}, + {"Sophia", "Loren", "70", "Osnabrück", "Schauspielerin", "Independent", "Kochen", "Ikonisch"}, + {"Tom", "Hanson", "36", "Regensburg", "Polizist", "Polizei", "Jagen", "Schützend"}, + }; + + // Drucke die Zeilen + for (int i = 0; i < 20; ++i) + { + table.printRow(rows[i]); + } + +} + +void loop() +{ + //TestRunner::run(); +} \ No newline at end of file