mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-13 13:30:00 -08:00
Merge pull request #8 from Continuum-Universes-Studio/feature/continuumatrice-product-migration
Feature/continuumatrice product migration
This commit is contained in:
commit
533d2f04c1
150 changed files with 5268 additions and 2563 deletions
|
|
@ -172,6 +172,13 @@ endfunction()
|
|||
# --- Unit tests ------------------------------------------------------------------
|
||||
|
||||
cockatrice_add_test(NAME expression_test SOURCES expression_test.cpp LIBS libcockatrice_utility)
|
||||
# XML v3/v4 exporters in libcockatrice_card reference VERSION_STRING.
|
||||
# Link the same generated version implementation used by the client/server.
|
||||
cockatrice_add_test(
|
||||
NAME essence_types_test
|
||||
SOURCES ${VERSION_STRING_CPP} essence_types_test.cpp
|
||||
LIBS libcockatrice_card
|
||||
)
|
||||
cockatrice_add_test(NAME clamped_arithmetic_test SOURCES clamped_arithmetic_test.cpp LIBS libcockatrice_utility)
|
||||
cockatrice_add_test(NAME test_age_formatting SOURCES test_age_formatting.cpp LIBS libcockatrice_utility)
|
||||
cockatrice_add_test(NAME user_data_backup_test SOURCES user_data_backup_test.cpp LIBS libcockatrice_utility)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <libcockatrice/card/card_info.h>
|
||||
#include <libcockatrice/card/database/card_database_data.h>
|
||||
#include <libcockatrice/card/database/parser/cockatrice_xml_4.h>
|
||||
#include <libcockatrice/card/game_specific_terms.h>
|
||||
#include <libcockatrice/card/lazy_properties_hash.h>
|
||||
#include <libcockatrice/card/printing/printing_info.h>
|
||||
#include <libcockatrice/card/relation/card_relation.h>
|
||||
|
|
@ -35,6 +36,7 @@
|
|||
#include <libcockatrice/interfaces/noop_card_set_priority_controller.h>
|
||||
#include <libcockatrice/utility/card_ref.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -65,6 +67,65 @@ TEST(CardDatabaseTest, LoadXml)
|
|||
ASSERT_EQ(NotLoaded, db->getLoadStatus()) << "Incorrect status after clear";
|
||||
}
|
||||
|
||||
TEST(CardDatabaseTest, EmptyLoadedSnapshotIsReadyNotLoading)
|
||||
{
|
||||
NoopCardPreferenceProvider prefs;
|
||||
TestCardDatabasePathProvider pathProvider;
|
||||
NoopCardSetPriorityController controller;
|
||||
CardDatabase db(nullptr, &prefs, &pathProvider, &controller);
|
||||
|
||||
EXPECT_EQ(NotLoaded, db.getLoadStatus());
|
||||
|
||||
// A newly bootstrapped Continuumatrice profile contains a valid, but empty,
|
||||
// XML v4 catalogue. Successful swap must not trap the visual browser on
|
||||
// "Loading database..." when there are zero cards.
|
||||
CardDatabaseData emptySnapshot;
|
||||
db.swapInDatabaseData(std::move(emptySnapshot));
|
||||
EXPECT_TRUE(db.getCardList().isEmpty());
|
||||
EXPECT_EQ(Ok, db.getLoadStatus());
|
||||
|
||||
db.clear();
|
||||
EXPECT_EQ(NotLoaded, db.getLoadStatus());
|
||||
}
|
||||
|
||||
TEST(CardInfoTest, ContinuumIdentityAndStatsHaveNativeAndLegacyPropertyAccess)
|
||||
{
|
||||
auto native = CardInfo::newInstance("Native Continuum Card");
|
||||
native->setProperty("identity", "The Shadow Queen");
|
||||
native->setProperty("attack", "10");
|
||||
native->setProperty("armour", "3");
|
||||
native->setProperty("health", "12");
|
||||
|
||||
EXPECT_EQ(native->getIdentity(), "The Shadow Queen");
|
||||
EXPECT_EQ(native->getAttack(), "10");
|
||||
EXPECT_EQ(native->getArmour(), "3");
|
||||
EXPECT_EQ(native->getHealth(), "12");
|
||||
EXPECT_EQ(native->getAtkHp(), "10/12");
|
||||
EXPECT_EQ(native->getPowTough(), "10/12"); // Legacy accessor remains useful.
|
||||
|
||||
auto legacyMse = CardInfo::newInstance("Legacy Continuum Card");
|
||||
legacyMse->setProperty("alias", "The Guardian");
|
||||
legacyMse->setProperty("strength", "9");
|
||||
legacyMse->setProperty("armor", "4");
|
||||
legacyMse->setProperty("health", "8");
|
||||
EXPECT_EQ(legacyMse->getIdentity(), "The Guardian");
|
||||
EXPECT_EQ(legacyMse->getAttack(), "9");
|
||||
EXPECT_EQ(legacyMse->getArmour(), "4");
|
||||
EXPECT_EQ(legacyMse->getHealth(), "8");
|
||||
EXPECT_EQ(legacyMse->getAtkHp(), "9/8");
|
||||
|
||||
auto oldPt = CardInfo::newInstance("Old Imported Card");
|
||||
oldPt->setProperty(Mtg::PowTough, "2/7");
|
||||
EXPECT_EQ(oldPt->getAttack(), "2");
|
||||
EXPECT_EQ(oldPt->getHealth(), "7");
|
||||
EXPECT_EQ(oldPt->getAtkHp(), "2/7");
|
||||
|
||||
// Missing values should not be represented as false zeroes.
|
||||
auto blank = CardInfo::newInstance("No Stats");
|
||||
EXPECT_TRUE(blank->getAtkHp().isEmpty());
|
||||
EXPECT_TRUE(blank->getArmour().isEmpty());
|
||||
}
|
||||
|
||||
TEST(CardDatabaseTest, LazyPropertiesHashStreamsBlobAccessors)
|
||||
{
|
||||
QHash<QString, QString> props;
|
||||
|
|
|
|||
83
tests/essence_types_test.cpp
Normal file
83
tests/essence_types_test.cpp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#include <gtest/gtest.h>
|
||||
#include <libcockatrice/card/card_info.h>
|
||||
#include <libcockatrice/card/essence_types.h>
|
||||
#include <libcockatrice/utility/essence_counter.h>
|
||||
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
|
||||
TEST(EssenceTypesTest, HasExactlySixteenCanonicalSymbols)
|
||||
{
|
||||
EXPECT_EQ(Essence::symbols(), QStringLiteral("MSOGEAQCJBRLVTPN"));
|
||||
EXPECT_EQ(Essence::symbols().size(), 16);
|
||||
EXPECT_TRUE(Essence::isSymbol(QChar('N'))); // N is colorless.
|
||||
EXPECT_TRUE(Essence::isSymbol(QChar('J'))); // J is Jasper, between C and B.
|
||||
EXPECT_FALSE(Essence::isSymbol(QChar('W'))); // MTG white is not Essence.
|
||||
EXPECT_FALSE(Essence::isSymbol(QChar('U'))); // MTG blue is not Essence.
|
||||
}
|
||||
|
||||
TEST(EssenceTypesTest, NetworkCounterNamesHaveStableRoundTrip)
|
||||
{
|
||||
EXPECT_EQ(Essence::symbols(), EssenceCounter::symbols());
|
||||
int position = 1; // Counter id 0 is the existing life counter.
|
||||
for (const QChar code : EssenceCounter::symbols()) {
|
||||
const QString networkName = EssenceCounter::name(code);
|
||||
EXPECT_TRUE(networkName.startsWith(QStringLiteral("essence:")));
|
||||
EXPECT_EQ(EssenceCounter::codeFromName(networkName), code);
|
||||
EXPECT_TRUE(EssenceCounter::isEssenceCounter(networkName));
|
||||
++position;
|
||||
}
|
||||
EXPECT_EQ(position, 17);
|
||||
EXPECT_TRUE(EssenceCounter::name(QChar('W')).isEmpty());
|
||||
EXPECT_FALSE(EssenceCounter::isEssenceCounter(QStringLiteral("essence:W")));
|
||||
EXPECT_FALSE(EssenceCounter::isEssenceCounter(QStringLiteral("essence:MM")));
|
||||
EXPECT_FALSE(EssenceCounter::isEssenceCounter(QStringLiteral("life")));
|
||||
EXPECT_EQ(EssenceCounter::clampBalance(-1), 0);
|
||||
EXPECT_EQ(EssenceCounter::clampBalance(-900), 0);
|
||||
EXPECT_EQ(EssenceCounter::clampBalance(0), 0);
|
||||
EXPECT_EQ(EssenceCounter::clampBalance(19), 19);
|
||||
}
|
||||
|
||||
TEST(EssenceTypesTest, ParsesAllBracedAndUnbracedTypes)
|
||||
{
|
||||
const QString raw = QStringLiteral("{M}{S}{O}{G}{E}{A}{Q}{C}{J}{B}{R}{L}{V}{T}{P}{N}");
|
||||
EXPECT_EQ(Essence::parseCost(raw).join(QString()), Essence::symbols());
|
||||
EXPECT_EQ(Essence::parseCost(QStringLiteral("MSN")).join(QString()), QStringLiteral("MSN"));
|
||||
EXPECT_EQ(Essence::parseCost(QStringLiteral("{C}{J}{B}{N}")).join(QString()), QStringLiteral("CJBN"));
|
||||
EXPECT_EQ(Essence::parseCost(QStringLiteral("{2}{N}{M}")).join(QStringLiteral("|")),
|
||||
QStringLiteral("2|N|M"));
|
||||
}
|
||||
|
||||
TEST(EssenceTypesTest, RejectsUnknownLegacyAndHybridCodes)
|
||||
{
|
||||
EXPECT_TRUE(Essence::parseCost(QStringLiteral("{W}{U}{Z}{M/N}")).isEmpty());
|
||||
EXPECT_EQ(Essence::parseCost(QStringLiteral("{N}")), QStringList{QStringLiteral("N")});
|
||||
}
|
||||
|
||||
TEST(EssenceTypesTest, NormalizesUniqueIdentityInCanonicalOrder)
|
||||
{
|
||||
EXPECT_EQ(Essence::normalizeIdentity(QStringLiteral("NSSMMO")), QStringLiteral("MSON"));
|
||||
EXPECT_EQ(Essence::normalizeIdentity(QStringLiteral("N")), QStringLiteral("N"));
|
||||
EXPECT_EQ(Essence::normalizeIdentity(QStringLiteral("BJNJC")), QStringLiteral("CJBN"));
|
||||
EXPECT_EQ(Essence::normalizeIdentity(QStringLiteral("WU")), QString());
|
||||
}
|
||||
|
||||
TEST(EssenceTypesTest, NativePropertiesTakePrecedenceOverLegacyFallbacks)
|
||||
{
|
||||
const QHash<QString, QString> props = {
|
||||
{QStringLiteral("essencecost"), QStringLiteral("{M}{N}")},
|
||||
{QStringLiteral("essence"), QStringLiteral("NM")},
|
||||
{QStringLiteral("essencevalue"), QStringLiteral("2")},
|
||||
{QStringLiteral("manacost"), QStringLiteral("{W}{U}")},
|
||||
{QStringLiteral("colors"), QStringLiteral("WU")},
|
||||
{QStringLiteral("cmc"), QStringLiteral("9")}
|
||||
};
|
||||
|
||||
const CardInfoPtr card = CardInfo::newInstance(QStringLiteral("Essence Test"),
|
||||
QString(), false, props, {}, {}, {}, {});
|
||||
ASSERT_TRUE(card);
|
||||
EXPECT_EQ(card->getManaCost(), QStringLiteral("{M}{N}"));
|
||||
EXPECT_EQ(card->getEssenceTypes(), QStringLiteral("MN"));
|
||||
EXPECT_EQ(card->getColors(), QStringLiteral("MN"));
|
||||
EXPECT_EQ(card->getCmc(), QStringLiteral("2"));
|
||||
}
|
||||
|
|
@ -103,6 +103,54 @@ TEST(ModelContract, CardDatabaseModelAndDisplayProxyKeepContract)
|
|||
EXPECT_LT(display.rowCount(), source.rowCount());
|
||||
}
|
||||
|
||||
TEST(ModelContract, ContinuumCardStatsAreSortableAndLabeled)
|
||||
{
|
||||
NoopCardPreferenceProvider prefs;
|
||||
TestCardDatabasePathProvider paths;
|
||||
NoopCardSetPriorityController setPriority;
|
||||
CardDatabase database(nullptr, &prefs, &paths, &setPriority);
|
||||
|
||||
auto swift = CardInfo::newInstance("Swift");
|
||||
swift->setProperty("identity", "Astra");
|
||||
swift->setProperty("attack", "9");
|
||||
swift->setProperty("armour", "10");
|
||||
swift->setProperty("health", "2");
|
||||
|
||||
auto sturdy = CardInfo::newInstance("Sturdy");
|
||||
sturdy->setProperty("identity", "Zephyr");
|
||||
sturdy->setProperty("attack", "10");
|
||||
sturdy->setProperty("armour", "2");
|
||||
sturdy->setProperty("health", "12");
|
||||
|
||||
database.addCard(swift);
|
||||
database.addCard(sturdy);
|
||||
|
||||
CardDatabaseModel source(&database, false);
|
||||
CardDatabaseDisplayModel display;
|
||||
display.setSourceModel(&source);
|
||||
|
||||
ASSERT_EQ(source.columnCount(), CardDatabaseModel::ColumnCount);
|
||||
ASSERT_EQ(display.rowCount(), 2);
|
||||
EXPECT_EQ(source.headerData(CardDatabaseModel::AtkHpColumn, Qt::Horizontal).toString(), "ATK/HP");
|
||||
EXPECT_EQ(source.headerData(CardDatabaseModel::IdentityColumn, Qt::Horizontal).toString(), "Identity");
|
||||
EXPECT_EQ(source.headerData(CardDatabaseModel::AttackColumn, Qt::Horizontal).toString(), "Attack");
|
||||
EXPECT_EQ(source.headerData(CardDatabaseModel::ArmourColumn, Qt::Horizontal).toString(), "Armour");
|
||||
EXPECT_EQ(source.headerData(CardDatabaseModel::HealthColumn, Qt::Horizontal).toString(), "Health");
|
||||
|
||||
const auto firstName = [&display] { return display.index(0, CardDatabaseModel::NameColumn).data().toString(); };
|
||||
|
||||
display.sort(CardDatabaseModel::AttackColumn, Qt::AscendingOrder);
|
||||
EXPECT_EQ(firstName(), "Swift") << "9 ATK should precede 10 ATK";
|
||||
display.sort(CardDatabaseModel::ArmourColumn, Qt::AscendingOrder);
|
||||
EXPECT_EQ(firstName(), "Sturdy") << "2 Armour should precede 10 Armour";
|
||||
display.sort(CardDatabaseModel::HealthColumn, Qt::AscendingOrder);
|
||||
EXPECT_EQ(firstName(), "Swift") << "2 HP should precede 12 HP";
|
||||
display.sort(CardDatabaseModel::AtkHpColumn, Qt::AscendingOrder);
|
||||
EXPECT_EQ(firstName(), "Swift") << "ATK/HP should sort attack and health numerically";
|
||||
display.sort(CardDatabaseModel::IdentityColumn, Qt::DescendingOrder);
|
||||
EXPECT_EQ(firstName(), "Sturdy") << "Identity should sort text from the alias/identity property";
|
||||
}
|
||||
|
||||
TEST(ModelContract, SetsModelKeepsContract)
|
||||
{
|
||||
CardDatabase database(nullptr, new NoopCardPreferenceProvider(), new TestCardDatabasePathProvider(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue