From ec6b0b94c6279530c3f3cbce98b5f1cbe33678cd Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 27 Jan 2020 06:00:46 +0400 Subject: [PATCH] Test framework: improved aliases support: * added check commands support; * added on of the choose method support; * added todo and checks for wrong test commands setup; --- .../org/mage/test/cards/copy/CloneTest.java | 3 + .../test/cards/copy/IdentityThiefTest.java | 9 +- .../java/org/mage/test/player/TestPlayer.java | 170 +++++++++++------- .../base/impl/CardTestPlayerAPIImpl.java | 93 +++++++--- 4 files changed, 187 insertions(+), 88 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java index 202b360e98..0d4a005c3c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java @@ -212,11 +212,14 @@ public class CloneTest extends CardTestPlayerBase { setChoice(playerA, "Elf"); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Clone"); + setChoice(playerB, "Yes"); setChoice(playerB, "Adaptive Automaton"); setChoice(playerB, "Goblin"); + setStrictChooseMode(true); setStopAt(2, PhaseStep.END_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Adaptive Automaton", 1); Permanent original = getPermanent("Adaptive Automaton", playerA); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/IdentityThiefTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/IdentityThiefTest.java index 72f7d1402a..4b175739f1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/IdentityThiefTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/IdentityThiefTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.copy; import mage.constants.PhaseStep; @@ -8,7 +7,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class IdentityThiefTest extends CardTestPlayerBase { @@ -33,10 +31,13 @@ public class IdentityThiefTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Molten Sentry"); attack(2, playerB, "Identity Thief"); + setChoice(playerB, "Yes"); addTarget(playerB, "Molten Sentry"); + setStrictChooseMode(true); setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertExileCount(playerA, 1); assertExileCount("Molten Sentry", 1); @@ -76,7 +77,7 @@ public class IdentityThiefTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Sylvan Advocate", 1); // {1}{G} 2/3 vigilance addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); addCard(Zone.HAND, playerA, "Battlegrowth"); // {G} instant - Put a +1/+1 counter on target creature. - + // Whenever Identity Thief attacks, you may exile another target nontoken creature. // If you do, Identity Thief becomes a copy of that creature until end of turn. // Return the exiled card to the battlefield under its owner's control at the beginning of the next end step. @@ -84,7 +85,7 @@ public class IdentityThiefTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Battlegrowth"); addTarget(playerA, "Sylvan Advocate"); - + attack(2, playerB, "Identity Thief"); addTarget(playerB, "Sylvan Advocate"); 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 e040b05632..b503e969b6 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 @@ -1,10 +1,5 @@ package org.mage.test.player; -import java.io.Serializable; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; import mage.MageItem; import mage.MageObject; import mage.MageObjectReference; @@ -61,6 +56,13 @@ import mage.util.CardUtil; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Ignore; + +import java.io.Serializable; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + import static org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl.*; /** @@ -140,7 +142,7 @@ public class TestPlayer implements Player { } public UUID getAliasByName(String searchName) { - if (searchName.startsWith("@")) { + if (searchName.startsWith(ALIASE_PREFIX)) { return this.aliases.getOrDefault(searchName.substring(1), null); } else { return this.aliases.getOrDefault(searchName, null); @@ -177,7 +179,7 @@ public class TestPlayer implements Player { /** * @param maxCallsWithoutAction max number of priority passes a player may - * have for this test (default = 100) + * have for this test (default = 100) */ public void setMaxCallsWithoutAction(int maxCallsWithoutAction) { this.maxCallsWithoutAction = maxCallsWithoutAction; @@ -358,7 +360,7 @@ public class TestPlayer implements Player { return false; } - if (nameOrAliase.startsWith("@") && object.getId().equals(getAliasByName(nameOrAliase))) { + if (nameOrAliase.startsWith(ALIASE_PREFIX) && object.getId().equals(getAliasByName(nameOrAliase))) { return true; } @@ -498,9 +500,9 @@ public class TestPlayer implements Player { for (PlayerAction action : tempActions) { if (action.getTurnNum() == game.getTurnNum() && action.getStep() == game.getStep().getType()) { - if (action.getAction().startsWith("activate:")) { + if (action.getAction().startsWith(ACTIVATE_ABILITY)) { String command = action.getAction(); - command = command.substring(command.indexOf("activate:") + 9); + command = command.substring(command.indexOf(ACTIVATE_ABILITY) + ACTIVATE_ABILITY.length()); groupsForTargetHandling = null; String[] groups = command.split("\\$"); if (groups.length > 2 && !checkExecuteCondition(groups, game)) { @@ -615,11 +617,11 @@ public class TestPlayer implements Player { actions.remove(action); } } - } else if (action.getAction().startsWith("check:")) { + } else if (action.getAction().startsWith(CHECK_PREFIX)) { String command = action.getAction(); - command = command.substring(command.indexOf("check:") + "check:".length()); + command = command.substring(command.indexOf(CHECK_PREFIX) + CHECK_PREFIX.length()); - String[] params = command.split("@"); + String[] params = command.split(CHECK_PARAM_DELIMETER); boolean wasProccessed = false; if (params.length > 0) { @@ -749,7 +751,7 @@ public class TestPlayer implements Player { String command = action.getAction(); command = command.substring(command.indexOf("show:") + "show:".length()); - String[] params = command.split("@"); + String[] params = command.split(CHECK_PARAM_DELIMETER); boolean wasProccessed = false; if (params.length > 0) { @@ -863,13 +865,21 @@ public class TestPlayer implements Player { } private Permanent findPermanentWithAssert(PlayerAction action, Game game, Player player, String cardName) { - Permanent founded = null; for (Permanent perm : game.getBattlefield().getAllPermanents()) { - if (perm.getName().equals(cardName) && perm.getControllerId().equals(player.getId())) { - return perm; + // need by controller + if (!perm.getControllerId().equals(player.getId())) { + continue; } + + // need by alias or by name + if (!isObjectHaveTargetNameOrAliase(perm, cardName)) { + continue; + } + + // all fine + return perm; } - Assert.assertNotNull(action.getActionName() + " - can''t find permanent to check: " + cardName, founded); + Assert.fail(action.getActionName() + " - can''t find permanent to check: " + cardName); return null; } @@ -903,12 +913,12 @@ public class TestPlayer implements Player { List data = cards.stream() .map(c -> (((c instanceof PermanentToken) ? "[T] " : "[C] ") - + c.getIdName() - + (c.isCopy() ? " [copy of " + c.getCopyFrom().getId().toString().substring(0, 3) + "]" : "") - + " - " + c.getPower().getValue() + "/" + c.getToughness().getValue() - + (c.isPlaneswalker() ? " - L" + c.getCounters(game).getCount(CounterType.LOYALTY) : "") - + ", " + (c.isTapped() ? "Tapped" : "Untapped") - + (c.getAttachedTo() == null ? "" : ", attached to " + game.getPermanent(c.getAttachedTo()).getIdName()))) + + c.getIdName() + + (c.isCopy() ? " [copy of " + c.getCopyFrom().getId().toString().substring(0, 3) + "]" : "") + + " - " + c.getPower().getValue() + "/" + c.getToughness().getValue() + + (c.isPlaneswalker() ? " - L" + c.getCounters(game).getCount(CounterType.LOYALTY) : "") + + ", " + (c.isTapped() ? "Tapped" : "Untapped") + + (c.getAttachedTo() == null ? "" : ", attached to " + game.getPermanent(c.getAttachedTo()).getIdName()))) .sorted() .collect(Collectors.toList()); @@ -932,11 +942,11 @@ public class TestPlayer implements Player { List data = abilities.stream() .map(a -> (a.getZone() + " -> " - + a.getSourceObject(game).getIdName() + " -> " - + (a.toString().length() > 0 - ? a.toString().substring(0, Math.min(20, a.toString().length()) - 1) - : a.getClass().getSimpleName()) - + "...")) + + a.getSourceObject(game).getIdName() + " -> " + + (a.toString().length() > 0 + ? a.toString().substring(0, Math.min(20, a.toString().length()) - 1) + : a.getClass().getSimpleName()) + + "...")) .sorted() .collect(Collectors.toList()); @@ -1290,7 +1300,7 @@ public class TestPlayer implements Player { UUID defenderId = null; boolean mustAttackByAction = false; boolean madeAttackByAction = false; - for (Iterator it = actions.iterator(); it.hasNext();) { + for (Iterator it = actions.iterator(); it.hasNext(); ) { PlayerAction action = it.next(); if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) { mustAttackByAction = true; @@ -1470,6 +1480,24 @@ public class TestPlayer implements Player { return "Target: " + (o != null ? o.getClass().getSimpleName() + ": " + o.getMessage() : "null"); } + private void assertAliaseSupportInChoices(boolean methodSupportAliases) { + // TODO: add alias support for all false methods (replace name compare by isObjectHaveTargetNameOrAliase) + if (!methodSupportAliases && !choices.isEmpty()) { + if (choices.get(0).contains(ALIASE_PREFIX)) { + Assert.fail("That choice method do not support aliases, but found " + choices.get(0)); + } + } + } + + private void assertAliaseSupportInTargets(boolean methodSupportAliases) { + // TODO: add alias support for all false methods (replace name compare by isObjectHaveTargetNameOrAliase) + if (!methodSupportAliases && !targets.isEmpty()) { + if (targets.get(0).contains(ALIASE_PREFIX)) { + Assert.fail("That target method do not support aliases, but found " + targets.get(0)); + } + } + } + private void chooseStrictModeFailed(String choiceType, Game game, String reason) { if (strictChooseMode) { Assert.fail("Missing " + choiceType + " def for" @@ -1508,6 +1536,7 @@ public class TestPlayer implements Player { @Override public boolean choose(Outcome outcome, Choice choice, Game game) { + assertAliaseSupportInChoices(false); if (!choices.isEmpty()) { if (choice.setChoiceByAnswers(choices, true)) { return true; @@ -1525,6 +1554,7 @@ public class TestPlayer implements Player { if (rEffects.size() <= 1) { return 0; } + assertAliaseSupportInChoices(false); if (!choices.isEmpty()) { String choice = choices.get(0); @@ -1547,13 +1577,14 @@ public class TestPlayer implements Player { @Override public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map options) { + // support aliases in choices UUID abilityControllerId = computerPlayer.getId(); if (target.getTargetController() != null && target.getAbilityController() != null) { abilityControllerId = target.getAbilityController(); } + assertAliaseSupportInChoices(true); if (!choices.isEmpty()) { - List usedChoices = new ArrayList<>(); List usedTargets = new ArrayList<>(); @@ -1571,8 +1602,8 @@ public class TestPlayer implements Player { } else { filterPermanent = ((TargetPermanent) target.getOriginalTarget()).getFilter(); } - for (String choose2 : choices) { - String[] targetList = choose2.split("\\^"); + for (String choiceRecord : choices) { + String[] targetList = choiceRecord.split("\\^"); boolean targetFound = false; for (String targetName : targetList) { boolean originOnly = false; @@ -1591,7 +1622,7 @@ public class TestPlayer implements Player { if (target.getTargets().contains(permanent.getId())) { continue; } - if (permanent.getName().equals(targetName)) { + if (isObjectHaveTargetNameOrAliase(permanent, targetName)) { if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) { if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { target.add(permanent.getId(), game); @@ -1599,7 +1630,7 @@ public class TestPlayer implements Player { break; } } - } else if ((permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { + } else if ((permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { // TODO: remove search by exp code? if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) { if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { target.add(permanent.getId(), game); @@ -1611,7 +1642,7 @@ public class TestPlayer implements Player { } } if (targetFound) { - choices.remove(choose2); + choices.remove(choiceRecord); return true; } } @@ -1656,7 +1687,7 @@ public class TestPlayer implements Player { CheckTargetsList: for (UUID targetId : possibleCards) { MageObject targetObject = game.getObject(targetId); - if (targetObject != null && targetObject.getName().equals(possibleChoice)) { + if (isObjectHaveTargetNameOrAliase(targetObject, possibleChoice)) { if (target.canTarget(targetObject.getId(), game)) { // only unique targets if (usedTargets.contains(targetObject.getId())) { @@ -1702,26 +1733,26 @@ public class TestPlayer implements Player { Set possibleTargets; TargetSource t = ((TargetSource) target.getOriginalTarget()); possibleTargets = t.possibleTargets(sourceId, abilityControllerId, game); - for (String choose2 : choices) { - String[] targetList = choose2.split("\\^"); + for (String choiceRecord : choices) { + String[] targetList = choiceRecord.split("\\^"); boolean targetFound = false; for (String targetName : targetList) { for (UUID targetId : possibleTargets) { MageObject targetObject = game.getObject(targetId); if (targetObject != null) { - if (targetObject.getName().equals(targetName)) { + if (isObjectHaveTargetNameOrAliase(targetObject, targetName)) { List alreadyTargetted = target.getTargets(); if (t.canTarget(targetObject.getId(), game)) { if (alreadyTargetted != null && !alreadyTargetted.contains(targetObject.getId())) { target.add(targetObject.getId(), game); - choices.remove(choose2); + choices.remove(choiceRecord); targetFound = true; } } } } if (targetFound) { - choices.remove(choose2); + choices.remove(choiceRecord); return true; } } @@ -1774,12 +1805,13 @@ public class TestPlayer implements Player { } UUID sourceId = source != null ? source.getSourceId() : null; + assertAliaseSupportInTargets(false); if (!targets.isEmpty()) { // skip targets if (targets.get(0).equals(TARGET_SKIP)) { Assert.assertTrue("found skip target, but it require more targets, needs " - + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more", + + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more", target.getTargets().size() >= target.getMinNumberOfTargets()); targets.remove(0); return true; @@ -2016,6 +2048,7 @@ public class TestPlayer implements Player { // wrong target settings by addTarget // how to fix: implement target class processing above + assertAliaseSupportInTargets(false); if (!targets.isEmpty()) { String message; @@ -2040,6 +2073,7 @@ public class TestPlayer implements Player { @Override public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) { + assertAliaseSupportInTargets(false); if (!targets.isEmpty()) { for (String targetDefinition : targets) { String[] targetList = targetDefinition.split("\\^"); @@ -2069,6 +2103,7 @@ public class TestPlayer implements Player { @Override public TriggeredAbility chooseTriggeredAbility(List abilities, Game game) { + assertAliaseSupportInChoices(false); if (!choices.isEmpty()) { for (TriggeredAbility ability : abilities) { if (ability.toString().startsWith(choices.get(0))) { @@ -2082,7 +2117,7 @@ public class TestPlayer implements Player { this.chooseStrictModeFailed("choice", game, "Triggered list (total " + abilities.size() + "):\n" - + abilities.stream().map(a -> getInfo(a, game)).collect(Collectors.joining("\n"))); + + abilities.stream().map(a -> getInfo(a, game)).collect(Collectors.joining("\n"))); return computerPlayer.chooseTriggeredAbility(abilities, game); } @@ -2096,6 +2131,7 @@ public class TestPlayer implements Player { if (message.equals("Scry 1?")) { return false; } + assertAliaseSupportInChoices(false); if (!choices.isEmpty()) { if (choices.get(0).equals("No")) { choices.remove(0); @@ -2117,6 +2153,7 @@ public class TestPlayer implements Player { @Override public int announceXMana(int min, int max, int multiplier, String message, Game game, Ability ability) { + assertAliaseSupportInChoices(false); if (!choices.isEmpty()) { for (String choice : choices) { if (choice.startsWith("X=")) { @@ -2134,6 +2171,7 @@ public class TestPlayer implements Player { @Override public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variablCost) { + assertAliaseSupportInChoices(false); if (!choices.isEmpty()) { if (choices.get(0).startsWith("X=")) { int xValue = Integer.parseInt(choices.get(0).substring(2)); @@ -2149,6 +2187,7 @@ public class TestPlayer implements Player { @Override public int getAmount(int min, int max, String message, Game game) { + assertAliaseSupportInChoices(false); if (!choices.isEmpty()) { if (choices.get(0).startsWith("X=")) { int xValue = Integer.parseInt(choices.get(0).substring(2)); @@ -3258,7 +3297,7 @@ public class TestPlayer implements Player { @Override public boolean choose(Outcome outcome, Target target, - UUID sourceId, Game game + UUID sourceId, Game game ) { // needed to call here the TestPlayer because it's overwitten return choose(outcome, target, sourceId, game, null); @@ -3266,8 +3305,9 @@ public class TestPlayer implements Player { @Override public boolean choose(Outcome outcome, Cards cards, - TargetCard target, Game game + TargetCard target, Game game ) { + assertAliaseSupportInChoices(false); if (!choices.isEmpty()) { for (String choose2 : choices) { // TODO: More targetting to fix @@ -3302,19 +3342,20 @@ public class TestPlayer implements Player { @Override public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, - Ability source, Game game + Ability source, Game game ) { // chooseTargetAmount calls for EACH target cycle (e.g. one target per click, see TargetAmount) // if use want to stop choosing then chooseTargetAmount must return false (example: up to xxx) Assert.assertNotEquals("chooseTargetAmount needs non zero amount remaining", 0, target.getAmountRemaining()); + assertAliaseSupportInTargets(false); if (!targets.isEmpty()) { // skip targets if (targets.get(0).equals(TARGET_SKIP)) { Assert.assertTrue("found skip target, but it require more targets, needs " - + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more", + + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more", target.getTargets().size() >= target.getMinNumberOfTargets()); targets.remove(0); return false; // false in chooseTargetAmount = stop to choose @@ -3367,15 +3408,15 @@ public class TestPlayer implements Player { @Override public boolean choosePile(Outcome outcome, String message, - List pile1, List pile2, - Game game + List pile1, List pile2, + Game game ) { return computerPlayer.choosePile(outcome, message, pile1, pile2, game); } @Override public boolean playMana(Ability ability, ManaCost unpaid, - String promptText, Game game + String promptText, Game game ) { groupsForTargetHandling = null; return computerPlayer.playMana(ability, unpaid, promptText, game); @@ -3389,15 +3430,15 @@ public class TestPlayer implements Player { @Override public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, - List blockerOrder, Game game + List blockerOrder, Game game ) { return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); } @Override public void assignDamage(int damage, List targets, - String singleTargetName, UUID sourceId, - Game game + String singleTargetName, UUID sourceId, + Game game ) { computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game); } @@ -3416,14 +3457,14 @@ public class TestPlayer implements Player { @Override public void pickCard(List cards, Deck deck, - Draft draft + Draft draft ) { computerPlayer.pickCard(cards, deck, draft); } @Override public boolean scry(int value, Ability source, - Game game + Game game ) { // Don't scry at the start of the game. if (game.getTurnNum() == 1 && game.getStep() == null) { @@ -3434,44 +3475,44 @@ public class TestPlayer implements Player { @Override public boolean surveil(int value, Ability source, - Game game + Game game ) { return computerPlayer.surveil(value, source, game); } @Override public boolean moveCards(Card card, Zone toZone, - Ability source, Game game + Ability source, Game game ) { return computerPlayer.moveCards(card, toZone, source, game); } @Override public boolean moveCards(Card card, Zone toZone, - Ability source, Game game, - boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects + Ability source, Game game, + boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects ) { return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects); } @Override public boolean moveCards(Cards cards, Zone toZone, - Ability source, Game game + Ability source, Game game ) { return computerPlayer.moveCards(cards, toZone, source, game); } @Override public boolean moveCards(Set cards, Zone toZone, - Ability source, Game game + Ability source, Game game ) { return computerPlayer.moveCards(cards, toZone, source, game); } @Override public boolean moveCards(Set cards, Zone toZone, - Ability source, Game game, - boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects + Ability source, Game game, + boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects ) { return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects); } @@ -3560,6 +3601,7 @@ public class TestPlayer implements Player { @Override public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) { String allInfo = ""; + assertAliaseSupportInChoices(false); if (!choices.isEmpty()) { Map useable = PlayerImpl.getSpellAbilities(this.getId(), card, game.getState().getZone(card.getId()), game); for (ActivatedAbility ability : useable.values()) { 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 1f8c4638e7..ece92f1f47 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 @@ -34,6 +34,7 @@ import org.mage.test.serverside.base.MageTestPlayerBase; import java.io.FileNotFoundException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.UUID; import java.util.regex.Matcher; @@ -43,10 +44,33 @@ import java.util.stream.Collectors; /** * API for test initialization and asserting the test results. * - * @author ayratn + * @author ayratn, JayDi85 */ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implements CardTestAPI { + public static final String ALIASE_PREFIX = "@"; // don't change -- it uses in user's tests + public static final String CHECK_PARAM_DELIMETER = "#"; + public static final String CHECK_PREFIX = "check:"; // prefix for all check commands + + static { + // aliases can be used in check commands, so all prefixes and delimeters must be unique + // already uses by targets: ^ $ [ ] + Assert.assertFalse("prefix must be unique", CHECK_PARAM_DELIMETER.contains(ALIASE_PREFIX)); + Assert.assertFalse("prefix must be unique", CHECK_PREFIX.contains(ALIASE_PREFIX)); + Assert.assertFalse("prefix must be unique", ALIASE_PREFIX.contains(CHECK_PREFIX)); + } + + // prefix for activate commands + public static final String ACTIVATE_ABILITY = "activate:"; + public static final String ACTIVATE_PLAY = "activate:Play "; + public static final String ACTIVATE_CAST = "activate:Cast "; + + static { + // cards can be played/casted by activate ability command too + Assert.assertTrue("musts contains activate ability part", ACTIVATE_PLAY.startsWith(ACTIVATE_ABILITY)); + Assert.assertTrue("musts contains activate ability part", ACTIVATE_CAST.startsWith(ACTIVATE_ABILITY)); + } + // TODO: add target player param to commands public static final String CHECK_COMMAND_PT = "PT"; public static final String CHECK_COMMAND_DAMAGE = "DAMAGE"; @@ -287,9 +311,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement // check commands private void check(String checkName, int turnNum, PhaseStep step, TestPlayer player, String command, String... params) { - String res = "check:" + command; + String res = CHECK_PREFIX + command; for (String param : params) { - res += "@" + param; + res += CHECK_PARAM_DELIMETER + param; } player.addAction(checkName, turnNum, step, res); } @@ -386,7 +410,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement private void show(String showName, int turnNum, PhaseStep step, TestPlayer player, String command, String... params) { String res = "show:" + command; for (String param : params) { - res += "@" + param; + res += CHECK_PARAM_DELIMETER + param; } player.addAction(showName, turnNum, step, res); } @@ -494,9 +518,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement // aliases for mage objects String aliasName = ""; boolean useAliasMultiNames = (count != 1); - if (cardName.contains("@")) { - aliasName = cardName.substring(cardName.indexOf("@") + 1); - cardName = cardName.substring(0, cardName.indexOf("@")); + if (cardName.contains(ALIASE_PREFIX)) { + aliasName = cardName.substring(cardName.indexOf(ALIASE_PREFIX) + ALIASE_PREFIX.length()); + cardName = cardName.substring(0, cardName.indexOf(ALIASE_PREFIX)); } // one card = one aliase, massive adds can use auto-name @@ -1339,23 +1363,27 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public void playLand(int turnNum, PhaseStep step, TestPlayer player, String cardName) { //Assert.assertNotEquals("", cardName); - player.addAction(turnNum, step, "activate:Play " + cardName); + assertAliaseSupportInActivateCommand(cardName, false); + player.addAction(turnNum, step, ACTIVATE_PLAY + cardName); } public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName) { //Assert.assertNotEquals("", cardName); - player.addAction(turnNum, step, "activate:Cast " + cardName); + assertAliaseSupportInActivateCommand(cardName, false); + player.addAction(turnNum, step, ACTIVATE_CAST + cardName); } public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, Player target) { //Assert.assertNotEquals("", cardName); // warning, target in spell cast command setups without choose target call - player.addAction(turnNum, step, "activate:Cast " + cardName + "$targetPlayer=" + target.getName()); + assertAliaseSupportInActivateCommand(cardName, false); + player.addAction(turnNum, step, ACTIVATE_CAST + cardName + "$targetPlayer=" + target.getName()); } public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, Player target, int manaInPool) { //Assert.assertNotEquals("", cardName); - player.addAction(turnNum, step, "activate:Cast " + cardName + "$targetPlayer=" + target.getName() + "$manaInPool=" + manaInPool); + assertAliaseSupportInActivateCommand(cardName, false); + player.addAction(turnNum, step, ACTIVATE_CAST + cardName + "$targetPlayer=" + target.getName() + "$manaInPool=" + manaInPool); } public void waitStackResolved(int turnNum, PhaseStep step, TestPlayer player) { @@ -1396,7 +1424,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement */ public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName) { //Assert.assertNotEquals("", cardName); - player.addAction(turnNum, step, "activate:Cast " + cardName + "$target=" + targetName); + assertAliaseSupportInActivateCommand(cardName, false); + player.addAction(turnNum, step, ACTIVATE_CAST + cardName + "$target=" + targetName); } public enum StackClause { @@ -1435,12 +1464,15 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement */ public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName, String spellOnStack, StackClause clause) { //Assert.assertNotEquals("", cardName); + assertAliaseSupportInActivateCommand(cardName, false); + assertAliaseSupportInActivateCommand(targetName, false); + assertAliaseSupportInActivateCommand(spellOnStack, false); if (StackClause.WHILE_ON_STACK == clause) { - player.addAction(turnNum, step, "activate:Cast " + cardName + player.addAction(turnNum, step, ACTIVATE_CAST + cardName + '$' + (targetName != null && targetName.startsWith("target") ? targetName : "target=" + targetName) + "$spellOnStack=" + spellOnStack); } else { - player.addAction(turnNum, step, "activate:Cast " + cardName + player.addAction(turnNum, step, ACTIVATE_CAST + cardName + '$' + (targetName != null && targetName.startsWith("target") ? targetName : "target=" + targetName) + "$!spellOnStack=" + spellOnStack); } @@ -1448,7 +1480,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName, String spellOnStack, String spellOnTopOfStack) { //Assert.assertNotEquals("", cardName); - String action = "activate:Cast " + cardName + "$target=" + targetName; + assertAliaseSupportInActivateCommand(cardName, false); + assertAliaseSupportInActivateCommand(targetName, false); + assertAliaseSupportInActivateCommand(spellOnStack, false); + assertAliaseSupportInActivateCommand(spellOnTopOfStack, false); + String action = ACTIVATE_CAST + cardName + "$target=" + targetName; if (spellOnStack != null && !spellOnStack.isEmpty()) { action += "$spellOnStack=" + spellOnStack; } @@ -1464,17 +1500,23 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability) { // TODO: it's uses computerPlayer to execute, only ability target will work, but choices and targets commands aren't - player.addAction(turnNum, step, "activate:" + ability); + assertAliaseSupportInActivateCommand(ability, false); + player.addAction(turnNum, step, ACTIVATE_ABILITY + ability); } public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, Player target) { // TODO: it's uses computerPlayer to execute, only ability target will work, but choices and targets commands aren't - player.addAction(turnNum, step, "activate:" + ability + "$targetPlayer=" + target.getName()); + assertAliaseSupportInActivateCommand(ability, false); + player.addAction(turnNum, step, ACTIVATE_ABILITY + ability + "$targetPlayer=" + target.getName()); } public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String... targetNames) { // TODO: it's uses computerPlayer to execute, only ability target will work, but choices and targets commands aren't - player.addAction(turnNum, step, "activate:" + ability + "$target=" + String.join("^", targetNames)); + assertAliaseSupportInActivateCommand(ability, false); + Arrays.stream(targetNames).forEach(n -> { + assertAliaseSupportInActivateCommand(n, false); + }); + player.addAction(turnNum, step, ACTIVATE_ABILITY + ability + "$target=" + String.join("^", targetNames)); } public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack) { @@ -1493,7 +1535,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param clause */ public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack, StackClause clause) { - StringBuilder sb = new StringBuilder("activate:").append(ability); + assertAliaseSupportInActivateCommand(ability, false); + assertAliaseSupportInActivateCommand(targetName, false); + StringBuilder sb = new StringBuilder(ACTIVATE_ABILITY).append(ability); if (targetName != null && !targetName.isEmpty()) { sb.append("$target=").append(targetName); } @@ -1553,7 +1597,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * For use choices set "Yes" or "No" the the choice string. For X values set * "X=[xValue]" example: for X=3 set choice string to "X=3". *
For ColorChoice use "Red", "Green", "Blue", "Black" or "White" - * + * * @param player * @param choice */ @@ -1676,4 +1720,13 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement if (playerC != null) waitStackResolved(turnNum, step, playerC); if (playerD != null) waitStackResolved(turnNum, step, playerD); } + + private void assertAliaseSupportInActivateCommand(String targetName, boolean methodSupportAliases) { + // TODO: add alias support for all false methods (replace name compare by isObjectHaveTargetNameOrAliase in activate code) + if (!methodSupportAliases) { + if (targetName != null && targetName.contains(ALIASE_PREFIX)) { + Assert.fail("That activate command do not support aliases, but found " + targetName); + } + } + } }