mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-05-26 00:49:57 -07:00
merge upstream/main
This commit is contained in:
@@ -18,7 +18,15 @@ def run_locations_benchmark():
|
||||
|
||||
class BenchmarkRunner:
|
||||
gen_steps: typing.Tuple[str, ...] = (
|
||||
"generate_early", "create_regions", "create_items", "set_rules", "generate_basic", "pre_fill")
|
||||
"generate_early",
|
||||
"create_regions",
|
||||
"create_items",
|
||||
"set_rules",
|
||||
"connect_entrances",
|
||||
"generate_basic",
|
||||
"pre_fill",
|
||||
)
|
||||
|
||||
rule_iterations: int = 100_000
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(ap-cpp-tests)
|
||||
|
||||
enable_testing()
|
||||
@@ -7,8 +7,8 @@ find_package(GTest REQUIRED)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
add_definitions("/source-charset:utf-8")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "/MTd")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "/MT")
|
||||
# set(CMAKE_CXX_FLAGS_DEBUG "/MDd") # this is the default
|
||||
# set(CMAKE_CXX_FLAGS_RELEASE "/MD") # this is the default
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
# enable static analysis for gcc
|
||||
add_compile_options(-fanalyzer -Werror)
|
||||
|
||||
@@ -5,7 +5,15 @@ from BaseClasses import CollectionState, Item, ItemClassification, Location, Mul
|
||||
from worlds import network_data_package
|
||||
from worlds.AutoWorld import World, call_all
|
||||
|
||||
gen_steps = ("generate_early", "create_regions", "create_items", "set_rules", "generate_basic", "pre_fill")
|
||||
gen_steps = (
|
||||
"generate_early",
|
||||
"create_regions",
|
||||
"create_items",
|
||||
"set_rules",
|
||||
"connect_entrances",
|
||||
"generate_basic",
|
||||
"pre_fill",
|
||||
)
|
||||
|
||||
|
||||
def setup_solo_multiworld(
|
||||
|
||||
@@ -311,6 +311,37 @@ class TestRandomizeEntrances(unittest.TestCase):
|
||||
self.assertEqual([], [exit_ for region in multiworld.get_regions()
|
||||
for exit_ in region.exits if not exit_.connected_region])
|
||||
|
||||
def test_minimal_entrance_rando_with_collect_override(self):
|
||||
"""
|
||||
tests that entrance randomization can complete with minimal accessibility and unreachable exits
|
||||
when the world defines a collect override that add extra values to prog_items
|
||||
"""
|
||||
multiworld = generate_test_multiworld()
|
||||
multiworld.worlds[1].options.accessibility = Accessibility.from_any(Accessibility.option_minimal)
|
||||
multiworld.completion_condition[1] = lambda state: state.can_reach("region24", player=1)
|
||||
generate_disconnected_region_grid(multiworld, 5, 1)
|
||||
prog_items = generate_items(10, 1, True)
|
||||
multiworld.itempool += prog_items
|
||||
filler_items = generate_items(15, 1, False)
|
||||
multiworld.itempool += filler_items
|
||||
e = multiworld.get_entrance("region1_right", 1)
|
||||
set_rule(e, lambda state: False)
|
||||
|
||||
old_collect = multiworld.worlds[1].collect
|
||||
|
||||
def new_collect(state, item):
|
||||
old_collect(state, item)
|
||||
state.prog_items[item.player]["counter"] += 300
|
||||
|
||||
multiworld.worlds[1].collect = new_collect
|
||||
|
||||
randomize_entrances(multiworld.worlds[1], False, directionally_matched_group_lookup)
|
||||
|
||||
self.assertEqual([], [entrance for region in multiworld.get_regions()
|
||||
for entrance in region.entrances if not entrance.parent_region])
|
||||
self.assertEqual([], [exit_ for region in multiworld.get_regions()
|
||||
for exit_ in region.exits if not exit_.connected_region])
|
||||
|
||||
def test_restrictive_region_requirement_does_not_fail(self):
|
||||
multiworld = generate_test_multiworld()
|
||||
generate_disconnected_region_grid(multiworld, 2, 1)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import unittest
|
||||
from worlds.AutoWorld import AutoWorldRegister, call_all, World
|
||||
from . import setup_solo_multiworld
|
||||
|
||||
|
||||
class TestBase(unittest.TestCase):
|
||||
def test_entrance_connection_steps(self):
|
||||
"""Tests that Entrances are connected and not changed after connect_entrances."""
|
||||
def get_entrance_name_to_source_and_target_dict(world: World):
|
||||
return [
|
||||
(entrance.name, entrance.parent_region, entrance.connected_region)
|
||||
for entrance in world.get_entrances()
|
||||
]
|
||||
|
||||
gen_steps = ("generate_early", "create_regions", "create_items", "set_rules", "connect_entrances")
|
||||
additional_steps = ("generate_basic", "pre_fill")
|
||||
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
with self.subTest("Game", game_name=game_name):
|
||||
multiworld = setup_solo_multiworld(world_type, gen_steps)
|
||||
|
||||
original_entrances = get_entrance_name_to_source_and_target_dict(multiworld.worlds[1])
|
||||
|
||||
self.assertTrue(
|
||||
all(entrance[1] is not None and entrance[2] is not None for entrance in original_entrances),
|
||||
f"{game_name} had unconnected entrances after connect_entrances"
|
||||
)
|
||||
|
||||
for step in additional_steps:
|
||||
with self.subTest("Step", step=step):
|
||||
call_all(multiworld, step)
|
||||
step_entrances = get_entrance_name_to_source_and_target_dict(multiworld.worlds[1])
|
||||
|
||||
self.assertEqual(
|
||||
original_entrances, step_entrances, f"{game_name} modified entrances during {step}"
|
||||
)
|
||||
|
||||
def test_all_state_before_connect_entrances(self):
|
||||
"""Before connect_entrances, Entrance objects may be unconnected.
|
||||
Thus, we test that get_all_state is performed with allow_partial_entrances if used before or during
|
||||
connect_entrances."""
|
||||
|
||||
gen_steps = ("generate_early", "create_regions", "create_items", "set_rules", "connect_entrances")
|
||||
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
with self.subTest("Game", game_name=game_name):
|
||||
multiworld = setup_solo_multiworld(world_type, ())
|
||||
|
||||
original_get_all_state = multiworld.get_all_state
|
||||
|
||||
def patched_get_all_state(use_cache: bool, allow_partial_entrances: bool = False):
|
||||
self.assertTrue(allow_partial_entrances, (
|
||||
"Before the connect_entrances step finishes, other worlds might still have partial entrances. "
|
||||
"As such, any call to get_all_state must use allow_partial_entrances = True."
|
||||
))
|
||||
|
||||
return original_get_all_state(use_cache, allow_partial_entrances)
|
||||
|
||||
multiworld.get_all_state = patched_get_all_state
|
||||
|
||||
for step in gen_steps:
|
||||
with self.subTest("Step", step=step):
|
||||
call_all(multiworld, step)
|
||||
@@ -1,6 +1,8 @@
|
||||
import unittest
|
||||
from typing import Callable, Dict, Optional
|
||||
|
||||
from typing_extensions import override
|
||||
|
||||
from BaseClasses import CollectionState, MultiWorld, Region
|
||||
|
||||
|
||||
@@ -8,6 +10,7 @@ class TestHelpers(unittest.TestCase):
|
||||
multiworld: MultiWorld
|
||||
player: int = 1
|
||||
|
||||
@override
|
||||
def setUp(self) -> None:
|
||||
self.multiworld = MultiWorld(self.player)
|
||||
self.multiworld.game[self.player] = "helper_test_game"
|
||||
@@ -38,15 +41,15 @@ class TestHelpers(unittest.TestCase):
|
||||
"TestRegion1": {"TestRegion2": "connection"},
|
||||
"TestRegion2": {"TestRegion1": None},
|
||||
}
|
||||
|
||||
|
||||
reg_exit_set: Dict[str, set[str]] = {
|
||||
"TestRegion1": {"TestRegion3"}
|
||||
}
|
||||
|
||||
|
||||
exit_rules: Dict[str, Callable[[CollectionState], bool]] = {
|
||||
"TestRegion1": lambda state: state.has("test_item", self.player)
|
||||
}
|
||||
|
||||
|
||||
self.multiworld.regions += [Region(region, self.player, self.multiworld, regions[region]) for region in regions]
|
||||
|
||||
with self.subTest("Test Location Creation Helper"):
|
||||
@@ -73,7 +76,7 @@ class TestHelpers(unittest.TestCase):
|
||||
entrance_name = exit_name if exit_name else f"{parent} -> {exit_reg}"
|
||||
self.assertEqual(exit_rules[exit_reg],
|
||||
self.multiworld.get_entrance(entrance_name, self.player).access_rule)
|
||||
|
||||
|
||||
for region in reg_exit_set:
|
||||
current_region = self.multiworld.get_region(region, self.player)
|
||||
current_region.add_exits(reg_exit_set[region])
|
||||
|
||||
@@ -39,7 +39,7 @@ class TestImplemented(unittest.TestCase):
|
||||
"""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", "Zillion"}:
|
||||
if game_name in {"Ocarina of Time"}:
|
||||
continue
|
||||
multiworld = setup_solo_multiworld(world_type)
|
||||
with self.subTest(game=game_name, seed=multiworld.seed):
|
||||
@@ -117,3 +117,12 @@ class TestImplemented(unittest.TestCase):
|
||||
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)
|
||||
|
||||
@@ -67,7 +67,7 @@ class TestBase(unittest.TestCase):
|
||||
def test_itempool_not_modified(self):
|
||||
"""Test that worlds don't modify the itempool after `create_items`"""
|
||||
gen_steps = ("generate_early", "create_regions", "create_items")
|
||||
additional_steps = ("set_rules", "generate_basic", "pre_fill")
|
||||
additional_steps = ("set_rules", "connect_entrances", "generate_basic", "pre_fill")
|
||||
excluded_games = ("Links Awakening DX", "Ocarina of Time", "SMZ3")
|
||||
worlds_to_test = {game: world
|
||||
for game, world in AutoWorldRegister.world_types.items() if game not in excluded_games}
|
||||
@@ -84,7 +84,7 @@ class TestBase(unittest.TestCase):
|
||||
def test_locality_not_modified(self):
|
||||
"""Test that worlds don't modify the locality of items after duplicates are resolved"""
|
||||
gen_steps = ("generate_early", "create_regions", "create_items")
|
||||
additional_steps = ("set_rules", "generate_basic", "pre_fill")
|
||||
additional_steps = ("set_rules", "connect_entrances", "generate_basic", "pre_fill")
|
||||
worlds_to_test = {game: world for game, world in AutoWorldRegister.world_types.items()}
|
||||
for game_name, world_type in worlds_to_test.items():
|
||||
with self.subTest("Game", game=game_name):
|
||||
|
||||
@@ -45,6 +45,12 @@ class TestBase(unittest.TestCase):
|
||||
self.assertEqual(location_count, len(multiworld.get_locations()),
|
||||
f"{game_name} modified locations count during rule creation")
|
||||
|
||||
call_all(multiworld, "connect_entrances")
|
||||
self.assertEqual(region_count, len(multiworld.get_regions()),
|
||||
f"{game_name} modified region count during rule creation")
|
||||
self.assertEqual(location_count, len(multiworld.get_locations()),
|
||||
f"{game_name} modified locations count during rule creation")
|
||||
|
||||
call_all(multiworld, "generate_basic")
|
||||
self.assertEqual(region_count, len(multiworld.get_regions()),
|
||||
f"{game_name} modified region count during generate_basic")
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import unittest
|
||||
|
||||
from BaseClasses import MultiWorld
|
||||
from worlds.AutoWorld import AutoWorldRegister
|
||||
from . import setup_solo_multiworld
|
||||
|
||||
|
||||
class TestWorldMemory(unittest.TestCase):
|
||||
def test_leak(self):
|
||||
def test_leak(self) -> None:
|
||||
"""Tests that worlds don't leak references to MultiWorld or themselves with default options."""
|
||||
import gc
|
||||
import weakref
|
||||
refs: dict[str, weakref.ReferenceType[MultiWorld]] = {}
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
with self.subTest("Game", game_name=game_name):
|
||||
with self.subTest("Game creation", game_name=game_name):
|
||||
weak = weakref.ref(setup_solo_multiworld(world_type))
|
||||
gc.collect()
|
||||
refs[game_name] = weak
|
||||
gc.collect()
|
||||
for game_name, weak in refs.items():
|
||||
with self.subTest("Game cleanup", game_name=game_name):
|
||||
self.assertFalse(weak(), "World leaked a reference")
|
||||
|
||||
@@ -3,7 +3,7 @@ from worlds.AutoWorld import AutoWorldRegister
|
||||
|
||||
|
||||
class TestNames(unittest.TestCase):
|
||||
def test_item_names_format(self):
|
||||
def test_item_names_format(self) -> None:
|
||||
"""Item names must not be all numeric in order to differentiate between ID and name in !hint"""
|
||||
for gamename, world_type in AutoWorldRegister.world_types.items():
|
||||
with self.subTest(game=gamename):
|
||||
@@ -11,7 +11,7 @@ class TestNames(unittest.TestCase):
|
||||
self.assertFalse(item_name.isnumeric(),
|
||||
f"Item name \"{item_name}\" is invalid. It must not be numeric.")
|
||||
|
||||
def test_location_name_format(self):
|
||||
def test_location_name_format(self) -> None:
|
||||
"""Location names must not be all numeric in order to differentiate between ID and name in !hint_location"""
|
||||
for gamename, world_type in AutoWorldRegister.world_types.items():
|
||||
with self.subTest(game=gamename):
|
||||
|
||||
@@ -2,11 +2,11 @@ import unittest
|
||||
|
||||
from BaseClasses import CollectionState
|
||||
from worlds.AutoWorld import AutoWorldRegister
|
||||
from . import setup_solo_multiworld
|
||||
from . import setup_solo_multiworld, gen_steps
|
||||
|
||||
|
||||
class TestBase(unittest.TestCase):
|
||||
gen_steps = ["generate_early", "create_regions", "create_items", "set_rules", "generate_basic", "pre_fill"]
|
||||
gen_steps = gen_steps
|
||||
|
||||
default_settings_unreachable_regions = {
|
||||
"A Link to the Past": {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import unittest
|
||||
|
||||
from worlds.AutoWorld import AutoWorldRegister, call_all
|
||||
from . import setup_solo_multiworld
|
||||
|
||||
|
||||
class TestBase(unittest.TestCase):
|
||||
gen_steps = (
|
||||
"generate_early",
|
||||
"create_regions",
|
||||
)
|
||||
|
||||
test_steps = (
|
||||
"create_items",
|
||||
"set_rules",
|
||||
"connect_entrances",
|
||||
"generate_basic",
|
||||
"pre_fill",
|
||||
)
|
||||
|
||||
def test_all_state_is_available(self):
|
||||
"""Ensure all_state can be created at certain steps."""
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
with self.subTest("Game", game=game_name):
|
||||
multiworld = setup_solo_multiworld(world_type, self.gen_steps)
|
||||
for step in self.test_steps:
|
||||
with self.subTest("Step", step=step):
|
||||
call_all(multiworld, step)
|
||||
self.assertTrue(multiworld.get_all_state(False, True))
|
||||
Reference in New Issue
Block a user