info2aufgabe3/test_bintree.c
shobayoeniolasi99076 a81141fb06 Sortierung+Binärbaum
2026-05-31 09:41:27 +02:00

593 lines
17 KiB
C

#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();
}