test: cover testSaveMigration

This commit is contained in:
dualfroz committed 2026-09-12 20:38:50 +02:00
1 parent f58da5b2f8
commit 278feeeb65
1 file changed
+157
+157
View File
@@ -156,3 +156,160 @@ VF_TEST(migrationAppliesOneStepPerVersionHop) {
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);