From 3fdf07677cc956d62785d0ae30f8148e583dd9dd Mon Sep 17 00:00:00 2001 From: agilbert1412 Date: Fri, 14 Apr 2023 23:42:02 -0400 Subject: [PATCH] Stardew Valley: Removed Pytest Requirement from everything (#1696) --- worlds/stardew_valley/logic.py | 29 ++-- worlds/stardew_valley/requirements.txt | 2 +- worlds/stardew_valley/test/TestLogic.py | 89 ++++++++---- worlds/stardew_valley/test/TestOptions.py | 157 +++++++++++----------- 4 files changed, 158 insertions(+), 119 deletions(-) diff --git a/worlds/stardew_valley/logic.py b/worlds/stardew_valley/logic.py index c4d157b8..41f83572 100644 --- a/worlds/stardew_valley/logic.py +++ b/worlds/stardew_valley/logic.py @@ -103,7 +103,7 @@ class StardewLogic: item_rules: Dict[str, StardewRule] = field(default_factory=dict) tree_fruit_rules: Dict[str, StardewRule] = field(default_factory=dict) seed_rules: Dict[str, StardewRule] = field(default_factory=dict) - crops_rules: Dict[str, StardewRule] = field(default_factory=dict) + crop_rules: Dict[str, StardewRule] = field(default_factory=dict) fish_rules: Dict[str, StardewRule] = field(default_factory=dict) museum_rules: Dict[str, StardewRule] = field(default_factory=dict) building_rules: Dict[str, StardewRule] = field(default_factory=dict) @@ -127,8 +127,8 @@ class StardewLogic: }) self.seed_rules.update({seed.name: self.can_buy_seed(seed) for seed in all_purchasable_seeds}) - self.crops_rules.update({crop.name: self.can_grow_crop(crop) for crop in all_crops}) - self.crops_rules.update({ + self.crop_rules.update({crop.name: self.can_grow_crop(crop) for crop in all_crops}) + self.crop_rules.update({ "Coffee Bean": (self.has_season("Spring") | self.has_season("Summer")) & self.has_traveling_merchant(), }) @@ -422,7 +422,7 @@ class StardewLogic: self.item_rules.update(self.museum_rules) self.item_rules.update(self.tree_fruit_rules) self.item_rules.update(self.seed_rules) - self.item_rules.update(self.crops_rules) + self.item_rules.update(self.crop_rules) self.building_rules.update({ "Barn": self.can_spend_money(6000) & self.has(["Wood", "Stone"]), @@ -795,6 +795,10 @@ class StardewLogic: if self.options[options.Friendsanity] == options.Friendsanity.option_none: return self.can_earn_relationship(npc, hearts) if npc not in all_villagers_by_name: + if npc == "Pet": + if self.options[options.Friendsanity] == options.Friendsanity.option_bachelors: + return self.can_befriend_pet(hearts) + return self.received(f"Pet: 1 <3", hearts) if npc == "Any" or npc == "Bachelor": possible_friends = [] for name in all_villagers_by_name: @@ -806,6 +810,11 @@ class StardewLogic: for name in all_villagers_by_name: mandatory_friends.append(self.has_relationship(name, hearts)) return And(mandatory_friends) + if npc.isnumeric(): + possible_friends = [] + for name in all_villagers_by_name: + possible_friends.append(self.has_relationship(name, hearts)) + return Count(int(npc), possible_friends) return self.can_earn_relationship(npc, hearts) villager = all_villagers_by_name[npc] @@ -888,9 +897,9 @@ class StardewLogic: # Catching every fish not expected # Shipping every item not expected self.can_get_married() & self.has_house(2), - self.has_lived_months(3), # 5 Friends (TODO) - self.has_lived_months(4), # 10 friends (TODO) - self.can_befriend_pet(5), # Max Pet + self.has_relationship("5", 8), # 5 Friends + self.has_relationship("10", 8), # 10 friends + self.has_relationship("Pet", 5), # Max Pet self.can_complete_community_center(), # Community Center Completion self.can_complete_community_center(), # CC Ceremony first point self.can_complete_community_center(), # CC Ceremony second point @@ -940,7 +949,11 @@ class StardewLogic: return region_rule & geodes_rule # & monster_rule & extra_rule def can_complete_museum(self) -> StardewRule: - rules = [self.can_mine_perfectly_in_the_skull_cavern(), self.received("Traveling Merchant Metal Detector", 4)] + rules = [self.can_mine_perfectly_in_the_skull_cavern()] + + if self.options[options.Museumsanity] != options.Museumsanity.option_none: + rules.append(self.received("Traveling Merchant Metal Detector", 4)) + for donation in all_museum_items: rules.append(self.can_find_museum_item(donation)) return And(rules) diff --git a/worlds/stardew_valley/requirements.txt b/worlds/stardew_valley/requirements.txt index b0922176..a7141f6a 100644 --- a/worlds/stardew_valley/requirements.txt +++ b/worlds/stardew_valley/requirements.txt @@ -1 +1 @@ -importlib_resources; python_version <= '3.8' +importlib_resources; python_version <= '3.8' \ No newline at end of file diff --git a/worlds/stardew_valley/test/TestLogic.py b/worlds/stardew_valley/test/TestLogic.py index beb61023..e1c5d215 100644 --- a/worlds/stardew_valley/test/TestLogic.py +++ b/worlds/stardew_valley/test/TestLogic.py @@ -1,4 +1,4 @@ -import pytest +import unittest from test.general import setup_solo_multiworld from .. import StardewValleyWorld, StardewLocation @@ -18,40 +18,71 @@ def collect_all(mw): collect_all(multi_world) -@pytest.mark.parametrize("bundle_item", all_bundle_items_except_money, - ids=[i.item.name for i in all_bundle_items_except_money]) -def test_given_bundle_item_then_is_available_in_logic(bundle_item: BundleItem): - assert bundle_item.item.name in logic.item_rules +class TestLogic(unittest.TestCase): + def test_given_bundle_item_then_is_available_in_logic(self): + for bundle_item in all_bundle_items_except_money: + with self.subTest(msg=bundle_item.item.name): + assert bundle_item.item.name in logic.item_rules + def test_given_item_rule_then_can_be_resolved(self): + for item in logic.item_rules.keys(): + with self.subTest(msg=item): + rule = logic.item_rules[item] + assert MISSING_ITEM not in repr(rule) + assert rule == False_() or rule(multi_world.state), f"Could not resolve item rule for {item} {rule}" -@pytest.mark.parametrize("item", logic.item_rules.keys(), ids=logic.item_rules.keys()) -def test_given_item_rule_then_can_be_resolved(item: str): - rule = logic.item_rules[item] + def test_given_building_rule_then_can_be_resolved(self): + for building in logic.building_rules.keys(): + with self.subTest(msg=building): + rule = logic.building_rules[building] + assert MISSING_ITEM not in repr(rule) + assert rule == False_() or rule(multi_world.state), f"Could not resolve building rule for {building} {rule}" - assert MISSING_ITEM not in repr(rule) - assert rule == False_() or rule(multi_world.state), f"Could not resolve rule for {item} {rule}" + def test_given_quest_rule_then_can_be_resolved(self): + for quest in logic.quest_rules.keys(): + with self.subTest(msg=quest): + rule = logic.quest_rules[quest] + assert MISSING_ITEM not in repr(rule) + assert rule == False_() or rule(multi_world.state), f"Could not resolve quest rule for {quest} {rule}" + def test_given_tree_fruit_rule_then_can_be_resolved(self): + for tree_fruit in logic.tree_fruit_rules.keys(): + with self.subTest(msg=tree_fruit): + rule = logic.tree_fruit_rules[tree_fruit] + assert MISSING_ITEM not in repr(rule) + assert rule == False_() or rule(multi_world.state), f"Could not resolve tree fruit rule for {tree_fruit} {rule}" -@pytest.mark.parametrize("item", logic.building_rules.keys(), ids=logic.building_rules.keys()) -def test_given_building_rule_then_can_be_resolved(item: str): - rule = logic.building_rules[item] + def test_given_seed_rule_then_can_be_resolved(self): + for seed in logic.seed_rules.keys(): + with self.subTest(msg=seed): + rule = logic.seed_rules[seed] + assert MISSING_ITEM not in repr(rule) + assert rule == False_() or rule(multi_world.state), f"Could not resolve seed rule for {seed} {rule}" - assert MISSING_ITEM not in repr(rule) - assert rule == False_() or rule(multi_world.state), f"Could not resolve rule for {item} {rule}" + def test_given_crop_rule_then_can_be_resolved(self): + for crop in logic.crop_rules.keys(): + with self.subTest(msg=crop): + rule = logic.crop_rules[crop] + assert MISSING_ITEM not in repr(rule) + assert rule == False_() or rule(multi_world.state), f"Could not resolve crop rule for {crop} {rule}" + def test_given_fish_rule_then_can_be_resolved(self): + for fish in logic.fish_rules.keys(): + with self.subTest(msg=fish): + rule = logic.fish_rules[fish] + assert MISSING_ITEM not in repr(rule) + assert rule == False_() or rule(multi_world.state), f"Could not resolve fish rule for {fish} {rule}" -@pytest.mark.parametrize("item", logic.quest_rules.keys(), ids=logic.quest_rules.keys()) -def test_given_quest_rule_then_can_be_resolved(item: str): - rule = logic.quest_rules[item] + def test_given_museum_rule_then_can_be_resolved(self): + for donation in logic.museum_rules.keys(): + with self.subTest(msg=donation): + rule = logic.museum_rules[donation] + assert MISSING_ITEM not in repr(rule) + assert rule == False_() or rule(multi_world.state), f"Could not resolve museum rule for {donation} {rule}" - assert MISSING_ITEM not in repr(rule) - assert rule == False_() or rule(multi_world.state), f"Could not resolve rule for {item} {rule}" - - -@pytest.mark.parametrize("location", multi_world.get_locations(1), - ids=[loc.name for loc in multi_world.get_locations(1)]) -def test_given_location_rule_then_can_be_resolved(location: StardewLocation): - rule = location.access_rule - - assert MISSING_ITEM not in repr(rule) - assert rule == False_() or rule(multi_world.state), f"Could not resolve rule for {location} {rule}" + def test_given_location_rule_then_can_be_resolved(self): + for location in multi_world.get_locations(1): + with self.subTest(msg=location.name): + rule = location.access_rule + assert MISSING_ITEM not in repr(rule) + assert rule == False_() or rule(multi_world.state), f"Could not resolve location rule for {location} {rule}" diff --git a/worlds/stardew_valley/test/TestOptions.py b/worlds/stardew_valley/test/TestOptions.py index 9907b45d..2d946e2f 100644 --- a/worlds/stardew_valley/test/TestOptions.py +++ b/worlds/stardew_valley/test/TestOptions.py @@ -1,10 +1,9 @@ import itertools - -import pytest +import unittest from BaseClasses import ItemClassification, MultiWorld from Options import SpecialRange -from . import setup_solo_multiworld +from . import setup_solo_multiworld, SVTestBase from .. import StardewItem, options from ..options import StardewOption, stardew_valley_option_classes @@ -12,142 +11,138 @@ SEASONS = {"Spring", "Summer", "Fall", "Winter"} TOOLS = {"Hoe", "Pickaxe", "Axe", "Watering Can", "Trash Can", "Fishing Rod"} -def basic_checks(multi_world: MultiWorld): - assert StardewItem("Victory", ItemClassification.progression, None, 1) in multi_world.get_items() - assert_can_win(multi_world) - assert len(multi_world.itempool) == len( - [location for location in multi_world.get_locations() if not location.event]) +def assert_can_win(multiworld: MultiWorld): + for item in multiworld.get_items(): + multiworld.state.collect(item) + + assert multiworld.find_item("Victory", 1).can_reach(multiworld.state) -def assert_can_win(multi_world: MultiWorld): - for item in multi_world.get_items(): - multi_world.state.collect(item) - - assert multi_world.find_item("Victory", 1).can_reach(multi_world.state) +def basic_checks(multiworld: MultiWorld): + assert StardewItem("Victory", ItemClassification.progression, None, 1) in multiworld.get_items() + assert_can_win(multiworld) + assert len(multiworld.itempool) == len( + [location for location in multiworld.get_locations() if not location.event]) -@pytest.mark.parametrize("option, value", [(option, value) - for option in stardew_valley_option_classes - if issubclass(option, SpecialRange) - for value in option.special_range_names]) -def test_given_special_range_when_generate_then_basic_checks(option: (SpecialRange, StardewOption), value): - multi_world = setup_solo_multiworld({option.internal_name: option.special_range_names[value]}) +class TestGenerateDynamicOptions(SVTestBase): + def test_given_special_range_when_generate_then_basic_checks(self): + for option in stardew_valley_option_classes: + if not issubclass(option, SpecialRange): + continue + with self.subTest(msg=option.internal_name): + for value in option.special_range_names: + multiworld = setup_solo_multiworld({option.internal_name: option.special_range_names[value]}) + basic_checks(multiworld) - basic_checks(multi_world) + def test_given_choice_when_generate_then_basic_checks(self): + for option in stardew_valley_option_classes: + if not option.options: + continue + with self.subTest(msg=option.internal_name): + for value in option.options: + multiworld = setup_solo_multiworld({option.internal_name: option.options[value]}) + basic_checks(multiworld) + + def test_given_option_combination_when_generate_then_basic_checks(self): + option_combinations = [{options.Goal.internal_name: options.Goal.option_master_angler, + options.ToolProgression.internal_name: options.ToolProgression.option_vanilla}] + ids = ["Master Angler + Vanilla tools"] + + for i in range(0, len(option_combinations)): + option_combination = option_combinations[i] + id = ids[i] + with self.subTest(msg=f"{id}"): + multi_world = setup_solo_multiworld(option_combination) + basic_checks(multi_world) -@pytest.mark.parametrize("option, value", [(option, value) - for option in stardew_valley_option_classes - if option.options - for value in option.options]) -def test_given_choice_when_generate_then_basic_checks(option, value): - multi_world = setup_solo_multiworld({option.internal_name: option.options[value]}) - - basic_checks(multi_world) - - -@pytest.mark.parametrize("option_combination", - [{options.Goal.internal_name: options.Goal.option_master_angler, - options.ToolProgression.internal_name: options.ToolProgression.option_vanilla}], - ids=["Master Angler + Vanilla tools"]) -def test_given_option_combination_when_generate_then_basic_checks(option_combination): - multi_world = setup_solo_multiworld(option_combination) - - basic_checks(multi_world) - - -class TestGoal: - @pytest.mark.parametrize("goal,location", [("community_center", "Complete Community Center"), +class TestGoal(SVTestBase): + def test_given_goal_when_generate_then_victory_is_in_correct_location(self): + for goal, location in [("community_center", "Complete Community Center"), ("grandpa_evaluation", "Succeed Grandpa's Evaluation"), ("bottom_of_the_mines", "Reach the Bottom of The Mines"), ("cryptic_note", "Complete Quest Cryptic Note"), - ("master_angler", "Catch Every Fish")]) - def test_given_goal_when_generate_then_victory_is_in_correct_location(self, goal, location): - multi_world = setup_solo_multiworld({options.Goal.internal_name: options.Goal.options[goal]}) - victory = multi_world.find_item("Victory", 1) - - assert victory.name == location + ("master_angler", "Catch Every Fish")]: + with self.subTest(msg=f"Goal: {goal}, Location: {location}"): + world_options = {options.Goal.internal_name: options.Goal.options[goal]} + multi_world = setup_solo_multiworld(world_options) + victory = multi_world.find_item("Victory", 1) + assert victory.name == location -class TestSeasonRandomization: +class TestSeasonRandomization(SVTestBase): def test_given_disabled_when_generate_then_all_seasons_are_precollected(self): - multi_world = setup_solo_multiworld({options.SeasonRandomization.internal_name: - options.SeasonRandomization.option_disabled}) + world_options = {options.SeasonRandomization.internal_name: options.SeasonRandomization.option_disabled} + multi_world = setup_solo_multiworld(world_options) precollected_items = {item.name for item in multi_world.precollected_items[1]} assert all([season in precollected_items for season in SEASONS]) - @pytest.mark.parametrize("value", [value for value in options.SeasonRandomization.options if "randomized" in value]) - def test_given_randomized_when_generate_then_all_seasons_are_in_the_pool_or_precollected(self, value): - multi_world = setup_solo_multiworld({options.SeasonRandomization.internal_name: - options.SeasonRandomization.options[value]}) - + def test_given_randomized_when_generate_then_all_seasons_are_in_the_pool_or_precollected(self): + world_options = {options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized} + multi_world = setup_solo_multiworld(world_options) precollected_items = {item.name for item in multi_world.precollected_items[1]} items = {item.name for item in multi_world.get_items()} | precollected_items assert all([season in items for season in SEASONS]) assert len(SEASONS.intersection(precollected_items)) == 1 def test_given_progressive_when_generate_then_3_progressive_seasons_are_in_the_pool(self): - multi_world = setup_solo_multiworld({options.SeasonRandomization.internal_name: - options.SeasonRandomization.option_progressive}) + world_options = {options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive} + multi_world = setup_solo_multiworld(world_options) items = [item.name for item in multi_world.get_items()] assert items.count("Progressive Season") == 3 -class TestBackpackProgression: +class TestBackpackProgression(SVTestBase): def test_given_vanilla_when_generate_then_no_backpack_in_pool(self): - multi_world = setup_solo_multiworld({options.BackpackProgression.internal_name: - options.BackpackProgression.option_vanilla}) + world_options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_vanilla} + multi_world = setup_solo_multiworld(world_options) assert "Progressive Backpack" not in {item.name for item in multi_world.get_items()} - @pytest.mark.parametrize("value", - [value for value in options.BackpackProgression.options if "progressive" in value]) - def test_given_progressive_when_generate_then_progressive_backpack_is_in_pool_two_times(self, value): - multi_world = setup_solo_multiworld({options.BackpackProgression.internal_name: - options.BackpackProgression.options[value]}) - + def test_given_progressive_when_generate_then_progressive_backpack_is_in_pool_two_times(self): + world_options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive} + multi_world = setup_solo_multiworld(world_options) items = [item.name for item in multi_world.get_items()] assert items.count("Progressive Backpack") == 2 - @pytest.mark.parametrize("value", - [value for value in options.BackpackProgression.options if "progressive" in value]) - def test_given_progressive_when_generate_then_backpack_upgrades_are_locations(self, value): - multi_world = setup_solo_multiworld({options.BackpackProgression.internal_name: - options.BackpackProgression.options[value]}) + def test_given_progressive_when_generate_then_backpack_upgrades_are_locations(self): + world_options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive} + multi_world = setup_solo_multiworld(world_options) locations = {locations.name for locations in multi_world.get_locations(1)} assert "Large Pack" in locations assert "Deluxe Pack" in locations def test_given_early_progressive_when_generate_then_progressive_backpack_is_in_early_pool(self): - multi_world = setup_solo_multiworld({options.BackpackProgression.internal_name: - options.BackpackProgression.option_early_progressive}) + world_options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_early_progressive} + multi_world = setup_solo_multiworld(world_options) assert "Progressive Backpack" in multi_world.early_items[1] -class TestToolProgression: +class TestToolProgression(SVTestBase): def test_given_vanilla_when_generate_then_no_tool_in_pool(self): - multi_world = setup_solo_multiworld({options.ToolProgression.internal_name: - options.ToolProgression.option_vanilla}) + world_options = {options.ToolProgression.internal_name: options.ToolProgression.option_vanilla} + multi_world = setup_solo_multiworld(world_options) items = {item.name for item in multi_world.get_items()} for tool in TOOLS: assert tool not in items def test_given_progressive_when_generate_then_progressive_tool_of_each_is_in_pool_four_times(self): - multi_world = setup_solo_multiworld({options.ToolProgression.internal_name: - options.ToolProgression.option_progressive}) + world_options = {options.ToolProgression.internal_name:options.ToolProgression.option_progressive} + multi_world = setup_solo_multiworld(world_options) items = [item.name for item in multi_world.get_items()] for tool in TOOLS: assert items.count("Progressive " + tool) == 4 def test_given_progressive_when_generate_then_tool_upgrades_are_locations(self): - multi_world = setup_solo_multiworld({options.ToolProgression.internal_name: - options.ToolProgression.option_progressive}) + world_options = {options.ToolProgression.internal_name: options.ToolProgression.option_progressive} + multi_world = setup_solo_multiworld(world_options) locations = {locations.name for locations in multi_world.get_locations(1)} for material, tool in itertools.product(["Copper", "Iron", "Gold", "Iridium"],