From ccd912be9157d7e64c7bb04bc0699cd682109768 Mon Sep 17 00:00:00 2001 From: "DESKTOP-0TGNH6T\\alial" Date: Mon, 8 Jun 2026 16:45:03 +0200 Subject: [PATCH] bintree and tests --- bintree.c | 132 ++++++++++++++++++++++++++++++++---- bintree.h | 15 +++-- bintreeTests.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++ makefile | 25 +++++-- numbersTests.c | 85 +++++++++++++++++++++++ 5 files changed, 414 insertions(+), 23 deletions(-) create mode 100644 bintreeTests.c create mode 100644 numbersTests.c diff --git a/bintree.c b/bintree.c index 5cf82a9..be251f0 100644 --- a/bintree.c +++ b/bintree.c @@ -1,36 +1,144 @@ +#include #include #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. */ +static TreeNode *createTreeNode(const void *data, size_t dataSize) +{ + TreeNode *node = 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). + if (data == NULL || dataSize == 0) + { + return NULL; + } + + node = (TreeNode *)malloc(sizeof(TreeNode)); + + if (node == NULL) + { + return NULL; + } + + node->data = malloc(dataSize); + + if (node->data == NULL) + { + free(node); + return NULL; + } + + memcpy(node->data, data, dataSize); + node->left = NULL; + node->right = NULL; + + return node; +} + +// Adds a copy of data's pointer destination to the tree using compareFct for ordering. +// If isDuplicate is NULL, duplicates are accepted. Otherwise duplicates are ignored and +// isDuplicate is set to 1. If a new value is inserted, isDuplicate is set to 0. TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate) { + int compareResult = 0; + if (data == NULL || dataSize == 0 || compareFct == NULL) + { + return root; + } + + if (root == NULL) + { + TreeNode *newNode = createTreeNode(data, dataSize); + + if (newNode != NULL && isDuplicate != NULL) + { + *isDuplicate = 0; + } + + return newNode; + } + + compareResult = compareFct(data, root->data); + + if (compareResult == 0 && isDuplicate != NULL) + { + *isDuplicate = 1; + return root; + } + + if (compareResult < 0) + { + root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate); + } + else + { + root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate); + } + + return root; } -// 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. +static StackNode *pushLeftPath(StackNode *stack, TreeNode *node) +{ + while (node != NULL) + { + stack = push(stack, node); + node = node->left; + } + + return stack; +} + +// Iterates over the tree in sorted order. If root is not NULL, a new iteration starts. +// If root is NULL, the next element of the last tree is returned. void *nextTreeData(TreeNode *root) { + static StackNode *iteratorStack = NULL; + TreeNode *currentNode = NULL; + void *data = NULL; + if (root != NULL) + { + clearStack(iteratorStack); + iteratorStack = NULL; + iteratorStack = pushLeftPath(iteratorStack, root); + } + + currentNode = (TreeNode *)top(iteratorStack); + + if (currentNode == NULL) + { + clearStack(iteratorStack); + iteratorStack = NULL; + return NULL; + } + + iteratorStack = pop(iteratorStack); + data = currentNode->data; + iteratorStack = pushLeftPath(iteratorStack, currentNode->right); + + return data; } -// Releases all memory resources (including data copies). +// Releases all memory resources, including the copied data in each tree node. void clearTree(TreeNode *root) { - + if (root != NULL) + { + clearTree(root->left); + clearTree(root->right); + free(root->data); + free(root); + } } // Returns the number of entries in the tree given by root. unsigned int treeSize(const TreeNode *root) { + if (root == NULL) + { + return 0; + } + return 1 + treeSize(root->left) + treeSize(root->right); } \ No newline at end of file diff --git a/bintree.h b/bintree.h index 25e16b2..acb0c41 100644 --- a/bintree.h +++ b/bintree.h @@ -12,15 +12,18 @@ typedef struct node struct node *right; } TreeNode; -// 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 duplicates are ignored and +// isDuplicate is set to 1. If a new entry is added, isDuplicate is set to 0. TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate); -// 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. + +// Iterates over the tree in sorted order. If root is not NULL, a new iteration starts. +// If root is NULL, the next entry of the last tree is returned. void *nextTreeData(TreeNode *root); -// Releases all memory resources (including data copies). + +// Releases all memory resources, including data copies. void clearTree(TreeNode *root); + // Returns the number of entries in the tree given by root. unsigned int treeSize(const TreeNode *root); diff --git a/bintreeTests.c b/bintreeTests.c new file mode 100644 index 0000000..c96af5c --- /dev/null +++ b/bintreeTests.c @@ -0,0 +1,180 @@ +#include +#include +#include "unity.h" +#include "bintree.h" + +static int compareUnsignedInt(const void *arg1, const void *arg2) +{ + unsigned int number1 = *(const unsigned int *)arg1; + unsigned int number2 = *(const unsigned int *)arg2; + + if (number1 < number2) + { + return -1; + } + + if (number1 > number2) + { + return 1; + } + + return 0; +} + +void test_addToTreeCreatesRootNodeAndCopiesData(void) +{ + TreeNode *root = NULL; + unsigned int value = 10; + + root = addToTree(root, &value, sizeof(unsigned int), compareUnsignedInt, NULL); + value = 99; + + TEST_ASSERT_NOT_NULL(root); + TEST_ASSERT_NOT_NULL(root->data); + TEST_ASSERT_EQUAL_UINT32(10, *(unsigned int *)root->data); + TEST_ASSERT_NULL(root->left); + TEST_ASSERT_NULL(root->right); + + clearTree(root); +} + +void test_addToTreeInsertsSmallerValueOnLeftSide(void) +{ + TreeNode *root = NULL; + unsigned int value1 = 10; + unsigned int value2 = 5; + + root = addToTree(root, &value1, sizeof(unsigned int), compareUnsignedInt, NULL); + root = addToTree(root, &value2, sizeof(unsigned int), compareUnsignedInt, NULL); + + TEST_ASSERT_NOT_NULL(root); + TEST_ASSERT_NOT_NULL(root->left); + TEST_ASSERT_EQUAL_UINT32(5, *(unsigned int *)root->left->data); + + clearTree(root); +} + +void test_addToTreeInsertsGreaterValueOnRightSide(void) +{ + TreeNode *root = NULL; + unsigned int value1 = 10; + unsigned int value2 = 20; + + root = addToTree(root, &value1, sizeof(unsigned int), compareUnsignedInt, NULL); + root = addToTree(root, &value2, sizeof(unsigned int), compareUnsignedInt, NULL); + + TEST_ASSERT_NOT_NULL(root); + TEST_ASSERT_NOT_NULL(root->right); + TEST_ASSERT_EQUAL_UINT32(20, *(unsigned int *)root->right->data); + + clearTree(root); +} + +void test_addToTreeDetectsDuplicateWhenFlagIsUsed(void) +{ + TreeNode *root = NULL; + unsigned int value1 = 10; + unsigned int value2 = 10; + int isDuplicate = 0; + + root = addToTree(root, &value1, sizeof(unsigned int), compareUnsignedInt, &isDuplicate); + TEST_ASSERT_EQUAL_INT(0, isDuplicate); + + root = addToTree(root, &value2, sizeof(unsigned int), compareUnsignedInt, &isDuplicate); + TEST_ASSERT_EQUAL_INT(1, isDuplicate); + TEST_ASSERT_EQUAL_UINT32(1, treeSize(root)); + + clearTree(root); +} + +void test_addToTreeAcceptsDuplicateWhenFlagIsNull(void) +{ + TreeNode *root = NULL; + unsigned int value = 10; + + root = addToTree(root, &value, sizeof(unsigned int), compareUnsignedInt, NULL); + root = addToTree(root, &value, sizeof(unsigned int), compareUnsignedInt, NULL); + + TEST_ASSERT_EQUAL_UINT32(2, treeSize(root)); + + clearTree(root); +} + +void test_treeSizeReturnsCorrectNumberOfNodes(void) +{ + TreeNode *root = NULL; + unsigned int values[] = {10, 5, 15, 3, 7}; + + for (unsigned int i = 0; i < 5; i++) + { + root = addToTree(root, &values[i], sizeof(unsigned int), compareUnsignedInt, NULL); + } + + TEST_ASSERT_EQUAL_UINT32(5, treeSize(root)); + + clearTree(root); +} + +void test_nextTreeDataReturnsValuesInSortedOrder(void) +{ + TreeNode *root = NULL; + unsigned int values[] = {10, 5, 15, 3, 7}; + unsigned int expectedValues[] = {3, 5, 7, 10, 15}; + + for (unsigned int i = 0; i < 5; i++) + { + root = addToTree(root, &values[i], sizeof(unsigned int), compareUnsignedInt, NULL); + } + + for (unsigned int i = 0; i < 5; i++) + { + unsigned int *currentValue = NULL; + + if (i == 0) + { + currentValue = (unsigned int *)nextTreeData(root); + } + else + { + currentValue = (unsigned int *)nextTreeData(NULL); + } + + TEST_ASSERT_NOT_NULL(currentValue); + TEST_ASSERT_EQUAL_UINT32(expectedValues[i], *currentValue); + } + + TEST_ASSERT_NULL(nextTreeData(NULL)); + + clearTree(root); +} + +void test_clearTreeDoesNotCrashOnNull(void) +{ + clearTree(NULL); + TEST_PASS(); +} + +void setUp(void) +{ +} + +void tearDown(void) +{ +} + +int main(void) +{ + UNITY_BEGIN(); + + printf("\n============================\nBinary tree tests\n============================\n"); + RUN_TEST(test_addToTreeCreatesRootNodeAndCopiesData); + RUN_TEST(test_addToTreeInsertsSmallerValueOnLeftSide); + RUN_TEST(test_addToTreeInsertsGreaterValueOnRightSide); + RUN_TEST(test_addToTreeDetectsDuplicateWhenFlagIsUsed); + RUN_TEST(test_addToTreeAcceptsDuplicateWhenFlagIsNull); + RUN_TEST(test_treeSizeReturnsCorrectNumberOfNodes); + RUN_TEST(test_nextTreeDataReturnsValuesInSortedOrder); + RUN_TEST(test_clearTreeDoesNotCrashOnNull); + + return UNITY_END(); +} \ No newline at end of file diff --git a/makefile b/makefile index 97c5e30..ddf1692 100644 --- a/makefile +++ b/makefile @@ -26,24 +26,39 @@ doble_initial: # -------------------------- program_obj_files = stack.o bintree.o numbers.o timer.o highscore.o -doble : main.o $(program_obj_files) +doble: main.o $(program_obj_files) $(CC) $(FLAGS) $^ -o doble $(program_obj_files): %.o: %.c $(CC) -c $(FLAGS) $^ -o $@ +main.o: main.c + $(CC) -c $(FLAGS) main.c -o main.o + # -------------------------- # Unit Tests # -------------------------- -unitTests: - echo "needs to be implemented" +bintreeTests: bintree.o stack.o bintreeTests.c $(unityfolder)/unity.c + $(CC) $(FLAGS) -I$(unityfolder) -o runBintreeTests bintreeTests.c bintree.o stack.o $(unityfolder)/unity.c + +numbersTests: numbers.o bintree.o stack.o numbersTests.c $(unityfolder)/unity.c + $(CC) $(FLAGS) -I$(unityfolder) -o runNumbersTests numbersTests.c numbers.o bintree.o stack.o $(unityfolder)/unity.c + +unitTests: bintreeTests numbersTests +ifeq ($(OS),Windows_NT) + .\runBintreeTests.exe + .\runNumbersTests.exe +else + ./runBintreeTests + ./runNumbersTests +endif # -------------------------- # Clean # -------------------------- clean: ifeq ($(OS),Windows_NT) - del /f *.o doble + del /f *.o *.exe else - rm -f *.o doble + rm -f *.o doble doble_initial runBintreeTests runNumbersTests endif \ No newline at end of file diff --git a/numbersTests.c b/numbersTests.c new file mode 100644 index 0000000..8efe5e0 --- /dev/null +++ b/numbersTests.c @@ -0,0 +1,85 @@ +#include +#include +#include "unity.h" +#include "numbers.h" + +void test_getDuplicateReturnsDuplicateFromUnsortedArray(void) +{ + unsigned int numbers[] = {8, 3, 12, 5, 3, 20}; + + TEST_ASSERT_EQUAL_UINT32(3, getDuplicate(numbers, 6)); +} + +void test_getDuplicateReturnsZeroForNullArray(void) +{ + TEST_ASSERT_EQUAL_UINT32(0, getDuplicate(NULL, 5)); +} + +void test_getDuplicateReturnsZeroIfThereIsNoDuplicate(void) +{ + unsigned int numbers[] = {1, 2, 3, 4, 5}; + + TEST_ASSERT_EQUAL_UINT32(0, getDuplicate(numbers, 5)); +} + +void test_getDuplicateDoesNotChangeOriginalArrayOrder(void) +{ + unsigned int numbers[] = {9, 4, 7, 4, 2}; + unsigned int expectedNumbers[] = {9, 4, 7, 4, 2}; + + TEST_ASSERT_EQUAL_UINT32(4, getDuplicate(numbers, 5)); + TEST_ASSERT_EQUAL_UINT32_ARRAY(expectedNumbers, numbers, 5); +} + +void test_createNumbersReturnsArrayWithCorrectDuplicate(void) +{ + const unsigned int len = 20; + unsigned int *numbers = createNumbers(len); + unsigned int duplicate = 0; + unsigned int duplicateCount = 0; + + TEST_ASSERT_NOT_NULL(numbers); + + duplicate = getDuplicate(numbers, len); + TEST_ASSERT_NOT_EQUAL(0, duplicate); + + for (unsigned int i = 0; i < len; i++) + { + if (numbers[i] == duplicate) + { + duplicateCount++; + } + } + + TEST_ASSERT_EQUAL_UINT32(2, duplicateCount); + + free(numbers); +} + +void test_createNumbersReturnsNullForTooSmallLength(void) +{ + TEST_ASSERT_NULL(createNumbers(1)); +} + +void setUp(void) +{ +} + +void tearDown(void) +{ +} + +int main(void) +{ + UNITY_BEGIN(); + + printf("\n============================\nNumbers tests\n============================\n"); + RUN_TEST(test_getDuplicateReturnsDuplicateFromUnsortedArray); + RUN_TEST(test_getDuplicateReturnsZeroForNullArray); + RUN_TEST(test_getDuplicateReturnsZeroIfThereIsNoDuplicate); + RUN_TEST(test_getDuplicateDoesNotChangeOriginalArrayOrder); + RUN_TEST(test_createNumbersReturnsArrayWithCorrectDuplicate); + RUN_TEST(test_createNumbersReturnsNullForTooSmallLength); + + return UNITY_END(); +} \ No newline at end of file