Skip to content

Commit

Permalink
Add E2E tests for evolution and move learning
Browse files Browse the repository at this point in the history
  • Loading branch information
hanzi committed Jan 23, 2025
1 parent 3a4c517 commit 7758ef4
Show file tree
Hide file tree
Showing 19 changed files with 242 additions and 2 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
120 changes: 120 additions & 0 deletions tests/test_battle_evolution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
from tests.utility import BotTestCase, with_save_state, with_frame_timeout


class TestBattleEvolution(BotTestCase):
@with_save_state(
[
"emerald/in_tall_grass_before_levelling_up_and_evolving.ss1",
"ruby/in_tall_grass_before_levelling_up_and_evolving.ss1",
"firered/in_tall_grass_before_levelling_up_and_evolving.ss1",
]
)
@with_frame_timeout(1500)
def test_it_stops_evolution(self):
from modules.config.schemas_v1 import Battle
from modules.context import context
from modules.modes import BattleAction
from modules.modes.util import spin
from modules.pokemon_party import get_party

context.config.battle = Battle(stop_evolution=True)

self.bot_mode.set_on_battle_started(lambda *args: BattleAction.Fight)
previous_species = get_party()[0].species_name_for_stats
previous_level = get_party()[0].level
yield from spin(lambda: self.stats.last_encounter is not None and self.stats.last_encounter.outcome is not None)
new_species = get_party()[0].species_name_for_stats
new_level = get_party()[0].level
self.assertGreater(new_level, previous_level)
self.assertEqual(previous_species, new_species)

@with_save_state(
[
"emerald/in_tall_grass_before_levelling_up_and_evolving.ss1",
"ruby/in_tall_grass_before_levelling_up_and_evolving.ss1",
"firered/in_tall_grass_before_levelling_up_and_evolving.ss1",
]
)
@with_frame_timeout(1500)
def test_it_allows_evolution(self):
from modules.config.schemas_v1 import Battle
from modules.context import context
from modules.modes import BattleAction
from modules.modes.util import spin
from modules.pokemon_party import get_party

context.config.battle = Battle(stop_evolution=False)

self.bot_mode.set_on_battle_started(lambda *args: BattleAction.Fight)
previous_species = get_party()[0].species_name_for_stats
previous_level = get_party()[0].level
yield from spin(lambda: self.stats.last_encounter is not None and self.stats.last_encounter.outcome is not None)
new_species = get_party()[0].species_name_for_stats
new_level = get_party()[0].level
self.assertGreater(new_level, previous_level)
self.assertNotEqual(previous_species, new_species)

@with_save_state(
[
# In the `*_before.ss1`, the _pre-evolution_ will learn a move after levelling up
# (i.e. learning _before_ the evolution happens.)
"emerald/in_tall_grass_before_levelling_up_and_evolving_with_new_move_before.ss1",
"ruby/in_tall_grass_before_levelling_up_and_evolving_with_new_move_before.ss1",
"firered/in_tall_grass_before_levelling_up_and_evolving_with_new_move_before.ss1",
# In the `*_after.ss1`, the _evolved Pokémon_ will learn a move (i.e. learning
# _after_ the evolution happens.)
"emerald/in_tall_grass_before_levelling_up_and_evolving_with_new_move_afterwards.ss1",
"ruby/in_tall_grass_before_levelling_up_and_evolving_with_new_move_afterwards.ss1",
"firered/in_tall_grass_before_levelling_up_and_evolving_with_new_move_afterwards.ss1",
]
)
@with_frame_timeout(1500)
def test_it_stops_evolution_with_new_move(self):
from modules.config.schemas_v1 import Battle
from modules.context import context
from modules.modes import BattleAction
from modules.modes.util import spin
from modules.pokemon_party import get_party

context.config.battle = Battle(stop_evolution=True, new_move="learn_best")

self.bot_mode.set_on_battle_started(lambda *args: BattleAction.Fight)
previous_species = get_party()[0].species_name_for_stats
previous_level = get_party()[0].level
yield from spin(lambda: self.stats.last_encounter is not None and self.stats.last_encounter.outcome is not None)
new_species = get_party()[0].species_name_for_stats
new_level = get_party()[0].level
self.assertGreater(new_level, previous_level)
self.assertEqual(previous_species, new_species)

@with_save_state(
[
"emerald/in_tall_grass_before_levelling_up_and_evolving_with_new_move_before.ss1",
"emerald/in_tall_grass_before_levelling_up_and_evolving_with_new_move_afterwards.ss1",
"ruby/in_tall_grass_before_levelling_up_and_evolving_with_new_move_before.ss1",
"ruby/in_tall_grass_before_levelling_up_and_evolving_with_new_move_afterwards.ss1",
"firered/in_tall_grass_before_levelling_up_and_evolving_with_new_move_before.ss1",
"firered/in_tall_grass_before_levelling_up_and_evolving_with_new_move_afterwards.ss1",
]
)
@with_frame_timeout(1500)
def test_it_allows_evolution_with_new_move(self):
from modules.config.schemas_v1 import Battle
from modules.context import context
from modules.modes import BattleAction
from modules.modes.util import spin
from modules.pokemon_party import get_party

context.config.battle = Battle(stop_evolution=False, new_move="learn_best")

self.bot_mode.set_on_battle_started(lambda *args: BattleAction.Fight)
previous_species = get_party()[0].species_name_for_stats
previous_level = get_party()[0].level
previous_moves = [learned_move.move.name for learned_move in get_party()[0].moves]
yield from spin(lambda: self.stats.last_encounter is not None and self.stats.last_encounter.outcome is not None)
new_species = get_party()[0].species_name_for_stats
new_level = get_party()[0].level
new_moves = [learned_move.move.name for learned_move in get_party()[0].moves]
self.assertGreater(new_level, previous_level)
self.assertNotEqual(previous_moves, new_moves)
self.assertNotEqual(previous_species, new_species)
98 changes: 98 additions & 0 deletions tests/test_battle_move_learning.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
from tests.utility import BotTestCase, with_save_state, with_frame_timeout


class TestBattleMoveLearning(BotTestCase):
@with_save_state(
[
"emerald/in_tall_grass_before_levelling_up_and_learning_move_with_empty_slot.ss1",
"ruby/in_tall_grass_before_levelling_up_and_learning_move_with_empty_slot.ss1",
"firered/in_tall_grass_before_levelling_up_and_learning_move_with_empty_slot.ss1",
]
)
@with_frame_timeout(1000)
def test_it_learns_move_with_empty_slot_available(self):
from modules.modes import BattleAction
from modules.modes.util import spin
from modules.pokemon_party import get_party

self.bot_mode.set_on_battle_started(lambda *args: BattleAction.Fight)
previous_level = get_party()[0].level
number_of_moves = len([move for move in get_party()[0].moves if move is not None])
yield from spin(lambda: get_party()[0].level > previous_level)
new_number_of_moves = len([move for move in get_party()[0].moves if move is not None])
self.assertGreater(new_number_of_moves, number_of_moves)

@with_save_state(
[
"emerald/in_tall_grass_before_levelling_up_and_learning_move_with_no_empty_slot.ss1",
"ruby/in_tall_grass_before_levelling_up_and_learning_move_with_no_empty_slot.ss1",
"firered/in_tall_grass_before_levelling_up_and_learning_move_with_no_empty_slot.ss1",
]
)
@with_frame_timeout(1500)
def test_it_replaces_existing_move(self):
from modules.config.schemas_v1 import Battle
from modules.context import context
from modules.modes import BattleAction
from modules.modes.util import spin
from modules.pokemon_party import get_party

context.config.battle = Battle(new_move="learn_best")

self.bot_mode.set_on_battle_started(lambda *args: BattleAction.Fight)
previous_moves = [learned_move.move.name for learned_move in get_party()[0].moves if learned_move is not None]
yield from spin(lambda: self.stats.last_encounter is not None and self.stats.last_encounter.outcome is not None)
new_moves = [learned_move.move.name for learned_move in get_party()[0].moves if learned_move is not None]
self.assertNotEqual(new_moves, previous_moves)

@with_save_state(
[
"emerald/in_tall_grass_before_levelling_up_and_learning_move_with_no_empty_slot.ss1",
"ruby/in_tall_grass_before_levelling_up_and_learning_move_with_no_empty_slot.ss1",
"firered/in_tall_grass_before_levelling_up_and_learning_move_with_no_empty_slot.ss1",
]
)
@with_frame_timeout(1500)
def test_it_does_not_learn_new_move(self):
from modules.config.schemas_v1 import Battle
from modules.context import context
from modules.modes import BattleAction
from modules.modes.util import spin
from modules.pokemon_party import get_party

context.config.battle = Battle(new_move="cancel")

self.bot_mode.set_on_battle_started(lambda *args: BattleAction.Fight)
previous_moves = [learned_move.move.name for learned_move in get_party()[0].moves if learned_move is not None]
yield from spin(lambda: self.stats.last_encounter is not None and self.stats.last_encounter.outcome is not None)
new_moves = [learned_move.move.name for learned_move in get_party()[0].moves if learned_move is not None]
self.assertEqual(new_moves, previous_moves)

@with_save_state(
[
"emerald/in_tall_grass_before_levelling_up_and_learning_move_with_no_empty_slot.ss1",
"ruby/in_tall_grass_before_levelling_up_and_learning_move_with_no_empty_slot.ss1",
"firered/in_tall_grass_before_levelling_up_and_learning_move_with_no_empty_slot.ss1",
]
)
@with_frame_timeout(1500)
def test_it_switches_to_manual_when_having_to_replace_new_move(self):
from modules.config.schemas_v1 import Battle
from modules.context import context
from modules.modes import BattleAction
from modules.modes.util import spin
from modules.pokemon_party import get_party

context.config.battle = Battle(new_move="stop")

self.bot_mode.set_on_battle_started(lambda *args: BattleAction.Fight)
self.bot_mode.allow_ending_on_manual_mode = True

previous_moves = [learned_move.move.name for learned_move in get_party()[0].moves if learned_move is not None]
for _ in spin(lambda: self.stats.last_encounter is not None and self.stats.last_encounter.outcome is not None):
if context.bot_mode == "Manual":
break
yield
new_moves = [learned_move.move.name for learned_move in get_party()[0].moves if learned_move is not None]
self.assertEqual(new_moves, previous_moves)
self.assertIsInManualMode()
2 changes: 1 addition & 1 deletion tests/test_mode_starter.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,6 @@ def test_it_stops_when_encountering_shiny(self):
if self.stats.last_encounter is not None:
self.assertEqual(expected_species, self.stats.last_encounter.species_name)
self.assertTrue(self.stats.last_encounter.is_shiny, "Encountered starter Pokémon was not shiny.")
self.assertEqual("Manual", context.bot_mode)
self.assertIsInManualMode()
return
yield
24 changes: 23 additions & 1 deletion tests/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ def _run_test(test_generator: Generator) -> None:
for listener in context.bot_listeners.copy():
listener.handle_frame(bot_mode, frame_info)
if len(context.controller_stack) > 0:
while len(context.controller_stack) > 1 and context.bot_mode == "Manual":
context.controller_stack.pop()
try:
next(context.controller_stack[-1])
except (StopIteration, GeneratorExit):
Expand Down Expand Up @@ -422,4 +424,24 @@ def rom(self) -> "ROM":

return context.rom

pass
def assertIsInManualMode(self) -> None:
from modules.context import context

self.assertEqual(
"Manual", context.bot_mode, f"Expected bot to be in Manual mode, but it is in {context.bot_mode} mode."
)

def assertIsNotInManualMode(self) -> None:
from modules.context import context

self.assertEqual(
context.bot_mode,
AutomatedTestBotMode.name(),
f"Expected bot to be in {AutomatedTestBotMode.name()} mode, but it is in {context.bot_mode} mode.",
)

self.assertIsInstance(
context.bot_mode_instance,
AutomatedTestBotMode,
f"Bot mode is not an instance of AutomatedTestBotMode but {context.bot_mode_instance.__class__.__name__} instead.",
)

0 comments on commit 7758ef4

Please sign in to comment.