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,168 @@
using PkmnLib.Dynamic.Libraries;
using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.Models.Choices;
using PkmnLib.Plugin.Gen7.Scripts.Moves;
using PkmnLib.Static.Libraries;
using PkmnLib.Static.Moves;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
/// <summary>
/// Tests for the <see cref="Metronome"/> script, which implements Metronome.
/// Behavior is verified against the Bulbapedia page for Metronome (Generation VII).
/// </summary>
public class MetronomeTests
{
/// <summary>
/// Creates a fully mocked test setup for Metronome tests. The user's move library and battle random are
/// mocked so that tests control which move <see cref="Metronome.ChangeMove"/> "randomly" rolls.
/// </summary>
private static (Metronome metronome, IMoveChoice choice, IPokemon user, IReadOnlyMoveLibrary moveLibrary,
IBattleRandom random) CreateTestSetup()
{
var metronome = new Metronome();
var user = Substitute.For<IPokemon>();
var choice = Substitute.For<IMoveChoice>();
choice.User.Returns(user);
var battle = Substitute.For<IBattle>();
var random = Substitute.For<IBattleRandom>();
battle.Random.Returns(random);
var battleData = Substitute.For<IPokemonBattleData>();
battleData.Battle.Returns(battle);
user.BattleData.Returns(battleData);
var moveLibrary = Substitute.For<IReadOnlyMoveLibrary>();
var staticLibrary = Substitute.For<IStaticLibrary>();
staticLibrary.Moves.Returns(moveLibrary);
var dynamicLibrary = Substitute.For<IDynamicLibrary>();
dynamicLibrary.StaticLibrary.Returns(staticLibrary);
user.Library.Returns(dynamicLibrary);
return (metronome, choice, user, moveLibrary, random);
}
/// <summary>
/// Creates a mocked <see cref="IMoveData"/> with the given name.
/// </summary>
private static IMoveData CreateMoveData(string name)
{
var moveData = Substitute.For<IMoveData>();
moveData.Name.Returns(new StringKey(name));
return moveData;
}
/// <summary>
/// Bulbapedia: "The Metronome user executes a randomly selected move."
/// The move rolled from the move library replaces the used move name.
/// </summary>
[Test]
public async Task ChangeMove_SelectableMoveRolled_MoveNameChangesToRolledMove()
{
// Arrange
var (metronome, choice, _, moveLibrary, random) = CreateTestSetup();
var tackle = CreateMoveData("tackle");
moveLibrary.GetRandom(random).Returns(tackle);
var moveName = new StringKey("metronome");
// Act
metronome.ChangeMove(choice, ref moveName);
// Assert
await Assert.That(moveName).IsEqualTo(new StringKey("tackle"));
}
/// <summary>
/// Technical test: if the user has no <see cref="IPokemon.BattleData"/>, the script cannot roll a random
/// move and must leave the move name unchanged.
/// </summary>
[Test]
public async Task ChangeMove_UserHasNoBattleData_MoveNameUnchanged()
{
// Arrange
var (metronome, choice, user, moveLibrary, random) = CreateTestSetup();
user.BattleData.Returns((IPokemonBattleData?)null);
var tackle = CreateMoveData("tackle");
moveLibrary.GetRandom(random).Returns(tackle);
var moveName = new StringKey("metronome");
// Act
metronome.ChangeMove(choice, ref moveName);
// Assert
await Assert.That(moveName).IsEqualTo(new StringKey("metronome"));
}
/// <summary>
/// Bulbapedia: "Metronome cannot select a Max Move, a Z-Move, or any of the moves" in its list of
/// unselectable moves. When an unselectable move is rolled, the script rerolls until it finds a
/// selectable one.
/// </summary>
[Test]
public async Task ChangeMove_UnselectableMoveRolledFirst_RerollsUntilSelectableMove()
{
// Arrange
var (metronome, choice, _, moveLibrary, random) = CreateTestSetup();
var protect = CreateMoveData("protect");
var tackle = CreateMoveData("tackle");
moveLibrary.GetRandom(random).Returns(protect, tackle);
var moveName = new StringKey("metronome");
// Act
metronome.ChangeMove(choice, ref moveName);
// Assert
await Assert.That(moveName).IsEqualTo(new StringKey("tackle"));
}
/// <summary>
/// Bulbapedia: "Metronome cannot select a Max Move, a Z-Move, or any of the moves" in its list of
/// unselectable moves. A sample of moves that are unselectable in Generation VII (including Metronome
/// itself) is rolled first; the script must skip them and use the next (selectable) roll instead.
/// </summary>
[Test, Arguments("metronome"), Arguments("protect"), Arguments("detect"), Arguments("endure"), Arguments("counter"),
Arguments("mirror_coat"), Arguments("mirror_move"), Arguments("mimic"), Arguments("sketch"),
Arguments("sleep_talk"), Arguments("struggle"), Arguments("transform"), Arguments("destiny_bond"),
Arguments("focus_punch"), Arguments("helping_hand"), Arguments("assist"), Arguments("copycat"),
Arguments("chatter"), Arguments("after_you"), Arguments("quash"), Arguments("wide_guard"),
Arguments("quick_guard"), Arguments("instruct"), Arguments("spectral_thief"), Arguments("crafty_shield"),
Arguments("snore")]
public async Task ChangeMove_Gen7UnselectableMoveRolled_IsNeverSelected(string unselectableMove)
{
// Arrange
var (metronome, choice, _, moveLibrary, random) = CreateTestSetup();
var rolled = CreateMoveData(unselectableMove);
var tackle = CreateMoveData("tackle");
moveLibrary.GetRandom(random).Returns(rolled, tackle);
var moveName = new StringKey("metronome");
// Act
metronome.ChangeMove(choice, ref moveName);
// Assert - the unselectable move was skipped in favor of the next roll.
await Assert.That(moveName).IsEqualTo(new StringKey("tackle"));
}
/// <summary>
/// Bulbapedia: "The Metronome user executes a randomly selected move." Moves such as Dig, Fly, and Roar do
/// not appear in the Generation VII column of Bulbapedia's list of moves Metronome cannot select, so
/// Metronome must be able to call them (they are only excluded for other copying moves such as Assist).
/// </summary>
[Test, Arguments("dig"), Arguments("fly"), Arguments("roar")]
public async Task ChangeMove_MoveSelectableByMetronomeButBannedForAssistRolled_IsSelected(string selectableMove)
{
// Arrange
var (metronome, choice, _, moveLibrary, random) = CreateTestSetup();
var rolled = CreateMoveData(selectableMove);
var tackle = CreateMoveData("tackle");
moveLibrary.GetRandom(random).Returns(rolled, tackle);
var moveName = new StringKey("metronome");
// Act
metronome.ChangeMove(choice, ref moveName);
// Assert - the first roll is selectable by Metronome in Generation VII, so it must be used.
await Assert.That(moveName).IsEqualTo(new StringKey(selectableMove));
}
}