More unit tests, fixes
This commit is contained in:
230
Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Moves/EncoreTests.cs
Normal file
230
Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Moves/EncoreTests.cs
Normal file
@@ -0,0 +1,230 @@
|
||||
using PkmnLib.Dynamic.Libraries;
|
||||
using PkmnLib.Dynamic.Models;
|
||||
using PkmnLib.Dynamic.Models.Choices;
|
||||
using PkmnLib.Dynamic.ScriptHandling;
|
||||
using PkmnLib.Dynamic.ScriptHandling.Registry;
|
||||
using PkmnLib.Plugin.Gen7.Common;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||
using PkmnLib.Static.Moves;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for the <see cref="Encore"/> move script and its <see cref="EncoreEffect"/>.
|
||||
/// Gen VII Bulbapedia behavior: "Encore temporarily prevents the target from using any move except the
|
||||
/// last used move." It fails if the target hasn't used a move yet or its last move was Struggle, and
|
||||
/// from Generation V onward the duration is "exactly 3 turns".
|
||||
/// </summary>
|
||||
public class EncoreTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a mocked move choice whose chosen move has the given name, optionally carrying the
|
||||
/// <see cref="MoveFlags.CantRepeat"/> flag.
|
||||
/// </summary>
|
||||
private static IMoveChoice CreateMoveChoice(string moveName, bool cantRepeat = false)
|
||||
{
|
||||
var moveData = Substitute.For<IMoveData>();
|
||||
moveData.Name.Returns(new StringKey(moveName));
|
||||
moveData.HasFlag(MoveFlags.CantRepeat).Returns(cantRepeat);
|
||||
var learnedMove = Substitute.For<ILearnedMove>();
|
||||
learnedMove.MoveData.Returns(moveData);
|
||||
var choice = Substitute.For<IMoveChoice>();
|
||||
choice.ChosenMove.Returns(learnedMove);
|
||||
return choice;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a fully mocked test setup where the target's volatile scripts are a real
|
||||
/// <see cref="ScriptSet"/> and its last used move is the given one (or none).
|
||||
/// </summary>
|
||||
private static (Encore script, IExecutingMove move, IPokemon target, IScriptSet targetVolatile, IHitData hitData)
|
||||
CreateTestSetup(string? lastUsedMove, bool lastMoveIsReplacement = false, bool lastMoveCantRepeat = false)
|
||||
{
|
||||
var script = new Encore();
|
||||
var move = Substitute.For<IExecutingMove>();
|
||||
|
||||
// Struggle is recognized through the misc library's replacement choice check, not by name.
|
||||
var miscLibrary = Substitute.For<IMiscLibrary>();
|
||||
var library = Substitute.For<IDynamicLibrary>();
|
||||
library.MiscLibrary.Returns(miscLibrary);
|
||||
var battle = Substitute.For<IBattle>();
|
||||
battle.Library.Returns(library);
|
||||
|
||||
var target = Substitute.For<IPokemon>();
|
||||
target.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
||||
IScriptSet targetVolatile = new ScriptSet(target);
|
||||
target.Volatile.Returns(targetVolatile);
|
||||
var targetBattleData = Substitute.For<IPokemonBattleData>();
|
||||
// Create the choice before the Returns call; configuring substitutes inside Returns is not allowed.
|
||||
var lastMoveChoice = lastUsedMove == null ? null : CreateMoveChoice(lastUsedMove, lastMoveCantRepeat);
|
||||
targetBattleData.LastMoveChoice.Returns(lastMoveChoice);
|
||||
targetBattleData.Battle.Returns(battle);
|
||||
target.BattleData.Returns(targetBattleData);
|
||||
if (lastMoveChoice != null)
|
||||
miscLibrary.IsReplacementChoice(lastMoveChoice).Returns(lastMoveIsReplacement);
|
||||
|
||||
var hitData = Substitute.For<IHitData>();
|
||||
move.GetHitData(target, 0).Returns(hitData);
|
||||
|
||||
return (script, move, target, targetVolatile, hitData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Encore temporarily prevents the target from using any move except the last used
|
||||
/// move." The target gains the <see cref="EncoreEffect"/> volatile script.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_TargetUsedMove_AddsEncoreEffectToTarget()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, target, targetVolatile, hitData) = CreateTestSetup("tackle");
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert
|
||||
await Assert.That(targetVolatile.Contains(ScriptUtils.ResolveName<EncoreEffect>())).IsTrue();
|
||||
hitData.DidNotReceive().Fail();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "It also fails if the foe hasn't used a move yet." Without a last move choice on the
|
||||
/// target, the hit fails and no encore is applied.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_TargetHasNotUsedMove_FailsHit()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, target, targetVolatile, hitData) = CreateTestSetup(null);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert
|
||||
hitData.Received(1).Fail();
|
||||
await Assert.That(targetVolatile.Contains(ScriptUtils.ResolveName<EncoreEffect>())).IsFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: Encore fails if the target's last move was Struggle. A last move that is a replacement
|
||||
/// choice (per <see cref="IMiscLibrary.IsReplacementChoice"/>) cannot be encored.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_TargetLastUsedStruggle_FailsHit()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, target, targetVolatile, hitData) = CreateTestSetup("struggle", true);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert
|
||||
hitData.Received(1).Fail();
|
||||
await Assert.That(targetVolatile.Contains(ScriptUtils.ResolveName<EncoreEffect>())).IsFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: Encore fails "if the opponent's last move was Transform, Mimic, Sketch, Mirror Move,
|
||||
/// Encore itself, Struggle, or a move with no PP remaining." These moves carry the
|
||||
/// <see cref="MoveFlags.CantRepeat"/> flag in the move data, which makes Encore fail.
|
||||
/// </summary>
|
||||
[Test, Arguments("encore"), Arguments("transform"), Arguments("mimic"), Arguments("sketch"),
|
||||
Arguments("mirror_move")]
|
||||
public async Task OnSecondaryEffect_TargetLastUsedUnrepeatableMove_FailsHit(string lastUsedMove)
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, target, targetVolatile, hitData) = CreateTestSetup(lastUsedMove, lastMoveCantRepeat: true);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert
|
||||
hitData.Received(1).Fail();
|
||||
await Assert.That(targetVolatile.Contains(ScriptUtils.ResolveName<EncoreEffect>())).IsFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: Encore fails "if the opponent's last move was Transform, Mimic, Sketch, Mirror Move,
|
||||
/// Encore itself" — integration check: the real Gen7 move data marks exactly these moves with the
|
||||
/// <see cref="MoveFlags.CantRepeat"/> flag.
|
||||
/// </summary>
|
||||
[Test, Arguments("encore"), Arguments("transform"), Arguments("mimic"), Arguments("sketch"),
|
||||
Arguments("mirror_move")]
|
||||
public async Task MoveData_UnrepeatableMove_HasCantRepeatFlag(string moveName)
|
||||
{
|
||||
// Arrange
|
||||
var library = LibraryHelpers.LoadLibrary();
|
||||
|
||||
// Act
|
||||
var found = library.StaticLibrary.Moves.TryGet(new StringKey(moveName), out var moveData);
|
||||
|
||||
// Assert
|
||||
await Assert.That(found).IsTrue();
|
||||
await Assert.That(moveData!.HasFlag(MoveFlags.CantRepeat)).IsTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Technical test: without battle data on the target (outside of battle) the effect does nothing and
|
||||
/// does not throw.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_TargetHasNoBattleData_DoesNothing()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, target, targetVolatile, hitData) = CreateTestSetup("tackle");
|
||||
target.BattleData.Returns((IPokemonBattleData?)null);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, target, 0);
|
||||
|
||||
// Assert
|
||||
hitData.DidNotReceive().Fail();
|
||||
await Assert.That(targetVolatile.Contains(ScriptUtils.ResolveName<EncoreEffect>())).IsFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Duration standardized to exactly 3 turns" (Generation V onward). After two
|
||||
/// end-of-turn ticks the effect is still active.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnEndTurn_TwoTurnsPassed_EffectStillActive()
|
||||
{
|
||||
// Arrange
|
||||
var owner = Substitute.For<IPokemon>();
|
||||
owner.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
||||
IScriptSet ownerVolatile = new ScriptSet(owner);
|
||||
var effect = new EncoreEffect(owner, new StringKey("tackle"), 3);
|
||||
ownerVolatile.Add(effect);
|
||||
|
||||
// Act
|
||||
for (var i = 0; i < 2; i++)
|
||||
effect.OnEndTurn(owner, Substitute.For<IBattle>());
|
||||
|
||||
// Assert
|
||||
await Assert.That(ownerVolatile.Contains(ScriptUtils.ResolveName<EncoreEffect>())).IsTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Duration standardized to exactly 3 turns" (Generation V onward). After the third
|
||||
/// end-of-turn tick the effect removes itself.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnEndTurn_ThreeTurnsPassed_EffectRemovesItself()
|
||||
{
|
||||
// Arrange
|
||||
var owner = Substitute.For<IPokemon>();
|
||||
owner.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
|
||||
IScriptSet ownerVolatile = new ScriptSet(owner);
|
||||
var effect = new EncoreEffect(owner, new StringKey("tackle"), 3);
|
||||
ownerVolatile.Add(effect);
|
||||
|
||||
// Act
|
||||
for (var i = 0; i < 3; i++)
|
||||
effect.OnEndTurn(owner, Substitute.For<IBattle>());
|
||||
|
||||
// Assert
|
||||
await Assert.That(ownerVolatile.Contains(ScriptUtils.ResolveName<EncoreEffect>())).IsFalse();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user