tests: added additional tests for Dryad Militant card and Madness abilities, added docs;

This commit is contained in:
Oleg Agafonov 2024-05-12 12:33:48 +04:00
parent 8c0ed8a749
commit d28b9e6d05
6 changed files with 283 additions and 43 deletions

View file

@ -59,7 +59,7 @@ public class UnboundFlourishingTest extends CardTestPlayerBase {
}
@Test
public void test_OnCastInstantOrSourcery_MustCopy() {
public void test_OnCastInstantOrSorcery_MustCopy() {
addCard(Zone.BATTLEFIELD, playerA, "Unbound Flourishing", 1);
//
// Banefire deals X damage to any target.

View file

@ -1,78 +1,287 @@
package org.mage.test.cards.replacement;
import mage.cards.Card;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.watchers.common.CardsExiledThisTurnWatcher;
import mage.watchers.common.CardsPutIntoGraveyardWatcher;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
/**
* Dryad Militant
* Creature - Dryad Soldier 1/1 {G/W}
* If an instant or sorcery card would be put into a graveyard from anywhere, exile it instead.
*
* @author LevelX2
* Dryad Militant oracle rules:
* <p>
* If an instant or sorcery spell destroys Dryad Militant directly (like Murder does), that instant or sorcery
* card will be put into its owners graveyard. However, if an instant or sorcery card deals lethal damage to
* Dryad Militant, Dryad Militant will remain on the battlefield until the next time state-based actions are
* checked, which is after the instant or sorcery finishes resolving.
* The instant or sorcery will be exiled.
* (2012-10-01) - supported, has tests
* <p>
* If an instant or sorcery card is discarded while Dryad Militant is on the battlefield, abilities that
* function when a card is discarded (such as madness) still work, even though that card never reaches a
* graveyard. In addition, spells or abilities that check the characteristics of a discarded card (such as
* Chandra Ablazes first ability) can find that card in exile.
* (2012-10-01) - supported, has tests
*
* @author LevelX2, JayDi85
*/
public class DryadMilitantTest extends CardTestPlayerBase {
/**
* Tests that an instant or sorcery card is moved to exile
*/
@Test
public void testNormalCase() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.HAND, playerA, "Lightning Bolt");
public void test_OnResolve_MustWork() {
// If an instant or sorcery card would be put into a graveyard from anywhere, exile it instead.
addCard(Zone.BATTLEFIELD, playerB, "Dryad Militant");
//
addCard(Zone.HAND, playerA, "Lightning Bolt");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerB, 17);
assertExileCount("Lightning Bolt", 1);
}
}
/**
* Tests if Dryad Militant dies by damage spell, the
* spell gets exiled
* spell gets exiled (see oracle rules)
*/
@Test
public void testDiesByDamage() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.HAND, playerA, "Lightning Bolt");
public void test_DiesByDamage_MustWork() {
// If an instant or sorcery card would be put into a graveyard from anywhere, exile it instead.
addCard(Zone.BATTLEFIELD, playerB, "Dryad Militant");
//
addCard(Zone.HAND, playerA, "Lightning Bolt");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Dryad Militant");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerB, 20);
assertExileCount("Lightning Bolt", 1);
}
}
/**
* Tests if Dryad Militant dies by destroy spell, the
* spell don't get exiled
* spell don't get exiled (see oracle rules)
*/
@Test
public void testDiesByDestroy() {
public void test_DiesByDestroy_MustNotWork() {
// If an instant or sorcery card would be put into a graveyard from anywhere, exile it instead.
addCard(Zone.BATTLEFIELD, playerB, "Dryad Militant");
//
// Destroy target creature. It can't be regenerated.
addCard(Zone.HAND, playerA, "Terminate"); // {B}{R}
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
addCard(Zone.HAND, playerA, "Terminate");
addCard(Zone.BATTLEFIELD, playerB, "Dryad Militant");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Terminate", "Dryad Militant");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerB, 20);
assertLife(playerB, 20);
assertHandCount(playerA, "Terminate", 0);
assertGraveyardCount(playerA, "Terminate", 1);
}
}
}
private void prepareGraveyardAndExileWatchers() {
currentGame.getState().addWatcher(new CardsPutIntoGraveyardWatcher());
currentGame.getState().addWatcher(new CardsExiledThisTurnWatcher());
}
private void assertCardsList(String info, Collection<Card> currentList, Collection<String> needList) {
String current = currentList.stream().map(Card::getName).sorted().collect(Collectors.joining("; "));
String need = needList.stream().sorted().collect(Collectors.joining("; "));
Assert.assertEquals(info, need, current);
}
private void assertReachesGraveyard(Collection<String> needList) {
CardsPutIntoGraveyardWatcher graveyardWatcher = currentGame.getState().getWatcher(CardsPutIntoGraveyardWatcher.class);
assertCardsList(
"graveyard must reaches " + needList.size() + " cards this turn",
graveyardWatcher.getCardsPutIntoGraveyardFromAnywhere(currentGame),
needList
);
}
private void assertReachesExile(Collection<String> needList) {
CardsExiledThisTurnWatcher exileWatcher = currentGame.getState().getWatcher(CardsExiledThisTurnWatcher.class);
assertCardsList(
"exile must reaches " + needList.size() + " cards this turn",
exileWatcher.getCardsExiledThisTurn(currentGame),
needList
);
}
@Test
public void test_MadnessCreature_OnDiscard() {
prepareGraveyardAndExileWatchers();
// Madness {B}{B} (If you discard this card, discard it into exile. When you do, cast it for its madness cost
// or put it into your graveyard.)
addCard(Zone.HAND, playerA, "Gorgon Recluse"); // {3}{B}{B}
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
//
// Target player discards a card.
addCard(Zone.HAND, playerA, "Raven's Crime"); // {B}
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
// discard
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raven's Crime", playerA);
setChoice(playerA, true); // use madness
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
// raven on cast go to grave
// gorgon on madness go to exile and cast instead grave
assertReachesGraveyard(Arrays.asList("Raven's Crime"));
assertReachesExile(Arrays.asList("Gorgon Recluse")); // was cast from exile
}
@Test
public void test_MadnessSorcery_OnDiscard() {
prepareGraveyardAndExileWatchers();
// Alchemists Greeting deals 4 damage to target creature.
// Madness {1}{R} (If you discard this card, discard it into exile. When you do, cast it for its madness
// cost or put it into your graveyard.)
addCard(Zone.HAND, playerA, "Alchemist's Greeting"); // {4}{R}
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1);
//
// Target player discards a card.
addCard(Zone.HAND, playerA, "Raven's Crime"); // {B}
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
// discard
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raven's Crime", playerA);
setChoice(playerA, true); // use madness
addTarget(playerA, "Grizzly Bears"); // target of madness card
showGraveyard("after grave", 1, PhaseStep.END_TURN, playerA);
showExile("after exile", 1, PhaseStep.END_TURN, playerA);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Grizzly Bears", 1);
assertGraveyardCount(playerA, "Alchemist's Greeting", 1); // go to grave after madness cast resolve
// raven on cast go to grave
// alchemist on discard with madness go to exile and grave
// bears on damage go to grave
assertReachesGraveyard(Arrays.asList("Raven's Crime", "Alchemist's Greeting", "Grizzly Bears"));
assertReachesExile(Arrays.asList("Alchemist's Greeting"));
}
/**
* Make sure no exile zone reaches due replacement effects (see oracle rules)
*/
@Test
public void test_MadnessSorcery_OnDiscardWithDryadMilitant_BySpell() {
prepareGraveyardAndExileWatchers();
// checking rule:
// If an instant or sorcery card is discarded while Dryad Militant is on the battlefield, abilities that
// function when a card is discarded (such as madness) still work, even though that card never reaches a
// graveyard.
// If an instant or sorcery card would be put into a graveyard from anywhere, exile it instead.
addCard(Zone.BATTLEFIELD, playerB, "Dryad Militant");
//
// Alchemists Greeting deals 4 damage to target creature.
// Madness {1}{R} (If you discard this card, discard it into exile. When you do, cast it for its madness
// cost or put it into your graveyard.)
addCard(Zone.HAND, playerA, "Alchemist's Greeting"); // {4}{R}
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1);
//
// Target player discards a card.
addCard(Zone.HAND, playerA, "Raven's Crime"); // {B}
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
// discard
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raven's Crime", playerA);
setChoice(playerA, "Alchemist's Greeting"); // choose replacement effects (madness first)
setChoice(playerA, true); // use madness
addTarget(playerA, "Grizzly Bears"); // target of madness card
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Grizzly Bears", 1);
assertExileCount(playerA, "Alchemist's Greeting", 1); // discard -> exile and cast madness -> exile instead grave
// raven on cast go to exile instead grave due dryad effect
// alchemist on discard with madness go to exile, after cast go to exile again instead grave due dryad effect
// bears on damage go to grave (ignored by dryad effect due it's not a instant/sorcery)
assertReachesGraveyard(Arrays.asList("Grizzly Bears"));
assertReachesExile(Arrays.asList("Raven's Crime", "Alchemist's Greeting", "Alchemist's Greeting"));
}
@Test
public void test_MadnessSorcery_OnDiscardWithDryadMilitant_ByChandraAblaze() {
prepareGraveyardAndExileWatchers();
// checking rule:
// In addition, spells or abilities that check the characteristics of a discarded card (such as
// Chandra Ablazes first ability) can find that card in exile.
// If an instant or sorcery card would be put into a graveyard from anywhere, exile it instead.
addCard(Zone.BATTLEFIELD, playerB, "Dryad Militant");
//
// Alchemists Greeting deals 4 damage to target creature.
// Madness {1}{R} (If you discard this card, discard it into exile. When you do, cast it for its madness
// cost or put it into your graveyard.)
addCard(Zone.HAND, playerA, "Alchemist's Greeting"); // {4}{R}
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1);
//
// +1: Discard a card. If a red card is discarded this way, Chandra Ablaze deals 4 damage to any target.
addCard(Zone.BATTLEFIELD, playerA, "Chandra Ablaze");
// discard
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1");
addTarget(playerA, playerB); // x4 damage by chandra
setChoice(playerA, "Alchemist's Greeting"); // discard by chandra
setChoice(playerA, "Alchemist's Greeting"); // choose replacement effects (madness first)
setChoice(playerA, true); // use madness
addTarget(playerA, "Grizzly Bears"); // target of madness card
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Grizzly Bears", 1);
assertExileCount(playerA, "Alchemist's Greeting", 1); // discard -> exile and cast madness -> exile instead grave
assertLife(playerB, 20 - 4); // by chandra
// alchemist on discard with madness go to exile, after cast go to exile again instead grave due dryad effect
// bears on damage go to grave (ignored by dryad effect due it's not a instant/sorcery)
assertReachesGraveyard(Arrays.asList("Grizzly Bears"));
assertReachesExile(Arrays.asList("Alchemist's Greeting", "Alchemist's Greeting"));
}
}