test: cover testSaveMigration
This commit is contained in:
1 parent
f58da5b2f8
commit
278feeeb65
1 file changed
+157
@@ -156,3 +156,160 @@ VF_TEST(migrationAppliesOneStepPerVersionHop) {
|
|||||||
CHECK_EQ(plan.steps.size(), static_cast<std::size_t>(2));
|
CHECK_EQ(plan.steps.size(), static_cast<std::size_t>(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VF_TEST(everyIntermediateVersionIsItselfAValidContainer) {
|
||||||
|
SaveContainer original = buildContainer(SaveVersion::V1, 3);
|
||||||
|
ByteBuffer legacy = writeSaveContainer(original);
|
||||||
|
SaveMigrator migrator;
|
||||||
|
SaveScanner scanner;
|
||||||
|
|
||||||
|
for (uint32_t target = 2; target <= 4; ++target) {
|
||||||
|
MigrationResult result = migrator.migrate(legacy.data(), legacy.size(),
|
||||||
|
static_cast<SaveVersion>(target));
|
||||||
|
CHECK(result.ok);
|
||||||
|
IntegrityReport report = scanner.scan(result.output);
|
||||||
|
CHECK(report.clean());
|
||||||
|
CHECK(report.version == static_cast<SaveVersion>(target));
|
||||||
|
CHECK_EQ(report.readableChunks, static_cast<std::size_t>(3));
|
||||||
|
CHECK(isCanonicalSaveLayout(result.output.data(), result.output.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(migrationDownwardIsRefusedWithAStatedReason) {
|
||||||
|
SaveContainer modern = buildContainer(SaveVersion::V4, 2);
|
||||||
|
ByteBuffer buffer = writeSaveContainer(modern);
|
||||||
|
|
||||||
|
SaveMigrator migrator;
|
||||||
|
MigrationResult result = migrator.migrate(buffer.data(), buffer.size(), SaveVersion::V2);
|
||||||
|
CHECK(!result.ok);
|
||||||
|
CHECK(result.output.empty());
|
||||||
|
CHECK(result.failure.find("downgrade") != std::string::npos);
|
||||||
|
CHECK(result.failure.find("v4 -> v2") != std::string::npos);
|
||||||
|
|
||||||
|
MigrationPlan plan = SaveMigrator::planFor(SaveVersion::V4, SaveVersion::V1);
|
||||||
|
CHECK(!plan.possible);
|
||||||
|
CHECK(plan.steps.empty());
|
||||||
|
CHECK(!plan.refusal.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(migrationToTheSameVersionCopiesTheBufferVerbatim) {
|
||||||
|
SaveContainer modern = buildContainer(SaveVersion::V4, 3);
|
||||||
|
ByteBuffer buffer = writeSaveContainer(modern);
|
||||||
|
|
||||||
|
SaveMigrator migrator;
|
||||||
|
MigrationResult result = migrator.migrate(buffer.data(), buffer.size(), SaveVersion::V4);
|
||||||
|
CHECK(result.ok);
|
||||||
|
CHECK(result.applied.empty());
|
||||||
|
CHECK(result.output == buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(fieldsAddedByAMigrationGetTheirDocumentedDefaults) {
|
||||||
|
SaveContainer original = buildContainer(SaveVersion::V2, 2);
|
||||||
|
ByteBuffer buffer = writeSaveContainer(original);
|
||||||
|
|
||||||
|
SaveMigrator migrator;
|
||||||
|
MigrationResult toV3 = migrator.migrate(buffer.data(), buffer.size(), SaveVersion::V3);
|
||||||
|
CHECK(toV3.ok);
|
||||||
|
SaveContainer lit;
|
||||||
|
CHECK(readSaveContainer(toV3.output.data(), toV3.output.size(), lit));
|
||||||
|
for (const SaveChunkRecord& record : lit.chunks) {
|
||||||
|
CHECK_EQ(record.light.size(), static_cast<std::size_t>(chunkVolume));
|
||||||
|
CHECK(record.light == defaultChunkLight(record.blocks, saveDefaultSkyLight));
|
||||||
|
}
|
||||||
|
|
||||||
|
MigrationResult toV4 = migrator.migrate(buffer.data(), buffer.size(), SaveVersion::V4);
|
||||||
|
CHECK(toV4.ok);
|
||||||
|
SaveContainer stamped;
|
||||||
|
CHECK(readSaveContainer(toV4.output.data(), toV4.output.size(), stamped));
|
||||||
|
for (const SaveChunkRecord& record : stamped.chunks) {
|
||||||
|
CHECK_EQ(record.meta.inhabitedTicks, static_cast<uint64_t>(0));
|
||||||
|
CHECK_EQ(record.meta.lastSavedTick, static_cast<uint64_t>(0));
|
||||||
|
CHECK_EQ(record.meta.generationStage, saveStageComplete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(migrationCountsTheFieldsEachStepCreated) {
|
||||||
|
SaveContainer original = buildContainer(SaveVersion::V1, 3);
|
||||||
|
ByteBuffer legacy = writeSaveContainer(original);
|
||||||
|
|
||||||
|
std::size_t expectedPaletteEntries = 0;
|
||||||
|
for (const SaveChunkRecord& record : original.chunks)
|
||||||
|
expectedPaletteEntries += distinctBlocks(record.blocks);
|
||||||
|
|
||||||
|
SaveMigrator migrator;
|
||||||
|
MigrationResult result = migrator.migrateToCurrent(legacy);
|
||||||
|
CHECK(result.ok);
|
||||||
|
CHECK_EQ(result.paletteEntriesCreated, expectedPaletteEntries);
|
||||||
|
CHECK_EQ(result.lightCellsInitialized, static_cast<std::size_t>(3 * chunkVolume));
|
||||||
|
CHECK_EQ(result.metadataRecordsCreated, static_cast<std::size_t>(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(migratorHonoursItsConfiguredDefaults) {
|
||||||
|
SaveContainer original = buildContainer(SaveVersion::V2, 2);
|
||||||
|
ByteBuffer buffer = writeSaveContainer(original);
|
||||||
|
|
||||||
|
SaveMigrator migrator;
|
||||||
|
migrator.skyLightForNewPlanes = 7;
|
||||||
|
migrator.metadataDefaults.inhabitedTicks = 1234;
|
||||||
|
migrator.metadataDefaults.lastSavedTick = 9999;
|
||||||
|
migrator.metadataDefaults.generationStage = 1;
|
||||||
|
|
||||||
|
MigrationResult result = migrator.migrateToCurrent(buffer);
|
||||||
|
CHECK(result.ok);
|
||||||
|
SaveContainer lifted;
|
||||||
|
CHECK(readSaveContainer(result.output.data(), result.output.size(), lifted));
|
||||||
|
for (const SaveChunkRecord& record : lifted.chunks) {
|
||||||
|
CHECK(record.light == defaultChunkLight(record.blocks, 7));
|
||||||
|
CHECK_EQ(record.meta.inhabitedTicks, static_cast<uint64_t>(1234));
|
||||||
|
CHECK_EQ(record.meta.lastSavedTick, static_cast<uint64_t>(9999));
|
||||||
|
CHECK_EQ(record.meta.generationStage, static_cast<uint8_t>(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(migratorCanCompressTheFinalHopAndStillReadItBack) {
|
||||||
|
SaveContainer original = buildContainer(SaveVersion::V1, 3);
|
||||||
|
ByteBuffer legacy = writeSaveContainer(original);
|
||||||
|
|
||||||
|
SaveMigrator plain;
|
||||||
|
MigrationResult loose = plain.migrateToCurrent(legacy);
|
||||||
|
CHECK(loose.ok);
|
||||||
|
|
||||||
|
SaveMigrator packing;
|
||||||
|
packing.compressMigratedPayloads = true;
|
||||||
|
MigrationResult tight = packing.migrateToCurrent(legacy);
|
||||||
|
CHECK(tight.ok);
|
||||||
|
CHECK(tight.output.size() < loose.output.size());
|
||||||
|
|
||||||
|
SaveContainer reloaded;
|
||||||
|
CHECK(readSaveContainer(tight.output.data(), tight.output.size(), reloaded));
|
||||||
|
CHECK(reloaded.compressPayloads);
|
||||||
|
for (const SaveChunkRecord& before : original.chunks) {
|
||||||
|
const SaveChunkRecord* after = reloaded.find(before.coord);
|
||||||
|
CHECK(after != nullptr);
|
||||||
|
if (after != nullptr) CHECK(sameBlocks(before.blocks, after->blocks));
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveScanner scanner;
|
||||||
|
CHECK(scanner.scan(tight.output).clean());
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(migrationRefusesAHeaderItCannotIdentify) {
|
||||||
|
SaveContainer original = buildContainer(SaveVersion::V1, 2);
|
||||||
|
ByteBuffer buffer = writeSaveContainer(original);
|
||||||
|
saveWriteU32At(buffer.data(), buffer.size(), 4, 7u);
|
||||||
|
|
||||||
|
SaveMigrator migrator;
|
||||||
|
MigrationResult result = migrator.migrateToCurrent(buffer);
|
||||||
|
CHECK(!result.ok);
|
||||||
|
CHECK(result.output.empty());
|
||||||
|
CHECK(result.failure.find("header") != std::string::npos);
|
||||||
|
|
||||||
|
ByteBuffer stump(20, 0);
|
||||||
|
MigrationResult tiny = migrator.migrateToCurrent(stump);
|
||||||
|
CHECK(!tiny.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(peekVersionReadsTheHeaderWithoutTheBody) {
|
||||||
|
SaveContainer original = buildContainer(SaveVersion::V3, 2);
|
||||||
|
ByteBuffer buffer = writeSaveContainer(original);
|
||||||
|
ByteBuffer headerOnly(buffer.begin(), buffer.begin() + saveHeaderBytes);
|
||||||
|
|
||||||
Reference in new issue
Block a user