Many more tests and fixes
All checks were successful
Build / Build (push) Successful in 3m12s

This commit is contained in:
2026-08-27 17:11:12 +02:00
parent 32b3ef9c4a
commit d2a82b5fe3
204 changed files with 20255 additions and 242 deletions

View File

@@ -0,0 +1,254 @@
using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.Models.Choices;
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Plugin.Gen7.Scripts.Moves;
using PkmnLib.Plugin.Gen7.Scripts.MoveVolatile;
using PkmnLib.Static.Moves;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
/// <summary>
/// Tests for the <see cref="FirePledge"/> move script.
/// Bulbapedia: "When two allies attempt to use Fire Pledge and either Water Pledge or Grass Pledge on the
/// same turn, the ally moving first will not itself use a move, but instead the ally moving second will use
/// a combined attack with a power of 150 and an additional effect immediately after it".
/// </summary>
public class FirePledgeTests
{
/// <summary>
/// Creates a move choice for a Pokémon on the given side, using a move with the given name.
/// The choice gets a real <see cref="ScriptSet"/> as its volatile set, so combination markers can be
/// added to and read from it.
/// </summary>
private static IMoveChoice CreateQueuedChoice(string moveName, byte sideIndex)
{
var choice = Substitute.For<IMoveChoice>();
choice.GetScripts().Returns(_ => new ScriptIterator(new List<IEnumerable<ScriptContainer>>()));
var pokemon = Substitute.For<IPokemon>();
var battleData = Substitute.For<IPokemonBattleData>();
battleData.SideIndex.Returns(sideIndex);
pokemon.BattleData.Returns(battleData);
choice.User.Returns(pokemon);
var moveData = Substitute.For<IMoveData>();
moveData.Name.Returns(new StringKey(moveName));
var learnedMove = Substitute.For<ILearnedMove>();
learnedMove.MoveData.Returns(moveData);
choice.ChosenMove.Returns(learnedMove);
var choiceVolatile = new ScriptSet(choice);
choice.Volatile.Returns(choiceVolatile);
return choice;
}
/// <summary>
/// Creates a fully mocked test setup for Fire Pledge tests. The user is on side 0.
/// </summary>
private static (FirePledge script, IExecutingMove move, IMoveChoice ownChoice) CreateTestSetup(
BattleChoiceQueue? queue)
{
var script = new FirePledge();
var move = Substitute.For<IExecutingMove>();
var battle = Substitute.For<IBattle>();
battle.ChoiceQueue.Returns(queue);
move.Battle.Returns(battle);
var user = Substitute.For<IPokemon>();
var userBattleData = Substitute.For<IPokemonBattleData>();
userBattleData.SideIndex.Returns((byte)0);
user.BattleData.Returns(userBattleData);
move.User.Returns(user);
var ownChoice = CreateQueuedChoice("fire_pledge", 0);
move.MoveChoice.Returns(ownChoice);
return (script, move, ownChoice);
}
/// <summary>
/// Bulbapedia: "the ally moving first will not itself use a move".
/// An ally on the same side still has Water Pledge queued, so this Fire Pledge is stopped.
/// </summary>
[Test]
public async Task StopBeforeMove_AllyWaterPledgeQueued_StopsMove()
{
// Arrange
var allyChoice = CreateQueuedChoice("water_pledge", 0);
var (script, move, _) = CreateTestSetup(new BattleChoiceQueue([allyChoice]));
var stop = false;
// Act
script.StopBeforeMove(move, ref stop);
// Assert
await Assert.That(stop).IsTrue();
}
/// <summary>
/// Bulbapedia: "If Fire Pledge and Water Pledge are used in the same turn, the ally moving second will
/// use Water Pledge with a power of 150, and create a rainbow on the user's side of the field for four
/// turns."
/// The queued ally choice is marked with the <see cref="FireWaterPledgeMove"/> volatile that implements
/// the combined move.
/// </summary>
[Test]
public async Task StopBeforeMove_AllyWaterPledgeQueued_MarksAllyChoiceAsCombinedFireWaterPledge()
{
// Arrange
var allyChoice = CreateQueuedChoice("water_pledge", 0);
var (script, move, _) = CreateTestSetup(new BattleChoiceQueue([allyChoice]));
var stop = false;
// Act
script.StopBeforeMove(move, ref stop);
// Assert
await Assert.That(allyChoice.Volatile.Contains<FireWaterPledgeMove>()).IsTrue();
}
/// <summary>
/// Bulbapedia: "the ally moving first will not itself use a move".
/// An ally on the same side still has Grass Pledge queued, so this Fire Pledge is stopped.
/// </summary>
[Test]
public async Task StopBeforeMove_AllyGrassPledgeQueued_StopsMove()
{
// Arrange
var allyChoice = CreateQueuedChoice("grass_pledge", 0);
var (script, move, _) = CreateTestSetup(new BattleChoiceQueue([allyChoice]));
var stop = false;
// Act
script.StopBeforeMove(move, ref stop);
// Assert
await Assert.That(stop).IsTrue();
}
/// <summary>
/// Bulbapedia: "If Fire Pledge and Grass Pledge are used in the same turn, the ally moving second will
/// use Fire Pledge with a power of 150, and create a sea of fire on the target's side of the field for
/// four turns."
/// The queued ally choice is marked with the <see cref="FireGrassPledgeMove"/> volatile that implements
/// the combined move.
/// </summary>
[Test]
public async Task StopBeforeMove_AllyGrassPledgeQueued_MarksAllyChoiceAsCombinedFireGrassPledge()
{
// Arrange
var allyChoice = CreateQueuedChoice("grass_pledge", 0);
var (script, move, _) = CreateTestSetup(new BattleChoiceQueue([allyChoice]));
var stop = false;
// Act
script.StopBeforeMove(move, ref stop);
// Assert
await Assert.That(allyChoice.Volatile.Contains<FireGrassPledgeMove>()).IsTrue();
}
/// <summary>
/// Bulbapedia: the combination only happens when allies use "Fire Pledge and either Water Pledge or
/// Grass Pledge on the same turn".
/// No Pledge move is queued by an ally, so Fire Pledge executes normally.
/// </summary>
[Test]
public async Task StopBeforeMove_NoPledgeMoveQueued_DoesNotStop()
{
// Arrange
var allyChoice = CreateQueuedChoice("tackle", 0);
var (script, move, _) = CreateTestSetup(new BattleChoiceQueue([allyChoice]));
var stop = false;
// Act
script.StopBeforeMove(move, ref stop);
// Assert
await Assert.That(stop).IsFalse();
}
/// <summary>
/// Bulbapedia: "When two allies attempt to use Fire Pledge and either Water Pledge or Grass Pledge on
/// the same turn" — a Pledge move queued by an opponent does not combine.
/// </summary>
[Test]
public async Task StopBeforeMove_OpponentWaterPledgeQueued_DoesNotStop()
{
// Arrange
var opponentChoice = CreateQueuedChoice("water_pledge", 1);
var (script, move, _) = CreateTestSetup(new BattleChoiceQueue([opponentChoice]));
var stop = false;
// Act
script.StopBeforeMove(move, ref stop);
// Assert
await Assert.That(stop).IsFalse();
await Assert.That(opponentChoice.Volatile.Contains<FireWaterPledgeMove>()).IsFalse();
}
/// <summary>
/// Bulbapedia: "the ally moving second will use Fire Pledge with a power of 150".
/// When this Fire Pledge choice is itself the combined move of Grass Pledge and Fire Pledge (marked
/// with <see cref="FireGrassPledgeMove"/>), it must execute instead of deferring again.
/// </summary>
[Test]
public async Task StopBeforeMove_ChoiceIsCombinedFireGrassPledge_DoesNotStop()
{
// Arrange
var allyChoice = CreateQueuedChoice("grass_pledge", 0);
var (script, move, ownChoice) = CreateTestSetup(new BattleChoiceQueue([allyChoice]));
ownChoice.Volatile.Add(new FireGrassPledgeMove());
var stop = false;
// Act
script.StopBeforeMove(move, ref stop);
// Assert
await Assert.That(stop).IsFalse();
await Assert.That(allyChoice.Volatile.Contains<FireGrassPledgeMove>()).IsFalse();
}
/// <summary>
/// Bulbapedia: "the ally moving second will use Water Pledge with a power of 150".
/// When this choice is the combined move of Fire Pledge and Water Pledge (marked with
/// <see cref="FireWaterPledgeMove"/>), it must execute instead of deferring again.
/// </summary>
[Test]
public async Task StopBeforeMove_ChoiceIsCombinedFireWaterPledge_DoesNotStop()
{
// Arrange
var allyChoice = CreateQueuedChoice("water_pledge", 0);
var (script, move, ownChoice) = CreateTestSetup(new BattleChoiceQueue([allyChoice]));
ownChoice.Volatile.Add(new FireWaterPledgeMove());
var stop = false;
// Act
script.StopBeforeMove(move, ref stop);
// Assert
await Assert.That(stop).IsFalse();
await Assert.That(allyChoice.Volatile.Contains<FireWaterPledgeMove>()).IsFalse();
}
/// <summary>
/// Technical test: when the battle has no active choice queue, the move executes normally without
/// throwing.
/// </summary>
[Test]
public async Task StopBeforeMove_NullChoiceQueue_DoesNotStop()
{
// Arrange
var (script, move, _) = CreateTestSetup(null);
var stop = false;
// Act
script.StopBeforeMove(move, ref stop);
// Assert
await Assert.That(stop).IsFalse();
}
}