test: cover texturedChunk and uniformChunk
This commit is contained in:
1 parent
2e636110ef
commit
52643276c2
1 file changed
+156
@@ -0,0 +1,156 @@
|
||||
#include "testHarness.h"
|
||||
|
||||
#include "vf/Block.h"
|
||||
#include "vf/SaveIntegrity.h"
|
||||
#include "vf/SaveMigration.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace vf;
|
||||
|
||||
namespace {
|
||||
|
||||
Chunk texturedChunk(int seed) {
|
||||
Chunk chunk;
|
||||
for (int x = 0; x < chunkSize; ++x)
|
||||
for (int y = 0; y < chunkSize; ++y)
|
||||
for (int z = 0; z < chunkSize; ++z) {
|
||||
int value = 1 + (x * 7 + y * 5 + z * 3 + seed) % 12;
|
||||
chunk.set(x, y, z, static_cast<Block>(value));
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
Chunk uniformChunk(Block block) {
|
||||
Chunk chunk;
|
||||
for (int x = 0; x < chunkSize; ++x)
|
||||
for (int y = 0; y < chunkSize; ++y)
|
||||
for (int z = 0; z < chunkSize; ++z) chunk.set(x, y, z, block);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
SaveChunkRecord buildRecord(const Vec3i& coord, const Chunk& blocks, SaveVersion version) {
|
||||
SaveChunkRecord record;
|
||||
record.coord = coord;
|
||||
record.blocks = blocks;
|
||||
if (versionHasLight(version)) record.light = defaultChunkLight(blocks, saveDefaultSkyLight);
|
||||
return record;
|
||||
}
|
||||
|
||||
SaveContainer buildContainer(SaveVersion version, int chunkTotal) {
|
||||
SaveContainer container;
|
||||
container.version = version;
|
||||
for (int i = 0; i < chunkTotal; ++i)
|
||||
container.chunks.push_back(
|
||||
buildRecord(Vec3i{i, 0, i * 2}, texturedChunk(i), version));
|
||||
return container;
|
||||
}
|
||||
|
||||
struct EntrySlot {
|
||||
std::size_t entryAt = 0;
|
||||
std::size_t payloadAt = 0;
|
||||
std::size_t payloadBytes = 0;
|
||||
};
|
||||
|
||||
EntrySlot slotOf(const ByteBuffer& buffer, std::size_t index) {
|
||||
EntrySlot slot;
|
||||
SaveHeader header;
|
||||
if (!readSaveHeader(buffer.data(), buffer.size(), header)) return slot;
|
||||
std::vector<SaveTableEntry> entries;
|
||||
if (!readSaveTable(buffer.data(), buffer.size(), header, entries)) return slot;
|
||||
if (index >= entries.size()) return slot;
|
||||
slot.entryAt = saveHeaderBytes + index * saveEntryBytes;
|
||||
slot.payloadAt = static_cast<std::size_t>(entries[index].sectorOffset) * header.sectorBytes;
|
||||
slot.payloadBytes = entries[index].payloadBytes;
|
||||
return slot;
|
||||
}
|
||||
|
||||
void refreshTableChecksum(ByteBuffer& buffer) {
|
||||
SaveHeader header;
|
||||
if (!readSaveHeader(buffer.data(), buffer.size(), header)) return;
|
||||
if (!versionHasChecksum(static_cast<SaveVersion>(header.version))) return;
|
||||
uint32_t checksum = fnv1a32(buffer.data() + saveHeaderBytes, saveTableBytes(header));
|
||||
saveWriteU32At(buffer.data(), buffer.size(), 24, checksum);
|
||||
}
|
||||
|
||||
bool sameBlocks(const Chunk& a, const Chunk& b) {
|
||||
for (std::size_t i = 0; i < a.raw().size(); ++i)
|
||||
if (a.raw()[i] != b.raw()[i]) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
VF_TEST(scannerFindsNothingWrongWithAFreshContainer) {
|
||||
SaveContainer container = buildContainer(SaveVersion::V4, 4);
|
||||
ByteBuffer buffer = writeSaveContainer(container);
|
||||
|
||||
SaveScanner scanner;
|
||||
IntegrityReport report = scanner.scan(buffer);
|
||||
CHECK(report.clean());
|
||||
CHECK(report.headerReadable);
|
||||
CHECK(!report.fatal());
|
||||
CHECK(!report.repairable());
|
||||
CHECK(report.version == SaveVersion::V4);
|
||||
CHECK_EQ(report.declaredChunks, static_cast<std::size_t>(4));
|
||||
CHECK_EQ(report.tableEntries, static_cast<std::size_t>(4));
|
||||
CHECK_EQ(report.readableChunks, static_cast<std::size_t>(4));
|
||||
CHECK_EQ(report.bufferBytes, buffer.size());
|
||||
}
|
||||
|
||||
VF_TEST(aBufferTooShortForAHeaderIsFatal) {
|
||||
ByteBuffer stump(11, 0);
|
||||
SaveScanner scanner;
|
||||
IntegrityReport report = scanner.scan(stump);
|
||||
CHECK_EQ(report.issues.size(), static_cast<std::size_t>(1));
|
||||
CHECK(report.issues[0].kind == IntegrityIssueKind::TruncatedBuffer);
|
||||
CHECK(report.issues[0].severity == IssueSeverity::Fatal);
|
||||
CHECK(!report.headerReadable);
|
||||
CHECK(report.fatal());
|
||||
}
|
||||
|
||||
VF_TEST(aWrongMagicIsFatalAtTheStartOfTheBuffer) {
|
||||
ByteBuffer buffer = writeSaveContainer(buildContainer(SaveVersion::V4, 2));
|
||||
saveWriteU32At(buffer.data(), buffer.size(), 0, 0x4E4F5045u);
|
||||
|
||||
SaveScanner scanner;
|
||||
IntegrityReport report = scanner.scan(buffer);
|
||||
CHECK_EQ(report.issues.size(), static_cast<std::size_t>(1));
|
||||
CHECK(report.issues[0].kind == IntegrityIssueKind::BadMagic);
|
||||
CHECK(report.issues[0].severity == IssueSeverity::Fatal);
|
||||
CHECK_EQ(report.issues[0].byteOffset, static_cast<std::size_t>(0));
|
||||
CHECK(!report.headerReadable);
|
||||
}
|
||||
|
||||
VF_TEST(anUnknownFormatVersionIsFatalAtTheVersionField) {
|
||||
ByteBuffer buffer = writeSaveContainer(buildContainer(SaveVersion::V4, 2));
|
||||
saveWriteU32At(buffer.data(), buffer.size(), 4, 9u);
|
||||
|
||||
SaveScanner scanner;
|
||||
IntegrityReport report = scanner.scan(buffer);
|
||||
CHECK_EQ(report.issues.size(), static_cast<std::size_t>(1));
|
||||
CHECK(report.issues[0].kind == IntegrityIssueKind::UnknownVersion);
|
||||
CHECK(report.issues[0].severity == IssueSeverity::Fatal);
|
||||
CHECK_EQ(report.issues[0].byteOffset, static_cast<std::size_t>(4));
|
||||
CHECK(report.issues[0].detail.find("v1..v4") != std::string::npos);
|
||||
}
|
||||
|
||||
VF_TEST(aStaleRecordChecksumIsRecoverableAtTheEndOfTheRecord) {
|
||||
ByteBuffer buffer = writeSaveContainer(buildContainer(SaveVersion::V4, 3));
|
||||
EntrySlot slot = slotOf(buffer, 1);
|
||||
std::size_t checksumAt = slot.payloadAt + slot.payloadBytes - 4;
|
||||
buffer[checksumAt] = static_cast<uint8_t>(buffer[checksumAt] ^ 0x01u);
|
||||
|
||||
SaveScanner scanner;
|
||||
IntegrityReport report = scanner.scan(buffer);
|
||||
CHECK_EQ(report.issues.size(), static_cast<std::size_t>(1));
|
||||
CHECK(report.issues[0].kind == IntegrityIssueKind::ChecksumMismatch);
|
||||
CHECK(report.issues[0].severity == IssueSeverity::Recoverable);
|
||||
CHECK_EQ(report.issues[0].byteOffset, checksumAt);
|
||||
CHECK_EQ(report.issues[0].entryIndex, 1);
|
||||
CHECK(report.issues[0].hasChunkCoord);
|
||||
CHECK(report.repairable());
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user