#include "unity.h" #include "numbers.h" #include #include #include /* ============================================================================ * 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(); }