#include #include #include #include #include #include #include #include #include #include namespace fs = std::filesystem; // Helper function to execute a command and capture its output bool execute_command(const std::string& cmd, std::string& output) { FILE* pipe = popen(cmd.c_str(), "r"); if (!pipe) return false; char buffer[128]; output.clear(); while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { output += buffer; } int status = pclose(pipe); return WIFEXITED(status) && WEXITSTATUS(status) == 0; } // Helper function to calculate file hash (simple checksum for testing) std::string calculate_file_hash(const fs::path& filepath) { std::ifstream file(filepath, std::ios::binary); if (!file.is_open()) return ""; std::ostringstream oss; oss << file.rdbuf(); std::string content = oss.str(); // Simple hash for demonstration size_t hash = 0; for (char c : content) { hash = hash * 31 + static_cast(c); } std::stringstream ss; ss << std::hex << hash; return ss.str(); } // Helper function to compare two files bool compare_files(const fs::path& file1, const fs::path& file2) { // Compare existence if (!fs::exists(file1) || !fs::exists(file2)) { std::cout << "File existence mismatch: " << file1 << " vs " << file2 << std::endl; return false; } // Compare size if (fs::file_size(file1) != fs::file_size(file2)) { std::cout << "File size mismatch: " << file1 << " (" << fs::file_size(file1) << ") vs " << file2 << " (" << fs::file_size(file2) << ")" << std::endl; return false; } // Compare content std::ifstream f1(file1, std::ios::binary); std::ifstream f2(file2, std::ios::binary); std::string content1((std::istreambuf_iterator(f1)), std::istreambuf_iterator()); std::string content2((std::istreambuf_iterator(f2)), std::istreambuf_iterator()); if (content1 != content2) { std::cout << "File content mismatch: " << file1 << " vs " << file2 << std::endl; return false; } // Compare permissions auto perms1 = fs::status(file1).permissions(); auto perms2 = fs::status(file2).permissions(); if (perms1 != perms2) { std::cout << "File permissions mismatch: " << file1 << " (" << static_cast(perms1) << ") vs " << file2 << " (" << static_cast(perms2) << ")" << std::endl; return false; } return true; } // Helper function to compare directories recursively bool compare_directories(const fs::path& dir1, const fs::path& dir2) { std::vector files1, files2; // Collect all files from dir1 for (const auto& entry : fs::recursive_directory_iterator(dir1)) { if (fs::is_regular_file(entry)) { files1.push_back(fs::relative(entry.path(), dir1)); } } // Collect all files from dir2 for (const auto& entry : fs::recursive_directory_iterator(dir2)) { if (fs::is_regular_file(entry)) { files2.push_back(fs::relative(entry.path(), dir2)); } } // Sort for comparison std::sort(files1.begin(), files1.end()); std::sort(files2.begin(), files2.end()); // Check if same files exist if (files1 != files2) { std::cout << "Directory structure mismatch!" << std::endl; return false; } // Compare each file bool all_match = true; for (const auto& rel_path : files1) { if (!compare_files(dir1 / rel_path, dir2 / rel_path)) { all_match = false; } } return all_match; } int main() { std::cout << "=== Dehydrate Test Program ===" << std::endl; // Create test directory structure fs::path test_root = "dehydrate_test_data"; fs::path original_dir = test_root / "original"; fs::path generated_dir = test_root / "generated"; fs::path recreated_dir = test_root / "recreated"; // Clean up any existing test data if (fs::exists(test_root)) { fs::remove_all(test_root); } // Create directories fs::create_directories(original_dir / "subdir"); fs::create_directories(generated_dir); fs::create_directories(recreated_dir); std::cout << "\n1. Creating test files..." << std::endl; // Create test file 1: Simple text file { std::ofstream file(original_dir / "test1.txt"); file << "This is a simple text file.\nIt has multiple lines.\nLine 3."; file.close(); fs::permissions(original_dir / "test1.txt", fs::perms::owner_read | fs::perms::owner_write); } // Create test file 2: Binary file with special characters { std::ofstream file(original_dir / "test2.bin", std::ios::binary); unsigned char binary_data[] = {0x00, 0xFF, 0x42, 0x13, 0x37, 0xDE, 0xAD, 0xBE, 0xEF}; file.write(reinterpret_cast(binary_data), sizeof(binary_data)); file.close(); fs::permissions(original_dir / "test2.bin", fs::perms::owner_all); } // Create test file 3: Executable script { std::ofstream file(original_dir / "test3.sh"); file << "#!/bin/bash\necho 'Hello from test script'\nexit 0"; file.close(); fs::permissions(original_dir / "test3.sh", fs::perms::owner_all | fs::perms::group_read | fs::perms::group_exec); } // Create test file 4: File in subdirectory { std::ofstream file(original_dir / "subdir" / "nested.txt"); file << "This file is in a subdirectory."; file.close(); fs::permissions(original_dir / "subdir" / "nested.txt", fs::perms::owner_read); } // Create test file 5: Very small file { std::ofstream file(original_dir / "small.txt"); file << "x"; // Single character to avoid empty file file.close(); fs::permissions(original_dir / "small.txt", fs::perms::owner_read | fs::perms::owner_write); } std::cout << "Created test files in: " << original_dir << std::endl; // Build dehydrate if not already built std::cout << "\n2. Building dehydrate tool..." << std::endl; std::string output; if (!fs::exists("../output/dehydrate")) { if (!execute_command("cd .. && ./build.sh", output)) { std::cerr << "Failed to build dehydrate!" << std::endl; return 1; } } // Test single file dehydration std::cout << "\n3. Testing single file dehydration..." << std::endl; { std::string cmd = "../output/dehydrate -s " + (original_dir / "test1.txt").string() + " " + generated_dir.string(); if (!execute_command(cmd, output)) { std::cerr << "Failed to dehydrate single file!" << std::endl; return 1; } // Check if generated files exist if (!fs::exists(generated_dir / "_test1.cpp") || !fs::exists(generated_dir / "_test1.hpp")) { std::cerr << "Generated files not found!" << std::endl; return 1; } std::cout << "Generated: _test1.cpp and _test1.hpp" << std::endl; } // Test directory dehydration std::cout << "\n4. Testing directory dehydration..." << std::endl; { std::string cmd = "../output/dehydrate -s " + original_dir.string() + " " + generated_dir.string(); if (!execute_command(cmd, output)) { std::cerr << "Failed to dehydrate directory!" << std::endl; return 1; } // Check if generated files exist if (!fs::exists(generated_dir / "_original.cpp") || !fs::exists(generated_dir / "_original.hpp")) { std::cerr << "Generated directory files not found!" << std::endl; return 1; } std::cout << "Generated: _original.cpp and _original.hpp" << std::endl; } // Create test program that uses the generated code std::cout << "\n5. Creating test program to recreate files..." << std::endl; { std::ofstream test_prog(test_root / "test_recreate.cpp"); test_prog << R"cpp( #include #include "generated/_test1.hpp" #include "generated/_original.hpp" int main() { std::cout << "Testing file recreation..." << std::endl; // Test single file recreation std::cout << "Recreating single file..." << std::endl; if (recreate_test1::recreate_file("recreated")) { std::cout << "Single file recreation returned true" << std::endl; } // Test directory recreation std::cout << "Recreating directory tree..." << std::endl; if (recreate_original::recreate_tree("recreated/tree")) { std::cout << "Directory recreation returned true" << std::endl; } return 0; } )cpp"; } // Compile the test program std::cout << "\n6. Compiling recreation test program..." << std::endl; { std::string cmd = "cd " + test_root.string() + " && g++ -std=c++23 -static -I. test_recreate.cpp generated/_test1.cpp generated/_original.cpp" + " -o test_recreate"; if (!execute_command(cmd, output)) { std::cerr << "Failed to compile test program!" << std::endl; std::cerr << "Output: " << output << std::endl; return 1; } } // Run the recreation test std::cout << "\n7. Running recreation test..." << std::endl; { std::string cmd = "cd " + test_root.string() + " && ./test_recreate"; if (!execute_command(cmd, output)) { std::cerr << "Failed to run recreation test!" << std::endl; return 1; } std::cout << output << std::endl; } // Compare results std::cout << "\n8. Comparing original and recreated files..." << std::endl; // Compare single file std::cout << "\nComparing single file recreation:" << std::endl; if (compare_files(original_dir / "test1.txt", recreated_dir / "test1.txt")) { std::cout << "✓ Single file matches!" << std::endl; } else { std::cout << "✗ Single file does NOT match!" << std::endl; } // Compare directory tree std::cout << "\nComparing directory tree recreation:" << std::endl; if (compare_directories(original_dir, recreated_dir / "tree")) { std::cout << "✓ Directory tree matches!" << std::endl; } else { std::cout << "✗ Directory tree does NOT match!" << std::endl; } // Test re-running recreation (should detect no changes needed) std::cout << "\n9. Testing re-run (should detect no changes)..." << std::endl; { std::string cmd = "cd " + test_root.string() + " && ./test_recreate"; if (!execute_command(cmd, output)) { std::cerr << "Failed to re-run recreation test!" << std::endl; return 1; } std::cout << output << std::endl; } // Modify a file and test update detection std::cout << "\n10. Testing update detection..." << std::endl; { // Modify the recreated file std::ofstream file(recreated_dir / "test1.txt"); file << "Modified content"; file.close(); // Re-run recreation std::string cmd = "cd " + test_root.string() + " && ./test_recreate"; if (!execute_command(cmd, output)) { std::cerr << "Failed to test update!" << std::endl; return 1; } std::cout << output << std::endl; // Verify it was restored if (compare_files(original_dir / "test1.txt", recreated_dir / "test1.txt")) { std::cout << "✓ File correctly restored after modification!" << std::endl; } else { std::cout << "✗ File NOT restored correctly!" << std::endl; } } std::cout << "\n=== Test Complete ===" << std::endl; return 0; }