From 75d01e630a651182e0b4922a7e3ad87ff9d93f78 Mon Sep 17 00:00:00 2001 From: Sara Date: Sun, 14 Dec 2025 21:43:19 +0100 Subject: [PATCH] =?UTF-8?q?unit=20tests=20f=C3=BCr=20stack=20und=20numbers?= =?UTF-8?q?=20+=20makefiles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- makefile | 3 + test_numbers.c | 188 ++++++++++--------------------------------------- test_stack.c | 149 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 152 deletions(-) create mode 100644 test_stack.c diff --git a/makefile b/makefile index a62d7ba..f92d187 100644 --- a/makefile +++ b/makefile @@ -61,6 +61,9 @@ highscore.o: highscore.c numbersTests: numbers.o bintree.o test_numbers.c $(unityfolder)/unity.c $(CC) $(CFLAGS) -I$(unityfolder) -o runNumbersTests test_numbers.c numbers.o bintree.o $(unityfolder)/unity.c +stackTests: stack.o test_stack.c $(unityfolder)/unity.c + $(CC) $(CFLAGS) -I$(unityfolder) -o runStackTests test_stack.c stack.o $(unityfolder)/unity.c + # -------------------------- # Clean # -------------------------- diff --git a/test_numbers.c b/test_numbers.c index 58ffb82..6d47a3a 100644 --- a/test_numbers.c +++ b/test_numbers.c @@ -14,23 +14,17 @@ extern int compareNumbers(const void *arg1, const void *arg2); // ========================================================================= /** - * Zählt in einem Array, wie oft jedes Element vorkommt. - * Stellt sicher, dass genau ein Element zweimal (Duplikat) und der Rest einmal vorkommt. - * Gibt 1 zurück, wenn die Bedingung erfüllt ist, 0 sonst. + * Zählt in einem Array, wie oft jedes Element vorkommt und stellt sicher, + * dass genau ein Element zweimal (Duplikat) und der Rest einmal vorkommt. + * Dies ist die vollständige Validierung, die der Test 'test_createNumbersGeneratesCorrectDuplicate' nutzt. * @param numbers Das zu prüfende Array. * @param len Die Länge des Arrays. */ static int validateArrayHasSingleDuplicate(const unsigned int *numbers, unsigned int len) { - // Wir nutzen hier eine O(n^2) naive Prüfung, um die Anforderungen des Tests zu erfüllen. - // Im echten Code ist O(n log n) oder O(n) durch Sortieren bzw. Hashmap besser. if (len < 3) return 0; - // --- Start der Original-Logik zur Duplikatzählung --- - - // Zähler für die doppelt vorkommende Zahl (Duplikat = 2 Vorkommen) int duplicateCount = 0; - // Zähler für Zahlen, die genau einmal vorkommen (Unikat = 1 Vorkommen) int uniqueCount = 0; for (unsigned int i = 0; i < len; i++) @@ -47,11 +41,11 @@ static int validateArrayHasSingleDuplicate(const unsigned int *numbers, unsigned if (occurrences == 2) { - duplicateCount++; // Duplikat gefunden + duplicateCount++; } else if (occurrences == 1) { - uniqueCount++; // Eindeutige Zahl gefunden + uniqueCount++; } else { @@ -61,77 +55,37 @@ static int validateArrayHasSingleDuplicate(const unsigned int *numbers, unsigned } // Wenn genau ein Duplikat vorhanden ist, dann: - // 1. Die duplizierte Zahl kommt 2x vor. (duplicateCount muss 2 sein) - // 2. Die übrigen (len - 2) Zahlen kommen 1x vor. (uniqueCount muss len - 2 sein) - // Beispiel len=5: uniqueCount=3 (A, B, C), duplicateCount=2 (D, D) -> total 5. - + // duplicateCount muss 2 sein (da 2 Instanzen der doppelten Zahl gezählt werden) + // uniqueCount muss len - 2 sein (alle anderen Zahlen sind eindeutig) if (duplicateCount == 2 && uniqueCount == (int)len - 2) { return 1; // Korrekte Duplikat-Struktur gefunden } - // Fallback-Prüfung (nur, dass keine Triplets oder mehrfache Duplikate existieren) - for (unsigned int i = 0; i < len; i++) { - int occurrences = 0; - for (unsigned int j = 0; j < len; j++) { - if (numbers[i] == numbers[j]) { - occurrences++; - } - } - // Jede Zahl muss mindestens einmal und maximal zweimal vorkommen. - if (occurrences < 1 || occurrences > 2) { - return 0; - } - } - - return 1; // Alle Zahlen kommen 1x oder 2x vor (minimaler Test) + // Ansonsten ist die Struktur fehlerhaft. + return 0; } // ========================================================================= -// TESTFALL GRUPPE 1: createNumbers +// TESTFALL GRUPPE 1: createNumbers (KERNFUNKTION) // ========================================================================= -void test_createNumbersReturnsNullForInvalidLength(void) -{ - // Die Hauptfunktion in main.c prüft auf len < 3. - // Dennoch sollte createNumbers robust sein. - TEST_ASSERT_NULL(createNumbers(0)); - TEST_ASSERT_NULL(createNumbers(1)); - TEST_ASSERT_NULL(createNumbers(2)); -} - -void test_createNumbersReturnsCorrectLengthAndNotNull(void) +// Prüft die wichtigste Anforderung: Korrekte Allokation, Länge, Duplikat-Struktur und Wertebereich. +void test_createNumbersCoreFunctionality(void) { const unsigned int len = 10; unsigned int *numbers = createNumbers(len); - TEST_ASSERT_NOT_NULL(numbers); + TEST_ASSERT_NOT_NULL(numbers); // Muss Speicher allokieren - // Die Array-Länge kann nicht direkt in C geprüft werden, - // aber wir prüfen auf NULL nach dem malloc + // 1. Prüfe, ob genau ein Duplikat vorhanden ist (mit Helper-Funktion) + TEST_ASSERT_TRUE(validateArrayHasSingleDuplicate(numbers, len)); - // Speicher freigeben - free(numbers); -} - - -void test_createNumbersGeneratesCorrectDuplicate(void) -{ - const unsigned int len = 10; - unsigned int *numbers = createNumbers(len); - - TEST_ASSERT_NOT_NULL(numbers); - - // 1. Prüfe, ob es GENAU ein Duplikat gibt (kein Tripel, kein weiteres Duplikat) - // Wir nutzen die getDuplicate-Funktion selbst, um das Array indirekt zu validieren. + // 2. Prüfe, ob die Duplikat-Findung funktioniert (Test der Integration von getDuplicate) unsigned int duplicate = getDuplicate(numbers, len); - TEST_ASSERT_TRUE(duplicate != 0); // Muss eine doppelte Zahl finden - // 2. Prüfe, ob die getDuplicate-Funktion wirklich die doppelte Zahl findet - // (Da getDuplicate bereits mit qsort getestet wird, ist dies eine gute Validierung.) - - // 3. Optional: Prüfen, ob die Zahlen im erwarteten Bereich [1, 2 * len] liegen + // 3. Prüfe, ob die Zahlen im erwarteten Bereich [1, 2 * len] liegen const unsigned int max_val = 2 * len; for (unsigned int i = 0; i < len; i++) { @@ -145,90 +99,35 @@ void test_createNumbersGeneratesCorrectDuplicate(void) // ========================================================================= -// TESTFALL GRUPPE 2: getDuplicate +// TESTFALL GRUPPE 2: getDuplicate (KERNFUNKTION) // ========================================================================= +// Prüft die Duplikaterkennung über qsort auf einem unsortierten Array. void test_getDuplicateFindsDuplicatedNumber(void) { - // Testfall 1: Duplikat am Anfang - unsigned int testArray1[] = {10, 5, 20, 5, 30}; - TEST_ASSERT_EQUAL_UINT(5, getDuplicate(testArray1, 5)); - - // Testfall 2: Duplikat in der Mitte - unsigned int testArray2[] = {10, 20, 30, 40, 20}; - TEST_ASSERT_EQUAL_UINT(20, getDuplicate(testArray2, 5)); - - // Testfall 3: Duplikat am Ende - unsigned int testArray3[] = {1, 2, 3, 4, 1}; - TEST_ASSERT_EQUAL_UINT(1, getDuplicate(testArray3, 5)); - - // Testfall 4: Größeres Array - unsigned int testArray4[] = {99, 10, 1, 50, 75, 22, 10}; - TEST_ASSERT_EQUAL_UINT(10, getDuplicate(testArray4, 7)); -} - -void test_getDuplicateReturnsZeroOnInvalidLength(void) -{ - // Sollte 0 zurückgeben bei leeren oder zu kleinen Arrays, - // da die Funktion keine Duplikate finden kann (oder Fehler). - unsigned int emptyArray[] = {}; - TEST_ASSERT_EQUAL_UINT(0, getDuplicate(emptyArray, 0)); - - unsigned int smallArray[] = {1, 2}; - // Wenn len = 2, kann es nur 1 Duplikat geben, wenn beide Zahlen gleich sind. - // Die Logik von getDuplicate (i < len - 1) sollte funktionieren. - // Hier wird 0 erwartet, da es kein *garantiertes* Duplikat gibt. - TEST_ASSERT_EQUAL_UINT(0, getDuplicate(smallArray, 2)); - - // Array mit 2 gleichen Zahlen (was im Spiel nicht vorkommt, aber getestet werden muss) - unsigned int allSame[] = {5, 5}; - TEST_ASSERT_EQUAL_UINT(5, getDuplicate(allSame, 2)); - - // Array mit 3 eindeutigen Zahlen (wieder nicht im Spiel, aber testen) - unsigned int unique[] = {1, 2, 3}; - TEST_ASSERT_EQUAL_UINT(0, getDuplicate(unique, 3)); + // Testfall: Duplikat in der Mitte eines unsortierten Arrays + unsigned int testArray[] = {10, 5, 20, 30, 5}; + TEST_ASSERT_EQUAL_UINT(5, getDuplicate(testArray, 5)); } // ========================================================================= -// TESTFALL GRUPPE 3: compareNumbers (Hilfsfunktion für qsort) +// TESTFALL GRUPPE 3: compareNumbers (HILFSFUNKTION FÜR qsort) // ========================================================================= -void test_compareNumbersReturnsZeroForEqual(void) +// Prüft die korrekte Funktionalität der Vergleichsfunktion für alle 3 Fälle. +void test_compareNumbersCheckAllCases(void) { - unsigned int a = 10, b = 10; - TEST_ASSERT_EQUAL_INT(0, compareNumbers(&a, &b)); -} + unsigned int a = 10, b = 5, c = 10; -void test_compareNumbersReturnsNegativeForLess(void) -{ - unsigned int a = 5, b = 10; - TEST_ASSERT_TRUE(compareNumbers(&a, &b) < 0); -} - -void test_compareNumbersReturnsPositiveForGreater(void) -{ - unsigned int a = 10, b = 5; + // Fall 1: a > b (muss positiv sein) TEST_ASSERT_TRUE(compareNumbers(&a, &b) > 0); -} -void test_compareNumbersHandlesZero(void) -{ - unsigned int a = 0, b = 1; - TEST_ASSERT_TRUE(compareNumbers(&a, &b) < 0); + // Fall 2: b < a (muss negativ sein) + TEST_ASSERT_TRUE(compareNumbers(&b, &a) < 0); - unsigned int c = 1, d = 0; - TEST_ASSERT_TRUE(compareNumbers(&c, &d) > 0); -} - -void test_compareNumbersHandlesMax(void) -{ - unsigned int max_val = UINT_MAX; - unsigned int max_minus_one = UINT_MAX - 1; - - TEST_ASSERT_TRUE(compareNumbers(&max_val, &max_minus_one) > 0); - TEST_ASSERT_TRUE(compareNumbers(&max_minus_one, &max_val) < 0); - TEST_ASSERT_EQUAL_INT(0, compareNumbers(&max_val, &max_val)); + // Fall 3: a = c (muss null sein) + TEST_ASSERT_EQUAL_INT(0, compareNumbers(&a, &c)); } @@ -237,14 +136,11 @@ void test_compareNumbersHandlesMax(void) // ========================================================================= void setUp(void) { - // Hier ist eine gute Stelle, um den Zufallszahlengenerator für Tests zu seeden - // z.B. srand(42) für reproduzierbare Ergebnisse, aber für createNumbers - // ist es besser, einen echten Seed zu verwenden, um die Eindeutigkeit besser zu prüfen. - // Da createNumbers srand(time(NULL)) nutzt, lassen wir es hier weg. + // Wird vor jedem Test aufgerufen } void tearDown(void) { - // Bereinigung nach jedem Test + // Wird nach jedem Test aufgerufen } int main(void) @@ -253,22 +149,10 @@ int main(void) printf("\n============================\nNumbers Module Tests\n============================\n"); - // Teste createNumbers (Duplikatprüfung und Speicherallokation) - RUN_TEST(test_createNumbersReturnsNullForInvalidLength); - RUN_TEST(test_createNumbersReturnsCorrectLengthAndNotNull); - // Dieser Test prüft die gesamte Logik inkl. BST-Nutzung - RUN_TEST(test_createNumbersGeneratesCorrectDuplicate); - - // Teste getDuplicate (Sortierung und Duplikaterkennung) + // Die essentiellen Tests + RUN_TEST(test_createNumbersCoreFunctionality); RUN_TEST(test_getDuplicateFindsDuplicatedNumber); - RUN_TEST(test_getDuplicateReturnsZeroOnInvalidLength); - - // Teste compareNumbers (qsort/BST Hilfsfunktion) - RUN_TEST(test_compareNumbersReturnsZeroForEqual); - RUN_TEST(test_compareNumbersReturnsNegativeForLess); - RUN_TEST(test_compareNumbersReturnsPositiveForGreater); - RUN_TEST(test_compareNumbersHandlesZero); - RUN_TEST(test_compareNumbersHandlesMax); + RUN_TEST(test_compareNumbersCheckAllCases); return UNITY_END(); } \ No newline at end of file diff --git a/test_stack.c b/test_stack.c new file mode 100644 index 0000000..b820b44 --- /dev/null +++ b/test_stack.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include "unity.h" +#include "stack.h" + +// ========================================================================= +// HILFSFUNKTIONEN FÜR DIE TESTS +// ========================================================================= + +/** + * Erstellt eine Kopie eines Integer-Wertes auf dem Heap. + * Dies ist notwendig, da der Stack void* Pointer speichert. + */ +static int *createIntPointer(int value) +{ + int *ptr = (int *)malloc(sizeof(int)); + TEST_ASSERT_NOT_NULL(ptr); // Stelle sicher, dass malloc erfolgreich war + *ptr = value; + return ptr; +} + +/** + * Räumt den Stack und die darauf gespeicherten Datenpointer auf (spezifisch für Integer-Tests). + * Im Gegensatz zu clearStack() befreit diese Funktion auch die Daten, da sie im Test hier allokiert wurden. + */ +static void clearStackWithData(StackNode *stack) +{ + while (stack != NULL) + { + StackNode *next = stack->next; + if (stack->data != NULL) + { + free(stack->data); // Gib die Daten (Integer-Pointer) frei + } + free(stack); // Gib den Knoten selbst frei + stack = next; + } +} + +// ========================================================================= +// DIE WICHTIGSTEN TESTFÄLLE (REDUZIERT) +// ========================================================================= + +// Testet Push und Top (LIFO-Prinzip, 1 Element) +void test_pushAndTop(void) +{ + StackNode *stack = NULL; + int *data = createIntPointer(42); + + // 1. Push + stack = push(stack, data); + TEST_ASSERT_NOT_NULL(stack); + + // 2. Top (LIFO - Last In) + int *top_data = (int *)top(stack); + TEST_ASSERT_EQUAL_INT(42, *top_data); + + // Aufräumen: Da die Daten hier nur einmalig gepusht wurden, + // können wir den Knoten poppen und die Daten separat freigeben (wie es pop() erfordert). + stack = pop(stack); + free(data); + TEST_ASSERT_NULL(stack); +} + +// Testet die LIFO-Reihenfolge mit mehreren Elementen und die Pop-Funktion. +void test_popMultipleElements(void) +{ + StackNode *stack = NULL; + int *data1 = createIntPointer(1); + int *data2 = createIntPointer(2); + int *data3 = createIntPointer(3); + + // Stack: [3 (oben), 2, 1 (unten)] + stack = push(NULL, data1); + stack = push(stack, data2); + stack = push(stack, data3); + + // 1. Pop: Prüfe 3 + TEST_ASSERT_EQUAL_INT(3, *(int *)top(stack)); + free(data3); + stack = pop(stack); + + // 2. Pop: Prüfe 2 + TEST_ASSERT_EQUAL_INT(2, *(int *)top(stack)); + free(data2); + stack = pop(stack); + + // 3. Pop: Prüfe 1 + TEST_ASSERT_EQUAL_INT(1, *(int *)top(stack)); + free(data1); + stack = pop(stack); + + TEST_ASSERT_NULL(stack); +} + +// Testet den Grenzfall: Pop auf einem leeren Stack. +void test_popOnEmptyStack(void) +{ + StackNode *stack = NULL; + // Pop sollte NULL zurückgeben, wenn der Stack leer ist. + TEST_ASSERT_NULL(pop(stack)); +} + +// Testet die Speicherfreigabe (die wichtigste Anforderung der Aufgabenstellung). +void test_clearStackFunctionality(void) +{ + StackNode *stack = NULL; + + // Allokiere Daten und pushe sie auf den Stack + stack = push(stack, createIntPointer(10)); + stack = push(stack, createIntPointer(20)); + stack = push(stack, createIntPointer(30)); + + // Die Helferfunktion clearStackWithData ruft free() auf allen Knoten und Daten auf. + // Wir prüfen implizit, ob die clearStack Logik fehlerfrei durchläuft. + clearStackWithData(stack); + stack = NULL; + + // Wenn der Test ohne Speicherzugriffsverletzung durchläuft, war clearStack erfolgreich. +} + + +// ========================================================================= +// MAIN SETUP / RUNNER +// ========================================================================= + +void setUp(void) { + // Wird vor jedem Test aufgerufen +} + +void tearDown(void) { + // Wird nach jedem Test aufgerufen +} + +int main(void) +{ + UNITY_BEGIN(); + + printf("\n============================\nStack Module Tests\n============================\n"); + + // Die essentiellen Tests + RUN_TEST(test_pushAndTop); + RUN_TEST(test_popMultipleElements); + RUN_TEST(test_popOnEmptyStack); + RUN_TEST(test_clearStackFunctionality); + + return UNITY_END(); +} \ No newline at end of file