#include #include #include #include #include #include #include #include #include "../native/dataset_hash/dataset_hash.h" // Get absolute path of current working directory static std::string get_cwd() { char buf[PATH_MAX]; if (getcwd(buf, sizeof(buf)) != nullptr) { return std::string(buf); } return ""; } // Test helper: create a file with content static int create_file(const char* path, const char* content) { FILE* f = fopen(path, "w"); if (!f) return -1; fprintf(f, "%s", content); fclose(f); return 0; } // Test: Recursive dataset hashing // Verifies that nested directories are traversed and files are sorted static int test_recursive_hashing() { std::string cwd = get_cwd(); if (cwd.empty()) return -1; char base_dir[4096]; snprintf(base_dir, sizeof(base_dir), "%s/test_recursive_XXXXXX", cwd.c_str()); if (mkdtemp(base_dir) == nullptr) return -1; // Create nested structure char subdir[4096]; char deeper[4096]; snprintf(subdir, sizeof(subdir), "%s/subdir", base_dir); snprintf(deeper, sizeof(deeper), "%s/subdir/deeper", base_dir); if (mkdir(subdir, 0755) != 0) { rmdir(base_dir); return -1; } if (mkdir(deeper, 0755) != 0) { rmdir(subdir); rmdir(base_dir); return -1; } // Create files char path_z[4096]; char path_b[4096]; char path_a[4096]; char path_deep[4096]; snprintf(path_z, sizeof(path_z), "%s/z_file.txt", base_dir); snprintf(path_b, sizeof(path_b), "%s/subdir/b_file.txt", base_dir); snprintf(path_a, sizeof(path_a), "%s/subdir/a_file.txt", base_dir); snprintf(path_deep, sizeof(path_deep), "%s/subdir/deeper/deep_file.txt", base_dir); if (create_file(path_z, "z content") != 0 || create_file(path_b, "b content") != 0 || create_file(path_a, "a content") != 0 || create_file(path_deep, "deep content") != 0) { unlink(path_z); unlink(path_b); unlink(path_a); unlink(path_deep); rmdir(deeper); rmdir(subdir); rmdir(base_dir); return -1; } // Hash the directory fh_context_t* ctx = fh_init(0); if (!ctx) { unlink(path_z); unlink(path_b); unlink(path_a); unlink(path_deep); rmdir(deeper); rmdir(subdir); rmdir(base_dir); return -1; } char* hash1 = fh_hash_directory(ctx, base_dir); if (!hash1 || strlen(hash1) != 64) { fh_cleanup(ctx); unlink(path_z); unlink(path_b); unlink(path_a); unlink(path_deep); rmdir(deeper); rmdir(subdir); rmdir(base_dir); return -1; } // Hash again - should produce identical result (deterministic) char* hash2 = fh_hash_directory(ctx, base_dir); if (!hash2 || strcmp(hash1, hash2) != 0) { fh_free_string(hash1); fh_cleanup(ctx); unlink(path_z); unlink(path_b); unlink(path_a); unlink(path_deep); rmdir(deeper); rmdir(subdir); rmdir(base_dir); return -1; } // Cleanup fh_free_string(hash1); fh_free_string(hash2); fh_cleanup(ctx); // Remove test files unlink(path_deep); unlink(path_a); unlink(path_b); unlink(path_z); rmdir(deeper); rmdir(subdir); rmdir(base_dir); return 0; } // Test: Empty nested directories static int test_empty_nested_dirs() { std::string cwd = get_cwd(); char base_dir[4096]; snprintf(base_dir, sizeof(base_dir), "%s/test_empty_XXXXXX", cwd.c_str()); if (mkdtemp(base_dir) == nullptr) return -1; char empty_subdir[4096]; snprintf(empty_subdir, sizeof(empty_subdir), "%s/empty_sub", base_dir); if (mkdir(empty_subdir, 0755) != 0) { rmdir(base_dir); return -1; } char path[4096]; snprintf(path, sizeof(path), "%s/only_file.txt", base_dir); if (create_file(path, "content") != 0) { rmdir(empty_subdir); rmdir(base_dir); return -1; } fh_context_t* ctx = fh_init(0); if (!ctx) { unlink(path); rmdir(empty_subdir); rmdir(base_dir); return -1; } char* hash = fh_hash_directory(ctx, base_dir); if (!hash || strlen(hash) != 64) { fh_cleanup(ctx); unlink(path); rmdir(empty_subdir); rmdir(base_dir); return -1; } fh_free_string(hash); fh_cleanup(ctx); unlink(path); rmdir(empty_subdir); rmdir(base_dir); return 0; } int main() { printf("Testing recursive dataset hashing...\n"); if (test_recursive_hashing() != 0) { printf("FAILED\n"); return 1; } if (test_empty_nested_dirs() != 0) { printf("FAILED\n"); return 1; } printf("All recursive dataset tests passed.\n"); return 0; }