From 3e0584d139435b6558f04e74b3c20d604a90d25d Mon Sep 17 00:00:00 2001 From: Francesco Date: Sun, 7 Sep 2025 17:35:30 +0200 Subject: [PATCH] feat: add tests --- README.md | 3 ++ examples/queue/q1.c | 51 -------------------------------- examples/string/str2.c | 31 ------------------- meson.build | 25 ++++++++-------- string/mystring.c | 35 ++++++++++++---------- {examples => test}/hashmap/hm1.c | 24 ++++++++++----- test/queue/q1.c | 48 ++++++++++++++++++++++++++++++ {examples => test}/string/str1.c | 24 +++++++-------- test/string/str2.c | 27 +++++++++++++++++ test/test.c | 26 ++++++++++++++++ 10 files changed, 165 insertions(+), 129 deletions(-) delete mode 100644 examples/queue/q1.c delete mode 100644 examples/string/str2.c rename {examples => test}/hashmap/hm1.c (82%) create mode 100644 test/queue/q1.c rename {examples => test}/string/str1.c (57%) create mode 100644 test/string/str2.c create mode 100644 test/test.c diff --git a/README.md b/README.md index 2d99769..a79a22d 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,6 @@ It is not production-ready. Use at your own risk. - Hashmaps - Thread-safe (MT Safe) - Dynamic strings - Thread-safe (MT Safe) - Circular Queue - Thread-safe (MT Safe) + +## Examples +You can find examples inside `test/` folder. diff --git a/examples/queue/q1.c b/examples/queue/q1.c deleted file mode 100644 index 1671829..0000000 --- a/examples/queue/q1.c +++ /dev/null @@ -1,51 +0,0 @@ -#include - -#include "../../queue/myqueue.h" - -int main(void) { - /* Allocate a new queue */ - /* Always remember to check return values */ - mcl_queue_s *queue = mcl_queue_new(3, sizeof(int)); - - int val, out; - - /* Push value to the ring buffer */ - val = 1; - mcl_queue_push(queue, &val); - - val = 2; - mcl_queue_push(queue, &val); - - /* Retrieve values */ - int front, rear; - mcl_queue_get_front(queue, &front); - mcl_queue_get_rear(queue, &rear); - printf("Front: %d, Rear: %d\n", front, rear); - - /* Remove an element from the buffer */ - mcl_queue_pop(queue, &out); - printf("Pop: %d\n", out); - - mcl_queue_get_front(queue, &front); - printf("Front after pop: %d\n", front); - - val = 3; - mcl_queue_push(queue, &val); - - mcl_queue_get_rear(queue, &rear); - printf("Rear after push 3: %d\n", rear); - - val = 4; - mcl_queue_push(queue, &val); - - /* Clear queue */ - while (mcl_queue_pop(queue, &out) == 0) { - printf("Pop: %d\n", out); - } - puts("Queue is now empty"); - - /* Deallocate memory */ - mcl_queue_free(queue); - - return 0; -} diff --git a/examples/string/str2.c b/examples/string/str2.c deleted file mode 100644 index a2a8823..0000000 --- a/examples/string/str2.c +++ /dev/null @@ -1,31 +0,0 @@ -#include - -#include "../../string/mystring.h" - -int main(void) { - mcl_string_s *s1 = mcl_string_new("Hello, world!", 0); - mcl_string_s *s2 = mcl_string_new("Hello, world!", 0); - - /* Dont' call mcl_string_cstr() more than once in the same printf function */ - printf("s1: %s\n", mcl_string_cstr(s1)); - printf("s2: %s\n", mcl_string_cstr(s2)); - - int ret = mcl_string_compare(s1, s2); - if (ret == 0) { - printf("Same string!\n"); - } - - mcl_string_clear(s1); - if (mcl_string_compare(s1, s2) != 0) { - printf("Not the same!\n"); - } - - mcl_string_toupper(s1); - mcl_string_tolower(s2); - - printf("s1: %s\n", mcl_string_cstr(s1)); - printf("s2: %s\n", mcl_string_cstr(s2)); - - mcl_string_free(s1); - mcl_string_free(s2); -} diff --git a/meson.build b/meson.build index b1a46fa..8466426 100644 --- a/meson.build +++ b/meson.build @@ -1,27 +1,26 @@ project( - 'examples', + 'testlib', 'c', version: '0.1', default_options: ['c_std=c17'], ) -string = files( - 'examples/string/str2.c', +src = files( + 'hashmap/myhashmap.c', + 'queue/myqueue.c', 'string/mystring.c', ) -queue = files( - 'examples/queue/q1.c', - 'queue/myqueue.c', +testlib = files( + 'test/test.c', + 'test/hashmap/hm1.c', + 'test/queue/q1.c', + 'test/string/str1.c', + 'test/string/str2.c', ) -hashmap = files( - 'examples/hashmap/hm1.c', - 'hashmap/myhashmap.c', -) +sources = src + testlib inc_dir = include_directories('string', 'queue', 'hashmap') -executable('string', string, include_directories: inc_dir) -executable('queue', queue, include_directories: inc_dir) -executable('hashmap', hashmap, include_directories: inc_dir) +executable('testlib', sources, include_directories: inc_dir) diff --git a/string/mystring.c b/string/mystring.c index a11b6a9..bcb8aa5 100644 --- a/string/mystring.c +++ b/string/mystring.c @@ -6,17 +6,18 @@ #include #include -/* Initialize Thread-Specific Data Keys */ +/* Initialize Thread-Specific Storage */ + static tss_t buffer_key; static once_flag buffer_once = ONCE_FLAG_INIT; typedef struct { - char *buf; - size_t cap; -} tl_buffer_t; + char *buf; /**< Allocated buffer */ + size_t cap; /**< Buffer's capacity */ +} tl_buffer_s; static void buffer_destructor(void *buf) { - tl_buffer_t *tb = (tl_buffer_t *)buf; + tl_buffer_s *tb = (tl_buffer_s *)buf; if (tb == NULL) { return; } @@ -26,8 +27,8 @@ static void buffer_destructor(void *buf) { static void buffer_key_init(void) { tss_create(&buffer_key, buffer_destructor); } -/* Dynamic String library */ -size_t mcl_next_power_two(size_t len) { +/* Returns the next power of two of a number */ +static size_t next_power_two(size_t len) { if (len == 0) return 1; size_t p = 1; @@ -55,6 +56,7 @@ mcl_string_s *mcl_string_new(const char *text, size_t initial_capacity) { str->size = strlen(text); if (initial_capacity != 0 && initial_capacity < (str->size + 1)) { + /* Can't allocate with this capacity */ free(str); return NULL; @@ -62,12 +64,13 @@ mcl_string_s *mcl_string_new(const char *text, size_t initial_capacity) { size_t capacity = initial_capacity; if (capacity == 0) { - capacity = mcl_next_power_two(str->size + 1); + /* Calculate the needed capacity */ + capacity = next_power_two(str->size + 1); } str->capacity = capacity; - /* Allocate data buffer */ + /* Allocate data (text) buffer */ str->data = malloc(str->capacity); if (str->data == NULL) { free(str); @@ -79,7 +82,7 @@ mcl_string_s *mcl_string_new(const char *text, size_t initial_capacity) { memcpy(str->data, text, str->size); str->data[str->size] = '\0'; - /* Init pthread mutex */ + /* Init mutex */ if (mtx_init(&str->lock, mtx_plain) != thrd_success) { free(str->data); free(str); @@ -95,7 +98,6 @@ int mcl_string_append(mcl_string_s *string, const char *text) { return -1; } - /* Lock resource */ if (mtx_lock(&string->lock) != thrd_success) { return -1; } @@ -112,7 +114,7 @@ int mcl_string_append(mcl_string_s *string, const char *text) { /* Check if we need to resize */ if (new_size + 1 > string->capacity) { - size_t new_capacity = mcl_next_power_two(new_size + 1); + size_t new_capacity = next_power_two(new_size + 1); /* Reallocate the buffer */ void *new_data = realloc(string->data, new_capacity); if (!new_data) { @@ -200,8 +202,10 @@ char *mcl_string_cstr(mcl_string_s *string) { size_t need = string->size + 1; - tl_buffer_t *tb = (tl_buffer_t *)tss_get(buffer_key); + /* Retrieve thread local buffer */ + tl_buffer_s *tb = (tl_buffer_s *)tss_get(buffer_key); if (tb == NULL) { + /* Not found, make a new one */ tb = malloc(sizeof(*tb)); if (tb == NULL) { mtx_unlock(&string->lock); @@ -209,7 +213,7 @@ char *mcl_string_cstr(mcl_string_s *string) { return NULL; } - tb->cap = mcl_next_power_two(need); + tb->cap = next_power_two(need); tb->buf = malloc(tb->cap); if (tb->buf == NULL) { free(tb); @@ -226,7 +230,8 @@ char *mcl_string_cstr(mcl_string_s *string) { return NULL; } } else if (tb->cap < need) { - size_t newcap = mcl_next_power_two(need); + /* Found, but we need a bigger buffer */ + size_t newcap = next_power_two(need); char *tmp = realloc(tb->buf, newcap); if (tmp == NULL) { mtx_unlock(&string->lock); diff --git a/examples/hashmap/hm1.c b/test/hashmap/hm1.c similarity index 82% rename from examples/hashmap/hm1.c rename to test/hashmap/hm1.c index 6d2af41..e44fdbc 100644 --- a/examples/hashmap/hm1.c +++ b/test/hashmap/hm1.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -42,39 +43,48 @@ void my_free_key(void *key) { free(key); } void my_free_value(void *value) { struct my_custom_type *mct = (struct my_custom_type *)value; + free(mct); } -int main(void) { +void test_hm1(void) { /* Allocate a new hashmap */ /* Pass your custom hash, equal and free functions */ /* This hashmap will contain names as keys and a custom type as value */ size_t key_size = sizeof(char) * MAX_STR_LEN; size_t value_size = sizeof(int) + sizeof(char) * MAX_STR_LEN; mcl_hashmap_s *map = mcl_hm_new(my_hash_func, my_equal_fun, my_free_key, my_free_value, key_size, value_size); + assert(map != NULL); - /* Set a new value */ + /* Make a new value */ struct my_custom_type p1 = { .age = 21, }; strncpy(p1.favourite_brand, "Ferrari", sizeof(p1.favourite_brand)); - mcl_hm_set(map, "John", &p1); + /* Insert a new pair */ + assert(mcl_hm_set(map, "John", &p1)); /* Retrieve the data */ + /* Remember to free the value from the get function */ mcl_bucket_s *john = mcl_hm_get(map, "John"); + assert(john != NULL); + char *name = (char *)john->key; struct my_custom_type *john_v = (struct my_custom_type *)john->value; int age = john_v->age; char *fav_brand = john_v->favourite_brand; - fprintf(stdout, "Name: %s\nAge: %d\nFavourite brand: %s\n", name, age, fav_brand); + + assert(strcmp(name, "John") == 0); + assert(age == 21); + assert(strcmp(fav_brand, "Ferrari") == 0); + + /* Free the bucket */ mcl_hm_free_bucket(john); /* Remove a key from hash map */ - mcl_hm_remove(map, "John"); + assert(mcl_hm_remove(map, "John")); /* Deallocate */ mcl_hm_free(map); - - return 0; } diff --git a/test/queue/q1.c b/test/queue/q1.c new file mode 100644 index 0000000..69411e0 --- /dev/null +++ b/test/queue/q1.c @@ -0,0 +1,48 @@ +#include + +#include "../../queue/myqueue.h" + +void test_q1(void) { + /* Allocate a new queue */ + mcl_queue_s *queue = mcl_queue_new(3, sizeof(int)); + assert(queue != NULL); + + int val, out; + + /* Push value to the ring buffer */ + val = 1; + assert(mcl_queue_push(queue, &val) == 0); + + val = 2; + assert(mcl_queue_push(queue, &val) == 0); + + /* Retrieve values */ + int front, rear; + assert(mcl_queue_get_front(queue, &front) == 0); + assert(mcl_queue_get_rear(queue, &rear) == 0); + assert(front == 1); + assert(rear == 2); + + /* Remove an element from the buffer */ + assert(mcl_queue_pop(queue, &out) == 0); + assert(out == 1); + + assert(mcl_queue_get_front(queue, &front) == 0); + assert(front == 2); + + val = 3; + assert(mcl_queue_push(queue, &val) == 0); + + assert(mcl_queue_get_rear(queue, &rear) == 0); + assert(rear == 3); + + val = 4; + assert(mcl_queue_push(queue, &val) == 0); + + /* Clear queue */ + while (mcl_queue_pop(queue, &out) == 0) {} + assert(queue->size == 0); + + /* Deallocate memory */ + mcl_queue_free(queue); +} diff --git a/examples/string/str1.c b/test/string/str1.c similarity index 57% rename from examples/string/str1.c rename to test/string/str1.c index 7d07df8..36924e8 100644 --- a/examples/string/str1.c +++ b/test/string/str1.c @@ -1,37 +1,37 @@ +#include #include #include +#include #include "../../string/mystring.h" -int main(void) { +void test_str1(void) { size_t length; size_t capacity; char *c_str; /* Allocate a new dynamic string with an initial capacity */ mcl_string_s *str = mcl_string_new("Hello, world!", 512); - if (str == NULL) { - printf("Failed to initialize string"); - exit(EXIT_FAILURE); - } + assert(str != NULL); - /* Retrieve a C str from mcl_string with mcl_string_cstr() */ + /* Retrieve a C str from string with mcl_string_cstr() */ c_str = mcl_string_cstr(str); length = mcl_string_length(str); capacity = mcl_string_capacity(str); - printf("%s\nlength: %zu, capacity: %zu\n", c_str, length, capacity); + assert(strcmp(c_str, "Hello, world!") == 0); + assert(length == 13); + assert(capacity == 512); /* Append text to a mcl_string */ - mcl_string_append(str, " How are you?"); + assert(mcl_string_append(str, " How are you?") == 0); - puts("After append:"); c_str = mcl_string_cstr(str); length = mcl_string_length(str); capacity = mcl_string_capacity(str); - printf("%s\nsize: %zu, cap: %zu\n", c_str, length, capacity); + assert(strcmp(c_str, "Hello, world! How are you?") == 0); + assert(length == 26); + assert(capacity == 512); /* Always deallocate memory */ mcl_string_free(str); - - return 0; } diff --git a/test/string/str2.c b/test/string/str2.c new file mode 100644 index 0000000..3d0a62b --- /dev/null +++ b/test/string/str2.c @@ -0,0 +1,27 @@ +#include +#include + +#include "../../string/mystring.h" + +void test_str2(void) { + mcl_string_s *s1 = mcl_string_new("Hello, world!", 0); + assert(s1 != NULL); + mcl_string_s *s2 = mcl_string_new("Hello, world!", 0); + assert(s2 != NULL); + /* Don't call mcl_string_cstr() more than once in the same printf function */ + + int ret = mcl_string_compare(s1, s2); + assert(ret == 0); + + mcl_string_clear(s1); + ret = mcl_string_compare(s1, s2); + assert(ret != 0); + + mcl_string_tolower(s1); + mcl_string_toupper(s2); + assert(strcmp(mcl_string_cstr(s1), "") == 0); + assert(strcmp(mcl_string_cstr(s2), "HELLO, WORLD!") == 0); + + mcl_string_free(s1); + mcl_string_free(s2); +} diff --git a/test/test.c b/test/test.c new file mode 100644 index 0000000..12cc589 --- /dev/null +++ b/test/test.c @@ -0,0 +1,26 @@ +/** + * Ignore this file + */ + +#include +#include + +int main(void) { + puts("==== [Running Hashmap tests] ===="); + test_hm1(); + puts("OK!"); + puts(""); + + puts("==== [Running Queue tests] ===="); + test_q1(); + puts("OK!"); + puts(""); + + puts("==== [Running String tests] ===="); + test_str1(); + test_str2(); + puts("OK!"); + puts(""); + + return 0; +}