From 50fd029c3bab38260b457f412bdec7cce561e59a Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sun, 10 Dec 2023 14:31:55 +0400 Subject: [PATCH] tests: improved show playable abilities to print playable list instead unique (see showAvailableAbilities), added custom effect for transform tests (see addCustomEffect_TransformTarget); --- .../java/org/mage/test/player/TestPlayer.java | 9 ++- .../serverside/base/MageTestPlayerBase.java | 20 +++++-- .../base/impl/CardTestPlayerAPIImpl.java | 9 ++- .../org/mage/test/utils/CardUtilTest.java | 32 ++++++++++- Mage/src/main/java/mage/util/CardUtil.java | 55 ++++++++++++++----- 5 files changed, 100 insertions(+), 25 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index e8870353692..02ab81b2c2f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -1026,10 +1026,10 @@ public class TestPlayer implements Player { wasProccessed = true; } - // show available abilities - if (params[0].equals(SHOW_COMMAND_AVAILABLE_ABILITIES) && params.length == 1) { + // show available abilities: show only unique list + if (params[0].equals(SHOW_COMMAND_AVAILABLE_ABILITIES) && params.length == 2) { printStart(game, action.getActionName()); - printAbilities(game, computerPlayer.getPlayable(game, true)); + printAbilities(game, computerPlayer.getPlayable(game, true, Zone.ALL, Boolean.parseBoolean(params[1]))); printEnd(); actions.remove(action); wasProccessed = true; @@ -1247,13 +1247,12 @@ public class TestPlayer implements Player { if (abilities == null) { return; } - List data = abilities.stream() .map(a -> (a.getZone() + " -> " + a.getSourceObject(game).getIdName() + " -> " + (a.toString().startsWith("Cast ") ? "[" + a.getManaCostsToPay().getText() + "] -> " : "") // printed cost, not modified + (a.toString().length() > 0 - ? a.toString().substring(0, Math.min(40, a.toString().length())) + "..." + ? CardUtil.substring(a.toString(), 40, "...") : a.getClass().getSimpleName()) )) .sorted() 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 cddbaafabb7..564f621c30c 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 @@ -9,10 +9,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.effects.common.ReturnFromExileEffect; -import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.*; import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; import mage.abilities.effects.common.cost.SpellsCostReductionAllEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; @@ -519,6 +516,21 @@ public abstract class MageTestPlayerBase { ); } + /** + * Add target transform ability that can be called by text "target transform" + * + * @param controller + */ + protected void addCustomEffect_TransformTarget(TestPlayer controller) { + Ability ability = new SimpleActivatedAbility(new TransformTargetEffect().setText("target transform"), new ManaCostsImpl<>("")); + ability.addTarget(new TargetPermanent()); + addCustomCardWithAbility( + "target transform for " + controller.getName(), + controller, + ability + ); + } + /** * Return target card to hand that can be called by text "return from ..." * 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 a92aa90b214..5d856bf4db5 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 @@ -540,7 +540,14 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } public void showAvailableAbilities(String showName, int turnNum, PhaseStep step, TestPlayer player) { - show(showName, turnNum, step, player, SHOW_COMMAND_AVAILABLE_ABILITIES); + showAvailableAbilities(showName, turnNum, step, player, true); + } + + /** + * @param showOnlyUniqueAbilities return full list or unique only (duplicated abilities with same name will be combined in one) + */ + public void showAvailableAbilities(String showName, int turnNum, PhaseStep step, TestPlayer player, Boolean showOnlyUniqueAbilities) { + show(showName, turnNum, step, player, SHOW_COMMAND_AVAILABLE_ABILITIES, showOnlyUniqueAbilities.toString()); } public void showAvailableMana(String showName, int turnNum, PhaseStep step, TestPlayer player) { diff --git a/Mage.Tests/src/test/java/org/mage/test/utils/CardUtilTest.java b/Mage.Tests/src/test/java/org/mage/test/utils/CardUtilTest.java index e6f26a98885..99e6845c60c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/utils/CardUtilTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/utils/CardUtilTest.java @@ -2,6 +2,8 @@ package org.mage.test.utils; import mage.constants.PhaseStep; import mage.constants.Zone; +import mage.util.CardUtil; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -75,7 +77,7 @@ public class CardUtilTest extends CardTestPlayerBase { @Test public void testJadziPlayingLandAndCast() { addCard(Zone.BATTLEFIELD, playerA, jadzi); - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1+1+1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1 + 1 + 1); addCard(Zone.HAND, playerA, "Lightning Bolt", 2); addCard(Zone.LIBRARY, playerA, "Cragcrown Pathway"); addCard(Zone.LIBRARY, playerA, akoumWarrior); @@ -92,4 +94,32 @@ public class CardUtilTest extends CardTestPlayerBase { assertPermanentCount(playerA, akoumWarrior, 1); assertPermanentCount(playerA, "Cragcrown Pathway", 1); } + + @Test + public void test_Substring() { + String str = "12345"; + String ending = "..."; + + Assert.assertEquals("", CardUtil.substring(str, 0)); + Assert.assertEquals("1", CardUtil.substring(str, 1)); + Assert.assertEquals("12", CardUtil.substring(str, 2)); + Assert.assertEquals("123", CardUtil.substring(str, 3)); + Assert.assertEquals("1234", CardUtil.substring(str, 4)); + Assert.assertEquals("12345", CardUtil.substring(str, 5)); + Assert.assertEquals("12345", CardUtil.substring(str, 6)); + Assert.assertEquals("12345", CardUtil.substring(str, 7)); + Assert.assertEquals("12345", CardUtil.substring(str, 8)); + Assert.assertEquals("12345", CardUtil.substring(str, 9)); + + Assert.assertEquals("", CardUtil.substring(str, 0, ending)); + Assert.assertEquals(".", CardUtil.substring(str, 1, ending)); + Assert.assertEquals("..", CardUtil.substring(str, 2, ending)); + Assert.assertEquals("...", CardUtil.substring(str, 3, ending)); + Assert.assertEquals("1...", CardUtil.substring(str, 4, ending)); + Assert.assertEquals("12345", CardUtil.substring(str, 5, ending)); + Assert.assertEquals("12345", CardUtil.substring(str, 6, ending)); + Assert.assertEquals("12345", CardUtil.substring(str, 7, ending)); + Assert.assertEquals("12345", CardUtil.substring(str, 8, ending)); + Assert.assertEquals("12345", CardUtil.substring(str, 9, ending)); + } } diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index b35b5c4c7ef..41db4d93109 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -905,7 +905,7 @@ public final class CardUtil { } public static String getSimpleCountersText(int amount, String forOne, String counterType) { - return numberToText(amount, forOne)+" "+counterType+" counter"+ (amount==1 ? "" : "s"); + return numberToText(amount, forOne) + " " + counterType + " counter" + (amount == 1 ? "" : "s"); } public static String getOneOneCountersText(int amount) { @@ -1654,6 +1654,7 @@ public final class CardUtil { } return zcc; } + /** * Create a MageObjectReference of the ability's source * Subtract 1 zcc if not on the stack, referencing when it was on the stack if it's a resolved permanent. @@ -1702,19 +1703,21 @@ public final class CardUtil { } return costTags; } + /** * Check if a specific tag exists in the cost tags of either the source ability, or the permanent source of the ability. * Works in any moment (even before source ability activated) * * @param game * @param source - * @param tag The tag's string identifier to look up + * @param tag The tag's string identifier to look up * @return if the tag was found */ public static boolean checkSourceCostsTagExists(Game game, Ability source, String tag) { Map costTags = getSourceCostsTagsMap(game, source); return costTags != null && costTags.containsKey(tag); } + /** * Find a specific tag in the cost tags of either the source ability, or the permanent source of the ability. * Works in any moment (even before source ability activated) @@ -1722,11 +1725,11 @@ public final class CardUtil { * * @param game * @param source - * @param tag The tag's string identifier to look up + * @param tag The tag's string identifier to look up * @param defaultValue A default value to return if the tag is not found * @return The object stored by the tag if found, the default if not */ - public static T getSourceCostsTag(Game game, Ability source, String tag, T defaultValue){ + public static T getSourceCostsTag(Game game, Ability source, String tag, T defaultValue) { Map costTags = getSourceCostsTagsMap(game, source); if (costTags != null) { Object value = costTags.getOrDefault(tag, defaultValue); @@ -1748,13 +1751,14 @@ public final class CardUtil { return "pay " + text; } - private static boolean isImmutableObject(Object o){ + private static boolean isImmutableObject(Object o) { return o == null || o instanceof Number || o instanceof Boolean || o instanceof String || o instanceof MageObjectReference || o instanceof UUID || o instanceof Enum; } - public static T deepCopyObject(T value){ + + public static T deepCopyObject(T value) { if (isImmutableObject(value)) { return value; } else if (value instanceof Copyable) { @@ -1781,53 +1785,58 @@ public final class CardUtil { return (T) deepCopyHashMap((HashMap) value); } else if (value instanceof List) { return (T) deepCopyList((List) value); - } else if (value instanceof AbstractMap.SimpleImmutableEntry){ //Used by Leonin Arbiter, Vessel Of The All Consuming Wanderer as a generic Pair class + } else if (value instanceof AbstractMap.SimpleImmutableEntry) { //Used by Leonin Arbiter, Vessel Of The All Consuming Wanderer as a generic Pair class AbstractMap.SimpleImmutableEntry entryValue = (AbstractMap.SimpleImmutableEntry) value; - return (T) new AbstractMap.SimpleImmutableEntry(deepCopyObject(entryValue.getKey()),deepCopyObject(entryValue.getValue())); + return (T) new AbstractMap.SimpleImmutableEntry(deepCopyObject(entryValue.getKey()), deepCopyObject(entryValue.getValue())); } else { throw new IllegalStateException("Unhandled object " + value.getClass().getSimpleName() + " during deep copy, must add explicit handling of all Object types"); } } + private static > TreeSet deepCopyTreeSet(TreeSet original) { if (original.getClass() != TreeSet.class) { throw new IllegalStateException("Unhandled TreeSet type " + original.getClass().getSimpleName() + " in deep copy"); } TreeSet newSet = new TreeSet<>(); - for (T value : original){ + for (T value : original) { newSet.add((T) deepCopyObject(value)); } return newSet; } + private static HashSet deepCopyHashSet(Set original) { if (original.getClass() != HashSet.class) { throw new IllegalStateException("Unhandled HashSet type " + original.getClass().getSimpleName() + " in deep copy"); } HashSet newSet = new HashSet<>(original.size()); - for (T value : original){ + for (T value : original) { newSet.add((T) deepCopyObject(value)); } return newSet; } + private static LinkedHashSet deepCopyLinkedHashSet(LinkedHashSet original) { if (original.getClass() != LinkedHashSet.class) { throw new IllegalStateException("Unhandled LinkedHashSet type " + original.getClass().getSimpleName() + " in deep copy"); } LinkedHashSet newSet = new LinkedHashSet<>(original.size()); - for (T value : original){ + for (T value : original) { newSet.add((T) deepCopyObject(value)); } return newSet; } + private static List deepCopyList(List original) { //always returns an ArrayList if (original.getClass() != ArrayList.class) { throw new IllegalStateException("Unhandled List type " + original.getClass().getSimpleName() + " in deep copy"); } ArrayList newList = new ArrayList<>(original.size()); - for (T value : original){ + for (T value : original) { newList.add((T) deepCopyObject(value)); } return newList; } + private static HashMap deepCopyHashMap(Map original) { if (original.getClass() != HashMap.class) { throw new IllegalStateException("Unhandled HashMap type " + original.getClass().getSimpleName() + " in deep copy"); @@ -1838,6 +1847,7 @@ public final class CardUtil { } return newMap; } + private static LinkedHashMap deepCopyLinkedHashMap(Map original) { if (original.getClass() != LinkedHashMap.class) { throw new IllegalStateException("Unhandled LinkedHashMap type " + original.getClass().getSimpleName() + " in deep copy"); @@ -1848,6 +1858,7 @@ public final class CardUtil { } return newMap; } + private static , V> EnumMap deepCopyEnumMap(Map original) { if (original.getClass() != EnumMap.class) { throw new IllegalStateException("Unhandled EnumMap type " + original.getClass().getSimpleName() + " in deep copy"); @@ -2076,18 +2087,34 @@ public final class CardUtil { } } + public static String substring(String str, int maxLength) { + return substring(str, maxLength, ""); + } + /** * Don't raise exception, so must be used instead standard substring calls all the time * * @param str * @param maxLength + * @param overflowEnding can add ... at the end * @return */ - public static String substring(String str, int maxLength) { + public static String substring(String str, int maxLength, String overflowEnding) { if (str == null || str.isEmpty()) { return str; } - return str.substring(0, Math.min(str.length(), maxLength)); + + // full + if (str.length() <= maxLength) { + return str; + } + + // short + if (maxLength <= overflowEnding.length()) { + return overflowEnding.substring(0, maxLength); + } else { + return (str + overflowEnding).substring(0, maxLength - overflowEnding.length()) + overflowEnding; + } }