From dcba0c8da0432bdbde6e9e1261350eec8cf3ecd0 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Sat, 12 Sep 2026 22:15:01 +0200 Subject: [PATCH] test: extend coverage of testSchematic --- tests/testSchematic.cpp | 179 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/tests/testSchematic.cpp b/tests/testSchematic.cpp index 408ca8c..f6b5b42 100644 --- a/tests/testSchematic.cpp +++ b/tests/testSchematic.cpp @@ -315,3 +315,182 @@ VF_TEST(aTruncatedBufferIsRejectedAtEveryCutPoint) { Schematic::capture(source, Selection::box({0, 0, 0}, {4, 4, 4}), "cut", "f", 1u).encode(); CHECK(bytes.size() > 40u); + Schematic loaded; + for (std::size_t keep : {std::size_t(0), std::size_t(3), std::size_t(11), bytes.size() - 5u, + bytes.size() - 1u}) { + ByteBuffer cut(bytes.begin(), bytes.begin() + static_cast(keep)); + SchematicError error = Schematic::decode(cut.data(), cut.size(), loaded); + CHECK(error == SchematicError::Truncated); + } + + // A body length claiming more bytes than the buffer holds is truncation too. + ByteBuffer lying = bytes; + writeU32(lying, 8, readU32(lying, 8) + 64u); + CHECK(Schematic::decode(lying.data(), lying.size(), loaded) == SchematicError::Truncated); +} + +VF_TEST(aFlippedByteInTheBodyIsCaughtByTheChecksum) { + World source = terrainWorld(5, 5); + ByteBuffer bytes = + Schematic::capture(source, Selection::box({0, 0, 0}, {4, 4, 4}), "bit", "f", 1u).encode(); + + Schematic loaded; + for (std::size_t offset : {bodyOffset, bodyOffset + 20u, bytes.size() - 6u}) { + ByteBuffer damaged = bytes; + damaged[offset] = static_cast(damaged[offset] ^ 0x40u); + CHECK(Schematic::decode(damaged.data(), damaged.size(), loaded) == + SchematicError::BadChecksum); + } + + ByteBuffer wrongChecksum = bytes; + writeU32(wrongChecksum, wrongChecksum.size() - 4u, 0xdeadbeefu); + CHECK(Schematic::decode(wrongChecksum.data(), wrongChecksum.size(), loaded) == + SchematicError::BadChecksum); +} + +VF_TEST(dimensionsThatDoNotMatchThePayloadAreRejected) { + World source = terrainWorld(5, 5); + ByteBuffer bytes = + Schematic::capture(source, Selection::box({0, 0, 0}, {4, 4, 4}), "dim", "f", 1u).encode(); + + Schematic control; + CHECK(Schematic::decode(bytes.data(), bytes.size(), control) == SchematicError::None); + CHECK(control.header().dims == Vec3i(5, 5, 5)); + + ByteBuffer widened = bytes; + patchBody(widened, 3u, 6u); + Schematic loaded; + CHECK(Schematic::decode(widened.data(), widened.size(), loaded) == + SchematicError::DimensionMismatch); + + ByteBuffer negative = bytes; + negative[bodyOffset] = 0xffu; + refreshChecksum(negative); + CHECK(Schematic::decode(negative.data(), negative.size(), loaded) == + SchematicError::DimensionMismatch); +} + +VF_TEST(aPaletteThatDoesNotDescribeRealBlocksIsRejected) { + World source = terrainWorld(5, 5); + ByteBuffer bytes = + Schematic::capture(source, Selection::box({0, 0, 0}, {4, 4, 4}), "pal", "f", 1u).encode(); + + Schematic loaded; + ByteBuffer unknownBlock = bytes; + patchBody(unknownBlock, paletteOffsetInBody, 250u); + CHECK(Schematic::decode(unknownBlock.data(), unknownBlock.size(), loaded) == + SchematicError::BadPalette); + + ByteBuffer emptyPalette = bytes; + patchBody(emptyPalette, 25u, 0u); + CHECK(Schematic::decode(emptyPalette.data(), emptyPalette.size(), loaded) == + SchematicError::BadPalette); +} + +VF_TEST(pasteModesDecideWhichExistingBlocksSurvive) { + ClipboardBuffer stamp({3, 1, 3}, {0, 0, 0}); + for (int z = 0; z < 3; ++z) + for (int x = 0; x < 3; ++x) stamp.put(x, 0, z, Block::Planks); + Schematic schematic = Schematic::fromBuffer(stamp, "floor", "foksiu", 2u); + + World overwrite; + for (int z = 0; z < 3; ++z) + for (int x = 0; x < 3; ++x) overwrite.setBlock(x, 0, z, Block::Stone); + CHECK_EQ(schematic.pasteInto(overwrite, {0, 0, 0}, ClipboardBuffer::PasteMode::Overwrite), 9u); + CHECK(overwrite.blockAt(1, 0, 1) == Block::Planks); + + World intoAir; + for (int z = 0; z < 3; ++z) + for (int x = 0; x < 3; ++x) intoAir.setBlock(x, 0, z, Block::Stone); + CHECK_EQ(schematic.pasteInto(intoAir, {0, 0, 0}, ClipboardBuffer::PasteMode::IntoAirOnly), 0u); + CHECK(intoAir.blockAt(1, 0, 1) == Block::Stone); + CHECK_EQ(schematic.pasteInto(intoAir, {0, 1, 0}, ClipboardBuffer::PasteMode::IntoAirOnly), 9u); + CHECK(intoAir.blockAt(1, 1, 1) == Block::Planks); + + World replaceOnly; + replaceOnly.setBlock(0, 0, 0, Block::Stone); + replaceOnly.setBlock(2, 0, 2, Block::Stone); + CHECK_EQ(schematic.pasteInto(replaceOnly, {0, 0, 0}, + ClipboardBuffer::PasteMode::ReplaceExisting), + 2u); + CHECK(replaceOnly.blockAt(0, 0, 0) == Block::Planks); + CHECK(replaceOnly.blockAt(1, 0, 1) == Block::Air); +} + +VF_TEST(theAnchorDecidesWhereAPastedSchematicLands) { + ClipboardBuffer stamp({3, 3, 3}, {1, 1, 1}); + for (int y = 0; y < 3; ++y) + for (int z = 0; z < 3; ++z) + for (int x = 0; x < 3; ++x) stamp.put(x, y, z, Block::Sandstone); + Schematic schematic = Schematic::fromBuffer(stamp, "cube", "foksiu", 4u); + + World world; + schematic.pasteInto(world, {50, 50, 50}, ClipboardBuffer::PasteMode::Overwrite); + CHECK(world.blockAt(50, 50, 50) == Block::Sandstone); + CHECK(world.blockAt(49, 49, 49) == Block::Sandstone); + CHECK(world.blockAt(51, 51, 51) == Block::Sandstone); + CHECK(world.blockAt(52, 50, 50) == Block::Air); + CHECK(world.blockAt(48, 50, 50) == Block::Air); +} + +VF_TEST(pastingThroughTheHistoryIsOneUndoableBatch) { + World world = terrainWorld(6, 6); + Selection region = Selection::box({0, 0, 0}, {5, 4, 5}); + Schematic saved = Schematic::capture(world, region, "copy", "foksiu", 1u); + + EditHistory history(1u << 20); + std::size_t before = 0; + for (int y = 0; y <= 4; ++y) + for (int z = 0; z <= 5; ++z) + for (int x = 0; x <= 5; ++x) + if (!isAir(world.blockAt(x, y, z))) ++before; + + std::size_t written = saved.pasteInto(world, history, {20, 0, 20}, + ClipboardBuffer::PasteMode::Overwrite, 5u); + CHECK(written > 0u); + CHECK_EQ(history.stats().batchCount, 1u); + CHECK(world.blockAt(20, 0, 20) == world.blockAt(0, 0, 0)); + + CHECK(history.undo(world)); + for (int y = 0; y <= 4; ++y) + for (int z = 0; z <= 5; ++z) + for (int x = 0; x <= 5; ++x) CHECK(isAir(world.blockAt(20 + x, y, 20 + z))); + + std::size_t after = 0; + for (int y = 0; y <= 4; ++y) + for (int z = 0; z <= 5; ++z) + for (int x = 0; x <= 5; ++x) + if (!isAir(world.blockAt(x, y, z))) ++after; + CHECK_EQ(after, before); +} + +VF_TEST(anEmptySchematicSurvivesTheRoundTrip) { + Schematic nothing = Schematic::fromBuffer(ClipboardBuffer{}, "void", "foksiu", 9u); + CHECK(nothing.header().dims == Vec3i(0, 0, 0)); + + ByteBuffer bytes = nothing.encode(); + Schematic loaded; + CHECK(Schematic::decode(bytes.data(), bytes.size(), loaded) == SchematicError::None); + CHECK(loaded.buffer().empty()); + CHECK(loaded.header().name == std::string("void")); + + World world; + CHECK_EQ(loaded.pasteInto(world, {0, 0, 0}, ClipboardBuffer::PasteMode::Overwrite), 0u); + CHECK_EQ(world.chunkCount(), 0u); +} + +VF_TEST(reEncodingADecodedSchematicProducesTheSameBytes) { + World source = terrainWorld(7, 7); + Schematic saved = Schematic::capture(source, Selection::sphere({3, 3, 3}, 3), "stable", + "foksiu", 42u); + ByteBuffer first = saved.encode(); + + Schematic loaded; + CHECK(Schematic::decode(first.data(), first.size(), loaded) == SchematicError::None); + ByteBuffer second = loaded.encode(); + CHECK(first == second); + + CHECK(std::string(Schematic::errorText(SchematicError::None)) == std::string("ok")); + CHECK(std::string(Schematic::errorText(SchematicError::BadPalette)) != + std::string(Schematic::errorText(SchematicError::BadChecksum))); +}