From 4276c6d6b01542181b79ea4678b5e857fae3e86d Mon Sep 17 00:00:00 2001 From: Phaneros <31861583+MatthewMarinets@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:45:38 -0700 Subject: [PATCH] sc2: Fixing random fill errors in unit tests (#6045) --- worlds/sc2/docs/setup_en.md | 33 ++++++++++++---- worlds/sc2/locations.py | 16 ++------ worlds/sc2/pool_filter.py | 20 +++++----- worlds/sc2/regions.py | 2 +- worlds/sc2/rules.py | 10 ++--- worlds/sc2/test/slow_tests.py | 52 +++++++++++++++++++++++++ worlds/sc2/test/test_base.py | 24 ++++++------ worlds/sc2/test/test_generation.py | 62 ++++++++++++++++-------------- worlds/sc2/test/test_regions.py | 23 +++++------ 9 files changed, 154 insertions(+), 88 deletions(-) create mode 100644 worlds/sc2/test/slow_tests.py diff --git a/worlds/sc2/docs/setup_en.md b/worlds/sc2/docs/setup_en.md index d6f9f6cda5..e70c15d28f 100644 --- a/worlds/sc2/docs/setup_en.md +++ b/worlds/sc2/docs/setup_en.md @@ -156,15 +156,17 @@ This page includes all data associated with all games. ## How do I join a MultiWorld game? -1. Run ArchipelagoStarcraft2Client.exe. +1. Run ArchipelagoLauncher.exe. - macOS users should instead follow the instructions found at ["Running in macOS"](#running-in-macos) for this step only. -2. In the Archipelago tab, type `/connect [server IP]`. +2. Search for the Starcraft 2 Client in the launcher to open the game-specific client + - Alternatively, steps 1 and 2 can be combined by providing the `"Starcraft 2 Client"` launch argument to the launcher. +3. In the Archipelago tab, type `/connect [server IP]`. - If you're running through the website, the server IP should be displayed near the top of the room page. - The server IP may also be typed into the top bar, and then clicking "Connect" -3. Type your slot name from your YAML when prompted. -4. If the server has a password, enter that when prompted. -5. Once connected, switch to the 'StarCraft 2 Launcher' tab in the client. There, you can see all the missions in your +4. Type your slot name from your YAML when prompted. +5. If the server has a password, enter that when prompted. +6. Once connected, switch to the 'StarCraft 2 Launcher' tab in the client. There, you can see all the missions in your world. Unreachable missions will have greyed-out text. Completed missions (all locations collected) will have white text. @@ -173,7 +175,22 @@ Mission buttons will have a color corresponding to the faction you play as in th Click on an available mission to start it. -## The game isn't launching when I try to start a mission. +## Troubleshooting + +### I can't connect to my seed. + +Rooms on the Archipelago website go to sleep after two hours of inactivity; reload or refresh the room page +to start them back up. +When restarting the room, the connection port may change (the numbers after "archipelago.gg:"), +make sure that is accurate. +Your slot name should be displayed on the room page as well; make sure that exactly matches the slot name you +type into your client, and note that it is case-sensitive. + +If none of these things solve the problem, visit the [Discord](https://discord.com/invite/8Z65BR2) and check +the #software-announcements channel to see if there's a listed outage, or visit the #starcraft-2 channel for +tech support. + +### The game isn't launching when I try to start a mission. Usually, this is caused by the mod files not being downloaded. Make sure you have run `/download_data` in the Archipelago tab before playing. @@ -183,12 +200,12 @@ Make sure that you are running an up-to-date version of the client. Check the [Archipelago Releases Page](https://github.com/ArchipelagoMW/Archipelago/releases) to look up what the latest version is (RC releases are not necessary; that stands for "Release Candidate"). -If these things are in order, check the log file for issues (stored at `[Archipelago Directory]/logs/Starcraft2Client.txt`). +If these things are in order, check the log file for issues (stored at `[Archipelago Directory]/logs/SC2Client_.txt`). If you can't figure out the log file, visit our [Discord's](https://discord.com/invite/8Z65BR2) tech-support channel for help. Please include a specific description of what's going wrong and attach your log file to your message. -## My keyboard shortcuts profile is not available when I play *StarCraft 2 Archipelago*. +### My keyboard shortcuts profile is not available when I play *StarCraft 2 Archipelago*. For your keyboard shortcuts profile to work in Archipelago, you need to copy your shortcuts file from `Documents/StarCraft II/Accounts/######/Hotkeys` to `Documents/StarCraft II/Hotkeys`. diff --git a/worlds/sc2/locations.py b/worlds/sc2/locations.py index 318d52f856..ddc188bad9 100644 --- a/worlds/sc2/locations.py +++ b/worlds/sc2/locations.py @@ -249,7 +249,6 @@ def get_locations(world: Optional["SC2World"]) -> Tuple[LocationData, ...]: LocationType.VICTORY, lambda state: ( logic.terran_common_unit(state) - and logic.terran_defense_rating(state, True) >= 2 and (adv_tactics or logic.terran_basic_anti_air(state)) ), ), @@ -271,10 +270,7 @@ def get_locations(world: Optional["SC2World"]) -> Tuple[LocationData, ...]: "Third Group Rescued", SC2WOL_LOC_ID_OFFSET + 303, LocationType.VANILLA, - lambda state: ( - logic.terran_common_unit(state) - and logic.terran_defense_rating(state, True) >= 2 - ), + logic.terran_common_unit, ), make_location_data( SC2Mission.ZERO_HOUR.mission_name, @@ -320,20 +316,14 @@ def get_locations(world: Optional["SC2World"]) -> Tuple[LocationData, ...]: "Hold Just a Little Longer", SC2WOL_LOC_ID_OFFSET + 309, LocationType.EXTRA, - lambda state: ( - logic.terran_common_unit(state) - and logic.terran_defense_rating(state, True) >= 2 - ), + logic.terran_common_unit, ), make_location_data( SC2Mission.ZERO_HOUR.mission_name, "Cavalry's on the Way", SC2WOL_LOC_ID_OFFSET + 310, LocationType.EXTRA, - lambda state: ( - logic.terran_common_unit(state) - and logic.terran_defense_rating(state, True) >= 2 - ), + logic.terran_common_unit, ), make_location_data( SC2Mission.EVACUATION.mission_name, diff --git a/worlds/sc2/pool_filter.py b/worlds/sc2/pool_filter.py index 0ffa08e010..abecf8264c 100644 --- a/worlds/sc2/pool_filter.py +++ b/worlds/sc2/pool_filter.py @@ -182,7 +182,7 @@ class ValidInventory: del self.logical_inventory[item.name] item.filter_flags |= remove_flag return "" - + def remove_child_items( parent_item: StarcraftItem, remove_flag: ItemFilterFlags = ItemFilterFlags.FilterExcluded, @@ -247,13 +247,13 @@ class ValidInventory: # Limit the maximum number of upgrades if max_upgrades_per_unit != -1: - for group_name, group_items in group_to_item.items(): - self.world.random.shuffle(group_to_item[group]) + for group_items in group_to_item.values(): + self.world.random.shuffle(group_items) cull_items_over_maximum(group_items, max_upgrades_per_unit) - + # Requesting minimum upgrades for items that have already been locked/placed when minimum required if min_upgrades_per_unit != -1: - for group_name, group_items in group_to_item.items(): + for group_items in group_to_item.values(): self.world.random.shuffle(group_items) request_minimum_items(group_items, min_upgrades_per_unit) @@ -349,7 +349,7 @@ class ValidInventory: ItemFilterFlags.Removed not in item.filter_flags and ((ItemFilterFlags.Unexcludable|ItemFilterFlags.Excluded) & item.filter_flags) != ItemFilterFlags.Excluded ) - + # Actually remove culled items; we won't re-add them inventory = [ item for item in inventory @@ -373,7 +373,7 @@ class ValidInventory: item for item in cullable_items if not ((ItemFilterFlags.Removed|ItemFilterFlags.Uncullable) & item.filter_flags) ] - + # Handle too many requested if current_inventory_size - start_inventory_size > inventory_size - filler_amount: for item in inventory: @@ -414,7 +414,7 @@ class ValidInventory: removable_transport_hooks = [item for item in inventory_transport_hooks if not (ItemFilterFlags.Unexcludable & item.filter_flags)] if len(inventory_transport_hooks) > 1 and removable_transport_hooks: inventory.remove(removable_transport_hooks[0]) - + # Weapon/Armour upgrades def exclude_wa(prefix: str) -> List[StarcraftItem]: return [ @@ -439,7 +439,7 @@ class ValidInventory: inventory = exclude_wa(item_names.PROTOSS_GROUND_UPGRADE_PREFIX) if used_item_names.isdisjoint(item_groups.protoss_air_wa): inventory = exclude_wa(item_names.PROTOSS_AIR_UPGRADE_PREFIX) - + # Part 4: Last-ditch effort to reduce inventory size; upgrades can go in start inventory current_inventory_size = len(inventory) precollect_items = current_inventory_size - inventory_size - start_inventory_size - filler_amount @@ -453,7 +453,7 @@ class ValidInventory: for item in promotable[:precollect_items]: item.filter_flags |= ItemFilterFlags.StartInventory start_inventory_size += 1 - + assert current_inventory_size - start_inventory_size <= inventory_size - filler_amount, ( f"Couldn't reduce inventory to fit. target={inventory_size}, poolsize={current_inventory_size}, " f"start_inventory={starcraft_item}, filler_amount={filler_amount}" diff --git a/worlds/sc2/regions.py b/worlds/sc2/regions.py index 4b02d294d1..299fcde3db 100644 --- a/worlds/sc2/regions.py +++ b/worlds/sc2/regions.py @@ -129,7 +129,7 @@ def adjust_mission_pools(world: 'SC2World', pools: SC2MOGenMissionPools): if grant_story_tech == GrantStoryTech.option_grant: # Additional starter mission if player is granted story tech pools.move_mission(SC2Mission.ENEMY_WITHIN, Difficulty.EASY, Difficulty.STARTER) - pools.move_mission(SC2Mission.THE_ESCAPE, Difficulty.MEDIUM, Difficulty.STARTER) + pools.move_mission(SC2Mission.THE_ESCAPE, Difficulty.EASY, Difficulty.STARTER) pools.move_mission(SC2Mission.IN_THE_ENEMY_S_SHADOW, Difficulty.MEDIUM, Difficulty.STARTER) if not war_council_nerfs or grant_story_tech == GrantStoryTech.option_grant: pools.move_mission(SC2Mission.TEMPLAR_S_RETURN, Difficulty.MEDIUM, Difficulty.STARTER) diff --git a/worlds/sc2/rules.py b/worlds/sc2/rules.py index 28a8804e5e..8d55c6a4e5 100644 --- a/worlds/sc2/rules.py +++ b/worlds/sc2/rules.py @@ -1660,11 +1660,11 @@ class SC2Logic: Created mainly for engine of destruction start, but works for other missions with no-build starts. """ return state.has_any(( - item_names.ZEALOT_WHIRLWIND, - item_names.SENTRY_DOUBLE_SHIELD_RECHARGE, - item_names.SLAYER_PHASE_BLINK, - item_names.STALKER_INSTIGATOR_SLAYER_DISINTEGRATING_PARTICLES, - item_names.STALKER_INSTIGATOR_SLAYER_PARTICLE_REFLECTION, + item_names.ZEALOT_WHIRLWIND, + item_names.SENTRY_DOUBLE_SHIELD_RECHARGE, + item_names.SLAYER_PHASE_BLINK, + item_names.STALKER_INSTIGATOR_SLAYER_DISINTEGRATING_PARTICLES, + item_names.STALKER_INSTIGATOR_SLAYER_PARTICLE_REFLECTION, ), self.player) # Mission-specific rules diff --git a/worlds/sc2/test/slow_tests.py b/worlds/sc2/test/slow_tests.py new file mode 100644 index 0000000000..90b6e7a982 --- /dev/null +++ b/worlds/sc2/test/slow_tests.py @@ -0,0 +1,52 @@ +""" +Slow-running tests that are run infrequently. +Run this file explicitly with `python3 -m unittest worlds.sc2.test.slow_tests` +""" +from .test_base import Sc2SetupTestBase + +from Fill import FillError +from .. import mission_tables, options + + +class LargeTests(Sc2SetupTestBase): + def test_any_starter_mission_works(self) -> None: + base_options = { + options.OPTION_NAME[options.SelectedRaces]: list(options.SelectedRaces.valid_keys), + options.OPTION_NAME[options.RequiredTactics]: options.RequiredTactics.option_standard, + options.OPTION_NAME[options.MissionOrder]: options.MissionOrder.option_custom, + options.OPTION_NAME[options.ExcludeOverpoweredItems]: True, + # options.OPTION_NAME[options.ExtraLocations]: options.ExtraLocations.option_disabled, + options.OPTION_NAME[options.VanillaLocations]: options.VanillaLocations.option_disabled, + } + missions_to_check = [ + mission for mission in mission_tables.SC2Mission + if mission.pool == mission_tables.MissionPools.STARTER + ] + failed_missions: list[tuple[mission_tables.SC2Mission, int]] = [] + NUM_ATTEMPTS = 3 + for mission in missions_to_check: + for attempt in range(NUM_ATTEMPTS): + mission_options = base_options | { + options.OPTION_NAME[options.CustomMissionOrder]: { + "Test Campaign": { + "Test Layout": { + "type": "hopscotch", + "size": 25, + "goal": True, + "missions": [ + {"index": 0, "mission_pool": [mission.mission_name]} + ] + } + } + } + } + try: + self.generate_world(mission_options) + self.fill_after_generation() + assert self.multiworld.worlds[1].custom_mission_order.get_starting_missions()[0] == mission + except FillError as ex: + failed_missions.append((mission, self.multiworld.seed)) + if failed_missions: + for failed_mission in failed_missions: + print(failed_mission) + self.assertFalse(failed_missions) diff --git a/worlds/sc2/test/test_base.py b/worlds/sc2/test/test_base.py index f0f778dc79..f6aaaddaba 100644 --- a/worlds/sc2/test/test_base.py +++ b/worlds/sc2/test/test_base.py @@ -1,4 +1,4 @@ -from typing import * +from typing import Any, cast import unittest import random from argparse import Namespace @@ -6,18 +6,11 @@ from BaseClasses import MultiWorld, CollectionState, PlandoOptions from Generate import get_seed_name from worlds import AutoWorld from test.general import gen_steps, call_all +from Fill import distribute_items_restrictive -from test.bases import WorldTestBase from .. import SC2World, SC2Campaign -from .. import client from .. import options -class Sc2TestBase(WorldTestBase): - game = client.SC2Context.game - world: SC2World - player: ClassVar[int] = 1 - skip_long_tests: bool = True - class Sc2SetupTestBase(unittest.TestCase): """ @@ -37,10 +30,11 @@ class Sc2SetupTestBase(unittest.TestCase): PROTOSS_CAMPAIGNS = { 'enabled_campaigns': {SC2Campaign.PROPHECY.campaign_name, SC2Campaign.PROLOGUE.campaign_name, SC2Campaign.LOTV.campaign_name,} } - seed: Optional[int] = None + seed: int | None = None game = SC2World.game player = 1 - def generate_world(self, options: Dict[str, Any]) -> None: + + def generate_world(self, options: dict[str, Any]) -> None: self.multiworld = MultiWorld(1) self.multiworld.game[self.player] = self.game self.multiworld.player_name = {self.player: "Tester"} @@ -63,3 +57,11 @@ class Sc2SetupTestBase(unittest.TestCase): except Exception as ex: ex.add_note(f"Seed: {self.multiworld.seed}") raise + + def fill_after_generation(self) -> None: + assert self.multiworld + try: + distribute_items_restrictive(self.multiworld) + except Exception as ex: + ex.add_note(f"Seed: {self.multiworld.seed}") + raise diff --git a/worlds/sc2/test/test_generation.py b/worlds/sc2/test/test_generation.py index 329cd593e1..708606f7bc 100644 --- a/worlds/sc2/test/test_generation.py +++ b/worlds/sc2/test/test_generation.py @@ -1,20 +1,24 @@ """ Unit tests for world generation """ -from typing import * - +from typing import Any from .test_base import Sc2SetupTestBase -from .. import mission_groups, mission_tables, options, locations, SC2Mission, SC2Campaign, SC2Race, unreleased_items, \ - RequiredTactics +from .. import ( + mission_groups, mission_tables, options, locations, + SC2Mission, SC2Campaign, SC2Race, unreleased_items, + RequiredTactics, +) from ..item import item_groups, item_tables, item_names from .. import get_all_missions, get_random_first_mission -from ..options import EnabledCampaigns, NovaGhostOfAChanceVariant, MissionOrder, ExcludeOverpoweredItems, \ - VanillaItemsOnly, MaximumCampaignSize +from ..options import ( + EnabledCampaigns, NovaGhostOfAChanceVariant, MissionOrder, ExcludeOverpoweredItems, + VanillaItemsOnly, MaximumCampaignSize, +) class TestItemFiltering(Sc2SetupTestBase): - def test_explicit_locks_excludes_interact_and_set_flags(self): + def test_explicit_locks_excludes_interact_and_set_flags(self) -> None: world_options = { **self.ALL_CAMPAIGNS, 'locked_items': { @@ -46,7 +50,7 @@ class TestItemFiltering(Sc2SetupTestBase): regen_biosteel_items = [x for x in itempool if x == item_names.PROGRESSIVE_REGENERATIVE_BIO_STEEL] self.assertEqual(len(regen_biosteel_items), 2) - def test_unexcludes_cancel_out_excludes(self): + def test_unexcludes_cancel_out_excludes(self) -> None: world_options = { 'grant_story_tech': options.GrantStoryTech.option_grant, 'excluded_items': { @@ -121,7 +125,7 @@ class TestItemFiltering(Sc2SetupTestBase): itempool = [item.name for item in self.multiworld.itempool] self.assertNotIn(item_names.MARINE, itempool) - def test_excluding_groups_excludes_all_items_in_group(self): + def test_excluding_groups_excludes_all_items_in_group(self) -> None: world_options = { 'excluded_items': { item_groups.ItemGroupNames.BARRACKS_UNITS.lower(): -1, @@ -133,7 +137,7 @@ class TestItemFiltering(Sc2SetupTestBase): for item_name in item_groups.barracks_units: self.assertNotIn(item_name, itempool) - def test_excluding_mission_groups_excludes_all_missions_in_group(self): + def test_excluding_mission_groups_excludes_all_missions_in_group(self) -> None: world_options = { **self.ZERG_CAMPAIGNS, 'enable_race_swap': options.EnableRaceSwapVariants.option_shuffle_all, @@ -164,7 +168,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.assertNotEqual(item_data.type, item_tables.TerranItemType.Nova_Gear) self.assertNotEqual(item_name, item_names.NOVA_PROGRESSIVE_STEALTH_SUIT_MODULE) - def test_starter_unit_populates_start_inventory(self): + def test_starter_unit_populates_start_inventory(self) -> None: world_options = { 'enabled_campaigns': { SC2Campaign.WOL.campaign_name, @@ -308,7 +312,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.generate_world(world_options) world_items = [(item.name, item_tables.item_table[item.name]) for item in self.multiworld.itempool] self.assertTrue(world_items) - occurrences: Dict[str, int] = {} + occurrences: dict[str, int] = {} for item_name, _ in world_items: if item_name in item_groups.terran_progressive_items: if item_name in item_groups.nova_equipment: @@ -528,7 +532,7 @@ class TestItemFiltering(Sc2SetupTestBase): Orbital command got replaced. The item is still there for backwards compatibility. It shouldn't be generated. """ - world_options = {} + world_options: dict[str, Any] = {} self.generate_world(world_options) itempool = [item.name for item in self.multiworld.itempool] @@ -595,7 +599,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.assertIn(speedrun_location_name, all_location_names) self.assertNotIn(speedrun_location_name, world_location_names) - def test_nco_and_wol_picks_correct_starting_mission(self): + def test_nco_and_wol_picks_correct_starting_mission(self) -> None: world_options = { 'mission_order': MissionOrder.option_vanilla, 'enabled_campaigns': { @@ -606,7 +610,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.generate_world(world_options) self.assertEqual(get_random_first_mission(self.world, self.world.custom_mission_order), mission_tables.SC2Mission.LIBERATION_DAY) - def test_excluding_mission_short_name_excludes_all_variants_of_mission(self): + def test_excluding_mission_short_name_excludes_all_variants_of_mission(self) -> None: world_options = { 'excluded_missions': [ mission_tables.SC2Mission.ZERO_HOUR.mission_name.split(" (")[0] @@ -625,7 +629,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.assertNotIn(mission_tables.SC2Mission.ZERO_HOUR_Z, missions) self.assertNotIn(mission_tables.SC2Mission.ZERO_HOUR_P, missions) - def test_excluding_mission_variant_excludes_just_that_variant(self): + def test_excluding_mission_variant_excludes_just_that_variant(self) -> None: world_options = { 'excluded_missions': [ mission_tables.SC2Mission.ZERO_HOUR.mission_name @@ -644,7 +648,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.assertIn(mission_tables.SC2Mission.ZERO_HOUR_Z, missions) self.assertIn(mission_tables.SC2Mission.ZERO_HOUR_P, missions) - def test_weapon_armor_upgrades(self): + def test_weapon_armor_upgrades(self) -> None: world_options = { # Vanilla WoL with all missions 'mission_order': options.MissionOrder.option_vanilla, @@ -682,7 +686,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.assertGreaterEqual(len(vehicle_weapon_items), 3) self.assertEqual(len(other_bundle_items), 0) - def test_weapon_armor_upgrades_with_bundles(self): + def test_weapon_armor_upgrades_with_bundles(self) -> None: world_options = { # Vanilla WoL with all missions 'mission_order': options.MissionOrder.option_vanilla, @@ -720,7 +724,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.assertGreaterEqual(len(vehicle_upgrade_items), 3) self.assertEqual(len(other_bundle_items), 0) - def test_weapon_armor_upgrades_all_in_air(self): + def test_weapon_armor_upgrades_all_in_air(self) -> None: world_options = { # Vanilla WoL with all missions 'mission_order': options.MissionOrder.option_vanilla, @@ -753,7 +757,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.assertGreaterEqual(len(vehicle_weapon_items), 3) self.assertGreaterEqual(len(ship_weapon_items), 3) - def test_weapon_armor_upgrades_generic_upgrade_missions(self): + def test_weapon_armor_upgrades_generic_upgrade_missions(self) -> None: """ Tests the case when there aren't enough missions in order to get required weapon/armor upgrades for logic requirements. @@ -782,7 +786,7 @@ class TestItemFiltering(Sc2SetupTestBase): # Under standard tactics you need to place L3 upgrades for available unit classes self.assertEqual(len(upgrade_items), 3) - def test_weapon_armor_upgrades_generic_upgrade_missions_no_logic(self): + def test_weapon_armor_upgrades_generic_upgrade_missions_no_logic(self) -> None: """ Tests the case when there aren't enough missions in order to get required weapon/armor upgrades for logic requirements. @@ -813,7 +817,7 @@ class TestItemFiltering(Sc2SetupTestBase): # No logic won't take the fallback to trigger self.assertEqual(len(upgrade_items), 0) - def test_weapon_armor_upgrades_generic_upgrade_missions_no_countermeasure_needed(self): + def test_weapon_armor_upgrades_generic_upgrade_missions_no_countermeasure_needed(self) -> None: world_options = { # Vanilla WoL with all missions 'mission_order': options.MissionOrder.option_vanilla, @@ -837,7 +841,7 @@ class TestItemFiltering(Sc2SetupTestBase): # No additional starting inventory item placement is needed self.assertEqual(len(upgrade_items), 0) - def test_kerrigan_levels_per_mission_triggering_pre_fill(self): + def test_kerrigan_levels_per_mission_triggering_pre_fill(self) -> None: world_options = { **self.ALL_CAMPAIGNS, 'mission_order': options.MissionOrder.option_custom, @@ -878,7 +882,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.assertGreater(len(kerrigan_1_stacks), 0) - def test_kerrigan_levels_per_mission_and_generic_upgrades_both_triggering_pre_fill(self): + def test_kerrigan_levels_per_mission_and_generic_upgrades_both_triggering_pre_fill(self) -> None: world_options = { **self.ALL_CAMPAIGNS, 'mission_order': options.MissionOrder.option_custom, @@ -925,7 +929,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.assertNotIn(item_names.KERRIGAN_LEVELS_70, itempool) self.assertNotIn(item_names.KERRIGAN_LEVELS_70, starting_inventory) - def test_locking_required_items(self): + def test_locking_required_items(self) -> None: world_options = { **self.ALL_CAMPAIGNS, 'mission_order': options.MissionOrder.option_custom, @@ -962,7 +966,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.assertIn(item_names.KERRIGAN_MEND, itempool) - def test_fully_balanced_mission_races(self): + def test_fully_balanced_mission_races(self) -> None: """ Tests whether fully balanced mission race balancing actually is fully balanced. """ @@ -1080,7 +1084,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.generate_world(world_options) itempool = [item.name for item in self.multiworld.itempool] - upgrade_item_counts: Dict[str, int] = {} + upgrade_item_counts: dict[str, int] = {} for item_name in itempool: if item_tables.item_table[item_name].type in ( item_tables.TerranItemType.Upgrade, @@ -1252,7 +1256,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.generate_world(world_options) itempool = [item.name for item in self.multiworld.itempool] - items_to_check: List[str] = unreleased_items + items_to_check: list[str] = unreleased_items for item in items_to_check: self.assertNotIn(item, itempool) @@ -1273,7 +1277,7 @@ class TestItemFiltering(Sc2SetupTestBase): self.generate_world(world_options) itempool = [item.name for item in self.multiworld.itempool] - items_to_check: List[str] = unreleased_items + items_to_check: list[str] = unreleased_items for item in items_to_check: self.assertIn(item, itempool) diff --git a/worlds/sc2/test/test_regions.py b/worlds/sc2/test/test_regions.py index 880a02f973..5d9870d894 100644 --- a/worlds/sc2/test/test_regions.py +++ b/worlds/sc2/test/test_regions.py @@ -1,9 +1,10 @@ import unittest -from .test_base import Sc2TestBase +from .test_base import Sc2SetupTestBase from .. import mission_tables, SC2Campaign from .. import options from ..mission_order.layout_types import Grid + class TestGridsizes(unittest.TestCase): def test_grid_sizes_meet_specs(self): self.assertTupleEqual((1, 2, 0), Grid.get_grid_dimensions(2)) @@ -24,17 +25,17 @@ class TestGridsizes(unittest.TestCase): self.assertTupleEqual((5, 7, 2), Grid.get_grid_dimensions(33)) -class TestGridGeneration(Sc2TestBase): - options = { - "mission_order": options.MissionOrder.option_grid, - "excluded_missions": [mission_tables.SC2Mission.ZERO_HOUR.mission_name,], - "enabled_campaigns": { - SC2Campaign.WOL.campaign_name, - SC2Campaign.PROPHECY.campaign_name, - } - } - +class TestGridGeneration(Sc2SetupTestBase): def test_size_matches_exclusions(self): + world_options = { + options.OPTION_NAME[options.MissionOrder]: options.MissionOrder.option_grid, + options.OPTION_NAME[options.ExcludedMissions]: [mission_tables.SC2Mission.ZERO_HOUR.mission_name], + options.OPTION_NAME[options.EnabledCampaigns]: { + SC2Campaign.WOL.campaign_name, + SC2Campaign.PROPHECY.campaign_name, + } + } + self.generate_world(world_options) self.assertNotIn(mission_tables.SC2Mission.ZERO_HOUR.mission_name, self.multiworld.regions) # WoL has 29 missions. -1 for Zero Hour being excluded, +1 for the automatically-added menu location self.assertEqual(len(self.multiworld.regions), 29)