diff --git a/cockatrice/src/game/player/player_actions.h b/cockatrice/src/game/player/player_actions.h index 0ca9ba76b..84a065479 100644 --- a/cockatrice/src/game/player/player_actions.h +++ b/cockatrice/src/game/player/player_actions.h @@ -272,6 +272,8 @@ private: * increments the given (active) tax counter by one. * @param selectedCards Cards to play * @param counterId The tax counter to increment (CounterIds::CommanderTax or PartnerTax) + * @note The increment is sent optimistically, not gated on the move succeeding; a rejected + * move can leave the tax incremented without a card being cast. */ void playAndIncreaseTax(QList selectedCards, int counterId); diff --git a/tests/command_zone_tests/CMakeLists.txt b/tests/command_zone_tests/CMakeLists.txt index 1bfe04a24..e428eae29 100644 --- a/tests/command_zone_tests/CMakeLists.txt +++ b/tests/command_zone_tests/CMakeLists.txt @@ -31,3 +31,20 @@ target_link_libraries( ) add_test(NAME counter_command_auth_test COMMAND counter_command_auth_test) + +add_executable(setup_zones_command_zone_test setup_zones_command_zone_test.cpp) + +if(NOT GTEST_FOUND) + add_dependencies(setup_zones_command_zone_test gtest) +endif() + +target_link_libraries( + setup_zones_command_zone_test + PRIVATE libcockatrice_network_server_remote + PRIVATE libcockatrice_rng + PRIVATE Threads::Threads + PRIVATE ${GTEST_BOTH_LIBRARIES} + PRIVATE ${TEST_QT_MODULES} +) + +add_test(NAME setup_zones_command_zone_test COMMAND setup_zones_command_zone_test) diff --git a/tests/command_zone_tests/setup_zones_command_zone_test.cpp b/tests/command_zone_tests/setup_zones_command_zone_test.cpp new file mode 100644 index 000000000..6b6b95615 --- /dev/null +++ b/tests/command_zone_tests/setup_zones_command_zone_test.cpp @@ -0,0 +1,134 @@ +/** @file setup_zones_command_zone_test.cpp + * @brief Tests for the command-zone branch of Server_Player::setupZones(). + * @ingroup Tests + */ + +#include "../movecard_tests/server_test_helpers.h" +#include "game/server_counter.h" +#include "game/server_game.h" +#include "game/server_player.h" +#include "server_room.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +RNG_Abstract *rng = nullptr; // required by linked server code + +namespace +{ +/** + * @brief A Server_Player whose protected deck member is populated with an empty deck. + * + * setupZones() reads deck->getCardNodes(); the base member is null by default, so this + * subclass injects an empty deck. An empty deck makes the card-loading loops no-ops, + * leaving only the zone/counter setup under test. The base destructor does not free deck + * (only prepareDestroy() does, which the test never calls), so this subclass deletes it. + */ +struct CommandZoneTestPlayer : Server_Player +{ + CommandZoneTestPlayer(Server_Game *_game, const ServerInfo_User &_userInfo) + : Server_Player(_game, 1, _userInfo, false, nullptr) + { + deck = new DeckList(); + } + + ~CommandZoneTestPlayer() override + { + delete deck; + deck = nullptr; + } +}; + +/** + * @brief Builds a Server_Player on a minimal fake game whose command zone is toggled by the + * constructor argument, then runs setupZones() so getCounters() reflects the result. + */ +struct SetupFixture +{ + ServerInfo_User user; + FakeServer server; + Server_Room room{0, 0, "", "", "", "", false, "", {}, &server}; + Server_Game game; + CommandZoneTestPlayer player; + + explicit SetupFixture(bool commandZoneEnabled) + : game(user, + 1, + "", + "", + 2, + QList(), + false, + false, + false, + false, + false, + false, + 20, + false, + commandZoneEnabled, + &room), + player(&game, user) + { + player.setupZones(); + } + + ~SetupFixture() + { + player.clearZones(); // owns and deletes the counters created by setupZones() + } +}; +} // namespace + +TEST(SetupZonesCommandZone, CreatesTaxCountersWhenEnabled) +{ + SetupFixture f(true); + const QMap &counters = f.player.getCounters(); + + EXPECT_TRUE(f.player.getZones().contains(ZoneNames::COMMAND)); + ASSERT_TRUE(counters.contains(CounterIds::CommanderTax)); + ASSERT_TRUE(counters.contains(CounterIds::PartnerTax)); + + const Server_Counter *commander = counters.value(CounterIds::CommanderTax); + const Server_Counter *partner = counters.value(CounterIds::PartnerTax); + + EXPECT_TRUE(commander->isActive()); + EXPECT_FALSE(partner->isActive()); + EXPECT_EQ(commander->getCount(), 0); + EXPECT_EQ(partner->getCount(), 0); +} + +TEST(SetupZonesCommandZone, TaxCountersUseCommanderBounds) +{ + SetupFixture f(true); + Server_Counter *commander = f.player.getCounters().value(CounterIds::CommanderTax); + ASSERT_NE(commander, nullptr); + + EXPECT_TRUE(commander->setCount(MAX_COUNTER_VALUE + 1000)); + EXPECT_EQ(commander->getCount(), MAX_COUNTER_VALUE); + EXPECT_TRUE(commander->setCount(-1)); + EXPECT_EQ(commander->getCount(), 0); +} + +TEST(SetupZonesCommandZone, NoTaxCountersWhenDisabled) +{ + SetupFixture f(false); + const QMap &counters = f.player.getCounters(); + + EXPECT_FALSE(f.player.getZones().contains(ZoneNames::COMMAND)); + EXPECT_FALSE(counters.contains(CounterIds::CommanderTax)); + EXPECT_FALSE(counters.contains(CounterIds::PartnerTax)); + EXPECT_TRUE(counters.contains(0)); +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}