Skip to content

Commit 5ca1aba

Browse files
authored
DLC Quest: Fix code structure, typos, poor code quality (#2066)
"Added a bunch of tests to make sure I don't break anything during refactoring Huge cleanup in the Regions file, extract methods, remove code duplicate, fix typos, fix variable naming conventions, etc. Small cleanup in other places, minor stuff just what was needed for Regions"
1 parent 11ebc52 commit 5ca1aba

File tree

7 files changed

+486
-308
lines changed

7 files changed

+486
-308
lines changed

worlds/dlcquest/Regions.py

+169-308
Large diffs are not rendered by default.
+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
from . import DLCQuestTestBase
2+
from .. import Options
3+
4+
sword = "Sword"
5+
gun = "Gun"
6+
wooden_sword = "Wooden Sword"
7+
pickaxe = "Pickaxe"
8+
humble_bindle = "Humble Indie Bindle"
9+
box_supplies = "Box of Various Supplies"
10+
items = [sword, gun, wooden_sword, pickaxe, humble_bindle, box_supplies]
11+
12+
important_pack = "Incredibly Important Pack"
13+
14+
15+
class TestItemShuffle(DLCQuestTestBase):
16+
options = {Options.ItemShuffle.internal_name: Options.ItemShuffle.option_shuffled,
17+
Options.Campaign.internal_name: Options.Campaign.option_both}
18+
19+
def test_items_in_pool(self):
20+
item_names = {item.name for item in self.multiworld.get_items()}
21+
for item in items:
22+
with self.subTest(f"{item}"):
23+
self.assertIn(item, item_names)
24+
25+
def test_item_locations_in_pool(self):
26+
location_names = {location.name for location in self.multiworld.get_locations()}
27+
for item_location in items:
28+
with self.subTest(f"{item_location}"):
29+
self.assertIn(item_location, location_names)
30+
31+
def test_sword_location_has_correct_rules(self):
32+
self.assertFalse(self.can_reach_location(sword))
33+
movement_pack = self.multiworld.create_item("Movement Pack", self.player)
34+
self.collect(movement_pack)
35+
self.assertFalse(self.can_reach_location(sword))
36+
time_pack = self.multiworld.create_item("Time is Money Pack", self.player)
37+
self.collect(time_pack)
38+
self.assertTrue(self.can_reach_location(sword))
39+
40+
def test_gun_location_has_correct_rules(self):
41+
self.assertFalse(self.can_reach_location(gun))
42+
movement_pack = self.multiworld.create_item("Movement Pack", self.player)
43+
self.collect(movement_pack)
44+
self.assertFalse(self.can_reach_location(gun))
45+
sword_item = self.multiworld.create_item(sword, self.player)
46+
self.collect(sword_item)
47+
self.assertFalse(self.can_reach_location(gun))
48+
gun_pack = self.multiworld.create_item("Gun Pack", self.player)
49+
self.collect(gun_pack)
50+
self.assertTrue(self.can_reach_location(gun))
51+
52+
def test_wooden_sword_location_has_correct_rules(self):
53+
self.assertFalse(self.can_reach_location(wooden_sword))
54+
important_pack_item = self.multiworld.create_item(important_pack, self.player)
55+
self.collect(important_pack_item)
56+
self.assertTrue(self.can_reach_location(wooden_sword))
57+
58+
def test_bindle_location_has_correct_rules(self):
59+
self.assertFalse(self.can_reach_location(humble_bindle))
60+
wooden_sword_item = self.multiworld.create_item(wooden_sword, self.player)
61+
self.collect(wooden_sword_item)
62+
self.assertFalse(self.can_reach_location(humble_bindle))
63+
plants_pack = self.multiworld.create_item("Harmless Plants Pack", self.player)
64+
self.collect(plants_pack)
65+
self.assertFalse(self.can_reach_location(humble_bindle))
66+
wall_jump_pack = self.multiworld.create_item("Wall Jump Pack", self.player)
67+
self.collect(wall_jump_pack)
68+
self.assertFalse(self.can_reach_location(humble_bindle))
69+
name_change_pack = self.multiworld.create_item("Name Change Pack", self.player)
70+
self.collect(name_change_pack)
71+
self.assertFalse(self.can_reach_location(humble_bindle))
72+
cut_content_pack = self.multiworld.create_item("Cut Content Pack", self.player)
73+
self.collect(cut_content_pack)
74+
self.assertFalse(self.can_reach_location(humble_bindle))
75+
box_supplies_item = self.multiworld.create_item(box_supplies, self.player)
76+
self.collect(box_supplies_item)
77+
self.assertTrue(self.can_reach_location(humble_bindle))
78+
79+
def test_box_supplies_location_has_correct_rules(self):
80+
self.assertFalse(self.can_reach_location(box_supplies))
81+
wooden_sword_item = self.multiworld.create_item(wooden_sword, self.player)
82+
self.collect(wooden_sword_item)
83+
self.assertFalse(self.can_reach_location(box_supplies))
84+
plants_pack = self.multiworld.create_item("Harmless Plants Pack", self.player)
85+
self.collect(plants_pack)
86+
self.assertFalse(self.can_reach_location(box_supplies))
87+
wall_jump_pack = self.multiworld.create_item("Wall Jump Pack", self.player)
88+
self.collect(wall_jump_pack)
89+
self.assertFalse(self.can_reach_location(box_supplies))
90+
name_change_pack = self.multiworld.create_item("Name Change Pack", self.player)
91+
self.collect(name_change_pack)
92+
self.assertFalse(self.can_reach_location(box_supplies))
93+
cut_content_pack = self.multiworld.create_item("Cut Content Pack", self.player)
94+
self.collect(cut_content_pack)
95+
self.assertTrue(self.can_reach_location(box_supplies))
96+
97+
def test_pickaxe_location_has_correct_rules(self):
98+
self.assertFalse(self.can_reach_location(pickaxe))
99+
wooden_sword_item = self.multiworld.create_item(wooden_sword, self.player)
100+
self.collect(wooden_sword_item)
101+
self.assertFalse(self.can_reach_location(pickaxe))
102+
plants_pack = self.multiworld.create_item("Harmless Plants Pack", self.player)
103+
self.collect(plants_pack)
104+
self.assertFalse(self.can_reach_location(pickaxe))
105+
wall_jump_pack = self.multiworld.create_item("Wall Jump Pack", self.player)
106+
self.collect(wall_jump_pack)
107+
self.assertFalse(self.can_reach_location(pickaxe))
108+
name_change_pack = self.multiworld.create_item("Name Change Pack", self.player)
109+
self.collect(name_change_pack)
110+
self.assertFalse(self.can_reach_location(pickaxe))
111+
bindle_item = self.multiworld.create_item("Humble Indie Bindle", self.player)
112+
self.collect(bindle_item)
113+
self.assertTrue(self.can_reach_location(pickaxe))
114+
115+
116+
class TestNoItemShuffle(DLCQuestTestBase):
117+
options = {Options.ItemShuffle.internal_name: Options.ItemShuffle.option_disabled,
118+
Options.Campaign.internal_name: Options.Campaign.option_both}
119+
120+
def test_items_not_in_pool(self):
121+
item_names = {item.name for item in self.multiworld.get_items()}
122+
for item in items:
123+
with self.subTest(f"{item}"):
124+
self.assertNotIn(item, item_names)
125+
126+
def test_item_locations_not_in_pool(self):
127+
location_names = {location.name for location in self.multiworld.get_locations()}
128+
for item_location in items:
129+
with self.subTest(f"{item_location}"):
130+
self.assertNotIn(item_location, location_names)
+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
from typing import Dict
2+
3+
from BaseClasses import MultiWorld
4+
from Options import SpecialRange
5+
from .option_names import options_to_include
6+
from .checks.world_checks import assert_can_win, assert_same_number_items_locations
7+
from . import DLCQuestTestBase, setup_dlc_quest_solo_multiworld
8+
from ... import AutoWorldRegister
9+
10+
11+
def basic_checks(tester: DLCQuestTestBase, multiworld: MultiWorld):
12+
assert_can_win(tester, multiworld)
13+
assert_same_number_items_locations(tester, multiworld)
14+
15+
16+
def get_option_choices(option) -> Dict[str, int]:
17+
if issubclass(option, SpecialRange):
18+
return option.special_range_names
19+
elif option.options:
20+
return option.options
21+
return {}
22+
23+
24+
class TestGenerateDynamicOptions(DLCQuestTestBase):
25+
def test_given_option_pair_when_generate_then_basic_checks(self):
26+
num_options = len(options_to_include)
27+
for option1_index in range(0, num_options):
28+
for option2_index in range(option1_index + 1, num_options):
29+
option1 = options_to_include[option1_index]
30+
option2 = options_to_include[option2_index]
31+
option1_choices = get_option_choices(option1)
32+
option2_choices = get_option_choices(option2)
33+
for key1 in option1_choices:
34+
for key2 in option2_choices:
35+
with self.subTest(f"{option1.internal_name}: {key1}, {option2.internal_name}: {key2}"):
36+
choices = {option1.internal_name: option1_choices[key1],
37+
option2.internal_name: option2_choices[key2]}
38+
multiworld = setup_dlc_quest_solo_multiworld(choices)
39+
basic_checks(self, multiworld)
40+
41+
def test_given_option_truple_when_generate_then_basic_checks(self):
42+
num_options = len(options_to_include)
43+
for option1_index in range(0, num_options):
44+
for option2_index in range(option1_index + 1, num_options):
45+
for option3_index in range(option2_index + 1, num_options):
46+
option1 = options_to_include[option1_index]
47+
option2 = options_to_include[option2_index]
48+
option3 = options_to_include[option3_index]
49+
option1_choices = get_option_choices(option1)
50+
option2_choices = get_option_choices(option2)
51+
option3_choices = get_option_choices(option3)
52+
for key1 in option1_choices:
53+
for key2 in option2_choices:
54+
for key3 in option3_choices:
55+
with self.subTest(f"{option1.internal_name}: {key1}, {option2.internal_name}: {key2}, {option3.internal_name}: {key3}"):
56+
choices = {option1.internal_name: option1_choices[key1],
57+
option2.internal_name: option2_choices[key2],
58+
option3.internal_name: option3_choices[key3]}
59+
multiworld = setup_dlc_quest_solo_multiworld(choices)
60+
basic_checks(self, multiworld)
61+
62+
def test_given_option_quartet_when_generate_then_basic_checks(self):
63+
num_options = len(options_to_include)
64+
for option1_index in range(0, num_options):
65+
for option2_index in range(option1_index + 1, num_options):
66+
for option3_index in range(option2_index + 1, num_options):
67+
for option4_index in range(option3_index + 1, num_options):
68+
option1 = options_to_include[option1_index]
69+
option2 = options_to_include[option2_index]
70+
option3 = options_to_include[option3_index]
71+
option4 = options_to_include[option4_index]
72+
option1_choices = get_option_choices(option1)
73+
option2_choices = get_option_choices(option2)
74+
option3_choices = get_option_choices(option3)
75+
option4_choices = get_option_choices(option4)
76+
for key1 in option1_choices:
77+
for key2 in option2_choices:
78+
for key3 in option3_choices:
79+
for key4 in option4_choices:
80+
with self.subTest(
81+
f"{option1.internal_name}: {key1}, {option2.internal_name}: {key2}, {option3.internal_name}: {key3}, {option4.internal_name}: {key4}"):
82+
choices = {option1.internal_name: option1_choices[key1],
83+
option2.internal_name: option2_choices[key2],
84+
option3.internal_name: option3_choices[key3],
85+
option4.internal_name: option4_choices[key4]}
86+
multiworld = setup_dlc_quest_solo_multiworld(choices)
87+
basic_checks(self, multiworld)

worlds/dlcquest/test/__init__.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from typing import ClassVar
2+
3+
from typing import Dict, FrozenSet, Tuple, Any
4+
from argparse import Namespace
5+
6+
from BaseClasses import MultiWorld
7+
from test.TestBase import WorldTestBase
8+
from .. import DLCqworld
9+
from test.general import gen_steps, setup_solo_multiworld as setup_base_solo_multiworld
10+
from worlds.AutoWorld import call_all
11+
12+
13+
class DLCQuestTestBase(WorldTestBase):
14+
game = "DLCQuest"
15+
world: DLCqworld
16+
player: ClassVar[int] = 1
17+
18+
def world_setup(self, *args, **kwargs):
19+
super().world_setup(*args, **kwargs)
20+
if self.constructed:
21+
self.world = self.multiworld.worlds[self.player] # noqa
22+
23+
@property
24+
def run_default_tests(self) -> bool:
25+
# world_setup is overridden, so it'd always run default tests when importing DLCQuestTestBase
26+
is_not_dlc_test = type(self) is not DLCQuestTestBase
27+
should_run_default_tests = is_not_dlc_test and super().run_default_tests
28+
return should_run_default_tests
29+
30+
31+
def setup_dlc_quest_solo_multiworld(test_options=None, seed=None, _cache: Dict[FrozenSet[Tuple[str, Any]], MultiWorld] = {}) -> MultiWorld: #noqa
32+
if test_options is None:
33+
test_options = {}
34+
35+
# Yes I reuse the worlds generated between tests, its speeds the execution by a couple seconds
36+
frozen_options = frozenset(test_options.items()).union({seed})
37+
if frozen_options in _cache:
38+
return _cache[frozen_options]
39+
40+
multiworld = setup_base_solo_multiworld(DLCqworld, ())
41+
multiworld.set_seed(seed)
42+
# print(f"Seed: {multiworld.seed}") # Uncomment to print the seed for every test
43+
args = Namespace()
44+
for name, option in DLCqworld.options_dataclass.type_hints.items():
45+
value = option(test_options[name]) if name in test_options else option.from_any(option.default)
46+
setattr(args, name, {1: value})
47+
multiworld.set_options(args)
48+
for step in gen_steps:
49+
call_all(multiworld, step)
50+
51+
_cache[frozen_options] = multiworld
52+
53+
return multiworld

worlds/dlcquest/test/checks/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from typing import List
2+
3+
from BaseClasses import MultiWorld, ItemClassification
4+
from .. import DLCQuestTestBase
5+
from ... import Options
6+
7+
8+
def get_all_item_names(multiworld: MultiWorld) -> List[str]:
9+
return [item.name for item in multiworld.itempool]
10+
11+
12+
def get_all_location_names(multiworld: MultiWorld) -> List[str]:
13+
return [location.name for location in multiworld.get_locations() if not location.event]
14+
15+
16+
def assert_victory_exists(tester: DLCQuestTestBase, multiworld: MultiWorld):
17+
campaign = multiworld.campaign[1]
18+
all_items = [item.name for item in multiworld.get_items()]
19+
if campaign == Options.Campaign.option_basic or campaign == Options.Campaign.option_both:
20+
tester.assertIn("Victory Basic", all_items)
21+
if campaign == Options.Campaign.option_live_freemium_or_die or campaign == Options.Campaign.option_both:
22+
tester.assertIn("Victory Freemium", all_items)
23+
24+
25+
def collect_all_then_assert_can_win(tester: DLCQuestTestBase, multiworld: MultiWorld):
26+
for item in multiworld.get_items():
27+
multiworld.state.collect(item)
28+
campaign = multiworld.campaign[1]
29+
if campaign == Options.Campaign.option_basic or campaign == Options.Campaign.option_both:
30+
tester.assertTrue(multiworld.find_item("Victory Basic", 1).can_reach(multiworld.state))
31+
if campaign == Options.Campaign.option_live_freemium_or_die or campaign == Options.Campaign.option_both:
32+
tester.assertTrue(multiworld.find_item("Victory Freemium", 1).can_reach(multiworld.state))
33+
34+
35+
def assert_can_win(tester: DLCQuestTestBase, multiworld: MultiWorld):
36+
assert_victory_exists(tester, multiworld)
37+
collect_all_then_assert_can_win(tester, multiworld)
38+
39+
40+
def assert_same_number_items_locations(tester: DLCQuestTestBase, multiworld: MultiWorld):
41+
non_event_locations = [location for location in multiworld.get_locations() if not location.event]
42+
tester.assertEqual(len(multiworld.itempool), len(non_event_locations))

worlds/dlcquest/test/option_names.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from .. import DLCqworld
2+
3+
options_to_exclude = ["progression_balancing", "accessibility", "start_inventory", "start_hints", "death_link"]
4+
options_to_include = [option for option_name, option in DLCqworld.options_dataclass.type_hints.items()
5+
if option_name not in options_to_exclude]

0 commit comments

Comments
 (0)