mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 01:23:24 -08:00
[Tests] Add a headless game logic regression suite (#7702)
* [Tests] Add headless game logic regression suite Cover CardState, CardZoneLogic and the Command_MoveCard protobufs PlayerActions emits, against libcockatrice_game with a recording AbstractClient and no graphics. The old game_smoke_test cases are migrated into the new suite. * [Tests] Cover game event application through GameEventHandler Feeds GameEventContainers through the real dispatcher and asserts the resulting zone and card state: move (including face down), the set-card-attr attributes plus the all-cards and does-not-untap branches, card counters, attach/detach, zone reveal properties, and drawing named cards from the deck. * [Tests] Replay a synthetic game state through the dispatcher Builds a GameReplay in code, feeds its containers through the same GameEventHandler path the client uses, and asserts the materialized players, zones, cards and attachments plus event ordering for move, attributes and counters. A small headless zone view stands in for the graphics side where processPlayerInfo asks for card items. * [Tests] Cover card flip, destroy, token and turn events Adds the remaining state-mutating event handlers: flip (face down keeps the ref, face up adopts the printing), destroy (removes the card and un-attaches its attached cards), create token (database P/T unless provided or face down) and player properties changed under the concede/unconcede contexts, plus active player and phase. * [Tests] Cover player lifecycle and counter events Adds the player-level events that mutate session state: leave removes the player from the manager, kicked emits gameClosed, game closed flips the closed flag and clears the started flag, host changed moves the host role to another player, and create/set/delete update the player counters with unknown ids ignored. * [Tests] Drive the zone view request through addCard The test emitted createCardRequested itself and then asserted it was emitted. Registering a ZoneViewZoneLogic on the zone and adding a card exercises the real prepareAddCard -> signal path, so the assertion now fails if addCard stops asking the graphics side to build the card. * [Tests] Skip the card database when listing tests gtest_discover_tests runs the binary with --gtest_list_tests during the build. That process built the card database and replaced its default path provider, leaving allocations that the sanitizer leg's default LeakSanitizer check reports at exit, so discovery failed on Ubuntu 26.04. No test runs in list mode, so return before the database setup. * [Tests] Fix fixture comment nit Wraps the RecordingClient::sent member name in backticks in the comment. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
5134e72eb4
commit
3cf8bdb00e
12 changed files with 1611 additions and 132 deletions
126
tests/game/game_test_main.cpp
Normal file
126
tests/game/game_test_main.cpp
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
* @file game_test_main.cpp
|
||||
*
|
||||
* Test entry point for the headless game-logic suite. The game library has no
|
||||
* graphics dependency, so a QCoreApplication is enough; the only process-wide
|
||||
* setup left is the card database, which the game logic still reaches through
|
||||
* CardDatabaseManager and which therefore has to exist before the first zone is
|
||||
* constructed.
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QLoggingCategory>
|
||||
#include <QStandardPaths>
|
||||
#include <QTemporaryDir>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <libcockatrice/card/database/card_database.h>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/interfaces/interface_card_database_path_provider.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class GameTestPathProvider : public ICardDatabasePathProvider
|
||||
{
|
||||
public:
|
||||
explicit GameTestPathProvider(QString baseDir) : baseDir_(std::move(baseDir))
|
||||
{
|
||||
}
|
||||
|
||||
QString getCardDatabasePath() const override
|
||||
{
|
||||
return baseDir_ + QStringLiteral("/cards.xml");
|
||||
}
|
||||
|
||||
QString getCustomCardDatabasePath() const override
|
||||
{
|
||||
return baseDir_ + QStringLiteral("/customsets/");
|
||||
}
|
||||
|
||||
QString getTokenDatabasePath() const override
|
||||
{
|
||||
return baseDir_ + QStringLiteral("/tokens.xml");
|
||||
}
|
||||
|
||||
QString getSpoilerCardDatabasePath() const override
|
||||
{
|
||||
return baseDir_ + QStringLiteral("/spoilers.xml");
|
||||
}
|
||||
|
||||
private:
|
||||
QString baseDir_;
|
||||
};
|
||||
|
||||
bool copyFixture(const QString &fileName, const QString &targetDir)
|
||||
{
|
||||
const QString source = QString::fromUtf8(GAME_TEST_DATA_DIR) + QLatin1Char('/') + fileName;
|
||||
const QString target = targetDir + QLatin1Char('/') + fileName;
|
||||
if (QFile::copy(source, target)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::fprintf(stderr, "Failed to copy test fixture %s to %s\n", qPrintable(source), qPrintable(target));
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
// gtest_discover_tests runs the binary with --gtest_list_tests while the
|
||||
// build is still in progress. No test runs there, and building the card
|
||||
// database would leave process-lifetime allocations that the sanitizer
|
||||
// leg's default leak detection reports at exit, failing discovery.
|
||||
if (::testing::GTEST_FLAG(list_tests)) {
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
QStandardPaths::setTestModeEnabled(true);
|
||||
|
||||
QTemporaryDir home;
|
||||
if (!home.isValid()) {
|
||||
std::fprintf(stderr, "Failed to create a temporary home directory\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
qputenv("HOME", home.path().toLocal8Bit());
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
QLoggingCategory::setFilterRules(QStringLiteral("card_database.loading=false\n"
|
||||
"card_database.loading.success_or_failure=false\n"
|
||||
"settings_cache.*=false"));
|
||||
|
||||
// Copy the fixtures so the database loader never writes its .cache file into
|
||||
// the source tree, then point the singleton at the copy.
|
||||
QTemporaryDir dataDir;
|
||||
if (!dataDir.isValid() || !QDir().mkpath(dataDir.filePath(QStringLiteral("customsets")))) {
|
||||
std::fprintf(stderr, "Failed to create a temporary card database directory\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!copyFixture(QStringLiteral("cards.xml"), dataDir.path()) ||
|
||||
!copyFixture(QStringLiteral("tokens.xml"), dataDir.path()) ||
|
||||
!copyFixture(QStringLiteral("spoilers.xml"), dataDir.path())) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Function-local static: constructed before the CardDatabase singleton below,
|
||||
// so it is destroyed after it and the raw pointer stored in the manager stays
|
||||
// valid for the database's lifetime.
|
||||
static GameTestPathProvider pathProvider(dataDir.path());
|
||||
CardDatabaseManager::setCardDatabasePathProvider(&pathProvider);
|
||||
|
||||
CardDatabase *database = CardDatabaseManager::getInstance();
|
||||
database->loadCardDatabases();
|
||||
if (database->getLoadStatus() != Ok) {
|
||||
std::fprintf(stderr, "Failed to load the game test card database (status %d)\n",
|
||||
static_cast<int>(database->getLoadStatus()));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue