#include #include #include #include #include "unity.h" #include "numbers.h" // Externe Deklaration der Vergleichsfunktion aus numbers.c (für den Test) extern int compareNumbers(const void *arg1, const void *arg2); // ========================================================================= // HILFSFUNKTIONEN // ========================================================================= /** * 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. * @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++) { int occurrences = 0; // Zähle, wie oft numbers[i] im gesamten Array vorkommt for (unsigned int j = 0; j < len; j++) { if (numbers[i] == numbers[j]) { occurrences++; } } if (occurrences == 2) { duplicateCount++; // Duplikat gefunden } else if (occurrences == 1) { uniqueCount++; // Eindeutige Zahl gefunden } else { // Eine Zahl kommt 0, 3 oder mehr Male vor -> Fehler return 0; } } // 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. 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) } // ========================================================================= // TESTFALL GRUPPE 1: createNumbers // ========================================================================= 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) { const unsigned int len = 10; unsigned int *numbers = createNumbers(len); TEST_ASSERT_NOT_NULL(numbers); // Die Array-Länge kann nicht direkt in C geprüft werden, // aber wir prüfen auf NULL nach dem malloc // 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. 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 const unsigned int max_val = 2 * len; for (unsigned int i = 0; i < len; i++) { TEST_ASSERT_TRUE(numbers[i] >= 1); TEST_ASSERT_TRUE(numbers[i] <= max_val); } // Speicher freigeben free(numbers); } // ========================================================================= // TESTFALL GRUPPE 2: getDuplicate // ========================================================================= 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 GRUPPE 3: compareNumbers (Hilfsfunktion für qsort) // ========================================================================= void test_compareNumbersReturnsZeroForEqual(void) { unsigned int a = 10, b = 10; TEST_ASSERT_EQUAL_INT(0, compareNumbers(&a, &b)); } 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; TEST_ASSERT_TRUE(compareNumbers(&a, &b) > 0); } void test_compareNumbersHandlesZero(void) { unsigned int a = 0, b = 1; TEST_ASSERT_TRUE(compareNumbers(&a, &b) < 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)); } // ========================================================================= // MAIN // ========================================================================= 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. } void tearDown(void) { // Bereinigung nach jedem Test } int main(void) { UNITY_BEGIN(); 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) 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); return UNITY_END(); }