Doble-Spiel/test_numbers.c
Sara 748a39e8c1 cleartree funktion in bintree; getDuplicate in numbers;
Unittests test-Numbers implemented + makefile einbindung
2025-12-04 21:37:05 +01:00

274 lines
9.1 KiB
C

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#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();
}