600 lines
18 KiB
C
600 lines
18 KiB
C
#include "unity.h"
|
|
#include "numbers.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
/* ============================================================================
|
|
* UNIT TESTS FOR SORTING AND DUPLICATE DETECTION (Sortierung)
|
|
*
|
|
* This test suite covers:
|
|
* - createNumbers(): Generate random numbers with exactly one duplicate
|
|
* - getDuplicate(): Find the duplicate number using sorting
|
|
* ========================================================================== */
|
|
|
|
// ============================================================================
|
|
// HELPER FUNCTIONS FOR TESTING
|
|
// ============================================================================
|
|
|
|
// Count how many times a specific value appears in an array
|
|
unsigned int countOccurrences(const unsigned int *numbers, unsigned int len, unsigned int value)
|
|
{
|
|
unsigned int count = 0;
|
|
for (unsigned int i = 0; i < len; i++)
|
|
{
|
|
if (numbers[i] == value)
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
// Check if a value is in the valid range [1, 2*len]
|
|
int isInValidRange(unsigned int value, unsigned int len)
|
|
{
|
|
return (value >= 1) && (value <= 2 * len);
|
|
}
|
|
|
|
// Count total number of distinct values in the array
|
|
unsigned int countDistinctValues(const unsigned int *numbers, unsigned int len)
|
|
{
|
|
unsigned int distinct = 0;
|
|
for (unsigned int i = 0; i < len; i++)
|
|
{
|
|
int isNew = 1;
|
|
for (unsigned int j = 0; j < i; j++)
|
|
{
|
|
if (numbers[i] == numbers[j])
|
|
{
|
|
isNew = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (isNew)
|
|
{
|
|
distinct++;
|
|
}
|
|
}
|
|
return distinct;
|
|
}
|
|
|
|
// Find the value that appears exactly twice
|
|
unsigned int findDuplicate(const unsigned int *numbers, unsigned int len)
|
|
{
|
|
for (unsigned int i = 0; i < len; i++)
|
|
{
|
|
unsigned int count = countOccurrences(numbers, len, numbers[i]);
|
|
if (count == 2)
|
|
{
|
|
return numbers[i];
|
|
}
|
|
}
|
|
return 0; // No duplicate found
|
|
}
|
|
|
|
// ============================================================================
|
|
// TEST SETUP AND TEARDOWN
|
|
// ============================================================================
|
|
|
|
void setUp(void)
|
|
{
|
|
// Setup before each test
|
|
}
|
|
|
|
void tearDown(void)
|
|
{
|
|
// Cleanup after each test
|
|
}
|
|
|
|
// ============================================================================
|
|
// TEST SUITE 1: createNumbers() - Input Validation and Error Handling
|
|
// ============================================================================
|
|
|
|
// Test 1.1: Invalid input - len = 0 should return NULL
|
|
void test_createNumbers_InvalidLenZero(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(0);
|
|
|
|
TEST_ASSERT_NULL(numbers);
|
|
}
|
|
|
|
// Test 1.2: Invalid input - len = 1 should return NULL (need at least 2 for duplicate)
|
|
void test_createNumbers_InvalidLenOne(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(1);
|
|
|
|
TEST_ASSERT_NULL(numbers);
|
|
}
|
|
|
|
// ============================================================================
|
|
// TEST SUITE 2: createNumbers() - Minimum Valid Input
|
|
// ============================================================================
|
|
|
|
// Test 2.1: Minimum valid input - len = 2
|
|
void test_createNumbers_MinimumSize(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(2);
|
|
|
|
// Should not be NULL
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
|
|
// One value should equal the other (the duplicate)
|
|
TEST_ASSERT_EQUAL_INT(numbers[0], numbers[1]);
|
|
|
|
free(numbers);
|
|
}
|
|
|
|
// ============================================================================
|
|
// TEST SUITE 3: createNumbers() - Array Size and Contents
|
|
// ============================================================================
|
|
|
|
// Test 3.1: Check array size for len=5
|
|
void test_createNumbers_ArraySize5(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(5);
|
|
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
|
|
// Access all 5 elements to verify they exist
|
|
for (unsigned int i = 0; i < 5; i++)
|
|
{
|
|
// Just verify we can read without segfault
|
|
(void)numbers[i];
|
|
}
|
|
|
|
free(numbers);
|
|
}
|
|
|
|
// Test 3.2: Check array size for len=10
|
|
void test_createNumbers_ArraySize10(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(10);
|
|
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
|
|
// Access all 10 elements
|
|
for (unsigned int i = 0; i < 10; i++)
|
|
{
|
|
// Just verify we can read without segfault
|
|
(void)numbers[i];
|
|
}
|
|
|
|
free(numbers);
|
|
}
|
|
|
|
// ============================================================================
|
|
// TEST SUITE 4: createNumbers() - Value Range Validation
|
|
// ============================================================================
|
|
|
|
// Test 4.1: All values are positive (basic sanity check)
|
|
void test_createNumbers_ValueRangeLen5(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(5);
|
|
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
|
|
// Check each value is positive and not too large
|
|
for (unsigned int i = 0; i < 5; i++)
|
|
{
|
|
TEST_ASSERT_TRUE(numbers[i] > 0);
|
|
TEST_ASSERT_TRUE(numbers[i] <= 10);
|
|
}
|
|
|
|
free(numbers);
|
|
}
|
|
|
|
// Test 4.2: All values are positive (for len=20)
|
|
void test_createNumbers_ValueRangeLen20(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(20);
|
|
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
|
|
// Check each value is positive and reasonable
|
|
for (unsigned int i = 0; i < 20; i++)
|
|
{
|
|
TEST_ASSERT_TRUE(numbers[i] > 0);
|
|
TEST_ASSERT_TRUE(numbers[i] <= 40);
|
|
}
|
|
|
|
free(numbers);
|
|
}
|
|
|
|
// ============================================================================
|
|
// TEST SUITE 5: createNumbers() - Duplicate Detection
|
|
// ============================================================================
|
|
|
|
// Test 5.1: Exactly one value appears twice (len=5)
|
|
void test_createNumbers_ExactlyOneDuplicateLen5(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(5);
|
|
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
|
|
// Count how many distinct values appear exactly twice
|
|
unsigned int valuesAppearingTwice = 0;
|
|
unsigned int counted[5] = {0}; // Track which values we've counted
|
|
|
|
for (unsigned int i = 0; i < 5; i++)
|
|
{
|
|
unsigned int value = numbers[i];
|
|
unsigned int count = countOccurrences(numbers, 5, value);
|
|
|
|
// If this value appears twice and we haven't counted it yet, count it now
|
|
if (count == 2)
|
|
{
|
|
int alreadyCounted = 0;
|
|
for (unsigned int j = 0; j < 5; j++)
|
|
{
|
|
if (counted[j] == value)
|
|
{
|
|
alreadyCounted = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!alreadyCounted)
|
|
{
|
|
valuesAppearingTwice++;
|
|
counted[i] = value; // Mark this value as counted
|
|
}
|
|
}
|
|
}
|
|
|
|
// Should have exactly one value that appears twice
|
|
TEST_ASSERT_EQUAL_INT(1, valuesAppearingTwice);
|
|
|
|
// Count distinct values - should be len-1 = 4
|
|
unsigned int distinct = countDistinctValues(numbers, 5);
|
|
TEST_ASSERT_EQUAL_INT(4, distinct);
|
|
|
|
free(numbers);
|
|
}
|
|
|
|
// Test 5.2: Exactly one value appears twice (len=10)
|
|
void test_createNumbers_ExactlyOneDuplicateLen10(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(10);
|
|
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
|
|
// Count how many distinct values appear exactly twice
|
|
unsigned int valuesAppearingTwice = 0;
|
|
unsigned int counted[10] = {0}; // Track which values we've counted
|
|
|
|
for (unsigned int i = 0; i < 10; i++)
|
|
{
|
|
unsigned int value = numbers[i];
|
|
unsigned int count = countOccurrences(numbers, 10, value);
|
|
|
|
// If this value appears twice and we haven't counted it yet, count it now
|
|
if (count == 2)
|
|
{
|
|
int alreadyCounted = 0;
|
|
for (unsigned int j = 0; j < 10; j++)
|
|
{
|
|
if (counted[j] == value)
|
|
{
|
|
alreadyCounted = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!alreadyCounted)
|
|
{
|
|
valuesAppearingTwice++;
|
|
counted[i] = value; // Mark this value as counted
|
|
}
|
|
}
|
|
}
|
|
|
|
// Should have exactly one value that appears twice
|
|
TEST_ASSERT_EQUAL_INT(1, valuesAppearingTwice);
|
|
|
|
// Count distinct values - should be len-1 = 9
|
|
unsigned int distinct = countDistinctValues(numbers, 10);
|
|
TEST_ASSERT_EQUAL_INT(9, distinct);
|
|
|
|
free(numbers);
|
|
}
|
|
|
|
// Test 5.3: No accidental triplicates (len=5)
|
|
void test_createNumbers_NoTriplicatesLen5(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(5);
|
|
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
|
|
// Count occurrences of each value
|
|
for (unsigned int i = 0; i < 5; i++)
|
|
{
|
|
unsigned int count = countOccurrences(numbers, 5, numbers[i]);
|
|
|
|
// Each value should appear 1 or 2 times, never 3 or more
|
|
TEST_ASSERT_TRUE(count == 1 || count == 2);
|
|
}
|
|
|
|
free(numbers);
|
|
}
|
|
|
|
// ============================================================================
|
|
// TEST SUITE 6: createNumbers() - No Accidental Duplicates Before Intentional One
|
|
// ============================================================================
|
|
|
|
// Test 6.1: Total count validation - sum should be len
|
|
void test_createNumbers_TotalCountLen5(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(5);
|
|
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
|
|
// Sum of all occurrences should equal len (5)
|
|
// (unique_count * 1) + (1 duplicate_value * 2) = len
|
|
// which simplifies to: (len-1) + 2 = len + 1 ... wait that's wrong
|
|
|
|
// Actually: if we have 4 unique values and 1 appears twice:
|
|
// 4 unique values, but 1 of them appears twice
|
|
// So: 3 values appearing once + 1 value appearing twice = 3 + 2 = 5 total entries
|
|
|
|
// Total unique values = len - 1 = 4
|
|
// But sum of elements = len = 5
|
|
// This checks out: (len-1-1) * 1 + 1 * 2 = (len-2) + 2 = len ✓
|
|
|
|
unsigned int distinct = countDistinctValues(numbers, 5);
|
|
TEST_ASSERT_EQUAL_INT(4, distinct);
|
|
|
|
free(numbers);
|
|
}
|
|
|
|
// ============================================================================
|
|
// TEST SUITE 7: createNumbers() - Randomness
|
|
// ============================================================================
|
|
|
|
// Test 7.1: Multiple calls produce different (or can produce different) results
|
|
void test_createNumbers_DifferentRandomResults(void)
|
|
{
|
|
unsigned int *numbers1 = createNumbers(5);
|
|
|
|
// Advance the random sequence a bit
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
rand();
|
|
}
|
|
|
|
unsigned int *numbers2 = createNumbers(5);
|
|
|
|
TEST_ASSERT_NOT_NULL(numbers1);
|
|
TEST_ASSERT_NOT_NULL(numbers2);
|
|
|
|
// It's possible (though unlikely) that two random arrays are identical
|
|
// So we'll just verify that the function works and can produce different results
|
|
// by checking that both have correct structure
|
|
int identical = 1;
|
|
for (unsigned int i = 0; i < 5; i++)
|
|
{
|
|
if (numbers1[i] != numbers2[i])
|
|
{
|
|
identical = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Note: identical could be 0 or 1, both are valid test outcomes
|
|
// The important thing is that both arrays are valid
|
|
(void)identical;
|
|
|
|
free(numbers1);
|
|
free(numbers2);
|
|
}
|
|
|
|
// ============================================================================
|
|
// TEST SUITE 8: getDuplicate() - Finding the Duplicate
|
|
// ============================================================================
|
|
|
|
// Test 8.1: getDuplicate finds the correct value for len=5
|
|
void test_getDuplicate_FindsCorrectValueLen5(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(5);
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
|
|
// Find the duplicate manually (using our test helper)
|
|
unsigned int expectedDuplicate = findDuplicate(numbers, 5);
|
|
TEST_ASSERT_NOT_EQUAL(0, expectedDuplicate);
|
|
|
|
// Test getDuplicate() - should find the same duplicate
|
|
unsigned int actualDuplicate = getDuplicate(numbers, 5);
|
|
TEST_ASSERT_EQUAL_INT(expectedDuplicate, actualDuplicate);
|
|
|
|
free(numbers);
|
|
}
|
|
|
|
// Test 8.2: getDuplicate finds the correct value for len=10
|
|
void test_getDuplicate_FindsCorrectValueLen10(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(10);
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
|
|
// Find the duplicate manually (using our test helper)
|
|
unsigned int expectedDuplicate = findDuplicate(numbers, 10);
|
|
TEST_ASSERT_NOT_EQUAL(0, expectedDuplicate);
|
|
|
|
// Test getDuplicate() - should find the same duplicate
|
|
unsigned int actualDuplicate = getDuplicate(numbers, 10);
|
|
TEST_ASSERT_EQUAL_INT(expectedDuplicate, actualDuplicate);
|
|
|
|
free(numbers);
|
|
}
|
|
|
|
// Test 8.3: getDuplicate handles NULL pointer gracefully
|
|
void test_getDuplicate_NullPointerReturnsZero(void)
|
|
{
|
|
unsigned int result = getDuplicate(NULL, 5);
|
|
TEST_ASSERT_EQUAL_INT(0, result);
|
|
}
|
|
|
|
// Test 8.4: getDuplicate handles len < 2 gracefully
|
|
void test_getDuplicate_InvalidLenReturnsZero(void)
|
|
{
|
|
unsigned int arr[] = {1};
|
|
|
|
// len = 0
|
|
unsigned int result0 = getDuplicate(arr, 0);
|
|
TEST_ASSERT_EQUAL_INT(0, result0);
|
|
|
|
// len = 1
|
|
unsigned int result1 = getDuplicate(arr, 1);
|
|
TEST_ASSERT_EQUAL_INT(0, result1);
|
|
}
|
|
|
|
// Test 8.5: getDuplicate works with len=20
|
|
void test_getDuplicate_FindsCorrectValueLen20(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(20);
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
|
|
// Find the duplicate using our test helper
|
|
unsigned int expectedDuplicate = findDuplicate(numbers, 20);
|
|
TEST_ASSERT_NOT_EQUAL(0, expectedDuplicate);
|
|
|
|
// Test getDuplicate()
|
|
unsigned int actualDuplicate = getDuplicate(numbers, 20);
|
|
TEST_ASSERT_EQUAL_INT(expectedDuplicate, actualDuplicate);
|
|
|
|
free(numbers);
|
|
}
|
|
|
|
// Test 8.6: getDuplicate doesn't modify the input array
|
|
void test_getDuplicate_DoesNotModifyInputArray(void)
|
|
{
|
|
unsigned int *original = createNumbers(8);
|
|
TEST_ASSERT_NOT_NULL(original);
|
|
|
|
// Create a copy to compare before and after
|
|
unsigned int *copy = (unsigned int *)malloc(8 * sizeof(unsigned int));
|
|
memcpy(copy, original, 8 * sizeof(unsigned int));
|
|
|
|
// Call getDuplicate (which creates its own sorted copy internally)
|
|
getDuplicate(original, 8);
|
|
|
|
// Verify the input array wasn't modified
|
|
for (unsigned int i = 0; i < 8; i++)
|
|
{
|
|
TEST_ASSERT_EQUAL_INT(copy[i], original[i]);
|
|
}
|
|
|
|
free(original);
|
|
free(copy);
|
|
}
|
|
|
|
// ============================================================================
|
|
// TEST SUITE 9: Integration Tests
|
|
// ============================================================================
|
|
|
|
// Test 9.1: Full workflow for len=3
|
|
void test_integration_CreateAndValidateLen3(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(3);
|
|
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
|
|
// Validate: 3 elements exist and have valid values
|
|
for (unsigned int i = 0; i < 3; i++)
|
|
{
|
|
TEST_ASSERT_TRUE(numbers[i] > 0);
|
|
}
|
|
|
|
// Validate: exactly 2 distinct values (one appears twice)
|
|
unsigned int distinct = countDistinctValues(numbers, 3);
|
|
TEST_ASSERT_EQUAL_INT(2, distinct);
|
|
|
|
free(numbers);
|
|
}
|
|
|
|
// Test 9.2: Full workflow for len=15
|
|
void test_integration_CreateAndValidateLen15(void)
|
|
{
|
|
unsigned int *numbers = createNumbers(15);
|
|
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
|
|
// Validate: 15 elements exist and have valid values
|
|
for (unsigned int i = 0; i < 15; i++)
|
|
{
|
|
TEST_ASSERT_TRUE(numbers[i] > 0);
|
|
}
|
|
|
|
// Validate: exactly 14 distinct values
|
|
unsigned int distinct = countDistinctValues(numbers, 15);
|
|
TEST_ASSERT_EQUAL_INT(14, distinct);
|
|
|
|
free(numbers);
|
|
}
|
|
|
|
// Test 9.3: Memory cleanup - no segfault
|
|
void test_integration_MultipleAllocationsAndFrees(void)
|
|
{
|
|
for (unsigned int run = 0; run < 5; run++)
|
|
{
|
|
unsigned int *numbers = createNumbers(5 + run);
|
|
TEST_ASSERT_NOT_NULL(numbers);
|
|
free(numbers);
|
|
}
|
|
|
|
// If we reach here without segfault, cleanup worked
|
|
TEST_ASSERT_TRUE(1);
|
|
}
|
|
|
|
// ============================================================================
|
|
// TEST RUNNER
|
|
// ============================================================================
|
|
|
|
int main(void)
|
|
{
|
|
UNITY_BEGIN();
|
|
|
|
// Initialize random number seed once at the beginning
|
|
srand(time(NULL));
|
|
|
|
// Test Suite 1: Input Validation
|
|
RUN_TEST(test_createNumbers_InvalidLenZero);
|
|
RUN_TEST(test_createNumbers_InvalidLenOne);
|
|
|
|
// Test Suite 2: Minimum Valid Input
|
|
RUN_TEST(test_createNumbers_MinimumSize);
|
|
|
|
// Test Suite 3: Array Size
|
|
RUN_TEST(test_createNumbers_ArraySize5);
|
|
RUN_TEST(test_createNumbers_ArraySize10);
|
|
|
|
// Test Suite 4: Value Range
|
|
RUN_TEST(test_createNumbers_ValueRangeLen5);
|
|
RUN_TEST(test_createNumbers_ValueRangeLen20);
|
|
|
|
// Test Suite 5: Duplicate Detection
|
|
RUN_TEST(test_createNumbers_ExactlyOneDuplicateLen5);
|
|
RUN_TEST(test_createNumbers_ExactlyOneDuplicateLen10);
|
|
RUN_TEST(test_createNumbers_NoTriplicatesLen5);
|
|
|
|
// Test Suite 6: No Accidental Duplicates
|
|
RUN_TEST(test_createNumbers_TotalCountLen5);
|
|
|
|
// Test Suite 7: Randomness
|
|
RUN_TEST(test_createNumbers_DifferentRandomResults);
|
|
|
|
// Test Suite 8: getDuplicate
|
|
RUN_TEST(test_getDuplicate_FindsCorrectValueLen5);
|
|
RUN_TEST(test_getDuplicate_FindsCorrectValueLen10);
|
|
RUN_TEST(test_getDuplicate_NullPointerReturnsZero);
|
|
RUN_TEST(test_getDuplicate_InvalidLenReturnsZero);
|
|
RUN_TEST(test_getDuplicate_FindsCorrectValueLen20);
|
|
RUN_TEST(test_getDuplicate_DoesNotModifyInputArray);
|
|
|
|
// Test Suite 9: Integration
|
|
RUN_TEST(test_integration_CreateAndValidateLen3);
|
|
RUN_TEST(test_integration_CreateAndValidateLen15);
|
|
RUN_TEST(test_integration_MultipleAllocationsAndFrees);
|
|
|
|
return UNITY_END();
|
|
}
|