#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) { (void)to_disk(path, content, strlen(content), false, false); } 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"; EXPECT_EQ_INT(mkdir(dir, 0755), 0); create_test_file(file1, content1); DirectoryScanner* scanner = directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 0, 0, 0, false, false, false, false, false); 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); const 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"; EXPECT_EQ_INT(mkdir(dir, 0755), 0); create_test_file(file1, content1); create_test_file(file2, content2); DirectoryScanner* scanner = directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 0, 0, 0, false, false, false, false, false); EXPECT_NOT_NULL(scanner); const 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); const Chunk* next = directory_scanner_next(scanner); EXPECT_NULL(next); chunk_destroy((void*)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"; EXPECT_EQ_INT(mkdir(root, 0755), 0); EXPECT_EQ_INT(mkdir(sub, 0755), 0); create_test_file(root_file, content); create_test_file(sub_file, content); DirectoryScanner* scanner = directory_scanner_create((char*)root, false, 0, NULL, 0, NULL, 0, 0, 0, 0, false, false, false, false, false); 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"; EXPECT_EQ_INT(mkdir(dir, 0755), 0); DirectoryScanner* scanner = directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 0, 0, 0, false, false, false, false, false); EXPECT_NOT_NULL(scanner); const Chunk* chunk = directory_scanner_next(scanner); EXPECT_NULL(chunk); directory_scanner_destroy(scanner); rmdir(dir); } /* --- Exclude/include pattern and size filter edge cases (Issue #56) --- */ static void test_scanner_exclude_pattern() { const char* dir = "test_scan_excl"; const char* f_txt = "test_scan_excl/keep.txt"; const char* f_tmp = "test_scan_excl/remove.tmp"; const char* content = "data"; EXPECT_EQ_INT(mkdir(dir, 0755), 0); create_test_file(f_txt, content); create_test_file(f_tmp, content); char* exclude[] = {"*.tmp"}; DirectoryScanner* scanner = directory_scanner_create((char*)dir, false, 0, exclude, 1, NULL, 0, 0, 0, 0, false, false, false, false, false); 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, f_txt); chunk_destroy(chunk); EXPECT_NULL(directory_scanner_next(scanner)); directory_scanner_destroy(scanner); unlink(f_txt); unlink(f_tmp); rmdir(dir); } static void test_scanner_exclude_subdirectory() { const char* root = "test_scan_excl_sub"; const char* sub = "test_scan_excl_sub/sub"; const char* root_txt = "test_scan_excl_sub/root.txt"; const char* sub_txt = "test_scan_excl_sub/sub/data.txt"; const char* sub_tmp = "test_scan_excl_sub/sub/temp.tmp"; const char* content = "data"; EXPECT_EQ_INT(mkdir(root, 0755), 0); EXPECT_EQ_INT(mkdir(sub, 0755), 0); create_test_file(root_txt, content); create_test_file(sub_txt, content); create_test_file(sub_tmp, content); char* exclude[] = {"*.tmp"}; DirectoryScanner* scanner = directory_scanner_create((char*)root, false, 0, exclude, 1, NULL, 0, 0, 0, 0, false, false, false, false, false); EXPECT_NOT_NULL(scanner); int total = 0; Chunk* chunk; while ((chunk = directory_scanner_next(scanner)) != NULL) { total += chunk->element_count; for (int i = 0; i < chunk->element_count; i++) { size_t len = strlen(chunk->items[i]->path); EXPECT_TRUE(len < 4 || strcmp(chunk->items[i]->path + len - 4, ".tmp") != 0); } chunk_destroy(chunk); } EXPECT_EQ_INT(total, 2); directory_scanner_destroy(scanner); unlink(root_txt); unlink(sub_txt); unlink(sub_tmp); rmdir(sub); rmdir(root); } static void test_scanner_include_and_exclude() { const char* dir = "test_scan_inc_exc"; const char* f_txt = "test_scan_inc_exc/a.txt"; const char* f_log = "test_scan_inc_exc/b.log"; const char* f_bak = "test_scan_inc_exc/c.bak"; const char* content = "filter"; EXPECT_EQ_INT(mkdir(dir, 0755), 0); create_test_file(f_txt, content); create_test_file(f_log, content); create_test_file(f_bak, content); char* exclude[] = {"*.bak"}; char* include[] = {"*.txt", "*.log"}; DirectoryScanner* scanner = directory_scanner_create((char*)dir, false, 0, exclude, 1, include, 2, 0, 0, 0, false, false, false, false, false); EXPECT_NOT_NULL(scanner); Chunk* chunk = directory_scanner_next(scanner); EXPECT_NOT_NULL(chunk); EXPECT_EQ_INT(chunk->element_count, 2); int found_txt = 0, found_log = 0; for (int i = 0; i < chunk->element_count; i++) { if (strstr(chunk->items[i]->path, "a.txt")) found_txt = 1; if (strstr(chunk->items[i]->path, "b.log")) found_log = 1; } EXPECT_TRUE(found_txt); EXPECT_TRUE(found_log); chunk_destroy(chunk); EXPECT_NULL(directory_scanner_next(scanner)); directory_scanner_destroy(scanner); unlink(f_txt); unlink(f_log); unlink(f_bak); rmdir(dir); } static void test_scanner_max_size() { const char* dir = "test_scan_max"; const char* small = "test_scan_max/small.txt"; const char* large = "test_scan_max/large.txt"; EXPECT_EQ_INT(mkdir(dir, 0755), 0); create_test_file(small, "tiny"); create_test_file(large, "this_content_is_longer_than_ten_chars"); /* max_size = 10 — only files <= 10 bytes */ DirectoryScanner* scanner = directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 10, 0, 0, false, false, false, false, false); 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, small); chunk_destroy(chunk); EXPECT_NULL(directory_scanner_next(scanner)); directory_scanner_destroy(scanner); unlink(small); unlink(large); rmdir(dir); } static void test_scanner_min_size() { const char* dir = "test_scan_min"; const char* empty_f = "test_scan_min/empty.txt"; const char* data_f = "test_scan_min/data.txt"; EXPECT_EQ_INT(mkdir(dir, 0755), 0); create_test_file(empty_f, ""); create_test_file(data_f, "some content here"); /* min_size = 1 — only files >= 1 byte */ DirectoryScanner* scanner = directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 0, 1, 0, false, false, false, false, false); 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, data_f); chunk_destroy(chunk); EXPECT_NULL(directory_scanner_next(scanner)); directory_scanner_destroy(scanner); unlink(empty_f); unlink(data_f); rmdir(dir); } static void test_scanner_size_range() { const char* dir = "test_scan_range"; const char* tiny = "test_scan_range/tiny.txt"; const char* medium = "test_scan_range/med.txt"; const char* huge = "test_scan_range/huge.txt"; EXPECT_EQ_INT(mkdir(dir, 0755), 0); create_test_file(tiny, "ab"); create_test_file(medium, "hello world"); create_test_file(huge, "this is a much larger file for testing size filters"); /* Only files between 3 and 20 bytes */ DirectoryScanner* scanner = directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 20, 3, 0, false, false, false, false, false); 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, medium); chunk_destroy(chunk); EXPECT_NULL(directory_scanner_next(scanner)); directory_scanner_destroy(scanner); unlink(tiny); unlink(medium); unlink(huge); rmdir(dir); } static void test_scanner_mixed_patterns() { /* Combine exclude, include, and size filters together */ const char* dir = "test_scan_mixed"; const char* a_txt = "test_scan_mixed/a.txt"; /* size ~= 5 */ const char* b_bin = "test_scan_mixed/b.bin"; /* size ~= 13 */ const char* c_txt = "test_scan_mixed/c.txt"; /* size ~= 5 */ const char* d_bak = "test_scan_mixed/d.bak"; /* size ~= 42 */ EXPECT_EQ_INT(mkdir(dir, 0755), 0); create_test_file(a_txt, "aaaaa"); create_test_file(b_bin, "bbbbbbbbbbbbb"); create_test_file(c_txt, "ccccc"); create_test_file(d_bak, "dddddddddddddddddddddddddddddddddddddddddd"); /* Exclude *.bak, include *.txt, min_size=3, max_size=10 */ char* exclude[] = {"*.bak"}; char* include[] = {"*.txt"}; DirectoryScanner* scanner = directory_scanner_create((char*)dir, false, 0, exclude, 1, include, 1, 10, 3, 0, false, false, false, false, false); EXPECT_NOT_NULL(scanner); Chunk* chunk = directory_scanner_next(scanner); EXPECT_NOT_NULL(chunk); /* Both a.txt and c.txt meet the criteria: .txt extension, size 5 <= 10 and >= 3 */ EXPECT_EQ_INT(chunk->element_count, 2); chunk_destroy(chunk); EXPECT_NULL(directory_scanner_next(scanner)); directory_scanner_destroy(scanner); unlink(a_txt); unlink(b_bin); unlink(c_txt); unlink(d_bak); rmdir(dir); } static void test_scanner_no_patterns() { /* Explicit test with no exclude/include patterns and no size filters. * This verifies that NULL/0 for all pattern parameters works correctly. */ const char* dir = "test_scan_none"; const char* f1 = "test_scan_none/f1.txt"; const char* f2 = "test_scan_none/f2.txt"; EXPECT_EQ_INT(mkdir(dir, 0755), 0); create_test_file(f1, "first"); create_test_file(f2, "second"); DirectoryScanner* scanner = directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 0, 0, 0, false, false, false, false, false); EXPECT_NOT_NULL(scanner); Chunk* chunk = directory_scanner_next(scanner); EXPECT_NOT_NULL(chunk); EXPECT_EQ_INT(chunk->element_count, 2); chunk_destroy(chunk); EXPECT_NULL(directory_scanner_next(scanner)); directory_scanner_destroy(scanner); unlink(f1); unlink(f2); rmdir(dir); } void test_scanner() { test_scanner_single_file(); test_scanner_multiple_files(); test_scanner_subdirectory(); test_scanner_empty_directory(); /* Issue #56: scanner pattern edge cases */ test_scanner_exclude_pattern(); test_scanner_exclude_subdirectory(); test_scanner_include_and_exclude(); test_scanner_max_size(); test_scanner_min_size(); test_scanner_size_range(); test_scanner_mixed_patterns(); test_scanner_no_patterns(); }