From 1150985c1f097f0424899c8f7a70009d13aab640 Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Mon, 3 Aug 2026 21:05:28 +0200 Subject: [PATCH] share one loadTestConfig() helper across the tests --- src/test/ArtifactWinConditionTest.cpp | 28 +++++----- src/test/BehaviorSystemTest.cpp | 8 +-- src/test/BlueprintTest.cpp | 54 +++++++++---------- src/test/BuildingConfigTest.cpp | 24 ++++----- src/test/BuildingTest.cpp | 74 +++++++++++++-------------- src/test/CMakeLists.txt | 2 + src/test/CombatSystemTest.cpp | 20 +++----- src/test/CommandTest.cpp | 29 ++++------- src/test/DeterminismTest.cpp | 12 ++--- src/test/ModuleConfigTest.cpp | 20 +++----- src/test/RecipeSchematicTest.cpp | 52 +++++++++---------- src/test/ReplayPlaybackTest.cpp | 18 +++---- src/test/ReplayRecorderTest.cpp | 7 +-- src/test/ShipModuleTest.cpp | 40 +++++++-------- src/test/ShipTest.cpp | 30 +++++------ src/test/ShipyardTest.cpp | 14 ++--- src/test/SimulationTest.cpp | 18 +++---- src/test/TestConfig.h | 15 ++++++ src/test/ThreatCostCalculatorTest.cpp | 30 +++++------ src/test/UnlockGroupTest.cpp | 8 +-- src/test/UnlockPrereqTest.cpp | 12 ++--- src/test/WaveSystemTest.cpp | 40 +++++++-------- 22 files changed, 247 insertions(+), 308 deletions(-) create mode 100644 src/test/TestConfig.h diff --git a/src/test/ArtifactWinConditionTest.cpp b/src/test/ArtifactWinConditionTest.cpp index 767c0b1..fe297c5 100644 --- a/src/test/ArtifactWinConditionTest.cpp +++ b/src/test/ArtifactWinConditionTest.cpp @@ -11,11 +11,7 @@ #include "Simulation.h" #include "SimulationTestAccess.h" #include "StationBodyComponent.h" - -static GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} +#include "TestConfig.h" static void killEnemyStations(Simulation& sim) { @@ -51,7 +47,7 @@ static int findArtifactChoiceIndex(const Simulation& sim) TEST_CASE("ArtifactWinCondition: artifact_chance_formula and artifact_win_count are loaded", "[artifact_win]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); CHECK(cfg.world.artifacts.artifactWinCount == 3); // 0.05 * x at x=2 should be 0.1 CHECK(cfg.world.artifacts.artifactChanceFormula.evaluate(2.0) == Approx(0.1)); @@ -64,7 +60,7 @@ TEST_CASE("ArtifactWinCondition: artifact_chance_formula and artifact_win_count TEST_CASE("ArtifactWinCondition: artifact count is 0 and isWon is false at game start", "[artifact_win]") { - const Simulation sim(loadConfig()); + const Simulation sim(loadTestConfig()); CHECK(sim.getArtifactCount() == 0); CHECK_FALSE(sim.isWon()); } @@ -76,7 +72,7 @@ TEST_CASE("ArtifactWinCondition: artifact count is 0 and isWon is false at game TEST_CASE("ArtifactWinCondition: artifact option appears when chance formula returns 1", "[artifact_win]") { - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); cfg.world.artifacts.artifactChanceFormula = Formula::compile("1"); Simulation sim(std::move(cfg)); @@ -95,7 +91,7 @@ TEST_CASE("ArtifactWinCondition: artifact option appears when chance formula ret TEST_CASE("ArtifactWinCondition: at most 2 schematic options accompany the artifact", "[artifact_win]") { - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); cfg.world.artifacts.artifactChanceFormula = Formula::compile("1"); Simulation sim(std::move(cfg)); @@ -112,7 +108,7 @@ TEST_CASE("ArtifactWinCondition: at most 2 schematic options accompany the artif TEST_CASE("ArtifactWinCondition: no artifact option when chance formula returns 0", "[artifact_win]") { - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); cfg.world.artifacts.artifactChanceFormula = Formula::compile("0"); Simulation sim(std::move(cfg)); @@ -133,7 +129,7 @@ TEST_CASE("ArtifactWinCondition: no artifact option when chance formula returns TEST_CASE("ArtifactWinCondition: selecting artifact increments artifact count", "[artifact_win]") { - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); cfg.world.artifacts.artifactChanceFormula = Formula::compile("1"); Simulation sim(std::move(cfg)); @@ -151,7 +147,7 @@ TEST_CASE("ArtifactWinCondition: selecting artifact increments artifact count", TEST_CASE("ArtifactWinCondition: selecting a non-artifact option does not increment artifact count", "[artifact_win]") { - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); cfg.world.artifacts.artifactChanceFormula = Formula::compile("1"); Simulation sim(std::move(cfg)); @@ -176,7 +172,7 @@ TEST_CASE("ArtifactWinCondition: selecting a non-artifact option does not increm TEST_CASE("ArtifactWinCondition: isWon becomes true when artifact count reaches win count", "[artifact_win]") { - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); cfg.world.artifacts.artifactChanceFormula = Formula::compile("1"); cfg.world.artifacts.artifactWinCount = 1; Simulation sim(std::move(cfg)); @@ -196,7 +192,7 @@ TEST_CASE("ArtifactWinCondition: isWon becomes true when artifact count reaches TEST_CASE("ArtifactWinCondition: isWon stays false when artifact count is below win count", "[artifact_win]") { - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); cfg.world.artifacts.artifactChanceFormula = Formula::compile("1"); cfg.world.artifacts.artifactWinCount = 2; Simulation sim(std::move(cfg)); @@ -212,7 +208,7 @@ TEST_CASE("ArtifactWinCondition: isWon stays false when artifact count is below TEST_CASE("ArtifactWinCondition: isWon becomes true after collecting required number of artifacts", "[artifact_win]") { - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); cfg.world.artifacts.artifactChanceFormula = Formula::compile("1"); cfg.world.artifacts.artifactWinCount = 2; Simulation sim(std::move(cfg)); @@ -237,7 +233,7 @@ TEST_CASE("ArtifactWinCondition: isWon becomes true after collecting required nu TEST_CASE("ArtifactWinCondition: reset clears artifact count and win state", "[artifact_win]") { - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); cfg.world.artifacts.artifactChanceFormula = Formula::compile("1"); cfg.world.artifacts.artifactWinCount = 1; Simulation sim(std::move(cfg)); diff --git a/src/test/BehaviorSystemTest.cpp b/src/test/BehaviorSystemTest.cpp index 2e3e84f..8e95ca3 100644 --- a/src/test/BehaviorSystemTest.cpp +++ b/src/test/BehaviorSystemTest.cpp @@ -45,16 +45,12 @@ #include "ShipLayout.h" #include "ShipSystem.h" #include "Tick.h" +#include "TestConfig.h" // --------------------------------------------------------------------------- // Fixture // --------------------------------------------------------------------------- -static GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} - struct Fixture { GameConfig cfg; @@ -75,7 +71,7 @@ struct Fixture std::vector beamEvents; explicit Fixture() - : cfg(loadConfig()) + : cfg(loadTestConfig()) , belts(cfg.world.beltSpeed_tps) , nextBuildingId(1) , stock(0) diff --git a/src/test/BlueprintTest.cpp b/src/test/BlueprintTest.cpp index 4c2d9d0..472067f 100644 --- a/src/test/BlueprintTest.cpp +++ b/src/test/BlueprintTest.cpp @@ -20,6 +20,7 @@ #include "SimulationTestAccess.h" #include "SurfaceMask.h" #include "Tick.h" +#include "TestConfig.h" // --------------------------------------------------------------------------- // Helpers that mirror the production implementations under test. @@ -147,11 +148,6 @@ static void applyRotationCCW(Blueprint& bp, const GameConfig& cfg) } } -static GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} - // Mirrors BlueprintPanel::createBlueprintFromSelection's player-placeable filter: // building types absent from buildings.toml (HQ, stations) or with playerPlaceable=false // are silently excluded before the bounding-box center and offsets are computed. @@ -312,7 +308,7 @@ TEST_CASE("Blueprint: non-axis-aligned offset rotates correctly", "[blueprint]") TEST_CASE("Blueprint: CW constellation rotation updates offset and building rotation", "[blueprint]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); // Building one tile to the right, facing East. Blueprint bp; bp.name = "test"; @@ -332,7 +328,7 @@ TEST_CASE("Blueprint: CW constellation rotation updates offset and building rota TEST_CASE("Blueprint: CCW constellation rotation updates offset and building rotation", "[blueprint]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); Blueprint bp; bp.name = "test"; BlueprintBuilding bb; @@ -351,7 +347,7 @@ TEST_CASE("Blueprint: CCW constellation rotation updates offset and building rot TEST_CASE("Blueprint: four CW rotations restore offset and building rotation", "[blueprint]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); Blueprint bp; bp.name = "test"; BlueprintBuilding bb; @@ -371,7 +367,7 @@ TEST_CASE("Blueprint: four CW rotations restore offset and building rotation", " TEST_CASE("Blueprint: multi-building constellation rotates symmetrically CW", "[blueprint]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); // Two buildings left and right of center; after CW they should be above and below. Blueprint bp; bp.name = "test"; @@ -398,7 +394,7 @@ TEST_CASE("Blueprint: multi-building constellation rotates symmetrically CW", "[ TEST_CASE("Blueprint: CW rotation keeps belt adjacent to miner output port", "[blueprint]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); // East miner: anchor (0,0), body cells (0,0),(1,0),(0,1). // Output port indicator '>' at (1,1) → port tile (1,1), direction East. @@ -436,7 +432,7 @@ TEST_CASE("Blueprint: CW rotation keeps belt adjacent to miner output port", "[b TEST_CASE("Blueprint: CCW rotation keeps belt adjacent to miner output port", "[blueprint]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); Blueprint bp; bp.name = "test"; @@ -473,7 +469,7 @@ TEST_CASE("Blueprint: CCW rotation keeps belt adjacent to miner output port", "[ TEST_CASE("Blueprint creation: non-player-placeable building alone yields empty blueprint", "[blueprint]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); // Hq has no entry in buildings.toml, so it is treated as non-player-placeable. const BuildingSpec hq{ QPoint(-5, 0), {QPoint(-5, 0)}, BuildingType::Hq, Rotation::East }; @@ -485,7 +481,7 @@ TEST_CASE("Blueprint creation: non-player-placeable building alone yields empty TEST_CASE("Blueprint creation: mixed selection keeps only player-placeable buildings", "[blueprint]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const BuildingSpec belt{ QPoint(-5, 0), {QPoint(-5, 0)}, BuildingType::Belt, Rotation::East }; const BuildingSpec hq { QPoint(-3, 0), {QPoint(-3, 0)}, BuildingType::Hq, Rotation::East }; @@ -498,7 +494,7 @@ TEST_CASE("Blueprint creation: mixed selection keeps only player-placeable build TEST_CASE("Blueprint creation: bounding box ignores non-player-placeable buildings", "[blueprint]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); // Belt at (-5, 0). HQ at (-3, 0) — excluded from the blueprint. // If HQ were included: bboxX = [-5, -3], center.x = -4, belt offset = -1. @@ -520,7 +516,7 @@ TEST_CASE("Blueprint placement: buildings land at anchor + offset from cursor", // Simulate placing a two-belt blueprint with offsets (-1, 0) and (+1, 0) // at cursor tile (-5, 0). Expected anchors: (-6, 0) and (-4, 0). // (Belt surface_mask ["A>"] — body at relative (0,0), port at (1,0).) - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); const QPoint cursor(-5, 0); const QPoint offsetA(-1, 0); @@ -540,7 +536,7 @@ TEST_CASE("Blueprint placement: buildings land at anchor + offset from cursor", TEST_CASE("Blueprint placement: cost is deducted for each building in sequence", "[blueprint]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); // Find belt cost from config (belt cost = 2 in test config). int beltCost = 0; @@ -563,7 +559,7 @@ TEST_CASE("Blueprint placement: cost is deducted for each building in sequence", TEST_CASE("Blueprint placement: insufficient blocks returns kInvalidBuildingId and deducts nothing", "[blueprint]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); // Find miner cost (15 in test config) — expensive enough to exhaust a small stock. int minerCost = 0; @@ -594,7 +590,7 @@ TEST_CASE("Blueprint placement: insufficient blocks returns kInvalidBuildingId a TEST_CASE("Simulation: tryPlaceBuilding rejects terrain-invalid placement and charges nothing", "[blueprint]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); const int startBlocks = sim.getBuildingBlocksStock(); // A miner is all-asteroid; placing it in space (x >= 0) violates the terrain @@ -610,7 +606,7 @@ TEST_CASE("Simulation: tryPlaceBuilding rejects terrain-invalid placement and ch TEST_CASE("Simulation: tryPlaceBuilding accepts a valid asteroid spot, occupies tiles, charges cost", "[blueprint]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); int minerCost = 0; for (const BuildingDef& def : sim.getConfig().buildings.buildings) @@ -663,7 +659,7 @@ TEST_CASE("Blueprint: building with no recipe has empty recipeId", "[blueprint]" TEST_CASE("Blueprint placement: setRecipe on construction site stores recipe", "[blueprint]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); // Miner body cells: (0,0),(1,0),(0,1) — all at x < 0, valid for asteroid. const BuildingId id = SimulationTestAccess::place(sim,BuildingType::Miner, QPoint(-2, 0), Rotation::East).value(); @@ -679,7 +675,7 @@ TEST_CASE("Blueprint placement: setRecipe on construction site stores recipe", " TEST_CASE("Blueprint placement: recipe transfers to building after construction completes", "[blueprint]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); const BuildingId id = SimulationTestAccess::place(sim,BuildingType::Miner, QPoint(-2, 0), Rotation::East).value(); REQUIRE(id != kInvalidBuildingId); @@ -705,7 +701,7 @@ TEST_CASE("Blueprint placement: recipe transfers to building after construction TEST_CASE("Blueprint creation: a construction site is captured", "[blueprint]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); // Freshly placed → a ConstructionSite (not ticked to completion). A 1x1 belt keeps // the body-cell bounding-box centered on the anchor, so a single site → zero offset. @@ -724,7 +720,7 @@ TEST_CASE("Blueprint creation: a construction site is captured", "[blueprint]") TEST_CASE("Blueprint creation: a construction site's recipe is captured", "[blueprint]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); const BuildingId id = SimulationTestAccess::place(sim, BuildingType::Miner, QPoint(-2, 0), Rotation::East).value(); @@ -740,7 +736,7 @@ TEST_CASE("Blueprint creation: a construction site's recipe is captured", "[blue TEST_CASE("Blueprint creation: mixed operational building and construction site are both captured", "[blueprint]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); // Building A: place, configure, and tick to completion so it is operational. const BuildingId idA = @@ -772,7 +768,7 @@ TEST_CASE("Blueprint creation: mixed operational building and construction site TEST_CASE("Blueprint creation: selectionHasPlaceableBuilding sees a construction site", "[blueprint]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); REQUIRE_FALSE(selectionHasPlaceableBuilding(sim, {})); @@ -787,7 +783,7 @@ TEST_CASE("Blueprint placement: interceptor schematic is unlocked at game start" { // "interceptor" has unlock_at_station_level = -1 in the test config. // This confirms the guard in placeBlueprintAtTile passes for start-unlocked schematics. - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); REQUIRE(sim.isSchematicUnlocked("interceptor")); } @@ -796,7 +792,7 @@ TEST_CASE("Blueprint placement: repair_ship schematic is locked at game start", // "repair_ship" has unlock_at_station_level = 0 in the test config. // This confirms the guard in placeBlueprintAtTile blocks locked schematics, // leaving the shipyard's schematic unset. - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); REQUIRE_FALSE(sim.isSchematicUnlocked("repair_ship")); } @@ -842,7 +838,7 @@ TEST_CASE("Blueprint: building without layout has nullopt shipLayout", "[bluepri TEST_CASE("Blueprint placement: setShipLayout on construction site stores layout", "[blueprint]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); // Shipyard surface_mask ["AAAS>","AAAS "] with Rotation::East: // A-tiles at (-3,0),(-2,0),(-1,0),(-3,1),(-2,1),(-1,1) — all x < 0, valid asteroid tiles. @@ -869,7 +865,7 @@ TEST_CASE("Blueprint placement: setShipLayout on construction site stores layout TEST_CASE("Blueprint placement: ship layout transfers to building after construction completes", "[blueprint]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); const BuildingId id = SimulationTestAccess::place(sim,BuildingType::Shipyard, QPoint(-3, 0), Rotation::East).value(); REQUIRE(id != kInvalidBuildingId); diff --git a/src/test/BuildingConfigTest.cpp b/src/test/BuildingConfigTest.cpp index 366fc34..7a86bb7 100644 --- a/src/test/BuildingConfigTest.cpp +++ b/src/test/BuildingConfigTest.cpp @@ -13,6 +13,7 @@ #include "ShipsConfig.h" #include "Simulation.h" #include "SimulationTestAccess.h" +#include "TestConfig.h" // readBuildingConfig underpins the copy-settings gesture (REQ-BLD-COPY-CONFIG): // it extracts a building's recipe / schematic / layout / splitter filters so they @@ -21,11 +22,6 @@ namespace { -GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} - const BuildingDef* findDef(const GameConfig& cfg, BuildingType type) { for (const BuildingDef& def : cfg.buildings.buildings) @@ -66,8 +62,8 @@ const ShipDef* findAvailableSchematic(const GameConfig& cfg) TEST_CASE("readBuildingConfig returns a miner's selected recipe", "[copyconfig]") { - const GameConfig cfg = loadConfig(); - Simulation sim(loadConfig(), 7); + const GameConfig cfg = loadTestConfig(); + Simulation sim(loadTestConfig(), 7); const BuildingId id = placeOperational(sim, cfg, BuildingType::Miner, QPoint(0, 0)); SimulationTestAccess::buildings(sim).setRecipe(id, "mine_iron_ore"); @@ -84,8 +80,8 @@ TEST_CASE("readBuildingConfig returns a miner's selected recipe", "[copyconfig]" TEST_CASE("readBuildingConfig leaves recipe unset when nothing is selected", "[copyconfig]") { - const GameConfig cfg = loadConfig(); - Simulation sim(loadConfig(), 7); + const GameConfig cfg = loadTestConfig(); + Simulation sim(loadTestConfig(), 7); const BuildingId id = placeOperational(sim, cfg, BuildingType::Assembler, QPoint(0, 0)); @@ -99,8 +95,8 @@ TEST_CASE("readBuildingConfig leaves recipe unset when nothing is selected", TEST_CASE("readBuildingConfig returns a shipyard's schematic and layout", "[copyconfig]") { - const GameConfig cfg = loadConfig(); - Simulation sim(loadConfig(), 7); + const GameConfig cfg = loadTestConfig(); + Simulation sim(loadTestConfig(), 7); const ShipDef* schematic = findAvailableSchematic(cfg); REQUIRE(schematic != nullptr); @@ -119,8 +115,8 @@ TEST_CASE("readBuildingConfig returns a shipyard's schematic and layout", TEST_CASE("readBuildingConfig reads a queued construction site", "[copyconfig]") { - const GameConfig cfg = loadConfig(); - Simulation sim(loadConfig(), 7); + const GameConfig cfg = loadTestConfig(); + Simulation sim(loadTestConfig(), 7); // A placed miner enters the construction queue as a site (not yet operational). const BuildingId id = @@ -140,6 +136,6 @@ TEST_CASE("readBuildingConfig reads a queued construction site", "[copyconfig]") TEST_CASE("readBuildingConfig returns nullopt for an unknown id", "[copyconfig]") { - Simulation sim(loadConfig(), 7); + Simulation sim(loadTestConfig(), 7); CHECK_FALSE(readBuildingConfig(sim, kInvalidBuildingId).has_value()); } diff --git a/src/test/BuildingTest.cpp b/src/test/BuildingTest.cpp index 8ca6df9..b886a5a 100644 --- a/src/test/BuildingTest.cpp +++ b/src/test/BuildingTest.cpp @@ -19,16 +19,12 @@ #include "Port.h" #include "Rotation.h" #include "Tick.h" +#include "TestConfig.h" // --------------------------------------------------------------------------- // Fixture helpers // --------------------------------------------------------------------------- -static GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} - static Item makeItem(const std::string& id) { Item item; @@ -86,7 +82,7 @@ static std::vector outputSideItems(const Building& b) // Owns a BuildingSystem and its dependencies for placement-bounds tests. struct PlacementFixture { - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); BeltSystem belts{cfg.world.beltSpeed_tps}; int stock = 0; std::mt19937 rng{0}; @@ -110,7 +106,7 @@ struct PlacementFixture TEST_CASE("BuildingSystem: place miner occupies expected body tiles", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -218,7 +214,7 @@ TEST_CASE("BuildingSystem: isPlacementValid enforces terrain and world bounds", TEST_CASE("BuildingSystem: placing a belt registers it with BeltSystem after construction", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -247,7 +243,7 @@ TEST_CASE("BuildingSystem: placing a belt registers it with BeltSystem after con TEST_CASE("BuildingSystem: placed building enters construction queue", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -289,7 +285,7 @@ TEST_CASE("BuildingSystem: deconstructing a construction site removes it instant TEST_CASE("BuildingSystem: first queued building starts construction immediately", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -307,7 +303,7 @@ TEST_CASE("BuildingSystem: first queued building starts construction immediately TEST_CASE("BuildingSystem: second queued building waits (completesAt == 0)", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -329,7 +325,7 @@ TEST_CASE("BuildingSystem: second queued building waits (completesAt == 0)", "[b TEST_CASE("BuildingSystem: construction completes after configured duration", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -495,7 +491,7 @@ TEST_CASE("BuildingSystem: splitter filters survive a queue/un-queue round-trip" TEST_CASE("BuildingSystem: second building starts after first completes", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -525,7 +521,7 @@ TEST_CASE("BuildingSystem: second building starts after first completes", "[buil TEST_CASE("BuildingSystem: miner produces iron_ore after recipe duration", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -558,7 +554,7 @@ TEST_CASE("BuildingSystem: miner produces iron_ore after recipe duration", "[bui TEST_CASE("BuildingSystem: miner output buffer stalls when full", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -598,7 +594,7 @@ TEST_CASE("BuildingSystem: miner output buffer stalls when full", "[building]") TEST_CASE("BuildingSystem: productionBuildingCount excludes construction sites", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -638,7 +634,7 @@ TEST_CASE("BuildingSystem: productionBuildingCount excludes construction sites", TEST_CASE("BuildingSystem: activeProductionBuildingCount tracks production cycle state", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -679,7 +675,7 @@ TEST_CASE("BuildingSystem: activeProductionBuildingCount tracks production cycle TEST_CASE("BuildingSystem: smelter input buffer fills from adjacent west-flowing belt", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); // Fast belt so items are immediately available for peek/take. BeltSystem belts(static_cast(kTickRateHz)); int stock = 0; @@ -722,7 +718,7 @@ TEST_CASE("BuildingSystem: smelter input buffer fills from adjacent west-flowing TEST_CASE("BuildingSystem: accepted input travels inward before entering the buffer", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(static_cast(kTickRateHz)); // fast belt: 1 tile/tick int stock = 0; std::mt19937 rng(0); @@ -763,7 +759,7 @@ TEST_CASE("BuildingSystem: accepted input travels inward before entering the buf TEST_CASE("BuildingSystem: input reservation caps buffered plus in-transit at the cap", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(static_cast(kTickRateHz)); // fast belt int stock = 0; std::mt19937 rng(0); @@ -805,7 +801,7 @@ TEST_CASE("BuildingSystem: input reservation caps buffered plus in-transit at th TEST_CASE("BuildingSystem: smelter auto-smelts ore without a recipe selection", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(static_cast(kTickRateHz)); int stock = 0; std::mt19937 rng(0); @@ -851,7 +847,7 @@ TEST_CASE("BuildingSystem: smelter auto-smelts ore without a recipe selection", TEST_CASE("BuildingSystem: smelter runs a satisfiable recipe while an incomplete batch waits", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(static_cast(kTickRateHz)); int stock = 0; std::mt19937 rng(0); @@ -905,7 +901,7 @@ TEST_CASE("BuildingSystem: smelter runs a satisfiable recipe while an incomplete TEST_CASE("BuildingSystem: miner output buffer drains onto adjacent belt", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(static_cast(kTickRateHz)); int stock = 0; std::mt19937 rng(0); @@ -944,7 +940,7 @@ TEST_CASE("BuildingSystem: miner output buffer drains onto adjacent belt", "[bui TEST_CASE("BuildingSystem: output port couples directly into an adjacent input port", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -984,7 +980,7 @@ TEST_CASE("BuildingSystem: output port couples directly into an adjacent input p TEST_CASE("BuildingSystem: direct coupling to a non-consumer leaves the item stuck", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -1023,7 +1019,7 @@ TEST_CASE("BuildingSystem: direct coupling to a non-consumer leaves the item stu TEST_CASE("BuildingSystem: setRecipe clears output buffer and active production", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(static_cast(kTickRateHz)); int stock = 0; std::mt19937 rng(0); @@ -1066,7 +1062,7 @@ TEST_CASE("BuildingSystem: setRecipe clears output buffer and active production" TEST_CASE("BuildingSystem: reprocessing plant output buffer capacity equals max output per roll", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -1097,7 +1093,7 @@ TEST_CASE("BuildingSystem: reprocessing plant output buffer capacity equals max TEST_CASE("BuildingSystem: reprocessing plant produces one cycle output then stalls", "[building]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(static_cast(kTickRateHz)); int stock = 0; // Seed chosen so first roll produces 2-item output (iron_ingot), filling buffer. @@ -1156,7 +1152,7 @@ TEST_CASE("BuildingSystem: reprocessing plant produces one cycle output then sta TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns nullopt when tile is empty", "[building][rotate-in-place]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -1175,7 +1171,7 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns nullopt when tile is TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns the site id for a queued belt (same type, different rotation)", "[building][rotate-in-place]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -1198,7 +1194,7 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns the site id for a que TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns the building id for a completed operational belt", "[building][rotate-in-place]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -1225,7 +1221,7 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns the building id for a TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns nullopt when building type differs", "[building][rotate-in-place]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -1247,7 +1243,7 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns nullopt when building TEST_CASE("BuildingSystem: findRotateInPlaceTarget never rotates a tunnel in place", "[building][rotate-in-place]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -1273,7 +1269,7 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget never rotates a tunnel in pla TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns nullopt when footprints only partially overlap", "[building][rotate-in-place]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -1297,7 +1293,7 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns nullopt when footprin TEST_CASE("BuildingSystem: findRotateInPlaceTarget works for a symmetric multi-tile building with rotated ghost", "[building][rotate-in-place]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -1326,7 +1322,7 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget works for a symmetric multi-t TEST_CASE("BuildingSystem: rotateInPlace updates the rotation field of a construction site", "[building][rotate-in-place]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -1349,7 +1345,7 @@ TEST_CASE("BuildingSystem: rotateInPlace updates the rotation field of a constru TEST_CASE("BuildingSystem: rotateInPlace preserves the construction progress of a queued site", "[building][rotate-in-place]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -1373,7 +1369,7 @@ TEST_CASE("BuildingSystem: rotateInPlace preserves the construction progress of TEST_CASE("BuildingSystem: rotateInPlace updates rotation and output port direction on an operational building", "[building][rotate-in-place]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); @@ -1404,7 +1400,7 @@ TEST_CASE("BuildingSystem: rotateInPlace updates rotation and output port direct TEST_CASE("BuildingSystem: rotateInPlace re-registers a belt tile with BeltSystem so it still accepts items", "[building][rotate-in-place]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); BeltSystem belts(cfg.world.beltSpeed_tps); int stock = 0; std::mt19937 rng(0); diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 3f88e32..4887c04 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -2,6 +2,8 @@ add_files( TEST_FILES + TestConfig.h + test.cpp FormulaTest.cpp ConfigLoaderTest.cpp diff --git a/src/test/CombatSystemTest.cpp b/src/test/CombatSystemTest.cpp index be33464..7afccd8 100644 --- a/src/test/CombatSystemTest.cpp +++ b/src/test/CombatSystemTest.cpp @@ -22,11 +22,7 @@ #include "StationBodyComponent.h" #include "Tick.h" #include "WeaponComponent.h" - -static GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} +#include "TestConfig.h" static const ShipDef* findCombatShip(const GameConfig& cfg) { @@ -64,7 +60,7 @@ struct CombatFixture CombatSystem combat; explicit CombatFixture() - : cfg(loadConfig()) + : cfg(loadTestConfig()) , rng(42) , nextBuildingId(1) , belts(cfg.world.beltSpeed_tps) @@ -178,7 +174,7 @@ TEST_CASE("CombatSystem: no fire when target is out of range", "[combat]") TEST_CASE("CombatSystem: player station fires at enemy ship in range", "[combat]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); // Find the player station entity via ECS. entt::entity stationEntity = entt::null; @@ -217,7 +213,7 @@ TEST_CASE("CombatSystem: player station fires at enemy ship in range", "[combat] TEST_CASE("CombatSystem: enemy station fires at player ship in range", "[combat]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); entt::entity stationEntity = entt::null; QVector2D stationCenter; @@ -255,7 +251,7 @@ TEST_CASE("CombatSystem: enemy station fires at player ship in range", "[combat] TEST_CASE("CombatSystem: player ship fires at enemy station in range", "[combat]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); entt::entity stationEntity = entt::null; QVector2D stationCenter; @@ -388,7 +384,7 @@ TEST_CASE("CombatSystem: damage still applied if shooter already dead", "[combat TEST_CASE("CombatSystem: dead ship is removed after tick step 9", "[combat]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const ShipDef* combatDef = findCombatShip(sim.getConfig()); REQUIRE(combatDef != nullptr); @@ -405,7 +401,7 @@ TEST_CASE("CombatSystem: dead ship is removed after tick step 9", "[combat]") TEST_CASE("CombatSystem: scrap is spawned on ship death", "[combat]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); // Scrap dropped on death is derived from the ship's as-built threat cost // (REQ-RES-DEBRIS-DROP): round(threat * scrap_per_threat). The interceptor's @@ -424,7 +420,7 @@ TEST_CASE("CombatSystem: scrap is spawned on ship death", "[combat]") TEST_CASE("CombatSystem: HQ death sets game over", "[combat]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); // Damage the HQ proxy entity (has HqProxy + Health). sim.getAdmin().forEach( diff --git a/src/test/CommandTest.cpp b/src/test/CommandTest.cpp index 462fada..e1b1bed 100644 --- a/src/test/CommandTest.cpp +++ b/src/test/CommandTest.cpp @@ -11,14 +11,7 @@ #include "Simulation.h" #include "SimulationTestAccess.h" #include "Tick.h" - -namespace -{ -GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} -} // namespace +#include "TestConfig.h" // The command chokepoint (Simulation::apply) must produce exactly the same state // as driving the underlying mutators directly — that equivalence is what lets a @@ -26,8 +19,8 @@ GameConfig loadConfig() TEST_CASE("apply(PlaceBuildingCommand) matches direct placement", "[command]") { - Simulation viaCommand(loadConfig(), 99); - Simulation viaDirect(loadConfig(), 99); + Simulation viaCommand(loadTestConfig(), 99); + Simulation viaDirect(loadTestConfig(), 99); PlaceBuildingCommand command; command.type = BuildingType::Miner; @@ -42,8 +35,8 @@ TEST_CASE("apply(PlaceBuildingCommand) matches direct placement", "[command]") TEST_CASE("apply(PlaceBuildingCommand) with recipe matches place-then-setRecipe", "[command]") { - Simulation viaCommand(loadConfig(), 99); - Simulation viaDirect(loadConfig(), 99); + Simulation viaCommand(loadTestConfig(), 99); + Simulation viaDirect(loadTestConfig(), 99); PlaceBuildingCommand command; command.type = BuildingType::Miner; @@ -61,8 +54,8 @@ TEST_CASE("apply(PlaceBuildingCommand) with recipe matches place-then-setRecipe" TEST_CASE("apply(DeconstructCommand) matches direct deconstruct", "[command]") { - Simulation viaCommand(loadConfig(), 99); - Simulation viaDirect(loadConfig(), 99); + Simulation viaCommand(loadTestConfig(), 99); + Simulation viaDirect(loadTestConfig(), 99); const BuildingId idA = SimulationTestAccess::place(viaCommand, BuildingType::Miner, QPoint(-3, 0), Rotation::East).value(); @@ -81,8 +74,8 @@ TEST_CASE("apply(DeconstructCommand) matches direct deconstruct", "[command]") TEST_CASE("apply(CancelDeconstructionCommand) matches direct cancelDeconstruction", "[command]") { - Simulation viaCommand(loadConfig(), 99); - Simulation viaDirect(loadConfig(), 99); + Simulation viaCommand(loadTestConfig(), 99); + Simulation viaDirect(loadTestConfig(), 99); const BuildingId idA = SimulationTestAccess::place(viaCommand, BuildingType::Miner, QPoint(-3, 0), Rotation::East).value(); @@ -110,8 +103,8 @@ TEST_CASE("apply(CancelDeconstructionCommand) matches direct cancelDeconstructio TEST_CASE("CommandManager drains queued commands in FIFO order through apply", "[command]") { - Simulation viaManager(loadConfig(), 99); - Simulation viaDirect(loadConfig(), 99); + Simulation viaManager(loadTestConfig(), 99); + Simulation viaDirect(loadTestConfig(), 99); CommandManager manager(viaManager); diff --git a/src/test/DeterminismTest.cpp b/src/test/DeterminismTest.cpp index faa1e42..80d2858 100644 --- a/src/test/DeterminismTest.cpp +++ b/src/test/DeterminismTest.cpp @@ -11,14 +11,10 @@ #include "SimulationTestAccess.h" #include "StateChecksum.h" #include "Tick.h" +#include "TestConfig.h" namespace { -GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} - constexpr int kScriptTicks = 2000; // Runs a fixed scripted session and returns the full-state checksum after every @@ -26,7 +22,7 @@ constexpr int kScriptTicks = 2000; // otherwise lets waves/combat run so the RNG stream and ECS state are exercised. std::vector runScriptedSession(unsigned int seed) { - Simulation sim(loadConfig(), seed); + Simulation sim(loadTestConfig(), seed); // Tick 0: a miner feeding a short belt line on the asteroid. SimulationTestAccess::place(sim, BuildingType::Miner, QPoint(-3, 0), Rotation::East); @@ -121,8 +117,8 @@ TEST_CASE("fingerprintRng: equal states match, advanced states differ", "[determ TEST_CASE("Simulation::rngFingerprint is stable for equal seeds", "[determinism]") { - const Simulation a(loadConfig(), 777); - const Simulation b(loadConfig(), 777); + const Simulation a(loadTestConfig(), 777); + const Simulation b(loadTestConfig(), 777); REQUIRE(a.getRngFingerprint() == b.getRngFingerprint()); } diff --git a/src/test/ModuleConfigTest.cpp b/src/test/ModuleConfigTest.cpp index 979891d..7214555 100644 --- a/src/test/ModuleConfigTest.cpp +++ b/src/test/ModuleConfigTest.cpp @@ -2,11 +2,7 @@ #include "ConfigLoader.h" #include "ModulesConfig.h" - -static GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} +#include "TestConfig.h" static const ModuleDef* findModule(const GameConfig& cfg, const std::string& id) { @@ -28,7 +24,7 @@ static const ModuleStatModifier* findModifier(const ModuleDef& def, const std::s TEST_CASE("ConfigLoader: loadModules parses modules.toml", "[config][modules]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); REQUIRE(cfg.modules.modules.size() >= 2); const ModuleDef& armor = cfg.modules.modules[0]; @@ -49,7 +45,7 @@ TEST_CASE("ConfigLoader: loadModules parses modules.toml", "[config][modules]") TEST_CASE("ConfigLoader: loadModules parses additive modifiers", "[config][modules]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); REQUIRE(cfg.modules.modules.size() >= 2); const ModuleDef& sensor = cfg.modules.modules[1]; @@ -62,7 +58,7 @@ TEST_CASE("ConfigLoader: loadModules parses additive modifiers", "[config][modul TEST_CASE("ConfigLoader: multiplicative modifier with unit suffix is parsed (weapon_primer)", "[config][modules]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ModuleDef* primer = findModule(cfg, "weapon_primer"); REQUIRE(primer != nullptr); REQUIRE(primer->statModifiers.size() == 1); @@ -74,7 +70,7 @@ TEST_CASE("ConfigLoader: multiplicative modifier with unit suffix is parsed (wea TEST_CASE("ConfigLoader: weapon_stabilizer parses two multiplicative weapon modifiers", "[config][modules]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ModuleDef* stab = findModule(cfg, "weapon_stabilizer"); REQUIRE(stab != nullptr); REQUIRE(stab->statModifiers.size() == 2); @@ -92,7 +88,7 @@ TEST_CASE("ConfigLoader: weapon_stabilizer parses two multiplicative weapon modi TEST_CASE("ConfigLoader: afterburner parses multiplicative speed and additive main_acceleration", "[config][modules]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ModuleDef* ab = findModule(cfg, "afterburner"); REQUIRE(ab != nullptr); REQUIRE(ab->statModifiers.size() == 2); @@ -110,7 +106,7 @@ TEST_CASE("ConfigLoader: afterburner parses multiplicative speed and additive ma TEST_CASE("ConfigLoader: maneuvering_thrusters parses multiplicative speed and additive maneuvering_acceleration", "[config][modules]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ModuleDef* mt = findModule(cfg, "maneuvering_thrusters"); REQUIRE(mt != nullptr); REQUIRE(mt->statModifiers.size() == 2); @@ -128,7 +124,7 @@ TEST_CASE("ConfigLoader: maneuvering_thrusters parses multiplicative speed and a TEST_CASE("ConfigLoader: loadShips parses layout field", "[config][ships]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); REQUIRE(!cfg.ships.ships.empty()); const ShipDef& ship = cfg.ships.ships[0]; diff --git a/src/test/RecipeSchematicTest.cpp b/src/test/RecipeSchematicTest.cpp index 183aa73..cf15b11 100644 --- a/src/test/RecipeSchematicTest.cpp +++ b/src/test/RecipeSchematicTest.cpp @@ -12,11 +12,7 @@ #include "Simulation.h" #include "SimulationTestAccess.h" #include "StationBodyComponent.h" - -static GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} +#include "TestConfig.h" // Zeros the HP of both enemy defence stations and advances one tick so that // tickDeathsAndLoot fires, triggering the push and schematic choices. @@ -62,7 +58,7 @@ static bool awaitRecipeUnlock(Simulation& sim, const std::string& recipeId, TEST_CASE("RecipeSchematic: unlocked_at_start = true parsed correctly", "[recipe_schematic]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const auto it = std::find_if(cfg.recipes.recipes.begin(), cfg.recipes.recipes.end(), [](const RecipeDef& r) { return r.id == "premium_circuit"; }); REQUIRE(it != cfg.recipes.recipes.end()); @@ -71,7 +67,7 @@ TEST_CASE("RecipeSchematic: unlocked_at_start = true parsed correctly", "[recipe TEST_CASE("RecipeSchematic: a gated recipe is granted by an unlock group", "[recipe_schematic]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const bool granted = std::any_of(cfg.unlocks.groups.begin(), cfg.unlocks.groups.end(), [](const UnlockGroupDef& g) { @@ -82,7 +78,7 @@ TEST_CASE("RecipeSchematic: a gated recipe is granted by an unlock group", "[rec TEST_CASE("RecipeSchematic: an untagged assembler recipe has unlocked_at_start = false", "[recipe_schematic]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const auto it = std::find_if(cfg.recipes.recipes.begin(), cfg.recipes.recipes.end(), [](const RecipeDef& r) { return r.id == "circuit_board"; }); REQUIRE(it != cfg.recipes.recipes.end()); @@ -96,21 +92,21 @@ TEST_CASE("RecipeSchematic: an untagged assembler recipe has unlocked_at_start = TEST_CASE("RecipeSchematic: recipe with unlock_at_station_level = -1 is unlocked at game start", "[recipe_schematic]") { - const Simulation sim(loadConfig()); + const Simulation sim(loadTestConfig()); REQUIRE(sim.isRecipeUnlocked("premium_circuit")); } TEST_CASE("RecipeSchematic: recipe with unlock_at_station_level = 0 is locked at game start", "[recipe_schematic]") { - const Simulation sim(loadConfig()); + const Simulation sim(loadTestConfig()); REQUIRE_FALSE(sim.isRecipeUnlocked("quick_circuit")); } TEST_CASE("RecipeSchematic: recipe with unlock_at_station_level = 1 is locked at game start", "[recipe_schematic]") { - const Simulation sim(loadConfig()); + const Simulation sim(loadTestConfig()); REQUIRE_FALSE(sim.isRecipeUnlocked("advanced_circuit")); } @@ -123,7 +119,7 @@ TEST_CASE("RecipeSchematic: -1 recipe seeds its output item into the implicit un { // premium_circuit is not needed by any ship or module schematic, so it can // only reach the implicit set via the -1 recipe seed in Phase 1. - const Simulation sim(loadConfig()); + const Simulation sim(loadTestConfig()); REQUIRE(sim.isItemUnlocked("premium_circuit")); } @@ -132,7 +128,7 @@ TEST_CASE("RecipeSchematic: -1 recipe's inputs are in the implicit unlock set", { // premium_circuit takes circuit_board as input; that item was already // implicitly unlocked by ship schematics, so it must remain unlocked. - const Simulation sim(loadConfig()); + const Simulation sim(loadTestConfig()); REQUIRE(sim.isItemUnlocked("circuit_board")); } @@ -142,7 +138,7 @@ TEST_CASE("RecipeSchematic: locked recipe's unique input is not in the implicit // exotic_alloy has unlock_at_station_level = 0 (locked at start) and takes // exotic_ore as input. exotic_ore is only reachable through this locked // recipe, so it must not appear in the implicit set. - const Simulation sim(loadConfig()); + const Simulation sim(loadTestConfig()); REQUIRE_FALSE(sim.isItemUnlocked("exotic_ore")); } @@ -152,7 +148,7 @@ TEST_CASE("RecipeSchematic: locked recipe's output item is not in the implicit u // exotic_alloy is produced only by the locked recipe of the same name and // is not needed by any schematic, so neither the item nor the recipe should // be unlocked at game start. - const Simulation sim(loadConfig()); + const Simulation sim(loadTestConfig()); REQUIRE_FALSE(sim.isItemUnlocked("exotic_alloy")); REQUIRE_FALSE(sim.isRecipeUnlocked("exotic_alloy")); } @@ -162,7 +158,7 @@ TEST_CASE("RecipeSchematic: normal implicit unlock is unaffected for untagged as { // circuit_board carries no unlock_at_station_level and is needed by ships // that start unlocked, so it must still be implicitly unlocked. - const Simulation sim(loadConfig()); + const Simulation sim(loadTestConfig()); REQUIRE(sim.isRecipeUnlocked("circuit_board")); } @@ -176,7 +172,7 @@ TEST_CASE("RecipeSchematic: eligible recipe schematic is eventually awarded on s // quick_circuit has unlock_at_station_level = 0 and produces circuit_board // (already implicitly unlocked), so it is eligible from the first station // destruction. With up to 150 trials it must be awarded at least once. - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); REQUIRE(awaitRecipeUnlock(sim, "quick_circuit")); } @@ -185,7 +181,7 @@ TEST_CASE("RecipeSchematic: an implicitly-gated recipe with no unlock group is n { // exotic_alloy is in no unlock group and its output/inputs are unreachable // via the item graph, so it can never be dropped or implicitly unlocked. - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); for (int i = 0; i < 50; ++i) { killEnemyStationsAndApply(sim); @@ -198,7 +194,7 @@ TEST_CASE("RecipeSchematic: recipe with level > destroyed station level is not a { // advanced_circuit has unlock_at_station_level = 1. Destroying a single // level-0 station set must not award it regardless of the RNG outcome. - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); killEnemyStationsAndApply(sim); REQUIRE_FALSE(sim.isRecipeUnlocked("advanced_circuit")); } @@ -208,14 +204,14 @@ TEST_CASE("RecipeSchematic: recipe with higher level is awarded once eligible st { // After enough destructions to pass station level 1, advanced_circuit must // eventually be awarded. - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); REQUIRE(awaitRecipeUnlock(sim, "advanced_circuit", 300)); } TEST_CASE("RecipeSchematic: awarded recipe schematic stays unlocked and is not awarded again", "[recipe_schematic]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); awaitRecipeUnlock(sim, "quick_circuit"); REQUIRE(sim.isRecipeUnlocked("quick_circuit")); @@ -231,7 +227,7 @@ TEST_CASE("RecipeSchematic: awarded recipe schematic stays unlocked and is not a TEST_CASE("RecipeSchematic: recipe schematic can appear in pending choices", "[recipe_schematic]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); bool foundRecipeChoice = false; for (int i = 0; i < 150 && !foundRecipeChoice; ++i) @@ -260,7 +256,7 @@ TEST_CASE("RecipeSchematic: recipe schematic can appear in pending choices", TEST_CASE("RecipeSchematic: reset re-locks a previously awarded recipe schematic", "[recipe_schematic]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); awaitRecipeUnlock(sim, "quick_circuit"); REQUIRE(sim.isRecipeUnlocked("quick_circuit")); @@ -272,7 +268,7 @@ TEST_CASE("RecipeSchematic: reset re-locks a previously awarded recipe schematic TEST_CASE("RecipeSchematic: reset keeps -1 recipes unlocked and their seed items accessible", "[recipe_schematic]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); sim.reset(); REQUIRE(sim.isRecipeUnlocked("premium_circuit")); @@ -286,7 +282,7 @@ TEST_CASE("RecipeSchematic: reset keeps -1 recipes unlocked and their seed items TEST_CASE("RecipeSchematic: newlyUnlockedRecipeIds is sorted, deduplicated, and empty for level-ups", "[recipe_schematic]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); for (int i = 0; i < 100; ++i) { @@ -310,8 +306,8 @@ TEST_CASE("RecipeSchematic: newlyUnlockedRecipeIds is sorted, deduplicated, and TEST_CASE("RecipeSchematic: newlyUnlockedRecipeIds matches recipes that actually become unlocked", "[recipe_schematic]") { - Simulation sim(loadConfig()); - const GameConfig cfg = loadConfig(); + Simulation sim(loadTestConfig()); + const GameConfig cfg = loadTestConfig(); auto unlockedTrackedRecipeIds = [&]() { @@ -365,7 +361,7 @@ TEST_CASE("SchematicDrop: an owned ship schematic is never offered again", { // repair_ship has unlock_at_station_level = 0 in the test config, so it is // locked at start and becomes eligible once a station set is destroyed. - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); REQUIRE_FALSE(sim.isSchematicUnlocked("repair_ship")); bool wasUnlocked = false; diff --git a/src/test/ReplayPlaybackTest.cpp b/src/test/ReplayPlaybackTest.cpp index b897377..7278a1f 100644 --- a/src/test/ReplayPlaybackTest.cpp +++ b/src/test/ReplayPlaybackTest.cpp @@ -16,14 +16,10 @@ #include "ReplayRecorder.h" #include "Rotation.h" #include "Simulation.h" +#include "TestConfig.h" namespace { -GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} - std::string tempOutputDir() { return (QDir::tempPath() + "/dota_factory_replay_playback_test").toStdString(); @@ -148,7 +144,7 @@ TEST_CASE("a recorded run replays to byte-identical state with no desync", "[rep std::string replayPath; std::uint64_t recordedFinalChecksum = 0; { - Simulation rec(loadConfig(), seed); + Simulation rec(loadTestConfig(), seed); CommandManager manager(rec); std::unique_ptr recorder = std::make_unique(CONFIG_DIR, tempOutputDir()); @@ -179,7 +175,7 @@ TEST_CASE("a recorded run replays to byte-identical state with no desync", "[rep REQUIRE_FALSE(parsed->entries.empty()); // --- Replay it. --- - Simulation play(loadConfig(), parsed->header.seed); + Simulation play(loadTestConfig(), parsed->header.seed); ReplayPlayer player(play, parsed->entries); player.start(); while (!player.isFinished()) @@ -201,7 +197,7 @@ TEST_CASE("ReplayPlayer reports a desync when a checksum is corrupted", "[replay std::string replayPath; { - Simulation rec(loadConfig(), seed); + Simulation rec(loadTestConfig(), seed); CommandManager manager(rec); std::unique_ptr recorder = std::make_unique(CONFIG_DIR, tempOutputDir()); @@ -228,7 +224,7 @@ TEST_CASE("ReplayPlayer reports a desync when a checksum is corrupted", "[replay } REQUIRE(corrupted); - Simulation play(loadConfig(), parsed->header.seed); + Simulation play(loadTestConfig(), parsed->header.seed); ReplayPlayer player(play, parsed->entries); player.start(); while (!player.isFinished()) @@ -250,7 +246,7 @@ TEST_CASE("a long recorded run (through waves and combat) replays with no desync std::string replayPath; std::uint64_t recordedFinalChecksum = 0; { - Simulation rec(loadConfig(), seed); + Simulation rec(loadTestConfig(), seed); CommandManager manager(rec); std::unique_ptr recorder = std::make_unique(CONFIG_DIR, tempOutputDir()); @@ -275,7 +271,7 @@ TEST_CASE("a long recorded run (through waves and combat) replays with no desync const std::optional parsed = readReplayFile(replayPath); REQUIRE(parsed.has_value()); - Simulation play(loadConfig(), parsed->header.seed); + Simulation play(loadTestConfig(), parsed->header.seed); ReplayPlayer player(play, parsed->entries); player.start(); while (!player.isFinished()) diff --git a/src/test/ReplayRecorderTest.cpp b/src/test/ReplayRecorderTest.cpp index d8205d4..cddee59 100644 --- a/src/test/ReplayRecorderTest.cpp +++ b/src/test/ReplayRecorderTest.cpp @@ -16,6 +16,7 @@ #include "GameConfig.h" #include "ReplayRecorder.h" #include "Simulation.h" +#include "TestConfig.h" namespace { @@ -144,7 +145,7 @@ TEST_CASE("ReplayRecorder writes a well-formed file", "[replay]") TEST_CASE("CommandManager records commands and an initial checksum on drain", "[replay]") { - Simulation sim(ConfigLoader::loadFromDirectory(CONFIG_DIR), 7u); + Simulation sim(loadTestConfig(), 7u); CommandManager manager(sim); std::unique_ptr recorder = @@ -187,7 +188,7 @@ TEST_CASE("ReplayRecorder startNewRun rolls to a new file", "[replay]") TEST_CASE("CommandManager rolls the replay file on a Reset command", "[replay]") { - Simulation sim(ConfigLoader::loadFromDirectory(CONFIG_DIR), 1u); + Simulation sim(loadTestConfig(), 1u); CommandManager manager(sim); std::unique_ptr recorder = @@ -197,7 +198,7 @@ TEST_CASE("CommandManager rolls the replay file on a Reset command", "[replay]") const std::string firstPath = recorderPtr->getCurrentFilePath(); std::shared_ptr reset = std::make_shared(); - reset->config = std::make_shared(ConfigLoader::loadFromDirectory(CONFIG_DIR)); + reset->config = std::make_shared(loadTestConfig()); reset->seed = 999u; manager.enqueue(reset); manager.drain(); diff --git a/src/test/ShipModuleTest.cpp b/src/test/ShipModuleTest.cpp index f78edca..3f83e1d 100644 --- a/src/test/ShipModuleTest.cpp +++ b/src/test/ShipModuleTest.cpp @@ -22,11 +22,7 @@ #include "SimulationTestAccess.h" #include "Tick.h" #include "WeaponComponent.h" - -static GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} +#include "TestConfig.h" static const ShipDef* findSchematic(const GameConfig& cfg, const std::string& id) { @@ -108,7 +104,7 @@ static void fillMaterials(Simulation& sim, BuildingId yardId, TEST_CASE("Ship spawn: no modules leaves base stats unchanged", "[modules]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const ShipDef* def = findSchematic(sim.getConfig(), "interceptor"); REQUIRE(def != nullptr); @@ -123,7 +119,7 @@ TEST_CASE("Ship spawn: no modules leaves base stats unchanged", "[modules]") TEST_CASE("Ship spawn: multiplicative HP module applies correctly", "[modules]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const ShipDef* def = findSchematic(sim.getConfig(), "interceptor"); REQUIRE(def != nullptr); @@ -148,7 +144,7 @@ TEST_CASE("Ship spawn: multiplicative HP module applies correctly", "[modules]") TEST_CASE("Ship spawn: additive sensor module applies correctly", "[modules]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const ShipDef* def = findSchematic(sim.getConfig(), "interceptor"); REQUIRE(def != nullptr); @@ -173,7 +169,7 @@ TEST_CASE("Ship spawn: additive sensor module applies correctly", "[modules]") TEST_CASE("Ship spawn: multiple modules stack correctly", "[modules]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const ShipDef* def = findSchematic(sim.getConfig(), "interceptor"); REQUIRE(def != nullptr); @@ -206,7 +202,7 @@ TEST_CASE("Ship spawn: multiple modules stack correctly", "[modules]") TEST_CASE("Shipyard: setShipLayout reinitializes buffers with module materials", "[modules][shipyard]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const BuildingDef* yardDef = findShipyardDef(sim.getConfig()); REQUIRE(yardDef != nullptr); @@ -233,7 +229,7 @@ TEST_CASE("Shipyard: setShipLayout reinitializes buffers with module materials", TEST_CASE("Shipyard: setShipLayout cancels in-progress production", "[modules][shipyard]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const ShipDef* def = findSchematic(sim.getConfig(), "interceptor"); REQUIRE(def != nullptr); const BuildingDef* yardDef = findShipyardDef(sim.getConfig()); @@ -269,7 +265,7 @@ TEST_CASE("Shipyard: setShipLayout cancels in-progress production", TEST_CASE("Shipyard: builds a bare hull when no layout is configured", "[modules][shipyard]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const ShipDef* def = findSchematic(sim.getConfig(), "interceptor"); REQUIRE(def != nullptr); // The schematic carries a weapon in its (wave-only) default loadout. This @@ -312,7 +308,7 @@ TEST_CASE("Shipyard: builds a bare hull when no layout is configured", TEST_CASE("Shipyard: setRecipe clears ship layout", "[modules][shipyard]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const BuildingDef* yardDef = findShipyardDef(sim.getConfig()); REQUIRE(yardDef != nullptr); @@ -341,7 +337,7 @@ TEST_CASE("Shipyard: setRecipe clears ship layout", "[modules][shipyard]") TEST_CASE("Shipyard: setRecipe with unchanged recipe keeps ship layout", "[modules][shipyard]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const BuildingDef* yardDef = findShipyardDef(sim.getConfig()); REQUIRE(yardDef != nullptr); @@ -376,7 +372,7 @@ TEST_CASE("Shipyard: setRecipe with unchanged recipe keeps ship layout", TEST_CASE("Ship spawn: weapon_primer multiplies attack rate in simulation", "[modules]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const ShipDef* def = findSchematic(sim.getConfig(), "interceptor"); REQUIRE(def != nullptr); @@ -401,7 +397,7 @@ TEST_CASE("Ship spawn: weapon_primer multiplies attack rate in simulation", "[mo TEST_CASE("Ship spawn: weapon_stabilizer multiplies attack range in simulation", "[modules]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const ShipDef* def = findSchematic(sim.getConfig(), "interceptor"); REQUIRE(def != nullptr); @@ -428,7 +424,7 @@ TEST_CASE("Ship spawn: weapon_stabilizer multiplies attack range in simulation", TEST_CASE("Ship spawn: afterburner additive main_acceleration is converted m/s² to tiles/tick", "[modules]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const ShipDef* def = findSchematic(sim.getConfig(), "interceptor"); REQUIRE(def != nullptr); @@ -453,7 +449,7 @@ TEST_CASE("Ship spawn: afterburner additive main_acceleration is converted m/s² TEST_CASE("Ship spawn: maneuvering_thrusters additive maneuvering_acceleration is converted m/s² to tiles/tick", "[modules]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const ShipDef* def = findSchematic(sim.getConfig(), "interceptor"); REQUIRE(def != nullptr); @@ -482,7 +478,7 @@ TEST_CASE("Ship spawn: maneuvering_thrusters additive maneuvering_acceleration i TEST_CASE("calculateShipStats: weapon_primer multiplies attack rate in stats view", "[modules]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ShipDef* def = findSchematic(cfg, "interceptor"); REQUIRE(def != nullptr); @@ -506,7 +502,7 @@ TEST_CASE("calculateShipStats: weapon_primer multiplies attack rate in stats vie TEST_CASE("calculateShipStats: weapon_stabilizer multiplies attack range in stats view", "[modules]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ShipDef* def = findSchematic(cfg, "interceptor"); REQUIRE(def != nullptr); @@ -532,7 +528,7 @@ TEST_CASE("calculateShipStats: weapon_stabilizer multiplies attack range in stat TEST_CASE("calculateShipStats: afterburner additive main_acceleration is converted m/s² to tiles/s²", "[modules]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ShipDef* def = findSchematic(cfg, "interceptor"); REQUIRE(def != nullptr); @@ -556,7 +552,7 @@ TEST_CASE("calculateShipStats: afterburner additive main_acceleration is convert TEST_CASE("calculateShipStats: maneuvering_thrusters additive maneuvering_acceleration is converted m/s² to tiles/s²", "[modules]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ShipDef* def = findSchematic(cfg, "interceptor"); REQUIRE(def != nullptr); diff --git a/src/test/ShipTest.cpp b/src/test/ShipTest.cpp index f370a5f..798e390 100644 --- a/src/test/ShipTest.cpp +++ b/src/test/ShipTest.cpp @@ -29,11 +29,7 @@ #include "ShipSystem.h" #include "Tick.h" #include "WeaponComponent.h" - -static GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} +#include "TestConfig.h" // --------------------------------------------------------------------------- // Helpers @@ -91,7 +87,7 @@ TEST_CASE("ShipSystem: interceptor spawn has weapon child and attack behavior, n "[ship]") { EntityAdmin admin; - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); ShipSystem ss(cfg, admin); const entt::entity e = ss.spawn("interceptor", QVector2D(0.0f, 0.0f)); @@ -115,7 +111,7 @@ TEST_CASE("ShipSystem: interceptor spawn has weapon child and attack behavior, n TEST_CASE("ShipSystem: enemy combat ship has no rally or retreat behavior", "[ship]") { EntityAdmin admin; - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); ShipSystem ss(cfg, admin); const entt::entity e = ss.spawn("interceptor", QVector2D(0.0f, 0.0f), /*isEnemy=*/true); @@ -129,7 +125,7 @@ TEST_CASE("ShipSystem: enemy combat ship has no rally or retreat behavior", "[sh TEST_CASE("ShipSystem: setRetreatEnabled(false) suppresses player retreat behavior", "[ship]") { EntityAdmin admin; - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); ShipSystem ss(cfg, admin); ss.setRetreatEnabled(false); @@ -144,7 +140,7 @@ TEST_CASE("ShipSystem: setRetreatEnabled(false) suppresses player retreat behavi TEST_CASE("ShipSystem: interceptor level 1 stats match config formulas", "[ship]") { EntityAdmin admin; - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); ShipSystem ss(cfg, admin); const entt::entity e = ss.spawn("interceptor", QVector2D(0.0f, 0.0f)); @@ -166,7 +162,7 @@ TEST_CASE("ShipSystem: interceptor level 1 stats match config formulas", "[ship] TEST_CASE("ShipSystem: interceptor hp matches config value", "[ship]") { EntityAdmin admin; - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); ShipSystem ss(cfg, admin); const entt::entity e = ss.spawn("interceptor", QVector2D(0.0f, 0.0f)); @@ -178,7 +174,7 @@ TEST_CASE("ShipSystem: interceptor hp matches config value", "[ship]") TEST_CASE("ShipSystem: interceptor maxSpeed_tpt matches config value / tileSize / kTickRateHz", "[ship]") { EntityAdmin admin; - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); ShipSystem ss(cfg, admin); const entt::entity e = ss.spawn("interceptor", QVector2D(0.0f, 0.0f)); @@ -196,7 +192,7 @@ TEST_CASE("ShipSystem: salvage_ship spawn with salvage module has cargo child an "[ship]") { EntityAdmin admin; - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); ShipSystem ss(cfg, admin); const ShipLayoutConfig layout = makeSingleModuleLayout("salvager"); @@ -212,7 +208,7 @@ TEST_CASE("ShipSystem: salvage_ship spawn with salvage module has cargo child an TEST_CASE("ShipSystem: salvage_ship cargo capacity matches config", "[ship]") { EntityAdmin admin; - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); ShipSystem ss(cfg, admin); const ShipLayoutConfig layout = makeSingleModuleLayout("salvager"); @@ -237,7 +233,7 @@ TEST_CASE("ShipSystem: repair_ship spawn with repair module has repair child and "[ship]") { EntityAdmin admin; - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); ShipSystem ss(cfg, admin); const ShipLayoutConfig layout = makeSingleModuleLayout("repair_tool"); @@ -252,7 +248,7 @@ TEST_CASE("ShipSystem: repair_ship spawn with repair module has repair child and TEST_CASE("ShipSystem: repair_ship level 1 repair stats match config formulas", "[ship]") { EntityAdmin admin; - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); ShipSystem ss(cfg, admin); const ShipLayoutConfig layout = makeSingleModuleLayout("repair_tool"); @@ -277,7 +273,7 @@ TEST_CASE("ShipSystem: repair_ship level 1 repair stats match config formulas", TEST_CASE("ShipSystem: spawned ships are valid entities", "[ship]") { EntityAdmin admin; - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); ShipSystem ss(cfg, admin); const entt::entity e1 = ss.spawn("interceptor", QVector2D(0.0f, 0.0f)); @@ -292,7 +288,7 @@ TEST_CASE("ShipSystem: spawned ships are valid entities", "[ship]") TEST_CASE("ShipSystem: despawn removes the ship and its weapon children", "[ship]") { EntityAdmin admin; - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); ShipSystem ss(cfg, admin); const entt::entity e = ss.spawn("interceptor", QVector2D(0.0f, 0.0f)); diff --git a/src/test/ShipyardTest.cpp b/src/test/ShipyardTest.cpp index d4e5980..d60f7a8 100644 --- a/src/test/ShipyardTest.cpp +++ b/src/test/ShipyardTest.cpp @@ -16,11 +16,7 @@ #include "Simulation.h" #include "SimulationTestAccess.h" #include "Tick.h" - -static GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} +#include "TestConfig.h" // A ship starts unlocked iff no unlock group grants it (REQ-LOCK-EXPLICIT). static bool startsUnlocked(const GameConfig& cfg, const std::string& shipId) @@ -98,7 +94,7 @@ static void fillMaterials(Simulation& sim, BuildingId yardId, const ShipDef& def TEST_CASE("Shipyard: spawns a player ship after production cycle completes", "[shipyard]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const ShipDef* def = findAvailableSchematic(sim.getConfig()); REQUIRE(def != nullptr); @@ -143,7 +139,7 @@ TEST_CASE("Shipyard: spawns a player ship after production cycle completes", TEST_CASE("Shipyard: does not spawn without a schematic set", "[shipyard]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const BuildingDef* yardDef = findShipyardDef(sim.getConfig()); REQUIRE(yardDef != nullptr); @@ -159,7 +155,7 @@ TEST_CASE("Shipyard: does not spawn without a schematic set", "[shipyard]") TEST_CASE("Shipyard: does not spawn with insufficient materials", "[shipyard]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const ShipDef* def = findAvailableSchematic(sim.getConfig()); REQUIRE(def != nullptr); @@ -183,7 +179,7 @@ TEST_CASE("Shipyard: does not spawn with insufficient materials", "[shipyard]") TEST_CASE("Shipyard: spawns a second ship after materials replenished", "[shipyard]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); const ShipDef* def = findAvailableSchematic(sim.getConfig()); REQUIRE(def != nullptr); diff --git a/src/test/SimulationTest.cpp b/src/test/SimulationTest.cpp index a2ffd95..1c83aa1 100644 --- a/src/test/SimulationTest.cpp +++ b/src/test/SimulationTest.cpp @@ -5,11 +5,7 @@ #include "Simulation.h" #include "Tick.h" #include "TickDriver.h" - -static GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} +#include "TestConfig.h" // --------------------------------------------------------------------------- // Simulation @@ -17,14 +13,14 @@ static GameConfig loadConfig() TEST_CASE("Simulation::currentTick starts at 0", "[simulation]") { - const Simulation sim(loadConfig()); + const Simulation sim(loadTestConfig()); REQUIRE(sim.getCurrentTick() == 0); } TEST_CASE("Simulation::tick increments currentTick by 1", "[simulation]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); sim.tick(); @@ -33,7 +29,7 @@ TEST_CASE("Simulation::tick increments currentTick by 1", "[simulation]") TEST_CASE("Simulation::tick 10 times yields currentTick == 10", "[simulation]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); for (int i = 0; i < 10; ++i) { @@ -45,14 +41,14 @@ TEST_CASE("Simulation::tick 10 times yields currentTick == 10", "[simulation]") TEST_CASE("Simulation::drainBeamFiredEvents returns empty initially", "[simulation]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); REQUIRE(sim.drainBeamFiredEvents().empty()); } TEST_CASE("Simulation::drainBeamFiredEvents clears queue on drain", "[simulation]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); // First drain: empty. sim.drainBeamFiredEvents(); @@ -63,7 +59,7 @@ TEST_CASE("Simulation::drainBeamFiredEvents clears queue on drain", "[simulation TEST_CASE("Simulation::hasSchematicChoicesPending returns false initially", "[simulation]") { - Simulation sim(loadConfig()); + Simulation sim(loadTestConfig()); REQUIRE_FALSE(sim.hasSchematicChoicesPending()); } diff --git a/src/test/TestConfig.h b/src/test/TestConfig.h new file mode 100644 index 0000000..53bf4cb --- /dev/null +++ b/src/test/TestConfig.h @@ -0,0 +1,15 @@ +#pragma once + +#include "ConfigLoader.h" +#include "GameConfig.h" + +// Loads the test fixture config set (CONFIG_DIR points at bin/test/data/config +// for the test target). Shared by every test that needs a full GameConfig, so +// the one-liner is not repeated per translation unit. +// +// Like SimulationTestAccess.h this header lives under src/test and is +// deliberately off the lib/ui/app include path. +inline GameConfig loadTestConfig() +{ + return ConfigLoader::loadFromDirectory(CONFIG_DIR); +} diff --git a/src/test/ThreatCostCalculatorTest.cpp b/src/test/ThreatCostCalculatorTest.cpp index 0c9542e..705a55c 100644 --- a/src/test/ThreatCostCalculatorTest.cpp +++ b/src/test/ThreatCostCalculatorTest.cpp @@ -2,15 +2,11 @@ #include "ConfigLoader.h" #include "ThreatCostCalculator.h" - -static GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} +#include "TestConfig.h" TEST_CASE("ThreatCostCalculator: miner item threat equals duration", "[threat]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ThreatCostTable& table = cfg.threatCosts; CHECK(table.itemThreat.at("iron_ore") == Approx(1.0)); @@ -19,7 +15,7 @@ TEST_CASE("ThreatCostCalculator: miner item threat equals duration", "[threat]") TEST_CASE("ThreatCostCalculator: smelter item threat includes input costs", "[threat]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ThreatCostTable& table = cfg.threatCosts; // iron_ingot: duration 2.0 + iron_ore(1.0) * 2 = 4.0 @@ -30,7 +26,7 @@ TEST_CASE("ThreatCostCalculator: smelter item threat includes input costs", "[th TEST_CASE("ThreatCostCalculator: assembler takes max across recipes", "[threat]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ThreatCostTable& table = cfg.threatCosts; // circuit_board has three non-reprocessing recipes: @@ -43,7 +39,7 @@ TEST_CASE("ThreatCostCalculator: assembler takes max across recipes", "[threat]" TEST_CASE("ThreatCostCalculator: scrap threat is 1 / scrap_per_threat", "[threat]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ThreatCostTable& table = cfg.threatCosts; // REQ-THREAT-SCRAP: scrap threat is the constant 1 / world.scrap_per_threat. @@ -54,7 +50,7 @@ TEST_CASE("ThreatCostCalculator: scrap threat is 1 / scrap_per_threat", "[threat TEST_CASE("ThreatCostCalculator: reprocessing-only item threat", "[threat]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ThreatCostTable& table = cfg.threatCosts; // advanced_alloy: reprocessing recipe with scrap*5, duration 3.0, probability 0.1 @@ -64,7 +60,7 @@ TEST_CASE("ThreatCostCalculator: reprocessing-only item threat", "[threat]") TEST_CASE("ThreatCostCalculator: ship threat with default modules", "[threat]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ThreatCostTable& table = cfg.threatCosts; // interceptor: 10 + iron_ingot(4)*3 + circuit_board(28)*1 + laser_cannon(5 + 4*1) = 59.0 @@ -80,7 +76,7 @@ TEST_CASE("ThreatCostCalculator: ship threat with default modules", "[threat]") TEST_CASE("ThreatCostCalculator: ship threat with custom modules", "[threat]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ThreatCostTable& table = cfg.threatCosts; // interceptor base: 10 + iron_ingot(4)*3 + circuit_board(28)*1 = 50.0 @@ -101,7 +97,7 @@ TEST_CASE("ThreatCostCalculator: ship threat with custom modules", "[threat]") TEST_CASE("ThreatCostCalculator: unknown ship returns zero", "[threat]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ThreatCostTable& table = cfg.threatCosts; double threat = calculateShipThreatCost(table, cfg, "nonexistent_ship", {}); @@ -113,7 +109,7 @@ TEST_CASE("ThreatCostCalculator: unknown ship returns zero", "[threat]") // be excluded. iron_ingot threat must not be inflated by the scrap path. TEST_CASE("ThreatCostCalculator: scrap-consuming recipe excluded when scrap-free recipe exists", "[threat]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ThreatCostTable& table = cfg.threatCosts; // scrap_iron recipe: duration=1.0, 1 scrap (threat=1.0) -> 1 iron_ingot. @@ -132,7 +128,7 @@ TEST_CASE("ThreatCostCalculator: scrap-consuming recipe excluded when scrap-free // Per-unit threat = (3.0 + iron_ore(1.0)*1) / 2 = 4.0 / 2 = 2.0. TEST_CASE("ThreatCostCalculator: per-unit division by output amount", "[threat]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ThreatCostTable& table = cfg.threatCosts; CHECK(table.itemThreat.at("dual_wire") == Approx(2.0)); @@ -146,7 +142,7 @@ TEST_CASE("ThreatCostCalculator: per-unit division by output amount", "[threat]" // downstream_product = 2.0 + 80.0*1 = 82.0. TEST_CASE("ThreatCostCalculator: downstream-of-reprocessing item resolves via fixpoint", "[threat]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ThreatCostTable& table = cfg.threatCosts; CHECK(table.itemThreat.at("advanced_alloy") == Approx(80.0)); @@ -161,7 +157,7 @@ TEST_CASE("ThreatCostCalculator: downstream-of-reprocessing item resolves via fi // expected threat = max(2.0, 29.0) = 29.0, not 2.0. TEST_CASE("ThreatCostCalculator: staggered recipes committed only when all computable", "[threat]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); const ThreatCostTable& table = cfg.threatCosts; CHECK(table.itemThreat.at("staggered_item") == Approx(29.0)); diff --git a/src/test/UnlockGroupTest.cpp b/src/test/UnlockGroupTest.cpp index 664cb07..e50b222 100644 --- a/src/test/UnlockGroupTest.cpp +++ b/src/test/UnlockGroupTest.cpp @@ -19,15 +19,11 @@ #include "SimulationTestAccess.h" #include "StationBodyComponent.h" #include "UnlocksConfig.h" +#include "TestConfig.h" namespace { -GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} - void killEnemyStations(Simulation& sim) { sim.getAdmin().forEach( @@ -42,7 +38,7 @@ void killEnemyStations(Simulation& sim) // bay together, gated to the given station level. GameConfig configWithSalvageGroup(int stationLevel) { - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); cfg.unlocks.groups.clear(); cfg.unlocks.groups.push_back( UnlockGroupDef{"salvage_operations", stationLevel, {}, {}, {"salvager"}, {"salvage_bay"}, {}}); diff --git a/src/test/UnlockPrereqTest.cpp b/src/test/UnlockPrereqTest.cpp index 233ef26..729b7d5 100644 --- a/src/test/UnlockPrereqTest.cpp +++ b/src/test/UnlockPrereqTest.cpp @@ -22,15 +22,11 @@ #include "SimulationTestAccess.h" #include "StationBodyComponent.h" #include "UnlocksConfig.h" +#include "TestConfig.h" namespace { -GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} - UnlockGroupDef& findGroup(GameConfig& cfg, const std::string& id) { for (UnlockGroupDef& group : cfg.unlocks.groups) @@ -85,7 +81,7 @@ TEST_CASE("UnlockPrereq: a group gated behind a locked ship is withheld from the // quick_circuit (recipe group, level 0) would normally be eligible at the // first station destruction. Gate it behind the repair_ship group (level 0, // locked at game start). - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); findGroup(cfg, "quick_circuit").requiredGroupIds = {"repair_ship"}; Simulation sim(std::move(cfg), 123); @@ -103,7 +99,7 @@ TEST_CASE("UnlockPrereq: a group gated behind a locked ship is withheld from the TEST_CASE("UnlockPrereq: the gated group becomes eligible once its prerequisite is awarded", "[unlock_prereq]") { - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); findGroup(cfg, "quick_circuit").requiredGroupIds = {"repair_ship"}; Simulation sim(std::move(cfg), 123); @@ -126,7 +122,7 @@ TEST_CASE("UnlockPrereq: a module group gated behind another module is withheld { // Make laser_cannon and armor_plate (both start-unlocked in the test config) // lockable via new unlock groups, with armor_plate gated behind laser_cannon. - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); cfg.unlocks.groups.push_back( UnlockGroupDef{"laser_cannon", 0, {}, {}, {"laser_cannon"}, {}, {}}); cfg.unlocks.groups.push_back( diff --git a/src/test/WaveSystemTest.cpp b/src/test/WaveSystemTest.cpp index 30f8b87..1b189bb 100644 --- a/src/test/WaveSystemTest.cpp +++ b/src/test/WaveSystemTest.cpp @@ -25,11 +25,7 @@ #include "Tick.h" #include "ThreatCostCalculator.h" #include "WaveSystem.h" - -static GameConfig loadConfig() -{ - return ConfigLoader::loadFromDirectory(CONFIG_DIR); -} +#include "TestConfig.h" // --------------------------------------------------------------------------- // Threat accumulation @@ -37,7 +33,7 @@ static GameConfig loadConfig() TEST_CASE("WaveSystem: threat accumulates at boss wave counter rate", "[wave]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); std::mt19937 rng(42); WaveSystem ws(cfg, rng); @@ -55,7 +51,7 @@ TEST_CASE("WaveSystem: threat accumulates at boss wave counter rate", "[wave]") TEST_CASE("WaveSystem: threatAccumulationRate matches the rate formula outside quiet windows", "[wave]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); std::mt19937 rng(42); WaveSystem ws(cfg, rng); @@ -65,7 +61,7 @@ TEST_CASE("WaveSystem: threatAccumulationRate matches the rate formula outside q TEST_CASE("WaveSystem: threatAccumulationRate is 0 during a quiet window", "[wave]") { - GameConfig cfg = loadConfig(); + GameConfig cfg = loadTestConfig(); // Start with the boss countdown already at the pre-boss quiet threshold. cfg.world.waves.bossCountdownSeconds = cfg.world.waves.bossQuietBeforeSeconds; std::mt19937 rng(42); @@ -83,7 +79,7 @@ TEST_CASE("WaveSystem: threatAccumulationRate is 0 during a quiet window", "[wav TEST_CASE("WaveSystem: generation starts at 0 and increments on station destruction", "[wave]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); std::mt19937 rng(42); WaveSystem ws(cfg, rng); @@ -100,7 +96,7 @@ TEST_CASE("WaveSystem: generation starts at 0 and increments on station destruct TEST_CASE("WaveSystem: Simulation pre-places HQ + 2 player + 2 enemy stations", "[wave]") { - const Simulation sim(loadConfig(), 42); + const Simulation sim(loadTestConfig(), 42); // HQ is still a Building (for belt integration). int hqCount = 0; @@ -126,7 +122,7 @@ TEST_CASE("WaveSystem: Simulation pre-places HQ + 2 player + 2 enemy stations", TEST_CASE("WaveSystem: HQ has correct initial HP from config", "[wave]") { - const Simulation sim(loadConfig(), 42); + const Simulation sim(loadTestConfig(), 42); const float expectedHp = static_cast(sim.getConfig().stations.hq.hpFormula.evaluate(0.0)); @@ -145,7 +141,7 @@ TEST_CASE("WaveSystem: HQ has correct initial HP from config", "[wave]") TEST_CASE("WaveSystem: HQ anchor is at asteroid right edge", "[wave]") { - const Simulation sim(loadConfig(), 42); + const Simulation sim(loadTestConfig(), 42); for (const Building& b : sim.getBuildings().getAllBuildings()) { @@ -162,7 +158,7 @@ TEST_CASE("WaveSystem: HQ anchor is at asteroid right edge", "[wave]") TEST_CASE("WaveSystem: player stations have weapon set", "[wave]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); int armedPlayerStations = 0; sim.getAdmin().forEach( @@ -183,7 +179,7 @@ TEST_CASE("WaveSystem: player stations have weapon set", "[wave]") TEST_CASE("WaveSystem: enemy stations have weapon set", "[wave]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); int armedEnemyStations = 0; sim.getAdmin().forEach( @@ -208,7 +204,7 @@ TEST_CASE("WaveSystem: enemy stations have weapon set", "[wave]") TEST_CASE("WaveSystem: enemy ships spawn after the initial gap elapses", "[wave]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); // The maximum gap is gapMaxSeconds = 45s → 1350 ticks. // Run 1500 ticks to guarantee at least one wave has triggered. @@ -234,7 +230,7 @@ TEST_CASE("WaveSystem: enemy ships spawn after the initial gap elapses", "[wave] TEST_CASE("WaveSystem: all ships have positive dynamic threat cost", "[wave]") { - const GameConfig cfg = loadConfig(); + const GameConfig cfg = loadTestConfig(); for (const ShipDef& def : cfg.ships.ships) { @@ -250,7 +246,7 @@ TEST_CASE("WaveSystem: all ships have positive dynamic threat cost", "[wave]") TEST_CASE("WaveSystem: destroying both enemy stations triggers a push", "[wave]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); // Damage both enemy stations to 0. sim.getAdmin().forEach( @@ -273,7 +269,7 @@ TEST_CASE("WaveSystem: destroying both enemy stations triggers a push", "[wave]" TEST_CASE("WaveSystem: push generates pending schematic choices", "[wave]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); sim.getAdmin().forEach( [](entt::entity /*e*/, const StationBodyComponent& /*sb*/, const FactionComponent& f, HealthComponent& h) @@ -291,7 +287,7 @@ TEST_CASE("WaveSystem: push generates pending schematic choices", "[wave]") TEST_CASE("WaveSystem: push schematic choices have valid ids", "[wave]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); sim.getAdmin().forEach( [](entt::entity /*e*/, const StationBodyComponent& /*sb*/, const FactionComponent& f, HealthComponent& h) @@ -339,7 +335,7 @@ TEST_CASE("WaveSystem: push schematic choices have valid ids", "[wave]") TEST_CASE("WaveSystem: schematic choices have no duplicates", "[wave]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); sim.getAdmin().forEach( [](entt::entity /*e*/, const StationBodyComponent& /*sb*/, const FactionComponent& f, HealthComponent& h) @@ -359,7 +355,7 @@ TEST_CASE("WaveSystem: schematic choices have no duplicates", "[wave]") TEST_CASE("WaveSystem: applySchematicChoice clears pending and applies", "[wave]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); sim.getAdmin().forEach( [](entt::entity /*e*/, const StationBodyComponent& /*sb*/, const FactionComponent& f, HealthComponent& h) @@ -375,7 +371,7 @@ TEST_CASE("WaveSystem: applySchematicChoice clears pending and applies", "[wave] TEST_CASE("WaveSystem: push places new enemy stations further right", "[wave]") { - Simulation sim(loadConfig(), 42); + Simulation sim(loadTestConfig(), 42); // Record the X position of the initial enemy stations. int initialX = std::numeric_limits::min();