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;
This commit is contained in:
Oleg Agafonov 2020-01-27 06:00:46 +04:00
parent 016f5fa9f5
commit ec6b0b94c6
4 changed files with 187 additions and 88 deletions

View file

@ -212,11 +212,14 @@ public class CloneTest extends CardTestPlayerBase {
setChoice(playerA, "Elf"); setChoice(playerA, "Elf");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Clone"); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Clone");
setChoice(playerB, "Yes");
setChoice(playerB, "Adaptive Automaton"); setChoice(playerB, "Adaptive Automaton");
setChoice(playerB, "Goblin"); setChoice(playerB, "Goblin");
setStrictChooseMode(true);
setStopAt(2, PhaseStep.END_COMBAT); setStopAt(2, PhaseStep.END_COMBAT);
execute(); execute();
assertAllCommandsUsed();
assertPermanentCount(playerA, "Adaptive Automaton", 1); assertPermanentCount(playerA, "Adaptive Automaton", 1);
Permanent original = getPermanent("Adaptive Automaton", playerA); Permanent original = getPermanent("Adaptive Automaton", playerA);

View file

@ -1,4 +1,3 @@
package org.mage.test.cards.copy; package org.mage.test.cards.copy;
import mage.constants.PhaseStep; import mage.constants.PhaseStep;
@ -8,7 +7,6 @@ import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase; import org.mage.test.serverside.base.CardTestPlayerBase;
/** /**
*
* @author LevelX2 * @author LevelX2
*/ */
public class IdentityThiefTest extends CardTestPlayerBase { public class IdentityThiefTest extends CardTestPlayerBase {
@ -33,10 +31,13 @@ public class IdentityThiefTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Molten Sentry"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Molten Sentry");
attack(2, playerB, "Identity Thief"); attack(2, playerB, "Identity Thief");
setChoice(playerB, "Yes");
addTarget(playerB, "Molten Sentry"); addTarget(playerB, "Molten Sentry");
setStrictChooseMode(true);
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
execute(); execute();
assertAllCommandsUsed();
assertExileCount(playerA, 1); assertExileCount(playerA, 1);
assertExileCount("Molten Sentry", 1); assertExileCount("Molten Sentry", 1);

View file

@ -1,10 +1,5 @@
package org.mage.test.player; 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.MageItem;
import mage.MageObject; import mage.MageObject;
import mage.MageObjectReference; import mage.MageObjectReference;
@ -61,6 +56,13 @@ import mage.util.CardUtil;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Ignore; 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.*; import static org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl.*;
/** /**
@ -140,7 +142,7 @@ public class TestPlayer implements Player {
} }
public UUID getAliasByName(String searchName) { public UUID getAliasByName(String searchName) {
if (searchName.startsWith("@")) { if (searchName.startsWith(ALIASE_PREFIX)) {
return this.aliases.getOrDefault(searchName.substring(1), null); return this.aliases.getOrDefault(searchName.substring(1), null);
} else { } else {
return this.aliases.getOrDefault(searchName, null); 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 * @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) { public void setMaxCallsWithoutAction(int maxCallsWithoutAction) {
this.maxCallsWithoutAction = maxCallsWithoutAction; this.maxCallsWithoutAction = maxCallsWithoutAction;
@ -358,7 +360,7 @@ public class TestPlayer implements Player {
return false; return false;
} }
if (nameOrAliase.startsWith("@") && object.getId().equals(getAliasByName(nameOrAliase))) { if (nameOrAliase.startsWith(ALIASE_PREFIX) && object.getId().equals(getAliasByName(nameOrAliase))) {
return true; return true;
} }
@ -498,9 +500,9 @@ public class TestPlayer implements Player {
for (PlayerAction action : tempActions) { for (PlayerAction action : tempActions) {
if (action.getTurnNum() == game.getTurnNum() && action.getStep() == game.getStep().getType()) { 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(); String command = action.getAction();
command = command.substring(command.indexOf("activate:") + 9); command = command.substring(command.indexOf(ACTIVATE_ABILITY) + ACTIVATE_ABILITY.length());
groupsForTargetHandling = null; groupsForTargetHandling = null;
String[] groups = command.split("\\$"); String[] groups = command.split("\\$");
if (groups.length > 2 && !checkExecuteCondition(groups, game)) { if (groups.length > 2 && !checkExecuteCondition(groups, game)) {
@ -615,11 +617,11 @@ public class TestPlayer implements Player {
actions.remove(action); actions.remove(action);
} }
} }
} else if (action.getAction().startsWith("check:")) { } else if (action.getAction().startsWith(CHECK_PREFIX)) {
String command = action.getAction(); 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; boolean wasProccessed = false;
if (params.length > 0) { if (params.length > 0) {
@ -749,7 +751,7 @@ public class TestPlayer implements Player {
String command = action.getAction(); String command = action.getAction();
command = command.substring(command.indexOf("show:") + "show:".length()); command = command.substring(command.indexOf("show:") + "show:".length());
String[] params = command.split("@"); String[] params = command.split(CHECK_PARAM_DELIMETER);
boolean wasProccessed = false; boolean wasProccessed = false;
if (params.length > 0) { if (params.length > 0) {
@ -863,13 +865,21 @@ public class TestPlayer implements Player {
} }
private Permanent findPermanentWithAssert(PlayerAction action, Game game, Player player, String cardName) { private Permanent findPermanentWithAssert(PlayerAction action, Game game, Player player, String cardName) {
Permanent founded = null;
for (Permanent perm : game.getBattlefield().getAllPermanents()) { for (Permanent perm : game.getBattlefield().getAllPermanents()) {
if (perm.getName().equals(cardName) && perm.getControllerId().equals(player.getId())) { // need by controller
return perm; 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; return null;
} }
@ -903,12 +913,12 @@ public class TestPlayer implements Player {
List<String> data = cards.stream() List<String> data = cards.stream()
.map(c -> (((c instanceof PermanentToken) ? "[T] " : "[C] ") .map(c -> (((c instanceof PermanentToken) ? "[T] " : "[C] ")
+ c.getIdName() + c.getIdName()
+ (c.isCopy() ? " [copy of " + c.getCopyFrom().getId().toString().substring(0, 3) + "]" : "") + (c.isCopy() ? " [copy of " + c.getCopyFrom().getId().toString().substring(0, 3) + "]" : "")
+ " - " + c.getPower().getValue() + "/" + c.getToughness().getValue() + " - " + c.getPower().getValue() + "/" + c.getToughness().getValue()
+ (c.isPlaneswalker() ? " - L" + c.getCounters(game).getCount(CounterType.LOYALTY) : "") + (c.isPlaneswalker() ? " - L" + c.getCounters(game).getCount(CounterType.LOYALTY) : "")
+ ", " + (c.isTapped() ? "Tapped" : "Untapped") + ", " + (c.isTapped() ? "Tapped" : "Untapped")
+ (c.getAttachedTo() == null ? "" : ", attached to " + game.getPermanent(c.getAttachedTo()).getIdName()))) + (c.getAttachedTo() == null ? "" : ", attached to " + game.getPermanent(c.getAttachedTo()).getIdName())))
.sorted() .sorted()
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -932,11 +942,11 @@ public class TestPlayer implements Player {
List<String> data = abilities.stream() List<String> data = abilities.stream()
.map(a -> (a.getZone() + " -> " .map(a -> (a.getZone() + " -> "
+ a.getSourceObject(game).getIdName() + " -> " + a.getSourceObject(game).getIdName() + " -> "
+ (a.toString().length() > 0 + (a.toString().length() > 0
? a.toString().substring(0, Math.min(20, a.toString().length()) - 1) ? a.toString().substring(0, Math.min(20, a.toString().length()) - 1)
: a.getClass().getSimpleName()) : a.getClass().getSimpleName())
+ "...")) + "..."))
.sorted() .sorted()
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -1290,7 +1300,7 @@ public class TestPlayer implements Player {
UUID defenderId = null; UUID defenderId = null;
boolean mustAttackByAction = false; boolean mustAttackByAction = false;
boolean madeAttackByAction = false; boolean madeAttackByAction = false;
for (Iterator<org.mage.test.player.PlayerAction> it = actions.iterator(); it.hasNext();) { for (Iterator<org.mage.test.player.PlayerAction> it = actions.iterator(); it.hasNext(); ) {
PlayerAction action = it.next(); PlayerAction action = it.next();
if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) { if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) {
mustAttackByAction = true; mustAttackByAction = true;
@ -1470,6 +1480,24 @@ public class TestPlayer implements Player {
return "Target: " + (o != null ? o.getClass().getSimpleName() + ": " + o.getMessage() : "null"); 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) { private void chooseStrictModeFailed(String choiceType, Game game, String reason) {
if (strictChooseMode) { if (strictChooseMode) {
Assert.fail("Missing " + choiceType + " def for" Assert.fail("Missing " + choiceType + " def for"
@ -1508,6 +1536,7 @@ public class TestPlayer implements Player {
@Override @Override
public boolean choose(Outcome outcome, Choice choice, Game game) { public boolean choose(Outcome outcome, Choice choice, Game game) {
assertAliaseSupportInChoices(false);
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
if (choice.setChoiceByAnswers(choices, true)) { if (choice.setChoiceByAnswers(choices, true)) {
return true; return true;
@ -1525,6 +1554,7 @@ public class TestPlayer implements Player {
if (rEffects.size() <= 1) { if (rEffects.size() <= 1) {
return 0; return 0;
} }
assertAliaseSupportInChoices(false);
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
String choice = choices.get(0); String choice = choices.get(0);
@ -1547,13 +1577,14 @@ public class TestPlayer implements Player {
@Override @Override
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map<String, Serializable> options) { public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map<String, Serializable> options) {
// support aliases in choices
UUID abilityControllerId = computerPlayer.getId(); UUID abilityControllerId = computerPlayer.getId();
if (target.getTargetController() != null && target.getAbilityController() != null) { if (target.getTargetController() != null && target.getAbilityController() != null) {
abilityControllerId = target.getAbilityController(); abilityControllerId = target.getAbilityController();
} }
assertAliaseSupportInChoices(true);
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
List<String> usedChoices = new ArrayList<>(); List<String> usedChoices = new ArrayList<>();
List<UUID> usedTargets = new ArrayList<>(); List<UUID> usedTargets = new ArrayList<>();
@ -1571,8 +1602,8 @@ public class TestPlayer implements Player {
} else { } else {
filterPermanent = ((TargetPermanent) target.getOriginalTarget()).getFilter(); filterPermanent = ((TargetPermanent) target.getOriginalTarget()).getFilter();
} }
for (String choose2 : choices) { for (String choiceRecord : choices) {
String[] targetList = choose2.split("\\^"); String[] targetList = choiceRecord.split("\\^");
boolean targetFound = false; boolean targetFound = false;
for (String targetName : targetList) { for (String targetName : targetList) {
boolean originOnly = false; boolean originOnly = false;
@ -1591,7 +1622,7 @@ public class TestPlayer implements Player {
if (target.getTargets().contains(permanent.getId())) { if (target.getTargets().contains(permanent.getId())) {
continue; continue;
} }
if (permanent.getName().equals(targetName)) { if (isObjectHaveTargetNameOrAliase(permanent, targetName)) {
if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) { if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) {
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
target.add(permanent.getId(), game); target.add(permanent.getId(), game);
@ -1599,7 +1630,7 @@ public class TestPlayer implements Player {
break; 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 (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) {
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
target.add(permanent.getId(), game); target.add(permanent.getId(), game);
@ -1611,7 +1642,7 @@ public class TestPlayer implements Player {
} }
} }
if (targetFound) { if (targetFound) {
choices.remove(choose2); choices.remove(choiceRecord);
return true; return true;
} }
} }
@ -1656,7 +1687,7 @@ public class TestPlayer implements Player {
CheckTargetsList: CheckTargetsList:
for (UUID targetId : possibleCards) { for (UUID targetId : possibleCards) {
MageObject targetObject = game.getObject(targetId); MageObject targetObject = game.getObject(targetId);
if (targetObject != null && targetObject.getName().equals(possibleChoice)) { if (isObjectHaveTargetNameOrAliase(targetObject, possibleChoice)) {
if (target.canTarget(targetObject.getId(), game)) { if (target.canTarget(targetObject.getId(), game)) {
// only unique targets // only unique targets
if (usedTargets.contains(targetObject.getId())) { if (usedTargets.contains(targetObject.getId())) {
@ -1702,26 +1733,26 @@ public class TestPlayer implements Player {
Set<UUID> possibleTargets; Set<UUID> possibleTargets;
TargetSource t = ((TargetSource) target.getOriginalTarget()); TargetSource t = ((TargetSource) target.getOriginalTarget());
possibleTargets = t.possibleTargets(sourceId, abilityControllerId, game); possibleTargets = t.possibleTargets(sourceId, abilityControllerId, game);
for (String choose2 : choices) { for (String choiceRecord : choices) {
String[] targetList = choose2.split("\\^"); String[] targetList = choiceRecord.split("\\^");
boolean targetFound = false; boolean targetFound = false;
for (String targetName : targetList) { for (String targetName : targetList) {
for (UUID targetId : possibleTargets) { for (UUID targetId : possibleTargets) {
MageObject targetObject = game.getObject(targetId); MageObject targetObject = game.getObject(targetId);
if (targetObject != null) { if (targetObject != null) {
if (targetObject.getName().equals(targetName)) { if (isObjectHaveTargetNameOrAliase(targetObject, targetName)) {
List<UUID> alreadyTargetted = target.getTargets(); List<UUID> alreadyTargetted = target.getTargets();
if (t.canTarget(targetObject.getId(), game)) { if (t.canTarget(targetObject.getId(), game)) {
if (alreadyTargetted != null && !alreadyTargetted.contains(targetObject.getId())) { if (alreadyTargetted != null && !alreadyTargetted.contains(targetObject.getId())) {
target.add(targetObject.getId(), game); target.add(targetObject.getId(), game);
choices.remove(choose2); choices.remove(choiceRecord);
targetFound = true; targetFound = true;
} }
} }
} }
} }
if (targetFound) { if (targetFound) {
choices.remove(choose2); choices.remove(choiceRecord);
return true; return true;
} }
} }
@ -1774,12 +1805,13 @@ public class TestPlayer implements Player {
} }
UUID sourceId = source != null ? source.getSourceId() : null; UUID sourceId = source != null ? source.getSourceId() : null;
assertAliaseSupportInTargets(false);
if (!targets.isEmpty()) { if (!targets.isEmpty()) {
// skip targets // skip targets
if (targets.get(0).equals(TARGET_SKIP)) { if (targets.get(0).equals(TARGET_SKIP)) {
Assert.assertTrue("found skip target, but it require more targets, needs " 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()); target.getTargets().size() >= target.getMinNumberOfTargets());
targets.remove(0); targets.remove(0);
return true; return true;
@ -2016,6 +2048,7 @@ public class TestPlayer implements Player {
// wrong target settings by addTarget // wrong target settings by addTarget
// how to fix: implement target class processing above // how to fix: implement target class processing above
assertAliaseSupportInTargets(false);
if (!targets.isEmpty()) { if (!targets.isEmpty()) {
String message; String message;
@ -2040,6 +2073,7 @@ public class TestPlayer implements Player {
@Override @Override
public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) { public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
assertAliaseSupportInTargets(false);
if (!targets.isEmpty()) { if (!targets.isEmpty()) {
for (String targetDefinition : targets) { for (String targetDefinition : targets) {
String[] targetList = targetDefinition.split("\\^"); String[] targetList = targetDefinition.split("\\^");
@ -2069,6 +2103,7 @@ public class TestPlayer implements Player {
@Override @Override
public TriggeredAbility chooseTriggeredAbility(List<TriggeredAbility> abilities, Game game) { public TriggeredAbility chooseTriggeredAbility(List<TriggeredAbility> abilities, Game game) {
assertAliaseSupportInChoices(false);
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
for (TriggeredAbility ability : abilities) { for (TriggeredAbility ability : abilities) {
if (ability.toString().startsWith(choices.get(0))) { if (ability.toString().startsWith(choices.get(0))) {
@ -2082,7 +2117,7 @@ public class TestPlayer implements Player {
this.chooseStrictModeFailed("choice", game, this.chooseStrictModeFailed("choice", game,
"Triggered list (total " + abilities.size() + "):\n" "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); return computerPlayer.chooseTriggeredAbility(abilities, game);
} }
@ -2096,6 +2131,7 @@ public class TestPlayer implements Player {
if (message.equals("Scry 1?")) { if (message.equals("Scry 1?")) {
return false; return false;
} }
assertAliaseSupportInChoices(false);
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
if (choices.get(0).equals("No")) { if (choices.get(0).equals("No")) {
choices.remove(0); choices.remove(0);
@ -2117,6 +2153,7 @@ public class TestPlayer implements Player {
@Override @Override
public int announceXMana(int min, int max, int multiplier, String message, Game game, Ability ability) { public int announceXMana(int min, int max, int multiplier, String message, Game game, Ability ability) {
assertAliaseSupportInChoices(false);
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
for (String choice : choices) { for (String choice : choices) {
if (choice.startsWith("X=")) { if (choice.startsWith("X=")) {
@ -2134,6 +2171,7 @@ public class TestPlayer implements Player {
@Override @Override
public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variablCost) { public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variablCost) {
assertAliaseSupportInChoices(false);
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
if (choices.get(0).startsWith("X=")) { if (choices.get(0).startsWith("X=")) {
int xValue = Integer.parseInt(choices.get(0).substring(2)); int xValue = Integer.parseInt(choices.get(0).substring(2));
@ -2149,6 +2187,7 @@ public class TestPlayer implements Player {
@Override @Override
public int getAmount(int min, int max, String message, Game game) { public int getAmount(int min, int max, String message, Game game) {
assertAliaseSupportInChoices(false);
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
if (choices.get(0).startsWith("X=")) { if (choices.get(0).startsWith("X=")) {
int xValue = Integer.parseInt(choices.get(0).substring(2)); int xValue = Integer.parseInt(choices.get(0).substring(2));
@ -3258,7 +3297,7 @@ public class TestPlayer implements Player {
@Override @Override
public boolean choose(Outcome outcome, Target target, 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 // needed to call here the TestPlayer because it's overwitten
return choose(outcome, target, sourceId, game, null); return choose(outcome, target, sourceId, game, null);
@ -3266,8 +3305,9 @@ public class TestPlayer implements Player {
@Override @Override
public boolean choose(Outcome outcome, Cards cards, public boolean choose(Outcome outcome, Cards cards,
TargetCard target, Game game TargetCard target, Game game
) { ) {
assertAliaseSupportInChoices(false);
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
for (String choose2 : choices) { for (String choose2 : choices) {
// TODO: More targetting to fix // TODO: More targetting to fix
@ -3302,19 +3342,20 @@ public class TestPlayer implements Player {
@Override @Override
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, 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) // 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) // 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()); Assert.assertNotEquals("chooseTargetAmount needs non zero amount remaining", 0, target.getAmountRemaining());
assertAliaseSupportInTargets(false);
if (!targets.isEmpty()) { if (!targets.isEmpty()) {
// skip targets // skip targets
if (targets.get(0).equals(TARGET_SKIP)) { if (targets.get(0).equals(TARGET_SKIP)) {
Assert.assertTrue("found skip target, but it require more targets, needs " 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()); target.getTargets().size() >= target.getMinNumberOfTargets());
targets.remove(0); targets.remove(0);
return false; // false in chooseTargetAmount = stop to choose return false; // false in chooseTargetAmount = stop to choose
@ -3367,15 +3408,15 @@ public class TestPlayer implements Player {
@Override @Override
public boolean choosePile(Outcome outcome, String message, public boolean choosePile(Outcome outcome, String message,
List<? extends Card> pile1, List<? extends Card> pile2, List<? extends Card> pile1, List<? extends Card> pile2,
Game game Game game
) { ) {
return computerPlayer.choosePile(outcome, message, pile1, pile2, game); return computerPlayer.choosePile(outcome, message, pile1, pile2, game);
} }
@Override @Override
public boolean playMana(Ability ability, ManaCost unpaid, public boolean playMana(Ability ability, ManaCost unpaid,
String promptText, Game game String promptText, Game game
) { ) {
groupsForTargetHandling = null; groupsForTargetHandling = null;
return computerPlayer.playMana(ability, unpaid, promptText, game); return computerPlayer.playMana(ability, unpaid, promptText, game);
@ -3389,15 +3430,15 @@ public class TestPlayer implements Player {
@Override @Override
public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup, public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup,
List<UUID> blockerOrder, Game game List<UUID> blockerOrder, Game game
) { ) {
return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
} }
@Override @Override
public void assignDamage(int damage, List<UUID> targets, public void assignDamage(int damage, List<UUID> targets,
String singleTargetName, UUID sourceId, String singleTargetName, UUID sourceId,
Game game Game game
) { ) {
computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game); computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game);
} }
@ -3416,14 +3457,14 @@ public class TestPlayer implements Player {
@Override @Override
public void pickCard(List<Card> cards, Deck deck, public void pickCard(List<Card> cards, Deck deck,
Draft draft Draft draft
) { ) {
computerPlayer.pickCard(cards, deck, draft); computerPlayer.pickCard(cards, deck, draft);
} }
@Override @Override
public boolean scry(int value, Ability source, public boolean scry(int value, Ability source,
Game game Game game
) { ) {
// Don't scry at the start of the game. // Don't scry at the start of the game.
if (game.getTurnNum() == 1 && game.getStep() == null) { if (game.getTurnNum() == 1 && game.getStep() == null) {
@ -3434,44 +3475,44 @@ public class TestPlayer implements Player {
@Override @Override
public boolean surveil(int value, Ability source, public boolean surveil(int value, Ability source,
Game game Game game
) { ) {
return computerPlayer.surveil(value, source, game); return computerPlayer.surveil(value, source, game);
} }
@Override @Override
public boolean moveCards(Card card, Zone toZone, public boolean moveCards(Card card, Zone toZone,
Ability source, Game game Ability source, Game game
) { ) {
return computerPlayer.moveCards(card, toZone, source, game); return computerPlayer.moveCards(card, toZone, source, game);
} }
@Override @Override
public boolean moveCards(Card card, Zone toZone, public boolean moveCards(Card card, Zone toZone,
Ability source, Game game, Ability source, Game game,
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
) { ) {
return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects); return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
} }
@Override @Override
public boolean moveCards(Cards cards, Zone toZone, public boolean moveCards(Cards cards, Zone toZone,
Ability source, Game game Ability source, Game game
) { ) {
return computerPlayer.moveCards(cards, toZone, source, game); return computerPlayer.moveCards(cards, toZone, source, game);
} }
@Override @Override
public boolean moveCards(Set<Card> cards, Zone toZone, public boolean moveCards(Set<Card> cards, Zone toZone,
Ability source, Game game Ability source, Game game
) { ) {
return computerPlayer.moveCards(cards, toZone, source, game); return computerPlayer.moveCards(cards, toZone, source, game);
} }
@Override @Override
public boolean moveCards(Set<Card> cards, Zone toZone, public boolean moveCards(Set<Card> cards, Zone toZone,
Ability source, Game game, Ability source, Game game,
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
) { ) {
return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects); return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
} }
@ -3560,6 +3601,7 @@ public class TestPlayer implements Player {
@Override @Override
public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) { public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) {
String allInfo = ""; String allInfo = "";
assertAliaseSupportInChoices(false);
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
Map<UUID, ActivatedAbility> useable = PlayerImpl.getSpellAbilities(this.getId(), card, game.getState().getZone(card.getId()), game); Map<UUID, ActivatedAbility> useable = PlayerImpl.getSpellAbilities(this.getId(), card, game.getState().getZone(card.getId()), game);
for (ActivatedAbility ability : useable.values()) { for (ActivatedAbility ability : useable.values()) {

View file

@ -34,6 +34,7 @@ import org.mage.test.serverside.base.MageTestPlayerBase;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -43,10 +44,33 @@ import java.util.stream.Collectors;
/** /**
* API for test initialization and asserting the test results. * API for test initialization and asserting the test results.
* *
* @author ayratn * @author ayratn, JayDi85
*/ */
public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implements CardTestAPI { 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 // TODO: add target player param to commands
public static final String CHECK_COMMAND_PT = "PT"; public static final String CHECK_COMMAND_PT = "PT";
public static final String CHECK_COMMAND_DAMAGE = "DAMAGE"; public static final String CHECK_COMMAND_DAMAGE = "DAMAGE";
@ -287,9 +311,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
// check commands // check commands
private void check(String checkName, int turnNum, PhaseStep step, TestPlayer player, String command, String... params) { 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) { for (String param : params) {
res += "@" + param; res += CHECK_PARAM_DELIMETER + param;
} }
player.addAction(checkName, turnNum, step, res); 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) { private void show(String showName, int turnNum, PhaseStep step, TestPlayer player, String command, String... params) {
String res = "show:" + command; String res = "show:" + command;
for (String param : params) { for (String param : params) {
res += "@" + param; res += CHECK_PARAM_DELIMETER + param;
} }
player.addAction(showName, turnNum, step, res); player.addAction(showName, turnNum, step, res);
} }
@ -494,9 +518,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
// aliases for mage objects // aliases for mage objects
String aliasName = ""; String aliasName = "";
boolean useAliasMultiNames = (count != 1); boolean useAliasMultiNames = (count != 1);
if (cardName.contains("@")) { if (cardName.contains(ALIASE_PREFIX)) {
aliasName = cardName.substring(cardName.indexOf("@") + 1); aliasName = cardName.substring(cardName.indexOf(ALIASE_PREFIX) + ALIASE_PREFIX.length());
cardName = cardName.substring(0, cardName.indexOf("@")); cardName = cardName.substring(0, cardName.indexOf(ALIASE_PREFIX));
} }
// one card = one aliase, massive adds can use auto-name // 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) { public void playLand(int turnNum, PhaseStep step, TestPlayer player, String cardName) {
//Assert.assertNotEquals("", 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) { public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName) {
//Assert.assertNotEquals("", 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) { public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, Player target) {
//Assert.assertNotEquals("", cardName); //Assert.assertNotEquals("", cardName);
// warning, target in spell cast command setups without choose target call // 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) { public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, Player target, int manaInPool) {
//Assert.assertNotEquals("", cardName); //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) { 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) { public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName) {
//Assert.assertNotEquals("", cardName); //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 { 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) { public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName, String spellOnStack, StackClause clause) {
//Assert.assertNotEquals("", cardName); //Assert.assertNotEquals("", cardName);
assertAliaseSupportInActivateCommand(cardName, false);
assertAliaseSupportInActivateCommand(targetName, false);
assertAliaseSupportInActivateCommand(spellOnStack, false);
if (StackClause.WHILE_ON_STACK == clause) { 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) + '$' + (targetName != null && targetName.startsWith("target") ? targetName : "target=" + targetName)
+ "$spellOnStack=" + spellOnStack); + "$spellOnStack=" + spellOnStack);
} else { } else {
player.addAction(turnNum, step, "activate:Cast " + cardName player.addAction(turnNum, step, ACTIVATE_CAST + cardName
+ '$' + (targetName != null && targetName.startsWith("target") ? targetName : "target=" + targetName) + '$' + (targetName != null && targetName.startsWith("target") ? targetName : "target=" + targetName)
+ "$!spellOnStack=" + spellOnStack); + "$!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) { public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName, String spellOnStack, String spellOnTopOfStack) {
//Assert.assertNotEquals("", cardName); //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()) { if (spellOnStack != null && !spellOnStack.isEmpty()) {
action += "$spellOnStack=" + spellOnStack; 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) { 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 // 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) { 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 // 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) { 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 // 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) { 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 * @param clause
*/ */
public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack, StackClause 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()) { if (targetName != null && !targetName.isEmpty()) {
sb.append("$target=").append(targetName); sb.append("$target=").append(targetName);
} }
@ -1676,4 +1720,13 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
if (playerC != null) waitStackResolved(turnNum, step, playerC); if (playerC != null) waitStackResolved(turnNum, step, playerC);
if (playerD != null) waitStackResolved(turnNum, step, playerD); 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);
}
}
}
} }