diff --git a/tests/testRegionCompact.cpp b/tests/testRegionCompact.cpp index 83137af..e51fd40 100644 --- a/tests/testRegionCompact.cpp +++ b/tests/testRegionCompact.cpp @@ -315,3 +315,110 @@ VF_TEST(repackingWithCompressionShrinksTheFileAndStillReadsBack) { CHECK(out.size() < buffer.size()); CHECK(isCanonicalSaveLayout(out.data(), out.size())); + SaveContainer reloaded; + CHECK(readSaveContainer(out.data(), out.size(), reloaded)); + CHECK_EQ(reloaded.chunks.size(), static_cast(3)); + for (const SaveChunkRecord& original : container.chunks) { + const SaveChunkRecord* back = reloaded.find(original.coord); + CHECK(back != nullptr); + if (back == nullptr) continue; + for (std::size_t i = 0; i < original.blocks.raw().size(); ++i) + CHECK(original.blocks.raw()[i] == back->blocks.raw()[i]); + CHECK(original.light == back->light); + CHECK_EQ(original.meta.generationStage, back->meta.generationStage); + } + SaveScanner scanner; + CHECK(scanner.scan(out).clean()); +} + +VF_TEST(compactionRefusesAContainerTheScannerCallsDamaged) { + ByteBuffer buffer = writeSaveContainer(uniformContainer(3)); + SaveHeader header; + CHECK(readSaveHeader(buffer.data(), buffer.size(), header)); + std::vector entries; + CHECK(readSaveTable(buffer.data(), buffer.size(), header, entries)); + std::size_t payloadAt = static_cast(entries[1].sectorOffset) * header.sectorBytes; + saveWriteU32At(buffer.data(), buffer.size(), payloadAt + 8, 0u); + + RegionCompactor compactor; + ByteBuffer out; + CompactionStats stats; + CHECK(!compactor.compact(buffer, out, stats)); + CHECK(!stats.ok); + CHECK(stats.failure.find("integrity") != std::string::npos); + CHECK(out.empty()); +} + +VF_TEST(compactionAndRepairCooperateOnADamagedFragmentedContainer) { + ByteBuffer buffer = writeSaveContainer(uniformContainer(4)); + CHECK(updateChunkInPlace(buffer, buildRecord(Vec3i{2, 0, 0}, busyChunk(6)), false)); + + SaveHeader header; + CHECK(readSaveHeader(buffer.data(), buffer.size(), header)); + std::vector entries; + CHECK(readSaveTable(buffer.data(), buffer.size(), header, entries)); + std::size_t payloadAt = static_cast(entries[0].sectorOffset) * header.sectorBytes; + saveWriteU32At(buffer.data(), buffer.size(), payloadAt + 8, 0u); + + RegionCompactor compactor; + ByteBuffer refused; + CompactionStats refusedStats; + CHECK(!compactor.compact(buffer, refused, refusedStats)); + + SaveScanner scanner; + RepairResult repaired = scanner.scanAndRepair(buffer); + CHECK(repaired.ok); + CHECK_EQ(repaired.chunksDropped, static_cast(1)); + + ByteBuffer out; + CompactionStats stats; + CHECK(compactor.compact(repaired.output, out, stats)); + CHECK(stats.ok); + CHECK_EQ(stats.fragmentationAfter, 0); + CHECK(isCanonicalSaveLayout(out.data(), out.size())); +} + +VF_TEST(inPlaceUpdateRefusesAChunkThatIsNotInTheTable) { + ByteBuffer buffer = writeSaveContainer(uniformContainer(2)); + ByteBuffer untouched = buffer; + CHECK(!updateChunkInPlace(buffer, buildRecord(Vec3i{9, 0, 0}, uniformChunk(Block::Sand)), + false)); + CHECK(buffer == untouched); + + ByteBuffer rubbish(40, 0); + CHECK(!updateChunkInPlace(rubbish, buildRecord(Vec3i{0, 0, 0}, uniformChunk(Block::Sand)), + false)); +} + +VF_TEST(inPlaceUpdateKeepsTheContainerReadableAndChecksummed) { + ByteBuffer buffer = writeSaveContainer(uniformContainer(3)); + CHECK(updateChunkInPlace(buffer, buildRecord(Vec3i{1, 0, 0}, busyChunk(21)), true)); + + SaveScanner scanner; + CHECK(scanner.scan(buffer).clean()); + + SaveContainer reloaded; + CHECK(readSaveContainer(buffer.data(), buffer.size(), reloaded)); + const SaveChunkRecord* updated = reloaded.find({1, 0, 0}); + CHECK(updated != nullptr); + if (updated == nullptr) return; + Chunk expected = busyChunk(21); + for (std::size_t i = 0; i < expected.raw().size(); ++i) + CHECK(expected.raw()[i] == updated->blocks.raw()[i]); +} + +VF_TEST(surveyAndFragmentationRejectBuffersThatAreNotContainers) { + RegionCompactor compactor; + ByteBuffer rubbish(500, 0x7F); + CHECK(compactor.survey(rubbish).empty()); + CHECK_EQ(compactor.fragmentationPercent(rubbish), -1); + + ByteBuffer stump(4, 0); + CHECK(compactor.survey(stump).empty()); + CHECK_EQ(compactor.fragmentationPercent(stump), -1); + + ByteBuffer out; + CompactionStats stats; + CHECK(!compactor.compact(rubbish, out, stats)); + CHECK(stats.failure.find("readable save container") != std::string::npos); +}