New testing frameworks for cards. BurntheImpure test.

This commit is contained in:
magenoxx 2011-03-10 15:27:15 +03:00
parent 196f7a32d8
commit 99735b368b
25 changed files with 1297 additions and 119 deletions

View file

@ -126,7 +126,7 @@ public class ComputerPlayer<T extends ComputerPlayer<T>> extends PlayerImpl<T> i
@Override
public boolean chooseMulligan(Game game) {
logger.debug("chooseMulligan");
if (hand.size() < 6)
if (hand.size() < 6 || isTestMode())
return false;
Set<Card> lands = hand.getCards(new FilterLandCard(), game);
if (lands.size() < 2 || lands.size() > hand.size() - 2)

View file

@ -5,6 +5,7 @@ import mage.cards.Card;
import mage.cards.decks.Deck;
import mage.game.Game;
import mage.game.GameException;
import mage.game.GameOptions;
import mage.game.TwoPlayerDuel;
import mage.game.permanent.PermanentCard;
import mage.players.Player;
@ -23,18 +24,6 @@ import java.util.regex.Matcher;
*/
public class PlayGameTest extends MageTestBase {
private List<Card> handCardsA = new ArrayList<Card>();
private List<Card> handCardsB = new ArrayList<Card>();
private List<PermanentCard> battlefieldCardsA = new ArrayList<PermanentCard>();
private List<PermanentCard> battlefieldCardsB = new ArrayList<PermanentCard>();
private List<Card> graveyardCardsA = new ArrayList<Card>();
private List<Card> graveyardCardsB = new ArrayList<Card>();
private List<Card> libraryCardsA = new ArrayList<Card>();
private List<Card> libraryCardsB = new ArrayList<Card>();
private Map<Constants.Zone, String> commandsA = new HashMap<Constants.Zone, String>();
private Map<Constants.Zone, String> commandsB = new HashMap<Constants.Zone, String>();
@Test
public void playOneGame() throws GameException, FileNotFoundException, IllegalArgumentException {
Game game = new TwoPlayerDuel(Constants.MultiplayerAttackOption.LEFT, Constants.RangeOfInfluence.ALL);
@ -68,7 +57,9 @@ public class PlayGameTest extends MageTestBase {
boolean testMode = true;
long t1 = System.nanoTime();
game.start(computerA.getId(), testMode);
GameOptions options = new GameOptions();
options.testMode = true;
game.start(computerA.getId(), options);
long t2 = System.nanoTime();
logger.info("Winner: " + game.getWinner());
@ -77,100 +68,4 @@ public class PlayGameTest extends MageTestBase {
throw new RuntimeException("Lost :(");
}*/
}
private void addCard(List<Card> cards, String name, int count) {
for (int i = 0; i < count; i++) {
Card card = Sets.findCard(name, true);
if (card == null) {
throw new IllegalArgumentException("Couldn't find a card for test: " + name);
}
cards.add(card);
}
}
private void parseScenario(String filename) throws FileNotFoundException {
File f = new File(filename);
Scanner scanner = new Scanner(f);
try {
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (line == null || line.isEmpty() || line.startsWith("#")) continue;
Matcher m = pattern.matcher(line);
if (m.matches()) {
String zone = m.group(1);
String nickname = m.group(2);
if (nickname.equals("ComputerA") || nickname.equals("ComputerB")) {
List<Card> cards = null;
List<PermanentCard> perms = null;
Constants.Zone gameZone;
if ("hand".equalsIgnoreCase(zone)) {
gameZone = Constants.Zone.HAND;
cards = nickname.equals("ComputerA") ? handCardsA : handCardsB;
} else if ("battlefield".equalsIgnoreCase(zone)) {
gameZone = Constants.Zone.BATTLEFIELD;
perms = nickname.equals("ComputerA") ? battlefieldCardsA : battlefieldCardsB;
} else if ("graveyard".equalsIgnoreCase(zone)) {
gameZone = Constants.Zone.GRAVEYARD;
cards = nickname.equals("ComputerA") ? graveyardCardsA : graveyardCardsB;
} else if ("library".equalsIgnoreCase(zone)) {
gameZone = Constants.Zone.LIBRARY;
cards = nickname.equals("ComputerA") ? libraryCardsA : libraryCardsB;
} else if ("player".equalsIgnoreCase(zone)) {
String command = m.group(3);
if ("life".equals(command)) {
if (nickname.equals("ComputerA")) {
commandsA.put(Constants.Zone.OUTSIDE, "life:" + m.group(4));
} else {
commandsB.put(Constants.Zone.OUTSIDE, "life:" + m.group(4));
}
}
continue;
} else {
continue; // go parse next line
}
String cardName = m.group(3);
Integer amount = Integer.parseInt(m.group(4));
boolean tapped = m.group(5) != null && m.group(5).equals(":{tapped}");
if (cardName.equals("clear")) {
if (nickname.equals("ComputerA")) {
commandsA.put(gameZone, "clear");
} else {
commandsB.put(gameZone, "clear");
}
} else {
for (int i = 0; i < amount; i++) {
Card card = Sets.findCard(cardName, true);
if (card != null) {
if (gameZone.equals(Constants.Zone.BATTLEFIELD)) {
PermanentCard p = new PermanentCard(card, null);
p.setTapped(tapped);
perms.add(p);
} else {
cards.add(card);
}
} else {
logger.fatal("Couldn't find a card: " + cardName);
logger.fatal("line: " + line);
}
}
}
} else {
logger.warn("Unknown player: " + nickname);
}
} else {
logger.warn("Init string wasn't parsed: " + line);
}
}
} finally {
scanner.close();
}
}
private Player createPlayer(String name, String playerType) {
return PlayerFactory.getInstance().createPlayer(playerType, name, Constants.RangeOfInfluence.ALL);
}
}

View file

@ -0,0 +1,143 @@
package org.mage.test.serverside.base;
import mage.Constants;
import mage.filter.Filter;
import mage.players.Player;
/**
* Interface for all test initialization and assertion operations.
*/
public interface CardTestAPI {
/**
* Types of game result.
*/
public enum GameResult {
WON,
LOST,
DRAW
}
//******* INITIALIZATION METHODS *******/
/**
* Default game initialization params for red player (that plays with Mountains)
*/
void useRedDefault();
/**
* Removes all cards from player's library from the game.
* Usually this should be used once before initialization to form the library in certain order.
*
* @param player {@link Player} to remove all library cards from.
*/
void removeAllCardsFromLibrary(Player player);
/**
* Add a card to specified zone of specified player.
*
* @param gameZone {@link Constants.Zone} to add cards to.
* @param player {@link Player} to add cards for. Use either computerA or computerB.
* @param cardName Card name in string format.
*/
void addCard(Constants.Zone gameZone, Player player, String cardName);
/**
* Add any amount of cards to specified zone of specified player.
*
* @param gameZone {@link Constants.Zone} to add cards to.
* @param player {@link Player} to add cards for. Use either computerA or computerB.
* @param cardName Card name in string format.
* @param count Amount of cards to be added.
*/
void addCard(Constants.Zone gameZone, Player player, String cardName, int count);
/**
* Add any amount of cards to specified zone of specified player.
*
* @param gameZone {@link Constants.Zone} to add cards to.
* @param player {@link Player} to add cards for. Use either computerA or computerB.
* @param cardName Card name in string format.
* @param count Amount of cards to be added.
* @param tapped In case gameZone is Battlefield, determines whether permanent should be tapped.
* In case gameZone is other than Battlefield, {@link IllegalArgumentException} is thrown
*/
void addCard(Constants.Zone gameZone, Player player, String cardName, int count, boolean tapped);
/**
* Set player's initial life count.
*
* @param player {@link Player} to set life count for.
* @param life Life count to set.
*/
void setLife(Player player, int life);
//******* GAME OPTIONS *******/
/**
* Define turn number to stop the game on.
*/
void setStopOnTurn(int turn);
//******* ASSERT METHODS *******/
/**
* Assert turn number after test execution.
*
* @param turn Expected turn number to compare with.
*/
void assertTurn(int turn) throws AssertionError;
/**
* Assert game result for the player after test execution.
*
* @param player {@link Player} to get game result for.
* @param result Expected {@link GameResult} to compare with.
*/
void assertResult(Player player, GameResult result) throws AssertionError;
/**
* Assert player's life count after test execution.
*
* @param player {@link Player} to get life for comparison.
* @param life Expected player's life to compare with.
*/
void assertLife(Player player, int life) throws AssertionError;
/**
* Assert creature's power and toughness by card name.
* <p/>
* Throws {@link AssertionError} in the following cases:
* 1. no such player
* 2. no such creature under player's control
* 3. depending on comparison scope:
* 3a. any: no creature under player's control with the specified p\t params
* 3b. all: there is at least one creature with the cardName with the different p\t params
*
* @param player {@link Player} to get creatures for comparison.
* @param cardName Card name to compare with.
* @param power Expected power to compare with.
* @param toughness Expected toughness to compare with.
* @param scope {@link Filter.ComparisonScope} Use ANY, if you want "at least one creature with given name should have specified p\t"
* Use ALL, if you want "all creature with gived name should have specified p\t"
*/
void assertPowerToughness(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope)
throws AssertionError;
/**
* Assert permanent count under player's control.
*
* @param player {@link Player} which permanents should be counted.
* @param count Expected count.
*/
void assertPermanentCount(Player player, int count) throws AssertionError;
/**
* Assert permanent count under player's control.
*
* @param player {@link Player} which permanents should be counted.
* @param cardName Name of the cards that should be counted.
* @param count Expected count.
*/
void assertPermanentCount(Player player, String cardName, int count) throws AssertionError;
}

View file

@ -0,0 +1,335 @@
package org.mage.test.serverside.base;
import mage.Constants;
import mage.cards.Card;
import mage.cards.decks.Deck;
import mage.filter.Filter;
import mage.game.Game;
import mage.game.GameException;
import mage.game.GameOptions;
import mage.game.TwoPlayerDuel;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.sets.Sets;
import org.junit.Assert;
import org.junit.Before;
import org.mage.test.serverside.base.impl.CardTestAPIImpl;
import java.io.File;
import java.io.FileNotFoundException;
/**
* Base class for testing single cards and effects.
*
* @author ayratn
*/
public abstract class CardTestBase extends CardTestAPIImpl {
protected enum AIType {
MinimaxHybrid,
MAD
}
protected enum ExpectedType {
TURN_NUMBER,
RESULT,
LIFE,
BATTLEFIELD,
GRAVEYARD,
UNKNOWN
}
/**
* Computer types used to test cards.
* By default: MAD.
*/
private AIType aiTypeA, aiTypeB;
public CardTestBase() {
aiTypeA = CardTestBase.AIType.MAD;
aiTypeB = CardTestBase.AIType.MAD;
}
public CardTestBase(AIType aiTypeA, AIType aiTypeB) {
this.aiTypeA = aiTypeA;
this.aiTypeB = aiTypeB;
}
@Before
public void reset() throws GameException, FileNotFoundException {
if (currentGame != null) {
logger.info("Resetting previous game and creating new one!");
currentGame = null;
System.gc();
}
Game game = new TwoPlayerDuel(Constants.MultiplayerAttackOption.LEFT, Constants.RangeOfInfluence.ALL);
computerA = aiTypeA.equals(CardTestBase.AIType.MinimaxHybrid) ?
createPlayer("ComputerA", "Computer - minimax hybrid") :
createPlayer("ComputerA", "Computer - mad");
computerA.setTestMode(true);
Deck deck = Deck.load(Sets.loadDeck("RB Aggro.dck"));
if (deck.getCards().size() < 40) {
throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size());
}
game.addPlayer(computerA, deck);
game.loadCards(deck.getCards(), computerA.getId());
computerB = aiTypeB.equals(CardTestBase.AIType.MinimaxHybrid) ?
createPlayer("ComputerB", "Computer - minimax hybrid") :
createPlayer("ComputerB", "Computer - mad");
computerB.setTestMode(true);
Deck deck2 = Deck.load(Sets.loadDeck("RB Aggro.dck"));
if (deck2.getCards().size() < 40) {
throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck2.getCards().size());
}
game.addPlayer(computerB, deck2);
game.loadCards(deck2.getCards(), computerB.getId());
activePlayer = computerA;
currentGame = game;
stopOnTurn = null;
handCardsA.clear();
handCardsB.clear();
battlefieldCardsA.clear();
battlefieldCardsB.clear();
graveyardCardsA.clear();
graveyardCardsB.clear();
libraryCardsA.clear();
libraryCardsB.clear();
commandsA.clear();
commandsB.clear();
}
public void load(String path) throws FileNotFoundException, GameException {
load(path, AIType.MAD, AIType.MAD);
}
public void load(String path, AIType aiTypeA, AIType aiTypeB) throws FileNotFoundException, GameException {
String cardPath = TESTS_PATH + path;
File checkFile = new File(cardPath);
if (!checkFile.exists()) {
throw new FileNotFoundException("Couldn't find test file: " + cardPath);
}
if (checkFile.isDirectory()) {
throw new FileNotFoundException("Couldn't find test file: " + cardPath + ". It is directory.");
}
if (currentGame != null) {
logger.info("Resetting previous game and creating new one!");
currentGame = null;
System.gc();
}
Game game = new TwoPlayerDuel(Constants.MultiplayerAttackOption.LEFT, Constants.RangeOfInfluence.ALL);
computerA = aiTypeA.equals(CardTestBase.AIType.MinimaxHybrid) ?
createPlayer("ComputerA", "Computer - minimax hybrid") :
createPlayer("ComputerA", "Computer - mad");
computerA.setTestMode(true);
Deck deck = Deck.load(Sets.loadDeck("RB Aggro.dck"));
if (deck.getCards().size() < 40) {
throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size());
}
game.addPlayer(computerA, deck);
game.loadCards(deck.getCards(), computerA.getId());
computerB = aiTypeB.equals(CardTestBase.AIType.MinimaxHybrid) ?
createPlayer("ComputerB", "Computer - minimax hybrid") :
createPlayer("ComputerB", "Computer - mad");
computerB.setTestMode(true);
Deck deck2 = Deck.load(Sets.loadDeck("RB Aggro.dck"));
if (deck2.getCards().size() < 40) {
throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck2.getCards().size());
}
game.addPlayer(computerB, deck2);
game.loadCards(deck2.getCards(), computerB.getId());
parseScenario(cardPath);
activePlayer = computerA;
currentGame = game;
}
/**
* Starts testing card by starting current game.
*
* @throws IllegalStateException In case game wasn't created previously. Use {@link #load} method to initialize the game.
*/
public void execute() throws IllegalStateException {
if (currentGame == null || activePlayer == null) {
throw new IllegalStateException("Game is not initialized. Use load method to load a test case and initialize a game.");
}
currentGame.cheat(computerA.getId(), commandsA);
currentGame.cheat(computerA.getId(), libraryCardsA, handCardsA, battlefieldCardsA, graveyardCardsA);
currentGame.cheat(computerB.getId(), commandsB);
currentGame.cheat(computerB.getId(), libraryCardsB, handCardsB, battlefieldCardsB, graveyardCardsB);
boolean testMode = true;
long t1 = System.nanoTime();
GameOptions gameOptions = new GameOptions();
gameOptions.testMode = true;
gameOptions.stopOnTurn = stopOnTurn;
currentGame.start(activePlayer.getId(), gameOptions);
long t2 = System.nanoTime();
logger.info("Winner: " + currentGame.getWinner());
logger.info("Time: " + (t2 - t1) / 1000000 + " ms");
assertTheResults();
}
/**
* Assert expected and actual results.
*/
private void assertTheResults() {
logger.info("Matching expected results:");
for (String line : expectedResults) {
boolean ok = false;
try {
ExpectedType type = getExpectedType(line);
if (type.equals(CardTestBase.ExpectedType.UNKNOWN)) {
throw new AssertionError("Unknown expected type, check the line in $expected section=" + line);
}
parseType(type, line);
ok = true;
} finally {
logger.info(" " + line + " - " + (ok ? "OK" : "ERROR"));
}
}
}
private ExpectedType getExpectedType(String line) {
if (line.startsWith("turn:")) {
return CardTestBase.ExpectedType.TURN_NUMBER;
}
if (line.startsWith("result:")) {
return CardTestBase.ExpectedType.RESULT;
}
if (line.startsWith("life:")) {
return CardTestBase.ExpectedType.LIFE;
}
if (line.startsWith("battlefield:")) {
return CardTestBase.ExpectedType.BATTLEFIELD;
}
if (line.startsWith("graveyard:")) {
return CardTestBase.ExpectedType.GRAVEYARD;
}
return CardTestBase.ExpectedType.UNKNOWN;
}
private void parseType(ExpectedType type, String line) {
if (type.equals(CardTestBase.ExpectedType.TURN_NUMBER)) {
int turn = getIntParam(line, 1);
Assert.assertEquals("Turn numbers are not equal", turn, currentGame.getTurnNum());
return;
}
if (type.equals(CardTestBase.ExpectedType.RESULT)) {
String expected = getStringParam(line, 1);
String actual = "draw";
if (currentGame.getWinner().equals("Player ComputerA is the winner")) {
actual = "won";
} else if (currentGame.getWinner().equals("Player ComputerB is the winner")) {
actual = "lost";
}
Assert.assertEquals("Game results are not equal", expected, actual);
return;
}
if (type.equals(CardTestBase.ExpectedType.LIFE)) {
String player = getStringParam(line, 1);
int expected = getIntParam(line, 2);
if (player.equals("ComputerA")) {
int actual = currentGame.getPlayer(computerA.getId()).getLife();
Assert.assertEquals("Life amounts are not equal", expected, actual);
} else if (player.equals("ComputerB")) {
int actual = currentGame.getPlayer(computerB.getId()).getLife();
Assert.assertEquals("Life amounts are not equal", expected, actual);
} else {
throw new IllegalArgumentException("Wrong player in 'life' line, player=" + player + ", line=" + line);
}
return;
}
if (type.equals(CardTestBase.ExpectedType.BATTLEFIELD)) {
String playerName = getStringParam(line, 1);
String cardName = getStringParam(line, 2);
int expectedCount = getIntParam(line, 3);
Player player = null;
if (playerName.equals("ComputerA")) {
player = currentGame.getPlayer(computerA.getId());
} else if (playerName.equals("ComputerB")) {
player = currentGame.getPlayer(computerB.getId());
} else {
throw new IllegalArgumentException("Wrong player in 'battlefield' line, player=" + player + ", line=" + line);
}
int actualCount = 0;
for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) {
if (permanent.getControllerId().equals(player.getId())) {
if (permanent.getName().equals(cardName)) {
actualCount++;
}
}
}
Assert.assertEquals("(Battlefield) Card counts are not equal (" + cardName + ")", expectedCount, actualCount);
return;
}
if (type.equals(CardTestBase.ExpectedType.GRAVEYARD)) {
String playerName = getStringParam(line, 1);
String cardName = getStringParam(line, 2);
int expectedCount = getIntParam(line, 3);
Player player = null;
if (playerName.equals("ComputerA")) {
player = currentGame.getPlayer(computerA.getId());
} else if (playerName.equals("ComputerB")) {
player = currentGame.getPlayer(computerB.getId());
} else {
throw new IllegalArgumentException("Wrong player in 'graveyard' line, player=" + player + ", line=" + line);
}
int actualCount = 0;
for (Card card : player.getGraveyard().getCards(currentGame)) {
if (card.getName().equals(cardName)) {
actualCount++;
}
}
Assert.assertEquals("(Graveyard) Card counts are not equal (" + cardName + ")", expectedCount, actualCount);
return;
}
}
private int getIntParam(String line, int index) {
String[] params = line.split(":");
if (index > params.length - 1) {
throw new IllegalArgumentException("Not correct line: " + line);
}
return Integer.parseInt(params[index]);
}
private String getStringParam(String line, int index) {
String[] params = line.split(":");
if (index > params.length - 1) {
throw new IllegalArgumentException("Not correct line: " + line);
}
return params[index];
}
protected void checkPermanentPT(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) {
if (currentGame == null) {
throw new IllegalStateException("Current game is null");
}
if (scope.equals(Filter.ComparisonScope.All)) {
throw new UnsupportedOperationException("ComparisonScope.All is not implemented.");
}
int count = 0;
int fit = 0;
for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(player.getId())) {
if (permanent.getName().equals(cardName)) {
Assert.assertEquals("Power is not the same", power, permanent.getPower().getValue());
Assert.assertEquals("Toughness is not the same", toughness, permanent.getToughness().getValue());
break;
}
}
}
}

View file

@ -1,7 +1,12 @@
package org.mage.test.serverside.base;
import mage.Constants;
import mage.cards.Card;
import mage.game.Game;
import mage.game.match.MatchType;
import mage.game.permanent.PermanentCard;
import mage.game.tournament.TournamentType;
import mage.players.Player;
import mage.server.game.DeckValidatorFactory;
import mage.server.game.GameFactory;
import mage.server.game.PlayerFactory;
@ -10,19 +15,25 @@ import mage.server.util.ConfigSettings;
import mage.server.util.PluginClassLoader;
import mage.server.util.config.GamePlugin;
import mage.server.util.config.Plugin;
import mage.sets.Sets;
import mage.util.Copier;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.junit.BeforeClass;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Base class for all tests.
*
* @author ayratn
*/
public class MageTestBase {
public abstract class MageTestBase {
protected static Logger logger = Logger.getLogger(MageTestBase.class);
public static PluginClassLoader classLoader = new PluginClassLoader();
@ -31,6 +42,58 @@ public class MageTestBase {
protected Pattern pattern = Pattern.compile("([a-zA-Z]*):([\\w]*):([a-zA-Z ,\\-.!'\\d]*):([\\d]*)(:\\{tapped\\})?");
protected List<Card> handCardsA = new ArrayList<Card>();
protected List<Card> handCardsB = new ArrayList<Card>();
protected List<PermanentCard> battlefieldCardsA = new ArrayList<PermanentCard>();
protected List<PermanentCard> battlefieldCardsB = new ArrayList<PermanentCard>();
protected List<Card> graveyardCardsA = new ArrayList<Card>();
protected List<Card> graveyardCardsB = new ArrayList<Card>();
protected List<Card> libraryCardsA = new ArrayList<Card>();
protected List<Card> libraryCardsB = new ArrayList<Card>();
protected Map<Constants.Zone, String> commandsA = new HashMap<Constants.Zone, String>();
protected Map<Constants.Zone, String> commandsB = new HashMap<Constants.Zone, String>();
protected Player computerA;
protected Player computerB;
/**
* Game instance initialized in load method.
*/
protected static Game currentGame = null;
/**
* Player thats starts the game first.
* By default, it is ComputerA.
*/
protected static Player activePlayer = null;
protected Integer stopOnTurn;
protected enum ParserState {
INIT,
OPTIONS,
EXPECTED
}
protected ParserState parserState;
/**
* Expected results of the test.
* Read from test case in {@link String} based format:
* <p/>
* Example:
* turn:1
* result:won:ComputerA
* life:ComputerA:20
* life:ComputerB:0
* battlefield:ComputerB:Tine Shrike:0
* graveyard:ComputerB:Tine Shrike:1
*/
protected List<String> expectedResults = new ArrayList<String>();
protected static final String TESTS_PATH = "tests" + File.separator;
@BeforeClass
public static void init() {
Logger.getRootLogger().setLevel(Level.DEBUG);
@ -108,4 +171,128 @@ public class MageTestBase {
file.delete();
}
}
protected void parseScenario(String filename) throws FileNotFoundException {
parserState = ParserState.INIT;
File f = new File(filename);
Scanner scanner = new Scanner(f);
try {
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (line == null || line.isEmpty() || line.startsWith("#")) continue;
if (line.startsWith("$include")) {
includeFrom(line);
continue;
}
if (line.startsWith("$expected")) {
parserState = ParserState.EXPECTED;
continue;
}
parseLine(line);
}
} finally {
scanner.close();
}
}
private void parseLine(String line) {
if (parserState.equals(ParserState.EXPECTED)) {
expectedResults.add(line); // just remember for future use
return;
}
Matcher m = pattern.matcher(line);
if (m.matches()) {
String zone = m.group(1);
String nickname = m.group(2);
if (nickname.equals("ComputerA") || nickname.equals("ComputerB")) {
List<Card> cards = null;
List<PermanentCard> perms = null;
Constants.Zone gameZone;
if ("hand".equalsIgnoreCase(zone)) {
gameZone = Constants.Zone.HAND;
cards = nickname.equals("ComputerA") ? handCardsA : handCardsB;
} else if ("battlefield".equalsIgnoreCase(zone)) {
gameZone = Constants.Zone.BATTLEFIELD;
perms = nickname.equals("ComputerA") ? battlefieldCardsA : battlefieldCardsB;
} else if ("graveyard".equalsIgnoreCase(zone)) {
gameZone = Constants.Zone.GRAVEYARD;
cards = nickname.equals("ComputerA") ? graveyardCardsA : graveyardCardsB;
} else if ("library".equalsIgnoreCase(zone)) {
gameZone = Constants.Zone.LIBRARY;
cards = nickname.equals("ComputerA") ? libraryCardsA : libraryCardsB;
} else if ("player".equalsIgnoreCase(zone)) {
String command = m.group(3);
if ("life".equals(command)) {
if (nickname.equals("ComputerA")) {
commandsA.put(Constants.Zone.OUTSIDE, "life:" + m.group(4));
} else {
commandsB.put(Constants.Zone.OUTSIDE, "life:" + m.group(4));
}
}
return;
} else {
return; // go parse next line
}
String cardName = m.group(3);
Integer amount = Integer.parseInt(m.group(4));
boolean tapped = m.group(5) != null && m.group(5).equals(":{tapped}");
if (cardName.equals("clear")) {
if (nickname.equals("ComputerA")) {
commandsA.put(gameZone, "clear");
} else {
commandsB.put(gameZone, "clear");
}
} else {
for (int i = 0; i < amount; i++) {
Card card = Sets.findCard(cardName, true);
if (card != null) {
if (gameZone.equals(Constants.Zone.BATTLEFIELD)) {
PermanentCard p = new PermanentCard(card, null);
p.setTapped(tapped);
perms.add(p);
} else {
cards.add(card);
}
} else {
logger.fatal("Couldn't find a card: " + cardName);
logger.fatal("line: " + line);
}
}
}
} else {
logger.warn("Unknown player: " + nickname);
}
} else {
logger.warn("Init string wasn't parsed: " + line);
}
}
private void includeFrom(String line) throws FileNotFoundException {
String[] params = line.split(" ");
if (params.length == 2) {
String paramName = params[1];
if (!paramName.contains("..")) {
String includePath = TESTS_PATH + paramName;
File f = new File(includePath);
if (f.exists()) {
parseScenario(includePath);
} else {
logger.warn("Ignored (file doesn't exist): " + line);
}
} else {
logger.warn("Ignored (wrong charactres): " + line);
}
} else {
logger.warn("Ignored (wrong size): " + line);
}
}
protected Player createPlayer(String name, String playerType) {
return PlayerFactory.getInstance().createPlayer(playerType, name, Constants.RangeOfInfluence.ALL);
}
}

View file

@ -0,0 +1,296 @@
package org.mage.test.serverside.base.impl;
import mage.Constants;
import mage.cards.Card;
import mage.filter.Filter;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard;
import mage.players.Player;
import mage.sets.Sets;
import org.junit.Assert;
import org.mage.test.serverside.base.CardTestAPI;
import org.mage.test.serverside.base.MageTestBase;
import java.util.List;
/**
* API for test initialization and asserting the test results.
*
* @author ayratn
*/
public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAPI {
/**
* Default game initialization params for red player (that plays with Mountains)
*/
public void useRedDefault() {
// *** ComputerA ***
// battlefield:ComputerA:Mountain:5
addCard(Constants.Zone.BATTLEFIELD, computerA, "Mountain", 5);
// hand:ComputerA:Mountain:4
addCard(Constants.Zone.HAND, computerA, "Mountain", 5);
// library:ComputerA:clear:0
removeAllCardsFromLibrary(computerA);
// library:ComputerA:Mountain:10
addCard(Constants.Zone.LIBRARY, computerA, "Mountain", 10);
// *** ComputerB ***
// battlefield:ComputerB:Plains:2
addCard(Constants.Zone.BATTLEFIELD, computerB, "Plains", 2);
// hand:ComputerB:Plains:2
addCard(Constants.Zone.HAND, computerB, "Plains", 2);
// library:ComputerB:clear:0
removeAllCardsFromLibrary(computerB);
// library:ComputerB:Plains:10
addCard(Constants.Zone.LIBRARY, computerB, "Plains", 10);
}
/**
* Removes all cards from player's library from the game.
* Usually this should be used once before initialization to form the library in certain order.
*
* @param player {@link Player} to remove all library cards from.
*/
public void removeAllCardsFromLibrary(Player player) {
if (player.equals(computerA)) {
commandsA.put(Constants.Zone.LIBRARY, "clear");
} else if (player.equals(computerB)) {
commandsB.put(Constants.Zone.LIBRARY, "clear");
}
}
/**
* Add a card to specified zone of specified player.
*
* @param gameZone {@link Constants.Zone} to add cards to.
* @param player {@link Player} to add cards for. Use either computerA or computerB.
* @param cardName Card name in string format.
*/
public void addCard(Constants.Zone gameZone, Player player, String cardName) {
addCard(gameZone, player, cardName, 1, false);
}
/**
* Add any amount of cards to specified zone of specified player.
*
* @param gameZone {@link Constants.Zone} to add cards to.
* @param player {@link Player} to add cards for. Use either computerA or computerB.
* @param cardName Card name in string format.
* @param count Amount of cards to be added.
*/
public void addCard(Constants.Zone gameZone, Player player, String cardName, int count) {
addCard(gameZone, player, cardName, count, false);
}
/**
* Add any amount of cards to specified zone of specified player.
*
* @param gameZone {@link Constants.Zone} to add cards to.
* @param player {@link Player} to add cards for. Use either computerA or computerB.
* @param cardName Card name in string format.
* @param count Amount of cards to be added.
* @param tapped In case gameZone is Battlefield, determines whether permanent should be tapped.
* In case gameZone is other than Battlefield, {@link IllegalArgumentException} is thrown
*/
public void addCard(Constants.Zone gameZone, Player player, String cardName, int count, boolean tapped) {
if (gameZone.equals(Constants.Zone.BATTLEFIELD)) {
for (int i = 0; i < count; i++) {
Card card = Sets.findCard(cardName, true);
PermanentCard p = new PermanentCard(card, null);
p.setTapped(tapped);
if (player.equals(computerA)) {
battlefieldCardsA.add(p);
} else if (player.equals(computerB)) {
battlefieldCardsB.add(p);
}
}
} else {
if (tapped) {
throw new IllegalArgumentException("Parameter tapped=true can be used only for Zone.BATTLEFIELD.");
}
List<Card> cards = getCardList(gameZone, player);
for (int i = 0; i < count; i++) {
Card card = Sets.findCard(cardName, true);
cards.add(card);
}
}
}
/**
* Returns card list containter for specified game zone and player.
*
* @param gameZone
* @param player
* @return
*/
private List<Card> getCardList(Constants.Zone gameZone, Player player) {
if (player.equals(computerA)) {
if (gameZone.equals(Constants.Zone.HAND)) {
return handCardsA;
} else if (gameZone.equals(Constants.Zone.GRAVEYARD)) {
return graveyardCardsA;
} else if (gameZone.equals(Constants.Zone.LIBRARY)) {
return libraryCardsA;
}
} else if (player.equals(computerB)) {
if (gameZone.equals(Constants.Zone.HAND)) {
return handCardsB;
} else if (gameZone.equals(Constants.Zone.GRAVEYARD)) {
return graveyardCardsB;
} else if (gameZone.equals(Constants.Zone.LIBRARY)) {
return libraryCardsB;
}
}
return null;
}
/**
* Set player's initial life count.
*
* @param player {@link Player} to set life count for.
* @param life Life count to set.
*/
public void setLife(Player player, int life) {
if (player.equals(computerA)) {
commandsA.put(Constants.Zone.OUTSIDE, "life:" + String.valueOf(life));
} else if (player.equals(computerB)) {
commandsB.put(Constants.Zone.OUTSIDE, "life:" + String.valueOf(life));
}
}
/**
* Define turn number to stop the game on.
*/
public void setStopOnTurn(int turn) {
stopOnTurn = turn == -1 ? null : Integer.valueOf(turn);
}
/**
* Assert turn number after test execution.
*
* @param turn Expected turn number to compare with. 1-based.
*/
public void assertTurn(int turn) throws AssertionError {
Assert.assertEquals("Turn numbers are not equal", turn, currentGame.getTurnNum());
}
/**
* Assert game result after test execution.
*
* @param result Expected {@link GameResult} to compare with.
*/
public void assertResult(Player player, GameResult result) throws AssertionError {
if (player.equals(computerA)) {
GameResult actual = CardTestAPI.GameResult.DRAW;
if (currentGame.getWinner().equals("Player ComputerA is the winner")) {
actual = CardTestAPI.GameResult.WON;
} else if (currentGame.getWinner().equals("Player ComputerB is the winner")) {
actual = CardTestAPI.GameResult.LOST;
}
Assert.assertEquals("Game results are not equal", result, actual);
} else if (player.equals(computerB)) {
GameResult actual = CardTestAPI.GameResult.DRAW;
if (currentGame.getWinner().equals("Player ComputerB is the winner")) {
actual = CardTestAPI.GameResult.WON;
} else if (currentGame.getWinner().equals("Player ComputerA is the winner")) {
actual = CardTestAPI.GameResult.LOST;
}
Assert.assertEquals("Game results are not equal", result, actual);
}
}
/**
* Assert player's life count after test execution.
*
* @param player {@link Player} to get life for comparison.
* @param life Expected player's life to compare with.
*/
public void assertLife(Player player, int life) throws AssertionError {
int actual = currentGame.getPlayer(player.getId()).getLife();
Assert.assertEquals("Life amounts are not equal", life, actual);
}
/**
* Assert creature's power and toughness by card name.
* <p/>
* Throws {@link AssertionError} in the following cases:
* 1. no such player
* 2. no such creature under player's control
* 3. depending on comparison scope:
* 3a. any: no creature under player's control with the specified p\t params
* 3b. all: there is at least one creature with the cardName with the different p\t params
*
* @param player {@link Player} to get creatures for comparison.
* @param cardName Card name to compare with.
* @param power Expected power to compare with.
* @param toughness Expected toughness to compare with.
* @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you want "at least one creature with given name should have specified p\t"
* Use ALL, if you want "all creature with gived name should have specified p\t"
*/
public void assertPowerToughness(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope)
throws AssertionError {
int count = 0;
int fit = 0;
for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(player.getId())) {
if (permanent.getName().equals(cardName)) {
count++;
if (scope.equals(Filter.ComparisonScope.All)) {
Assert.assertEquals("Power is not the same (" + power + " vs. " + permanent.getPower().getValue() + ")",
power, permanent.getPower().getValue());
Assert.assertEquals("Toughness is not the same (" + toughness + " vs. " + permanent.getToughness().getValue() + ")",
toughness, permanent.getToughness().getValue());
} else if (scope.equals(Filter.ComparisonScope.Any)) {
if (power == permanent.getPower().getValue() && toughness == permanent.getToughness().getValue()) {
fit++;
break;
}
}
}
}
Assert.assertTrue("There is no such permanent under player's control, player=" + player.getName() +
", cardName=" + cardName, count > 0);
if (scope.equals(Filter.ComparisonScope.Any)) {
Assert.assertTrue("There is no such creature under player's control with specified power&toughness, player=" + player.getName() +
", cardName=" + cardName, fit > 0);
}
}
/**
* Assert permanent count under player's control.
*
* @param player {@link Player} which permanents should be counted.
* @param count Expected count.
*/
public void assertPermanentCount(Player player, int count) throws AssertionError {
int actualCount = 0;
for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) {
if (permanent.getControllerId().equals(player.getId())) {
actualCount++;
}
}
Assert.assertEquals("(Battlefield) Card counts are not equal ", count, actualCount);
}
/**
* Assert permanent count under player's control.
*
* @param player {@link Player} which permanents should be counted.
* @param cardName Name of the cards that should be counted.
* @param count Expected count.
*/
public void assertPermanentCount(Player player, String cardName, int count) throws AssertionError {
int actualCount = 0;
for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) {
if (permanent.getControllerId().equals(player.getId())) {
if (permanent.getName().equals(cardName)) {
actualCount++;
}
}
}
Assert.assertEquals("(Battlefield) Card counts are not equal (" + cardName + ")", count, actualCount);
}
}

View file

@ -0,0 +1,23 @@
package org.mage.test.serverside.cards.effects;
import com.sun.xml.bind.v2.schemagen.xmlschema.Any;
import mage.filter.Filter;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestBase;
/**
* Tests continuous boost effect like "White creatures you control get +1/+1".
*
* @author ayratn
*/
public class BoostContinuousEffectTest extends CardTestBase {
@Test
public void testHonorOfThePoor() throws Exception {
load("M11/Honor of the Pure.test");
execute();
checkPermanentPT(computerA, "Tine Shrike", 3, 2, Filter.ComparisonScope.Any);
checkPermanentPT(computerA, "Runeclaw Bear", 2, 2, Filter.ComparisonScope.Any);
}
}

View file

@ -0,0 +1,74 @@
package org.mage.test.serverside.cards.single.mbs;
import mage.Constants;
import mage.filter.Filter;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestAPI;
import org.mage.test.serverside.base.CardTestBase;
/**
* First JUnit tests for Mage card.
*
* @ayratn
*/
public class BurntheImpureTest extends CardTestBase {
/**
* Reproduces the test written in MBS/Burn the Impure.test
*
* Actually it can be tested with one java line that loads all test metadata from text file:
* load("MBS/Burn the Impure.test");
*
* But it was decided to use java code only.
*
* @throws Exception
*/
@Test
public void testVersusInfectCreature() throws Exception {
// $include red.default
useRedDefault();
// hand:ComputerA:Burn the Impure:1
addCard(Constants.Zone.HAND, computerA, "Burn the Impure");
// battlefield:ComputerB:Tine Shrike:1
addCard(Constants.Zone.BATTLEFIELD, computerB, "Tine Shrike");
// player:ComputerB:life:3
setLife(computerB, 3);
setStopOnTurn(2);
execute();
// turn:1
assertTurn(1);
// result:won
assertResult(computerA, CardTestAPI.GameResult.WON);
// life:ComputerA:20
assertLife(computerA, 20);
// life:ComputerB:0
assertLife(computerB, 0);
// assert Tine Shrike has been killed
assertPermanentCount(computerB, "Tine Shrike", 0);
}
/**
* load("MBS/Burn the Impure - no infect.test");
* @throws Exception
*/
@Test
public void testVersusNonInfectCreature() throws Exception {
useRedDefault();
addCard(Constants.Zone.HAND, computerA, "Burn the Impure");
addCard(Constants.Zone.BATTLEFIELD, computerB, "Runeclaw Bear", 3);
setLife(computerB, 3);
setStopOnTurn(2);
execute();
assertTurn(2);
assertResult(computerA, CardTestAPI.GameResult.DRAW);
assertLife(computerA, 20);
assertLife(computerB, 3);
assertPermanentCount(computerB, "Runeclaw Bear", 2);
assertPowerToughness(computerB, "Runeclaw Bear", 2, 2, Filter.ComparisonScope.Any);
assertPowerToughness(computerB, "Runeclaw Bear", 2, 2, Filter.ComparisonScope.All);
}
}

View file

@ -0,0 +1,29 @@
### Test playing Honor of the Pure ###
#######################################
$include green.white.default
### ComputerA ###
# Hand
hand:ComputerA:Honor of the Pure:1
# Battlefield
battlefield:ComputerB:Tine Shrike:1
battlefield:ComputerB:Runeclaw Bear:1
### ComputerB ###
# nothing
$options
exit_on_turn:2
$expected
turn:1
result:draw
life:ComputerA:20
life:ComputerB:20
battlefield:ComputerA:Tine Shrike:1
battlefield:ComputerA:Runeclaw Bear:1
# note: abilities are tested in code

View file

@ -0,0 +1,30 @@
### Test playing Burn the Impure ###
### Target: creature with no Infect ###
#######################################
$include red.default
### ComputerA ###
# Hand
hand:ComputerA:Burn the Impure:1
### ComputerB ###
# Battlefield
battlefield:ComputerB:Runeclaw Bear:1
# Life
player:ComputerB:life:3
$options
exit_on_turn:2
$expected
turn:1
result:draw
life:ComputerA:20
life:ComputerB:3
#battlefield:ComputerB:Runeclaw Bear:0
#graveyard:ComputerB:Runeclaw Bear:1

View file

@ -0,0 +1,29 @@
### Test playing Burn the Impure ###
### Target: creature with Infect ###
#######################################
$include red.default
### ComputerA ###
# Hand
hand:ComputerA:Burn the Impure:1
### ComputerB ###
# Battlefield
battlefield:ComputerB:Tine Shrike:1
# Life
player:ComputerB:life:3
$options
exit_on_turn:2
$expected
turn:1
result:won
life:ComputerA:20
life:ComputerB:0
#battlefield:ComputerB:Tine Shrike:0
#graveyard:ComputerB:Tine Shrike:1

View file

@ -0,0 +1,31 @@
# Default init game state for white player (that plays with Plains)
### ComputerA ###
# Battlefield
battlefield:ComputerA:Plains:5
battlefield:ComputerA:Forest:5
# Hand
hand:ComputerA:Plains:2
hand:ComputerA:Forest:2
# Library
# from down to top
library:ComputerA:clear:0
library:ComputerA:Plains:5
library:ComputerA:Forest:5
### ComputerB ###
# Battlefield
battlefield:ComputerB:Mountain:2
# Hand
hand:ComputerB:Mountain:2
# Library
# from down to top
library:ComputerB:clear:0
library:ComputerB:Mountain:10

View file

@ -0,0 +1,28 @@
# Default init game state for red player (that plays with Mountains)
### ComputerA ###
# Battlefield
battlefield:ComputerA:Mountain:5
# Hand
hand:ComputerA:Mountain:4
# Library
# from down to top
library:ComputerA:clear:0
library:ComputerA:Mountain:10
### ComputerB ###
# Battlefield
battlefield:ComputerB:Plains:2
# Hand
hand:ComputerB:Plains:2
# Library
# from down to top
library:ComputerB:clear:0
library:ComputerB:Plains:10

View file

@ -0,0 +1,28 @@
# Default init game state for white player (that plays with Plains)
### ComputerA ###
# Battlefield
battlefield:ComputerA:Plains:5
# Hand
hand:ComputerA:Plains:4
# Library
# from down to top
library:ComputerA:clear:0
library:ComputerA:Plains:10
### ComputerB ###
# Battlefield
battlefield:ComputerB:Mountain:2
# Hand
hand:ComputerB:Mountain:2
# Library
# from down to top
library:ComputerB:clear:0
library:ComputerB:Mountain:10

View file

@ -139,7 +139,7 @@ public interface Game extends MageItem, Serializable {
//game play methods
//public void init(UUID choosingPlayerId);
public void start(UUID choosingPlayerId);
public void start(UUID choosingPlayerId, boolean testMode);
public void start(UUID choosingPlayerId, GameOptions options);
public void end();
public void mulligan(UUID playerId);
public void quit(UUID playerId);

View file

@ -292,19 +292,20 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
@Override
public void start(UUID choosingPlayerId) {
start(choosingPlayerId, false);
start(choosingPlayerId, GameOptions.getDefault());
}
@Override
public void start(UUID choosingPlayerId, boolean testMode) {
init(choosingPlayerId, testMode);
public void start(UUID choosingPlayerId, GameOptions options) {
init(choosingPlayerId, options.testMode);
PlayerList players = state.getPlayerList(startingPlayerId);
Player player = getPlayer(players.get());
while (!isGameOver()) {
if (player.getId().equals(startingPlayerId)) {
//if (player.getId().equals(startingPlayerId)) {
state.setTurnNum(state.getTurnNum() + 1);
fireInformEvent("Turn " + Integer.toString(state.getTurnNum()));
}
//}
if (checkStopOnTurnOption(options)) return;
state.setActivePlayerId(player.getId());
state.getTurn().play(this, player.getId());
if (isGameOver())
@ -318,6 +319,17 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
saveState();
}
private boolean checkStopOnTurnOption(GameOptions options) {
if (options.stopOnTurn != null) {
if (options.stopOnTurn.equals(state.getTurnNum())) {
winnerId = null; //DRAW
saveState();
return true;
}
}
return false;
}
protected void init(UUID choosingPlayerId, boolean testMode) {
for (Player player: state.getPlayers().values()) {
player.beginTurn(this);

View file

@ -0,0 +1,27 @@
package mage.game;
/**
* Game options for Mage game.
* Mainly used in tests to configure {@link GameImpl} with specific params.
*
* @author ayratn
*/
public class GameOptions {
private static GameOptions defInstance = new GameOptions();
public static GameOptions getDefault() {
return defInstance;
}
/**
* Defines the running mode. There are some exclusions made for test mode.
*/
public boolean testMode = false;
/**
* Defines the turn number game should stop on.
* By default, is null meaning that game shouldn't stop on any specific turn.
*/
public Integer stopOnTurn = null;
}

View file

@ -97,6 +97,9 @@ public interface Player extends MageItem, Copyable<Player> {
public ManaPool getManaPool();
public Set<UUID> getInRange();
public boolean isTestMode();
public void setTestMode(boolean value);
public void init(Game game);
public void init(Game game, boolean testMode);
public void useDeck(Deck deck, Game game);

View file

@ -98,6 +98,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
protected boolean left;
protected RangeOfInfluence range;
protected Set<UUID> inRange = new HashSet<UUID>();
protected boolean isTestMode = false;
@Override
public abstract T copy();
@ -1013,4 +1014,11 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
}
}
public boolean isTestMode() {
return isTestMode;
}
public void setTestMode(boolean value) {
this.isTestMode = value;
}
}