#include "catch.hpp" #include "Building.h" #include "BuildingConfig.h" #include "BuildingSystem.h" #include "BuildingType.h" #include "ConfigLoader.h" #include "GameConfig.h" #include "Rotation.h" #include "ShipLayout.h" #include "ShipsConfig.h" #include "Simulation.h" #include "SimulationTestAccess.h" // readBuildingConfig underpins the copy-settings gesture (REQ-BLD-COPY-CONFIG): // it extracts a building's recipe / schematic / layout / splitter filters so they // can be stamped onto a same-type building. It reads operational buildings and // construction sites alike. namespace { GameConfig loadConfig() { return ConfigLoader::loadFromDirectory(CONFIG_DIR); } const BuildingDef* findDef(const GameConfig& cfg, BuildingType type) { for (const BuildingDef& def : cfg.buildings.buildings) { if (def.type == type) { return &def; } } return nullptr; } BuildingId placeOperational(Simulation& sim, const GameConfig& cfg, BuildingType type, QPoint anchor) { const BuildingDef* def = findDef(cfg, type); REQUIRE(def != nullptr); return SimulationTestAccess::buildings(sim).placeImmediate( type, def->surfaceMask, anchor, Rotation::East); } const ShipDef* findAvailableSchematic(const GameConfig& cfg) { for (const ShipDef& def : cfg.ships.ships) { if (def.unlockAtStationLevel == -1) { return &def; } } return nullptr; } } // namespace TEST_CASE("readBuildingConfig returns a miner's selected recipe", "[copyconfig]") { const GameConfig cfg = loadConfig(); Simulation sim(loadConfig(), 7); const BuildingId id = placeOperational(sim, cfg, BuildingType::Miner, QPoint(0, 0)); SimulationTestAccess::buildings(sim).setRecipe(id, "mine_iron_ore"); const std::optional config = readBuildingConfig(sim, id); REQUIRE(config.has_value()); CHECK(config->type == BuildingType::Miner); REQUIRE(config->recipeId.has_value()); CHECK(*config->recipeId == "mine_iron_ore"); CHECK_FALSE(config->isSplitter); CHECK_FALSE(config->shipLayout.has_value()); } TEST_CASE("readBuildingConfig leaves recipe unset when nothing is selected", "[copyconfig]") { const GameConfig cfg = loadConfig(); Simulation sim(loadConfig(), 7); const BuildingId id = placeOperational(sim, cfg, BuildingType::Assembler, QPoint(0, 0)); const std::optional config = readBuildingConfig(sim, id); REQUIRE(config.has_value()); CHECK(config->type == BuildingType::Assembler); CHECK_FALSE(config->recipeId.has_value()); // nothing to copy CHECK_FALSE(config->isSplitter); } TEST_CASE("readBuildingConfig returns a shipyard's schematic and layout", "[copyconfig]") { const GameConfig cfg = loadConfig(); Simulation sim(loadConfig(), 7); const ShipDef* schematic = findAvailableSchematic(cfg); REQUIRE(schematic != nullptr); const BuildingId id = placeOperational(sim, cfg, BuildingType::Shipyard, QPoint(0, 0)); SimulationTestAccess::buildings(sim).setRecipe(id, schematic->id); SimulationTestAccess::buildings(sim).setShipLayout(id, ShipLayoutConfig{}); const std::optional config = readBuildingConfig(sim, id); REQUIRE(config.has_value()); CHECK(config->type == BuildingType::Shipyard); REQUIRE(config->recipeId.has_value()); CHECK(*config->recipeId == schematic->id); CHECK(config->shipLayout.has_value()); } TEST_CASE("readBuildingConfig reads a queued construction site", "[copyconfig]") { const GameConfig cfg = loadConfig(); Simulation sim(loadConfig(), 7); // A placed miner enters the construction queue as a site (not yet operational). const BuildingId id = SimulationTestAccess::place(sim, BuildingType::Miner, QPoint(-2, 0), Rotation::East); REQUIRE(id != kInvalidBuildingId); REQUIRE(sim.buildings().findBuilding(id) == nullptr); REQUIRE(sim.buildings().findSite(id) != nullptr); SimulationTestAccess::buildings(sim).setRecipe(id, "mine_iron_ore"); const std::optional config = readBuildingConfig(sim, id); REQUIRE(config.has_value()); CHECK(config->type == BuildingType::Miner); REQUIRE(config->recipeId.has_value()); CHECK(*config->recipeId == "mine_iron_ore"); } TEST_CASE("readBuildingConfig returns nullopt for an unknown id", "[copyconfig]") { Simulation sim(loadConfig(), 7); CHECK_FALSE(readBuildingConfig(sim, kInvalidBuildingId).has_value()); }