mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
Tests: now autoload tests from /worlds/*/test (#1318)
Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
def load_tests(loader, standard_tests, pattern):
|
||||
import os
|
||||
import unittest
|
||||
from ..TestBase import file_path
|
||||
from worlds.AutoWorld import AutoWorldRegister
|
||||
|
||||
suite = unittest.TestSuite()
|
||||
suite.addTests(standard_tests)
|
||||
folders = [os.path.join(os.path.split(world.__file__)[0], "test")
|
||||
for world in AutoWorldRegister.world_types.values()]
|
||||
for folder in folders:
|
||||
if os.path.exists(folder):
|
||||
suite.addTests(loader.discover(folder, top_level_dir=file_path))
|
||||
return suite
|
||||
|
@@ -1,23 +0,0 @@
|
||||
from typing import Dict
|
||||
|
||||
from . import RLTestBase
|
||||
from worlds.rogue_legacy.Items import RLItemData, item_table
|
||||
from worlds.rogue_legacy.Locations import RLLocationData, location_table
|
||||
|
||||
|
||||
class UniqueTest(RLTestBase):
|
||||
@staticmethod
|
||||
def test_item_ids_are_all_unique():
|
||||
item_ids: Dict[int, str] = {}
|
||||
for name, data in item_table.items():
|
||||
assert data.code not in item_ids.keys(), f"'{name}': {data.code}, is not unique. " \
|
||||
f"'{item_ids[data.code]}' also has this identifier."
|
||||
item_ids[data.code] = name
|
||||
|
||||
@staticmethod
|
||||
def test_location_ids_are_all_unique():
|
||||
location_ids: Dict[int, str] = {}
|
||||
for name, data in location_table.items():
|
||||
assert data.code not in location_ids.keys(), f"'{name}': {data.code}, is not unique. " \
|
||||
f"'{location_ids[data.code]}' also has this identifier."
|
||||
location_ids[data.code] = name
|
@@ -1,5 +0,0 @@
|
||||
from test.worlds.test_base import WorldTestBase
|
||||
|
||||
|
||||
class RLTestBase(WorldTestBase):
|
||||
game = "Rogue Legacy"
|
@@ -1,22 +0,0 @@
|
||||
import typing
|
||||
from . import SoETestBase
|
||||
|
||||
|
||||
class AccessTest(SoETestBase):
|
||||
@staticmethod
|
||||
def _resolveGourds(gourds: typing.Dict[str, typing.Iterable[int]]):
|
||||
return [f"{name} #{number}" for name, numbers in gourds.items() for number in numbers]
|
||||
|
||||
def testBronzeAxe(self):
|
||||
gourds = {
|
||||
"Pyramid bottom": (118, 121, 122, 123, 124, 125),
|
||||
"Pyramid top": (140,)
|
||||
}
|
||||
locations = ["Rimsala"] + self._resolveGourds(gourds)
|
||||
items = [["Bronze Axe"]]
|
||||
self.assertAccessDependency(locations, items)
|
||||
|
||||
def testBronzeSpearPlus(self):
|
||||
locations = ["Megataur"]
|
||||
items = [["Bronze Spear"], ["Lance (Weapon)"], ["Laser Lance"]]
|
||||
self.assertAccessDependency(locations, items)
|
@@ -1,53 +0,0 @@
|
||||
from . import SoETestBase
|
||||
|
||||
|
||||
class TestFragmentGoal(SoETestBase):
|
||||
options = {
|
||||
"energy_core": "fragments",
|
||||
"available_fragments": 21,
|
||||
"required_fragments": 20,
|
||||
}
|
||||
|
||||
def testFragments(self):
|
||||
self.collect_by_name(["Gladiator Sword", "Diamond Eye", "Wheel", "Gauge"])
|
||||
self.assertBeatable(False) # 0 fragments
|
||||
fragments = self.get_items_by_name("Energy Core Fragment")
|
||||
victory = self.get_item_by_name("Victory")
|
||||
self.collect(fragments[:-2]) # 1 too few
|
||||
self.assertEqual(self.count("Energy Core Fragment"), 19)
|
||||
self.assertBeatable(False)
|
||||
self.collect(fragments[-2:-1]) # exact
|
||||
self.assertEqual(self.count("Energy Core Fragment"), 20)
|
||||
self.assertBeatable(True)
|
||||
self.remove([victory]) # reset
|
||||
self.collect(fragments[-1:]) # 1 extra
|
||||
self.assertEqual(self.count("Energy Core Fragment"), 21)
|
||||
self.assertBeatable(True)
|
||||
|
||||
def testNoWeapon(self):
|
||||
self.collect_by_name(["Diamond Eye", "Wheel", "Gauge", "Energy Core Fragment"])
|
||||
self.assertBeatable(False)
|
||||
|
||||
def testNoRocket(self):
|
||||
self.collect_by_name(["Gladiator Sword", "Diamond Eye", "Wheel", "Energy Core Fragment"])
|
||||
self.assertBeatable(False)
|
||||
|
||||
|
||||
class TestShuffleGoal(SoETestBase):
|
||||
options = {
|
||||
"energy_core": "shuffle",
|
||||
}
|
||||
|
||||
def testCore(self):
|
||||
self.collect_by_name(["Gladiator Sword", "Diamond Eye", "Wheel", "Gauge"])
|
||||
self.assertBeatable(False)
|
||||
self.collect_by_name(["Energy Core"])
|
||||
self.assertBeatable(True)
|
||||
|
||||
def testNoWeapon(self):
|
||||
self.collect_by_name(["Diamond Eye", "Wheel", "Gauge", "Energy Core"])
|
||||
self.assertBeatable(False)
|
||||
|
||||
def testNoRocket(self):
|
||||
self.collect_by_name(["Gladiator Sword", "Diamond Eye", "Wheel", "Energy Core"])
|
||||
self.assertBeatable(False)
|
@@ -1,5 +0,0 @@
|
||||
from test.worlds.test_base import WorldTestBase
|
||||
|
||||
|
||||
class SoETestBase(WorldTestBase):
|
||||
game = "Secret of Evermore"
|
@@ -1,98 +0,0 @@
|
||||
import typing
|
||||
import unittest
|
||||
from argparse import Namespace
|
||||
from test.general import gen_steps
|
||||
from BaseClasses import MultiWorld, Item
|
||||
from worlds import AutoWorld
|
||||
from worlds.AutoWorld import call_all
|
||||
|
||||
|
||||
class WorldTestBase(unittest.TestCase):
|
||||
options: typing.Dict[str, typing.Any] = {}
|
||||
multiworld: MultiWorld
|
||||
|
||||
game: typing.ClassVar[str] # define game name in subclass, example "Secret of Evermore"
|
||||
auto_construct: typing.ClassVar[bool] = True
|
||||
""" automatically set up a world for each test in this class """
|
||||
|
||||
def setUp(self) -> None:
|
||||
if self.auto_construct:
|
||||
self.world_setup()
|
||||
|
||||
def world_setup(self, seed: typing.Optional[int] = None) -> None:
|
||||
if not hasattr(self, "game"):
|
||||
raise NotImplementedError("didn't define game name")
|
||||
self.multiworld = MultiWorld(1)
|
||||
self.multiworld.game[1] = self.game
|
||||
self.multiworld.player_name = {1: "Tester"}
|
||||
self.multiworld.set_seed(seed)
|
||||
args = Namespace()
|
||||
for name, option in AutoWorld.AutoWorldRegister.world_types[self.game].option_definitions.items():
|
||||
setattr(args, name, {
|
||||
1: option.from_any(self.options.get(name, getattr(option, "default")))
|
||||
})
|
||||
self.multiworld.set_options(args)
|
||||
self.multiworld.set_default_common_options()
|
||||
for step in gen_steps:
|
||||
call_all(self.multiworld, step)
|
||||
|
||||
def collect_all_but(self, item_names: typing.Union[str, typing.Iterable[str]]) -> None:
|
||||
if isinstance(item_names, str):
|
||||
item_names = (item_names,)
|
||||
for item in self.multiworld.get_items():
|
||||
if item.name not in item_names:
|
||||
self.multiworld.state.collect(item)
|
||||
|
||||
def get_item_by_name(self, item_name: str) -> Item:
|
||||
for item in self.multiworld.get_items():
|
||||
if item.name == item_name:
|
||||
return item
|
||||
raise ValueError("No such item")
|
||||
|
||||
def get_items_by_name(self, item_names: typing.Union[str, typing.Iterable[str]]) -> typing.List[Item]:
|
||||
if isinstance(item_names, str):
|
||||
item_names = (item_names,)
|
||||
return [item for item in self.multiworld.itempool if item.name in item_names]
|
||||
|
||||
def collect_by_name(self, item_names: typing.Union[str, typing.Iterable[str]]) -> typing.List[Item]:
|
||||
""" collect all of the items in the item pool that have the given names """
|
||||
items = self.get_items_by_name(item_names)
|
||||
self.collect(items)
|
||||
return items
|
||||
|
||||
def collect(self, items: typing.Union[Item, typing.Iterable[Item]]) -> None:
|
||||
if isinstance(items, Item):
|
||||
items = (items,)
|
||||
for item in items:
|
||||
self.multiworld.state.collect(item)
|
||||
|
||||
def remove(self, items: typing.Union[Item, typing.Iterable[Item]]) -> None:
|
||||
if isinstance(items, Item):
|
||||
items = (items,)
|
||||
for item in items:
|
||||
if item.location and item.location.event and item.location in self.multiworld.state.events:
|
||||
self.multiworld.state.events.remove(item.location)
|
||||
self.multiworld.state.remove(item)
|
||||
|
||||
def can_reach_location(self, location: str) -> bool:
|
||||
return self.multiworld.state.can_reach(location, "Location", 1)
|
||||
|
||||
def count(self, item_name: str) -> int:
|
||||
return self.multiworld.state.count(item_name, 1)
|
||||
|
||||
def assertAccessDependency(self,
|
||||
locations: typing.List[str],
|
||||
possible_items: typing.Iterable[typing.Iterable[str]]) -> None:
|
||||
all_items = [item_name for item_names in possible_items for item_name in item_names]
|
||||
|
||||
self.collect_all_but(all_items)
|
||||
for location in self.multiworld.get_locations():
|
||||
self.assertEqual(self.multiworld.state.can_reach(location), location.name not in locations)
|
||||
for item_names in possible_items:
|
||||
items = self.collect_by_name(item_names)
|
||||
for location in locations:
|
||||
self.assertTrue(self.can_reach_location(location))
|
||||
self.remove(items)
|
||||
|
||||
def assertBeatable(self, beatable: bool):
|
||||
self.assertEqual(self.multiworld.can_beat_game(self.multiworld.state), beatable)
|
@@ -1,149 +0,0 @@
|
||||
from . import ZillionTestBase
|
||||
|
||||
|
||||
class TestGoalVanilla(ZillionTestBase):
|
||||
options = {
|
||||
"start_char": "JJ",
|
||||
"jump_levels": "vanilla",
|
||||
"gun_levels": "vanilla",
|
||||
"floppy_disk_count": 7,
|
||||
"floppy_req": 6,
|
||||
}
|
||||
|
||||
def test_floppies(self) -> None:
|
||||
self.collect_by_name(["Apple", "Champ", "Red ID Card"])
|
||||
self.assertBeatable(False) # 0 floppies
|
||||
floppies = self.get_items_by_name("Floppy Disk")
|
||||
win = self.get_item_by_name("Win")
|
||||
self.collect(floppies[:-2]) # 1 too few
|
||||
self.assertEqual(self.count("Floppy Disk"), 5)
|
||||
self.assertBeatable(False)
|
||||
self.collect(floppies[-2:-1]) # exact
|
||||
self.assertEqual(self.count("Floppy Disk"), 6)
|
||||
self.assertBeatable(True)
|
||||
self.remove([win]) # reset
|
||||
self.collect(floppies[-1:]) # 1 extra
|
||||
self.assertEqual(self.count("Floppy Disk"), 7)
|
||||
self.assertBeatable(True)
|
||||
|
||||
def test_with_everything(self) -> None:
|
||||
self.collect_by_name(["Apple", "Champ", "Red ID Card", "Floppy Disk"])
|
||||
self.assertBeatable(True)
|
||||
|
||||
def test_no_jump(self) -> None:
|
||||
self.collect_by_name(["Champ", "Red ID Card", "Floppy Disk"])
|
||||
self.assertBeatable(False)
|
||||
|
||||
def test_no_gun(self) -> None:
|
||||
self.ensure_gun_3_requirement()
|
||||
self.collect_by_name(["Apple", "Red ID Card", "Floppy Disk"])
|
||||
self.assertBeatable(False)
|
||||
|
||||
def test_no_red(self) -> None:
|
||||
self.collect_by_name(["Apple", "Champ", "Floppy Disk"])
|
||||
self.assertBeatable(False)
|
||||
|
||||
|
||||
class TestGoalBalanced(ZillionTestBase):
|
||||
options = {
|
||||
"start_char": "JJ",
|
||||
"jump_levels": "balanced",
|
||||
"gun_levels": "balanced",
|
||||
}
|
||||
|
||||
def test_jump(self) -> None:
|
||||
self.collect_by_name(["Red ID Card", "Floppy Disk", "Zillion"])
|
||||
self.assertBeatable(False) # not enough jump
|
||||
opas = self.get_items_by_name("Opa-Opa")
|
||||
self.collect(opas[:1]) # too few
|
||||
self.assertEqual(self.count("Opa-Opa"), 1)
|
||||
self.assertBeatable(False)
|
||||
self.collect(opas[1:])
|
||||
self.assertBeatable(True)
|
||||
|
||||
def test_guns(self) -> None:
|
||||
self.ensure_gun_3_requirement()
|
||||
self.collect_by_name(["Red ID Card", "Floppy Disk", "Opa-Opa"])
|
||||
self.assertBeatable(False) # not enough gun
|
||||
guns = self.get_items_by_name("Zillion")
|
||||
self.collect(guns[:1]) # too few
|
||||
self.assertEqual(self.count("Zillion"), 1)
|
||||
self.assertBeatable(False)
|
||||
self.collect(guns[1:])
|
||||
self.assertBeatable(True)
|
||||
|
||||
|
||||
class TestGoalRestrictive(ZillionTestBase):
|
||||
options = {
|
||||
"start_char": "JJ",
|
||||
"jump_levels": "restrictive",
|
||||
"gun_levels": "restrictive",
|
||||
}
|
||||
|
||||
def test_jump(self) -> None:
|
||||
self.collect_by_name(["Champ", "Red ID Card", "Floppy Disk", "Zillion"])
|
||||
self.assertBeatable(False) # not enough jump
|
||||
self.collect_by_name("Opa-Opa")
|
||||
self.assertBeatable(False) # with all opas, jj champ can't jump
|
||||
self.collect_by_name("Apple")
|
||||
self.assertBeatable(True)
|
||||
|
||||
def test_guns(self) -> None:
|
||||
self.ensure_gun_3_requirement()
|
||||
self.collect_by_name(["Apple", "Red ID Card", "Floppy Disk", "Opa-Opa"])
|
||||
self.assertBeatable(False) # not enough gun
|
||||
self.collect_by_name("Zillion")
|
||||
self.assertBeatable(False) # with all guns, jj apple can't gun
|
||||
self.collect_by_name("Champ")
|
||||
self.assertBeatable(True)
|
||||
|
||||
|
||||
class TestGoalAppleStart(ZillionTestBase):
|
||||
""" creation of character rescue items has some special interactions with logic """
|
||||
options = {
|
||||
"start_char": "Apple",
|
||||
"jump_levels": "balanced",
|
||||
"gun_levels": "low",
|
||||
"zillion_count": 5
|
||||
}
|
||||
|
||||
def test_guns_jj_first(self) -> None:
|
||||
""" with low gun levels, 5 Zillion is enough to get JJ to gun 3 """
|
||||
self.ensure_gun_3_requirement()
|
||||
self.collect_by_name(["JJ", "Red ID Card", "Floppy Disk", "Opa-Opa"])
|
||||
self.assertBeatable(False) # not enough gun
|
||||
self.collect_by_name("Zillion")
|
||||
self.assertBeatable(True)
|
||||
|
||||
def test_guns_zillions_first(self) -> None:
|
||||
""" with low gun levels, 5 Zillion is enough to get JJ to gun 3 """
|
||||
self.ensure_gun_3_requirement()
|
||||
self.collect_by_name(["Zillion", "Red ID Card", "Floppy Disk", "Opa-Opa"])
|
||||
self.assertBeatable(False) # not enough gun
|
||||
self.collect_by_name("JJ")
|
||||
self.assertBeatable(True)
|
||||
|
||||
|
||||
class TestGoalChampStart(ZillionTestBase):
|
||||
""" creation of character rescue items has some special interactions with logic """
|
||||
options = {
|
||||
"start_char": "Champ",
|
||||
"jump_levels": "low",
|
||||
"gun_levels": "balanced",
|
||||
"opa_opa_count": 5,
|
||||
"opas_per_level": 1
|
||||
}
|
||||
|
||||
def test_jump_jj_first(self) -> None:
|
||||
""" with low jump levels, 5 level-ups is enough to get JJ to jump 3 """
|
||||
self.collect_by_name(["JJ", "Red ID Card", "Floppy Disk", "Zillion"])
|
||||
self.assertBeatable(False) # not enough jump
|
||||
self.collect_by_name("Opa-Opa")
|
||||
self.assertBeatable(True)
|
||||
|
||||
def test_jump_opa_first(self) -> None:
|
||||
""" with low jump levels, 5 level-ups is enough to get JJ to jump 3 """
|
||||
self.collect_by_name(["Opa-Opa", "Red ID Card", "Floppy Disk", "Zillion"])
|
||||
self.assertBeatable(False) # not enough jump
|
||||
self.collect_by_name("JJ")
|
||||
self.assertBeatable(True)
|
@@ -1,26 +0,0 @@
|
||||
from test.worlds.zillion import ZillionTestBase
|
||||
|
||||
from worlds.zillion.options import ZillionJumpLevels, ZillionGunLevels, validate
|
||||
from zilliandomizer.options import VBLR_CHOICES
|
||||
|
||||
|
||||
class OptionsTest(ZillionTestBase):
|
||||
auto_construct = False
|
||||
|
||||
def test_validate_default(self) -> None:
|
||||
self.world_setup()
|
||||
validate(self.multiworld, 1)
|
||||
|
||||
def test_vblr_ap_to_zz(self) -> None:
|
||||
""" all of the valid values for the AP options map to valid values for ZZ options """
|
||||
for option_name, vblr_class in (
|
||||
("jump_levels", ZillionJumpLevels),
|
||||
("gun_levels", ZillionGunLevels)
|
||||
):
|
||||
for value in vblr_class.name_lookup.values():
|
||||
self.options = {option_name: value}
|
||||
self.world_setup()
|
||||
zz_options, _item_counts = validate(self.multiworld, 1)
|
||||
assert getattr(zz_options, option_name) in VBLR_CHOICES
|
||||
|
||||
# TODO: test validate with invalid combinations of options
|
@@ -1,29 +0,0 @@
|
||||
from typing import cast
|
||||
from test.worlds.zillion import ZillionTestBase
|
||||
|
||||
from worlds.zillion import ZillionWorld
|
||||
|
||||
|
||||
class SeedTest(ZillionTestBase):
|
||||
auto_construct = False
|
||||
|
||||
def test_reproduce_seed(self) -> None:
|
||||
self.world_setup(42)
|
||||
z_world = cast(ZillionWorld, self.multiworld.worlds[1])
|
||||
r = z_world.zz_system.randomizer
|
||||
assert r
|
||||
randomized_requirements_first = tuple(
|
||||
location.req.gun
|
||||
for location in r.locations.values()
|
||||
)
|
||||
|
||||
self.world_setup(42)
|
||||
z_world = cast(ZillionWorld, self.multiworld.worlds[1])
|
||||
r = z_world.zz_system.randomizer
|
||||
assert r
|
||||
randomized_requirements_second = tuple(
|
||||
location.req.gun
|
||||
for location in r.locations.values()
|
||||
)
|
||||
|
||||
assert randomized_requirements_first == randomized_requirements_second
|
@@ -1,20 +0,0 @@
|
||||
from typing import cast
|
||||
from test.worlds.test_base import WorldTestBase
|
||||
from worlds.zillion import ZillionWorld
|
||||
|
||||
|
||||
class ZillionTestBase(WorldTestBase):
|
||||
game = "Zillion"
|
||||
|
||||
def ensure_gun_3_requirement(self) -> None:
|
||||
"""
|
||||
There's a low probability that gun 3 is not required.
|
||||
|
||||
This makes sure that gun 3 is required by making all the canisters
|
||||
in O-7 (including key word canisters) require gun 3.
|
||||
"""
|
||||
zz_world = cast(ZillionWorld, self.multiworld.worlds[1])
|
||||
assert zz_world.zz_system.randomizer
|
||||
for zz_loc_name, zz_loc in zz_world.zz_system.randomizer.locations.items():
|
||||
if zz_loc_name.startswith("r15c6"):
|
||||
zz_loc.req.gun = 3
|
Reference in New Issue
Block a user