test: cover texturedChunk and buildRecord
This commit is contained in:
1 parent
a7d9cecd35
commit
f58da5b2f8
1 file changed
+158
@@ -0,0 +1,158 @@
|
||||
#include "testHarness.h"
|
||||
|
||||
#include "vf/Block.h"
|
||||
#include "vf/Region.h"
|
||||
#include "vf/SaveIntegrity.h"
|
||||
#include "vf/SaveMigration.h"
|
||||
|
||||
#include <cstdint>
|
||||
#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;
|
||||
}
|
||||
|
||||
SaveChunkRecord buildRecord(const Vec3i& coord, int seed, SaveVersion version) {
|
||||
SaveChunkRecord record;
|
||||
record.coord = coord;
|
||||
record.blocks = texturedChunk(seed);
|
||||
if (versionHasLight(version))
|
||||
record.light = defaultChunkLight(record.blocks, saveDefaultSkyLight);
|
||||
return record;
|
||||
}
|
||||
|
||||
SaveContainer buildContainer(SaveVersion version, int chunkTotal) {
|
||||
SaveContainer container;
|
||||
container.version = version;
|
||||
for (int i = 0; i < chunkTotal; ++i) {
|
||||
Vec3i coord{i % 4, i / 4, (i * 3) % 5};
|
||||
container.chunks.push_back(buildRecord(coord, i, version));
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
std::size_t distinctBlocks(const Chunk& chunk) {
|
||||
bool seen[256] = {false};
|
||||
std::size_t total = 0;
|
||||
for (Block block : chunk.raw()) {
|
||||
std::size_t slot = static_cast<std::size_t>(block);
|
||||
if (seen[slot]) continue;
|
||||
seen[slot] = true;
|
||||
++total;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
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(containerRoundTripsAtEveryFormatVersion) {
|
||||
for (uint32_t v = 1; v <= 4; ++v) {
|
||||
SaveVersion version = static_cast<SaveVersion>(v);
|
||||
SaveContainer original = buildContainer(version, 3);
|
||||
ByteBuffer buffer = writeSaveContainer(original);
|
||||
|
||||
SaveContainer reloaded;
|
||||
CHECK(readSaveContainer(buffer.data(), buffer.size(), reloaded));
|
||||
CHECK(reloaded.version == version);
|
||||
CHECK_EQ(reloaded.chunks.size(), original.chunks.size());
|
||||
for (const SaveChunkRecord& record : original.chunks) {
|
||||
const SaveChunkRecord* back = reloaded.find(record.coord);
|
||||
CHECK(back != nullptr);
|
||||
if (back == nullptr) continue;
|
||||
CHECK(sameBlocks(record.blocks, back->blocks));
|
||||
CHECK(record.light == back->light);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VF_TEST(everyFreshContainerUsesTheCanonicalLayout) {
|
||||
for (uint32_t v = 1; v <= 4; ++v) {
|
||||
SaveContainer container = buildContainer(static_cast<SaveVersion>(v), 4);
|
||||
ByteBuffer buffer = writeSaveContainer(container);
|
||||
CHECK(isCanonicalSaveLayout(buffer.data(), buffer.size()));
|
||||
CHECK_EQ(buffer.size() % saveSectorBytes, static_cast<std::size_t>(0));
|
||||
}
|
||||
}
|
||||
|
||||
VF_TEST(versionFeatureFlagsMatchTheDocumentedFormatTable) {
|
||||
CHECK(!versionHasPalette(SaveVersion::V1));
|
||||
CHECK(versionHasPalette(SaveVersion::V2));
|
||||
CHECK(!versionHasLight(SaveVersion::V2));
|
||||
CHECK(versionHasLight(SaveVersion::V3));
|
||||
CHECK(!versionHasChecksum(SaveVersion::V2));
|
||||
CHECK(versionHasChecksum(SaveVersion::V3));
|
||||
CHECK(!versionHasMetadata(SaveVersion::V3));
|
||||
CHECK(versionHasMetadata(SaveVersion::V4));
|
||||
CHECK(currentSaveVersion == SaveVersion::V4);
|
||||
CHECK(oldestSaveVersion == SaveVersion::V1);
|
||||
CHECK(std::string(saveVersionName(SaveVersion::V1)) == "v1");
|
||||
CHECK(std::string(saveVersionName(SaveVersion::V4)) == "v4");
|
||||
CHECK(isKnownSaveVersion(1u));
|
||||
CHECK(isKnownSaveVersion(4u));
|
||||
CHECK(!isKnownSaveVersion(0u));
|
||||
CHECK(!isKnownSaveVersion(5u));
|
||||
}
|
||||
|
||||
VF_TEST(migrationChainFromV1ToV4KeepsEveryBlockIdentical) {
|
||||
SaveContainer original = buildContainer(SaveVersion::V1, 5);
|
||||
ByteBuffer legacy = writeSaveContainer(original);
|
||||
|
||||
SaveMigrator migrator;
|
||||
MigrationResult result = migrator.migrateToCurrent(legacy);
|
||||
CHECK(result.ok);
|
||||
CHECK(result.failure.empty());
|
||||
CHECK(result.from == SaveVersion::V1);
|
||||
CHECK(result.to == SaveVersion::V4);
|
||||
CHECK_EQ(result.chunksMigrated, static_cast<std::size_t>(5));
|
||||
|
||||
SaveContainer lifted;
|
||||
CHECK(readSaveContainer(result.output.data(), result.output.size(), lifted));
|
||||
CHECK(lifted.version == SaveVersion::V4);
|
||||
CHECK_EQ(lifted.chunks.size(), original.chunks.size());
|
||||
for (const SaveChunkRecord& before : original.chunks) {
|
||||
const SaveChunkRecord* after = lifted.find(before.coord);
|
||||
CHECK(after != nullptr);
|
||||
if (after == nullptr) continue;
|
||||
CHECK(sameBlocks(before.blocks, after->blocks));
|
||||
}
|
||||
}
|
||||
|
||||
VF_TEST(migrationAppliesOneStepPerVersionHop) {
|
||||
SaveContainer original = buildContainer(SaveVersion::V1, 2);
|
||||
ByteBuffer legacy = writeSaveContainer(original);
|
||||
|
||||
SaveMigrator migrator;
|
||||
MigrationResult result = migrator.migrateToCurrent(legacy);
|
||||
CHECK(result.ok);
|
||||
CHECK_EQ(result.applied.size(), static_cast<std::size_t>(3));
|
||||
CHECK(result.applied[0].from == SaveVersion::V1);
|
||||
CHECK(result.applied[0].to == SaveVersion::V2);
|
||||
CHECK(result.applied[1].from == SaveVersion::V2);
|
||||
CHECK(result.applied[1].to == SaveVersion::V3);
|
||||
CHECK(result.applied[2].from == SaveVersion::V3);
|
||||
CHECK(result.applied[2].to == SaveVersion::V4);
|
||||
for (const MigrationStep& step : result.applied) CHECK(!step.summary.empty());
|
||||
|
||||
MigrationPlan plan = SaveMigrator::planFor(SaveVersion::V2, SaveVersion::V4);
|
||||
CHECK(plan.possible);
|
||||
CHECK(plan.refusal.empty());
|
||||
CHECK_EQ(plan.steps.size(), static_cast<std::size_t>(2));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user