#include "gtest/gtest.h" #include #include #include #include #include #include namespace { DeckList makeDeck(std::initializer_list> mainCards, std::initializer_list> sideCards = {}) { DeckList deck; for (const auto &[name, count] : mainCards) { for (int i = 0; i < count; ++i) { deck.addCard(name, "main"); } } for (const auto &[name, count] : sideCards) { for (int i = 0; i < count; ++i) { deck.addCard(name, "side"); } } return deck; } int countOf(const DeckList &deck, const char *zone, const char *name) { int count = 0; deck.forEachCard([&](InnerDecklistNode *zoneNode, DecklistCardNode *card) { if (zoneNode->getName() == zone && card->getName() == name) { count += card->getNumber(); } }); return count; } } // namespace TEST(DeckDiffTest, IdenticalDecksHaveEmptyDiff) { DeckList first = makeDeck({{"Lightning Bolt", 4}}); DeckList second = makeDeck({{"Lightning Bolt", 4}}); EXPECT_TRUE(DeckDiff::diff(first, second).isEmpty()); } TEST(DeckDiffTest, ChangesAreSortedByZoneThenName) { DeckList oldDeck = makeDeck({{"Mountain", 20}, {"Lightning Bolt", 4}}, {{"Shock", 2}}); DeckList newDeck = makeDeck({{"Mountain", 19}, {"Shock", 5}}, {{"Shock", 1}, {"Teferi", 1}}); DeckDiffResult result = DeckDiff::diff(oldDeck, newDeck); ASSERT_EQ(result.changes.size(), 5); EXPECT_EQ(result.changes.at(0).zone, "main"); EXPECT_EQ(result.changes.at(0).cardName, "Lightning Bolt"); EXPECT_EQ(result.changes.at(1).cardName, "Mountain"); EXPECT_EQ(result.changes.at(2).cardName, "Shock"); EXPECT_EQ(result.changes.at(3).zone, "side"); EXPECT_EQ(result.changes.at(3).cardName, "Shock"); EXPECT_EQ(result.changes.at(4).cardName, "Teferi"); } TEST(DeckDiffTest, ReportsAddedAndRemovedCopies) { DeckList oldDeck = makeDeck({{"Lightning Bolt", 4}, {"Mountain", 20}}); DeckList newDeck = makeDeck({{"Lightning Bolt", 2}, {"Shock", 4}, {"Mountain", 20}}); DeckDiffResult result = DeckDiff::diff(oldDeck, newDeck); ASSERT_EQ(result.changes.size(), 2); EXPECT_EQ(result.changes.at(0).cardName, "Lightning Bolt"); EXPECT_EQ(result.changes.at(0).oldCount, 4); EXPECT_EQ(result.changes.at(0).newCount, 2); EXPECT_EQ(result.changes.at(0).delta(), -2); EXPECT_EQ(result.changes.at(1).cardName, "Shock"); EXPECT_EQ(result.changes.at(1).oldCount, 0); EXPECT_EQ(result.changes.at(1).newCount, 4); } TEST(DeckDiffTest, ZoneMoveIsTwoChanges) { DeckList oldDeck = makeDeck({{"Teferi", 1}}); DeckList newDeck = makeDeck({}, {{"Teferi", 1}}); DeckDiffResult result = DeckDiff::diff(oldDeck, newDeck); ASSERT_EQ(result.changes.size(), 2); EXPECT_EQ(result.changes.at(0).zone, "main"); EXPECT_EQ(result.changes.at(0).newCount, 0); EXPECT_EQ(result.changes.at(1).zone, "side"); EXPECT_EQ(result.changes.at(1).oldCount, 0); } TEST(DeckDiffTest, DisjointChangesCanAutoMerge) { DeckList base = makeDeck({{"Lightning Bolt", 4}, {"Mountain", 20}}); DeckList local = makeDeck({{"Lightning Bolt", 4}, {"Mountain", 18}}); DeckList remote = makeDeck({{"Shock", 2}, {"Lightning Bolt", 4}, {"Mountain", 20}}); DeckDiffResult localChanges = DeckDiff::diff(base, local); DeckDiffResult remoteChanges = DeckDiff::diff(base, remote); EXPECT_TRUE(DeckDiff::canAutoMerge(localChanges, remoteChanges)); DeckList merged = DeckDiff::merge(base, localChanges, remoteChanges); EXPECT_EQ(countOf(merged, "main", "Mountain"), 18); EXPECT_EQ(countOf(merged, "main", "Shock"), 2); EXPECT_EQ(countOf(merged, "main", "Lightning Bolt"), 4); } TEST(DeckDiffTest, OverlappingChangesCannotAutoMergeAndRemoteWins) { DeckList base = makeDeck({{"Lightning Bolt", 4}}); DeckList local = makeDeck({{"Lightning Bolt", 3}}); DeckList remote = makeDeck({{"Lightning Bolt", 1}}); DeckDiffResult localChanges = DeckDiff::diff(base, local); DeckDiffResult remoteChanges = DeckDiff::diff(base, remote); EXPECT_FALSE(DeckDiff::canAutoMerge(localChanges, remoteChanges)); DeckList merged = DeckDiff::merge(base, localChanges, remoteChanges); EXPECT_EQ(countOf(merged, "main", "Lightning Bolt"), 1); } TEST(DeckDiffTest, MergeKeepsBaseMetadata) { DeckList base = makeDeck({{"Lightning Bolt", 4}}); base.setName("Affinity v3"); DeckList remote = makeDeck({{"Lightning Bolt", 4}, {"Shock", 1}}); DeckList merged = DeckDiff::merge(base, DeckDiff::diff(base, base), DeckDiff::diff(base, remote)); EXPECT_EQ(merged.getName(), "Affinity v3"); EXPECT_EQ(countOf(merged, "main", "Shock"), 1); } int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }