Files
voxelforge/tests/testWorldBackup.cpp
T

161 lines
5.9 KiB
C++

#include "testHarness.h"
#include "vf/Block.h"
#include "vf/RegionCompact.h"
#include "vf/SaveIntegrity.h"
#include "vf/SaveMigration.h"
#include "vf/WorldBackup.h"
#include <cstdint>
#include <string>
#include <vector>
using namespace vf;
namespace {
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;
}
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;
}
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 baseContainer() {
SaveContainer container;
container.version = SaveVersion::V4;
container.chunks.push_back(buildRecord({0, 0, 0}, uniformChunk(Block::Stone), SaveVersion::V4));
container.chunks.push_back(buildRecord({1, 0, 0}, uniformChunk(Block::Dirt), SaveVersion::V4));
container.chunks.push_back(buildRecord({2, 0, 0}, uniformChunk(Block::Sand), SaveVersion::V4));
return container;
}
void replaceChunk(SaveContainer& container, const Vec3i& coord, const Chunk& blocks) {
for (SaveChunkRecord& record : container.chunks) {
if (!(record.coord == coord)) continue;
record.blocks = blocks;
record.light = defaultChunkLight(blocks, saveDefaultSkyLight);
return;
}
container.chunks.push_back(buildRecord(coord, blocks, container.version));
}
void dropChunk(SaveContainer& container, const Vec3i& coord) {
for (std::size_t i = 0; i < container.chunks.size(); ++i) {
if (!(container.chunks[i].coord == coord)) continue;
container.chunks.erase(container.chunks.begin() + static_cast<std::ptrdiff_t>(i));
return;
}
}
} // namespace
VF_TEST(aFullBackupRestoresTheExactBytesItCaptured) {
ByteBuffer save = writeSaveContainer(baseContainer());
BackupStore store;
uint32_t id = store.capture(BackupKind::Full, save, 0);
CHECK(id != 0u);
CHECK(store.lastFailure().empty());
RestoreResult restored = store.restore(id);
CHECK(restored.ok);
CHECK(restored.failure.empty());
CHECK(restored.save == save);
CHECK_EQ(restored.chunksRestored, static_cast<std::size_t>(3));
CHECK_EQ(restored.chunksFromFull, static_cast<std::size_t>(3));
CHECK_EQ(restored.chunksFromIncrements, static_cast<std::size_t>(0));
CHECK_EQ(restored.incrementsApplied, static_cast<std::size_t>(0));
CHECK_EQ(restored.chain.size(), static_cast<std::size_t>(1));
}
VF_TEST(everyPointOfABackupChainRestoresByteForByte) {
SaveContainer container = baseContainer();
ByteBuffer save0 = writeSaveContainer(container);
replaceChunk(container, {1, 0, 0}, texturedChunk(3));
ByteBuffer save1 = writeSaveContainer(container);
replaceChunk(container, {2, 0, 0}, texturedChunk(4));
replaceChunk(container, {3, 0, 0}, uniformChunk(Block::Gravel));
ByteBuffer save2 = writeSaveContainer(container);
dropChunk(container, {0, 0, 0});
ByteBuffer save3 = writeSaveContainer(container);
BackupStore store;
uint32_t id0 = store.capture(BackupKind::Full, save0, 0);
uint32_t id1 = store.capture(BackupKind::Incremental, save1, 1000);
uint32_t id2 = store.capture(BackupKind::Incremental, save2, 2000);
uint32_t id3 = store.capture(BackupKind::Incremental, save3, 3000);
CHECK(id0 != 0u);
CHECK(id1 != 0u);
CHECK(id2 != 0u);
CHECK(id3 != 0u);
CHECK(store.restore(id0).save == save0);
CHECK(store.restore(id1).save == save1);
CHECK(store.restore(id2).save == save2);
CHECK(store.restore(id3).save == save3);
RestoreResult latest = store.restoreLatest();
CHECK(latest.ok);
CHECK(latest.save == save3);
CHECK_EQ(latest.chain.size(), static_cast<std::size_t>(4));
CHECK_EQ(latest.incrementsApplied, static_cast<std::size_t>(3));
CHECK_EQ(latest.chunksRestored, static_cast<std::size_t>(3));
CHECK_EQ(latest.chunksRemoved, static_cast<std::size_t>(1));
}
VF_TEST(anIncrementalKeepsOnlyTheChunksThatMoved) {
SaveContainer container = baseContainer();
ByteBuffer save0 = writeSaveContainer(container);
replaceChunk(container, {1, 0, 0}, texturedChunk(3));
ByteBuffer save1 = writeSaveContainer(container);
replaceChunk(container, {2, 0, 0}, texturedChunk(4));
replaceChunk(container, {3, 0, 0}, uniformChunk(Block::Gravel));
ByteBuffer save2 = writeSaveContainer(container);
dropChunk(container, {0, 0, 0});
ByteBuffer save3 = writeSaveContainer(container);
BackupStore store;
uint32_t id0 = store.capture(BackupKind::Full, save0, 0);
uint32_t id1 = store.capture(BackupKind::Incremental, save1, 1000);
uint32_t id2 = store.capture(BackupKind::Incremental, save2, 2000);
uint32_t id3 = store.capture(BackupKind::Incremental, save3, 3000);
const BackupEntry* full = store.index().find(id0);
const BackupEntry* first = store.index().find(id1);
const BackupEntry* second = store.index().find(id2);
const BackupEntry* third = store.index().find(id3);
CHECK(full != nullptr);
CHECK(first != nullptr);
CHECK(second != nullptr);
CHECK(third != nullptr);
if (full == nullptr || first == nullptr || second == nullptr || third == nullptr) return;
CHECK(full->kind == BackupKind::Full);
CHECK_EQ(full->stored.size(), static_cast<std::size_t>(3));
CHECK_EQ(full->state.size(), static_cast<std::size_t>(3));
CHECK_EQ(full->parentId, static_cast<uint32_t>(0));