Initial commit of mtgonline project

This commit is contained in:
2026-07-18 04:57:40 +00:00
commit 86c12376f8
1870 changed files with 547994 additions and 0 deletions
+89
View File
@@ -0,0 +1,89 @@
# NOTE: Qt modules for tests are defined centrally in cmake/FindQtRuntime.cmake (the _TEST_NEEDED variable).
# If a new test needs additional Qt modules, add them there — not in individual test CMakeLists.txt files.
enable_testing()
add_test(NAME dummy_test COMMAND dummy_test)
add_test(NAME expression_test COMMAND expression_test)
add_test(NAME clamped_arithmetic_test COMMAND clamped_arithmetic_test)
add_test(NAME test_age_formatting COMMAND test_age_formatting)
add_test(NAME password_hash_test COMMAND password_hash_test)
add_test(NAME server_card_counter_test COMMAND server_card_counter_test)
add_test(NAME server_counter_test COMMAND server_counter_test)
add_test(NAME deck_hash_performance_test COMMAND deck_hash_performance_test)
set_tests_properties(deck_hash_performance_test PROPERTIES TIMEOUT 5)
# Find GTest
add_executable(dummy_test dummy_test.cpp)
add_executable(expression_test expression_test.cpp)
add_executable(clamped_arithmetic_test clamped_arithmetic_test.cpp)
add_executable(test_age_formatting test_age_formatting.cpp)
add_executable(password_hash_test password_hash_test.cpp)
add_executable(deck_hash_performance_test deck_hash_performance_test.cpp)
add_executable(server_card_counter_test server_card_counter_test.cpp)
add_executable(server_counter_test server_counter_test.cpp)
find_package(GTest)
if(NOT GTEST_FOUND)
if(NOT EXISTS "${CMAKE_BINARY_DIR}/gtest-build")
message(STATUS "Downloading googletest")
configure_file(
"${CMAKE_SOURCE_DIR}/cmake/gtest-CMakeLists.txt.in" "${CMAKE_BINARY_DIR}/gtest-download/CMakeLists.txt"
)
execute_process(
COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gtest-download
)
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gtest-download)
else()
message(STATUS "GoogleTest directory exists")
endif()
# Add gtest directly to our build
add_subdirectory(${CMAKE_BINARY_DIR}/gtest-src ${CMAKE_BINARY_DIR}/gtest-build EXCLUDE_FROM_ALL)
# Add the gtest include directory, since gtest
# doesn't add that dependency to its gtest target
target_include_directories(gtest INTERFACE "$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/gtest-src/include>")
set(GTEST_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/gtest-src/include")
set(GTEST_BOTH_LIBRARIES gtest)
add_dependencies(dummy_test gtest)
add_dependencies(expression_test gtest)
add_dependencies(clamped_arithmetic_test gtest)
add_dependencies(test_age_formatting gtest)
add_dependencies(password_hash_test gtest)
add_dependencies(deck_hash_performance_test gtest)
add_dependencies(server_card_counter_test gtest)
add_dependencies(server_counter_test gtest)
endif()
include_directories(${GTEST_INCLUDE_DIRS})
target_link_libraries(dummy_test Threads::Threads ${GTEST_BOTH_LIBRARIES})
target_link_libraries(expression_test libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES})
target_link_libraries(
clamped_arithmetic_test libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}
)
target_link_libraries(
test_age_formatting libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}
)
target_link_libraries(
password_hash_test libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}
)
target_link_libraries(
deck_hash_performance_test libcockatrice_deck_list libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES}
${TEST_QT_MODULES}
)
target_link_libraries(
server_card_counter_test libcockatrice_network Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}
)
target_link_libraries(
server_counter_test libcockatrice_network Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}
)
add_subdirectory(card_zone_algorithms)
add_subdirectory(carddatabase)
add_subdirectory(loading_from_clipboard)
add_subdirectory(movecard_tests)
add_subdirectory(oracle)
@@ -0,0 +1,15 @@
add_executable(card_zone_algorithms_test card_zone_algorithms_test.cpp)
target_include_directories(card_zone_algorithms_test PRIVATE ${CMAKE_SOURCE_DIR}/cockatrice/src/game/zones)
target_link_libraries(
card_zone_algorithms_test
PRIVATE Threads::Threads
PRIVATE ${GTEST_BOTH_LIBRARIES}
)
add_test(NAME card_zone_algorithms_test COMMAND card_zone_algorithms_test)
if(NOT GTEST_FOUND)
add_dependencies(card_zone_algorithms_test gtest)
endif()
@@ -0,0 +1,159 @@
#include "card_zone_algorithms.h"
#include <gtest/gtest.h>
#include <vector>
struct MockCardRef
{
};
struct MockCard
{
int idSet = 0;
bool idWasCalled = false;
MockCardRef cardRefSet{};
bool cardRefWasCalled = false;
bool resetStateCalled = false;
bool resetStateKeepAnnotations = false;
bool visibleSet = false;
void setId(int id)
{
idSet = id;
idWasCalled = true;
}
void setCardRef(MockCardRef ref)
{
cardRefSet = ref;
cardRefWasCalled = true;
}
void resetState(bool keepAnnotations)
{
resetStateCalled = true;
resetStateKeepAnnotations = keepAnnotations;
}
void setVisible(bool visible)
{
visibleSet = visible;
}
};
class MockCardList
{
std::vector<MockCard *> cards;
bool contentsKnown;
public:
explicit MockCardList(bool _contentsKnown) : contentsKnown(_contentsKnown)
{
}
int size() const
{
return static_cast<int>(cards.size());
}
void insert(int index, MockCard *card)
{
cards.insert(cards.begin() + index, card);
}
bool getContentsKnown() const
{
return contentsKnown;
}
MockCard *at(int index) const
{
return cards.at(index);
}
};
class AddCardAlgorithmTest : public ::testing::Test
{
protected:
MockCardList knownList{true};
MockCardList unknownList{false};
};
TEST_F(AddCardAlgorithmTest, NegativeIndexClampsToEnd)
{
MockCard a, b;
CardZoneAlgorithms::addCardToList(knownList, &a, 0, false);
CardZoneAlgorithms::addCardToList(knownList, &b, -1, false);
EXPECT_EQ(knownList.at(0), &a);
EXPECT_EQ(knownList.at(1), &b);
EXPECT_EQ(knownList.size(), 2);
}
TEST_F(AddCardAlgorithmTest, IndexBeyondSizeClampsToEnd)
{
MockCard a, b;
CardZoneAlgorithms::addCardToList(knownList, &a, 0, false);
CardZoneAlgorithms::addCardToList(knownList, &b, 999, false);
EXPECT_EQ(knownList.at(0), &a);
EXPECT_EQ(knownList.at(1), &b);
EXPECT_EQ(knownList.size(), 2);
}
TEST_F(AddCardAlgorithmTest, ContentsKnownPreservesIdentity)
{
MockCard card;
CardZoneAlgorithms::addCardToList(knownList, &card, 0, false);
EXPECT_FALSE(card.idWasCalled);
EXPECT_FALSE(card.cardRefWasCalled);
EXPECT_TRUE(card.visibleSet);
}
TEST_F(AddCardAlgorithmTest, ContentsUnknownClearsIdentity)
{
MockCard card;
CardZoneAlgorithms::addCardToList(unknownList, &card, 0, false);
EXPECT_TRUE(card.idWasCalled);
EXPECT_EQ(card.idSet, -1);
EXPECT_TRUE(card.cardRefWasCalled);
}
TEST_F(AddCardAlgorithmTest, MidListInsertionPreservesOrder)
{
MockCard a, b, c;
CardZoneAlgorithms::addCardToList(knownList, &a, 0, false);
CardZoneAlgorithms::addCardToList(knownList, &b, 1, false);
CardZoneAlgorithms::addCardToList(knownList, &c, 1, false);
EXPECT_EQ(knownList.size(), 3);
EXPECT_EQ(knownList.at(0), &a);
EXPECT_EQ(knownList.at(1), &c);
EXPECT_EQ(knownList.at(2), &b);
}
TEST_F(AddCardAlgorithmTest, KeepAnnotationsFalsePassedThrough)
{
MockCard card;
CardZoneAlgorithms::addCardToList(knownList, &card, 0, false);
EXPECT_TRUE(card.resetStateCalled);
EXPECT_FALSE(card.resetStateKeepAnnotations);
}
TEST_F(AddCardAlgorithmTest, KeepAnnotationsTruePassedThrough)
{
MockCard card;
CardZoneAlgorithms::addCardToList(knownList, &card, 0, true);
EXPECT_TRUE(card.resetStateCalled);
EXPECT_TRUE(card.resetStateKeepAnnotations);
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+51
View File
@@ -0,0 +1,51 @@
cmake_minimum_required(VERSION 3.16)
project(CardDatabaseTests VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
# ------------------------
# Definitions
# ------------------------
add_definitions("-DCARDDB_DATADIR=\"${CMAKE_CURRENT_SOURCE_DIR}/data/\"")
# ------------------------
# Card Database Test
# ------------------------
add_executable(carddatabase_test ${MOCKS_SOURCES} ${VERSION_STRING_CPP} carddatabase_test.cpp mocks.cpp)
target_link_libraries(
carddatabase_test
PRIVATE libcockatrice_card
PRIVATE Threads::Threads
PRIVATE ${GTEST_BOTH_LIBRARIES}
PRIVATE ${TEST_QT_MODULES}
)
add_test(NAME carddatabase_test COMMAND carddatabase_test)
# ------------------------
# Filter String Test
# (guard must match the condition for libcockatrice_filters in the root CMakeLists.txt)
# ------------------------
if(WITH_ORACLE OR WITH_CLIENT)
add_executable(filter_string_test ${MOCKS_SOURCES} ${VERSION_STRING_CPP} filter_string_test.cpp mocks.cpp)
target_link_libraries(
filter_string_test
PRIVATE libcockatrice_filters
PRIVATE Threads::Threads
PRIVATE ${GTEST_BOTH_LIBRARIES}
PRIVATE ${TEST_QT_MODULES}
)
add_test(NAME filter_string_test COMMAND filter_string_test)
if(NOT GTEST_FOUND)
add_dependencies(filter_string_test gtest)
endif()
endif()
# ------------------------
# Dependencies on gtest
# ------------------------
if(NOT GTEST_FOUND)
add_dependencies(carddatabase_test gtest)
endif()
@@ -0,0 +1,42 @@
#include "mocks.h"
#include "test_card_database_path_provider.h"
#include "gtest/gtest.h"
#include <libcockatrice/interfaces/noop_card_preference_provider.h>
#include <libcockatrice/interfaces/noop_card_set_priority_controller.h>
namespace
{
TEST(CardDatabaseTest, LoadXml)
{
CardDatabase *db = new CardDatabase(nullptr, new NoopCardPreferenceProvider(), new TestCardDatabasePathProvider(),
new NoopCardSetPriorityController());
// ensure the card database is empty at start
ASSERT_EQ(0, db->getCardList().size()) << "Cards not empty at start";
ASSERT_EQ(0, db->getSetList().size()) << "Sets not empty at start";
ASSERT_EQ(0, db->query()->getAllMainCardTypes().size()) << "Types not empty at start";
ASSERT_EQ(NotLoaded, db->getLoadStatus()) << "Incorrect status at start";
// load dummy cards and test result
db->loadCardDatabases();
ASSERT_EQ(9, db->getCardList().size()) << "Wrong card count after load";
ASSERT_EQ(5, db->getSetList().size()) << "Wrong sets count after load";
ASSERT_EQ(3, db->query()->getAllMainCardTypes().size()) << "Wrong types count after load";
ASSERT_EQ(Ok, db->getLoadStatus()) << "Wrong status after load";
// ensure the card database is empty after clear()
db->clear();
ASSERT_EQ(0, db->getCardList().size()) << "Cards not empty after clear";
ASSERT_EQ(0, db->getSetList().size()) << "Sets not empty after clear";
ASSERT_EQ(0, db->query()->getAllMainCardTypes().size()) << "Types not empty after clear";
ASSERT_EQ(NotLoaded, db->getLoadStatus()) << "Incorrect status after clear";
}
} // namespace
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
@@ -0,0 +1,82 @@
#include "mocks.h"
#include "test_card_database_path_provider.h"
#include "gtest/gtest.h"
#include <libcockatrice/filters/filter_string.h>
#include <libcockatrice/interfaces/noop_card_preference_provider.h>
#include <libcockatrice/interfaces/noop_card_set_priority_controller.h>
#define QUERY(name, card, query, match) \
TEST_F(CardQuery, name) \
{ \
ASSERT_EQ(FilterString(query).check(card), match); \
}
namespace
{
class CardQuery : public ::testing::Test
{
protected:
void SetUp() override
{
CardDatabase *db = new CardDatabase(nullptr, new NoopCardPreferenceProvider(),
new TestCardDatabasePathProvider(), new NoopCardSetPriorityController());
db->loadCardDatabases();
cat = db->query()->getCardBySimpleName("Cat");
notDeadAfterAll = db->query()->getCardBySimpleName("Not Dead");
truth = db->query()->getCardBySimpleName("Truth");
doctor = db->query()->getCardBySimpleName("Doctor");
}
// void TearDown() override {}
CardData cat;
CardData notDeadAfterAll;
CardData truth;
CardData doctor;
};
QUERY(Empty, cat, "", true)
QUERY(Typing, cat, "t", true)
QUERY(NonMatchingType, cat, "t:kithkin", false)
QUERY(MatchingType, cat, "t:creature", true)
QUERY(MatchingCreatureType, cat, "t:cat", true)
QUERY(PartialMatchingType, cat, "t:ca", false)
QUERY(MatchingMultiWordType, doctor, "t:\"Time Lord\"", true)
QUERY(Not1, cat, "NOT t:kithkin", true)
QUERY(Not2, cat, "NOT t:creature", false)
QUERY(NonKeyword1, cat, "not t:kithkin", false)
QUERY(NonKeyword2, cat, "t:bat or t:creature", false)
QUERY(NonKeyword3, notDeadAfterAll, "not dead", true)
QUERY(NonKeyword4, truth, "truth or trail", false)
QUERY(Case, cat, "t:cReAtUrE", true)
QUERY(And, cat, "t:creature t:creature", true)
QUERY(And2, cat, "t:creature t:sorcery", false)
QUERY(Or, cat, "t:bat OR t:creature", true)
QUERY(Cmc1, cat, "cmc=2", true)
QUERY(Cmc2, cat, "cmc>3", false)
QUERY(Cmc3, cat, "cmc>1", true)
QUERY(Quotes, cat, "t:\"creature\"", true)
QUERY(Field, cat, "pt:\"3/3\"", true)
QUERY(Color1, cat, "c:g", true)
QUERY(Color2, cat, "c:gw", true)
QUERY(Color3, cat, "c!g", true)
QUERY(Color4, cat, "c!gw", false)
QUERY(BracketNextToUnquotedString, cat, "(o:woof OR o:meow)", true)
} // namespace
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+6
View File
@@ -0,0 +1,6 @@
#include "mocks.h"
void CardPictureLoader::clearPixmapCache(CardInfoPtr /* card */)
{
}
+16
View File
@@ -0,0 +1,16 @@
/*
* Beware of this preprocessor hack used to redefine the settingCache class
* instead of including it and all of its dependencies.
* Always set header guards of mocked objects before including any headers
* with mocked objects.
*/
#define PICTURELOADER_H
#include <libcockatrice/card/database/card_database.h>
class CardPictureLoader
{
public:
static void clearPixmapCache(CardInfoPtr card);
};
@@ -0,0 +1,28 @@
#ifndef COCKATRICE_TEST_CARD_DATABASE_PATH_PROVIDER_H
#define COCKATRICE_TEST_CARD_DATABASE_PATH_PROVIDER_H
#include <libcockatrice/interfaces/interface_card_database_path_provider.h>
class TestCardDatabasePathProvider : public ICardDatabasePathProvider
{
public:
QString getCardDatabasePath() const override
{
return QString("%1/cards.xml").arg(CARDDB_DATADIR);
}
QString getCustomCardDatabasePath() const override
{
return QString("%1/customsets/").arg(CARDDB_DATADIR);
}
QString getTokenDatabasePath() const override
{
return QString("%1/tokens.xml").arg(CARDDB_DATADIR);
}
QString getSpoilerCardDatabasePath() const override
{
return QString("%1/spoiler.xml").arg(CARDDB_DATADIR);
}
};
#endif // COCKATRICE_TEST_CARD_DATABASE_PATH_PROVIDER_H
+44
View File
@@ -0,0 +1,44 @@
/** @file clamped_arithmetic_test.cpp
* @brief Tests for shared helpers in clamped_arithmetic.h.
* @ingroup Tests
*/
#include <gtest/gtest.h>
#include <libcockatrice/utility/clamped_arithmetic.h>
#include <limits>
TEST(AddClamped, AddsWithinBounds)
{
EXPECT_EQ(addClamped(5, 3, 0, 100), 8);
EXPECT_EQ(addClamped(10, -3, 0, 100), 7);
}
TEST(AddClamped, ClampsToUpperAndLowerBound)
{
EXPECT_EQ(addClamped(99, 5, 0, 100), 100); // saturates at max
EXPECT_EQ(addClamped(2, -10, 0, 100), 0); // saturates at min
EXPECT_EQ(addClamped(999, 1, 0, 999), 999); // crossing the counter cap holds at the bound
}
TEST(AddClamped, IntOverflowDoesNotWrap)
{
// The 64-bit intermediate must prevent signed-int overflow UB.
constexpr int intMax = std::numeric_limits<int>::max();
constexpr int intMin = std::numeric_limits<int>::min();
EXPECT_EQ(addClamped(intMax, 1, intMin, intMax), intMax);
EXPECT_EQ(addClamped(intMax, intMax, intMin, intMax), intMax);
}
TEST(AddClamped, IntUnderflowDoesNotWrap)
{
constexpr int intMax = std::numeric_limits<int>::max();
constexpr int intMin = std::numeric_limits<int>::min();
EXPECT_EQ(addClamped(intMin, -1, intMin, intMax), intMin);
EXPECT_EQ(addClamped(intMin, intMin, intMin, intMax), intMin);
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+81
View File
@@ -0,0 +1,81 @@
#include "gtest/gtest.h"
#include <QDebug>
#include <libcockatrice/deck_list/deck_list.h>
static constexpr int amount = 1e5;
QString repeatDeck;
QString numberDeck;
QString uniquesDeck;
QString uniquesXorDeck;
QString duplicatesDeck;
TEST(DeckHashTest, RepeatTest)
{
DeckList decklist(repeatDeck);
for (int i = 0; i < amount; ++i) {
decklist.getDeckHash();
decklist.refreshDeckHash();
}
auto hash = decklist.getDeckHash().toStdString();
ASSERT_EQ(hash, "5cac19qm") << "The hash does not match!";
}
TEST(DeckHashTest, NumberTest)
{
DeckList decklist(numberDeck);
auto hash = decklist.getDeckHash().toStdString();
ASSERT_EQ(hash, "e0m38p19") << "The hash does not match!";
}
TEST(DeckHashTest, UniquesTest)
{
DeckList decklist(uniquesDeck);
auto hash = decklist.getDeckHash().toStdString();
ASSERT_EQ(hash, "88prk025") << "The hash does not match!";
}
TEST(DeckHashTest, UniquesTestXor)
{
DeckList decklist(uniquesXorDeck);
auto hash = decklist.getDeckHash().toStdString();
ASSERT_EQ(hash, "hkn6q4pf") << "The hash does not match!";
}
TEST(DeckHashTest, DuplicatesTest)
{
DeckList decklist(duplicatesDeck);
auto hash = decklist.getDeckHash().toStdString();
ASSERT_EQ(hash, "ekt6tg1h") << "The hash does not match!";
}
int main(int argc, char **argv)
{
const QString deckStart =
R"(<?xml version="1.0"?><cockatrice_deck version="1"><deckname></deckname><comments></comments><zone name="main">)";
const QString deckEnd = R"(</zone></cockatrice_deck>)";
repeatDeck =
deckStart +
R"(<card number="1" name="Mountain"/><card number="2" name="Island"/></zone><zone name="side"><card number="3" name="Forest"/>)" +
deckEnd;
numberDeck = deckStart + QString(R"(<card number="%1" name="Island"/>)").arg(amount) + deckEnd;
QStringList deckString{deckStart};
QStringList deckStringXor = deckString;
int len = QString::number(amount).length();
for (int i = 0; i < amount; ++i) {
// creates already sorted list
deckString << R"(<card number="1" name="card )" << QString::number(i).rightJustified(len, '0') << R"("/>)";
// xor in order to mess with sorting
deckStringXor << R"(<card number="1" name="card )" << QString::number(i ^ amount) << R"("/>)";
}
deckString << deckEnd;
deckStringXor << deckEnd;
uniquesDeck = deckString.join("");
uniquesXorDeck = deckStringXor.join("");
duplicatesDeck = deckStart + QString(R"(<card number="1" name="card"/>)").repeated(amount) + deckEnd;
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+19
View File
@@ -0,0 +1,19 @@
#include "gtest/gtest.h"
namespace
{
class FooTest : public ::testing::Test
{
};
TEST(DummyTest, Works)
{
ASSERT_EQ(1, 1) << "One is not equal to one";
}
} // namespace
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+31
View File
@@ -0,0 +1,31 @@
#include "gtest/gtest.h"
#include <QtMath>
#include <libcockatrice/utility/expression.h>
#define TEST_EXPR(name, a, b) \
TEST(ExpressionTest, name) \
{ \
Expression exp(8); \
ASSERT_EQ(exp.parse(a), b) << a; \
}
namespace
{
TEST_EXPR(Number, "1", 1)
TEST_EXPR(Multiply, "2*2", 4)
TEST_EXPR(Whitespace, "3 * 3", 9)
TEST_EXPR(Powers, "2^8", 256)
TEST_EXPR(OrderOfOperations, "2+2*2", 6)
TEST_EXPR(Fn, "2*cos(1)", 2 * qCos(1))
TEST_EXPR(Variable, "x / 2", 4)
TEST_EXPR(Negative, "-2 * 2", -4)
TEST_EXPR(UnknownFnReturnsZero, "blah(22)", 0)
} // namespace
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
@@ -0,0 +1,12 @@
add_definitions("-DCARDDB_DATADIR=\"${CMAKE_CURRENT_SOURCE_DIR}/data/\"")
add_executable(loading_from_clipboard_test ${VERSION_STRING_CPP} clipboard_testing.cpp loading_from_clipboard_test.cpp)
if(NOT GTEST_FOUND)
add_dependencies(loading_from_clipboard_test gtest)
endif()
target_link_libraries(
loading_from_clipboard_test libcockatrice_deck_list libcockatrice_card Threads::Threads ${GTEST_BOTH_LIBRARIES}
${TEST_QT_MODULES}
)
add_test(NAME loading_from_clipboard_test COMMAND loading_from_clipboard_test)
@@ -0,0 +1,55 @@
#include "clipboard_testing.h"
#include <QTextStream>
#include <libcockatrice/card/import/card_name_normalizer.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
DeckList getDeckList(const QString &clipboard)
{
DeckList deckList;
QString cp(clipboard);
QTextStream stream(&cp); // text stream requires local copy
deckList.loadFromStream_Plain(stream, false, CardNameNormalizer());
return deckList;
}
void testEmpty(const QString &clipboard)
{
DeckList deckList = getDeckList(clipboard);
ASSERT_TRUE(deckList.getCardList().isEmpty());
}
void testHash(const QString &clipboard, const std::string &hash)
{
DeckList deckList = getDeckList(clipboard);
ASSERT_EQ(deckList.getDeckHash().toStdString(), hash);
}
void testDeck(const QString &clipboard, const Result &result)
{
DeckList deckList = getDeckList(clipboard);
ASSERT_EQ(result.name, deckList.getName().toStdString());
ASSERT_EQ(result.comments, deckList.getComments().toStdString());
CardRows mainboard;
CardRows sideboard;
auto extractCards = [&mainboard, &sideboard](const InnerDecklistNode *innerDecklistNode,
const DecklistCardNode *card) {
if (innerDecklistNode->getName() == DECK_ZONE_MAIN) {
mainboard.append({card->getName().toStdString(), card->getNumber()});
} else if (innerDecklistNode->getName() == DECK_ZONE_SIDE) {
sideboard.append({card->getName().toStdString(), card->getNumber()});
} else {
FAIL();
}
};
deckList.forEachCard(extractCards);
ASSERT_EQ(result.mainboard, mainboard);
ASSERT_EQ(result.sideboard, sideboard);
}
@@ -0,0 +1,27 @@
#ifndef CLIPBOARD_TESTING_H
#define CLIPBOARD_TESTING_H
#include "gtest/gtest.h"
#include <libcockatrice/deck_list/deck_list.h>
// using std types because qt types aren't understood by gtest (without this you'll get less nice errors)
using CardRows = QVector<std::pair<std::string, int>>;
struct Result
{
std::string name;
std::string comments;
CardRows mainboard;
CardRows sideboard;
Result(std::string _name, std::string _comments, CardRows _mainboard, CardRows _sideboard)
: name(_name), comments(_comments), mainboard(_mainboard), sideboard(_sideboard)
{
}
};
void testEmpty(const QString &clipboard);
void testHash(const QString &clipboard, const std::string &hash);
void testDeck(const QString &clipboard, const Result &result);
#endif // CLIPBOARD_TESTING_H
@@ -0,0 +1,226 @@
#include "clipboard_testing.h"
// Testing is done by using the DeckList::loadFromString_Plain function in common/decklist.h
// It does not check if cards are in the database at all, so no comparisons to the database will be made.
TEST(LoadingFromClipboardTest, EmptyDeck)
{
testEmpty("");
}
TEST(LoadingFromClipboardTest, EmptySideboard)
{
testEmpty("Sideboard");
}
TEST(LoadingFromClipboardTest, QuantityPrefixed)
{
QString clipboard("1 Mountain\n"
"2x Island\n"
"3x Forest\n");
Result result("", "", {{"Mountain", 1}, {"Island", 2}, {"Forest", 3}}, {});
testDeck(clipboard, result);
}
TEST(LoadingFromClipboardTest, CommentsAreIgnored)
{
QString clipboard("//1 Mountain\n"
"//2x Island\n"
"//SB:2x Island\n");
testEmpty(clipboard);
}
TEST(LoadingFromClipboardTest, SideboardPrefix)
{
QString clipboard("1 Mountain\n"
"SB: 1 Mountain\n"
"sb: 2x Island\n"
"2 Swamp\n"
"\n"
"3 Plains\n");
Result result("", "", {{"Mountain", 1}, {"Swamp", 2}, {"Plains", 3}}, {{"Mountain", 1}, {"Island", 2}});
testDeck(clipboard, result);
}
TEST(LoadingFromClipboardTest, SideboardLine)
{
QString clipboard("1 Mountain\n"
"2 Swamp\n"
"\n"
"3 Plains\n"
"sideboard\n"
"1 Mountain\n"
"2x Island\n");
Result result("", "", {{"Mountain", 1}, {"Swamp", 2}, {"Plains", 3}}, {{"Mountain", 1}, {"Island", 2}});
testDeck(clipboard, result);
}
TEST(LoadingFromClipboardTest, UnknownCardsAreNotDiscarded)
{
QString clipboard("1 CardThatDoesNotExistInCardsXml\n");
Result result("", "", {{"CardThatDoesNotExistInCardsXml", 1}}, {});
testDeck(clipboard, result);
}
TEST(LoadingFromClipboardTest, WeirdWhitespaceIsIgnored)
{
QString clipboard(
"\t\tSb:\t1\tOur Market Research Shows That Players Like Really Long Card Names So We Made "
" This Card to Have\tthe Absolute \t Longest Card Name \tEver Elemental\t\n\t");
Result result("", "", {},
{{"Our Market Research Shows That Players Like Really Long Card Names So We Made This Card to Have "
"the Absolute Longest Card Name Ever Elemental",
1}});
testDeck(clipboard, result);
}
TEST(LoadingFromClipboardTest, RemoveBlankEntriesFromBeginningAndEnd)
{
QString clipboard("\n"
"\n"
"\n"
"1x Algae Gharial\n"
"3x CardThatDoesNotExistInCardsXml\n"
"2x Phelddagrif\n"
"\n"
"\n");
Result result("", "", {{"Algae Gharial", 1}, {"CardThatDoesNotExistInCardsXml", 3}, {"Phelddagrif", 2}}, {});
testDeck(clipboard, result);
}
TEST(LoadingFromClipboardTest, UseFirstBlankIfOnlyOneBlankToSplitSideboard)
{
QString clipboard("1x Algae Gharial\n"
"3x CardThatDoesNotExistInCardsXml\n"
"\n"
"2x Phelddagrif\n");
Result result("", "", {{"Algae Gharial", 1}, {"CardThatDoesNotExistInCardsXml", 3}}, {{"Phelddagrif", 2}});
testDeck(clipboard, result);
}
TEST(LoadingFromClipboardTest, IfMultipleScatteredBlanksAllMainBoard)
{
QString clipboard("1x Algae Gharial\n"
"3x CardThatDoesNotExistInCardsXml\n"
"\n"
"2x Phelddagrif\n"
"\n"
"3 Giant Growth\n");
Result result(
"", "", {{"Algae Gharial", 1}, {"CardThatDoesNotExistInCardsXml", 3}, {"Phelddagrif", 2}, {"Giant Growth", 3}},
{});
testDeck(clipboard, result);
}
TEST(LoadingFromClipboardTest, EdgeCaseTesting)
{
QString clipboard(R"(
// DeckName
// Comment 1
//
//Comment [two]
//(test) Æ | / (3)
// Mainboard (11 cards)
Æther Adept
2x Fire // Ice
1 Minsc & Boo, Timeless Heroes
3 Pain/Suffering
4X [B] Forest (3)
// Sideboard (11 cards)
5x [WTH] Natures Resurgence
6X Gaea's Skyfolk
7 B.F.M. (Big Furry Monster)
)");
Result result("DeckName", "Comment 1\n\nComment [two]\n(test) Æ | / (3)",
{{"Aether Adept", 1},
{"Fire // Ice", 2},
{"Minsc & Boo, Timeless Heroes", 1},
{"Pain // Suffering", 3},
{"Forest", 4}},
{{"Nature's Resurgence", 5}, {"Gaea's Skyfolk", 6}, {"B.F.M. (Big Furry Monster)", 7}});
testDeck(clipboard, result);
}
TEST(LoadingFromClipboardTest, CommentsBeforeCardsTesting)
{
QString clipboard("// Title from website.com\n"
"// A nice deck\n"
"// With nice cards\n"
"\n"
"// Mainboard\n"
"1 test1\n"
"Sideboard\n"
"2 test2\n");
Result result("Title from website.com", "A nice deck\nWith nice cards", {{"test1", 1}}, {{"test2", 2}});
testDeck(clipboard, result);
}
TEST(LoadingFromClipboardTest, mainboardAsLine)
{
QString clipboard("// Deck Name\n"
"\n"
"MainBoard: 3 cards\n"
"3 card\n"
"\n"
"SideBoard: 2 cards\n"
"2 sidecard\n");
Result result("Deck Name", "", {{"card", 3}}, {{"sidecard", 2}});
testDeck(clipboard, result);
}
TEST(LoadingFromClipboardTest, deckAsCard)
{
QString clipboard("6 Deck of Cards But Animated\n"
"\n"
"7 Sideboard Card\n");
Result result("", "", {{"Deck of Cards But Animated", 6}}, {{"Sideboard Card", 7}});
testDeck(clipboard, result);
}
TEST(LoadingFromClipboardTest, emptyMainBoard)
{
QString clipboard("deck\n"
"\n"
"sideboard\n");
testEmpty(clipboard);
}
TEST(LoadingFromClipboardTest, emptyHash)
{
QString clipboard("");
testHash(clipboard, "r8sq7riu");
}
TEST(LoadingFromClipboardTest, deckHash)
{
QString clipboard("1 Mountain\n"
"2 Island\n"
"SB: 3 Forest\n");
testHash(clipboard, "5cac19qm");
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+16
View File
@@ -0,0 +1,16 @@
add_executable(reverse_card_move_test reverse_card_move_test.cpp)
if(NOT GTEST_FOUND)
add_dependencies(reverse_card_move_test gtest)
endif()
target_link_libraries(
reverse_card_move_test
PRIVATE libcockatrice_network_server_remote
PRIVATE libcockatrice_rng
PRIVATE Threads::Threads
PRIVATE ${GTEST_BOTH_LIBRARIES}
PRIVATE ${TEST_QT_MODULES}
)
add_test(NAME reverse_card_move_test COMMAND reverse_card_move_test)
@@ -0,0 +1,91 @@
#include "game/server_abstract_player.h"
#include "game/server_card.h"
#include "game/server_cardzone.h"
#include "game/server_game.h"
#include "server_response_containers.h"
#include "server_room.h"
#include "server_test_helpers.h"
#include <gtest/gtest.h>
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
#include <libcockatrice/rng/rng_abstract.h>
#include <libcockatrice/utility/zone_names.h>
RNG_Abstract *rng = nullptr; // this needs to be defined due to other functions in server
TEST(ReverseCardMoveTest, MoveCardFromBottomTest)
{
ServerInfo_User user;
user.set_name("test-user");
// instantiate a fake server instance
FakeServer server;
Server_Room room(0, 0, "", "", "", "", false, "", {}, &server);
Server_Game game(user, 1, "", "", 2, QList<int>(), false, false, false, false, false, false, 20, false, &room);
Server_AbstractPlayer player(&game, 1, user, false, nullptr);
Server_CardZone deckZone(&player, ZoneNames::DECK, true, ServerInfo_Zone::PublicZone);
Server_CardZone exileZone(&player, ZoneNames::EXILE, true, ServerInfo_Zone::PublicZone);
// setup the deck with 20 useless cards
for (int i = 0; i < 20; i++) {
auto *cardUseless = new Server_Card({"Card Useless", "card-Useless"}, player.newCardId(), i, 0);
deckZone.insertCard(cardUseless, i, 0);
}
// add 4 cards to the end of it
auto *cardA = new Server_Card({"Card A", "card-a"}, player.newCardId(), 20, 0);
auto *cardB = new Server_Card({"Card B", "card-b"}, player.newCardId(), 21, 0);
auto *cardC = new Server_Card({"Card C", "card-c"}, player.newCardId(), 22, 0);
auto *cardD = new Server_Card({"Card D", "card-d"}, player.newCardId(), 23, 0);
deckZone.insertCard(cardA, 20, 0);
deckZone.insertCard(cardB, 21, 0);
deckZone.insertCard(cardC, 22, 0);
deckZone.insertCard(cardD, 23, 0);
// try to move them, with the expected client given order (n-3, n-2, n-1, n)
CardToMove moveA;
moveA.set_card_id(cardA->getId());
CardToMove moveB;
moveB.set_card_id(cardB->getId());
CardToMove moveC;
moveC.set_card_id(cardC->getId());
CardToMove moveD;
moveD.set_card_id(cardD->getId());
QList<const CardToMove *> cardsToMove = {&moveA, &moveB, &moveC, &moveD};
GameEventStorage ges;
const auto response = player.moveCard(ges, &deckZone, cardsToMove, &exileZone, 0, 0, false, false, false);
EXPECT_EQ(response, Response::RespOk);
int positionA;
int positionB;
int positionC;
int positionD;
// find the cards in the destination zone and check they are the right card
EXPECT_EQ(exileZone.getCard(cardA->getId(), &positionA), cardA);
EXPECT_EQ(exileZone.getCard(cardB->getId(), &positionB), cardB);
EXPECT_EQ(exileZone.getCard(cardC->getId(), &positionC), cardC);
EXPECT_EQ(exileZone.getCard(cardD->getId(), &positionD), cardD);
// check that they are at the expected index
EXPECT_EQ(cardA->getX(), 3);
EXPECT_EQ(cardB->getX(), 2);
EXPECT_EQ(cardC->getX(), 1);
EXPECT_EQ(cardD->getX(), 0);
// also check if the given positions are correct
EXPECT_EQ(positionA, 3);
EXPECT_EQ(positionB, 2);
EXPECT_EQ(positionC, 1);
EXPECT_EQ(positionD, 0);
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
@@ -0,0 +1,42 @@
#include "server.h"
#include "server_database_interface.h"
class MockDatabaseInterface : public Server_DatabaseInterface
{
public:
AuthenticationResult checkUserPassword(Server_ProtocolHandler *,
const QString &,
const QString &,
const QString &,
QString &,
int &,
bool) override
{
return NotLoggedIn;
}
ServerInfo_User getUserData(const QString &, bool) override
{
return ServerInfo_User();
}
int getNextGameId() override
{
return 1;
}
int getNextReplayId() override
{
return 1;
}
int getActiveUserCount(QString) override
{
return 1;
}
};
class FakeServer : public Server
{
public:
FakeServer()
{
setDatabaseInterface(new MockDatabaseInterface());
}
};
+9
View File
@@ -0,0 +1,9 @@
add_executable(parse_cipt_test ../../oracle/src/parsehelpers.cpp parse_cipt_test.cpp)
if(NOT GTEST_FOUND)
add_dependencies(parse_cipt_test gtest)
endif()
target_link_libraries(parse_cipt_test Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES})
add_test(NAME parse_cipt_test COMMAND parse_cipt_test)
+219
View File
@@ -0,0 +1,219 @@
#include "../../oracle/src/parsehelpers.h"
#include "gtest/gtest.h"
TEST(ParseCiptTest, parsesThisEntersTapped)
{
auto name = "Boring Fields";
auto text = "This land enters tapped.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesThisEntersTheBattlefieldTapped)
{
auto name = "Boring Fields";
auto text = "This land enters the battlefield tapped.\n"
"{T}: Add {G}.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesItEntersTappedAtEndOfSentence)
{
auto name = "Shocking Fields";
auto text = "As this land enters, you may pay 2 life. If you don't, it enters tapped.\n"
"{T}: Add {G}.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesThisEntersTappedWhenNotOnFirstLine)
{
auto name = "Boring Fields";
auto text = "Flying\n"
"This land enters tapped.\n"
"{T}: Add {G}.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesFullNameWithUnderscoreAppendedText)
{
auto name = "Boring Fields_SL50";
auto text = "Boring Fields enters tapped.\n"
"{T}: Add {G}.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesFullNameWithBracketsAppendedText)
{
auto name = "Boring Fields (SL50)";
auto text = "Boring Fields enters tapped.\n"
"{T}: Add {G}.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesFullNameWithComma)
{
auto name = "Bob, the Legend";
auto text = "Bob, the Legend enters tapped.\n"
"Whenever Bob attacks, you win the game.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesFullNameWithCommaAtEndOfSentence)
{
auto name = "Bob, the Legend";
auto text = "As Bob, the Legend enters, you may pay 2 life. If you don't, Bob, the Legend enters tapped.\n"
"Whenever Bob attacks, you win the game.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesFullNameWithApostropheAtEndOfSentence)
{
auto name = "Bob's Bobber";
auto text = "As Bob's Bobber enters, you may pay 2 life. If you don't, Bob's Bobber enters tapped.\n"
"Whenever Bob attacks, you win the game.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesShortnameEndingWithComma)
{
auto name = "Bob, the Legend";
auto text = "Bob enters tapped.\n"
"Whenever Bob attacks, you win the game.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesShortnameEndingWithSpace)
{
auto name = "Bob the Legend";
auto text = "Bob enters tapped.\n"
"Whenever Bob attacks, you win the game.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesMultiWordShortnameEndingWithComma)
{
auto name = "Bob Dod, the Legend";
auto text = "Bob Dod enters tapped.\n"
"Whenever Bob Dod attacks, you win the game.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesMultiWordShortnameEndingWithSpace)
{
auto name = "Bob Dod the Legend";
auto text = "Bob Dod enters tapped.\n"
"Whenever Bob Dod attacks, you win the game.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesShortnameEndingWithSpaceWithUnderscoreAppendedText)
{
auto name = "Bob the Legend_SL50";
auto text = "Bob enters tapped.\n"
"Whenever Bob attacks, you win the game.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesShortnameEndingWithSpaceWithBracketsAppendedText)
{
auto name = "Bob the Legend (SL50)";
auto text = "Bob enters tapped.\n"
"Whenever Bob attacks, you win the game.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesMultiWordShortnameEndingWithSpaceWithUnderscoreAppendedText)
{
auto name = "Bob Dod the Legend_SL50";
auto text = "Bob Dod enters tapped.\n"
"Whenever Bob Dod attacks, you win the game.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesMultiWordShortnameEndingWithSpaceWithBracketsAppendedText)
{
auto name = "Bob Dod the Legend (SL50)";
auto text = "Bob Dod enters tapped.\n"
"Whenever Bob Dod attacks, you win the game.";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, rejectsEmptyText)
{
auto name = "Vanilla Dude";
auto text = "";
ASSERT_FALSE(parseCipt(name, text));
}
TEST(ParseCiptTest, rejectsEntersTappedUnless)
{
auto name = "Fast Fields";
auto text = "This land enters tapped unless you control another land.";
ASSERT_FALSE(parseCipt(name, text));
}
TEST(ParseCiptTest, rejectsWhenNameIsDifferent)
{
auto name = "Boring Fields";
auto text = "Fast Fields enters tapped.";
ASSERT_FALSE(parseCipt(name, text));
}
TEST(ParseCiptTest, rejectsOtherCreaturesEnterTapped)
{
auto name = "Imposing Guy";
auto text = "Other creatures enter tapped.";
ASSERT_FALSE(parseCipt(name, text));
}
TEST(ParseCiptTest, rejectsAbilityGrantingEntersTapped)
{
auto name = "Imposing Guy";
auto text = "Other creatures have \"This creature enters tapped\".";
ASSERT_FALSE(parseCipt(name, text));
}
TEST(ParseCiptTest, parsesEntersTappedAndAbilityGrantingEntersTappedOnSameCard)
{
auto name = "Imposing Guy";
auto text = "This creature enters tapped."
"Other creatures have \"This creature enters tapped\".";
ASSERT_TRUE(parseCipt(name, text));
}
TEST(ParseCiptTest, rejectsItEntersTappedAndAttacking)
{
auto name = "Token Maker";
auto text = "When Token Maker attacks, create a token. It enters tapped and attacking.";
ASSERT_FALSE(parseCipt(name, text));
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+38
View File
@@ -0,0 +1,38 @@
#include "gtest/gtest.h"
#include <libcockatrice/rng/rng_abstract.h>
#include <libcockatrice/rng/rng_sfmt.h>
#include <libcockatrice/utility/passwordhasher.h>
RNG_Abstract *rng;
namespace
{
class PasswordHashTest : public ::testing::Test
{
protected:
void SetUp() override
{
rng = new RNG_SFMT;
}
void TearDown() override
{
delete rng;
}
};
TEST(PasswordHashTest, RegressionTest)
{
QString salt = "saltsaltsaltsalt";
QString password = "password";
QString expected = "vmKoWv975yf+WT2QCXhW48JNzZ2ghGxdgNvuKLBU0h7s6AQHSG72J6QO4ZswuSeqvBbAXbmgJSRBaSJrgc55WA==";
QString hash = PasswordHasher::computeHash(password, salt);
ASSERT_EQ(hash, salt + expected) << "The computed hash value remains the same";
}
} // namespace
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+183
View File
@@ -0,0 +1,183 @@
/** @file server_card_counter_test.cpp
* @brief Tests for Server_Card counter operations.
* @ingroup Tests
*/
#include <gtest/gtest.h>
#include <libcockatrice/network/server/remote/game/server_card.h>
#include <libcockatrice/protocol/pb/event_set_card_counter.pb.h>
#include <libcockatrice/utility/card_ref.h>
#include <libcockatrice/utility/counter_limits.h>
#include <limits>
TEST(ServerCardCounter, IncrementNewCounter)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
EXPECT_TRUE(card.incrementCounter(1, 10));
EXPECT_EQ(card.getCounter(1), 10);
}
TEST(ServerCardCounter, IncrementExistingCounter)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, 50));
EXPECT_TRUE(card.incrementCounter(1, 10));
EXPECT_EQ(card.getCounter(1), 60);
}
TEST(ServerCardCounter, IncrementOverflowProtection)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, MAX_COUNTER_VALUE));
EXPECT_FALSE(card.incrementCounter(1, 1));
EXPECT_EQ(card.getCounter(1), MAX_COUNTER_VALUE);
}
TEST(ServerCardCounter, DecrementUnderflowProtection)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, 5));
EXPECT_TRUE(card.incrementCounter(1, -10));
EXPECT_EQ(card.getCounter(1), 0);
EXPECT_FALSE(card.getCounters().contains(1));
}
TEST(ServerCardCounter, ReturnsFalseWhenUnchanged)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, 50));
EXPECT_FALSE(card.incrementCounter(1, 0));
EXPECT_EQ(card.getCounter(1), 50);
}
TEST(ServerCardCounter, DecrementToZeroRemovesCounter)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, 10));
EXPECT_TRUE(card.incrementCounter(1, -10));
EXPECT_EQ(card.getCounter(1), 0);
EXPECT_FALSE(card.getCounters().contains(1));
}
TEST(ServerCardCounter, SetToZeroRemovesCounter)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, 10));
EXPECT_TRUE(card.setCounter(1, 0));
EXPECT_EQ(card.getCounter(1), 0);
EXPECT_FALSE(card.getCounters().contains(1));
}
TEST(ServerCardCounter, SetCounterReturnsFalseWhenUnchanged)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, 50));
EXPECT_FALSE(card.setCounter(1, 50));
EXPECT_EQ(card.getCounter(1), 50);
}
TEST(ServerCardCounter, SetCounterReturnsTrueWhenChanged)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, 50));
EXPECT_TRUE(card.setCounter(1, 100));
EXPECT_EQ(card.getCounter(1), 100);
}
TEST(ServerCardCounter, SetCounterEventNotPopulatedWhenUnchanged)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, 50));
Event_SetCardCounter event;
event.set_counter_id(999);
event.set_counter_value(999);
EXPECT_FALSE(card.setCounter(1, 50, &event));
EXPECT_EQ(event.counter_id(), 999);
EXPECT_EQ(event.counter_value(), 999);
}
TEST(ServerCardCounter, IncrementCounterPopulatesEvent)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, 50));
Event_SetCardCounter event;
EXPECT_TRUE(card.incrementCounter(1, 10, &event));
EXPECT_EQ(event.counter_id(), 1);
EXPECT_EQ(event.counter_value(), 60);
}
TEST(ServerCardCounter, IncrementCounterEventReflectsClampedValue)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, MAX_COUNTER_VALUE - 5));
Event_SetCardCounter event;
EXPECT_TRUE(card.incrementCounter(1, 10, &event));
EXPECT_EQ(event.counter_id(), 1);
EXPECT_EQ(event.counter_value(), MAX_COUNTER_VALUE);
}
TEST(ServerCardCounter, IncrementCounterNoEventWhenNullptr)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, 50));
EXPECT_TRUE(card.incrementCounter(1, 10, nullptr));
EXPECT_EQ(card.getCounter(1), 60);
}
TEST(ServerCardCounter, IncrementCounterEventNotPopulatedWhenUnchanged)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, MAX_COUNTER_VALUE));
Event_SetCardCounter event;
event.set_counter_id(999);
event.set_counter_value(999);
EXPECT_FALSE(card.incrementCounter(1, 1, &event));
EXPECT_EQ(event.counter_id(), 999);
EXPECT_EQ(event.counter_value(), 999);
}
TEST(ServerCardCounter, SetCounterClampsNegativeToZero)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
EXPECT_FALSE(card.setCounter(1, -5));
EXPECT_EQ(card.getCounter(1), 0);
EXPECT_FALSE(card.getCounters().contains(1));
}
TEST(ServerCardCounter, SetCounterClampsAboveMaxToMax)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
EXPECT_TRUE(card.setCounter(1, 1500));
EXPECT_EQ(card.getCounter(1), MAX_COUNTER_VALUE);
}
TEST(ServerCardCounter, IncrementDoesNotGoBelowZero)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, 5));
EXPECT_TRUE(card.incrementCounter(1, -10));
EXPECT_EQ(card.getCounter(1), 0);
EXPECT_FALSE(card.getCounters().contains(1));
}
TEST(ServerCardCounter, IncrementDoesNotExceedMax)
{
Server_Card card(CardRef{"TestCard", ""}, 1, 0, 0);
ASSERT_TRUE(card.setCounter(1, MAX_COUNTER_VALUE - 5));
EXPECT_TRUE(card.incrementCounter(1, 10));
EXPECT_EQ(card.getCounter(1), MAX_COUNTER_VALUE);
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+86
View File
@@ -0,0 +1,86 @@
/** @file server_counter_test.cpp
* @brief Tests for Server_Counter operations.
* @ingroup Tests
*/
#include <gtest/gtest.h>
#include <libcockatrice/network/server/remote/game/server_counter.h>
#include <limits>
TEST(ServerCounter, IncrementDoesNotOverflow)
{
Server_Counter c(1, "test", color(), 10, std::numeric_limits<int>::max());
bool changed = c.incrementCount(1);
EXPECT_FALSE(changed);
EXPECT_EQ(c.getCount(), std::numeric_limits<int>::max());
}
TEST(ServerCounter, DecrementDoesNotUnderflow)
{
Server_Counter c(1, "test", color(), 10, std::numeric_limits<int>::min());
bool changed = c.incrementCount(-1);
EXPECT_FALSE(changed);
EXPECT_EQ(c.getCount(), std::numeric_limits<int>::min());
}
TEST(ServerCounter, SetCountReturnsFalseWhenUnchanged)
{
Server_Counter c(1, "test", color(), 10, 50);
bool changed = c.setCount(50);
EXPECT_FALSE(changed);
}
TEST(ServerCounter, IncrementReturnsChangeStatus)
{
Server_Counter c(1, "test", color(), 10, 50);
EXPECT_TRUE(c.incrementCount(10));
EXPECT_EQ(c.getCount(), 60);
EXPECT_FALSE(c.incrementCount(0));
EXPECT_EQ(c.getCount(), 60);
}
TEST(ServerCounter, LargePositiveDeltaDoesNotOverflow)
{
Server_Counter c(1, "test", color(), 10, std::numeric_limits<int>::max() - 10);
bool changed = c.incrementCount(std::numeric_limits<int>::max());
EXPECT_TRUE(changed); // Value changes from INT_MAX-10 to INT_MAX (clamped)
EXPECT_EQ(c.getCount(), std::numeric_limits<int>::max());
}
TEST(ServerCounter, LargeNegativeDeltaDoesNotUnderflow)
{
Server_Counter c(1, "test", color(), 10, std::numeric_limits<int>::min() + 10);
bool changed = c.incrementCount(std::numeric_limits<int>::min());
EXPECT_TRUE(changed); // Value changes from INT_MIN+10 to INT_MIN (clamped)
EXPECT_EQ(c.getCount(), std::numeric_limits<int>::min());
}
TEST(ServerCounter, SetCountReturnsTrueWhenChanged)
{
Server_Counter c(1, "test", color(), 10, 50);
EXPECT_TRUE(c.setCount(100));
EXPECT_EQ(c.getCount(), 100);
}
TEST(ServerCounter, BasicIncrementWorks)
{
Server_Counter c(1, "test", color(), 10, 50);
EXPECT_TRUE(c.incrementCount(10));
EXPECT_EQ(c.getCount(), 60);
EXPECT_TRUE(c.incrementCount(-20));
EXPECT_EQ(c.getCount(), 40);
}
TEST(ServerCounter, MixedExtremesDoNotClamp)
{
Server_Counter c(1, "test", color(), 10, std::numeric_limits<int>::max());
bool changed = c.incrementCount(std::numeric_limits<int>::min());
EXPECT_TRUE(changed);
EXPECT_EQ(c.getCount(), -1);
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+43
View File
@@ -0,0 +1,43 @@
#include "gtest/gtest.h"
#include <libcockatrice/utility/days_years_between.h>
namespace
{
using dayyear = QPair<int, int>;
TEST(AgeFormatting, Zero)
{
auto got = getDaysAndYearsBetween(QDate(2000, 1, 1), QDate(2000, 1, 1));
ASSERT_EQ(got, dayyear(0, 0)) << "these are the same day";
}
TEST(AgeFormatting, LeapDay)
{
auto got = getDaysAndYearsBetween(QDate(2000, 2, 28), QDate(2000, 3, 1));
ASSERT_EQ(got, dayyear(2, 0)) << "there is a leap day in between these days";
}
TEST(AgeFormatting, LeapYear)
{
auto got = getDaysAndYearsBetween(QDate(2000, 1, 1), QDate(2001, 1, 1));
ASSERT_EQ(got, dayyear(0, 1)) << "there is a leap day in between these dates, but that's fine";
}
TEST(AgeFormatting, LeapDayWithYear)
{
auto got = getDaysAndYearsBetween(QDate(2000, 2, 28), QDate(2001, 3, 1));
ASSERT_EQ(got, dayyear(1, 1)) << "there is a leap day in between these days but not in the last year";
}
TEST(AgeFormatting, LeapDayThisYear)
{
auto got = getDaysAndYearsBetween(QDate(2003, 2, 28), QDate(2004, 3, 1));
ASSERT_EQ(got, dayyear(2, 1)) << "there is a leap day in between these days this year";
}
} // namespace
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}