Initial commit
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
#include "test_array_list.h"
|
||||
#include "test_chunk.h"
|
||||
#include "test_compression.h"
|
||||
#include "test_config.h"
|
||||
#include "test_queue.h"
|
||||
#include "test_scanner.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);
|
||||
RUN_TEST(test_compression);
|
||||
RUN_TEST(test_scanner);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#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);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef TEST_ARRAY_LIST_H
|
||||
#define TEST_ARRAY_LIST_H
|
||||
|
||||
void test_array_list();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,95 @@
|
||||
|
||||
#include "chunk.h"
|
||||
#include "test_utils.h"
|
||||
#include "utils.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);
|
||||
|
||||
File *f = file_create(test_path);
|
||||
EXPECT_NOT_NULL(f);
|
||||
EXPECT_EQ_STR(f->path, test_path);
|
||||
EXPECT_NOT_NULL(f->data);
|
||||
EXPECT_NULL(f->data->data);
|
||||
EXPECT_EQ_INT((int)f->data->size, 0);
|
||||
|
||||
struct stat st;
|
||||
stat(test_path, &st);
|
||||
f->data->size = st.st_size;
|
||||
|
||||
file_load_data(f);
|
||||
EXPECT_NOT_NULL(f->data);
|
||||
EXPECT_NOT_NULL(f->data->data);
|
||||
EXPECT_EQ_INT((int)f->data->size, (int)test_len);
|
||||
EXPECT_EQ_INT(memcmp(f->data->data, test_content, test_len), 0);
|
||||
|
||||
file_destroy(f);
|
||||
unlink(test_path);
|
||||
}
|
||||
|
||||
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);
|
||||
f1->data->size = st1.st_size;
|
||||
File *f2 = file_create(path2);
|
||||
f2->data->size = st2.st_size;
|
||||
|
||||
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]);
|
||||
|
||||
// load data before serializing
|
||||
file_load_data(f1);
|
||||
file_load_data(f2);
|
||||
|
||||
// Test chunk_serialize / chunk_deserialize round-trip
|
||||
Data *serialized = chunk_serialize(chunk, false);
|
||||
EXPECT_NOT_NULL(serialized);
|
||||
|
||||
Chunk *deserialized = chunk_deserialize(serialized, false);
|
||||
EXPECT_NOT_NULL(deserialized);
|
||||
EXPECT_EQ_INT(deserialized->element_count, 2);
|
||||
EXPECT_EQ_STR(deserialized->items[0]->path, path1);
|
||||
EXPECT_EQ_STR(deserialized->items[1]->path, path2);
|
||||
EXPECT_EQ_INT((int)deserialized->items[0]->data->size, (int)len1);
|
||||
EXPECT_EQ_INT((int)deserialized->items[1]->data->size, (int)len2);
|
||||
EXPECT_EQ_INT(memcmp(deserialized->items[0]->data->data, content1, len1), 0);
|
||||
EXPECT_EQ_INT(memcmp(deserialized->items[1]->data->data, content2, len2), 0);
|
||||
|
||||
data_destroy(serialized);
|
||||
chunk_destroy(deserialized);
|
||||
|
||||
chunk_destroy(chunk);
|
||||
|
||||
unlink(path1);
|
||||
unlink(path2);
|
||||
}
|
||||
|
||||
void test_chunk() {
|
||||
test_file_operations();
|
||||
test_chunk_operations();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef TEST_CHUNK_H
|
||||
#define TEST_CHUNK_H
|
||||
|
||||
void test_chunk();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
#include "test_utils.h"
|
||||
#include "chunk.h"
|
||||
#include "compression.h"
|
||||
#include "data.h"
|
||||
#include "file.h"
|
||||
#include "utils.h"
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void test_data_compress_decompress_roundtrip() {
|
||||
char original[] = "Hello, World! This is test data for compression round-trip!";
|
||||
size_t len = strlen(original);
|
||||
|
||||
char *buf = malloc(len);
|
||||
memcpy(buf, original, len);
|
||||
Data *original_data = data_create(buf, len);
|
||||
EXPECT_NOT_NULL(original_data);
|
||||
|
||||
Data *compressed = data_compress(original_data, 3);
|
||||
EXPECT_NOT_NULL(compressed);
|
||||
|
||||
Data *decompressed = data_decompress(compressed);
|
||||
EXPECT_NOT_NULL(decompressed);
|
||||
EXPECT_EQ_INT((int)decompressed->size, (int)len);
|
||||
EXPECT_EQ_INT(memcmp(decompressed->data, original, len), 0);
|
||||
|
||||
data_destroy(original_data);
|
||||
data_destroy(compressed);
|
||||
data_destroy(decompressed);
|
||||
}
|
||||
|
||||
static void test_data_compress_decompress_large() {
|
||||
size_t size = 1024 * 10;
|
||||
char *original = malloc(size);
|
||||
EXPECT_NOT_NULL(original);
|
||||
for (size_t i = 0; i < size; i++)
|
||||
original[i] = (char)(i % 256);
|
||||
|
||||
Data *original_data = data_create(original, size);
|
||||
EXPECT_NOT_NULL(original_data);
|
||||
|
||||
Data *compressed = data_compress(original_data, 1);
|
||||
EXPECT_NOT_NULL(compressed);
|
||||
|
||||
Data *decompressed = data_decompress(compressed);
|
||||
EXPECT_NOT_NULL(decompressed);
|
||||
EXPECT_EQ_INT((int)decompressed->size, (int)size);
|
||||
EXPECT_EQ_INT(memcmp(decompressed->data, original, size), 0);
|
||||
|
||||
data_destroy(original_data);
|
||||
data_destroy(compressed);
|
||||
data_destroy(decompressed);
|
||||
}
|
||||
|
||||
static void test_chunk_compress_decompress_roundtrip() {
|
||||
char *path1 = "temp_comp_test_1.txt";
|
||||
char *content1 = "chunk compression test file 1";
|
||||
unsigned long long len1 = strlen(content1);
|
||||
|
||||
char *path2 = "temp_comp_test_2.txt";
|
||||
char *content2 = "chunk compression test file 2 with more data";
|
||||
unsigned long long len2 = strlen(content2);
|
||||
|
||||
to_disk(path1, content1, len1);
|
||||
to_disk(path2, content2, len2);
|
||||
|
||||
struct stat st1, st2;
|
||||
EXPECT_EQ_INT(stat(path1, &st1), 0);
|
||||
EXPECT_EQ_INT(stat(path2, &st2), 0);
|
||||
|
||||
File *f1 = file_create(path1);
|
||||
f1->data->size = st1.st_size;
|
||||
File *f2 = file_create(path2);
|
||||
f2->data->size = st2.st_size;
|
||||
EXPECT_NOT_NULL(f1);
|
||||
EXPECT_NOT_NULL(f2);
|
||||
|
||||
file_load_data(f1);
|
||||
file_load_data(f2);
|
||||
|
||||
File *files[2] = {f1, f2};
|
||||
Chunk *chunk = chunk_create(files, 2);
|
||||
EXPECT_NOT_NULL(chunk);
|
||||
|
||||
Data *compressed = chunk_compress(chunk, 3, false);
|
||||
EXPECT_NOT_NULL(compressed);
|
||||
|
||||
Data *decompressed_data = data_decompress(compressed);
|
||||
EXPECT_NOT_NULL(decompressed_data);
|
||||
|
||||
Chunk *decompressed_chunk = chunk_deserialize(decompressed_data, false);
|
||||
EXPECT_NOT_NULL(decompressed_chunk);
|
||||
EXPECT_EQ_INT(decompressed_chunk->element_count, 2);
|
||||
|
||||
EXPECT_EQ_STR(decompressed_chunk->items[0]->path, path1);
|
||||
EXPECT_EQ_INT((int)decompressed_chunk->items[0]->data->size, (int)len1);
|
||||
EXPECT_EQ_INT(memcmp(decompressed_chunk->items[0]->data->data, content1, len1), 0);
|
||||
|
||||
EXPECT_EQ_STR(decompressed_chunk->items[1]->path, path2);
|
||||
EXPECT_EQ_INT((int)decompressed_chunk->items[1]->data->size, (int)len2);
|
||||
EXPECT_EQ_INT(memcmp(decompressed_chunk->items[1]->data->data, content2, len2), 0);
|
||||
|
||||
chunk_destroy(chunk);
|
||||
data_destroy(compressed);
|
||||
data_destroy(decompressed_data);
|
||||
chunk_destroy(decompressed_chunk);
|
||||
|
||||
unlink(path1);
|
||||
unlink(path2);
|
||||
}
|
||||
|
||||
void test_compression() {
|
||||
test_data_compress_decompress_roundtrip();
|
||||
test_data_compress_decompress_large();
|
||||
test_chunk_compress_decompress_roundtrip();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef TEST_COMPRESSION_H
|
||||
#define TEST_COMPRESSION_H
|
||||
|
||||
void test_compression();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
#include "test_config.h"
|
||||
#include "config.h"
|
||||
#include "multiprocessing.h"
|
||||
#include "queue.h"
|
||||
#include "test_utils.h"
|
||||
#include "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, 1, false, 0);
|
||||
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_EQ_INT(cfg->transport, TRANSPORT_TCP);
|
||||
EXPECT_NULL(cfg->ssh_destination);
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
static void test_config_ssh_dest() {
|
||||
Config *cfg = config_create(str_dup("1.0"), str_dup("/src"), str_dup("user@host:/dst"),
|
||||
true, false, false, false, false, 1, false, 0);
|
||||
EXPECT_NOT_NULL(cfg);
|
||||
EXPECT_EQ_INT(cfg->transport, TRANSPORT_TCP);
|
||||
EXPECT_NULL(cfg->ssh_destination);
|
||||
EXPECT_EQ_STR(cfg->receive_root_directory, "user@host:/dst");
|
||||
|
||||
config_parse_ssh_dest(cfg);
|
||||
EXPECT_EQ_INT(cfg->transport, TRANSPORT_SSH);
|
||||
EXPECT_EQ_STR(cfg->ssh_destination, "user@host:/dst");
|
||||
EXPECT_EQ_STR(cfg->receive_root_directory, "/dst");
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
static void test_config_ssh_dest_local_path() {
|
||||
Config *cfg = config_create(str_dup("1.0"), str_dup("/src"), str_dup("/local/path"),
|
||||
true, false, false, false, false, 1, false, 0);
|
||||
config_parse_ssh_dest(cfg);
|
||||
EXPECT_EQ_INT(cfg->transport, TRANSPORT_TCP);
|
||||
EXPECT_NULL(cfg->ssh_destination);
|
||||
EXPECT_EQ_STR(cfg->receive_root_directory, "/local/path");
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
static void test_config_ssh_dest_no_user() {
|
||||
Config *cfg = config_create(str_dup("1.0"), str_dup("/src"), str_dup("host:/remote"),
|
||||
true, false, false, false, false, 1, false, 0);
|
||||
config_parse_ssh_dest(cfg);
|
||||
EXPECT_EQ_INT(cfg->transport, TRANSPORT_SSH);
|
||||
EXPECT_EQ_STR(cfg->ssh_destination, "host:/remote");
|
||||
EXPECT_EQ_STR(cfg->receive_root_directory, "/remote");
|
||||
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, false, 1, false, 0);
|
||||
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, false, 1, false, 0);
|
||||
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_config_ssh_dest();
|
||||
test_config_ssh_dest_local_path();
|
||||
test_config_ssh_dest_no_user();
|
||||
test_pipeline_sender_lifecycle();
|
||||
test_pipeline_receiver_lifecycle();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef TEST_CONFIG_H
|
||||
#define TEST_CONFIG_H
|
||||
|
||||
void test_config();
|
||||
|
||||
#endif
|
||||
@@ -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();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef TEST_QUEUE_H
|
||||
#define TEST_QUEUE_H
|
||||
|
||||
void test_queue();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,124 @@
|
||||
#include "test_utils.h"
|
||||
#include "scanner.h"
|
||||
#include "file.h"
|
||||
#include "utils.h"
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void create_test_file(const char *path, const char *content) {
|
||||
to_disk(path, content, strlen(content));
|
||||
}
|
||||
|
||||
static void test_scanner_single_file() {
|
||||
const char *dir = "test_scan_dir_single";
|
||||
const char *file1 = "test_scan_dir_single/file1.txt";
|
||||
const char *content1 = "hello scanner";
|
||||
|
||||
mkdir(dir, 0755);
|
||||
create_test_file(file1, content1);
|
||||
|
||||
DirectoryScanner *scanner = directory_scanner_create((char *)dir, false, 0, NULL, 0);
|
||||
EXPECT_NOT_NULL(scanner);
|
||||
|
||||
Chunk *chunk = directory_scanner_next(scanner);
|
||||
EXPECT_NOT_NULL(chunk);
|
||||
EXPECT_EQ_INT(chunk->element_count, 1);
|
||||
EXPECT_EQ_STR(chunk->items[0]->path, file1);
|
||||
|
||||
Chunk *next = directory_scanner_next(scanner);
|
||||
EXPECT_NULL(next);
|
||||
|
||||
chunk_destroy(chunk);
|
||||
directory_scanner_destroy(scanner);
|
||||
unlink(file1);
|
||||
rmdir(dir);
|
||||
}
|
||||
|
||||
static void test_scanner_multiple_files() {
|
||||
const char *dir = "test_scan_dir_multi";
|
||||
const char *file1 = "test_scan_dir_multi/a.txt";
|
||||
const char *file2 = "test_scan_dir_multi/b.txt";
|
||||
const char *content1 = "alpha";
|
||||
const char *content2 = "beta";
|
||||
|
||||
mkdir(dir, 0755);
|
||||
create_test_file(file1, content1);
|
||||
create_test_file(file2, content2);
|
||||
|
||||
DirectoryScanner *scanner = directory_scanner_create((char *)dir, false, 0, NULL, 0);
|
||||
EXPECT_NOT_NULL(scanner);
|
||||
|
||||
Chunk *chunk = directory_scanner_next(scanner);
|
||||
EXPECT_NOT_NULL(chunk);
|
||||
EXPECT_EQ_INT(chunk->element_count, 2);
|
||||
|
||||
int found1 = 0, found2 = 0;
|
||||
for (int i = 0; i < chunk->element_count; i++) {
|
||||
if (strcmp(chunk->items[i]->path, file1) == 0) found1 = 1;
|
||||
if (strcmp(chunk->items[i]->path, file2) == 0) found2 = 1;
|
||||
}
|
||||
EXPECT_TRUE(found1);
|
||||
EXPECT_TRUE(found2);
|
||||
|
||||
Chunk *next = directory_scanner_next(scanner);
|
||||
EXPECT_NULL(next);
|
||||
|
||||
chunk_destroy(chunk);
|
||||
directory_scanner_destroy(scanner);
|
||||
unlink(file1);
|
||||
unlink(file2);
|
||||
rmdir(dir);
|
||||
}
|
||||
|
||||
static void test_scanner_subdirectory() {
|
||||
const char *root = "test_scan_sub";
|
||||
const char *sub = "test_scan_sub/sub";
|
||||
const char *root_file = "test_scan_sub/root.txt";
|
||||
const char *sub_file = "test_scan_sub/sub/sub_file.txt";
|
||||
const char *content = "nested content";
|
||||
|
||||
mkdir(root, 0755);
|
||||
mkdir(sub, 0755);
|
||||
create_test_file(root_file, content);
|
||||
create_test_file(sub_file, content);
|
||||
|
||||
DirectoryScanner *scanner = directory_scanner_create((char *)root, false, 0, NULL, 0);
|
||||
EXPECT_NOT_NULL(scanner);
|
||||
|
||||
int total_files = 0;
|
||||
Chunk *chunk;
|
||||
while ((chunk = directory_scanner_next(scanner)) != NULL) {
|
||||
total_files += chunk->element_count;
|
||||
chunk_destroy(chunk);
|
||||
}
|
||||
EXPECT_EQ_INT(total_files, 2);
|
||||
|
||||
directory_scanner_destroy(scanner);
|
||||
unlink(root_file);
|
||||
unlink(sub_file);
|
||||
rmdir(sub);
|
||||
rmdir(root);
|
||||
}
|
||||
|
||||
static void test_scanner_empty_directory() {
|
||||
const char *dir = "test_scan_empty";
|
||||
|
||||
mkdir(dir, 0755);
|
||||
|
||||
DirectoryScanner *scanner = directory_scanner_create((char *)dir, false, 0, NULL, 0);
|
||||
EXPECT_NOT_NULL(scanner);
|
||||
|
||||
Chunk *chunk = directory_scanner_next(scanner);
|
||||
EXPECT_NULL(chunk);
|
||||
|
||||
directory_scanner_destroy(scanner);
|
||||
rmdir(dir);
|
||||
}
|
||||
|
||||
void test_scanner() {
|
||||
test_scanner_single_file();
|
||||
test_scanner_multiple_files();
|
||||
test_scanner_subdirectory();
|
||||
test_scanner_empty_directory();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef TEST_SCANNER_H
|
||||
#define TEST_SCANNER_H
|
||||
|
||||
void test_scanner();
|
||||
|
||||
#endif
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef TEST_SHARED_UTILS_H
|
||||
#define TEST_SHARED_UTILS_H
|
||||
|
||||
void test_shared_utils();
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user