From 2cf005f9712f545caeecd79ddb363fd1857f8d53 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 18 Sep 2021 14:03:54 +0400 Subject: [PATCH] Tests: improved performance in game tests; --- .../test/serverside/base/MageTestBase.java | 28 +++++---- .../serverside/base/MageTestPlayerBase.java | 16 +++-- .../base/impl/CardTestPlayerAPIImpl.java | 8 +-- Mage/src/main/java/mage/cards/decks/Deck.java | 59 +++++++++++++++---- .../java/mage/cards/decks/DeckCardInfo.java | 19 +++++- .../java/mage/cards/decks/DeckCardLayout.java | 27 ++++++++- 6 files changed, 123 insertions(+), 34 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java index 8fa7443227..996948230d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java @@ -102,21 +102,25 @@ public abstract class MageTestBase { @BeforeClass public static void init() { Logger.getRootLogger().setLevel(Level.DEBUG); - deleteSavedGames(); - ConfigSettings config = new ConfigWrapper(ConfigFactory.loadFromFile("config/config.xml")); - config.getGameTypes().forEach((gameType) -> { - GameFactory.instance.addGameType(gameType.getName(), loadGameType(gameType), loadPlugin(gameType)); - }); - config.getTournamentTypes().forEach((tournamentType) -> { - TournamentFactory.instance.addTournamentType(tournamentType.getName(), loadTournamentType(tournamentType), loadPlugin(tournamentType)); - }); - config.getPlayerTypes().forEach((playerType) -> { - PlayerFactory.instance.addPlayerType(playerType.getName(), loadPlugin(playerType)); - }); + + // one time init for all tests + if (GameFactory.instance.getGameTypes().isEmpty()) { + deleteSavedGames(); + ConfigSettings config = new ConfigWrapper(ConfigFactory.loadFromFile("config/config.xml")); + config.getGameTypes().forEach((gameType) -> { + GameFactory.instance.addGameType(gameType.getName(), loadGameType(gameType), loadPlugin(gameType)); + }); + config.getTournamentTypes().forEach((tournamentType) -> { + TournamentFactory.instance.addTournamentType(tournamentType.getName(), loadTournamentType(tournamentType), loadPlugin(tournamentType)); + }); + config.getPlayerTypes().forEach((playerType) -> { + PlayerFactory.instance.addPlayerType(playerType.getName(), loadPlugin(playerType)); + }); // for (Plugin plugin : config.getDeckTypes()) { // DeckValidatorFactory.getInstance().addDeckType(plugin.getName(), loadPlugin(plugin)); // } - Copier.setLoader(classLoader); + Copier.setLoader(classLoader); + } } @SuppressWarnings("UseSpecificCatch") diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java index 2db1d1b67f..864103cd90 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java @@ -81,7 +81,8 @@ public abstract class MageTestPlayerBase { protected Map> commands = new HashMap<>(); - protected static Map loadedDeckCardLists = new HashMap<>(); // test decks buffer + protected static Map loadedDecks = new HashMap<>(); // deck's cache + protected static Map loadedCardInfo = new HashMap<>(); // db card's cache protected TestPlayer playerA; protected TestPlayer playerB; @@ -131,12 +132,15 @@ public abstract class MageTestPlayerBase { logger.debug("Logging level: " + logger.getLevel()); logger.debug("Default charset: " + Charset.defaultCharset()); - deleteSavedGames(); - ConfigSettings config = new ConfigWrapper(ConfigFactory.loadFromFile("config/config.xml")); - for (GamePlugin plugin : config.getGameTypes()) { - GameFactory.instance.addGameType(plugin.getName(), loadGameType(plugin), loadPlugin(plugin)); + // one time init for all tests + if (GameFactory.instance.getGameTypes().isEmpty()) { + deleteSavedGames(); + ConfigSettings config = new ConfigWrapper(ConfigFactory.loadFromFile("config/config.xml")); + for (GamePlugin plugin : config.getGameTypes()) { + GameFactory.instance.addGameType(plugin.getName(), loadGameType(plugin), loadPlugin(plugin)); + } + Copier.setLoader(classLoader); } - Copier.setLoader(classLoader); } private static Class loadPlugin(Plugin plugin) { diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index ae6a38570e..02e7ae5a01 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -259,13 +259,13 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement logger.debug("Loading deck..."); DeckCardLists list; - if (loadedDeckCardLists.containsKey(deckName)) { - list = loadedDeckCardLists.get(deckName); + if (loadedDecks.containsKey(deckName)) { + list = loadedDecks.get(deckName); } else { list = DeckImporter.importDeckFromFile(deckName, true); - loadedDeckCardLists.put(deckName, list); + loadedDecks.put(deckName, list); } - Deck deck = Deck.load(list, false, false); + Deck deck = Deck.load(list, false, false, loadedCardInfo); logger.debug("Done!"); if (deck.getCards().size() < 40) { throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size()); diff --git a/Mage/src/main/java/mage/cards/decks/Deck.java b/Mage/src/main/java/mage/cards/decks/Deck.java index 1a5dd01bb2..0869c68d39 100644 --- a/Mage/src/main/java/mage/cards/decks/Deck.java +++ b/Mage/src/main/java/mage/cards/decks/Deck.java @@ -4,13 +4,17 @@ import mage.cards.Card; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; import mage.game.GameException; +import mage.util.Copyable; import mage.util.DeckUtil; import org.apache.log4j.Logger; import java.io.Serializable; import java.util.*; +import java.util.stream.Collectors; -public class Deck implements Serializable { +public class Deck implements Serializable, Copyable { + + static final int MAX_CARDS_PER_DECK = 1000; private String name; private final Set cards = new LinkedHashSet<>(); @@ -20,6 +24,20 @@ public class Deck implements Serializable { private long deckHashCode = 0; private long deckCompleteHashCode = 0; + public Deck() { + super(); + } + + public Deck(final Deck deck) { + this.name = deck.name; + this.cards.addAll(deck.cards.stream().map(Card::copy).collect(Collectors.toList())); + this.sideboard.addAll(deck.sideboard.stream().map(Card::copy).collect(Collectors.toList())); + this.cardsLayout = deck.cardsLayout == null ? null : deck.cardsLayout.copy(); + this.sideboardLayout = deck.sideboardLayout == null ? null : deck.sideboardLayout.copy(); + this.deckHashCode = deck.deckHashCode; + this.deckCompleteHashCode = deck.deckCompleteHashCode; + } + public static Deck load(DeckCardLists deckCardLists) throws GameException { return Deck.load(deckCardLists, false); } @@ -51,6 +69,10 @@ public class Deck implements Serializable { return currentDeck; } + public static Deck load(DeckCardLists deckCardLists, boolean ignoreErrors, boolean mockCards) throws GameException { + return load(deckCardLists, ignoreErrors, mockCards, null); + } + /** * Warning, AI can't play Mock cards, so call it with extra params in real games or tests * @@ -60,17 +82,17 @@ public class Deck implements Serializable { * @return * @throws GameException */ - public static Deck load(DeckCardLists deckCardLists, boolean ignoreErrors, boolean mockCards) throws GameException { + public static Deck load(DeckCardLists deckCardLists, boolean ignoreErrors, boolean mockCards, Map cardInfoCache) throws GameException { Deck deck = new Deck(); deck.setName(deckCardLists.getName()); - deck.cardsLayout = deckCardLists.getCardLayout(); - deck.sideboardLayout = deckCardLists.getSideboardLayout(); + deck.cardsLayout = deckCardLists.getCardLayout() == null ? null : deckCardLists.getCardLayout().copy(); + deck.sideboardLayout = deckCardLists.getSideboardLayout() == null ? null : deckCardLists.getSideboardLayout().copy(); List deckCardNames = new ArrayList<>(); int totalCards = 0; for (DeckCardInfo deckCardInfo : deckCardLists.getCards()) { - Card card = createCard(deckCardInfo, mockCards); + Card card = createCard(deckCardInfo, mockCards, cardInfoCache); if (card != null) { - if (totalCards > 1000) { + if (totalCards > MAX_CARDS_PER_DECK) { break; } deck.cards.add(card); @@ -83,9 +105,9 @@ public class Deck implements Serializable { } List sbCardNames = new ArrayList<>(); for (DeckCardInfo deckCardInfo : deckCardLists.getSideboard()) { - Card card = createCard(deckCardInfo, mockCards); + Card card = createCard(deckCardInfo, mockCards, cardInfoCache); if (card != null) { - if (totalCards > 1000) { + if (totalCards > MAX_CARDS_PER_DECK) { break; } deck.sideboard.add(card); @@ -127,8 +149,21 @@ public class Deck implements Serializable { } - private static Card createCard(DeckCardInfo deckCardInfo, boolean mockCards) { - CardInfo cardInfo = CardRepository.instance.findCard(deckCardInfo.getSetCode(), deckCardInfo.getCardNum()); + private static Card createCard(DeckCardInfo deckCardInfo, boolean mockCards, Map cardInfoCache) { + CardInfo cardInfo; + if (cardInfoCache != null) { + // from cache + String key = String.format("%s_%s", deckCardInfo.getSetCode(), deckCardInfo.getCardNum()); + cardInfo = cardInfoCache.getOrDefault(key, null); + if (cardInfo == null) { + cardInfo = CardRepository.instance.findCard(deckCardInfo.getSetCode(), deckCardInfo.getCardNum()); + cardInfoCache.put(key, cardInfo); + } + } else { + // from db + cardInfo = CardRepository.instance.findCard(deckCardInfo.getSetCode(), deckCardInfo.getCardNum()); + } + if (cardInfo == null) { return null; } @@ -226,4 +261,8 @@ public class Deck implements Serializable { this.sideboardLayout = null; } + @Override + public Deck copy() { + return new Deck(this); + } } diff --git a/Mage/src/main/java/mage/cards/decks/DeckCardInfo.java b/Mage/src/main/java/mage/cards/decks/DeckCardInfo.java index 991af94fd0..6423805e31 100644 --- a/Mage/src/main/java/mage/cards/decks/DeckCardInfo.java +++ b/Mage/src/main/java/mage/cards/decks/DeckCardInfo.java @@ -2,19 +2,32 @@ package mage.cards.decks; +import mage.util.Copyable; + import java.io.Serializable; /** * * @author LevelX2 */ -public class DeckCardInfo implements Serializable { +public class DeckCardInfo implements Serializable, Copyable { private String cardName; private String setCode; private String cardNum; private int quantity; + public DeckCardInfo() { + super(); + } + + public DeckCardInfo(final DeckCardInfo info) { + this.cardName = info.cardName; + this.setCode = info.setCode; + this.cardNum = info.cardNum; + this.quantity = info.quantity; + } + public DeckCardInfo(String cardName, String cardNum, String setCode) { this(cardName, cardNum, setCode, 1); } @@ -51,4 +64,8 @@ public class DeckCardInfo implements Serializable { return setCode + cardNum; } + @Override + public DeckCardInfo copy() { + return new DeckCardInfo(this); + } } diff --git a/Mage/src/main/java/mage/cards/decks/DeckCardLayout.java b/Mage/src/main/java/mage/cards/decks/DeckCardLayout.java index eaf6dc4da9..9bf9645f19 100644 --- a/Mage/src/main/java/mage/cards/decks/DeckCardLayout.java +++ b/Mage/src/main/java/mage/cards/decks/DeckCardLayout.java @@ -1,15 +1,35 @@ package mage.cards.decks; +import mage.util.Copyable; + +import java.util.ArrayList; import java.util.List; /** * Created by stravant@gmail.com on 2016-10-03. */ -public class DeckCardLayout { +public class DeckCardLayout implements Copyable { private final List>> cards; private final String settings; + public DeckCardLayout(final DeckCardLayout layout) { + this.cards = new ArrayList<>(); + for (int i1 = 0; i1 < layout.cards.size(); i1++) { + List> list1 = new ArrayList<>(); + this.cards.add(list1); + for (int i2 = 0; i2 < layout.cards.get(i1).size(); i2++) { + List list2 = new ArrayList<>(); + list1.add(list2); + for (int i3 = 0; i3 < layout.cards.get(i1).get(i2).size(); i3++) { + DeckCardInfo info = layout.cards.get(i1).get(i2).get(i3); + list2.add(info.copy()); + } + } + } + this.settings = layout.settings; + } + public DeckCardLayout(List>> cards, String settings) { this.cards = cards; this.settings = settings; @@ -22,4 +42,9 @@ public class DeckCardLayout { public String getSettings() { return settings; } + + @Override + public DeckCardLayout copy() { + return new DeckCardLayout(this); + } }