diff --git a/.woodpecker/test.yml b/.woodpecker.yml similarity index 58% rename from .woodpecker/test.yml rename to .woodpecker.yml index a603923..a7e8fe3 100644 --- a/.woodpecker/test.yml +++ b/.woodpecker.yml @@ -11,12 +11,26 @@ branches: platform: ${PLATFORM} pipeline: - build-and-test: + lint: image: *image pull: true commands: - - make test - - make clean - - make test CFLAGS='-O3 -Werror -Wall' + - make lint + when: + event: [push, pull_request] + + build: + image: *image + commands: + - make + - make clean + - CFLAGS='-O3' make + when: + event: [push, pull_request] + + test: + image: *image + commands: + - make test when: event: [push, pull_request] diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml deleted file mode 100644 index b9d2806..0000000 --- a/.woodpecker/lint.yml +++ /dev/null @@ -1,16 +0,0 @@ -variables: - &image 'git.rustybever.be/chewing_bever/c-devop:alpine3.17' - -branches: - exclude: [ main ] -platform: linux/amd64 - -pipeline: - lint: - image: *image - pull: true - commands: - - make lint - - make objs CFLAGS='-Werror -fsyntax-only' - when: - event: [push, pull_request] diff --git a/.woodpecker/test-mem.yml b/.woodpecker/test-mem.yml deleted file mode 100644 index 48896a5..0000000 --- a/.woodpecker/test-mem.yml +++ /dev/null @@ -1,20 +0,0 @@ -variables: - &image 'git.rustybever.be/chewing_bever/c-devop:alpine3.17' - -branches: - exclude: [ main ] -platform: linux/amd64 - -depends_on: - - test - -pipeline: - test: - image: *image - pull: true - commands: - - make test-mem - - make clean - - make test-mem CFLAGS='-O3 -Werror -Wall' - when: - event: [push, pull_request] diff --git a/Makefile b/Makefile index ac56c97..6c02db1 100644 --- a/Makefile +++ b/Makefile @@ -8,8 +8,6 @@ SRC_DIR ?= src TEST_DIR ?= test INC_DIRS ?= include -LIB := $(BUILD_DIR)/$(LIB_FILENAME) - SRCS != find '$(SRC_DIR)' -iname '*.c' SRCS_H != find $(INC_DIRS) -iname '*.h' SRCS_TEST != find '$(TEST_DIR)' -iname '*.c' @@ -19,8 +17,6 @@ OBJS_TEST := $(SRCS_TEST:%=$(BUILD_DIR)/%.o) DEPS := $(SRCS:%=$(BUILD_DIR)/%.d) $(SRCS_TEST:%=$(BUILD_DIR)/%.d) BINS_TEST := $(OBJS_TEST:%.c.o=%) -TARGETS_TEST := $(BINS_TEST:%=test-%) -TARGETS_MEM_TEST := $(BINS_TEST:%=test-mem-%) INC_FLAGS := $(addprefix -I,$(INC_DIRS)) @@ -29,66 +25,42 @@ INC_FLAGS := $(addprefix -I,$(INC_DIRS)) # object file is also recompiled if only a header is changed. # -MP: generate a dummy target for every header file (according to the docs it # prevents some errors when removing header files) -CFLAGS ?= -MMD -MP -g -VIETERCFLAGS := $(INC_FLAGS) $(CFLAGS) -Wall -Wextra +CFLAGS ?= -MMD -MP -Wall -Werror -Wextra +CFLAGS += $(INC_FLAGS) .PHONY: all all: vieter # =====COMPILATION===== -# Utility used by the CI to lint -.PHONY: objs -objs: $(OBJS) - .PHONY: vieter -vieter: $(LIB) +vieter: $(BUILD_DIR)/$(LIB_FILENAME) $(BUILD_DIR)/$(LIB_FILENAME): $(OBJS) ar -rcs $@ $(OBJS) $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c mkdir -p $(dir $@) - $(CC) $(VIETERCFLAGS) -c $< -o $@ + $(CC) $(CFLAGS) -c $< -o $@ # =====TESTING===== .PHONY: test -test: $(TARGETS_TEST) - -.PHONY: test-mem -test-mem: $(TARGETS_MEM_TEST) - -.PHONY: $(TARGETS_TEST) -$(TARGETS_TEST): test-%: % - ./$^ - -.PHONY: $(TARGETS_MEM_TEST) -$(TARGETS_MEM_TEST): test-mem-%: % - valgrind --tool=memcheck --error-exitcode=1 --track-origins=yes --leak-check=full ./$^ +test: build-test + @ $(foreach bin,$(BINS_TEST),./$(bin);) .PHONY: build-test build-test: $(BINS_TEST) # For simplicity, we link every object file to each of the test files. This # might be changed later if this starts to become too slow. -<<<<<<< HEAD -$(BINS_TEST): %: %.c.o $(LIB) - $(CC) \ - $^ -o $@ -======= $(BINS_TEST): %: %.c.o $(OBJS) $(CC) $^ -larchive -o $@ ->>>>>>> c94ab92 (refactor: Add libarchive link to test compilation area of the Makefile. Created test units with xcursor-dmz as test package.) -# Along with the include directory, each test includes $(TEST_DIR) (which -# contains the acutest.h header file), and the src directory of the module it's -# testing. This allows tests to access internal methods, which aren't publicly -# exposed. +# Each test includes the test directory, which contains the acutest header file $(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c mkdir -p $(dir $@) - $(CC) $(VIETERCFLAGS) -I$(TEST_DIR) \ - -I$(dir $(@:$(BUILD_DIR)/$(TEST_DIR)/%=$(SRC_DIR)/%)) \ - -c $< -o $@ + $(CC) $(CFLAGS) -I$(TEST_DIR) -c $< -o $@ + # =====MAINTENANCE===== .PHONY: lint diff --git a/README.md b/README.md index d1fef1a..724ba54 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,15 @@ # libvieter -This library powers part of Vieter, most notably the sections that can easily +This library powers part of Vieter, most noteably the sections that can easily be implemented in C (or just parts I want to implement in C because it's fun). -The goal of this library is to be as self-contained as possible; data -structures should be implemented manually if possible. +The goal of this library is to be completely self-contained, meaning any +required data structures have to be implemented as well. It can only depend on +the C standard libraries. -See the [source code](src) for the list of modules. +Currently it contains the following: + +* Cron expression parser & next time calculator ## Development @@ -15,41 +18,13 @@ See the [source code](src) for the list of modules. Everything is handled by the provided Makefile. To compile the static library, simply run `make`. -### Project structure - -Each module has its own subdirectory inside `src`, e.g. `src/cron`. This -directory contains the actual implementation of a module, along with any -internally used header files. Each internal function should be defined in a -header file, as to make testing these possible. - -Each module should also have its own header file inside the `include` -directory. This header file defines the public API that the library exposes for -this specific module. - -Any code in a module may only import internal headers from that module, along -with any of the public API header files. Modules should not depend on each -other's internal implementationns. - -Each module should contain a README describing its contents. - -All file names, function names... (even internals) should follow snake case -convention and have a prefix unique to that module, starting with `vieter_`. -For example, the `cron` modules uses the `vieter_cron_` prefix for everything. - -Header files should only import what they explicitely need. If some function is -only used in a .c file, the import should be placed in the .c file instead. - ### Testing This library uses [Acutest](https://github.com/mity/acutest) for its tests. Tests should be placed in the `test` subdirectory, further divided into -directories that correspond to those in `src`. Test files should begin with +directories that correspond those in `src`. Test files should begin with `test_`, and their format should follow the expected format for Acutest. -Each `test_` is compiled separately into a binary, linked with libvieter. A -test file can import any of the public API header files, along with any header -files defined in its respective module. This allows testing internal functions. - To run the tests, simply run `make test`. If you wish to only run a specific test binary, you can find them in `build/test`. diff --git a/include/vieter_cron.h b/include/vieter_cron.h index 459a49f..9088d4d 100644 --- a/include/vieter_cron.h +++ b/include/vieter_cron.h @@ -1,6 +1,7 @@ #ifndef VIETER_CRON #define VIETER_CRON +#include #include #include #include @@ -34,38 +35,18 @@ typedef struct vieter_cron_simple_time { int minute; } vieter_cron_simple_time; -/* - * Allocate and initialize a new empty cron expression. - */ -vieter_cron_expression *vieter_cron_expr_init(); +vieter_cron_expression *ce_init(); -/* - * Deallocate a cron expression. - */ void vieter_cron_expr_free(vieter_cron_expression *ce); -/* - * Given a cron expression and a reference time, calculate the next time after - * the reference time that this expression matches. - */ void vieter_cron_expr_next(vieter_cron_simple_time *out, vieter_cron_expression *ce, vieter_cron_simple_time *ref); -/* - * Convencience wrapper around vieter_cron_expr_next that uses the current time - * as the reference time. - */ void vieter_cron_expr_next_from_now(vieter_cron_simple_time *out, vieter_cron_expression *ce); -/* - * Try to parse a string into a cron expression. Note that the cron expression - * is updated in-place, meaning it can contain invalid information if the - * function returns an error. The cron expression should only be used if the - * function succeeded. - */ -vieter_cron_parse_error vieter_cron_expr_parse(vieter_cron_expression *out, - const char *expression); +enum vieter_cron_parse_error vieter_cron_expr_parse(vieter_cron_expression *out, + const char *expression); #endif diff --git a/include/vieter_heap.h b/include/vieter_heap.h deleted file mode 100644 index ce6f3a4..0000000 --- a/include/vieter_heap.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef VIETER_HEAP -#define VIETER_HEAP - -#include - -typedef struct vieter_heap vieter_heap; - -typedef enum vieter_heap_error { - vieter_heap_ok = 0, - vieter_heap_empty = 1 -} vieter_heap_error; - -/* - * Allocate and initialize an empty heap. - */ -vieter_heap *vieter_heap_init(); - -/* - * Deallocate a heap. - */ -void vieter_heap_free(vieter_heap *heap); - -/* - * Return how many elements are currently in the heap. - */ -uint64_t vieter_heap_size(vieter_heap *heap); - -/* - * Insert a new value into the heap. - */ -vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key, - void *data); - -/* - * Remove the smallest element from the heap. - */ -vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap); - -/* - * Get the smallest element in the heap without removing it. - */ -vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap); - -/* - * Acquire a read lock on the heap. Return value is the result of - * pthread_rwlock_rdlock. - */ -int vieter_heap_rlock(vieter_heap *heap); - -/* - * Acquire a write lock on the heap. Return value is the result of - * pthread_rwlock_wrlock. - */ -int vieter_heap_wlock(vieter_heap *heap); - -/* - * Unlock the lock after having acquired it. Return value is the result of - * pthread_rwlock_unlock. - */ -int vieter_heap_unlock(vieter_heap *heap); - -#endif diff --git a/src/cron/vieter_cron_expression.c b/src/cron/expression.c similarity index 100% rename from src/cron/vieter_cron_expression.c rename to src/cron/expression.c diff --git a/src/cron/vieter_cron_parse.c b/src/cron/parse.c similarity index 99% rename from src/cron/vieter_cron_parse.c rename to src/cron/parse.c index a82d1c3..5c8da06 100644 --- a/src/cron/vieter_cron_parse.c +++ b/src/cron/parse.c @@ -1,8 +1,4 @@ #include "vieter_cron.h" -#include -#include -#include -#include // This prefix is needed to properly compile const uint8_t parse_month_days[] = {31, 28, 31, 30, 31, 30, diff --git a/src/heap/README.md b/src/heap/README.md deleted file mode 100644 index 8942763..0000000 --- a/src/heap/README.md +++ /dev/null @@ -1,33 +0,0 @@ -This min-heap implementation is a pretty standard binomial heap. - -## Representation in memory - -A heap consists of one or more binomial trees, each with a different order `k` -and `2^k` total nodes. This heap can contain `2^64 - 1` elements at most, which -is far more than your memory can contain, but it's still fun to mention. - -A tree does not have its own memory structure; a node that's the root of a -binomial tree is simply called the tree. - -Each node has the following layout: - -```c -typedef struct vieter_heap_node { - uint64_t key; - void *data; - struct vieter_heap_node *largest_order; - union { - struct vieter_heap_node *next_tree; - struct vieter_heap_node *next_largest_order; - } ptr; - uint8_t order; -} vieter_heap_node; -``` - -Each node has a pointer to its child with the largest order (if the node's -order is `0`, this pointer will be NULL). Each non-root node has a pointer to -its sibling with the next-highest order. These pointers allow the children of a -binomial tree to be recombined into a new tree, once their root has been -pop'ed. - -Roots point to the binomial tree in the heap with the next largest order. diff --git a/src/heap/vieter_heap.c b/src/heap/vieter_heap.c deleted file mode 100644 index b10d94c..0000000 --- a/src/heap/vieter_heap.c +++ /dev/null @@ -1,96 +0,0 @@ -#include "vieter_heap_internal.h" - -#include - -vieter_heap *vieter_heap_init() { - vieter_heap *heap = calloc(1, sizeof(vieter_heap)); - - pthread_rwlock_init(&heap->lock, NULL); - - return heap; -} - -uint64_t vieter_heap_size(vieter_heap *heap) { - uint64_t size = 0; - vieter_heap_node *tree = heap->tree; - - while (tree != NULL) { - size |= (uint64_t)1 << tree->order; - - tree = tree->ptr.next_tree; - } - - return size; -} - -void vieter_heap_free(vieter_heap *heap) { - vieter_heap_node *tree = heap->tree; - vieter_heap_node *next; - - while (tree != NULL) { - next = tree->ptr.next_tree; - vieter_heap_tree_free(tree); - tree = next; - } - - free(heap); -} - -vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key, - void *data) { - vieter_heap_node *new_tree = vieter_heap_node_init(); - new_tree->key = key; - new_tree->data = data; - new_tree->order = 0; - - if (heap->tree == NULL) { - heap->tree = new_tree; - } else { - heap->tree = vieter_heap_tree_merge(heap->tree, new_tree); - } - - return vieter_heap_ok; -} - -vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap) { - if (heap->tree == NULL) { - return vieter_heap_empty; - } - - heap->tree = vieter_heap_tree_pop(out, heap->tree); - - return vieter_heap_ok; -} - -vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap) { - if (heap->tree == NULL) { - return vieter_heap_empty; - } - - vieter_heap_node *tree = heap->tree; - uint64_t smallest_key = tree->key; - *out = tree->data; - - while (tree->ptr.next_tree != NULL) { - tree = tree->ptr.next_tree; - - if (tree->key < smallest_key) { - smallest_key = tree->key; - *out = tree->data; - } - } - - return vieter_heap_ok; -} - -int vieter_heap_rlock(vieter_heap *heap) { - return pthread_rwlock_rdlock(&heap->lock); -} - -int vieter_heap_wlock(vieter_heap *heap) { - return pthread_rwlock_wrlock(&heap->lock); -} - -int vieter_heap_unlock(vieter_heap *heap) { - return pthread_rwlock_unlock(&heap->lock); -} diff --git a/src/heap/vieter_heap_internal.h b/src/heap/vieter_heap_internal.h deleted file mode 100644 index 41850e9..0000000 --- a/src/heap/vieter_heap_internal.h +++ /dev/null @@ -1,8 +0,0 @@ -#include "vieter_heap.h" -#include "vieter_heap_tree.h" -#include - -struct vieter_heap { - vieter_heap_node *tree; - pthread_rwlock_t lock; -}; diff --git a/src/heap/vieter_heap_tree.c b/src/heap/vieter_heap_tree.c deleted file mode 100644 index 10c5437..0000000 --- a/src/heap/vieter_heap_tree.c +++ /dev/null @@ -1,192 +0,0 @@ -#include "vieter_heap_tree.h" - -vieter_heap_node *vieter_heap_node_init() { - return calloc(1, sizeof(vieter_heap_node)); -} - -void vieter_heap_node_free(vieter_heap_node *node) { free(node); } - -void vieter_heap_tree_free(vieter_heap_node *root) { - if (root->order == 0) { - goto end; - } - - uint64_t size = 1; - vieter_heap_node **stack = - malloc(((uint64_t)1 << root->order) * sizeof(vieter_heap_node *)); - stack[0] = root->largest_order; - - vieter_heap_node *node; - - while (size > 0) { - node = stack[size - 1]; - size--; - - if (node->largest_order != NULL) { - stack[size] = node->largest_order; - size++; - } - - if (node->ptr.next_largest_order != NULL) { - stack[size] = node->ptr.next_largest_order; - size++; - } - - vieter_heap_node_free(node); - } - - free(stack); - -end: - vieter_heap_node_free(root); -} - -vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a, - vieter_heap_node *root_b) { - vieter_heap_node *root, *child; - - if (root_a->key <= root_b->key) { - root = root_a; - child = root_b; - } else { - root = root_b; - child = root_a; - } - - child->ptr.next_largest_order = root->largest_order; - root->largest_order = child; - - root->order++; - - return root; -} - -vieter_heap_node *vieter_heap_tree_merge(vieter_heap_node *target_tree, - vieter_heap_node *other_tree) { - vieter_heap_node *out = target_tree; - - vieter_heap_node *next_other_tree, *next_target_tree; - vieter_heap_node *previous_target_tree = NULL; - - while (target_tree != NULL && other_tree != NULL) { - if (target_tree->order == other_tree->order) { - next_other_tree = other_tree->ptr.next_tree; - next_target_tree = target_tree->ptr.next_tree; - - target_tree = vieter_heap_tree_merge_same_order(target_tree, other_tree); - - target_tree->ptr.next_tree = next_target_tree; - - // If this merge produces a binomial tree whose size is already in - // target, it will be the next target. Therefore, we can merge target's - // trees until we no longer have a duplicate depth. - while (next_target_tree != NULL && - next_target_tree->order == target_tree->order) { - next_target_tree = next_target_tree->ptr.next_tree; - target_tree = vieter_heap_tree_merge_same_order( - target_tree, target_tree->ptr.next_tree); - target_tree->ptr.next_tree = next_target_tree; - } - - if (previous_target_tree != NULL) { - previous_target_tree->ptr.next_tree = target_tree; - } else { - out = target_tree; - } - - other_tree = next_other_tree; - } else if (target_tree->order > other_tree->order) { - next_other_tree = other_tree->ptr.next_tree; - - if (previous_target_tree == NULL) { - previous_target_tree = other_tree; - out = other_tree; - } else { - previous_target_tree->ptr.next_tree = other_tree; - - // This single missing line right here broke this entire function for - // nearly a week. - previous_target_tree = other_tree; - } - - other_tree->ptr.next_tree = target_tree; - other_tree = next_other_tree; - } else { - if (previous_target_tree == NULL) { - out = target_tree; - } - - previous_target_tree = target_tree; - target_tree = target_tree->ptr.next_tree; - } - } - - // Append final part of tree to target - if (target_tree == NULL) { - previous_target_tree->ptr.next_tree = other_tree; - } - - return out; -} - -vieter_heap_node *vieter_heap_tree_pop(void **out, vieter_heap_node *tree) { - vieter_heap_node *tree_before_smallest = NULL; - vieter_heap_node *previous_tree = NULL; - vieter_heap_node *original_root = tree; - - uint64_t smallest_key = tree->key; - - while (tree->ptr.next_tree != NULL) { - previous_tree = tree; - tree = tree->ptr.next_tree; - - if (tree->key < smallest_key) { - smallest_key = tree->key; - tree_before_smallest = previous_tree; - } - } - - vieter_heap_node *tree_to_pop; - - if (tree_before_smallest != NULL) { - tree_to_pop = tree_before_smallest->ptr.next_tree; - tree_before_smallest->ptr.next_tree = tree_to_pop->ptr.next_tree; - } else { - tree_to_pop = original_root; - original_root = original_root->ptr.next_tree; - } - - *out = tree_to_pop->data; - - if (tree_to_pop->order == 0) { - vieter_heap_tree_free(tree_to_pop); - - return original_root; - } - - // Each child has a pointer to its sibling with the next largest order. If we - // want to convert this list of children into their own tree, these pointers - // have to be reversed. - previous_tree = tree_to_pop->largest_order; - vieter_heap_node_free(tree_to_pop); - - tree = previous_tree->ptr.next_largest_order; - previous_tree->ptr.next_tree = NULL; - - vieter_heap_node *next_tree; - - while (tree != NULL) { - next_tree = tree->ptr.next_largest_order; - tree->ptr.next_tree = previous_tree; - - previous_tree = tree; - tree = next_tree; - } - - // original_root is zero if the heap only contained a single tree. - if (original_root != NULL) { - return vieter_heap_tree_merge(original_root, previous_tree); - } else { - return previous_tree; - } -} diff --git a/src/heap/vieter_heap_tree.h b/src/heap/vieter_heap_tree.h deleted file mode 100644 index 0a299db..0000000 --- a/src/heap/vieter_heap_tree.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef VIETER_HEAP_TREE -#define VIETER_HEAP_TREE - -#include -#include - -typedef struct vieter_heap_node { - uint64_t key; - void *data; - struct vieter_heap_node *largest_order; - union { - // Roots point to next tree in the heap, other nodes point to their first - // neighbour. - struct vieter_heap_node *next_tree; - struct vieter_heap_node *next_largest_order; - } ptr; - uint8_t order; -} vieter_heap_node; - -/* - * Allocate and initialize a heap node object. - */ -vieter_heap_node *vieter_heap_node_init(); - -/* - * Deallocate a node object. - */ -void vieter_heap_node_free(vieter_heap_node *node); - -/* - * Deallocate a node's entire structure. - */ -void vieter_heap_tree_free(vieter_heap_node *root); - -/* - * Given the roots of the smallest trees in two heaps, merge them into a single - * large heap. - */ -vieter_heap_node *vieter_heap_tree_merge(vieter_heap_node *root_a, vieter_heap_node *root_b); - -/* - * Given the roots of two trees of the same order, merge them into a heap of one - * order larger. - */ -vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a, - vieter_heap_node *root_b); - -/* - * Remove the smallest element from the given heap. - */ -vieter_heap_node *vieter_heap_tree_pop(void **out, vieter_heap_node *root); - -#endif diff --git a/test/heap/test_heap.c b/test/heap/test_heap.c deleted file mode 100644 index f77b0dc..0000000 --- a/test/heap/test_heap.c +++ /dev/null @@ -1,187 +0,0 @@ -#include "acutest.h" -#include "vieter_heap_internal.h" -#include - -#define TEST_SIZE(heap, size) \ - TEST_CHECK(vieter_heap_size(heap) == size); \ - TEST_MSG("Size: %zu, expected: %lu", vieter_heap_size(heap), (uint64_t)size) - -void test_init() { - vieter_heap *heap = vieter_heap_init(); - TEST_CHECK(heap != NULL); - TEST_SIZE(heap, 0); - vieter_heap_free(heap); -} - -void count_nodes(uint64_t *counter, vieter_heap_node *root) { - (*counter)++; - - if (root->largest_order != NULL) { - count_nodes(counter, root->largest_order); - } - - // This will also traverse the various trees - if (root->ptr.next_largest_order != NULL) { - count_nodes(counter, root->ptr.next_largest_order); - } -} - -uint64_t count_nodes_heap(vieter_heap *heap) { - uint64_t counter = 0; - - if (heap->tree != NULL) { - count_nodes(&counter, heap->tree); - } - - return counter; -} - -void test_insert() { - vieter_heap *heap = vieter_heap_init(); - TEST_SIZE(heap, 0); - - void *data; - - for (uint64_t i = 50; i > 0; i--) { - vieter_heap_insert(heap, i, (void *)i); - TEST_SIZE(heap, (uint64_t)51 - i); - TEST_CHECK(count_nodes_heap(heap) == (uint64_t)51 - i); - - data = 0; - - TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); - TEST_CHECK_(data == (void *)i, "%lX == %lX", (uint64_t)data, i); - } - - vieter_heap_free(heap); -} - -void test_insert_random() { - srand(1); - - vieter_heap *heap = vieter_heap_init(); - TEST_SIZE(heap, 0); - - uint64_t num = rand(); - uint64_t smallest = num; - - void *data = NULL; - - for (uint64_t i = 0; i < 5000; i++) { - vieter_heap_insert(heap, num, (void *)num); - TEST_SIZE(heap, i + 1); - TEST_CHECK(count_nodes_heap(heap) == (uint64_t)i + 1); - - if (num < smallest) { - smallest = num; - } - - TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); - TEST_CHECK(data == (void *)smallest); - - data = NULL; - - num = rand(); - } - - vieter_heap_free(heap); -} - -void test_pop() { - const uint64_t n = 500; - - vieter_heap *heap = vieter_heap_init(); - TEST_SIZE(heap, 0); - - void *data; - - for (uint64_t i = n; i > 0; i--) { - vieter_heap_insert(heap, i, (void *)i); - TEST_SIZE(heap, (uint64_t)n + 1 - i); - TEST_CHECK(count_nodes_heap(heap) == (uint64_t)n + 1 - i); - - TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); - TEST_CHECK(data == (void*)i); - } - - data = NULL; - - for (uint64_t i = 1; i <= n; i++) { - TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok); - TEST_CHECK(data == (void*)i); - TEST_SIZE(heap, (uint64_t)n - i); - } - - vieter_heap_free(heap); -} - -int uint64_t_compare(const void *a, const void *b) { - if ((*(uint64_t *)a) < (*(uint64_t *)b)) { - return -1; - } else if ((*(uint64_t *)a) > (*(uint64_t *)b)) { - return 1; - } else { - return 0; - } -} - -void test_pop_random() { - const uint64_t n = 500; - - srand(0); - - vieter_heap *heap = vieter_heap_init(); - - uint64_t *numbers = malloc(n * sizeof(uint64_t)); - uint64_t num; - - for (uint64_t i = 0; i < n; i++) { - num = rand(); - vieter_heap_insert(heap, num, (void *)num); - TEST_SIZE(heap, i + 1); - TEST_CHECK(count_nodes_heap(heap) == i + 1); - - numbers[i] = num; - } - - - qsort(numbers, n, sizeof(uint64_t), uint64_t_compare); - - void *data = NULL; - - for (uint64_t i = 0; i < n; i++) { - TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); - TEST_CHECK_(data == (void *)numbers[i], "peek %lx == %lx", (uint64_t)data, numbers[i]); - - data = NULL; - - TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok); - TEST_CHECK_(data == (void *)numbers[i], "pop %lx == %lx", (uint64_t)data, numbers[i]); - TEST_SIZE(heap, n - i - 1); - TEST_CHECK(count_nodes_heap(heap) == n - i - 1); - - // Assure each size is also a valid heap after inserting - vieter_heap_insert(heap, numbers[i], (void *)numbers[i]); - TEST_SIZE(heap, n - i); - TEST_CHECK(count_nodes_heap(heap) == n - i); - - data = NULL; - - TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok); - TEST_CHECK_(data == (void *)numbers[i], "pop %lx == %lx", (uint64_t)data, numbers[i]); - TEST_SIZE(heap, n - i - 1); - TEST_CHECK(count_nodes_heap(heap) == n - i - 1); - } - - vieter_heap_free(heap); - free(numbers); -} - -TEST_LIST = { - {"init", test_init}, - {"insert", test_insert}, - {"insert random", test_insert_random}, - {"pop", test_pop}, - {"pop random", test_pop_random}, - {NULL, NULL} -}; diff --git a/test/heap/test_merge.c b/test/heap/test_merge.c deleted file mode 100644 index 10053f2..0000000 --- a/test/heap/test_merge.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "acutest.h" -#include "vieter_heap.h" -#include "vieter_heap_tree.h" -#include - -void test_merge_same_order() { - vieter_heap_node *root_a = vieter_heap_node_init(); - root_a->key = 1; - root_a->order = 0; - - vieter_heap_node *root_b = vieter_heap_node_init(); - root_b->key = 2; - root_b->order = 0; - - vieter_heap_node *merged = vieter_heap_tree_merge_same_order(root_a, root_b); - - TEST_CHECK(merged == root_a); - TEST_CHECK(merged->key == 1); - TEST_CHECK(merged->largest_order == root_b); - TEST_CHECK(merged->ptr.next_largest_order == NULL); - - vieter_heap_tree_free(merged); -} - -TEST_LIST = { - {"merge same order", test_merge_same_order}, - {NULL, NULL} -};