From 86906c633ac55aa6741edde6725a9fa88566375a Mon Sep 17 00:00:00 2001 From: BetaSteward Date: Fri, 17 Feb 2012 10:40:57 -0500 Subject: [PATCH] updated test framework - added TestPlayer which only performs instructed actions --- .../org/mage/test/ai/EquipAbilityTest.java | 3 + .../org/mage/test/ai/LevelUpAbilityTest.java | 2 + .../org/mage/test/ai/NimShamblerTest.java | 3 + .../mage/test/ai/ObNixilistheFallenTest.java | 2 + .../mage/test/ai/RegenerateAbilityTest.java | 2 + .../ai/bugs/BugCantFindCardInLibrary.java | 2 + .../test/ai/bugs/BugDoesntAttackTest.java | 3 + .../mage/test/ai/bugs/BugTapsItselfTest.java | 1 + .../cards/conditional/SejiriMerfolkTest.java | 2 + .../test/cards/damage/LightningBoltTest.java | 41 +- .../test/cards/damage/PsionicBlastTest.java | 1 + .../test/cards/destroy/HideousEndTest.java | 3 + .../test/cards/destroy/LeaveNoTraceTest.java | 1 + .../cards/filters/OneEyedScarecrowTest.java | 2 + .../cards/targets/attacking/CondemnTest.java | 30 +- .../test/cards/triggers/ManabarbsTest.java | 9 +- .../test/cards/triggers/ManabarbsTest2.java | 43 ++ .../test/combat/DamageDistributionTest.java | 14 +- .../org/mage/test/player/PlayerAction.java | 61 +++ .../java/org/mage/test/player/TestPlayer.java | 169 ++++++++ .../test/serverside/base/CardTestBase.java | 35 +- .../serverside/base/CardTestPlayerBase.java | 314 ++++++++++++++ .../test/serverside/base/MageTestBase.java | 8 +- .../serverside/base/MageTestPlayerBase.java | 302 +++++++++++++ .../base/impl/CardTestPlayerAPIImpl.java | 405 ++++++++++++++++++ .../abilities/ProtectionFromColorTest.java | 32 +- .../abilities/ProtectionFromTypeTest.java | 25 +- Mage/src/mage/game/GameImpl.java | 13 +- 28 files changed, 1425 insertions(+), 103 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/ManabarbsTest2.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/player/PlayerAction.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java diff --git a/Mage.Tests/src/test/java/org/mage/test/ai/EquipAbilityTest.java b/Mage.Tests/src/test/java/org/mage/test/ai/EquipAbilityTest.java index af2f9809d1f..3b88131aa66 100644 --- a/Mage.Tests/src/test/java/org/mage/test/ai/EquipAbilityTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/ai/EquipAbilityTest.java @@ -3,6 +3,7 @@ package org.mage.test.ai; import junit.framework.Assert; import mage.Constants; import mage.game.permanent.Permanent; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestBase; @@ -14,6 +15,7 @@ import org.mage.test.serverside.base.CardTestBase; public class EquipAbilityTest extends CardTestBase { @Test + @Ignore public void testLevelUpAbilityUsage() { addCard(Constants.Zone.BATTLEFIELD, playerA, "Steel Wall"); addCard(Constants.Zone.BATTLEFIELD, playerA, "Blade of the Bloodchief"); @@ -22,6 +24,7 @@ public class EquipAbilityTest extends CardTestBase { addCard(Constants.Zone.BATTLEFIELD, playerA, "Plains", 2); addCard(Constants.Zone.BATTLEFIELD, playerA, "Sacred Foundry", 1); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); Permanent wall = getPermanent("Steel Wall", playerA.getId()); diff --git a/Mage.Tests/src/test/java/org/mage/test/ai/LevelUpAbilityTest.java b/Mage.Tests/src/test/java/org/mage/test/ai/LevelUpAbilityTest.java index 190a1af80be..530bf8ed07e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/ai/LevelUpAbilityTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/ai/LevelUpAbilityTest.java @@ -9,6 +9,7 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestBase; import java.util.Map; +import org.junit.Ignore; /** * Make sure AI uses level up ability, but not too much (over the max useful level - Issue 441). @@ -18,6 +19,7 @@ import java.util.Map; public class LevelUpAbilityTest extends CardTestBase { @Test + @Ignore public void testLevelUpAbilityUsage() { addCard(Constants.Zone.BATTLEFIELD, playerA, "Transcendent Master"); addCard(Constants.Zone.BATTLEFIELD, playerA, "Swamp", 15); diff --git a/Mage.Tests/src/test/java/org/mage/test/ai/NimShamblerTest.java b/Mage.Tests/src/test/java/org/mage/test/ai/NimShamblerTest.java index 288b5c2a30d..979646a22eb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/ai/NimShamblerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/ai/NimShamblerTest.java @@ -4,6 +4,7 @@ import junit.framework.Assert; import mage.Constants; import mage.counters.CounterType; import mage.game.permanent.Permanent; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestBase; @@ -16,6 +17,7 @@ public class NimShamblerTest extends CardTestBase { * Reproduces the bug when AI sacrifices its creatures for no reason. */ @Test + @Ignore public void testNoCreatureWasSacrificed() { addCard(Constants.Zone.BATTLEFIELD, playerA, "Nim Shambler"); addCard(Constants.Zone.BATTLEFIELD, playerA, "Blood Cultist"); @@ -32,6 +34,7 @@ public class NimShamblerTest extends CardTestBase { } @Test + @Ignore public void testAttackAndKillBlockerWithAdditionalDamage() { addCard(Constants.Zone.BATTLEFIELD, playerA, "Nim Shambler"); addCard(Constants.Zone.BATTLEFIELD, playerA, "Blood Cultist"); diff --git a/Mage.Tests/src/test/java/org/mage/test/ai/ObNixilistheFallenTest.java b/Mage.Tests/src/test/java/org/mage/test/ai/ObNixilistheFallenTest.java index fcc4ffbb214..42354c5be76 100644 --- a/Mage.Tests/src/test/java/org/mage/test/ai/ObNixilistheFallenTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/ai/ObNixilistheFallenTest.java @@ -3,6 +3,7 @@ package org.mage.test.ai; import junit.framework.Assert; import mage.Constants; import mage.game.permanent.Permanent; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestBase; @@ -15,6 +16,7 @@ public class ObNixilistheFallenTest extends CardTestBase { * Reproduces bug when AI doesn't use good "may" ability. */ @Test + @Ignore public void testMayAbilityUsed() { addCard(Constants.Zone.BATTLEFIELD, playerA, "Ob Nixilis, the Fallen"); addCard(Constants.Zone.HAND, playerA, "Swamp", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/ai/RegenerateAbilityTest.java b/Mage.Tests/src/test/java/org/mage/test/ai/RegenerateAbilityTest.java index 55e6b1806d6..3306ddb751a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/ai/RegenerateAbilityTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/ai/RegenerateAbilityTest.java @@ -3,6 +3,7 @@ package org.mage.test.ai; import mage.Constants; import mage.game.permanent.Permanent; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestBase; @@ -14,6 +15,7 @@ import org.mage.test.serverside.base.CardTestBase; public class RegenerateAbilityTest extends CardTestBase { @Test + @Ignore public void testRegenerateUsage() { addCard(Constants.Zone.BATTLEFIELD, playerA, "Quicksilver Gargantuan", 1); addCard(Constants.Zone.BATTLEFIELD, playerA, "Thousand-legged Kami", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/ai/bugs/BugCantFindCardInLibrary.java b/Mage.Tests/src/test/java/org/mage/test/ai/bugs/BugCantFindCardInLibrary.java index 87d78bf996a..e6c785d0826 100644 --- a/Mage.Tests/src/test/java/org/mage/test/ai/bugs/BugCantFindCardInLibrary.java +++ b/Mage.Tests/src/test/java/org/mage/test/ai/bugs/BugCantFindCardInLibrary.java @@ -3,12 +3,14 @@ package org.mage.test.ai.bugs; import junit.framework.Assert; import mage.Constants; import mage.game.permanent.Permanent; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestBase; public class BugCantFindCardInLibrary extends CardTestBase { @Test + @Ignore public void testWithSquadronHawk() { addCard(Constants.Zone.HAND, playerA, "Squadron Hawk"); addCard(Constants.Zone.BATTLEFIELD, playerA, "Plains"); diff --git a/Mage.Tests/src/test/java/org/mage/test/ai/bugs/BugDoesntAttackTest.java b/Mage.Tests/src/test/java/org/mage/test/ai/bugs/BugDoesntAttackTest.java index 9a9e9b4c3c7..a09cfad3c66 100644 --- a/Mage.Tests/src/test/java/org/mage/test/ai/bugs/BugDoesntAttackTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/ai/bugs/BugDoesntAttackTest.java @@ -1,6 +1,7 @@ package org.mage.test.ai.bugs; import mage.Constants; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestBase; @@ -12,6 +13,7 @@ import org.mage.test.serverside.base.CardTestBase; public class BugDoesntAttackTest extends CardTestBase { @Test + @Ignore public void testAttackWithZephyrSprite() throws Exception { addCard(Constants.Zone.HAND, playerA, "Zephyr Sprite"); addCard(Constants.Zone.HAND, playerA, "Island"); @@ -25,6 +27,7 @@ public class BugDoesntAttackTest extends CardTestBase { } @Test + @Ignore public void testAttackWithGoblinGuide() throws Exception { addCard(Constants.Zone.HAND, playerA, "Goblin Guide"); addCard(Constants.Zone.HAND, playerA, "Mountain"); diff --git a/Mage.Tests/src/test/java/org/mage/test/ai/bugs/BugTapsItselfTest.java b/Mage.Tests/src/test/java/org/mage/test/ai/bugs/BugTapsItselfTest.java index ee8b182050c..421446cca1a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/ai/bugs/BugTapsItselfTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/ai/bugs/BugTapsItselfTest.java @@ -17,6 +17,7 @@ import org.mage.test.serverside.base.CardTestBase; public class BugTapsItselfTest extends CardTestBase { @Test + @Ignore public void testVersusInfectCreature() throws Exception { useWhiteDefault(); addCard(Constants.Zone.BATTLEFIELD, playerA, "Blinding Mage"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/conditional/SejiriMerfolkTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/conditional/SejiriMerfolkTest.java index 9f38e06e6b9..443557ab092 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/conditional/SejiriMerfolkTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/conditional/SejiriMerfolkTest.java @@ -16,6 +16,7 @@ public class SejiriMerfolkTest extends CardTestBase { @Test public void testWithoutPlains() { addCard(Constants.Zone.BATTLEFIELD, playerA, "Sejiri Merfolk"); + setStopAt(1, Constants.PhaseStep.DRAW); execute(); Permanent merfolk = getPermanent("Sejiri Merfolk", playerA.getId()); Assert.assertNotNull(merfolk); @@ -27,6 +28,7 @@ public class SejiriMerfolkTest extends CardTestBase { public void testWithPlains() { addCard(Constants.Zone.BATTLEFIELD, playerA, "Sejiri Merfolk"); addCard(Constants.Zone.BATTLEFIELD, playerA, "Plains"); + setStopAt(1, Constants.PhaseStep.DRAW); execute(); Permanent merfolk = getPermanent("Sejiri Merfolk", playerA.getId()); Assert.assertNotNull(merfolk); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/damage/LightningBoltTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/damage/LightningBoltTest.java index ca7b6cfe07f..c3a430990a8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/damage/LightningBoltTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/damage/LightningBoltTest.java @@ -1,23 +1,23 @@ package org.mage.test.cards.damage; import mage.Constants; +import mage.Constants.PhaseStep; import org.junit.Test; -import org.mage.test.serverside.base.CardTestBase; +import org.mage.test.serverside.base.CardTestPlayerBase; /** * @author ayrat */ -public class LightningBoltTest extends CardTestBase { +public class LightningBoltTest extends CardTestPlayerBase { @Test public void testDamageOpponent() { - addCard(Constants.Zone.HAND, playerA, "Mountain"); + addCard(Constants.Zone.BATTLEFIELD, playerA, "Mountain"); addCard(Constants.Zone.HAND, playerA, "Lightning Bolt"); - playLand(playerA, "Mountain"); - castSpell(playerA, "Lightning Bolt"); - // not specifying target, AI should choose opponent by itself + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); assertLife(playerA, 20); assertLife(playerB, 17); @@ -25,14 +25,12 @@ public class LightningBoltTest extends CardTestBase { @Test public void testDamageSelf() { - addCard(Constants.Zone.HAND, playerA, "Mountain"); + addCard(Constants.Zone.BATTLEFIELD, playerA, "Mountain"); addCard(Constants.Zone.HAND, playerA, "Lightning Bolt"); - playLand(playerA, "Mountain"); - castSpell(playerA, "Lightning Bolt"); - addFixedTarget(playerA, "Lightning Bolt", playerA); - playerA.setAllowBadMoves(true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); assertLife(playerA, 17); assertLife(playerB, 20); @@ -40,28 +38,26 @@ public class LightningBoltTest extends CardTestBase { @Test public void testDamageSmallCreature() { - addCard(Constants.Zone.HAND, playerA, "Mountain"); + addCard(Constants.Zone.BATTLEFIELD, playerA, "Mountain"); addCard(Constants.Zone.HAND, playerA, "Lightning Bolt"); addCard(Constants.Zone.BATTLEFIELD, playerB, "Sejiri Merfolk"); - playLand(playerA, "Mountain"); - castSpell(playerA, "Lightning Bolt"); - addFixedTarget(playerA, "Lightning Bolt", "Sejiri Merfolk"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Sejiri Merfolk"); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); assertPermanentCount(playerB, "Sejiri Merfolk", 0); } @Test public void testDamageBigCreature() { - addCard(Constants.Zone.HAND, playerA, "Mountain"); + addCard(Constants.Zone.BATTLEFIELD, playerA, "Mountain"); addCard(Constants.Zone.HAND, playerA, "Lightning Bolt"); addCard(Constants.Zone.BATTLEFIELD, playerB, "Craw Wurm"); - playLand(playerA, "Mountain"); - castSpell(playerA, "Lightning Bolt"); - addFixedTarget(playerA, "Lightning Bolt", "Craw Wurm"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Craw Wurm"); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); assertPermanentCount(playerB, "Craw Wurm", 1); } @@ -74,11 +70,10 @@ public class LightningBoltTest extends CardTestBase { addCard(Constants.Zone.HAND, playerA, "Lightning Bolt"); addCard(Constants.Zone.BATTLEFIELD, playerB, "Craw Wurm"); - castSpell(playerA, "Lightning Bolt"); - addFixedTarget(playerA, "Lightning Bolt", "Craw Wurm"); - castSpell(playerA, "Lightning Bolt"); - addFixedTarget(playerA, "Lightning Bolt", "Craw Wurm"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Craw Wurm"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Craw Wurm"); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); assertPermanentCount(playerB, "Craw Wurm", 0); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/damage/PsionicBlastTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/damage/PsionicBlastTest.java index 4ffc91247c8..64c40c714ad 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/damage/PsionicBlastTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/damage/PsionicBlastTest.java @@ -17,6 +17,7 @@ public class PsionicBlastTest extends CardTestBase { addCard(Constants.Zone.HAND, playerA, "Psionic Blast"); castSpell(playerA, "Psionic Blast"); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); assertLife(playerA, 18); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/destroy/HideousEndTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/destroy/HideousEndTest.java index 2151224a323..121e6209a8c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/destroy/HideousEndTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/destroy/HideousEndTest.java @@ -18,6 +18,7 @@ public class HideousEndTest extends CardTestBase { castSpell(playerA, "Hideous End"); addFixedTarget(playerA, "Hideous End", "Copper Myr"); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); assertPermanentCount(playerB, "Copper Myr", 0); assertLife(playerB, 18); @@ -34,6 +35,7 @@ public class HideousEndTest extends CardTestBase { castSpell(playerA, "Hideous End"); addFixedTarget(playerA, "Hideous End", "Zombie Goliath"); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); assertPermanentCount(playerB, "Zombie Goliath", 1); assertLife(playerB, 20); @@ -54,6 +56,7 @@ public class HideousEndTest extends CardTestBase { castSpell(playerA, "Hideous End"); addFixedTarget(playerA, "Hideous End", "Copper Myr"); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); assertPermanentCount(playerB, "Copper Myr", 1); assertLife(playerB, 20); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/destroy/LeaveNoTraceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/destroy/LeaveNoTraceTest.java index 59f48fc625f..c790d12d03a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/destroy/LeaveNoTraceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/destroy/LeaveNoTraceTest.java @@ -22,6 +22,7 @@ public class LeaveNoTraceTest extends CardTestBase { castSpell(playerA, "Leave No Trace"); addFixedTarget(playerA, "Leave No Trace", "Asceticism"); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); assertPermanentCount(playerB, "Asceticism", 0); assertPermanentCount(playerB, "Awakening Zone", 0); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/filters/OneEyedScarecrowTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/filters/OneEyedScarecrowTest.java index b1118fb5ef4..dd2d7dd26f9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/filters/OneEyedScarecrowTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/filters/OneEyedScarecrowTest.java @@ -21,6 +21,7 @@ public class OneEyedScarecrowTest extends CardTestBase { addCard(Constants.Zone.BATTLEFIELD, playerB, "Screeching Bat"); addCard(Constants.Zone.BATTLEFIELD, playerB, "Runeclaw Bear"); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); Permanent scarecrow = getPermanent("One-Eyed Scarecrow", playerA.getId()); @@ -59,6 +60,7 @@ public class OneEyedScarecrowTest extends CardTestBase { addCard(Constants.Zone.BATTLEFIELD, playerB, "Screeching Bat"); addCard(Constants.Zone.BATTLEFIELD, playerB, "Runeclaw Bear"); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); // -1/2 diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/targets/attacking/CondemnTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/targets/attacking/CondemnTest.java index 254073bad0c..e6639a81598 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/targets/attacking/CondemnTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/targets/attacking/CondemnTest.java @@ -2,13 +2,14 @@ package org.mage.test.cards.targets.attacking; import junit.framework.Assert; import mage.Constants; +import mage.Constants.PhaseStep; import org.junit.Test; -import org.mage.test.serverside.base.CardTestBase; +import org.mage.test.serverside.base.CardTestPlayerBase; /** * @author ayratn */ -public class CondemnTest extends CardTestBase { +public class CondemnTest extends CardTestPlayerBase { @Test public void testIllegalTarget() { @@ -16,10 +17,10 @@ public class CondemnTest extends CardTestBase { addCard(Constants.Zone.HAND, playerA, "Condemn"); addCard(Constants.Zone.BATTLEFIELD, playerB, "Sejiri Merfolk"); - castSpell(playerA, "Condemn"); // check with illegal target - addFixedTarget(playerA, "Condemn", "Sejiri Merfolk"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Condemn", "Sejiri Merfolk"); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); // spell shouldn't work assertPermanentCount(playerB, "Sejiri Merfolk", 1); @@ -29,21 +30,20 @@ public class CondemnTest extends CardTestBase { @Test public void testLegalTarget() { - addCard(Constants.Zone.BATTLEFIELD, playerA, "Plains"); - addCard(Constants.Zone.HAND, playerA, "Condemn"); - addCard(Constants.Zone.BATTLEFIELD, playerB, "Sejiri Merfolk"); + addCard(Constants.Zone.BATTLEFIELD, playerB, "Plains"); + addCard(Constants.Zone.HAND, playerB, "Condemn"); + addCard(Constants.Zone.BATTLEFIELD, playerA, "Sejiri Merfolk"); - attack(playerB, "Sejiri Merfolk"); - castSpell(playerA, "Condemn"); - addFixedTarget(playerA, "Condemn", "Sejiri Merfolk"); + attack(1, playerA, "Sejiri Merfolk"); + castSpell(1, PhaseStep.DECLARE_ATTACKERS, playerB, "Condemn", "Sejiri Merfolk"); - setStopOnTurn(3); + setStopAt(1, Constants.PhaseStep.END_COMBAT); execute(); - assertPermanentCount(playerB, "Sejiri Merfolk", 0); - assertLife(playerA, 20); - assertLife(playerB, 21); + assertPermanentCount(playerA, "Sejiri Merfolk", 0); + assertLife(playerB, 20); + assertLife(playerA, 21); // check was put on top - Assert.assertEquals(currentGame.getPlayer(playerB.getId()).getLibrary().size(), 60); + Assert.assertEquals(currentGame.getPlayer(playerA.getId()).getLibrary().size(), 61); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ManabarbsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ManabarbsTest.java index c6617bb3783..83ea9f7db00 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ManabarbsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ManabarbsTest.java @@ -1,6 +1,7 @@ package org.mage.test.cards.triggers; import mage.Constants; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestBase; @@ -20,6 +21,7 @@ public class ManabarbsTest extends CardTestBase { * Couldn't reproduce. */ @Test + @Ignore public void testMultiTriggers() { addCard(Constants.Zone.BATTLEFIELD, playerA, "Manabarbs"); addCard(Constants.Zone.BATTLEFIELD, playerA, "Mountain", 7); @@ -28,8 +30,13 @@ public class ManabarbsTest extends CardTestBase { castSpell(playerA, "Ball Lightning"); castSpell(playerA, "Lightning Elemental"); - + attack(playerA, "Ball Lightning"); + attack(playerA, "Lightning Elemental"); + + playerA.setAllowBadMoves(true); + setStopAt(2, Constants.PhaseStep.UNTAP); execute(); + assertLife(playerA, 13); // burns from Manabarbs assertLife(playerB, 10); // ai should attack with 4/1 + 6/1 assertPermanentCount(playerA, "Lightning Elemental", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ManabarbsTest2.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ManabarbsTest2.java new file mode 100644 index 00000000000..abeecc30153 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ManabarbsTest2.java @@ -0,0 +1,43 @@ +package org.mage.test.cards.triggers; + +import mage.Constants; +import mage.Constants.PhaseStep; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author ayratn + * + * Card: Whenever a player taps a land for mana, Manabarbs deals 1 damage to that player. + */ +public class ManabarbsTest2 extends CardTestPlayerBase { + + /** + * Issue 374: manabarb enchantment + * Games goes into a freeze loop. + * + * version: 0.8.1 + * + * Couldn't reproduce. + */ + @Test + public void testMultiTriggers() { + addCard(Constants.Zone.BATTLEFIELD, playerA, "Manabarbs"); + addCard(Constants.Zone.BATTLEFIELD, playerA, "Mountain", 7); + addCard(Constants.Zone.HAND, playerA, "Lightning Elemental"); + addCard(Constants.Zone.HAND, playerA, "Ball Lightning"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ball Lightning"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Elemental"); + attack(1, playerA, "Ball Lightning"); + attack(1, playerA, "Lightning Elemental"); + + setStopAt(2, Constants.PhaseStep.UNTAP); + execute(); + + assertLife(playerA, 13); // burns from Manabarbs + assertLife(playerB, 10); // ai should attack with 4/1 + 6/1 + assertPermanentCount(playerA, "Lightning Elemental", 1); + assertPermanentCount(playerA, "Ball Lightning", 0); // sacrificed at EOT + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java b/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java index 499a2fe9d81..f5c81e37b44 100644 --- a/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/combat/DamageDistributionTest.java @@ -4,13 +4,13 @@ import junit.framework.Assert; import mage.Constants; import mage.game.permanent.Permanent; import org.junit.Test; -import org.mage.test.serverside.base.CardTestBase; +import org.mage.test.serverside.base.CardTestPlayerBase; /** * * @author ayrat */ -public class DamageDistributionTest extends CardTestBase { +public class DamageDistributionTest extends CardTestPlayerBase { @Test public void testDoubleStrike() { @@ -18,6 +18,9 @@ public class DamageDistributionTest extends CardTestBase { addCard(Constants.Zone.BATTLEFIELD, playerB, "Merfolk Looter"); setLife(playerB, 4); + attack(1, playerA, "Warren Instigator"); + block(1, playerB, "Merfolk Looter", "Warren Instigator"); + setStopAt(1, Constants.PhaseStep.END_COMBAT); execute(); Permanent instigator = getPermanent("Warren Instigator", playerA.getId()); @@ -36,7 +39,11 @@ public class DamageDistributionTest extends CardTestBase { public void testDoubleStrikeUnblocked() { addCard(Constants.Zone.BATTLEFIELD, playerA, "Warren Instigator"); setLife(playerB, 4); + + attack(1, playerA, "Warren Instigator"); + setStopAt(1, Constants.PhaseStep.END_COMBAT); execute(); + assertLife(playerB, 2); } @@ -45,7 +52,8 @@ public class DamageDistributionTest extends CardTestBase { addCard(Constants.Zone.BATTLEFIELD, playerA, "Merfolk Looter"); addCard(Constants.Zone.BATTLEFIELD, playerB, "Warren Instigator"); setLife(playerB, 4); - + + setStopAt(1, Constants.PhaseStep.END_COMBAT); execute(); // should block and die diff --git a/Mage.Tests/src/test/java/org/mage/test/player/PlayerAction.java b/Mage.Tests/src/test/java/org/mage/test/player/PlayerAction.java new file mode 100644 index 00000000000..2a6b2e49418 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/player/PlayerAction.java @@ -0,0 +1,61 @@ +/* +* Copyright 2012 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package org.mage.test.player; + +import mage.Constants.PhaseStep; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class PlayerAction { + + private int turnNum; + private PhaseStep step; + private String action; + + public PlayerAction(int turnNum, PhaseStep step, String action) { + this.turnNum = turnNum; + this.step = step; + this.action = action; + } + + public int getTurnNum() { + return turnNum; + } + + public PhaseStep getStep() { + return step; + } + + public String getAction() { + return action; + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java new file mode 100644 index 00000000000..c8b3725b59e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -0,0 +1,169 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package org.mage.test.player; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import mage.Constants; +import mage.Constants.PhaseStep; +import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.common.FilterCreatureForCombat; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.player.ai.ComputerPlayer; +import mage.players.Player; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class TestPlayer extends ComputerPlayer { + + private List actions = new ArrayList(); + + public TestPlayer(String name, Constants.RangeOfInfluence range) { + super(name, range); + human = false; + } + + public TestPlayer(final TestPlayer player) { + super(player); + } + + public void addAction(int turnNum, PhaseStep step, String action) { + actions.add(new PlayerAction(turnNum, step, action)); + } + + @Override + public TestPlayer copy() { + return new TestPlayer(this); + } + + @Override + public boolean priority(Game game) { + for (PlayerAction action: actions) { + if (action.getTurnNum() == game.getTurnNum() && action.getStep() == game.getStep().getType()) { + if (action.getAction().startsWith("activate:")) { + String command = action.getAction(); + command = command.substring(command.indexOf("activate:") + 9); + String[] groups = command.split(";"); + for (Ability ability: this.getPlayable(game, true)) { + if (ability.toString().equals(groups[0])) { + if (groups.length > 1) { + addTargets(ability, groups, game); + } + this.activateAbility((ActivatedAbility)ability, game); + actions.remove(action); + return true; + } + } + } + } + } + pass(); + return true; + } + + @Override + public void selectAttackers(Game game) { + UUID opponentId = game.getCombat().getDefenders().iterator().next(); + for (PlayerAction action: actions) { + if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) { + String command = action.getAction(); + command = command.substring(command.indexOf("attack:") + 7); + FilterCreatureForCombat filter = new FilterCreatureForCombat(); + filter.getName().add(command); + Permanent attacker = findPermanent(filter, playerId, game); + if (attacker != null && attacker.canAttack(game)) { + this.declareAttacker(attacker.getId(), opponentId, game); + } + } + } + } + + @Override + public void selectBlockers(Game game) { + UUID opponentId = game.getOpponents(playerId).iterator().next(); + for (PlayerAction action: actions) { + if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("block:")) { + String command = action.getAction(); + command = command.substring(command.indexOf("block:") + 6); + String[] groups = command.split(";"); + FilterCreatureForCombat filterBlocker = new FilterCreatureForCombat(); + filterBlocker.getName().add(groups[0]); + Permanent blocker = findPermanent(filterBlocker, playerId, game); + if (blocker != null) { + FilterAttackingCreature filterAttacker = new FilterAttackingCreature(); + filterAttacker.getName().add(groups[1]); + Permanent attacker = findPermanent(filterAttacker, opponentId, game); + if (attacker != null) { + this.declareBlocker(blocker.getId(), attacker.getId(), game); + } + } + } + } + } + + protected Permanent findPermanent(FilterPermanent filter, UUID controllerId, Game game) { + List permanents = game.getBattlefield().getAllActivePermanents(filter, controllerId); + if (permanents.size() > 0) + return permanents.get(0); + return null; + } + + private void addTargets(Ability ability, String[] groups, Game game) { + for (int i = 1; i < groups.length; i++) { + String group = groups[i]; + String target; + if (group.startsWith("targetPlayer=")) { + target = group.substring(group.indexOf("targetPlayer=") + 13); + for (Player player: game.getPlayers().values()) { + if (player.getName().equals(target)) { + ability.getTargets().get(0).addTarget(player.getId(), ability, game); + break; + } + } + } + else if (group.startsWith("target=")) { + target = group.substring(group.indexOf("target=") + 7); + for (Permanent permanent: game.getBattlefield().getAllActivePermanents()) { + if (permanent.getName().equals(target)) { + ability.getTargets().get(0).addTarget(permanent.getId(), ability, game); + break; + } + } + } + } + } + +} 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 index 05bdded6854..511249f2167 100644 --- 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 @@ -28,7 +28,8 @@ public abstract class CardTestBase extends CardTestAPIImpl { protected enum AIType { MinimaxHybrid, - MAD + MAD, + MonteCarlo } protected enum ExpectedType { @@ -56,6 +57,22 @@ public abstract class CardTestBase extends CardTestAPIImpl { this.aiTypeB = aiTypeB; } + protected Player createNewPlayer(String playerName, AIType aiType) { + Player player = null; + switch (aiType) { + case MinimaxHybrid: + player = createPlayer(playerName, "Computer - minimax hybrid"); + break; + case MAD: + player = createPlayer(playerName, "Computer - mad"); + break; + case MonteCarlo: + player = createPlayer(playerName, "Computer - monte carlo"); + break; + } + return player; + } + @Before public void reset() throws GameException, FileNotFoundException { if (currentGame != null) { @@ -66,9 +83,7 @@ public abstract class CardTestBase extends CardTestAPIImpl { Game game = new TwoPlayerDuel(Constants.MultiplayerAttackOption.LEFT, Constants.RangeOfInfluence.ALL); - playerA = aiTypeA.equals(CardTestBase.AIType.MinimaxHybrid) ? - createPlayer("PlayerA", "Computer - minimax hybrid") : - createPlayer("PlayerA", "Computer - mad"); + playerA = createNewPlayer("PlayerA", aiTypeA); playerA.setTestMode(true); logger.info("Loading deck..."); Deck deck = Deck.load(Sets.loadDeck("RB Aggro.dck")); @@ -79,9 +94,7 @@ public abstract class CardTestBase extends CardTestAPIImpl { game.addPlayer(playerA, deck); game.loadCards(deck.getCards(), playerA.getId()); - playerB = aiTypeB.equals(CardTestBase.AIType.MinimaxHybrid) ? - createPlayer("PlayerB", "Computer - minimax hybrid") : - createPlayer("PlayerB", "Computer - mad"); + playerB = createNewPlayer("PlayerB", aiTypeB); playerB.setTestMode(true); Deck deck2 = Deck.load(Sets.loadDeck("RB Aggro.dck")); if (deck2.getCards().size() < 40) { @@ -128,9 +141,7 @@ public abstract class CardTestBase extends CardTestAPIImpl { Game game = new TwoPlayerDuel(Constants.MultiplayerAttackOption.LEFT, Constants.RangeOfInfluence.ALL); - playerA = aiTypeA.equals(CardTestBase.AIType.MinimaxHybrid) ? - createPlayer("ComputerA", "Computer - minimax hybrid") : - createPlayer("ComputerA", "Computer - mad"); + playerA = createNewPlayer("ComputerA", aiTypeA); playerA.setTestMode(true); Deck deck = Deck.load(Sets.loadDeck("RB Aggro.dck")); @@ -141,9 +152,7 @@ public abstract class CardTestBase extends CardTestAPIImpl { game.addPlayer(playerA, deck); game.loadCards(deck.getCards(), playerA.getId()); - playerB = aiTypeB.equals(CardTestBase.AIType.MinimaxHybrid) ? - createPlayer("ComputerB", "Computer - minimax hybrid") : - createPlayer("ComputerB", "Computer - mad"); + playerB = createNewPlayer("ComputerB", aiTypeB); playerB.setTestMode(true); Deck deck2 = Deck.load(Sets.loadDeck("RB Aggro.dck")); if (deck2.getCards().size() < 40) { diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java new file mode 100644 index 00000000000..3dab993c95a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java @@ -0,0 +1,314 @@ +package org.mage.test.serverside.base; + +import java.io.File; +import java.io.FileNotFoundException; +import mage.Constants; +import mage.Constants.PhaseStep; +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.player.TestPlayer; +import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; + +/** + * Base class for testing single cards and effects. + * + * @author ayratn + */ +public abstract class CardTestPlayerBase extends CardTestPlayerAPIImpl { + + protected enum ExpectedType { + TURN_NUMBER, + RESULT, + LIFE, + BATTLEFIELD, + GRAVEYARD, + UNKNOWN + } + + public CardTestPlayerBase() { + } + + protected TestPlayer createNewPlayer(String playerName) { + return createPlayer(playerName); + } + + @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); + + playerA = createNewPlayer("PlayerA"); + playerA.setTestMode(true); + logger.info("Loading deck..."); + Deck deck = Deck.load(Sets.loadDeck("RB Aggro.dck")); + logger.info("Done!"); + if (deck.getCards().size() < 40) { + throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size()); + } + game.addPlayer(playerA, deck); + game.loadCards(deck.getCards(), playerA.getId()); + + playerB = createNewPlayer("PlayerB"); + playerB.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(playerB, deck2); + game.loadCards(deck2.getCards(), playerB.getId()); + activePlayer = playerA; + currentGame = game; + + stopOnTurn = 2; + stopAtStep = PhaseStep.UNTAP; + 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 { + 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); + + playerA = createNewPlayer("ComputerA"); + playerA.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(playerA, deck); + game.loadCards(deck.getCards(), playerA.getId()); + + playerB = createNewPlayer("ComputerB"); + playerB.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(playerB, deck2); + game.loadCards(deck2.getCards(), playerB.getId()); + + parseScenario(cardPath); + + activePlayer = playerA; + 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(playerA.getId(), commandsA); + currentGame.cheat(playerA.getId(), libraryCardsA, handCardsA, battlefieldCardsA, graveyardCardsA); + currentGame.cheat(playerB.getId(), commandsB); + currentGame.cheat(playerB.getId(), libraryCardsB, handCardsB, battlefieldCardsB, graveyardCardsB); + + boolean testMode = true; + long t1 = System.nanoTime(); + GameOptions gameOptions = new GameOptions(); + gameOptions.testMode = true; + gameOptions.stopOnTurn = stopOnTurn; + gameOptions.stopAtStep = stopAtStep; + 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(CardTestPlayerBase.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 CardTestPlayerBase.ExpectedType.TURN_NUMBER; + } + if (line.startsWith("result:")) { + return CardTestPlayerBase.ExpectedType.RESULT; + } + if (line.startsWith("life:")) { + return CardTestPlayerBase.ExpectedType.LIFE; + } + if (line.startsWith("battlefield:")) { + return CardTestPlayerBase.ExpectedType.BATTLEFIELD; + } + if (line.startsWith("graveyard:")) { + return CardTestPlayerBase.ExpectedType.GRAVEYARD; + } + return CardTestPlayerBase.ExpectedType.UNKNOWN; + } + + private void parseType(ExpectedType type, String line) { + if (type.equals(CardTestPlayerBase.ExpectedType.TURN_NUMBER)) { + int turn = getIntParam(line, 1); + Assert.assertEquals("Turn numbers are not equal", turn, currentGame.getTurnNum()); + return; + } + if (type.equals(CardTestPlayerBase.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(CardTestPlayerBase.ExpectedType.LIFE)) { + String player = getStringParam(line, 1); + int expected = getIntParam(line, 2); + if (player.equals("ComputerA")) { + int actual = currentGame.getPlayer(playerA.getId()).getLife(); + Assert.assertEquals("Life amounts are not equal", expected, actual); + } else if (player.equals("ComputerB")) { + int actual = currentGame.getPlayer(playerB.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(CardTestPlayerBase.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(playerA.getId()); + } else if (playerName.equals("ComputerB")) { + player = currentGame.getPlayer(playerB.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(CardTestPlayerBase.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(playerA.getId()); + } else if (playerName.equals("ComputerB")) { + player = currentGame.getPlayer(playerB.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 72caeacae81..19727975e18 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 @@ -113,9 +113,9 @@ public abstract class MageTestBase { for (Plugin plugin : config.getPlayerTypes()) { PlayerFactory.getInstance().addPlayerType(plugin.getName(), loadPlugin(plugin)); } - for (Plugin plugin : config.getDeckTypes()) { - DeckValidatorFactory.getInstance().addDeckType(plugin.getName(), loadPlugin(plugin)); - } +// for (Plugin plugin : config.getDeckTypes()) { +// DeckValidatorFactory.getInstance().addDeckType(plugin.getName(), loadPlugin(plugin)); +// } Copier.setLoader(classLoader); } @@ -296,6 +296,6 @@ public abstract class MageTestBase { } protected Player createPlayer(String name, String playerType) { - return PlayerFactory.getInstance().createPlayer(playerType, name, Constants.RangeOfInfluence.ALL, 10); + return PlayerFactory.getInstance().createPlayer(playerType, name, Constants.RangeOfInfluence.ALL, 5); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java new file mode 100644 index 00000000000..d4d299a7fac --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java @@ -0,0 +1,302 @@ +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; +import mage.server.tournament.TournamentFactory; +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 java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import mage.Constants.PhaseStep; +import org.mage.test.player.TestPlayer; + +/** + * Base class for all tests. + * + * @author ayratn + */ +public abstract class MageTestPlayerBase { + protected static Logger logger = Logger.getLogger(MageTestPlayerBase.class); + + public static PluginClassLoader classLoader = new PluginClassLoader(); + + private final static String pluginFolder = "plugins"; + + 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 TestPlayer playerA; + protected TestPlayer playerB; + + /** + * 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 PhaseStep stopAtStep = PhaseStep.UNTAP; + + 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); + logger.info("Starting MAGE tests"); + logger.info("Logging level: " + logger.getLevel()); + deleteSavedGames(); + ConfigSettings config = ConfigSettings.getInstance(); + for (GamePlugin plugin : config.getGameTypes()) { + GameFactory.getInstance().addGameType(plugin.getName(), loadGameType(plugin), loadPlugin(plugin)); + } +// for (GamePlugin plugin : config.getTournamentTypes()) { +// TournamentFactory.getInstance().addTournamentType(plugin.getName(), loadTournamentType(plugin), loadPlugin(plugin)); +// } +// for (Plugin plugin : config.getPlayerTypes()) { +// PlayerFactory.getInstance().addPlayerType(plugin.getName(), loadPlugin(plugin)); +// } +// for (Plugin plugin : config.getDeckTypes()) { +// DeckValidatorFactory.getInstance().addDeckType(plugin.getName(), loadPlugin(plugin)); +// } + Copier.setLoader(classLoader); + } + + private static Class loadPlugin(Plugin plugin) { + try { + classLoader.addURL(new File(pluginFolder + "/" + plugin.getJar()).toURI().toURL()); + logger.info("Loading plugin: " + plugin.getClassName()); + return Class.forName(plugin.getClassName(), true, classLoader); + } catch (ClassNotFoundException ex) { + logger.warn("Plugin not Found:" + plugin.getJar() + " - check plugin folder"); + } catch (Exception ex) { + logger.fatal("Error loading plugin " + plugin.getJar(), ex); + } + return null; + } + + private static MatchType loadGameType(GamePlugin plugin) { + try { + classLoader.addURL(new File(pluginFolder + "/" + plugin.getJar()).toURI().toURL()); + logger.info("Loading game type: " + plugin.getClassName()); + return (MatchType) Class.forName(plugin.getTypeName(), true, classLoader).newInstance(); + } catch (ClassNotFoundException ex) { + logger.warn("Game type not found:" + plugin.getJar() + " - check plugin folder"); + } catch (Exception ex) { + logger.fatal("Error loading game type " + plugin.getJar(), ex); + } + return null; + } + + private static TournamentType loadTournamentType(GamePlugin plugin) { + try { + classLoader.addURL(new File(pluginFolder + "/" + plugin.getJar()).toURI().toURL()); + logger.info("Loading tournament type: " + plugin.getClassName()); + return (TournamentType) Class.forName(plugin.getTypeName(), true, classLoader).newInstance(); + } catch (ClassNotFoundException ex) { + logger.warn("Tournament type not found:" + plugin.getJar() + " - check plugin folder"); + } catch (Exception ex) { + logger.fatal("Error loading game type " + plugin.getJar(), ex); + } + return null; + } + + private static void deleteSavedGames() { + File directory = new File("saved/"); + if (!directory.exists()) + directory.mkdirs(); + File[] files = directory.listFiles( + new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".game"); + } + } + ); + for (File file : files) { + 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 TestPlayer createPlayer(String name) { + return new TestPlayer(name, Constants.RangeOfInfluence.ALL); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java new file mode 100644 index 00000000000..4f98a12f9d0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -0,0 +1,405 @@ +package org.mage.test.serverside.base.impl; + +import mage.Constants; +import mage.abilities.Ability; +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.MageTestPlayerBase; + +import java.util.List; +import java.util.UUID; +import mage.Constants.PhaseStep; +import org.mage.test.player.TestPlayer; + +/** + * API for test initialization and asserting the test results. + * + * @author ayratn + */ +public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase 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, playerA, "Mountain", 5); + // hand:ComputerA:Mountain:4 + addCard(Constants.Zone.HAND, playerA, "Mountain", 5); + // library:ComputerA:clear:0 + removeAllCardsFromLibrary(playerA); + // library:ComputerA:Mountain:10 + addCard(Constants.Zone.LIBRARY, playerA, "Mountain", 10); + + // *** ComputerB *** + // battlefield:ComputerB:Plains:2 + addCard(Constants.Zone.BATTLEFIELD, playerB, "Plains", 2); + // hand:ComputerB:Plains:2 + addCard(Constants.Zone.HAND, playerB, "Plains", 2); + // library:ComputerB:clear:0 + removeAllCardsFromLibrary(playerB); + // library:ComputerB:Plains:10 + addCard(Constants.Zone.LIBRARY, playerB, "Plains", 10); + } + + /** + * Default game initialization params for white player (that plays with Plains) + */ + public void useWhiteDefault() { + // *** ComputerA *** + addCard(Constants.Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Constants.Zone.HAND, playerA, "Plains", 5); + removeAllCardsFromLibrary(playerA); + addCard(Constants.Zone.LIBRARY, playerA, "Plains", 10); + + // *** ComputerB *** + addCard(Constants.Zone.BATTLEFIELD, playerB, "Plains", 2); + addCard(Constants.Zone.HAND, playerB, "Plains", 2); + removeAllCardsFromLibrary(playerB); + addCard(Constants.Zone.LIBRARY, playerB, "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(playerA)) { + commandsA.put(Constants.Zone.LIBRARY, "clear"); + } else if (player.equals(playerB)) { + 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 playerA or playerB. + * @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 playerA or playerB. + * @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 playerA or playerB. + * @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); + if (card == null) { + throw new IllegalArgumentException("[TEST] Couldn't find a card: " + cardName); + } + PermanentCard p = new PermanentCard(card, null); + p.setTapped(tapped); + if (player.equals(playerA)) { + battlefieldCardsA.add(p); + } else if (player.equals(playerB)) { + 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(playerA)) { + 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(playerB)) { + 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(playerA)) { + commandsA.put(Constants.Zone.OUTSIDE, "life:" + String.valueOf(life)); + } else if (player.equals(playerB)) { + 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); + stopAtStep = PhaseStep.UNTAP; + } + + /** + * Define turn number and step to stop the game on. + */ + public void setStopAt(int turn, PhaseStep step) { + stopOnTurn = turn == -1 ? null : Integer.valueOf(turn); + stopAtStep = step; + } + + /** + * 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(playerA)) { + GameResult actual = CardTestAPI.GameResult.DRAW; + if (currentGame.getWinner().equals("Player PlayerA is the winner")) { + actual = CardTestAPI.GameResult.WON; + } else if (currentGame.getWinner().equals("Player PlayerB is the winner")) { + actual = CardTestAPI.GameResult.LOST; + } + Assert.assertEquals("Game results are not equal", result, actual); + } else if (player.equals(playerB)) { + GameResult actual = CardTestAPI.GameResult.DRAW; + if (currentGame.getWinner().equals("Player PlayerB is the winner")) { + actual = CardTestAPI.GameResult.WON; + } else if (currentGame.getWinner().equals("Player PlayerA 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); + } + } + + /** + * {@inheritDoc} + */ + public void assertAbilities(Player player, String cardName, List abilities) + throws AssertionError { + int count = 0; + Permanent found = null; + for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(player.getId())) { + if (permanent.getName().equals(cardName)) { + found = permanent; + } + } + + Assert.assertNotNull("There is no such permanent under player's control, player=" + player.getName() + + ", cardName=" + cardName, found); + + Assert.assertTrue("There is more than one such permanent under player's control, player=" + player.getName() + + ", cardName=" + cardName, count == 1); + + for (Ability ability : abilities) { + Assert.assertTrue("No such ability=" + ability.toString() + ", player=" + player.getName() + + ", cardName" + cardName, found.getAbilities().contains(ability)); + } + } + + /** + * 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); + } + + public Permanent getPermanent(String cardName, UUID controller) { + Permanent permanent0 = null; + int count = 0; + for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) { + if (permanent.getControllerId().equals(controller)) { + if (permanent.getName().equals(cardName)) { + permanent0 = permanent; + count++; + } + } + } + Assert.assertNotNull("Couldn't find a card with specified name: " + cardName, permanent0); + Assert.assertEquals("More than one permanent was found: " + cardName + "(" + count + ")", 1, count); + return permanent0; + } + + public void playLand(int turnNum, PhaseStep step, TestPlayer player, String cardName) { + player.addAction(turnNum, step, "activate:Cast " + cardName); + } + + public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName) { + player.addAction(turnNum, step, "activate:Cast " + cardName); + } + + public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, Player target) { + player.addAction(turnNum, step, "activate:Cast " + cardName + ";targetPlayer=" + target.getName()); + } + + public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName) { + player.addAction(turnNum, step, "activate:Cast " + cardName + ";target=" + targetName); + } + + public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, Player target) { + player.addAction(turnNum, step, "activate:" + ability + ";target=" + target.getName()); + } + + public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName) { + player.addAction(turnNum, step, "activate:" + ability + ";target=" + targetName); + } + + public void useAbility(int turnNum, PhaseStep step, TestPlayer player, String cardName) { + } + + public void attack(int turnNum, TestPlayer player, String attacker) { + player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:"+attacker); + } + + public void block(int turnNum, TestPlayer player, String blocker, String attacker) { + player.addAction(turnNum, PhaseStep.DECLARE_BLOCKERS, "block:"+blocker+";"+attacker); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/cards/abilities/ProtectionFromColorTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/cards/abilities/ProtectionFromColorTest.java index 60261f53991..8896b80fdd8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/cards/abilities/ProtectionFromColorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/cards/abilities/ProtectionFromColorTest.java @@ -1,56 +1,42 @@ package org.mage.test.serverside.cards.abilities; import mage.Constants; +import mage.Constants.PhaseStep; import org.junit.Test; import org.mage.test.serverside.base.CardTestAPI; -import org.mage.test.serverside.base.CardTestBase; +import org.mage.test.serverside.base.CardTestPlayerBase; /** * @author ayratn */ -public class ProtectionFromColorTest extends CardTestBase { +public class ProtectionFromColorTest extends CardTestPlayerBase { @Test public void testAgainstAbilityInTheStack() { - useRedDefault(); addCard(Constants.Zone.BATTLEFIELD, playerA, "Royal Assassin"); // tapped White Knight with Protection from Black addCard(Constants.Zone.BATTLEFIELD, playerB, "White Knight", 1, true); - addCard(Constants.Zone.BATTLEFIELD, playerB, "Runeclaw Bear", 1, true); - // one not tapped White Knight to prevent AI from attacking - addCard(Constants.Zone.BATTLEFIELD, playerB, "White Knight", 1, false); - setStopOnTurn(2); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Destroy target tapped creature. ", "White Knight"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); - assertTurn(2); - assertResult(playerA, CardTestAPI.GameResult.DRAW); - assertLife(playerA, 20); - assertLife(playerB, 20); - // no one should be destroyed - assertPermanentCount(playerB, "White Knight", 2); - assertPermanentCount(playerB, "Runeclaw Bear", 0); + assertPermanentCount(playerB, "White Knight", 1); } @Test public void testAgainstAbilityInTheStackNoProtection() { - useRedDefault(); addCard(Constants.Zone.BATTLEFIELD, playerA, "Royal Assassin"); addCard(Constants.Zone.BATTLEFIELD, playerB, "Runeclaw Bear", 1, true); - addCard(Constants.Zone.BATTLEFIELD, playerB, "Runeclaw Bear", 1, false); - setStopOnTurn(2); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Destroy target tapped creature. ", "Runeclaw Bear"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); - assertTurn(2); - assertResult(playerA, CardTestAPI.GameResult.DRAW); - assertLife(playerA, 20); - assertLife(playerB, 20); - // One should have beendestroyed by Royal Assassin - assertPermanentCount(playerB, "Runeclaw Bear", 1); + assertPermanentCount(playerB, "Runeclaw Bear", 0); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/cards/abilities/ProtectionFromTypeTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/cards/abilities/ProtectionFromTypeTest.java index a3fc1bd0141..37f777915a1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/cards/abilities/ProtectionFromTypeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/cards/abilities/ProtectionFromTypeTest.java @@ -3,27 +3,24 @@ package org.mage.test.serverside.cards.abilities; import mage.Constants; import org.junit.Test; import org.mage.test.serverside.base.CardTestBase; +import org.mage.test.serverside.base.CardTestPlayerBase; /** * @author ayratn */ -public class ProtectionFromTypeTest extends CardTestBase { +public class ProtectionFromTypeTest extends CardTestPlayerBase { @Test public void testProtectionFromArtifacts() { useRedDefault(); - addCard(Constants.Zone.HAND, playerA, "Trigon of Corruption"); + addCard(Constants.Zone.BATTLEFIELD, playerA, "Trigon of Corruption"); addCard(Constants.Zone.BATTLEFIELD, playerB, "Tel-Jilad Fallen"); - setStopOnTurn(2); + activateAbility(1, Constants.PhaseStep.PRECOMBAT_MAIN, playerA, "{2},Remove a Charge counter from {this}, {T}: put a -1/-1 counter on target creature. ", "Tel-Jilad Fallen"); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); - assertTurn(2); - assertResult(playerA, GameResult.DRAW); - assertLife(playerA, 20); - assertLife(playerB, 20); - // no one should be destroyed assertPermanentCount(playerB, "Tel-Jilad Fallen", 1); } @@ -31,19 +28,15 @@ public class ProtectionFromTypeTest extends CardTestBase { @Test public void testNoProtection() { useRedDefault(); - addCard(Constants.Zone.HAND, playerA, "Trigon of Corruption"); + addCard(Constants.Zone.BATTLEFIELD, playerA, "Trigon of Corruption"); addCard(Constants.Zone.BATTLEFIELD, playerB, "Coral Merfolk"); - setStopOnTurn(2); + activateAbility(1, Constants.PhaseStep.PRECOMBAT_MAIN, playerA, "{2},Remove a Charge counter from {this}, {T}: put a -1/-1 counter on target creature. ", "Coral Merfolk"); + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); execute(); - assertTurn(2); - assertResult(playerA, GameResult.DRAW); - assertLife(playerA, 20); - assertLife(playerB, 20); - - // no one should be destroyed + // Coral Merfolk should be destroyed assertPermanentCount(playerB, "Coral Merfolk", 0); } } diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index b4ea9c7884a..b968248ae8c 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -1311,11 +1311,14 @@ public abstract class GameImpl> implements Game, Serializa player.getGraveyard().add(card); } for (PermanentCard card : battlefield) { - card.setOwnerId(ownerId); - PermanentCard permanent = new PermanentCard(card, ownerId); - permanent.setTapped(card.isTapped()); - ((PermanentImpl)permanent).removeSummoningSickness(); - getBattlefield().addPermanent(permanent); + setZone(card.getId(), Zone.BATTLEFIELD); + card.setOwnerId(ownerId); + PermanentCard permanent = new PermanentCard(card, ownerId); + getBattlefield().addPermanent(permanent); + permanent.entersBattlefield(permanent.getId(), this); + ((PermanentImpl)permanent).removeSummoningSickness(); + if (card.isTapped()) + permanent.setTapped(true); } applyEffects(); }