add tests for blueprint serialization

This commit is contained in:
2026-04-28 21:38:20 +02:00
parent ced4ab5fe3
commit 1e6d838258
2 changed files with 188 additions and 0 deletions

View File

@@ -0,0 +1,187 @@
#include "catch.hpp"
#include <stdexcept>
#include <vector>
#include <QPoint>
#include <QString>
#include "Blueprint.h"
#include "BlueprintSerializer.h"
#include "BuildingType.h"
#include "Rotation.h"
namespace
{
Blueprint makeBlueprintWith(const std::vector<BlueprintBuilding>& buildings,
const QString& name = "Test")
{
Blueprint bp;
bp.name = name;
bp.buildings = buildings;
return bp;
}
BlueprintBuilding makeBuilding(BuildingType type,
Rotation rotation,
QPoint offset,
std::string recipeId = "")
{
BlueprintBuilding b;
b.type = type;
b.rotation = rotation;
b.offset = offset;
b.recipeId = std::move(recipeId);
return b;
}
} // namespace
// ---------------------------------------------------------------------------
// Case 1 — single blueprint, one building: all fields survive round-trip
// ---------------------------------------------------------------------------
TEST_CASE("BlueprintSerializer: single blueprint round-trips all fields", "[serializer]")
{
const BlueprintBuilding building =
makeBuilding(BuildingType::Miner, Rotation::East, QPoint(2, -1), "mine_iron_ore");
const Blueprint original = makeBlueprintWith({building}, "My Miner");
const std::string toml = BlueprintSerializer::serialize({original});
const std::vector<Blueprint> loaded = BlueprintSerializer::deserialize(toml);
REQUIRE(loaded.size() == 1);
REQUIRE(loaded[0].name == "My Miner");
REQUIRE(loaded[0].buildings.size() == 1);
const BlueprintBuilding& b = loaded[0].buildings[0];
REQUIRE(b.type == BuildingType::Miner);
REQUIRE(b.rotation == Rotation::East);
REQUIRE(b.offset == QPoint(2, -1));
REQUIRE(b.recipeId == "mine_iron_ore");
}
// ---------------------------------------------------------------------------
// Case 2 — multiple blueprints: count and order preserved
// ---------------------------------------------------------------------------
TEST_CASE("BlueprintSerializer: multiple blueprints preserve count and order", "[serializer]")
{
const Blueprint first = makeBlueprintWith(
{makeBuilding(BuildingType::Belt, Rotation::North, QPoint(0, 0))}, "First");
const Blueprint second = makeBlueprintWith(
{makeBuilding(BuildingType::Smelter, Rotation::South, QPoint(1, 0))}, "Second");
const Blueprint third = makeBlueprintWith(
{makeBuilding(BuildingType::Assembler, Rotation::West, QPoint(0, 1))}, "Third");
const std::string toml = BlueprintSerializer::serialize({first, second, third});
const std::vector<Blueprint> loaded = BlueprintSerializer::deserialize(toml);
REQUIRE(loaded.size() == 3);
REQUIRE(loaded[0].name == "First");
REQUIRE(loaded[1].name == "Second");
REQUIRE(loaded[2].name == "Third");
}
// ---------------------------------------------------------------------------
// Case 3 — all four Rotation values round-trip
// ---------------------------------------------------------------------------
TEST_CASE("BlueprintSerializer: all rotations round-trip", "[serializer]")
{
const std::vector<Rotation> rotations = {
Rotation::North, Rotation::East, Rotation::South, Rotation::West
};
for (const Rotation rot : rotations)
{
const Blueprint bp = makeBlueprintWith(
{makeBuilding(BuildingType::Belt, rot, QPoint(0, 0))});
const std::string toml = BlueprintSerializer::serialize({bp});
const std::vector<Blueprint> loaded = BlueprintSerializer::deserialize(toml);
REQUIRE(loaded.size() == 1);
REQUIRE(loaded[0].buildings.size() == 1);
REQUIRE(loaded[0].buildings[0].rotation == rot);
}
}
// ---------------------------------------------------------------------------
// Case 5 — negative and zero offsets survive intact
// ---------------------------------------------------------------------------
TEST_CASE("BlueprintSerializer: negative and zero offsets round-trip", "[serializer]")
{
const Blueprint bp = makeBlueprintWith({
makeBuilding(BuildingType::Belt, Rotation::North, QPoint(0, 0)),
makeBuilding(BuildingType::Belt, Rotation::North, QPoint(-3, -2)),
makeBuilding(BuildingType::Belt, Rotation::North, QPoint(5, 4)),
});
const std::string toml = BlueprintSerializer::serialize({bp});
const std::vector<Blueprint> loaded = BlueprintSerializer::deserialize(toml);
REQUIRE(loaded[0].buildings[0].offset == QPoint(0, 0));
REQUIRE(loaded[0].buildings[1].offset == QPoint(-3, -2));
REQUIRE(loaded[0].buildings[2].offset == QPoint(5, 4));
}
// ---------------------------------------------------------------------------
// Case 6 — empty and non-empty recipeId both round-trip correctly
// ---------------------------------------------------------------------------
TEST_CASE("BlueprintSerializer: empty and non-empty recipeId round-trip", "[serializer]")
{
const Blueprint bp = makeBlueprintWith({
makeBuilding(BuildingType::Miner, Rotation::North, QPoint(0, 0), "mine_iron_ore"),
makeBuilding(BuildingType::Assembler, Rotation::North, QPoint(3, 0), ""),
});
const std::string toml = BlueprintSerializer::serialize({bp});
const std::vector<Blueprint> loaded = BlueprintSerializer::deserialize(toml);
REQUIRE(loaded[0].buildings[0].recipeId == "mine_iron_ore");
REQUIRE(loaded[0].buildings[1].recipeId == "");
}
// ---------------------------------------------------------------------------
// Case 8 — empty blueprint list round-trips to empty vector
// ---------------------------------------------------------------------------
TEST_CASE("BlueprintSerializer: empty list round-trips to empty vector", "[serializer]")
{
const std::string toml = BlueprintSerializer::serialize({});
const std::vector<Blueprint> loaded = BlueprintSerializer::deserialize(toml);
REQUIRE(loaded.empty());
}
// ---------------------------------------------------------------------------
// Case 9 — malformed TOML throws std::runtime_error
// ---------------------------------------------------------------------------
TEST_CASE("BlueprintSerializer: malformed TOML throws", "[serializer]")
{
REQUIRE_THROWS_AS(
BlueprintSerializer::deserialize("[[blueprint\nname = 42"),
std::runtime_error);
}
// ---------------------------------------------------------------------------
// Case 10 — unknown building type string throws std::runtime_error
// ---------------------------------------------------------------------------
TEST_CASE("BlueprintSerializer: unknown building type throws", "[serializer]")
{
const std::string badToml =
"[[blueprint]]\n"
"name = \"Broken\"\n"
"buildings = [{type = \"unicorn\", rotation = \"north\","
" offset_x = 0, offset_y = 0, recipe_id = \"\"}]\n";
REQUIRE_THROWS_AS(
BlueprintSerializer::deserialize(badToml),
std::runtime_error);
}

View File

@@ -16,4 +16,5 @@ add_files(
CombatSystemTest.cpp CombatSystemTest.cpp
ShipyardTest.cpp ShipyardTest.cpp
BlueprintTest.cpp BlueprintTest.cpp
BlueprintSerializerTest.cpp
) )