feat: add tests

This commit is contained in:
2025-09-07 17:35:30 +02:00
parent be154db6cf
commit 3e0584d139
10 changed files with 165 additions and 129 deletions

View File

@@ -8,3 +8,6 @@ It is not production-ready. Use at your own risk.
- Hashmaps - Thread-safe (MT Safe) - Hashmaps - Thread-safe (MT Safe)
- Dynamic strings - Thread-safe (MT Safe) - Dynamic strings - Thread-safe (MT Safe)
- Circular Queue - Thread-safe (MT Safe) - Circular Queue - Thread-safe (MT Safe)
## Examples
You can find examples inside `test/` folder.

View File

@@ -1,51 +0,0 @@
#include <stdio.h>
#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;
}

View File

@@ -1,31 +0,0 @@
#include <stdio.h>
#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);
}

View File

@@ -1,27 +1,26 @@
project( project(
'examples', 'testlib',
'c', 'c',
version: '0.1', version: '0.1',
default_options: ['c_std=c17'], default_options: ['c_std=c17'],
) )
string = files( src = files(
'examples/string/str2.c', 'hashmap/myhashmap.c',
'queue/myqueue.c',
'string/mystring.c', 'string/mystring.c',
) )
queue = files( testlib = files(
'examples/queue/q1.c', 'test/test.c',
'queue/myqueue.c', 'test/hashmap/hm1.c',
'test/queue/q1.c',
'test/string/str1.c',
'test/string/str2.c',
) )
hashmap = files( sources = src + testlib
'examples/hashmap/hm1.c',
'hashmap/myhashmap.c',
)
inc_dir = include_directories('string', 'queue', 'hashmap') inc_dir = include_directories('string', 'queue', 'hashmap')
executable('string', string, include_directories: inc_dir) executable('testlib', sources, include_directories: inc_dir)
executable('queue', queue, include_directories: inc_dir)
executable('hashmap', hashmap, include_directories: inc_dir)

View File

@@ -6,17 +6,18 @@
#include <string.h> #include <string.h>
#include <threads.h> #include <threads.h>
/* Initialize Thread-Specific Data Keys */ /* Initialize Thread-Specific Storage */
static tss_t buffer_key; static tss_t buffer_key;
static once_flag buffer_once = ONCE_FLAG_INIT; static once_flag buffer_once = ONCE_FLAG_INIT;
typedef struct { typedef struct {
char *buf; char *buf; /**< Allocated buffer */
size_t cap; size_t cap; /**< Buffer's capacity */
} tl_buffer_t; } tl_buffer_s;
static void buffer_destructor(void *buf) { 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) { if (tb == NULL) {
return; return;
} }
@@ -26,8 +27,8 @@ static void buffer_destructor(void *buf) {
static void buffer_key_init(void) { tss_create(&buffer_key, buffer_destructor); } static void buffer_key_init(void) { tss_create(&buffer_key, buffer_destructor); }
/* Dynamic String library */ /* Returns the next power of two of a number */
size_t mcl_next_power_two(size_t len) { static size_t next_power_two(size_t len) {
if (len == 0) return 1; if (len == 0) return 1;
size_t p = 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); str->size = strlen(text);
if (initial_capacity != 0 && initial_capacity < (str->size + 1)) { if (initial_capacity != 0 && initial_capacity < (str->size + 1)) {
/* Can't allocate with this capacity */
free(str); free(str);
return NULL; return NULL;
@@ -62,12 +64,13 @@ mcl_string_s *mcl_string_new(const char *text, size_t initial_capacity) {
size_t capacity = initial_capacity; size_t capacity = initial_capacity;
if (capacity == 0) { 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; str->capacity = capacity;
/* Allocate data buffer */ /* Allocate data (text) buffer */
str->data = malloc(str->capacity); str->data = malloc(str->capacity);
if (str->data == NULL) { if (str->data == NULL) {
free(str); 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); memcpy(str->data, text, str->size);
str->data[str->size] = '\0'; str->data[str->size] = '\0';
/* Init pthread mutex */ /* Init mutex */
if (mtx_init(&str->lock, mtx_plain) != thrd_success) { if (mtx_init(&str->lock, mtx_plain) != thrd_success) {
free(str->data); free(str->data);
free(str); free(str);
@@ -95,7 +98,6 @@ int mcl_string_append(mcl_string_s *string, const char *text) {
return -1; return -1;
} }
/* Lock resource */
if (mtx_lock(&string->lock) != thrd_success) { if (mtx_lock(&string->lock) != thrd_success) {
return -1; return -1;
} }
@@ -112,7 +114,7 @@ int mcl_string_append(mcl_string_s *string, const char *text) {
/* Check if we need to resize */ /* Check if we need to resize */
if (new_size + 1 > string->capacity) { 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 */ /* Reallocate the buffer */
void *new_data = realloc(string->data, new_capacity); void *new_data = realloc(string->data, new_capacity);
if (!new_data) { if (!new_data) {
@@ -200,8 +202,10 @@ char *mcl_string_cstr(mcl_string_s *string) {
size_t need = string->size + 1; 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) { if (tb == NULL) {
/* Not found, make a new one */
tb = malloc(sizeof(*tb)); tb = malloc(sizeof(*tb));
if (tb == NULL) { if (tb == NULL) {
mtx_unlock(&string->lock); mtx_unlock(&string->lock);
@@ -209,7 +213,7 @@ char *mcl_string_cstr(mcl_string_s *string) {
return NULL; return NULL;
} }
tb->cap = mcl_next_power_two(need); tb->cap = next_power_two(need);
tb->buf = malloc(tb->cap); tb->buf = malloc(tb->cap);
if (tb->buf == NULL) { if (tb->buf == NULL) {
free(tb); free(tb);
@@ -226,7 +230,8 @@ char *mcl_string_cstr(mcl_string_s *string) {
return NULL; return NULL;
} }
} else if (tb->cap < need) { } 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); char *tmp = realloc(tb->buf, newcap);
if (tmp == NULL) { if (tmp == NULL) {
mtx_unlock(&string->lock); mtx_unlock(&string->lock);

View File

@@ -1,3 +1,4 @@
#include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -42,39 +43,48 @@ void my_free_key(void *key) { free(key); }
void my_free_value(void *value) { void my_free_value(void *value) {
struct my_custom_type *mct = (struct my_custom_type *)value; struct my_custom_type *mct = (struct my_custom_type *)value;
free(mct); free(mct);
} }
int main(void) { void test_hm1(void) {
/* Allocate a new hashmap */ /* Allocate a new hashmap */
/* Pass your custom hash, equal and free functions */ /* Pass your custom hash, equal and free functions */
/* This hashmap will contain names as keys and a custom type as value */ /* This hashmap will contain names as keys and a custom type as value */
size_t key_size = sizeof(char) * MAX_STR_LEN; size_t key_size = sizeof(char) * MAX_STR_LEN;
size_t value_size = sizeof(int) + 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); 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 = { struct my_custom_type p1 = {
.age = 21, .age = 21,
}; };
strncpy(p1.favourite_brand, "Ferrari", sizeof(p1.favourite_brand)); 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 */ /* Retrieve the data */
/* Remember to free the value from the get function */
mcl_bucket_s *john = mcl_hm_get(map, "John"); mcl_bucket_s *john = mcl_hm_get(map, "John");
assert(john != NULL);
char *name = (char *)john->key; char *name = (char *)john->key;
struct my_custom_type *john_v = (struct my_custom_type *)john->value; struct my_custom_type *john_v = (struct my_custom_type *)john->value;
int age = john_v->age; int age = john_v->age;
char *fav_brand = john_v->favourite_brand; 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); mcl_hm_free_bucket(john);
/* Remove a key from hash map */ /* Remove a key from hash map */
mcl_hm_remove(map, "John"); assert(mcl_hm_remove(map, "John"));
/* Deallocate */ /* Deallocate */
mcl_hm_free(map); mcl_hm_free(map);
return 0;
} }

48
test/queue/q1.c Normal file
View File

@@ -0,0 +1,48 @@
#include <assert.h>
#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);
}

View File

@@ -1,37 +1,37 @@
#include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "../../string/mystring.h" #include "../../string/mystring.h"
int main(void) { void test_str1(void) {
size_t length; size_t length;
size_t capacity; size_t capacity;
char *c_str; char *c_str;
/* Allocate a new dynamic string with an initial capacity */ /* Allocate a new dynamic string with an initial capacity */
mcl_string_s *str = mcl_string_new("Hello, world!", 512); mcl_string_s *str = mcl_string_new("Hello, world!", 512);
if (str == NULL) { assert(str != NULL);
printf("Failed to initialize string");
exit(EXIT_FAILURE);
}
/* 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); c_str = mcl_string_cstr(str);
length = mcl_string_length(str); length = mcl_string_length(str);
capacity = mcl_string_capacity(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 */ /* 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); c_str = mcl_string_cstr(str);
length = mcl_string_length(str); length = mcl_string_length(str);
capacity = mcl_string_capacity(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 */ /* Always deallocate memory */
mcl_string_free(str); mcl_string_free(str);
return 0;
} }

27
test/string/str2.c Normal file
View File

@@ -0,0 +1,27 @@
#include <assert.h>
#include <string.h>
#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);
}

26
test/test.c Normal file
View File

@@ -0,0 +1,26 @@
/**
* Ignore this file
*/
#include <assert.h>
#include <stdio.h>
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;
}