Loading test/parity_test.cpp +117 −0 Original line number Diff line number Diff line Loading @@ -3,12 +3,15 @@ #include <paritypp/parity.h> #include <paritypp/auth.h> #include <paritypp/protocol.h> #include <paritypp/server.h> #include <iostream> #include <cstring> #include <cassert> #include <vector> #include <algorithm> #include <filesystem> #include <cstdlib> using namespace paritypp; Loading Loading @@ -340,6 +343,119 @@ static void test_auth_protocol() { std::cout << " Auth protocol tests passed." << std::endl; } static void test_file_block_store() { std::cout << "=== File Block Store (write-hole protection) ===" << std::endl; std::string dir = "/tmp/paritypp_test_" + std::to_string(getpid()); std::filesystem::remove_all(dir); // Test 1: basic store/fetch with CRC32 { file_block_store store(dir, 0); // threshold=0: flush every write std::vector<uint8_t> data = {1, 2, 3, 4, 5, 6, 7, 8}; assert(store.store(100, 0, data.data(), data.size())); std::vector<uint8_t> out; assert(store.fetch(100, 0, out)); assert(out == data); assert(store.total_blocks() == 1); assert(store.total_groups() == 1); } // Test 2: reopen and verify data survives (CRC32 validated on scan) { file_block_store store(dir, 0); std::vector<uint8_t> out; assert(store.fetch(100, 0, out)); std::vector<uint8_t> expected = {1, 2, 3, 4, 5, 6, 7, 8}; assert(out == expected); assert(store.total_blocks() == 1); } // Test 3: overwrite + vacuum { file_block_store store(dir, 0); std::vector<uint8_t> data2 = {10, 20, 30}; assert(store.store(100, 0, data2.data(), data2.size())); std::vector<uint8_t> out; assert(store.fetch(100, 0, out)); assert(out == data2); assert(store.vacuum()); out.clear(); assert(store.fetch(100, 0, out)); assert(out == data2); } // Test 4: multiple groups and blocks { file_block_store store(dir, 0); for (uint64_t g = 200; g < 205; ++g) { for (uint32_t b = 0; b < 4; ++b) { std::vector<uint8_t> d(64, static_cast<uint8_t>(g + b)); store.store(g, b, d.data(), d.size()); } } assert(store.total_groups() >= 5); // 200-204 + maybe 100 from above for (uint64_t g = 200; g < 205; ++g) { auto blocks = store.list_blocks(g); assert(blocks.size() == 4); for (uint32_t b = 0; b < 4; ++b) { std::vector<uint8_t> out; assert(store.fetch(g, b, out)); assert(out.size() == 64); assert(out[0] == static_cast<uint8_t>(g + b)); } } } // Test 5: remove group { file_block_store store(dir, 0); assert(store.remove_group(200)); auto blocks = store.list_blocks(200); assert(blocks.empty()); } // Test 6: WAL recovery — simulate crash by corrupting end of data file { // Write some data with buffering (threshold > 0) std::filesystem::remove_all(dir); { file_block_store store(dir, 0); std::vector<uint8_t> good = {0xAA, 0xBB, 0xCC, 0xDD}; store.store(300, 0, good.data(), good.size()); } // Append garbage to simulate a partial/corrupt write { std::string data_path = dir + "/blocks.bin"; FILE* f = fopen(data_path.c_str(), "ab"); assert(f); uint8_t garbage[10] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}; fwrite(garbage, 1, sizeof(garbage), f); fclose(f); } // Reopen — should detect corrupt trailing record via CRC and truncate { file_block_store store(dir, 0); std::vector<uint8_t> out; assert(store.fetch(300, 0, out)); std::vector<uint8_t> expected = {0xAA, 0xBB, 0xCC, 0xDD}; assert(out == expected); assert(store.total_blocks() == 1); } } std::filesystem::remove_all(dir); std::cout << " File block store tests passed." << std::endl; } int main() { std::cout << "libparitypp Test Suite" << std::endl; std::cout << "=====================" << std::endl << std::endl; Loading @@ -354,6 +470,7 @@ int main() { test_auth_store(); test_auth_hmac(); test_auth_protocol(); test_file_block_store(); std::cout << std::endl << "All tests passed!" << std::endl; return 0; Loading Loading
test/parity_test.cpp +117 −0 Original line number Diff line number Diff line Loading @@ -3,12 +3,15 @@ #include <paritypp/parity.h> #include <paritypp/auth.h> #include <paritypp/protocol.h> #include <paritypp/server.h> #include <iostream> #include <cstring> #include <cassert> #include <vector> #include <algorithm> #include <filesystem> #include <cstdlib> using namespace paritypp; Loading Loading @@ -340,6 +343,119 @@ static void test_auth_protocol() { std::cout << " Auth protocol tests passed." << std::endl; } static void test_file_block_store() { std::cout << "=== File Block Store (write-hole protection) ===" << std::endl; std::string dir = "/tmp/paritypp_test_" + std::to_string(getpid()); std::filesystem::remove_all(dir); // Test 1: basic store/fetch with CRC32 { file_block_store store(dir, 0); // threshold=0: flush every write std::vector<uint8_t> data = {1, 2, 3, 4, 5, 6, 7, 8}; assert(store.store(100, 0, data.data(), data.size())); std::vector<uint8_t> out; assert(store.fetch(100, 0, out)); assert(out == data); assert(store.total_blocks() == 1); assert(store.total_groups() == 1); } // Test 2: reopen and verify data survives (CRC32 validated on scan) { file_block_store store(dir, 0); std::vector<uint8_t> out; assert(store.fetch(100, 0, out)); std::vector<uint8_t> expected = {1, 2, 3, 4, 5, 6, 7, 8}; assert(out == expected); assert(store.total_blocks() == 1); } // Test 3: overwrite + vacuum { file_block_store store(dir, 0); std::vector<uint8_t> data2 = {10, 20, 30}; assert(store.store(100, 0, data2.data(), data2.size())); std::vector<uint8_t> out; assert(store.fetch(100, 0, out)); assert(out == data2); assert(store.vacuum()); out.clear(); assert(store.fetch(100, 0, out)); assert(out == data2); } // Test 4: multiple groups and blocks { file_block_store store(dir, 0); for (uint64_t g = 200; g < 205; ++g) { for (uint32_t b = 0; b < 4; ++b) { std::vector<uint8_t> d(64, static_cast<uint8_t>(g + b)); store.store(g, b, d.data(), d.size()); } } assert(store.total_groups() >= 5); // 200-204 + maybe 100 from above for (uint64_t g = 200; g < 205; ++g) { auto blocks = store.list_blocks(g); assert(blocks.size() == 4); for (uint32_t b = 0; b < 4; ++b) { std::vector<uint8_t> out; assert(store.fetch(g, b, out)); assert(out.size() == 64); assert(out[0] == static_cast<uint8_t>(g + b)); } } } // Test 5: remove group { file_block_store store(dir, 0); assert(store.remove_group(200)); auto blocks = store.list_blocks(200); assert(blocks.empty()); } // Test 6: WAL recovery — simulate crash by corrupting end of data file { // Write some data with buffering (threshold > 0) std::filesystem::remove_all(dir); { file_block_store store(dir, 0); std::vector<uint8_t> good = {0xAA, 0xBB, 0xCC, 0xDD}; store.store(300, 0, good.data(), good.size()); } // Append garbage to simulate a partial/corrupt write { std::string data_path = dir + "/blocks.bin"; FILE* f = fopen(data_path.c_str(), "ab"); assert(f); uint8_t garbage[10] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}; fwrite(garbage, 1, sizeof(garbage), f); fclose(f); } // Reopen — should detect corrupt trailing record via CRC and truncate { file_block_store store(dir, 0); std::vector<uint8_t> out; assert(store.fetch(300, 0, out)); std::vector<uint8_t> expected = {0xAA, 0xBB, 0xCC, 0xDD}; assert(out == expected); assert(store.total_blocks() == 1); } } std::filesystem::remove_all(dir); std::cout << " File block store tests passed." << std::endl; } int main() { std::cout << "libparitypp Test Suite" << std::endl; std::cout << "=====================" << std::endl << std::endl; Loading @@ -354,6 +470,7 @@ int main() { test_auth_store(); test_auth_hmac(); test_auth_protocol(); test_file_block_store(); std::cout << std::endl << "All tests passed!" << std::endl; return 0; Loading