diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b12812..6555105 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,7 @@ add_executable(client ${CLIENT_SRCS} ${SHARED_SRCS}) target_include_directories(client PRIVATE src/shared src/server src/client) target_link_libraries(client PRIVATE Threads::Threads ${ZSTD_LIBRARY}) -add_executable(tests ${TEST_SRCS} ${SHARED_SRCS}) +add_executable(tests ${TEST_SRCS} ${SHARED_SRCS} src/client/scanner.c) target_include_directories(tests PRIVATE tests src/shared src/server src/client) target_link_libraries(tests PRIVATE Threads::Threads ${ZSTD_LIBRARY}) diff --git a/src/shared/chunk.c b/src/shared/chunk.c index 9bdb0c1..87d5f1e 100644 --- a/src/shared/chunk.c +++ b/src/shared/chunk.c @@ -131,7 +131,9 @@ Data *chunk_compress(Chunk *chunk, int compression_level) { } log_message(LOG_LEVEL_DEBUG, "Chunk succesfully compressed"); - return data_compress(data, compression_level); + Data *compressed = data_compress(data, compression_level); + data_destroy(data); + return compressed; } Chunk *chunk_decompress(Data *compressed_data) { @@ -142,7 +144,7 @@ Chunk *chunk_decompress(Data *compressed_data) { return NULL; } - ArrayList *files = array_list_create((void (*)(void *))data_destroy); + ArrayList *files = array_list_create(file_destroy); char *data_pointer = uncompressed_data->data; size_t remaining_size = uncompressed_data->size; @@ -207,7 +209,16 @@ Chunk *chunk_decompress(Data *compressed_data) { return NULL; } - file->data = data_create(data_pointer, data_size); + void *file_data = malloc(data_size); + if (file_data == NULL) { + perror("Could not allocate memory for file data"); + free(path); + array_list_delete(files); + data_destroy(uncompressed_data); + return NULL; + } + memcpy(file_data, data_pointer, data_size); + file->data = data_create(file_data, data_size); data_pointer += data_size; remaining_size -= data_size; @@ -219,8 +230,9 @@ Chunk *chunk_decompress(Data *compressed_data) { File **file_array = (File **)array_list_to_array(files); Chunk *chunk = chunk_create(file_array, files->size); - // Clean up + // Clean up - files are now owned by the chunk free(file_array); + files->item_destroyer = NULL; array_list_delete(files); data_destroy(uncompressed_data); diff --git a/src/shared/data.c b/src/shared/data.c index d77130a..689c4d4 100644 --- a/src/shared/data.c +++ b/src/shared/data.c @@ -25,6 +25,7 @@ Data *data_create(void *data, size_t data_size) { } void data_destroy(Data *data) { + if (data == NULL) return; free(data->data); free(data); } diff --git a/tests/runner.c b/tests/runner.c index 40dd500..b05f007 100644 --- a/tests/runner.c +++ b/tests/runner.c @@ -1,7 +1,9 @@ #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 @@ -19,6 +21,8 @@ int main() { 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); diff --git a/tests/test_compression.c b/tests/test_compression.c new file mode 100644 index 0000000..4542b79 --- /dev/null +++ b/tests/test_compression.c @@ -0,0 +1,110 @@ +#include "test_utils.h" +#include "chunk.h" +#include "data.h" +#include "file.h" +#include "utils.h" +#include +#include +#include + +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, &st1); + File *f2 = file_create(path2, &st2); + 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); + EXPECT_NOT_NULL(compressed); + + Chunk *decompressed_chunk = chunk_decompress(compressed); + 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); + 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(); +} diff --git a/tests/test_compression.h b/tests/test_compression.h new file mode 100644 index 0000000..d86ff93 --- /dev/null +++ b/tests/test_compression.h @@ -0,0 +1,6 @@ +#ifndef TEST_COMPRESSION_H +#define TEST_COMPRESSION_H + +void test_compression(); + +#endif diff --git a/tests/test_scanner.c b/tests/test_scanner.c new file mode 100644 index 0000000..4446333 --- /dev/null +++ b/tests/test_scanner.c @@ -0,0 +1,124 @@ +#include "test_utils.h" +#include "scanner.h" +#include "file.h" +#include "utils.h" +#include +#include +#include + +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); + 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); + 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); + 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); + 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(); +} diff --git a/tests/test_scanner.h b/tests/test_scanner.h new file mode 100644 index 0000000..a8c6276 --- /dev/null +++ b/tests/test_scanner.h @@ -0,0 +1,6 @@ +#ifndef TEST_SCANNER_H +#define TEST_SCANNER_H + +void test_scanner(); + +#endif