Sortierung+Binärbaum
This commit is contained in:
parent
a5c502da46
commit
a81141fb06
178
bintree.c
178
bintree.c
@ -2,35 +2,183 @@
|
||||
#include "stack.h"
|
||||
#include "bintree.h"
|
||||
|
||||
//TODO: binären Suchbaum implementieren
|
||||
/* * `addToTree`: fügt ein neues Element in den Baum ein (rekursiv),
|
||||
* `clearTree`: gibt den gesamten Baum frei (rekursiv),
|
||||
* `treeSize`: zählt die Knoten im Baum (rekursiv),
|
||||
* `nextTreeData`: Traversierung mit Hilfe des zuvor implementierten Stacks. */
|
||||
// Global static stack used for in-order tree traversal in nextTreeData()
|
||||
// This is needed because nextTreeData() follows strtok-like usage where consecutive calls
|
||||
// with NULL argument continue the iteration
|
||||
static StackNode *traversalStack = NULL;
|
||||
|
||||
// Adds a copy of data's pointer destination to the tree using compareFct for ordering. Accepts duplicates
|
||||
// if isDuplicate is NULL, otherwise ignores duplicates and sets isDuplicate to 1 (or to 0 if a new entry is added).
|
||||
// Adds a copy of data's pointer destination to the tree using compareFct for ordering.
|
||||
// Accepts duplicates if isDuplicate is NULL, otherwise ignores duplicates and sets isDuplicate to 1 (or to 0 if a new entry is added).
|
||||
// Parameters:
|
||||
// - root: pointer to the root of the tree (can be NULL for first insertion)
|
||||
// - data: pointer to the data to be inserted
|
||||
// - dataSize: size in bytes of the data to allocate and copy
|
||||
// - compareFct: comparison function that returns <0 if arg1<arg2, 0 if equal, >0 if arg1>arg2
|
||||
// - isDuplicate: pointer to int flag (NULL to allow duplicates, non-NULL to detect and reject duplicates)
|
||||
// Returns: pointer to the root of the modified tree
|
||||
TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate)
|
||||
{
|
||||
|
||||
// Base case: if root is NULL, we've found the insertion point
|
||||
// Create a new tree node here
|
||||
if (root == NULL)
|
||||
{
|
||||
// Allocate memory for the new tree node
|
||||
TreeNode *newNode = (TreeNode *)malloc(sizeof(TreeNode));
|
||||
if (newNode == NULL)
|
||||
{
|
||||
return NULL; // Allocation failed
|
||||
}
|
||||
|
||||
// Iterates over the tree given by root. Follows the usage of strtok. If tree is NULL, the next entry of the last tree given is returned in ordering direction.
|
||||
// Use your implementation of a stack to organize the iterator. Push the root node and all left nodes first. On returning the next element,
|
||||
// push the top node and push all its left nodes.
|
||||
// Allocate memory for a copy of the data
|
||||
newNode->data = malloc(dataSize);
|
||||
if (newNode->data == NULL)
|
||||
{
|
||||
free(newNode); // Free the node if data allocation failed
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Copy the data into the newly allocated space
|
||||
memcpy(newNode->data, data, dataSize);
|
||||
|
||||
// Initialize the left and right child pointers to NULL
|
||||
newNode->left = NULL;
|
||||
newNode->right = NULL;
|
||||
|
||||
// If isDuplicate is not NULL, set it to 0 to indicate a new entry was added
|
||||
if (isDuplicate != NULL)
|
||||
{
|
||||
*isDuplicate = 0;
|
||||
}
|
||||
|
||||
// Return the newly created node
|
||||
return newNode;
|
||||
}
|
||||
|
||||
// Recursive case: compare the data with the root's data to determine where to insert
|
||||
int comparisonResult = compareFct(data, root->data);
|
||||
|
||||
// If data is less than root->data, insert into the left subtree
|
||||
if (comparisonResult < 0)
|
||||
{
|
||||
root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate);
|
||||
}
|
||||
// If data is greater than root->data, insert into the right subtree
|
||||
else if (comparisonResult > 0)
|
||||
{
|
||||
root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate);
|
||||
}
|
||||
// If data equals root->data, we have a duplicate
|
||||
else
|
||||
{
|
||||
// If isDuplicate is not NULL, set it to 1 to indicate a duplicate was found
|
||||
if (isDuplicate != NULL)
|
||||
{
|
||||
*isDuplicate = 1;
|
||||
}
|
||||
// If isDuplicate is NULL, we allow the duplicate to be inserted again (in either subtree)
|
||||
// For standard BST behavior, we typically insert duplicates in the right subtree
|
||||
else
|
||||
{
|
||||
root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the root (possibly with new subtrees added)
|
||||
return root;
|
||||
}
|
||||
|
||||
// Iterates over the tree given by root in in-order traversal (Left, Root, Right).
|
||||
// Follows the usage pattern of strtok: first call with root pointer, subsequent calls with NULL to get next element.
|
||||
// Uses a static stack to maintain traversal state between calls.
|
||||
// Parameters:
|
||||
// - root: pointer to tree root for first call, NULL for subsequent calls
|
||||
// Returns: pointer to the data of the next node in in-order sequence, or NULL when traversal is complete
|
||||
void *nextTreeData(TreeNode *root)
|
||||
{
|
||||
// If root is not NULL, we're starting a new traversal
|
||||
if (root != NULL)
|
||||
{
|
||||
// Clear any existing traversal state
|
||||
clearStack(traversalStack);
|
||||
traversalStack = NULL;
|
||||
|
||||
// Initialize the traversal by pushing root and all its left descendants
|
||||
// In in-order traversal, we always visit left children first
|
||||
TreeNode *current = root;
|
||||
while (current != NULL)
|
||||
{
|
||||
// Push current node onto the stack
|
||||
traversalStack = push(traversalStack, current);
|
||||
// Move to the left child
|
||||
current = current->left;
|
||||
}
|
||||
}
|
||||
|
||||
// Releases all memory resources (including data copies).
|
||||
// If the traversal stack is empty, we've visited all nodes
|
||||
if (traversalStack == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Pop the next node from the stack
|
||||
TreeNode *nextNode = (TreeNode *)top(traversalStack);
|
||||
traversalStack = pop(traversalStack);
|
||||
|
||||
// If the popped node has a right child, push it and all its left descendants
|
||||
// This handles the "Root, Right" part of in-order traversal
|
||||
if (nextNode->right != NULL)
|
||||
{
|
||||
TreeNode *current = nextNode->right;
|
||||
while (current != NULL)
|
||||
{
|
||||
// Push the current node
|
||||
traversalStack = push(traversalStack, current);
|
||||
// Move to the left child of the right subtree
|
||||
current = current->left;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the data of the node we just processed
|
||||
return nextNode->data;
|
||||
}
|
||||
|
||||
// Releases all memory resources used by the tree.
|
||||
// Recursively frees all nodes and their data copies.
|
||||
// Parameters:
|
||||
// - root: pointer to the root of the tree to clear
|
||||
// Returns: void
|
||||
void clearTree(TreeNode *root)
|
||||
{
|
||||
|
||||
// Base case: if root is NULL, there's nothing to free
|
||||
if (root == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Returns the number of entries in the tree given by root.
|
||||
// Recursive case: first recursively clear the left subtree
|
||||
clearTree(root->left);
|
||||
|
||||
// Then recursively clear the right subtree
|
||||
clearTree(root->right);
|
||||
|
||||
// Finally, free the data and the node itself
|
||||
free(root->data);
|
||||
free(root);
|
||||
}
|
||||
|
||||
// Returns the number of entries (nodes) in the tree.
|
||||
// Recursively counts nodes in the left and right subtrees.
|
||||
// Parameters:
|
||||
// - root: pointer to the root of the tree
|
||||
// Returns: unsigned int representing the total number of nodes in the tree
|
||||
unsigned int treeSize(const TreeNode *root)
|
||||
{
|
||||
|
||||
// Base case: if root is NULL, there are no nodes
|
||||
if (root == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Recursive case: count 1 for the current node, plus all nodes in left and right subtrees
|
||||
return 1 + treeSize(root->left) + treeSize(root->right);
|
||||
}
|
||||
BIN
bintree_tests
Executable file
BIN
bintree_tests
Executable file
Binary file not shown.
BIN
doble_initial
Executable file
BIN
doble_initial
Executable file
Binary file not shown.
@ -1 +1,2 @@
|
||||
player_name;6999
|
||||
player1;3999
|
||||
|
||||
19
makefile
19
makefile
@ -38,12 +38,27 @@ $(program_obj_files): %.o: %.c
|
||||
unitTests:
|
||||
echo "needs to be implemented"
|
||||
|
||||
# --------------------------
|
||||
# Test: test_numbers
|
||||
# --------------------------
|
||||
test_numbers: stack.o bintree.o numbers.o unity.o
|
||||
$(CC) $(FLAGS) -Iunity test_numbers.c $^ -o runTest_numbers
|
||||
|
||||
unity.o:
|
||||
$(CC) -c $(FLAGS) -Iunity unity/unity.c -o unity.o
|
||||
|
||||
# --------------------------
|
||||
# Test: test_bintree
|
||||
# --------------------------
|
||||
test_bintree: stack.o bintree.o unity.o
|
||||
$(CC) $(FLAGS) -Iunity test_bintree.c $^ -o runTest_bintree
|
||||
|
||||
# --------------------------
|
||||
# Clean
|
||||
# --------------------------
|
||||
clean:
|
||||
ifeq ($(OS),Windows_NT)
|
||||
del /f *.o doble
|
||||
del /f *.o doble runTest_numbers runTest_bintree
|
||||
else
|
||||
rm -f *.o doble
|
||||
rm -f *.o doble runTest_numbers runTest_bintree
|
||||
endif
|
||||
139
numbers.c
139
numbers.c
@ -11,16 +11,151 @@
|
||||
* Duplizieren eines zufälligen Eintrags im Array.
|
||||
* in `getDuplicate()`: Sortieren des Arrays und Erkennen der doppelten Zahl durch Vergleich benachbarter Elemente. */
|
||||
|
||||
// Comparison function for unsigned integers used in the binary search tree
|
||||
// Returns: <0 if a < b, 0 if a == b, >0 if a > b
|
||||
// This function is passed to addToTree to determine ordering
|
||||
int compareUnsignedInts(const void *a, const void *b)
|
||||
{
|
||||
unsigned int valA = *(const unsigned int *)a;
|
||||
unsigned int valB = *(const unsigned int *)b;
|
||||
|
||||
// Simple subtraction works for unsigned integers in this case
|
||||
// (Note: in general subtraction can overflow, but our range is small)
|
||||
if (valA < valB) return -1;
|
||||
if (valA > valB) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns len random numbers between 1 and 2x len in random order which are all different, except for two entries.
|
||||
// Returns NULL on errors. Use your implementation of the binary search tree to check for possible duplicates while
|
||||
// creating random numbers.
|
||||
//
|
||||
// Algorithm:
|
||||
// 1. Generate len-1 unique random numbers using the BST for duplicate detection
|
||||
// 2. Randomly duplicate one of those numbers by inserting it at a random position
|
||||
// 3. Final array has len entries with exactly one value appearing twice
|
||||
unsigned int *createNumbers(unsigned int len)
|
||||
{
|
||||
// Step 1: Validate input - we need at least 2 elements to create a duplicate
|
||||
if (len < 2)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Step 2: Allocate memory for the final array (len elements total)
|
||||
unsigned int *numbers = (unsigned int *)malloc(len * sizeof(unsigned int));
|
||||
if (numbers == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Step 3: Create binary search tree to track generated numbers and prevent accidental duplicates
|
||||
// Note: Random seed should be initialized once in main() with srand(time(NULL))
|
||||
// Calling srand() here would reset the seed each time, reducing randomness
|
||||
TreeNode *usedNumbers = NULL;
|
||||
|
||||
// Step 4: Generate len-1 unique random numbers (we'll add one duplicate later)
|
||||
unsigned int arrayIndex = 0;
|
||||
|
||||
while (arrayIndex < len - 1)
|
||||
{
|
||||
// Generate random number in range [1, 2×len]
|
||||
unsigned int randomNumber = (rand() % (2 * len)) + 1;
|
||||
|
||||
// Check if this number was already generated
|
||||
int isDuplicate = 0;
|
||||
usedNumbers = addToTree(usedNumbers, &randomNumber, sizeof(unsigned int), compareUnsignedInts, &isDuplicate);
|
||||
|
||||
if (usedNumbers == NULL)
|
||||
{
|
||||
free(numbers);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// If new number, add to result array; if duplicate, retry
|
||||
if (isDuplicate == 0)
|
||||
{
|
||||
numbers[arrayIndex] = randomNumber;
|
||||
arrayIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5: Pick a random value from our generated numbers to duplicate
|
||||
unsigned int indexToDuplicate = rand() % (len - 1);
|
||||
unsigned int valueToDuplicate = numbers[indexToDuplicate];
|
||||
|
||||
// Step 6: Pick a random position to insert the duplicate (anywhere in the array)
|
||||
unsigned int insertPos = rand() % len;
|
||||
|
||||
// Step 7: Shift elements right to make room for the duplicate
|
||||
for (unsigned int i = len - 1; i > insertPos; i--)
|
||||
{
|
||||
numbers[i] = numbers[i - 1];
|
||||
}
|
||||
|
||||
// Step 8: Insert the duplicate at the chosen position
|
||||
numbers[insertPos] = valueToDuplicate;
|
||||
|
||||
// Step 9: Clean up the binary search tree (we don't need it anymore)
|
||||
clearTree(usedNumbers);
|
||||
|
||||
// Return the array with len elements (len-1 unique + 1 duplicate)
|
||||
return numbers;
|
||||
}
|
||||
|
||||
// Comparison function for qsort to sort unsigned integers in ascending order
|
||||
// Returns: <0 if a < b, 0 if a == b, >0 if a > b
|
||||
static int compareUnsigned(const void *a, const void *b)
|
||||
{
|
||||
unsigned int valA = *(const unsigned int *)a;
|
||||
unsigned int valB = *(const unsigned int *)b;
|
||||
|
||||
if (valA < valB) return -1;
|
||||
if (valA > valB) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns only the only number in numbers which is present twice. Returns zero on errors.
|
||||
unsigned int getDuplicate(const unsigned int numbers[], unsigned int len)
|
||||
//
|
||||
// Algorithm:
|
||||
// 1. Create a copy of the array to avoid modifying the input
|
||||
// 2. Sort the copy using qsort()
|
||||
// 3. Compare adjacent elements to find the duplicate
|
||||
// 4. Return the duplicate value, or 0 on error
|
||||
unsigned int getDuplicate(const unsigned int *numbers, unsigned int len)
|
||||
{
|
||||
|
||||
// Step 1: Input validation - need at least 2 elements to have a duplicate
|
||||
if (numbers == NULL || len < 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Step 2: Create a copy of the array to sort (we don't modify the input)
|
||||
unsigned int *sorted = (unsigned int *)malloc(len * sizeof(unsigned int));
|
||||
if (sorted == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Step 3: Copy the array
|
||||
memcpy(sorted, numbers, len * sizeof(unsigned int));
|
||||
|
||||
// Step 4: Sort the copy
|
||||
qsort(sorted, len, sizeof(unsigned int), compareUnsigned);
|
||||
|
||||
// Step 5: Compare adjacent elements to find the duplicate
|
||||
for (unsigned int i = 0; i < len - 1; i++)
|
||||
{
|
||||
if (sorted[i] == sorted[i + 1])
|
||||
{
|
||||
// Found the duplicate!
|
||||
unsigned int result = sorted[i];
|
||||
free(sorted);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6: No duplicate found (should not happen if input is valid)
|
||||
free(sorted);
|
||||
return 0;
|
||||
}
|
||||
BIN
runTest_bintree
Executable file
BIN
runTest_bintree
Executable file
Binary file not shown.
BIN
runTest_numbers
Executable file
BIN
runTest_numbers
Executable file
Binary file not shown.
92
stack.c
92
stack.c
@ -1,33 +1,99 @@
|
||||
#include <stdlib.h>
|
||||
#include "stack.h"
|
||||
|
||||
//TODO: grundlegende Stackfunktionen implementieren:
|
||||
/* * `push`: legt ein Element oben auf den Stack,
|
||||
* `pop`: entfernt das oberste Element,
|
||||
* `top`: liefert das oberste Element zurück,
|
||||
* `clearStack`: gibt den gesamten Speicher frei. */
|
||||
|
||||
// Pushes data as pointer onto the stack.
|
||||
// Parameters:
|
||||
// - stack: pointer to the current top of the stack (can be NULL if stack is empty)
|
||||
// - data: void pointer to the data to be pushed onto the stack
|
||||
// Returns: pointer to the new top of the stack (the newly created node)
|
||||
// Note: The function allocates memory for a new StackNode, sets its data pointer
|
||||
// and links it to the previous top, making it the new top of the stack.
|
||||
StackNode *push(StackNode *stack, void *data)
|
||||
{
|
||||
// Allocate memory for a new StackNode
|
||||
StackNode *newNode = (StackNode *)malloc(sizeof(StackNode));
|
||||
|
||||
// Check if memory allocation was successful
|
||||
if (newNode == NULL)
|
||||
{
|
||||
return NULL; // Return NULL if allocation failed
|
||||
}
|
||||
|
||||
// Deletes the top element of the stack (latest added element) and releases its memory. (Pointer to data has to be
|
||||
// freed by caller.)
|
||||
// Store the data pointer in the new node
|
||||
newNode->data = data;
|
||||
|
||||
// Link the new node to the previous top of the stack
|
||||
// If stack was NULL (empty), newNode->next will be NULL
|
||||
// If stack was not NULL, newNode->next points to the old top
|
||||
newNode->next = stack;
|
||||
|
||||
// Return the new node, which is now the new top of the stack
|
||||
return newNode;
|
||||
}
|
||||
|
||||
// Deletes the top element of the stack (latest added element) and releases its memory.
|
||||
// Parameters:
|
||||
// - stack: pointer to the current top of the stack
|
||||
// Returns: pointer to the new top of the stack (the second element, or NULL if stack becomes empty)
|
||||
// Note: This function only frees the StackNode structure itself. The caller is responsible
|
||||
// for freeing the data that the node was pointing to, if that data was dynamically allocated.
|
||||
StackNode *pop(StackNode *stack)
|
||||
{
|
||||
|
||||
// Check if the stack is empty
|
||||
if (stack == NULL)
|
||||
{
|
||||
return NULL; // Cannot pop from an empty stack
|
||||
}
|
||||
|
||||
// Returns the data of the top element.
|
||||
// Save a pointer to the next node (which will become the new top)
|
||||
StackNode *newTop = stack->next;
|
||||
|
||||
// Free the memory of the current top node
|
||||
// NOTE: We do NOT free stack->data here because the caller may still need it
|
||||
// or may be responsible for freeing it themselves
|
||||
free(stack);
|
||||
|
||||
// Return the new top of the stack (or NULL if the stack is now empty)
|
||||
return newTop;
|
||||
}
|
||||
|
||||
// Returns the data of the top element without removing it from the stack.
|
||||
// Parameters:
|
||||
// - stack: pointer to the current top of the stack
|
||||
// Returns: void pointer to the data of the top element, or NULL if stack is empty
|
||||
// Note: This is a "peek" operation - it looks at the top without removing it.
|
||||
void *top(StackNode *stack)
|
||||
{
|
||||
|
||||
// Check if the stack is empty
|
||||
if (stack == NULL)
|
||||
{
|
||||
return NULL; // Return NULL if stack is empty
|
||||
}
|
||||
|
||||
// Clears stack and releases all memory.
|
||||
// Return the data pointer stored in the top node
|
||||
// The caller can then use this pointer to access the actual data
|
||||
return stack->data;
|
||||
}
|
||||
|
||||
// Clears the entire stack and releases all memory used by the StackNodes.
|
||||
// Parameters:
|
||||
// - stack: pointer to the current top of the stack
|
||||
// Returns: void
|
||||
// Note: This function only frees the StackNode structures. The caller is responsible
|
||||
// for freeing any dynamically allocated data that the nodes were pointing to,
|
||||
// unless that is done elsewhere in the program before calling clearStack.
|
||||
void clearStack(StackNode *stack)
|
||||
{
|
||||
|
||||
// Base case: if the stack is NULL (empty), we're done
|
||||
if (stack == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Recursive case: first, recursively clear all the nodes below this one
|
||||
// This processes the stack from top to bottom
|
||||
clearStack(stack->next);
|
||||
|
||||
// After recursively clearing all deeper nodes, free the current node
|
||||
free(stack);
|
||||
}
|
||||
7
stack.h
7
stack.h
@ -7,7 +7,12 @@ The latest element is taken from the stack. */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
//TODO: passenden Datentyp als struct anlegen
|
||||
// Stack node structure: each node contains data and a pointer to the next node
|
||||
typedef struct stackNode
|
||||
{
|
||||
void *data;
|
||||
struct stackNode *next;
|
||||
} StackNode;
|
||||
|
||||
// Pushes data as pointer onto the stack.
|
||||
StackNode *push(StackNode *stack, void *data);
|
||||
|
||||
592
test_bintree.c
Normal file
592
test_bintree.c
Normal file
@ -0,0 +1,592 @@
|
||||
#include "unity.h"
|
||||
#include "bintree.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ============================================================================
|
||||
* UNIT TESTS FOR BINARY SEARCH TREE IMPLEMENTATION (Binärer Suchbaum)
|
||||
*
|
||||
* This test suite covers all four functions:
|
||||
* - addToTree(): Insert elements maintaining BST ordering
|
||||
* - clearTree(): Free all memory in the tree
|
||||
* - treeSize(): Count nodes in the tree
|
||||
* - nextTreeData(): In-order traversal using stack
|
||||
* ========================================================================== */
|
||||
|
||||
// ============================================================================
|
||||
// HELPER FUNCTIONS FOR TESTING
|
||||
// ============================================================================
|
||||
|
||||
// Comparison function for integers (ascending order: smaller values first)
|
||||
// Returns: <0 if a < b, 0 if a == b, >0 if a > b
|
||||
int compareIntegers(const void *a, const void *b)
|
||||
{
|
||||
int valA = *(const int *)a;
|
||||
int valB = *(const int *)b;
|
||||
return valA - valB;
|
||||
}
|
||||
|
||||
// Comparison function for strings (alphabetical order)
|
||||
int compareStrings(const void *a, const void *b)
|
||||
{
|
||||
return strcmp((const char *)a, (const char *)b);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TEST SETUP AND TEARDOWN
|
||||
// ============================================================================
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
// Perform any setup needed before each test
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
// Perform any cleanup needed after each test
|
||||
// Individual tests are responsible for clearing their trees
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TEST SUITE 1: addToTree() - Inserting Elements
|
||||
// ============================================================================
|
||||
|
||||
// Test 1.1: Insert a single element into an empty tree (NULL root)
|
||||
void test_addToTree_SingleElement(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int value = 42;
|
||||
int isDuplicate = 0;
|
||||
|
||||
// Insert the first element into an empty tree
|
||||
root = addToTree(root, &value, sizeof(int), compareIntegers, &isDuplicate);
|
||||
|
||||
// Verify the root is not NULL
|
||||
TEST_ASSERT_NOT_NULL(root);
|
||||
|
||||
// Verify the data was copied correctly
|
||||
TEST_ASSERT_EQUAL_INT(42, *(int *)root->data);
|
||||
|
||||
// Verify isDuplicate was set to 0 (new entry added)
|
||||
TEST_ASSERT_EQUAL_INT(0, isDuplicate);
|
||||
|
||||
// Verify the node has no children
|
||||
TEST_ASSERT_NULL(root->left);
|
||||
TEST_ASSERT_NULL(root->right);
|
||||
|
||||
// Cleanup
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// Test 1.2: Insert multiple elements in ascending order
|
||||
void test_addToTree_AscendingOrder(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int values[] = {10, 20, 30};
|
||||
|
||||
// Insert values in ascending order
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
root = addToTree(root, &values[i], sizeof(int), compareIntegers, NULL);
|
||||
}
|
||||
|
||||
// Verify root value
|
||||
TEST_ASSERT_EQUAL_INT(10, *(int *)root->data);
|
||||
|
||||
// Verify each value goes to the right (since they're ascending)
|
||||
TEST_ASSERT_NULL(root->left);
|
||||
TEST_ASSERT_NOT_NULL(root->right);
|
||||
TEST_ASSERT_EQUAL_INT(20, *(int *)root->right->data);
|
||||
|
||||
TEST_ASSERT_NULL(root->right->left);
|
||||
TEST_ASSERT_NOT_NULL(root->right->right);
|
||||
TEST_ASSERT_EQUAL_INT(30, *(int *)root->right->right->data);
|
||||
|
||||
// Cleanup
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// Test 1.3: Insert multiple elements in descending order
|
||||
void test_addToTree_DescendingOrder(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int values[] = {30, 20, 10};
|
||||
|
||||
// Insert values in descending order
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
root = addToTree(root, &values[i], sizeof(int), compareIntegers, NULL);
|
||||
}
|
||||
|
||||
// Verify root value
|
||||
TEST_ASSERT_EQUAL_INT(30, *(int *)root->data);
|
||||
|
||||
// Verify each value goes to the left (since they're descending)
|
||||
TEST_ASSERT_NULL(root->right);
|
||||
TEST_ASSERT_NOT_NULL(root->left);
|
||||
TEST_ASSERT_EQUAL_INT(20, *(int *)root->left->data);
|
||||
|
||||
TEST_ASSERT_NULL(root->left->right);
|
||||
TEST_ASSERT_NOT_NULL(root->left->left);
|
||||
TEST_ASSERT_EQUAL_INT(10, *(int *)root->left->left->data);
|
||||
|
||||
// Cleanup
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// Test 1.4: Insert elements in balanced order (creating a balanced tree)
|
||||
void test_addToTree_BalancedTree(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int values[] = {20, 10, 30, 5, 15, 25, 35};
|
||||
|
||||
// Insert values to create a somewhat balanced tree
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
root = addToTree(root, &values[i], sizeof(int), compareIntegers, NULL);
|
||||
}
|
||||
|
||||
// Verify root value
|
||||
TEST_ASSERT_EQUAL_INT(20, *(int *)root->data);
|
||||
|
||||
// Verify left subtree
|
||||
TEST_ASSERT_EQUAL_INT(10, *(int *)root->left->data);
|
||||
TEST_ASSERT_EQUAL_INT(5, *(int *)root->left->left->data);
|
||||
TEST_ASSERT_EQUAL_INT(15, *(int *)root->left->right->data);
|
||||
|
||||
// Verify right subtree
|
||||
TEST_ASSERT_EQUAL_INT(30, *(int *)root->right->data);
|
||||
TEST_ASSERT_EQUAL_INT(25, *(int *)root->right->left->data);
|
||||
TEST_ASSERT_EQUAL_INT(35, *(int *)root->right->right->data);
|
||||
|
||||
// Cleanup
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// Test 1.5: Attempt to insert a duplicate when isDuplicate is NULL (allows duplicates)
|
||||
void test_addToTree_AllowDuplicates(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int value1 = 42;
|
||||
int value2 = 42;
|
||||
|
||||
// Insert first instance
|
||||
root = addToTree(root, &value1, sizeof(int), compareIntegers, NULL);
|
||||
|
||||
// Insert duplicate (isDuplicate is NULL, so duplicates are allowed)
|
||||
root = addToTree(root, &value2, sizeof(int), compareIntegers, NULL);
|
||||
|
||||
// Verify tree has both entries (duplicate goes right)
|
||||
TEST_ASSERT_EQUAL_INT(42, *(int *)root->data);
|
||||
TEST_ASSERT_NULL(root->left);
|
||||
TEST_ASSERT_NOT_NULL(root->right);
|
||||
TEST_ASSERT_EQUAL_INT(42, *(int *)root->right->data);
|
||||
|
||||
// Cleanup
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// Test 1.6: Attempt to insert a duplicate when isDuplicate is provided (rejects duplicates)
|
||||
void test_addToTree_RejectDuplicates(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int value1 = 42;
|
||||
int value2 = 42;
|
||||
int isDuplicate = 0;
|
||||
|
||||
// Insert first instance
|
||||
root = addToTree(root, &value1, sizeof(int), compareIntegers, &isDuplicate);
|
||||
TEST_ASSERT_EQUAL_INT(0, isDuplicate);
|
||||
|
||||
// Attempt to insert duplicate
|
||||
root = addToTree(root, &value2, sizeof(int), compareIntegers, &isDuplicate);
|
||||
|
||||
// Verify isDuplicate was set to 1
|
||||
TEST_ASSERT_EQUAL_INT(1, isDuplicate);
|
||||
|
||||
// Verify tree still has only one node with the first value
|
||||
TEST_ASSERT_EQUAL_INT(42, *(int *)root->data);
|
||||
TEST_ASSERT_NULL(root->left);
|
||||
TEST_ASSERT_NULL(root->right);
|
||||
|
||||
// Cleanup
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// Test 1.7: Insert multiple elements with duplicate detection
|
||||
void test_addToTree_MultipleWithDuplicateDetection(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int values[] = {20, 10, 30, 10}; // 10 is a duplicate
|
||||
int isDuplicate = 0;
|
||||
|
||||
// Insert 20
|
||||
root = addToTree(root, &values[0], sizeof(int), compareIntegers, &isDuplicate);
|
||||
TEST_ASSERT_EQUAL_INT(0, isDuplicate);
|
||||
|
||||
// Insert 10 (new)
|
||||
root = addToTree(root, &values[1], sizeof(int), compareIntegers, &isDuplicate);
|
||||
TEST_ASSERT_EQUAL_INT(0, isDuplicate);
|
||||
|
||||
// Insert 30 (new)
|
||||
root = addToTree(root, &values[2], sizeof(int), compareIntegers, &isDuplicate);
|
||||
TEST_ASSERT_EQUAL_INT(0, isDuplicate);
|
||||
|
||||
// Insert 10 (duplicate)
|
||||
root = addToTree(root, &values[3], sizeof(int), compareIntegers, &isDuplicate);
|
||||
TEST_ASSERT_EQUAL_INT(1, isDuplicate);
|
||||
|
||||
// Verify tree structure hasn't changed (3 nodes, not 4)
|
||||
TEST_ASSERT_EQUAL_INT(3, treeSize(root));
|
||||
|
||||
// Cleanup
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// Test 1.8: Insert strings into a tree
|
||||
void test_addToTree_WithStrings(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
const char *words[] = {"dog", "cat", "elephant", "bear"};
|
||||
|
||||
// Insert strings
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
// Note: We pass pointer to the string pointer
|
||||
root = addToTree(root, &words[i], sizeof(char *), compareStrings, NULL);
|
||||
}
|
||||
|
||||
// Verify root value (first insertion)
|
||||
TEST_ASSERT_EQUAL_STRING("dog", *(char **)root->data);
|
||||
|
||||
// Verify tree has 4 nodes
|
||||
TEST_ASSERT_EQUAL_INT(4, treeSize(root));
|
||||
|
||||
// Cleanup
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TEST SUITE 2: treeSize() - Counting Nodes
|
||||
// ============================================================================
|
||||
|
||||
// Test 2.1: Size of empty tree (NULL root)
|
||||
void test_treeSize_EmptyTree(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
unsigned int size = treeSize(root);
|
||||
|
||||
// Verify empty tree has size 0
|
||||
TEST_ASSERT_EQUAL_INT(0, size);
|
||||
}
|
||||
|
||||
// Test 2.2: Size of single-node tree
|
||||
void test_treeSize_SingleNode(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int value = 42;
|
||||
|
||||
root = addToTree(root, &value, sizeof(int), compareIntegers, NULL);
|
||||
unsigned int size = treeSize(root);
|
||||
|
||||
// Verify single-node tree has size 1
|
||||
TEST_ASSERT_EQUAL_INT(1, size);
|
||||
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// Test 2.2: Size of multi-node tree
|
||||
void test_treeSize_MultipleNodes(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int values[] = {20, 10, 30, 5, 15, 25, 35};
|
||||
|
||||
// Insert all values
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
root = addToTree(root, &values[i], sizeof(int), compareIntegers, NULL);
|
||||
}
|
||||
|
||||
unsigned int size = treeSize(root);
|
||||
|
||||
// Verify tree has 7 nodes
|
||||
TEST_ASSERT_EQUAL_INT(7, size);
|
||||
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// Test 2.3: Size after multiple insertions
|
||||
void test_treeSize_IncrementalGrowth(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int values[] = {20, 10, 30, 5, 15};
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
root = addToTree(root, &values[i], sizeof(int), compareIntegers, NULL);
|
||||
|
||||
// Check size increases with each insertion
|
||||
unsigned int expectedSize = i + 1;
|
||||
TEST_ASSERT_EQUAL_INT(expectedSize, treeSize(root));
|
||||
}
|
||||
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TEST SUITE 3: clearTree() - Freeing Memory
|
||||
// ============================================================================
|
||||
|
||||
// Test 3.1: Clear empty tree (NULL root)
|
||||
void test_clearTree_EmptyTree(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
|
||||
// Should not crash when clearing NULL
|
||||
clearTree(root);
|
||||
|
||||
// Verify still NULL after clearing
|
||||
TEST_ASSERT_NULL(root);
|
||||
}
|
||||
|
||||
// Test 3.2: Clear single-node tree
|
||||
void test_clearTree_SingleNode(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int value = 42;
|
||||
|
||||
root = addToTree(root, &value, sizeof(int), compareIntegers, NULL);
|
||||
|
||||
// Clear the tree
|
||||
clearTree(root);
|
||||
root = NULL; // Reset pointer
|
||||
|
||||
// Verify root is now NULL
|
||||
TEST_ASSERT_NULL(root);
|
||||
}
|
||||
|
||||
// Test 3.3: Clear multi-node tree
|
||||
void test_clearTree_MultipleNodes(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int values[] = {20, 10, 30, 5, 15, 25, 35};
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
root = addToTree(root, &values[i], sizeof(int), compareIntegers, NULL);
|
||||
}
|
||||
|
||||
// Clear the entire tree
|
||||
clearTree(root);
|
||||
root = NULL;
|
||||
|
||||
// Verify root is now NULL
|
||||
TEST_ASSERT_NULL(root);
|
||||
|
||||
// If we got here without segfault, the memory was properly freed
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TEST SUITE 4: nextTreeData() - In-Order Traversal
|
||||
// ============================================================================
|
||||
|
||||
// Test 4.1: In-order traversal of simple tree (ascending linear)
|
||||
void test_nextTreeData_AscendingLinearTree(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int values[] = {1, 2, 3};
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
root = addToTree(root, &values[i], sizeof(int), compareIntegers, NULL);
|
||||
}
|
||||
|
||||
// Traverse and collect results
|
||||
int result1 = *(int *)nextTreeData(root);
|
||||
int result2 = *(int *)nextTreeData(NULL);
|
||||
int result3 = *(int *)nextTreeData(NULL);
|
||||
void *result4 = nextTreeData(NULL);
|
||||
|
||||
// Verify in-order traversal produces ascending order
|
||||
TEST_ASSERT_EQUAL_INT(1, result1);
|
||||
TEST_ASSERT_EQUAL_INT(2, result2);
|
||||
TEST_ASSERT_EQUAL_INT(3, result3);
|
||||
TEST_ASSERT_NULL(result4); // No more elements
|
||||
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// Test 4.2: In-order traversal of balanced tree
|
||||
void test_nextTreeData_BalancedTree(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int values[] = {20, 10, 30, 5, 15, 25, 35};
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
root = addToTree(root, &values[i], sizeof(int), compareIntegers, NULL);
|
||||
}
|
||||
|
||||
// Traverse and collect results
|
||||
int results[7];
|
||||
results[0] = *(int *)nextTreeData(root);
|
||||
for (int i = 1; i < 7; i++)
|
||||
{
|
||||
results[i] = *(int *)nextTreeData(NULL);
|
||||
}
|
||||
|
||||
// Verify in-order traversal produces ascending order
|
||||
int expectedOrder[] = {5, 10, 15, 20, 25, 30, 35};
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_INT(expectedOrder[i], results[i]);
|
||||
}
|
||||
|
||||
// Verify traversal is complete
|
||||
void *result = nextTreeData(NULL);
|
||||
TEST_ASSERT_NULL(result);
|
||||
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// Test 4.3: In-order traversal of descending linear tree
|
||||
void test_nextTreeData_DescendingLinearTree(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int values[] = {30, 20, 10};
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
root = addToTree(root, &values[i], sizeof(int), compareIntegers, NULL);
|
||||
}
|
||||
|
||||
// Traverse and collect results
|
||||
int result1 = *(int *)nextTreeData(root);
|
||||
int result2 = *(int *)nextTreeData(NULL);
|
||||
int result3 = *(int *)nextTreeData(NULL);
|
||||
void *result4 = nextTreeData(NULL);
|
||||
|
||||
// Verify in-order traversal produces ascending order (despite insertion order)
|
||||
TEST_ASSERT_EQUAL_INT(10, result1);
|
||||
TEST_ASSERT_EQUAL_INT(20, result2);
|
||||
TEST_ASSERT_EQUAL_INT(30, result3);
|
||||
TEST_ASSERT_NULL(result4);
|
||||
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// Test 4.4: Traversal of single-node tree
|
||||
void test_nextTreeData_SingleNode(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int value = 42;
|
||||
|
||||
root = addToTree(root, &value, sizeof(int), compareIntegers, NULL);
|
||||
|
||||
int result1 = *(int *)nextTreeData(root);
|
||||
void *result2 = nextTreeData(NULL);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(42, result1);
|
||||
TEST_ASSERT_NULL(result2);
|
||||
|
||||
clearTree(root);
|
||||
}
|
||||
|
||||
// Test 4.5: Traversal of empty tree
|
||||
void test_nextTreeData_EmptyTree(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
|
||||
void *result = nextTreeData(root);
|
||||
|
||||
TEST_ASSERT_NULL(result);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// INTEGRATION TEST: Complete BST Workflow
|
||||
// ============================================================================
|
||||
|
||||
// Test 5.1: Complete workflow - insert, size, traverse, clear
|
||||
void test_integration_CompleteWorkflow(void)
|
||||
{
|
||||
TreeNode *root = NULL;
|
||||
int values[] = {50, 25, 75, 12, 37, 62, 87};
|
||||
int isDuplicate = 0;
|
||||
|
||||
// Insert all values
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
root = addToTree(root, &values[i], sizeof(int), compareIntegers, &isDuplicate);
|
||||
TEST_ASSERT_EQUAL_INT(0, isDuplicate);
|
||||
}
|
||||
|
||||
// Verify size
|
||||
TEST_ASSERT_EQUAL_INT(7, treeSize(root));
|
||||
|
||||
// Verify in-order traversal
|
||||
int expectedOrder[] = {12, 25, 37, 50, 62, 75, 87};
|
||||
int results[7];
|
||||
|
||||
results[0] = *(int *)nextTreeData(root);
|
||||
for (int i = 1; i < 7; i++)
|
||||
{
|
||||
results[i] = *(int *)nextTreeData(NULL);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_INT(expectedOrder[i], results[i]);
|
||||
}
|
||||
|
||||
// Verify duplicate detection
|
||||
root = addToTree(root, &values[0], sizeof(int), compareIntegers, &isDuplicate);
|
||||
TEST_ASSERT_EQUAL_INT(1, isDuplicate);
|
||||
TEST_ASSERT_EQUAL_INT(7, treeSize(root)); // Size should not increase
|
||||
|
||||
// Clear and verify
|
||||
clearTree(root);
|
||||
root = NULL;
|
||||
TEST_ASSERT_NULL(root);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TEST RUNNER
|
||||
// ============================================================================
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
|
||||
// Test Suite 1: addToTree()
|
||||
RUN_TEST(test_addToTree_SingleElement);
|
||||
RUN_TEST(test_addToTree_AscendingOrder);
|
||||
RUN_TEST(test_addToTree_DescendingOrder);
|
||||
RUN_TEST(test_addToTree_BalancedTree);
|
||||
RUN_TEST(test_addToTree_AllowDuplicates);
|
||||
RUN_TEST(test_addToTree_RejectDuplicates);
|
||||
RUN_TEST(test_addToTree_MultipleWithDuplicateDetection);
|
||||
RUN_TEST(test_addToTree_WithStrings);
|
||||
|
||||
// Test Suite 2: treeSize()
|
||||
RUN_TEST(test_treeSize_EmptyTree);
|
||||
RUN_TEST(test_treeSize_SingleNode);
|
||||
RUN_TEST(test_treeSize_MultipleNodes);
|
||||
RUN_TEST(test_treeSize_IncrementalGrowth);
|
||||
|
||||
// Test Suite 3: clearTree()
|
||||
RUN_TEST(test_clearTree_EmptyTree);
|
||||
RUN_TEST(test_clearTree_SingleNode);
|
||||
RUN_TEST(test_clearTree_MultipleNodes);
|
||||
|
||||
// Test Suite 4: nextTreeData()
|
||||
RUN_TEST(test_nextTreeData_AscendingLinearTree);
|
||||
RUN_TEST(test_nextTreeData_BalancedTree);
|
||||
RUN_TEST(test_nextTreeData_DescendingLinearTree);
|
||||
RUN_TEST(test_nextTreeData_SingleNode);
|
||||
RUN_TEST(test_nextTreeData_EmptyTree);
|
||||
|
||||
// Integration Test
|
||||
RUN_TEST(test_integration_CompleteWorkflow);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
BIN
test_debug
Executable file
BIN
test_debug
Executable file
Binary file not shown.
61
test_debug.c
Normal file
61
test_debug.c
Normal file
@ -0,0 +1,61 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "numbers.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("=== Testing createNumbers() ===\n\n");
|
||||
|
||||
for (int run = 0; run < 3; run++)
|
||||
{
|
||||
printf("RUN %d:\n", run + 1);
|
||||
unsigned int *numbers = createNumbers(5);
|
||||
|
||||
if (numbers == NULL)
|
||||
{
|
||||
printf("ERROR: createNumbers returned NULL\n\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("Array contents: ");
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
printf("%u ", numbers[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Count occurrences of each value
|
||||
printf("Value counts:\n");
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
int count = 0;
|
||||
for (int j = 0; j < 5; j++)
|
||||
{
|
||||
if (numbers[i] == numbers[j])
|
||||
count++;
|
||||
}
|
||||
printf(" Value %u appears %d times\n", numbers[i], count);
|
||||
}
|
||||
|
||||
// Check for duplicates
|
||||
int duplicateCount = 0;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
int count = 0;
|
||||
for (int j = 0; j < 5; j++)
|
||||
{
|
||||
if (numbers[i] == numbers[j])
|
||||
count++;
|
||||
}
|
||||
if (count == 2 && (i == 0 || numbers[i] != numbers[i-1]))
|
||||
{
|
||||
duplicateCount++;
|
||||
}
|
||||
}
|
||||
printf("Duplicate values found: %d\n\n", duplicateCount);
|
||||
|
||||
free(numbers);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
test_numbers
Executable file
BIN
test_numbers
Executable file
Binary file not shown.
599
test_numbers.c
Normal file
599
test_numbers.c
Normal file
@ -0,0 +1,599 @@
|
||||
#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();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user