test: add more cases for testSaveIntegrity

This commit is contained in:
dualfroz committed 2026-09-12 20:23:24 +02:00
1 parent a7065034d1
commit a7d9cecd35
1 file changed
+119
+119
View File
@@ -475,3 +475,122 @@ VF_TEST(repairDropsInsteadOfRebuildingWhenRegenerationIsTurnedOff) {
CHECK_EQ(repaired.chunksDropped, static_cast<std::size_t>(1)); CHECK_EQ(repaired.chunksDropped, static_cast<std::size_t>(1));
} }
VF_TEST(repairRemovesAnOrphanEntryAndKeepsTheRest) {
ByteBuffer buffer = writeSaveContainer(buildContainer(SaveVersion::V4, 3));
EntrySlot slot = slotOf(buffer, 1);
saveWriteU32At(buffer.data(), buffer.size(), slot.entryAt + 16, 0u);
refreshTableChecksum(buffer);
SaveScanner scanner;
RepairResult repaired = scanner.scanAndRepair(buffer);
CHECK(repaired.ok);
CHECK_EQ(repaired.orphanEntriesRemoved, static_cast<std::size_t>(1));
CHECK_EQ(repaired.chunksPreserved, static_cast<std::size_t>(2));
SaveContainer rebuilt;
CHECK(readSaveContainer(repaired.output.data(), repaired.output.size(), rebuilt));
CHECK_EQ(rebuilt.chunks.size(), static_cast<std::size_t>(2));
CHECK(scanner.scan(repaired.output).clean());
}
VF_TEST(repairIsRefusedWhenTheHeaderItselfIsFatal) {
ByteBuffer buffer = writeSaveContainer(buildContainer(SaveVersion::V4, 2));
saveWriteU32At(buffer.data(), buffer.size(), 0, 0u);
SaveScanner scanner;
IntegrityReport report = scanner.scan(buffer);
RepairPlan plan = scanner.planRepair(buffer.data(), buffer.size(), report);
CHECK(!plan.possible);
CHECK(plan.actions.empty());
CHECK(plan.refusal.find("backup") != std::string::npos);
RepairResult repaired = scanner.repair(buffer.data(), buffer.size(), plan);
CHECK(!repaired.ok);
CHECK(repaired.output.empty());
CHECK_EQ(repaired.failure, plan.refusal);
}
VF_TEST(repairPlanIsRefusedForABufferItWasNotBuiltFrom) {
ByteBuffer first = writeSaveContainer(buildContainer(SaveVersion::V4, 3));
ByteBuffer second = writeSaveContainer(buildContainer(SaveVersion::V4, 2));
SaveScanner scanner;
IntegrityReport report = scanner.scan(first);
RepairPlan plan = scanner.planRepair(second.data(), second.size(), report);
CHECK(!plan.possible);
CHECK(plan.refusal.find("different buffer") != std::string::npos);
}
VF_TEST(regeneratedTerrainIsDeterministicAndFollowsTheSeed) {
Chunk first = regenerateChunkFromSeed(99u, Vec3i{1, 2, 3});
Chunk again = regenerateChunkFromSeed(99u, Vec3i{1, 2, 3});
Chunk elsewhere = regenerateChunkFromSeed(99u, Vec3i{4, 2, 3});
Chunk otherSeed = regenerateChunkFromSeed(100u, Vec3i{1, 2, 3});
CHECK(sameBlocks(first, again));
CHECK(!sameBlocks(first, elsewhere));
CHECK(!sameBlocks(first, otherSeed));
CHECK_EQ(regeneratedSurfaceHeight(99u, 0, 0), regeneratedSurfaceHeight(99u, 0, 0));
CHECK(regeneratedSurfaceHeight(99u, 0, 0) >= 32);
CHECK(regeneratedSurfaceHeight(99u, 0, 0) <= 64);
CHECK(regeneratedSurfaceHeight(99u, 7, 9) >= 32);
CHECK(regeneratedSurfaceHeight(99u, 7, 9) <= 64);
}
VF_TEST(severityAndActionNamesStayReadable) {
CHECK(std::string(issueSeverityName(IssueSeverity::Info)) == "info");
CHECK(std::string(issueSeverityName(IssueSeverity::Warning)) == "warning");
CHECK(std::string(issueSeverityName(IssueSeverity::Recoverable)) == "recoverable");
CHECK(std::string(issueSeverityName(IssueSeverity::Fatal)) == "fatal");
CHECK(IssueSeverity::Info < IssueSeverity::Warning);
CHECK(IssueSeverity::Warning < IssueSeverity::Recoverable);
CHECK(IssueSeverity::Recoverable < IssueSeverity::Fatal);
CHECK(std::string(integrityIssueName(IntegrityIssueKind::BadMagic)) == "badMagic");
CHECK(std::string(integrityIssueName(IntegrityIssueKind::LightingInconsistent)) ==
"lightingInconsistent");
CHECK(std::string(repairActionName(RepairActionKind::RegenerateChunk)) == "regenerateChunk");
CHECK(std::string(repairActionName(RepairActionKind::RemoveOrphanEntry)) ==
"removeOrphanEntry");
}
VF_TEST(aContainerDamagedInSeveralPlacesIsSalvagedInOnePass) {
SaveContainer container = buildContainer(SaveVersion::V4, 5);
ByteBuffer healthy = writeSaveContainer(container);
ByteBuffer damaged = healthy;
EntrySlot broken = slotOf(damaged, 0);
saveWriteU32At(damaged.data(), damaged.size(), broken.payloadAt + 8, 0u);
EntrySlot orphan = slotOf(damaged, 2);
saveWriteU32At(damaged.data(), damaged.size(), orphan.entryAt + 16, 0u);
EntrySlot stale = slotOf(damaged, 3);
std::size_t checksumAt = stale.payloadAt + stale.payloadBytes - 4;
damaged[checksumAt] = static_cast<uint8_t>(damaged[checksumAt] ^ 0x0Fu);
refreshTableChecksum(damaged);
SaveScanner scanner;
IntegrityReport report = scanner.scan(damaged);
CHECK_EQ(report.countOf(IntegrityIssueKind::MalformedRecord), static_cast<std::size_t>(1));
CHECK_EQ(report.countOf(IntegrityIssueKind::OrphanEntry), static_cast<std::size_t>(1));
CHECK_EQ(report.countOf(IntegrityIssueKind::ChecksumMismatch), static_cast<std::size_t>(1));
RepairResult repaired = scanner.scanAndRepair(damaged);
CHECK(repaired.ok);
CHECK_EQ(repaired.chunksDropped, static_cast<std::size_t>(1));
CHECK_EQ(repaired.orphanEntriesRemoved, static_cast<std::size_t>(1));
CHECK_EQ(repaired.checksumsRewritten, static_cast<std::size_t>(1));
CHECK_EQ(repaired.chunksPreserved, static_cast<std::size_t>(3));
CHECK(scanner.scan(repaired.output).clean());
const Vec3i survivors[3] = {{1, 0, 2}, {3, 0, 6}, {4, 0, 8}};
for (const Vec3i& coord : survivors) {
ByteBuffer before;
ByteBuffer after;
CHECK(readChunkPayloadBytes(healthy.data(), healthy.size(), coord, before));
CHECK(readChunkPayloadBytes(repaired.output.data(), repaired.output.size(), coord, after));
CHECK(before == after);
}
}