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)
- Dynamic strings - 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(
'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)

View File

@@ -6,17 +6,18 @@
#include <string.h>
#include <threads.h>
/* 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);

View File

@@ -1,3 +1,4 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -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;
}

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 <stdlib.h>
#include <string.h>
#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;
}

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;
}