From 99735b368bf9b967b9c2b3e3e0ffb40952fc5d75 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Thu, 10 Mar 2011 15:27:15 +0300 Subject: [PATCH] New testing frameworks for cards. BurntheImpure test. --- .../java/mage/player/ai/ComputerPlayer.java | 2 +- Mage.Server/plugins/mage-player-ai-ma.jar | Bin 34101 -> 43108 bytes Mage.Server/plugins/mage-player-ai.jar | Bin 42229 -> 42466 bytes Mage.Server/plugins/mage-player-aiminimax.jar | Bin 39053 -> 39059 bytes Mage.Server/plugins/mage-player-human.jar | Bin 11693 -> 11698 bytes Mage.Tests/plugins/mage-player-ai-ma.jar | Bin 43082 -> 43108 bytes Mage.Tests/plugins/mage-player-aiminimax.jar | Bin 39053 -> 39059 bytes .../mage/test/serverside/PlayGameTest.java | 113 +----- .../test/serverside/base/CardTestAPI.java | 143 ++++++++ .../test/serverside/base/CardTestBase.java | 335 ++++++++++++++++++ .../test/serverside/base/MageTestBase.java | 193 +++++++++- .../serverside/base/impl/CardTestAPIImpl.java | 296 ++++++++++++++++ .../effects/BoostContinuousEffectTest.java | 23 ++ .../cards/single/mbs/BurntheImpureTest.java | 74 ++++ Mage.Tests/tests/M11/Honor of the Pure.test | 29 ++ .../MBS/Burn the Impure - no infect.test | 30 ++ Mage.Tests/tests/MBS/Burn the Impure.test | 29 ++ Mage.Tests/tests/green.white.default | 31 ++ Mage.Tests/tests/red.default | 28 ++ Mage.Tests/tests/white.default | 28 ++ Mage/src/mage/game/Game.java | 2 +- Mage/src/mage/game/GameImpl.java | 22 +- Mage/src/mage/game/GameOptions.java | 27 ++ Mage/src/mage/players/Player.java | 3 + Mage/src/mage/players/PlayerImpl.java | 8 + 25 files changed, 1297 insertions(+), 119 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestAPI.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestBase.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/serverside/cards/effects/BoostContinuousEffectTest.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/serverside/cards/single/mbs/BurntheImpureTest.java create mode 100644 Mage.Tests/tests/M11/Honor of the Pure.test create mode 100644 Mage.Tests/tests/MBS/Burn the Impure - no infect.test create mode 100644 Mage.Tests/tests/MBS/Burn the Impure.test create mode 100644 Mage.Tests/tests/green.white.default create mode 100644 Mage.Tests/tests/red.default create mode 100644 Mage.Tests/tests/white.default create mode 100644 Mage/src/mage/game/GameOptions.java diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index d9fb5f50cb..36c55ce246 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -126,7 +126,7 @@ public class ComputerPlayer> extends PlayerImpl i @Override public boolean chooseMulligan(Game game) { logger.debug("chooseMulligan"); - if (hand.size() < 6) + if (hand.size() < 6 || isTestMode()) return false; Set lands = hand.getCards(new FilterLandCard(), game); if (lands.size() < 2 || lands.size() > hand.size() - 2) diff --git a/Mage.Server/plugins/mage-player-ai-ma.jar b/Mage.Server/plugins/mage-player-ai-ma.jar index 457fe25f12fc0488669a159ff5ec8e5b4e48199e..c448c463f4be08476d1b878de692a0fd00e651be 100644 GIT binary patch delta 23039 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 s%aQ+8^RJu!mg@ZXU^05 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 delta 22031 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 jCSRMsx+2EJ$RSb~W-ZD|psV diff --git a/Mage.Server/plugins/mage-player-aiminimax.jar b/Mage.Server/plugins/mage-player-aiminimax.jar index 7e6aedbce48c860b850b83eeed7bb7d813d73589..7aaa7fc459507cedc12ececddd0ad420bbf880ba 100644 GIT binary patch delta 1131 zcmZWoZAg<*6ux^mm&}ber?!vK`Av$bX{jxS(?*n*k|0WRXny48LX#*>OboQNwz-{s zXh9SunMF2~K2$P9lhBq$;E$xRKO&+Ls1T8L-#fEjy+7W2?sJ~=oWnWqF}W#lQ-a(q zHiw7kvsP!Y3L*rnbq1CHrI*Gk3^iL-m9h10k^1hmw66L#;roE1KD)6au;L>3duzq8 zc36v;@skQREwda=Um#({(09Lv^_3z<49D#?K6BMKT8>!c``j{eaP#3?Q z8-J($;OObA)@c^=zGd*mzlH0$Su84mz1eiISd*DuhiU7+{d9?BE*l@8&4&;zsmJi^ zOv&l;_^o0}n_M~n*I8pMsV}eH%3;XvWXOuZY6}jbQ=gT%;~r25NparbO%6i$npNPj zk(t4|O-_Y#Ad#m4B?Ktwu$;jYcM%&Q3l587gw1dX(Xv6zw+pRL;}EJXLnwkll=Cn# z3KoD*6ccBa+yz`n{x(H%Dkg?NvOSOtINL6#(v<|dY8Mme7KZeN7!rfW9z^7(GO`D{ z{cu7FLeY%8r*u%K+XHb9K8Zc(kW<+PU^%7GU=YEilNaVo?}&;eof>Vx1&MG$%*yW@ zFgQO%VWtR;aer<4%qn?35GYe?G*V?KKnJF^Pfa;%y`1UThrt-+FF>T(b<0yS$Aka!EN$ t>SY>CfL;vZErJ&=Id#B{p%f5w^8f8qXlD>L*a6cWD!9-o%zUv zP81m1HdiP+$LYoCF`(+asK5*|;EOtlnk>ht1)?{5Gd^MhGYnWZLKsij9GSr)8eCs> z!3+fpEr^J(wVE7Qq%86q3z(4~_naHdh|G$BXy1~v3&JSOR}ceBO=(SL1v7qi%?3+N zp3w`oeDcv=W01t;-@Ts9A6EKpcJHfY;s6TRRf``poqT43SN%b*Lk2ty7pfQh7r0Tm z_^m_YhURufYY~>ejW3ckcZ4L#zI*@n?Z;Wh+}fgXAND0L=soHnyeUNcmU5N7h4mL( zzw6Rfuje%Oex1CjXPdP|$E^MDDC%Qfrg^0xyUwI5eTPE76!Xz&17jNQ7)i>!sM5e zg}_4pfI_?$Kq2|beiodQJty;kjntX>+WWaI1H(>N1_l)rYptxoiu>m0K@0BRN=6}{(*GtFK zH_&5M$Om5?@3W7e2JxNLVBOR$!ow@VIkpGcRUcU+C@eui3j;hb27>^@ zTgT^_N!r|C5k75cCP&7}b=vZiy|hKZLW$bajJ=aK%wf1b{VREGc7Fz>blm2dzqam zFt%;3Pfi_>F3)p=2Y8Dzj0br3aKj!_pxZ}wyKWCAm0F>`_yY+j~34JuNgJ^Pbtr)odWZaKUy$y+uKokfu`8 z;wbio3+xN7Ws8_-rYrsUo>qI<+iKI17nzMe+ZgpX1*u$?TCh~|llY5GL9R=^Gmdc< zE!`?2{&A;e^y)Pe4sGE6U$;_r_8SN1Acfy=+vDX6J0{eX?diN+ZeehbKfs%jNrV|5 zD0Y+IX&R#j%Va$*Q!|ho!2t&)Sm7K2hPRIUfV|wq^i+K`(c?JgI z00stS6gBP4lMS@xCoj?#0xRU1#%dZ0H0%P<6KW_5Co50>pd~u_l(O*TKiWLtnBvxv zWd8qOXR^AE99T1wdIkwNM}Xn~|46mT8`Y#H*Xuwu_UTBn{Qut%64cXzg)s*MFxKrP z!#8%lV`N}>1ayEQ+zgO!u7i~8OxD*G274QvDu4{AsWy^ diff --git a/Mage.Tests/plugins/mage-player-ai-ma.jar b/Mage.Tests/plugins/mage-player-ai-ma.jar index 849a57c7a3215d925039c145bfd5fb9f7b9e0354..c448c463f4be08476d1b878de692a0fd00e651be 100644 GIT binary patch delta 13609 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 YrMBqbxdf2qA{>Z7H6Dt4@jr_H0nJ_iSpWb4 delta 13537 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 handCardsA = new ArrayList(); - private List handCardsB = new ArrayList(); - private List battlefieldCardsA = new ArrayList(); - private List battlefieldCardsB = new ArrayList(); - private List graveyardCardsA = new ArrayList(); - private List graveyardCardsB = new ArrayList(); - private List libraryCardsA = new ArrayList(); - private List libraryCardsB = new ArrayList(); - - private Map commandsA = new HashMap(); - private Map commandsB = new HashMap(); - @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 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 cards = null; - List 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); - } } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestAPI.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestAPI.java new file mode 100644 index 0000000000..de598db5b0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestAPI.java @@ -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. + *

+ * 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; +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestBase.java new file mode 100644 index 0000000000..07ccb98b63 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestBase.java @@ -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; + } + } + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java index 03518fd762..00d5c7c183 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java @@ -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 handCardsA = new ArrayList(); + protected List handCardsB = new ArrayList(); + protected List battlefieldCardsA = new ArrayList(); + protected List battlefieldCardsB = new ArrayList(); + protected List graveyardCardsA = new ArrayList(); + protected List graveyardCardsB = new ArrayList(); + protected List libraryCardsA = new ArrayList(); + protected List libraryCardsB = new ArrayList(); + + protected Map commandsA = new HashMap(); + protected Map commandsB = new HashMap(); + + 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: + *

+ * Example: + * turn:1 + * result:won:ComputerA + * life:ComputerA:20 + * life:ComputerB:0 + * battlefield:ComputerB:Tine Shrike:0 + * graveyard:ComputerB:Tine Shrike:1 + */ + protected List expectedResults = new ArrayList(); + + 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 cards = null; + List 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); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java new file mode 100644 index 0000000000..e84866d601 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java @@ -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 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 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. + *

+ * 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); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/cards/effects/BoostContinuousEffectTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/cards/effects/BoostContinuousEffectTest.java new file mode 100644 index 0000000000..fc1948a013 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/cards/effects/BoostContinuousEffectTest.java @@ -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); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/cards/single/mbs/BurntheImpureTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/cards/single/mbs/BurntheImpureTest.java new file mode 100644 index 0000000000..46d5c9022e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/cards/single/mbs/BurntheImpureTest.java @@ -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); + } +} diff --git a/Mage.Tests/tests/M11/Honor of the Pure.test b/Mage.Tests/tests/M11/Honor of the Pure.test new file mode 100644 index 0000000000..17ee83f0f3 --- /dev/null +++ b/Mage.Tests/tests/M11/Honor of the Pure.test @@ -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 \ No newline at end of file diff --git a/Mage.Tests/tests/MBS/Burn the Impure - no infect.test b/Mage.Tests/tests/MBS/Burn the Impure - no infect.test new file mode 100644 index 0000000000..c32bd845e6 --- /dev/null +++ b/Mage.Tests/tests/MBS/Burn the Impure - no infect.test @@ -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 + + diff --git a/Mage.Tests/tests/MBS/Burn the Impure.test b/Mage.Tests/tests/MBS/Burn the Impure.test new file mode 100644 index 0000000000..2b72110fa1 --- /dev/null +++ b/Mage.Tests/tests/MBS/Burn the Impure.test @@ -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 + diff --git a/Mage.Tests/tests/green.white.default b/Mage.Tests/tests/green.white.default new file mode 100644 index 0000000000..fb76ec3502 --- /dev/null +++ b/Mage.Tests/tests/green.white.default @@ -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 + diff --git a/Mage.Tests/tests/red.default b/Mage.Tests/tests/red.default new file mode 100644 index 0000000000..4f2a2f72a9 --- /dev/null +++ b/Mage.Tests/tests/red.default @@ -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 + diff --git a/Mage.Tests/tests/white.default b/Mage.Tests/tests/white.default new file mode 100644 index 0000000000..44e51196b2 --- /dev/null +++ b/Mage.Tests/tests/white.default @@ -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 + diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index 147e876934..0b03427c5d 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -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); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 90559d77d6..45d011f96a 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -292,19 +292,20 @@ public abstract class GameImpl> 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> 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); diff --git a/Mage/src/mage/game/GameOptions.java b/Mage/src/mage/game/GameOptions.java new file mode 100644 index 0000000000..c4fe621afa --- /dev/null +++ b/Mage/src/mage/game/GameOptions.java @@ -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; +} diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index 3cf0a99349..3519e39791 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -96,6 +96,9 @@ public interface Player extends MageItem, Copyable { public boolean hasLeft(); public ManaPool getManaPool(); public Set getInRange(); + + public boolean isTestMode(); + public void setTestMode(boolean value); public void init(Game game); public void init(Game game, boolean testMode); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 0821edb05a..22f955ded4 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -98,6 +98,7 @@ public abstract class PlayerImpl> implements Player, Ser protected boolean left; protected RangeOfInfluence range; protected Set inRange = new HashSet(); + protected boolean isTestMode = false; @Override public abstract T copy(); @@ -1013,4 +1014,11 @@ public abstract class PlayerImpl> implements Player, Ser } } + public boolean isTestMode() { + return isTestMode; + } + + public void setTestMode(boolean value) { + this.isTestMode = value; + } }