diff --git a/forge-core/src/main/java/forge/card/CardDb.java b/forge-core/src/main/java/forge/card/CardDb.java index 13819c4d070..ee64447166f 100644 --- a/forge-core/src/main/java/forge/card/CardDb.java +++ b/forge-core/src/main/java/forge/card/CardDb.java @@ -612,10 +612,20 @@ public final class CardDb implements ICardDatabase, IDeckGenPool { return artPref.accept(ed); } })); + + /* At this point, it may be possible that Art Preference is too-strict for the requested card! + i.e. acceptedEditions.size() == 0! + This may be the case of Cards Only available in NON-CORE/EXPANSIONS/REPRINT sets. + (NOTE: We've already checked that any print of the request card exists in the DB) + 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) + 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, artIndex, cr.isFoil); diff --git a/forge-gui-desktop/src/test/java/forge/card/CardDbTestCase.java b/forge-gui-desktop/src/test/java/forge/card/CardDbTestCase.java index d3d8b674303..7755bcb4fdb 100644 --- a/forge-gui-desktop/src/test/java/forge/card/CardDbTestCase.java +++ b/forge-gui-desktop/src/test/java/forge/card/CardDbTestCase.java @@ -15,57 +15,57 @@ import static org.testng.Assert.*; public class CardDbTestCase extends ForgeCardMockTestCase { - private LegacyCardDb legacyCardDb; - private CardDb cardDb; + protected LegacyCardDb legacyCardDb; + protected CardDb cardDb; // Shivan Dragon is great as it has multiple arts from re-prints - private final String cardNameShivanDragon = "Shivan Dragon"; - private final String editionShivanDragon = "2ED"; - private final String collNrShivanDragon = "175"; + protected final String cardNameShivanDragon = "Shivan Dragon"; + protected final String editionShivanDragon = "2ED"; + protected final String collNrShivanDragon = "175"; // Test Foil case - first foil ever printed! - private final String cardNameFoilLightningDragon = "Lightning Dragon+"; - private final String cardNameLightningDragon = "Lightning Dragon"; - private final String editionLightningDragon = "PUSG"; - private final String collNrLightningDragon = "202"; + protected final String cardNameFoilLightningDragon = "Lightning Dragon+"; + protected final String cardNameLightningDragon = "Lightning Dragon"; + protected final String editionLightningDragon = "PUSG"; + protected final String collNrLightningDragon = "202"; // Get a card with multiple arts - private final String cardNameHymnToTourach = "Hymn to Tourach"; // good 'ol hymn w/ four different art - private final String[] collectorNumbersHymnToTourach = {"38a", "38b", "38c", "38d"}; - private final String editionHymnToTourach = "FEM"; + protected final String cardNameHymnToTourach = "Hymn to Tourach"; // good 'ol hymn w/ four different art + protected final String[] collectorNumbersHymnToTourach = {"38a", "38b", "38c", "38d"}; + protected final String editionHymnToTourach = "FEM"; //Get Card From Editions Test fixtures - private final String oldFrameShivanDragonEdition = "LEA"; - private final String newFrameShivanDragonEdition = "M20"; + protected final String oldFrameShivanDragonEdition = "LEA"; + protected final String newFrameShivanDragonEdition = "M20"; - private final String oldFrameLightningDragonEdition = "USG"; - private final String oldFrameLightningDragonEditionNoPromo = "USG"; + protected final String oldFrameLightningDragonEdition = "USG"; + protected final String oldFrameLightningDragonEditionNoPromo = "USG"; - private final String newFrameLightningDragonEdition = "VMA"; - private final String newFrameLightningDragonEditionNoPromo = "USG"; + protected final String newFrameLightningDragonEdition = "VMA"; + protected final String newFrameLightningDragonEditionNoPromo = "USG"; - private final String newFrameHymnToTourachEdition = "EMA"; - private final String newFrameHymnToTourachEditionNoPromo = "EMA"; - private final String oldFrameHymnToTourachEdition = "FEM"; - private final String oldFrameHymnToTourachEditionNoPromo = "FEM"; + protected final String newFrameHymnToTourachEdition = "EMA"; + protected final String newFrameHymnToTourachEditionNoPromo = "EMA"; + protected final String oldFrameHymnToTourachEdition = "FEM"; + protected final String oldFrameHymnToTourachEditionNoPromo = "FEM"; // Test Dates and Editions - private final String printedBeforeFromTheVaultDate = "2008-10-01"; - private final String latestFrameShivanDragonEditionBefore = "DRB"; - private final String latestFrameShivanDragonEditionBeforeNoPromo = "10E"; - private final String latestFrameLightningDragonEditionBefore = "MBP"; - private final String latestFrameLightningDragonEditionBeforeNoPromo = "USG"; - private final String printedBeforeEternalMasters = "2015-01-01"; - private final String latestFrameHymnToTourachEditionBefore = "VMA"; - private final String latestFrameHymnToTourachEditionBeforeNoPromo = "FEM"; + protected final String printedBeforeFromTheVaultDate = "2008-10-01"; + protected final String latestFrameShivanDragonEditionBefore = "DRB"; + protected final String latestFrameShivanDragonEditionBeforeNoPromo = "10E"; + protected final String latestFrameLightningDragonEditionBefore = "MBP"; + protected final String latestFrameLightningDragonEditionBeforeNoPromo = "USG"; + protected final String printedBeforeEternalMasters = "2015-01-01"; + protected final String latestFrameHymnToTourachEditionBefore = "VMA"; + protected final String latestFrameHymnToTourachEditionBeforeNoPromo = "FEM"; // Get a card that has lots of editions so that we can test fetching for specific editions and print dates - private final String cardNameCounterspell = "Counterspell"; - private final String[] editionsCounterspell = {"3ED", "4ED", "ICE", "5ED", "TMP", "S99", "MMQ", "A25", "MH2"}; - private final String counterspellPrintedBeforeMasters25 = "2018-03-15"; // One day before Master25 release - private final String[] counterspellLatestBeforeMasters25 = {"MPS_AKH", "EMA"}; - private final String counterspellPrintedBeforeEternalMasters = "2016-06-09"; // One day before Eternal Masters release - private final String[] counterspellLatestBeforeEternalMasters = {"TPR", "7ED"}; + protected final String cardNameCounterspell = "Counterspell"; + protected final String[] editionsCounterspell = {"3ED", "4ED", "ICE", "5ED", "TMP", "S99", "MMQ", "A25", "MH2"}; + protected final String counterspellPrintedBeforeMasters25 = "2018-03-15"; // One day before Master25 release + protected final String[] counterspellLatestBeforeMasters25 = {"MPS_AKH", "EMA"}; + protected final String counterspellPrintedBeforeEternalMasters = "2016-06-09"; // One day before Eternal Masters release + protected final String[] counterspellLatestBeforeEternalMasters = {"TPR", "7ED"}; @BeforeMethod public void setup(){ @@ -1292,16 +1292,118 @@ public class CardDbTestCase extends ForgeCardMockTestCase { assertEquals(this.cardDb.getCardArtPreference(), CardDb.CardArtPreference.LATEST_ART_ALL_EDITIONS); } - @Test(enabled = false) - public void testCardsNotFoundWhileLoadingDecks(){ - String cardName = "Yavimaya, Cradle of Growth"; - PaperCard cardInDb = this.cardDb.getCard(cardName); - assertNotNull(cardInDb); - assertEquals(cardInDb.getEdition(), "MH2"); + /** + * This set is crucial to test Card Art Preference and Strict Policies. + * In particular, we wish to test whether the DB is robust enough to retrieve + * the card even if Art Preference is too strict, that is: the card is only + * available in Filtered sets. + * + * When this happens, we also want to be sure that retrieved card will be + * still compliant with Art Preference, when multiple candidates are possible + * (therefore, latest or original art first) + * + * For this test we will use the following card/editions as fixtures: + * - Militant Angel: ONLY available in forge in Game Night + * - Loyal Unicorn: Available in Forge in The List, and COMMANDER 2018 + * - Selfless Squire: Available in Forge in COMMANDER 2021, Treasure Chest, and COMMANDER 2016 + * - Atog: Test card available in Promo and Non-Promo Print. We will use this card as reference + * which will have multiple editions returned over the preference selections. + */ + @Test + public void testCardsAlwaysReturnedEvenIfCardArtPreferenceIsTooStrict(){ + // REFERENCE CASE - NO FILTER + assertEquals(this.cardDb.getCardArtPreference(), CardDb.CardArtPreference.LATEST_ART_ALL_EDITIONS); - cardName = "Urza's Saga"; - cardInDb = this.cardDb.getCard(cardName); - assertNotNull(cardInDb); + PaperCard atog = this.cardDb.getCard("Atog"); + assertNotNull(atog); + assertEquals(atog.getEdition(), "ME4"); // Game Night + + PaperCard militantAngel = this.cardDb.getCard("Militant Angel"); + assertNotNull(militantAngel); + assertEquals(militantAngel.getEdition(), "GNT"); // Game Night + + // Loyal Unicorn: Available in Forge in The List and COMMANDER 2018 + PaperCard loyalUnicorn = this.cardDb.getCard("Loyal Unicorn"); + assertNotNull(loyalUnicorn); + assertEquals(loyalUnicorn.getEdition(), "PLIST"); // The List + + // Selfless Squire: Available in Forge in COMMANDER 2021; Treasure Chest; COMMANDER 2016 + PaperCard selflessSquire = this.cardDb.getCard("Selfless Squire"); + assertNotNull(selflessSquire); + assertEquals(selflessSquire.getEdition(), "C21"); // The List + + // Set Strictness to Expansions and Reprint Only (LATEST) + this.cardDb.setCardArtPreference(true, true); + assertEquals(this.cardDb.getCardArtPreference(), CardDb.CardArtPreference.LATEST_ART_CORE_EXPANSIONS_REPRINT_ONLY); + + // ONLY CHANGE HERE IS FOR ATOG + atog = this.cardDb.getCard("Atog"); + assertNotNull(atog); + assertEquals(atog.getEdition(), "MRD"); // Game Night + + militantAngel = this.cardDb.getCard("Militant Angel"); + assertNotNull(militantAngel); + assertEquals(militantAngel.getEdition(), "GNT"); // Game Night + + // Loyal Unicorn: Available in Forge in The List and COMMANDER 2018 + loyalUnicorn = this.cardDb.getCard("Loyal Unicorn"); + assertNotNull(loyalUnicorn); + assertEquals(loyalUnicorn.getEdition(), "PLIST"); // The List + + // Selfless Squire: Available in Forge in COMMANDER 2021; Treasure Chest; COMMANDER 2016 + selflessSquire = this.cardDb.getCard("Selfless Squire"); + assertNotNull(selflessSquire); + assertEquals(selflessSquire.getEdition(), "C21"); // The List + + // Set Strictness to ORIGINAL ART NO FILTER - Ref case + this.cardDb.setCardArtPreference(false, false); + assertEquals(this.cardDb.getCardArtPreference(), CardDb.CardArtPreference.ORIGINAL_ART_ALL_EDITIONS); + + // ONLY CHANGE HERE IS FOR ATOG + atog = this.cardDb.getCard("Atog"); + assertNotNull(atog); + assertEquals(atog.getEdition(), "ATQ"); // Game Night + + militantAngel = this.cardDb.getCard("Militant Angel"); + assertNotNull(militantAngel); + assertEquals(militantAngel.getEdition(), "GNT"); + + // Loyal Unicorn: Available in Forge in The List and COMMANDER 2018 + loyalUnicorn = this.cardDb.getCard("Loyal Unicorn"); + assertNotNull(loyalUnicorn); + assertEquals(loyalUnicorn.getEdition(), "C18"); + + // Selfless Squire: Available in Forge in COMMANDER 2021; Treasure Chest; COMMANDER 2016 + selflessSquire = this.cardDb.getCard("Selfless Squire"); + assertNotNull(selflessSquire); + assertEquals(selflessSquire.getEdition(), "C16"); + + // Set Strictness to ORIGINAL ART NO FILTER - Ref case + this.cardDb.setCardArtPreference(false, true); + assertEquals(this.cardDb.getCardArtPreference(), CardDb.CardArtPreference.ORIGINAL_ART_CORE_EXPANSIONS_REPRINT_ONLY); + + // ONLY CHANGE HERE IS FOR ATOG + atog = this.cardDb.getCard("Atog"); + assertNotNull(atog); + assertEquals(atog.getEdition(), "ATQ"); // Game Night + + militantAngel = this.cardDb.getCard("Militant Angel"); + assertNotNull(militantAngel); + assertEquals(militantAngel.getEdition(), "GNT"); + + // Loyal Unicorn: Available in Forge in The List and COMMANDER 2018 + loyalUnicorn = this.cardDb.getCard("Loyal Unicorn"); + assertNotNull(loyalUnicorn); + assertEquals(loyalUnicorn.getEdition(), "PLIST"); // This is returned as this is a REPRINT Set!! + + // Selfless Squire: Available in Forge in COMMANDER 2021; Treasure Chest; COMMANDER 2016 + selflessSquire = this.cardDb.getCard("Selfless Squire"); + assertNotNull(selflessSquire); + assertEquals(selflessSquire.getEdition(), "C16"); + + // Set Art Preference back to default + this.cardDb.setCardArtPreference(true, false); + assertEquals(this.cardDb.getCardArtPreference(), CardDb.CardArtPreference.LATEST_ART_ALL_EDITIONS); } } diff --git a/forge-gui-desktop/src/test/java/forge/card/ForgeCardMockTestCase.java b/forge-gui-desktop/src/test/java/forge/card/ForgeCardMockTestCase.java index 260ee45719d..dd492eb197e 100644 --- a/forge-gui-desktop/src/test/java/forge/card/ForgeCardMockTestCase.java +++ b/forge-gui-desktop/src/test/java/forge/card/ForgeCardMockTestCase.java @@ -5,7 +5,6 @@ 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; @@ -35,7 +34,7 @@ import java.util.ResourceBundle; @SuppressStaticInitializationFor({"forge.ImageCache", "forge.localinstance.properties.ForgeConstants"}) public class ForgeCardMockTestCase extends PowerMockTestCase { - private static String getUserDir() { + protected static String getUserDir() { // Adapted - reduced version from ForgeProfileProperties (which is private) final String osName = System.getProperty("os.name"); final String homeDir = System.getProperty("user.home"); @@ -59,7 +58,7 @@ public class ForgeCardMockTestCase extends PowerMockTestCase { return fallbackDataDir; } - private void initForgeConstants() throws IllegalAccessException { + protected void initForgeConstants() throws IllegalAccessException { PowerMockito.mockStatic(ForgeConstants.class); // Path Sep Field fPathSep = PowerMockito.field(ForgeConstants.class, "PATH_SEPARATOR"); @@ -132,7 +131,6 @@ public class ForgeCardMockTestCase extends PowerMockTestCase { PowerMockito.mockStatic(ImageIO.class); PowerMockito.mockStatic(ImageCache.class); PowerMockito.mockStatic(ImageKeys.class); - PowerMockito.when(ImageKeys.hasImage(Mockito.any(PaperCard.class))).thenReturn(true); initForgeConstants(); //Mocking some more static stuff