130 lines
4.5 KiB
C++
130 lines
4.5 KiB
C++
#include "catch.hpp"
|
|
|
|
#include <memory>
|
|
|
|
#include "BuildingSystem.h"
|
|
#include "Command.h"
|
|
#include "CommandManager.h"
|
|
#include "ConfigLoader.h"
|
|
#include "GameConfig.h"
|
|
#include "Rotation.h"
|
|
#include "Simulation.h"
|
|
#include "SimulationTestAccess.h"
|
|
#include "Tick.h"
|
|
#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
|
|
// recorded command stream reproduce a live run (see docs/replay_design.md).
|
|
|
|
TEST_CASE("apply(PlaceBuildingCommand) matches direct placement", "[command]")
|
|
{
|
|
Simulation viaCommand(loadTestConfig(), 99);
|
|
Simulation viaDirect(loadTestConfig(), 99);
|
|
|
|
PlaceBuildingCommand command;
|
|
command.type = BuildingType::Miner;
|
|
command.anchor = QPoint(-3, 0);
|
|
command.rotation = Rotation::East;
|
|
viaCommand.apply(command);
|
|
|
|
SimulationTestAccess::place(viaDirect, BuildingType::Miner, QPoint(-3, 0), Rotation::East);
|
|
|
|
REQUIRE(viaCommand.computeStateChecksum() == viaDirect.computeStateChecksum());
|
|
}
|
|
|
|
TEST_CASE("apply(PlaceBuildingCommand) with recipe matches place-then-setRecipe", "[command]")
|
|
{
|
|
Simulation viaCommand(loadTestConfig(), 99);
|
|
Simulation viaDirect(loadTestConfig(), 99);
|
|
|
|
PlaceBuildingCommand command;
|
|
command.type = BuildingType::Miner;
|
|
command.anchor = QPoint(-2, 0);
|
|
command.rotation = Rotation::East;
|
|
command.recipeId = "mine_iron_ore";
|
|
viaCommand.apply(command);
|
|
|
|
const BuildingId id =
|
|
SimulationTestAccess::place(viaDirect, BuildingType::Miner, QPoint(-2, 0), Rotation::East).value();
|
|
SimulationTestAccess::buildings(viaDirect).setRecipe(SimulationTestAccess::state(viaDirect), id, "mine_iron_ore");
|
|
|
|
REQUIRE(viaCommand.computeStateChecksum() == viaDirect.computeStateChecksum());
|
|
}
|
|
|
|
TEST_CASE("apply(DeconstructCommand) matches direct deconstruct", "[command]")
|
|
{
|
|
Simulation viaCommand(loadTestConfig(), 99);
|
|
Simulation viaDirect(loadTestConfig(), 99);
|
|
|
|
const BuildingId idA =
|
|
SimulationTestAccess::place(viaCommand, BuildingType::Miner, QPoint(-3, 0), Rotation::East).value();
|
|
const BuildingId idB =
|
|
SimulationTestAccess::place(viaDirect, BuildingType::Miner, QPoint(-3, 0), Rotation::East).value();
|
|
REQUIRE(idA == idB);
|
|
|
|
DeconstructCommand command;
|
|
command.id = idA;
|
|
viaCommand.apply(command);
|
|
|
|
SimulationTestAccess::deconstruct(viaDirect, idB);
|
|
|
|
REQUIRE(viaCommand.computeStateChecksum() == viaDirect.computeStateChecksum());
|
|
}
|
|
|
|
TEST_CASE("apply(CancelDeconstructionCommand) matches direct cancelDeconstruction", "[command]")
|
|
{
|
|
Simulation viaCommand(loadTestConfig(), 99);
|
|
Simulation viaDirect(loadTestConfig(), 99);
|
|
|
|
const BuildingId idA =
|
|
SimulationTestAccess::place(viaCommand, BuildingType::Miner, QPoint(-3, 0), Rotation::East).value();
|
|
const BuildingId idB =
|
|
SimulationTestAccess::place(viaDirect, BuildingType::Miner, QPoint(-3, 0), Rotation::East).value();
|
|
REQUIRE(idA == idB);
|
|
|
|
// Build the miners, then queue both for deconstruction identically.
|
|
for (int i = 0; i < static_cast<int>(secondsToTicks(10.0)) + 1; ++i)
|
|
{
|
|
viaCommand.tick();
|
|
viaDirect.tick();
|
|
}
|
|
SimulationTestAccess::deconstruct(viaCommand, idA);
|
|
SimulationTestAccess::deconstruct(viaDirect, idB);
|
|
|
|
CancelDeconstructionCommand command;
|
|
command.id = idA;
|
|
viaCommand.apply(command);
|
|
|
|
SimulationTestAccess::cancelDeconstruction(viaDirect, idB);
|
|
|
|
REQUIRE(viaCommand.computeStateChecksum() == viaDirect.computeStateChecksum());
|
|
}
|
|
|
|
TEST_CASE("CommandManager drains queued commands in FIFO order through apply", "[command]")
|
|
{
|
|
Simulation viaManager(loadTestConfig(), 99);
|
|
Simulation viaDirect(loadTestConfig(), 99);
|
|
|
|
CommandManager manager(viaManager);
|
|
|
|
std::shared_ptr<PlaceBuildingCommand> first = std::make_shared<PlaceBuildingCommand>();
|
|
first->type = BuildingType::Miner;
|
|
first->anchor = QPoint(-3, 0);
|
|
std::shared_ptr<PlaceBuildingCommand> second = std::make_shared<PlaceBuildingCommand>();
|
|
second->type = BuildingType::Belt;
|
|
second->anchor = QPoint(-2, 0);
|
|
|
|
manager.enqueue(first);
|
|
manager.enqueue(second);
|
|
REQUIRE(manager.hasPending());
|
|
|
|
manager.drain();
|
|
REQUIRE_FALSE(manager.hasPending());
|
|
|
|
SimulationTestAccess::place(viaDirect, BuildingType::Miner, QPoint(-3, 0), Rotation::East);
|
|
SimulationTestAccess::place(viaDirect, BuildingType::Belt, QPoint(-2, 0), Rotation::East);
|
|
|
|
REQUIRE(viaManager.computeStateChecksum() == viaDirect.computeStateChecksum());
|
|
}
|