129 lines
7.8 KiB
Python
129 lines
7.8 KiB
Python
import unittest
|
|
|
|
from Fill import distribute_items_restrictive
|
|
from NetUtils import encode
|
|
from worlds.AutoWorld import AutoWorldRegister, call_all
|
|
from worlds import failed_world_loads
|
|
from . import setup_solo_multiworld
|
|
|
|
|
|
class TestImplemented(unittest.TestCase):
|
|
def test_completion_condition(self):
|
|
"""Ensure a completion condition is set that has requirements."""
|
|
for game_name, world_type in AutoWorldRegister.world_types.items():
|
|
if not world_type.hidden and game_name not in {"Sudoku"}:
|
|
with self.subTest(game_name):
|
|
multiworld = setup_solo_multiworld(world_type)
|
|
self.assertFalse(multiworld.completion_condition[1](multiworld.state))
|
|
|
|
def test_entrance_parents(self):
|
|
"""Tests that the parents of created Entrances match the exiting Region."""
|
|
for game_name, world_type in AutoWorldRegister.world_types.items():
|
|
if not world_type.hidden:
|
|
with self.subTest(game_name):
|
|
multiworld = setup_solo_multiworld(world_type)
|
|
for region in multiworld.regions:
|
|
for exit in region.exits:
|
|
self.assertEqual(exit.parent_region, region)
|
|
|
|
def test_stage_methods(self):
|
|
"""Tests that worlds don't try to implement certain steps that are only ever called as stage."""
|
|
for game_name, world_type in AutoWorldRegister.world_types.items():
|
|
if not world_type.hidden:
|
|
with self.subTest(game_name):
|
|
for method in ("assert_generate",):
|
|
self.assertFalse(hasattr(world_type, method),
|
|
f"{method} must be implemented as a @classmethod named stage_{method}.")
|
|
|
|
def test_slot_data(self):
|
|
"""Tests that if a world creates slot data, it's json serializable."""
|
|
for game_name, world_type in AutoWorldRegister.world_types.items():
|
|
# has an await for generate_output which isn't being called
|
|
if game_name in {"Ocarina of Time"}:
|
|
continue
|
|
multiworld = setup_solo_multiworld(world_type)
|
|
with self.subTest(game=game_name, seed=multiworld.seed):
|
|
distribute_items_restrictive(multiworld)
|
|
call_all(multiworld, "post_fill")
|
|
for key, data in multiworld.worlds[1].fill_slot_data().items():
|
|
self.assertIsInstance(key, str, "keys in slot data must be a string")
|
|
self.assertIsInstance(encode(data), str, f"object {type(data).__name__} not serializable.")
|
|
|
|
def test_no_failed_world_loads(self):
|
|
if failed_world_loads:
|
|
self.fail(f"The following worlds failed to load: {failed_world_loads}")
|
|
|
|
def test_explicit_indirect_conditions_spheres(self):
|
|
"""Tests that worlds using explicit indirect conditions produce identical spheres as when using implicit
|
|
indirect conditions"""
|
|
# Because the iteration order of blocked_connections in CollectionState.update_reachable_regions() is
|
|
# nondeterministic, this test may sometimes pass with the same seed even when there are missing indirect
|
|
# conditions.
|
|
for game_name, world_type in AutoWorldRegister.world_types.items():
|
|
multiworld = setup_solo_multiworld(world_type)
|
|
world = multiworld.get_game_worlds(game_name)[0]
|
|
if not world.explicit_indirect_conditions:
|
|
# The world does not use explicit indirect conditions, so it can be skipped.
|
|
continue
|
|
# The world may override explicit_indirect_conditions as a property that cannot be set, so try modifying it.
|
|
try:
|
|
world.explicit_indirect_conditions = False
|
|
world.explicit_indirect_conditions = True
|
|
except Exception:
|
|
# Could not modify the attribute, so skip this world.
|
|
with self.subTest(game=game_name, skipped="world.explicit_indirect_conditions could not be set"):
|
|
continue
|
|
with self.subTest(game=game_name, seed=multiworld.seed):
|
|
distribute_items_restrictive(multiworld)
|
|
call_all(multiworld, "post_fill")
|
|
|
|
# Note: `multiworld.get_spheres()` iterates a set of locations, so the order that locations are checked
|
|
# is nondeterministic and may vary between runs with the same seed.
|
|
explicit_spheres = list(multiworld.get_spheres())
|
|
# Disable explicit indirect conditions and produce a second list of spheres.
|
|
world.explicit_indirect_conditions = False
|
|
implicit_spheres = list(multiworld.get_spheres())
|
|
|
|
# Both lists should be identical.
|
|
if explicit_spheres == implicit_spheres:
|
|
# Test passed.
|
|
continue
|
|
|
|
# Find the first sphere that was different and provide a useful failure message.
|
|
zipped = zip(explicit_spheres, implicit_spheres)
|
|
for sphere_num, (sphere_explicit, sphere_implicit) in enumerate(zipped, start=1):
|
|
# Each sphere created with explicit indirect conditions should be identical to the sphere created
|
|
# with implicit indirect conditions.
|
|
if sphere_explicit != sphere_implicit:
|
|
reachable_only_with_implicit = sorted(sphere_implicit - sphere_explicit)
|
|
if reachable_only_with_implicit:
|
|
locations_and_parents = [(loc, loc.parent_region) for loc in reachable_only_with_implicit]
|
|
self.fail(f"Sphere {sphere_num} created with explicit indirect conditions did not contain"
|
|
f" the same locations as sphere {sphere_num} created with implicit indirect"
|
|
f" conditions. There may be missing indirect conditions for connections to the"
|
|
f" locations' parent regions or connections from other regions which connect to"
|
|
f" these regions."
|
|
f"\nLocations that should have been reachable in sphere {sphere_num} and their"
|
|
f" parent regions:"
|
|
f"\n{locations_and_parents}")
|
|
else:
|
|
# Some locations were only present in the sphere created with explicit indirect conditions.
|
|
# This should not happen because missing indirect conditions should only reduce
|
|
# accessibility, not increase accessibility.
|
|
reachable_only_with_explicit = sorted(sphere_explicit - sphere_implicit)
|
|
self.fail(f"Sphere {sphere_num} created with explicit indirect conditions contained more"
|
|
f" locations than sphere {sphere_num} created with implicit indirect conditions."
|
|
f" This should not happen."
|
|
f"\nUnexpectedly reachable locations in sphere {sphere_num}:"
|
|
f"\n{reachable_only_with_explicit}")
|
|
self.fail("Unreachable")
|
|
|
|
def test_no_items_or_locations_or_regions_submitted_in_init(self):
|
|
"""Test that worlds don't submit items/locations/regions to the multiworld in __init__"""
|
|
for game_name, world_type in AutoWorldRegister.world_types.items():
|
|
with self.subTest("Game", game=game_name):
|
|
multiworld = setup_solo_multiworld(world_type, ())
|
|
self.assertEqual(len(multiworld.itempool), 0)
|
|
self.assertEqual(len(multiworld.get_locations()), 0)
|
|
self.assertEqual(len(multiworld.get_regions()), 0)
|