Merge branch 'patch-carddb-performance' into 'master'

CardDb Optimisations

See merge request core-developers/forge!5258
This commit is contained in:
Michael Kamensky
2021-08-27 04:20:40 +00:00
15 changed files with 324 additions and 147 deletions

View File

@@ -2,13 +2,11 @@ package forge;
import forge.item.PaperCard;
import forge.util.FileUtil;
import forge.util.ImageUtil;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
public final class ImageKeys {
public static final String CARD_PREFIX = "c:";
@@ -68,9 +66,8 @@ public final class ImageKeys {
}
public static File getImageFile(String key) {
if (StringUtils.isEmpty(key)) {
if (StringUtils.isEmpty(key))
return null;
}
final String dir;
final String filename;
@@ -220,14 +217,35 @@ public final class ImageKeys {
//shortcut for determining if a card image exists for a given card
//should only be called from PaperCard.hasImage()
static HashMap<String, HashSet<String>> cachedContent=new HashMap<>();
public static boolean hasImage(PaperCard pc) {
Boolean editionHasImage = editionImageLookup.get(pc.getEdition());
if (editionHasImage == null) {
String setFolder = getSetFolder(pc.getEdition());
editionHasImage = FileUtil.isDirectoryWithFiles(CACHE_CARD_PICS_DIR + setFolder);
editionImageLookup.put(pc.getEdition(), editionHasImage);
if (editionHasImage){
File f = new File(CACHE_CARD_PICS_DIR + setFolder); // no need to check this, otherwise editionHasImage would be false!
HashSet<String> setFolderContent = new HashSet<>();
for (String filename : Arrays.asList(f.list())) {
// TODO: should this use FILE_EXTENSIONS ?
if (!filename.endsWith(".jpg") && !filename.endsWith(".png"))
continue; // not image - not interested
setFolderContent.add(filename.split("\\.")[0]); // get rid of any full or fullborder
}
cachedContent.put(setFolder, setFolderContent);
}
}
String[] keyParts = pc.getImageKeyFromSet().split(File.separator);
HashSet<String> content = cachedContent.getOrDefault(keyParts[0], null);
//avoid checking for file if edition doesn't have any images
return editionHasImage && getImageFile(ImageUtil.getImageKey(pc, false, true)) != null;
return editionHasImage && hitCache(content, keyParts[1]);
}
private static boolean hitCache(HashSet<String> cache, String filename){
if (cache == null || cache.isEmpty())
return false;
final String keyPrefix = filename.split("\\.")[0];
return cache.contains(keyPrefix);
}
}

View File

@@ -90,6 +90,12 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
this.collectorNumber = collectorNumber;
}
public static String compose(String cardName, boolean isFoil){
if (isFoil)
return cardName+foilSuffix;
return cardName;
}
public static String compose(String cardName, String setCode) {
setCode = setCode != null ? setCode : "";
cardName = cardName != null ? cardName : "";
@@ -135,7 +141,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
}
private static boolean isArtIndex(String s) {
return StringUtils.isNumeric(s) && s.length() == 1;
return StringUtils.isNumeric(s) && s.length() <= 2 ; // only artIndex between 1-99
}
private static boolean isSetCode(String s) {
@@ -462,15 +468,15 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
}
// 2. Card lookup in edition with specified filter didn't work.
// So now check whether the cards exists in the DB first,
// So now check whether the cards exist in the DB first,
// and select pick the card based on current SetPreference policy as a fallback
Collection<PaperCard> cards = getAllCards(request.cardName);
if (cards == null)
if (cards.isEmpty()) // Never null being this a view in MultiMap
return null;
// Either No Edition has been specified OR as a fallback in case of any error!
// get card using the default card art preference
result = getCardFromEditions(request.cardName, this.defaultCardArtPreference, request.artIndex);
return result != null && request.isFoil ? result.getFoiled() : result;
String cardRequest = CardRequest.compose(request.cardName, request.isFoil);
return getCardFromEditions(cardRequest, this.defaultCardArtPreference, request.artIndex);
}
/*
@@ -510,9 +516,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
cardName = cardNameRequest.cardName;
isFoil = isFoil || cardNameRequest.isFoil;
List<PaperCard> cards = getAllCards(cardName);
// Look for Code or Code2 to make the retrieval more robust
List<PaperCard> candidates = Lists.newArrayList(Iterables.filter(cards, new Predicate<PaperCard>() {
List<PaperCard> candidates = getAllCards(cardName, new Predicate<PaperCard>() {
@Override
public boolean apply(PaperCard c) {
boolean artIndexFilter = true;
@@ -526,21 +530,17 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
collectorNumberFilter = (c.getCollectorNumber().equals(collectorNumber));
return setFilter && artIndexFilter && collectorNumberFilter;
}
}));
});
if (candidates.isEmpty())
return null;
PaperCard candidate = candidates.get(0);
Iterator<PaperCard> candidatesIterator = candidates.iterator();
PaperCard candidate = candidatesIterator.next();
// Before returning make sure that actual candidate has Image.
// If not, try to replace current candidate with one having image,
// so to align this implementation with old one.
if (!candidate.hasImage()) {
for (PaperCard card : candidates) {
if (card.hasImage()) {
candidate = card;
break; // found, ready to go
}
}
while (!candidate.hasImage() && candidatesIterator.hasNext()) {
candidate = candidatesIterator.next();
}
return isFoil ? candidate.getFoiled() : candidate;
}
@@ -567,8 +567,8 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
}
@Override
public PaperCard getCardFromEditions(final String cardName, final CardArtPreference artPreference, int artIndex) {
return this.tryToGetCardFromEditions(cardName, artPreference, artIndex);
public PaperCard getCardFromEditions(final String cardInfo, final CardArtPreference artPreference, int artIndex) {
return this.tryToGetCardFromEditions(cardInfo, artPreference, artIndex);
}
/*
@@ -634,38 +634,52 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
if (cr.artIndex != artIndex && artIndex > IPaperCard.DEFAULT_ART_INDEX )
cr.artIndex = artIndex; // 2nd cond. is to verify that some actual value has been passed in.
List<PaperCard> cards = getAllCards(cr.cardName);
List<PaperCard> cards;
if (releaseDate != null) {
cards = Lists.newArrayList(Iterables.filter(cards, new Predicate<PaperCard>() {
cards = getAllCards(cr.cardName, new Predicate<PaperCard>() {
@Override
public boolean apply(PaperCard c) {
if (c.getArtIndex() != cr.artIndex)
return false; // not interested anyway!
CardEdition ed = editions.get(c.getEdition());
if (ed == null) return false;
if (releasedBeforeFlag)
return ed.getDate().before(releaseDate);
else
return ed.getDate().after(releaseDate);
}
}));
}
});
} else // filter candidates based on requested artIndex
cards = getAllCards(cr.cardName, new Predicate<PaperCard>() {
@Override
public boolean apply(PaperCard card) {
return card.getArtIndex() == cr.artIndex;
}
});
if (cards.size() == 0) // Don't bother continuing! No card has been found!
return null;
if (cards.size() == 1) // if only one candidate, there much else we should do
return cr.isFoil ? cards.get(0).getFoiled() : cards.get(0);
/* 2. Retrieve cards based of [Frame]Set Preference
================================================ */
// Collect the list of all editions found for target card
LinkedHashSet<CardEdition> cardEditions = new LinkedHashSet<>();
List<CardEdition> cardEditions = new ArrayList<>();
Map<String, PaperCard> candidatesCard = new HashMap<>();
for (PaperCard card : cards) {
String setCode = card.getEdition();
CardEdition ed;
if (setCode.equals(CardEdition.UNKNOWN.getCode()))
cardEditions.add(CardEdition.UNKNOWN);
else {
CardEdition ed = editions.get(card.getEdition());
if (ed != null)
cardEditions.add(ed);
ed = CardEdition.UNKNOWN;
else
ed = editions.get(card.getEdition());
if (ed != null) {
cardEditions.add(ed);
candidatesCard.put(setCode, card);
}
}
if (cardEditions.isEmpty())
return null; // nothing to do
// Filter Cards Editions based on set preferences
List<CardEdition> acceptedEditions = Lists.newArrayList(Iterables.filter(cardEditions, new Predicate<CardEdition>() {
@Override
@@ -681,26 +695,24 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
If this happens, we won't try to iterate over an empty list. Instead, we will fall back
to original lists of editions (unfiltered, of course) AND STILL sorted according to chosen art preference.
*/
if (acceptedEditions.size() == 0)
if (acceptedEditions.isEmpty())
acceptedEditions.addAll(cardEditions);
Collections.sort(acceptedEditions); // CardEdition correctly sort by (release) date
if (artPref.latestFirst)
Collections.reverse(acceptedEditions); // newest editions first
PaperCard candidate = null;
for (CardEdition ed : acceptedEditions) {
PaperCard cardFromSet = getCardFromSet(cr.cardName, ed, cr.artIndex, cr.isFoil);
if (candidate == null && cardFromSet != null)
// save the first card found, as the last backup in case no other candidate *with image* will be found
candidate = cardFromSet;
if (cardFromSet != null && cardFromSet.hasImage()) {
candidate = cardFromSet;
break; // we're done here: found card **with Image**
}
if (acceptedEditions.size() > 1) {
Collections.sort(acceptedEditions); // CardEdition correctly sort by (release) date
if (artPref.latestFirst)
Collections.reverse(acceptedEditions); // newest editions first
}
//If any, we're sure that at least one candidate is always returned nevertheless it has image or not
return candidate; // any foil request already handled in getCardFromSet
final Iterator<CardEdition> editionIterator = acceptedEditions.iterator();
CardEdition ed = editionIterator.next();
PaperCard candidate = candidatesCard.get(ed.getCode());
while (!candidate.hasImage() && editionIterator.hasNext()) {
ed = editionIterator.next();
candidate = candidatesCard.get(ed.getCode());
}
//If any, we're sure that at least one candidate is always returned despite it having any image
return cr.isFoil ? candidate.getFoiled() : candidate;
}
@Override
@@ -716,18 +728,16 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
}
@Override
public int getArtCount(String cardName, String setName) {
if (cardName == null || setName == null)
public int getArtCount(String cardName, String setCode) {
if (cardName == null || setCode == null)
return 0;
Collection<PaperCard> cards = getAllCards(cardName);
if (null == cards || cards.size() == 0)
return 0;
int artCount = 0;
for (PaperCard pc : cards) {
if (pc.getEdition().equalsIgnoreCase(setName))
artCount++;
}
return artCount;
Collection<PaperCard> cardsInSet = getAllCards(cardName, new Predicate<PaperCard>() {
@Override
public boolean apply(PaperCard card) {
return card.getEdition().equalsIgnoreCase(setCode);
}
});
return cardsInSet.size();
}
// returns a list of all cards from their respective latest (or preferred) editions
@@ -834,6 +844,11 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
return Lists.newArrayList(Iterables.filter(getAllCards(), predicate));
}
@Override
public List<PaperCard> getAllCards(final String cardName, Predicate<PaperCard> predicate){
return Lists.newArrayList(Iterables.filter(getAllCards(cardName), predicate));
}
/**
* Returns a modifiable list of cards matching the given predicate
*/
@@ -1006,29 +1021,33 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
// 1. generate all paper cards from edition data we have (either explicit, or found in res/editions, or add to unknown edition)
List<PaperCard> paperCards = new ArrayList<>();
if (null == whenItWasPrinted || whenItWasPrinted.isEmpty()) {
// TODO Not performant Each time we "putCard" we loop through ALL CARDS IN ALL editions
// @friarsol: Not performant Each time we "putCard" we loop through ALL CARDS IN ALL editions
// @leriomaggio: DONE! re-using here the same strategy implemented for lazy-loading!
for (CardEdition e : editions.getOrderedEditions()) {
int artIdx = IPaperCard.DEFAULT_ART_INDEX;
for (CardInSet cis : e.getAllCardsInSet()) {
if (!cis.name.equals(cardName)) {
continue;
}
paperCards.add(new PaperCard(rules, e.getCode(), cis.rarity, artIdx++));
}
for (CardInSet cis : e.getCardInSet(cardName))
paperCards.add(new PaperCard(rules, e.getCode(), cis.rarity, artIdx++, false,
cis.collectorNumber, cis.artistName));
}
} else {
String lastEdition = null;
int artIdx = 0;
for (Pair<String, CardRarity> tuple : whenItWasPrinted) {
if (!tuple.getKey().equals(lastEdition)) {
artIdx = IPaperCard.DEFAULT_ART_INDEX;
artIdx = IPaperCard.DEFAULT_ART_INDEX; // reset artIndex
lastEdition = tuple.getKey();
}
CardEdition ed = editions.get(lastEdition);
if (null == ed) {
if (ed == null) {
continue;
}
paperCards.add(new PaperCard(rules, lastEdition, tuple.getValue(), artIdx++));
List<CardInSet> cardsInSet = ed.getCardInSet(cardName);
if (cardsInSet.isEmpty())
continue;
int cardInSetIndex = Math.max(artIdx-1, 0); // make sure doesn't go below zero
CardInSet cds = cardsInSet.get(cardInSetIndex); // use ArtIndex to get the right Coll. Number
paperCards.add(new PaperCard(rules, lastEdition, tuple.getValue(), artIdx++, false,
cds.collectorNumber, cds.artistName));
}
}
if (paperCards.isEmpty()) {

View File

@@ -373,6 +373,13 @@ public final class CardEdition implements Comparable<CardEdition> {
}
private ListMultimap<String, CardInSet> cardsInSetLookupMap = null;
/**
* Get all the CardInSet instances with the input card name.
* @param cardName Name of the Card to look for.
* @return A List of all the CardInSet instances for a given name.
* If not fount, an Empty sequence (view) will be returned instead!
*/
public List<CardInSet> getCardInSet(String cardName){
if (cardsInSetLookupMap == null) {
// initialise

View File

@@ -79,6 +79,7 @@ public interface ICardDatabase extends Iterable<PaperCard> {
Collection<PaperCard> getAllCards();
Collection<PaperCard> getAllCards(String cardName);
Collection<PaperCard> getAllCards(Predicate<PaperCard> predicate);
Collection<PaperCard> getAllCards(String cardName,Predicate<PaperCard> predicate);
Collection<PaperCard> getAllCards(CardEdition edition);
Collection<PaperCard> getUniqueCards();

View File

@@ -20,6 +20,7 @@ public interface IPaperCard extends InventoryItem, Serializable {
String NO_COLLECTOR_NUMBER = "N.A."; // Placeholder for No-Collection number available
int DEFAULT_ART_INDEX = 1;
int NO_ART_INDEX = -1; // Placeholder when NO ArtIndex is Specified
String NO_ARTIST_NAME = "";
/**
* Number of filters based on CardPrinted values.

View File

@@ -25,6 +25,7 @@ import forge.card.CardEdition;
import forge.card.CardRarity;
import forge.card.CardRules;
import forge.util.CardTranslation;
import forge.util.ImageUtil;
import forge.util.Localizer;
import forge.util.TextUtil;
@@ -52,7 +53,7 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
By default the attribute is marked as "unset" so that it could be retrieved and set.
(see getCollectorNumber())
*/
private String collectorNumber = null;
private String collectorNumber;
private final String artist;
private final int artIndex;
private final boolean foil;
@@ -75,18 +76,6 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
@Override
public String getCollectorNumber() {
/* The collectorNumber attribute is managed in a property-like fashion.
By default it is marked as "unset" (-1), which integrates with all constructors
invocations not including this as an extra parameter. In this way, the new
attribute could be added to the API with minimum disruption to code
throughout the other packages.
If "unset", the corresponding collectorNumber will be retrieved
from the corresponding CardEdition (see retrieveCollectorNumber)
* */
if (collectorNumber == null) {
collectorNumber = this.retrieveCollectorNumber();
}
return collectorNumber;
}
@@ -152,6 +141,13 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
return hasImage;
}
private String imageKeyFromSet = null;
public String getImageKeyFromSet() {
if (this.imageKeyFromSet == null)
this.imageKeyFromSet = ImageUtil.getImageKey(this, false, true);
return imageKeyFromSet;
}
/**
* Lambda to get rules for selects from list of printed cards.
*/
@@ -169,15 +165,12 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
};
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0){
this(rules0, edition0, rarity0, IPaperCard.DEFAULT_ART_INDEX);
this(rules0, edition0, rarity0, IPaperCard.DEFAULT_ART_INDEX, false,
IPaperCard.NO_COLLECTOR_NUMBER, IPaperCard.NO_ARTIST_NAME);
}
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0, final int artIndex0) {
this(rules0, edition0, rarity0, artIndex0, false, "");
}
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0, final int artIndex0,
final boolean foil0, final String artist0) {
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0,
final int artIndex0, final boolean foil0, final String collectorNumber0, final String artist0) {
if (rules0 == null || edition0 == null || rarity0 == null) {
throw new IllegalArgumentException("Cannot create card without rules, edition or rarity");
}
@@ -187,16 +180,8 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
artIndex = Math.max(artIndex0, IPaperCard.DEFAULT_ART_INDEX);
foil = foil0;
rarity = rarity0;
artist = (artist0 != null ? artist0 : "");
}
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0,
final int artIndex0, final boolean foil0, final String collectorNumber0, final String artist) {
this(rules0, edition0, rarity0, artIndex0, foil0, artist);
if ((collectorNumber0 == null) || (collectorNumber0.length() == 0))
collectorNumber = IPaperCard.NO_COLLECTOR_NUMBER;
else
collectorNumber = collectorNumber0;
artist = (artist0 != null ? artist0 : IPaperCard.NO_ARTIST_NAME);
collectorNumber = (collectorNumber0 != null) && (collectorNumber0.length() > 0) ? collectorNumber0 : IPaperCard.NO_COLLECTOR_NUMBER;
}
// Want this class to be a key for HashTable
@@ -268,10 +253,14 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
return CardEdition.CardInSet.getSortableCollectorNumber(collectorNumber);
}
private String sortableCNKey = null;
public String getCollectorNumberSortingKey(){
// Hardly the case, but just invoke getter rather than direct
// attribute to be sure that collectorNumber has been retrieved already!
return makeCollectorNumberSortingKey(getCollectorNumber());
if (sortableCNKey == null) {
// Hardly the case, but just invoke getter rather than direct
// attribute to be sure that collectorNumber has been retrieved already!
sortableCNKey = makeCollectorNumberSortingKey(getCollectorNumber());
}
return sortableCNKey;
}
@@ -309,32 +298,6 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
rarity = pc.getRarity();
}
// FIXME: @leriomaggio - remember to get rid of this method once and for all :)
private String retrieveCollectorNumber() {
StaticData data = StaticData.instance();
CardEdition edition = data.getEditions().get(this.edition);
if (edition == null) {
edition = data.getCustomEditions().get(this.edition);
if (edition == null) // don't bother continuing - non-existing card!
return NO_COLLECTOR_NUMBER;
}
int artIndexCount = 0;
String collectorNumberInEdition = "";
for (CardEdition.CardInSet card : edition.getAllCardsInSet()) {
if (card.name.equalsIgnoreCase(this.name)) {
artIndexCount += 1;
if (artIndexCount == this.artIndex) {
collectorNumberInEdition = card.collectorNumber;
break;
}
}
}
// CardEdition stores collectorNumber as a String, which is null if there isn't any.
// In this case, the NO_COLLECTOR_NUMBER value (i.e. 0) is returned.
return ((collectorNumberInEdition != null) && (collectorNumberInEdition.length() > 0)) ?
collectorNumberInEdition : NO_COLLECTOR_NUMBER;
}
@Override
public String getImageKey(boolean altState) {
String imageKey = ImageKeys.CARD_PREFIX + name + CardDb.NameSetSeparator

View File

@@ -78,8 +78,10 @@ public final class FileUtil {
}
public static boolean isDirectoryWithFiles(final String path) {
if (path == null) return false;
final File f = new File(path);
return f.exists() && f.isDirectory() && f.list().length > 0;
final String[] fileList = f.list();
return fileList!=null && fileList.length > 0;
}
public static boolean ensureDirectoryExists(final String path) {

View File

@@ -56,7 +56,7 @@ public class ImageUtil {
int artIdx = cp.getArtIndex() - 1;
if (hasManyPictures) {
if (cntPictures <= artIdx) // prevent overflow
artIdx = cntPictures == 0 ? 0 : artIdx % cntPictures;
artIdx = artIdx % cntPictures;
s.append(artIdx + 1);
}

View File

@@ -184,7 +184,7 @@ public class ImageCache {
if (useArtCrop) {
if (ipc != null && ipc.getRules().getSplitType() == CardSplitType.Flip) {
// Art crop will always use front face as image key for flip cards
imageKey = ImageUtil.getImageKey((PaperCard) ipc, false, true);
imageKey = ((PaperCard) ipc).getImageKeyFromSet(); // ImageUtil.getImageKey((PaperCard) ipc, false, true);
}
imageKey = TextUtil.fastReplace(imageKey, ".full", ".artcrop");
}

View File

@@ -536,13 +536,7 @@ public abstract class ACEditorBase<TItem extends InventoryItem, TModel extends D
CardManager cardManager = (CardManager) CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController().getDeckManager();
PaperCard existingCard = cardManager.getSelectedItem();
// make a foiled version based on the original
PaperCard foiledCard = new PaperCard(
existingCard.getRules(),
existingCard.getEdition(),
existingCard.getRarity(),
existingCard.getArtIndex(),
true,
existingCard.getArtist());
PaperCard foiledCard = existingCard.getFoiled();
// remove *quantity* instances of existing card
CDeckEditorUI.SINGLETON_INSTANCE.removeSelectedCards(false, quantity);
// add *quantity* into the deck and set them as selected

View File

@@ -0,0 +1,91 @@
package forge.card;
import forge.item.PaperCard;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.Collection;
import java.util.Set;
import java.util.TreeSet;
import static org.testng.Assert.assertNotNull;
public class CardDbPerformanceTests extends CardDbTestCase {
private Set<String> fullDbCardNames = new TreeSet<>();
@Override
@BeforeMethod
public void setup() {
super.setup();
Collection<PaperCard> uniqueCards = this.cardDb.getUniqueCards();
for (PaperCard card : uniqueCards)
this.fullDbCardNames.add(card.getName());
}
@Test
public void testBenchmarkFullDbGetCardLegacyImplementation() {
int nRuns = 100;
long averageTime = 0;
long minTime = 10000; // 10 secs
long maxTime = 0;
for (int r = 1; r <= nRuns; r++) {
long start = System.currentTimeMillis();
for (String name : this.fullDbCardNames) {
PaperCard card = this.legacyCardDb.getCard(name);
assertNotNull(card);
}
long timeRun = System.currentTimeMillis() - start;
averageTime += timeRun;
if (timeRun < minTime)
minTime = timeRun;
if (timeRun > maxTime)
maxTime = timeRun;
}
System.out.println("[LEGACY] Total Time (in sec): " + ((double) averageTime)/ 1000);
System.out.println("[LEGACY] Average Time (in sec): " + ((double) averageTime / nRuns)/ 1000);
System.out.println("[LEGACY] Best Time (in sec): " + ((double) minTime)/ 1000);
System.out.println("[LEGACY] Worst Time (in sec): " + ((double) maxTime)/ 1000);
}
@Test
public void testBenchmarkFullDbGetCardNewDbImplementation() {
int nRuns = 100;
long averageTime = 0;
long minTime = 10000; // 10 secs
long maxTime = 0;
for (int r = 1; r <= nRuns; r++) {
long start = System.currentTimeMillis();
for (String name : this.fullDbCardNames) {
PaperCard card = this.cardDb.getCard(name);
assertNotNull(card);
}
long timeRun = System.currentTimeMillis() - start;
averageTime += timeRun;
if (timeRun < minTime)
minTime = timeRun;
if (timeRun > maxTime)
maxTime = timeRun;
}
System.out.println("[NEW] Total Time (in sec): " + ((double) averageTime)/ 1000);
System.out.println("[NEW] Average Time (in sec): " + ((double) averageTime / nRuns)/ 1000);
System.out.println("[NEW] Best Time (in sec): " + ((double) minTime)/ 1000);
System.out.println("[NEW] Worst Time (in sec): " + ((double) maxTime)/ 1000);
}
@Test
public void testGetCardFullDbNewImplementationToProfile(){
for (String name : this.fullDbCardNames) {
PaperCard card = this.cardDb.getCard(name);
assertNotNull(card);
}
}
@Test
public void testGetCardFullDbLegacyImplementationToProfile(){
for (String name : this.fullDbCardNames) {
PaperCard card = this.legacyCardDb.getCard(name);
assertNotNull(card);
}
}
}

View File

@@ -1,5 +1,6 @@
package forge.card;
import com.google.common.base.Predicate;
import forge.StaticData;
import forge.item.IPaperCard;
import forge.item.PaperCard;
@@ -10,7 +11,10 @@ import org.testng.annotations.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import static org.testng.Assert.*;
@@ -101,6 +105,44 @@ public class CardDbTestCase extends ForgeCardMockTestCase {
this.legacyCardDb = new LegacyCardDb(data.getCommonCards().getAllCards(), data.getEditions());
}
/*
* TEST FOR GET ALL CARDS
*/
@Test
public void testGetAllCardsWithName(){
List<PaperCard> allCounterSpellPrints = this.cardDb.getAllCards(this.cardNameCounterspell);
assertNotNull(allCounterSpellPrints);
for (PaperCard card : allCounterSpellPrints)
assertEquals(card.getName(), this.cardNameCounterspell);
}
@Test
public void testGetAllCardsThatWerePrintedInSets(){
List<String> allowedSets = new ArrayList<>();
allowedSets.add(this.latestArtShivanDragonEdition);
Predicate<PaperCard> wasPrinted = (Predicate<PaperCard>) this.cardDb.wasPrintedInSets(allowedSets);
List<PaperCard> allCardsInSet = this.cardDb.getAllCards(wasPrinted);
assertNotNull(allCardsInSet);
}
@Test void testGetAllCardsOfaGivenNameAndLegalInSets(){
List<String> allowedSets = new ArrayList<>(Arrays.asList(this.editionsCounterspell));
Predicate<PaperCard> printedInSets = (Predicate<PaperCard>) this.cardDb.wasPrintedInSets(allowedSets);
List<PaperCard> allCounterSpellsInSets = this.cardDb.getAllCards(this.cardNameCounterspell, printedInSets);
assertNotNull(allCounterSpellsInSets);
assertTrue(allCounterSpellsInSets.size() > 0);
assertTrue(allCounterSpellsInSets.size() > 1);
for (PaperCard card : allCounterSpellsInSets) {
assertEquals(card.getName(), this.cardNameCounterspell);
}
}
/*
* TEST FOR CARD RETRIEVAL METHODS
*/
@Test
public void testGetCardByName() {
PaperCard legacyCard = this.legacyCardDb.getCard(cardNameShivanDragon);
@@ -2019,6 +2061,30 @@ public class CardDbTestCase extends ForgeCardMockTestCase {
assertEquals(legacyAinokCard, ainokCard);
}
@Test
public void testGetIslandsFromEditionsWithSpecificArtIndex(){
String cardName = "Island";
assertEquals(this.cardDb.getCardArtPreference(), CardDb.CardArtPreference.LATEST_ART_ALL_EDITIONS);
PaperCard islandLatest = this.cardDb.getCardFromEditions(cardName, CardDb.CardArtPreference.LATEST_ART_ALL_EDITIONS, 12);
assertNotNull(islandLatest);
assertEquals(islandLatest.getName(), "Island");
assertEquals(islandLatest.getEdition(), "SLD");
assertEquals(islandLatest.getArtIndex(), 12);
// PALP
PaperCard islandOriginal = this.cardDb.getCardFromEditions(cardName, CardDb.CardArtPreference.ORIGINAL_ART_CORE_EXPANSIONS_REPRINT_ONLY, 12);
assertNotNull(islandOriginal);
assertEquals(islandOriginal.getName(), "Island");
assertEquals(islandOriginal.getEdition(), "SLD");
assertEquals(islandOriginal.getArtIndex(), 12);
}
@Test
public void testMaxArtCountForBasicLand(){
int maxArtIndex = this.cardDb.getMaxArtIndex("Island");
assertEquals(maxArtIndex, 13);
}
}

View File

@@ -128,7 +128,7 @@ public class CardRequestTestCase {
request = CardRequest.fromString(requestString);
assertEquals(request.cardName, cardName);
assertEquals(request.edition, edition);
assertEquals(request.artIndex, IPaperCard.DEFAULT_ART_INDEX);
assertEquals(request.artIndex, 20);
assertEquals(request.collectorNumber, IPaperCard.NO_COLLECTOR_NUMBER);
@@ -215,4 +215,15 @@ public class CardRequestTestCase {
assertNotEquals(request.artIndex, newRequest.artIndex);
}
@Test
public void testCreatingCardRequestWithArtIndexGreaterThanNine(){
String requestString = CardRequest.compose("Island", "SLD", 13);
CardRequest request = CardRequest.fromString(requestString);
assertEquals(request.cardName, "Island");
assertEquals(request.edition, "SLD");
assertEquals(request.artIndex, 13);
assertEquals(request.collectorNumber, IPaperCard.NO_COLLECTOR_NUMBER);
}
}

View File

@@ -5,6 +5,7 @@ import forge.ImageKeys;
import forge.Singletons;
import forge.StaticData;
import forge.gamesimulationtests.util.CardDatabaseHelper;
import forge.item.PaperCard;
import forge.localinstance.properties.ForgeConstants;
import forge.localinstance.properties.ForgePreferences;
import forge.model.FModel;
@@ -133,6 +134,9 @@ public class ForgeCardMockTestCase extends PowerMockTestCase {
PowerMockito.mockStatic(ImageKeys.class);
initForgeConstants();
// Always Has Image (there is a separated test case to cover the opposite case)
PowerMockito.when(ImageKeys.hasImage(Mockito.any(PaperCard.class))).thenReturn(true);
//Mocking some more static stuff
PowerMockito.mockStatic(Singletons.class);
PowerMockito.mockStatic(FModel.class);

View File

@@ -183,7 +183,7 @@ public class LegacyCardDb {
}
public PaperCard getFoiled(PaperCard card0) {
return new PaperCard(card0.getRules(), card0.getEdition(), card0.getRarity(), card0.getArtIndex(), true, card0.getArtist());
return card0.getFoiled();
}
public PaperCard getCardFromEdition(final String cardName, LegacySetPreference fromSet) {