First Commit

This commit is contained in:
Theo Tappe
2026-06-10 16:58:35 +02:00
commit 1e5eeb704f
42 changed files with 4037 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
#include "test_array_list.h"
#include "test_chunk.h"
#include "test_config.h"
#include "test_queue.h"
#include "test_shared_utils.h"
#include "test_utils.h"
#include <stdio.h>
// Define global test state variables
int tests_run = 0;
int tests_failed = 0;
bool current_test_failed = false;
int main() {
printf("\033[1;36m=== RUNNING UNIT TESTS ===\033[0m\n\n");
RUN_TEST(test_queue);
RUN_TEST(test_array_list);
RUN_TEST(test_shared_utils);
RUN_TEST(test_chunk);
RUN_TEST(test_config);
printf("\n\033[1;36m=== TEST SUMMARY ===\033[0m\n");
printf("Total Tests Run: %d\n", tests_run);
if (tests_failed > 0) {
printf("Status: \033[1;31m%d FAILED\033[0m\n", tests_failed);
return 1;
} else {
printf("Status: \033[1;32mALL PASSED\033[0m\n");
return 0;
}
}
+64
View File
@@ -0,0 +1,64 @@
#include "test_array_list.h"
#include "array_list.h"
#include "test_utils.h"
#include <stdlib.h>
static int destroyer_calls = 0;
static void test_destroyer(void *item) {
destroyer_calls++;
free(item);
}
void test_array_list() {
ArrayList *list = array_list_create(free);
EXPECT_NOT_NULL(list);
EXPECT_EQ_INT(list->size, 0);
EXPECT_EQ_INT(list->capacity, 100);
// Test adding
int *val1 = malloc(sizeof(int));
*val1 = 42;
array_list_add(list, val1);
EXPECT_EQ_INT(list->size, 1);
EXPECT_EQ_INT(*(int *)list->items[0], 42);
// Test extending capacity
// Initial capacity is 100. Let's add 105 elements.
for (int i = 0; i < 105; i++) {
int *val = malloc(sizeof(int));
*val = i;
array_list_add(list, val);
}
EXPECT_EQ_INT(list->size, 106);
EXPECT_EQ_INT(list->capacity, 200); // 100 * 2
// Verify contents
EXPECT_EQ_INT(*(int *)list->items[0], 42);
EXPECT_EQ_INT(*(int *)list->items[1], 0);
EXPECT_EQ_INT(*(int *)list->items[105], 104);
// Test array conversion
void **arr = array_list_to_array(list);
EXPECT_NOT_NULL(arr);
EXPECT_EQ_INT(*(int *)arr[0], 42);
EXPECT_EQ_INT(*(int *)arr[105], 104);
free(arr);
// Delete list, verifying the destroyer is called 106 times
destroyer_calls = 0;
list->item_destroyer = test_destroyer;
array_list_delete(list);
EXPECT_EQ_INT(destroyer_calls, 106);
// Test clear with NULL destroyer
list = array_list_create(NULL);
int a = 1, b = 2;
array_list_add(list, &a);
array_list_add(list, &b);
EXPECT_EQ_INT(list->size, 2);
array_list_clear(list);
EXPECT_EQ_INT(list->size, 0);
EXPECT_NULL(list->items[0]);
EXPECT_NULL(list->items[1]);
array_list_delete(list);
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef TEST_ARRAY_LIST_H
#define TEST_ARRAY_LIST_H
void test_array_list();
#endif
+147
View File
@@ -0,0 +1,147 @@
#include "test_chunk.h"
#include "chunk.h"
#include "utils.h"
#include "test_utils.h"
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
static void test_file_operations() {
char *test_path = "temp_file_test.txt";
char *test_content = "Hello, Chunk System!";
unsigned long long test_len = strlen(test_content);
to_disk(test_path, test_content, test_len);
struct stat st;
int stat_res = stat(test_path, &st);
EXPECT_EQ_INT(stat_res, 0);
EXPECT_EQ_INT((int)st.st_size, (int)test_len);
File *f = file_create(test_path, &st);
EXPECT_NOT_NULL(f);
EXPECT_EQ_STR(f->path, test_path);
EXPECT_NULL(f->data);
file_load_data(f);
EXPECT_NOT_NULL(f->data);
EXPECT_EQ_INT(memcmp(f->data, test_content, test_len), 0);
file_destroy(f);
unlink(test_path);
}
static void test_file_receive_operations() {
char *path = str_dup("temp_receive.txt");
char *data = str_dup("receive data content");
unsigned long long size = strlen(data);
DataFragment *df = data_fragment_create(data, size);
EXPECT_NOT_NULL(df);
EXPECT_EQ_INT((int)df->size, (int)size);
EXPECT_EQ_STR(df->data, "receive data content");
FileReceive *fr = file_receive_create(path, df);
EXPECT_NOT_NULL(fr);
EXPECT_EQ_STR(fr->path, "temp_receive.txt");
EXPECT_NOT_NULL(fr->data_fragment);
EXPECT_EQ_STR(fr->data_fragment->data, "receive data content");
file_receive_destroy(fr);
}
static void test_chunk_operations() {
char *path1 = "temp_chunk_1.txt";
char *content1 = "chunk item 1";
unsigned long long len1 = strlen(content1);
char *path2 = "temp_chunk_2.txt";
char *content2 = "chunk item number 2";
unsigned long long len2 = strlen(content2);
to_disk(path1, content1, len1);
to_disk(path2, content2, len2);
struct stat st1, st2;
stat(path1, &st1);
stat(path2, &st2);
File *f1 = file_create(path1, &st1);
File *f2 = file_create(path2, &st2);
File *files[2] = {f1, f2};
Chunk *chunk = chunk_create(files, 2);
EXPECT_NOT_NULL(chunk);
EXPECT_EQ_INT(chunk->element_count, 2);
EXPECT_NOT_NULL(chunk->items[0]);
EXPECT_NOT_NULL(chunk->items[1]);
Data *formatted = chunk_format(chunk);
EXPECT_NOT_NULL(formatted);
unsigned long long expected_size =
(sizeof(int) + strlen(path1) + sizeof(unsigned long long) + len1) +
(sizeof(int) + strlen(path2) + sizeof(unsigned long long) + len2);
EXPECT_EQ_INT((int)formatted->data_size, (int)expected_size);
char *ptr = (char *)formatted->data;
// File 1
int p_len1;
memcpy(&p_len1, ptr, sizeof(int));
ptr += sizeof(int);
EXPECT_EQ_INT(p_len1, (int)strlen(path1));
char read_path1[256];
memcpy(read_path1, ptr, p_len1);
read_path1[p_len1] = '\0';
ptr += p_len1;
EXPECT_EQ_STR(read_path1, path1);
unsigned long long d_len1;
memcpy(&d_len1, ptr, sizeof(unsigned long long));
ptr += sizeof(unsigned long long);
EXPECT_EQ_INT((int)d_len1, (int)len1);
char read_content1[256];
memcpy(read_content1, ptr, d_len1);
read_content1[d_len1] = '\0';
ptr += d_len1;
EXPECT_EQ_STR(read_content1, content1);
// File 2
int p_len2;
memcpy(&p_len2, ptr, sizeof(int));
ptr += sizeof(int);
EXPECT_EQ_INT(p_len2, (int)strlen(path2));
char read_path2[256];
memcpy(read_path2, ptr, p_len2);
read_path2[p_len2] = '\0';
ptr += p_len2;
EXPECT_EQ_STR(read_path2, path2);
unsigned long long d_len2;
memcpy(&d_len2, ptr, sizeof(unsigned long long));
ptr += sizeof(unsigned long long);
EXPECT_EQ_INT((int)d_len2, (int)len2);
char read_content2[256];
memcpy(read_content2, ptr, d_len2);
read_content2[d_len2] = '\0';
ptr += d_len2;
EXPECT_EQ_STR(read_content2, content2);
chunk_data_delete(formatted);
chunk_destroy(chunk);
unlink(path1);
unlink(path2);
}
void test_chunk() {
test_file_operations();
test_file_receive_operations();
test_chunk_operations();
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef TEST_CHUNK_H
#define TEST_CHUNK_H
void test_chunk();
#endif
+58
View File
@@ -0,0 +1,58 @@
#include "test_config.h"
#include "config.h"
#include "multiprocessing.h"
#include "queue.h"
#include "utils.h"
#include "test_utils.h"
#include <stdlib.h>
static void test_config_lifecycle() {
Config *cfg = config_create(str_dup("1.0"), str_dup("/src"), str_dup("/dst"), true, true, false, false, false, 4);
EXPECT_NOT_NULL(cfg);
EXPECT_EQ_STR(cfg->version, "1.0");
EXPECT_EQ_STR(cfg->send_directory, "/src");
EXPECT_EQ_STR(cfg->receive_root_directory, "/dst");
EXPECT_TRUE(cfg->save_to_disk);
EXPECT_TRUE(cfg->use_multithreading);
EXPECT_FALSE(cfg->use_chunk_serialization);
EXPECT_FALSE(cfg->use_compression);
EXPECT_FALSE(cfg->use_multiprocessing);
EXPECT_EQ_INT(cfg->num_connections, 4);
config_delete(cfg);
}
static void test_pipeline_sender_lifecycle() {
Config *cfg = config_create(str_dup("2.0"), str_dup("/src2"), str_dup("/dst2"), false, false, true, true, true, 8);
Queue *q1 = queue_create(5, NULL);
Queue *q2 = queue_create(15, NULL);
PipelineContextSender *pcs = pipeline_context_sender_create(cfg, q1, q2);
EXPECT_NOT_NULL(pcs);
EXPECT_EQ_STR(pcs->config->version, "2.0");
EXPECT_EQ_INT(pcs->queue_scanner->capacity, 5);
EXPECT_EQ_INT(pcs->queue_loader->capacity, 15);
EXPECT_FALSE(pcs->scanner_done);
EXPECT_FALSE(pcs->loader_done);
pipeline_context_sender_destroy(pcs);
}
static void test_pipeline_receiver_lifecycle() {
Config *cfg = config_create(str_dup("3.0"), str_dup("/src3"), str_dup("/dst3"), true, true, true, true, true, 2);
Queue *q = queue_create(20, NULL);
PipelineContextReceiver *pcr = pipeline_context_receiver_create(cfg, q, 42);
EXPECT_NOT_NULL(pcr);
EXPECT_EQ_STR(pcr->config->version, "3.0");
EXPECT_EQ_INT(pcr->queue->capacity, 20);
EXPECT_EQ_INT(pcr->file_descriptor, 42);
EXPECT_FALSE(pcr->receiver_done);
pipeline_context_receiver_destroy(pcr);
}
void test_config() {
test_config_lifecycle();
test_pipeline_sender_lifecycle();
test_pipeline_receiver_lifecycle();
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef TEST_CONFIG_H
#define TEST_CONFIG_H
void test_config();
#endif
+206
View File
@@ -0,0 +1,206 @@
#include "test_queue.h"
#include "queue.h"
#include "test_utils.h"
#include <stdlib.h>
#include <threads.h>
#include <stdbool.h>
#include <stdio.h>
static void test_queue_basic() {
Queue *q = queue_create(10, NULL);
EXPECT_NOT_NULL(q);
EXPECT_TRUE(queue_is_empty(q));
EXPECT_FALSE(queue_is_full(q));
int *vals[5];
for (int i = 0; i < 5; i++) {
vals[i] = malloc(sizeof(int));
*vals[i] = (i + 1) * 10;
queue_enqueue(q, vals[i]);
}
EXPECT_FALSE(queue_is_empty(q));
EXPECT_FALSE(queue_is_full(q));
EXPECT_EQ_INT(q->size, 5);
int *v1 = (int *)queue_dequeue(q);
EXPECT_NOT_NULL(v1);
EXPECT_EQ_INT(*v1, 10);
free(v1);
int *v2 = (int *)queue_dequeue(q);
EXPECT_NOT_NULL(v2);
EXPECT_EQ_INT(*v2, 20);
free(v2);
EXPECT_EQ_INT(q->size, 3);
int *vals2[3];
for (int i = 0; i < 3; i++) {
vals2[i] = malloc(sizeof(int));
*vals2[i] = (i + 6) * 10;
queue_enqueue(q, vals2[i]);
}
EXPECT_EQ_INT(q->size, 6);
int expected_vals[] = {30, 40, 50, 60, 70, 80};
for (int i = 0; i < 6; i++) {
int *v = (int *)queue_dequeue(q);
EXPECT_NOT_NULL(v);
EXPECT_EQ_INT(*v, expected_vals[i]);
free(v);
}
EXPECT_TRUE(queue_is_empty(q));
queue_destroy(q);
}
static void test_queue_resize() {
Queue *q = queue_create(3, NULL);
EXPECT_NOT_NULL(q);
EXPECT_EQ_INT(q->capacity, 3);
int a = 1, b = 2, c = 3, d = 4, e = 5;
queue_enqueue(q, &a);
queue_enqueue(q, &b);
queue_enqueue(q, &c);
EXPECT_TRUE(queue_is_full(q));
int *v1 = (int *)queue_dequeue(q);
EXPECT_NOT_NULL(v1);
EXPECT_EQ_INT(*v1, 1);
// Now front = 1, rear = 0, size = 2 (wrapped state)
queue_enqueue(q, &d);
EXPECT_TRUE(queue_is_full(q));
// This enqueue triggers capacity doubling
queue_enqueue(q, &e);
EXPECT_FALSE(queue_is_full(q));
EXPECT_EQ_INT(q->capacity, 6);
EXPECT_EQ_INT(q->size, 4);
// Dequeue all and check order: B, C, D, E
int *v2 = (int *)queue_dequeue(q);
EXPECT_NOT_NULL(v2);
EXPECT_EQ_INT(*v2, 2);
int *v3 = (int *)queue_dequeue(q);
EXPECT_NOT_NULL(v3);
EXPECT_EQ_INT(*v3, 3);
int *v4 = (int *)queue_dequeue(q);
EXPECT_NOT_NULL(v4);
EXPECT_EQ_INT(*v4, 4);
int *v5 = (int *)queue_dequeue(q);
EXPECT_NOT_NULL(v5);
EXPECT_EQ_INT(*v5, 5);
EXPECT_TRUE(queue_is_empty(q));
queue_destroy(q);
}
static int destroyer_calls = 0;
static void my_destroyer(void *item) {
destroyer_calls++;
free(item);
}
static void test_queue_destroyer() {
destroyer_calls = 0;
Queue *q = queue_create(5, my_destroyer);
EXPECT_NOT_NULL(q);
for (int i = 0; i < 3; i++) {
int *val = malloc(sizeof(int));
*val = i;
queue_enqueue(q, val);
}
int *v = (int *)queue_dequeue(q);
EXPECT_NOT_NULL(v);
EXPECT_EQ_INT(*v, 0);
free(v);
queue_destroy(q);
EXPECT_EQ_INT(destroyer_calls, 2);
}
typedef struct {
Queue *q;
mtx_t *mutex;
cnd_t *cnd_empty;
cnd_t *cnd_full;
bool done;
int sum;
} ThreadContext;
static int consumer_func(void *arg) {
ThreadContext *ctx = (ThreadContext *)arg;
while (true) {
int *val = (int *)queue_dequeue_multithreaded(ctx->q, ctx->mutex, ctx->cnd_empty, ctx->cnd_full, &ctx->done);
if (val == NULL) {
break;
}
ctx->sum += *val;
free(val);
}
return 0;
}
static void test_queue_multithreaded() {
Queue *q = queue_create(2, NULL);
mtx_t mutex;
cnd_t cnd_empty;
cnd_t cnd_full;
mtx_init(&mutex, mtx_plain);
cnd_init(&cnd_empty);
cnd_init(&cnd_full);
ThreadContext ctx = {
.q = q,
.mutex = &mutex,
.cnd_empty = &cnd_empty,
.cnd_full = &cnd_full,
.done = false,
.sum = 0
};
thrd_t consumer;
int res = thrd_create(&consumer, consumer_func, &ctx);
EXPECT_EQ_INT(res, thrd_success);
for (int i = 1; i <= 100; i++) {
int *val = malloc(sizeof(int));
*val = i;
queue_enqueue_multithreaded(q, val, &mutex, &cnd_empty, &cnd_full);
}
mtx_lock(&mutex);
ctx.done = true;
cnd_signal(&cnd_empty);
mtx_unlock(&mutex);
int join_res;
thrd_join(consumer, &join_res);
EXPECT_EQ_INT(ctx.sum, 5050);
EXPECT_TRUE(queue_is_empty(q));
queue_destroy(q);
mtx_destroy(&mutex);
cnd_destroy(&cnd_empty);
cnd_destroy(&cnd_full);
}
void test_queue() {
test_queue_basic();
test_queue_resize();
test_queue_destroyer();
test_queue_multithreaded();
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef TEST_QUEUE_H
#define TEST_QUEUE_H
void test_queue();
#endif
+62
View File
@@ -0,0 +1,62 @@
#include "test_shared_utils.h"
#include "utils.h"
#include "test_utils.h"
#include <stdlib.h>
#include <string.h>
void test_shared_utils() {
// Test str_dup
char *dup_null = str_dup(NULL);
EXPECT_NULL(dup_null);
char *dup_empty = str_dup("");
EXPECT_NOT_NULL(dup_empty);
EXPECT_EQ_STR(dup_empty, "");
free(dup_empty);
char *dup_normal = str_dup("hello world");
EXPECT_NOT_NULL(dup_normal);
EXPECT_EQ_STR(dup_normal, "hello world");
free(dup_normal);
// Test path_cat
char *cat1 = path_cat("/foo", "/bar");
EXPECT_NOT_NULL(cat1);
EXPECT_EQ_STR(cat1, "/foo/bar");
free(cat1);
char *cat2 = path_cat("/foo/", "/bar");
EXPECT_NOT_NULL(cat2);
EXPECT_EQ_STR(cat2, "/foo/bar");
free(cat2);
char *cat3 = path_cat("/foo", "bar");
EXPECT_NOT_NULL(cat3);
EXPECT_EQ_STR(cat3, "/foo/bar");
free(cat3);
char *cat4 = path_cat("/foo/", "bar");
EXPECT_NOT_NULL(cat4);
EXPECT_EQ_STR(cat4, "/foo/bar");
free(cat4);
char *cat_empty1 = path_cat("", "/bar");
EXPECT_NOT_NULL(cat_empty1);
EXPECT_EQ_STR(cat_empty1, "/bar");
free(cat_empty1);
char *cat_empty2 = path_cat("/foo", "");
EXPECT_NOT_NULL(cat_empty2);
EXPECT_EQ_STR(cat_empty2, "/foo");
free(cat_empty2);
char *cat_null1 = path_cat(NULL, "/bar");
EXPECT_NOT_NULL(cat_null1);
EXPECT_EQ_STR(cat_null1, "/bar");
free(cat_null1);
char *cat_null2 = path_cat("/foo", NULL);
EXPECT_NOT_NULL(cat_null2);
EXPECT_EQ_STR(cat_null2, "/foo");
free(cat_null2);
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef TEST_SHARED_UTILS_H
#define TEST_SHARED_UTILS_H
void test_shared_utils();
#endif
+94
View File
@@ -0,0 +1,94 @@
#ifndef TEST_UTILS_H
#define TEST_UTILS_H
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
// Global test suite status
extern int tests_run;
extern int tests_failed;
extern bool current_test_failed;
// Helper to run a test function
#define RUN_TEST(test_func) \
do { \
printf("Running %s...\n", #test_func); \
tests_run++; \
current_test_failed = false; \
test_func(); \
if (current_test_failed) { \
tests_failed++; \
printf(" \033[1;31m[FAILED]\033[0m %s\n", #test_func); \
} else { \
printf(" \033[1;32m[PASSED]\033[0m %s\n", #test_func); \
} \
} while (0)
// Assertion macros
#define EXPECT_TRUE(condition) \
do { \
if (!(condition)) { \
printf(" \033[1;31m[FAIL]\033[0m %s:%d: Assertion failed: %s is false\n", __FILE__, __LINE__, #condition); \
current_test_failed = true; \
return; \
} \
} while (0)
#define EXPECT_FALSE(condition) \
do { \
if (condition) { \
printf(" \033[1;31m[FAIL]\033[0m %s:%d: Assertion failed: %s is true\n", __FILE__, __LINE__, #condition); \
current_test_failed = true; \
return; \
} \
} while (0)
#define EXPECT_EQ_INT(actual, expected) \
do { \
int act = (actual); \
int exp = (expected); \
if (act != exp) { \
printf(" \033[1;31m[FAIL]\033[0m %s:%d: Expected %d, got %d\n", __FILE__, __LINE__, exp, act); \
current_test_failed = true; \
return; \
} \
} while (0)
#define EXPECT_EQ_STR(actual, expected) \
do { \
const char *act = (actual); \
const char *exp = (expected); \
if (act == NULL || exp == NULL) { \
if (act != exp) { \
printf(" \033[1;31m[FAIL]\033[0m %s:%d: Expected %s, got %s\n", __FILE__, __LINE__, \
exp ? exp : "NULL", act ? act : "NULL"); \
current_test_failed = true; \
return; \
} \
} else if (strcmp(act, exp) != 0) { \
printf(" \033[1;31m[FAIL]\033[0m %s:%d: Expected \"%s\", got \"%s\"\n", __FILE__, __LINE__, exp, act); \
current_test_failed = true; \
return; \
} \
} while (0)
#define EXPECT_NOT_NULL(ptr) \
do { \
if ((ptr) == NULL) { \
printf(" \033[1;31m[FAIL]\033[0m %s:%d: Expected non-null pointer, got NULL\n", __FILE__, __LINE__); \
current_test_failed = true; \
return; \
} \
} while (0)
#define EXPECT_NULL(ptr) \
do { \
if ((ptr) != NULL) { \
printf(" \033[1;31m[FAIL]\033[0m %s:%d: Expected NULL, got %p\n", __FILE__, __LINE__, (void*)(ptr)); \
current_test_failed = true; \
return; \
} \
} while (0)
#endif