// Standalone tests for shell/package/package_rollback — no REAPER, no framework. // Pins the discriminator's mechanics: a file is recorded only when this journal's // own write landed it, a pre-existing destination is refused untouched, and // rollback deletes exactly the recorded set — a bystander file beside them stays, // and a vanished file is tolerated rather than failed. #include "../src/shell/package/package_rollback.h" #include #include #include #include #include using namespace reasampler; namespace fs = std::filesystem; static int g_fail = 0; #define CHECK(cond) do { if(!(cond)) { \ std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0) static std::vector patternBytes(std::size_t n, std::uint8_t seed) { std::vector v(n); for (std::size_t i = 0; i < n; ++i) v[i] = static_cast(seed + i * 7u); return v; } static void writeScratchFile(const std::string& path, const std::vector& bytes) { std::ofstream f(path, std::ios::binary | std::ios::trunc); f.write(reinterpret_cast(bytes.data()), static_cast(bytes.size())); } static std::vector readAll(const std::string& path) { std::ifstream f(path, std::ios::binary); return std::vector(std::istreambuf_iterator(f), std::istreambuf_iterator()); } static void testLandRecordsOnSuccessOnly() { LandedFileJournal journal; const std::string path = "rb_land.bin"; const std::vector bytes = patternBytes(32, 1); CHECK(journal.writeLandedFile(path, PayloadBuffer(bytes))); CHECK(readAll(path) == bytes); CHECK(journal.landedPaths() == (std::vector{path})); CHECK(!fs::exists(path + ".rsbanktmp")); journal.rollback(); CHECK(!fs::exists(path)); } static void testExistingDestinationRefusedUntouched() { LandedFileJournal journal; const std::string path = "rb_existing.bin"; const std::vector original = patternBytes(16, 0x60); writeScratchFile(path, original); CHECK(!journal.writeLandedFile(path, PayloadBuffer(patternBytes(8, 1)))); CHECK(readAll(path) == original); // never overwritten CHECK(journal.empty()); // a refused write is not recorded const RollbackResult result = journal.rollback(); CHECK(result.deletedCount == 0); CHECK(fs::exists(path)); // rollback cannot touch a file it did not write std::error_code ec; fs::remove(path, ec); } static void testEmptyPayloadRefused() { LandedFileJournal journal; CHECK(!journal.writeLandedFile("rb_empty.bin", PayloadBuffer{})); CHECK(!fs::exists("rb_empty.bin")); CHECK(journal.empty()); } static void testRollbackDeletesExactlyTheRecordedSet() { LandedFileJournal journal; CHECK(journal.writeLandedFile("rb_a.bin", PayloadBuffer(patternBytes(8, 1)))); CHECK(journal.writeLandedFile("rb_c.bin", PayloadBuffer(patternBytes(8, 2)))); writeScratchFile("rb_bystander.bin", patternBytes(8, 3)); // not journal-written const RollbackResult result = journal.rollback(); CHECK(result.deletedCount == 2); CHECK(result.alreadyAbsentCount == 0); CHECK(result.failedCount == 0); CHECK(!fs::exists("rb_a.bin")); CHECK(!fs::exists("rb_c.bin")); CHECK(fs::exists("rb_bystander.bin")); // exactly the given files, nothing else CHECK(journal.empty()); const RollbackResult second = journal.rollback(); // cleared: a no-op CHECK(second.deletedCount == 0); CHECK(fs::exists("rb_bystander.bin")); std::error_code ec; fs::remove("rb_bystander.bin", ec); } static void testVanishedFileIsToleratedNotFailed() { LandedFileJournal journal; CHECK(journal.writeLandedFile("rb_gone.bin", PayloadBuffer(patternBytes(8, 1)))); std::error_code ec; fs::remove("rb_gone.bin", ec); // vanished between land and rollback CHECK(!ec); const RollbackResult result = journal.rollback(); CHECK(result.deletedCount == 0); CHECK(result.alreadyAbsentCount == 1); CHECK(result.failedCount == 0); } int main() { testLandRecordsOnSuccessOnly(); testExistingDestinationRefusedUntouched(); testEmptyPayloadRefused(); testRollbackDeletesExactlyTheRecordedSet(); testVanishedFileIsToleratedNotFailed(); if (g_fail == 0) std::printf("package_rollback: all tests passed\n"); else std::printf("package_rollback: %d CHECK(s) FAILED\n", g_fail); return g_fail == 0 ? 0 : 1; }