SoE: use new AP API and naming and make APworld (#2701)
* SoE: new file naming also fixes test base deprecation * SoE: use options_dataclass * SoE: moar typing * SoE: no more multiworld.random * SoE: replace LogicMixin by SoEPlayerLogic object * SoE: add test that rocket parts always exist * SoE: Even moar typing * SoE: can haz apworld now * SoE: pep up test naming * SoE: use self.options for trap chances * SoE: remove unused import with outdated comment * SoE: move flag and trap extraction to dataclass as suggested by beauxq * SoE: test trap option parsing and item generation
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
from test.TestBase import WorldTestBase
|
||||
from test.bases import WorldTestBase
|
||||
from typing import Iterable
|
||||
|
||||
|
||||
@@ -18,3 +18,14 @@ class SoETestBase(WorldTestBase):
|
||||
for location in unreachable:
|
||||
self.assertFalse(self.can_reach_location(location),
|
||||
f"{location} is reachable but shouldn't be")
|
||||
|
||||
def testRocketPartsExist(self):
|
||||
"""Tests that rocket parts exist and are unique"""
|
||||
self.assertEqual(len(self.get_items_by_name("Gauge")), 1)
|
||||
self.assertEqual(len(self.get_items_by_name("Wheel")), 1)
|
||||
diamond_eyes = self.get_items_by_name("Diamond Eye")
|
||||
self.assertEqual(len(diamond_eyes), 3)
|
||||
# verify diamond eyes are individual items
|
||||
self.assertFalse(diamond_eyes[0] is diamond_eyes[1])
|
||||
self.assertFalse(diamond_eyes[0] is diamond_eyes[2])
|
||||
self.assertFalse(diamond_eyes[1] is diamond_eyes[2])
|
||||
|
||||
@@ -7,7 +7,7 @@ class AccessTest(SoETestBase):
|
||||
def _resolveGourds(gourds: typing.Dict[str, typing.Iterable[int]]):
|
||||
return [f"{name} #{number}" for name, numbers in gourds.items() for number in numbers]
|
||||
|
||||
def testBronzeAxe(self):
|
||||
def test_bronze_axe(self):
|
||||
gourds = {
|
||||
"Pyramid bottom": (118, 121, 122, 123, 124, 125),
|
||||
"Pyramid top": (140,)
|
||||
@@ -16,7 +16,7 @@ class AccessTest(SoETestBase):
|
||||
items = [["Bronze Axe"]]
|
||||
self.assertAccessDependency(locations, items)
|
||||
|
||||
def testBronzeSpearPlus(self):
|
||||
def test_bronze_spear_plus(self):
|
||||
locations = ["Megataur"]
|
||||
items = [["Bronze Spear"], ["Lance (Weapon)"], ["Laser Lance"]]
|
||||
self.assertAccessDependency(locations, items)
|
||||
|
||||
@@ -8,7 +8,7 @@ class TestFragmentGoal(SoETestBase):
|
||||
"required_fragments": 20,
|
||||
}
|
||||
|
||||
def testFragments(self):
|
||||
def test_fragments(self):
|
||||
self.collect_by_name(["Gladiator Sword", "Diamond Eye", "Wheel", "Gauge"])
|
||||
self.assertBeatable(False) # 0 fragments
|
||||
fragments = self.get_items_by_name("Energy Core Fragment")
|
||||
@@ -24,11 +24,11 @@ class TestFragmentGoal(SoETestBase):
|
||||
self.assertEqual(self.count("Energy Core Fragment"), 21)
|
||||
self.assertBeatable(True)
|
||||
|
||||
def testNoWeapon(self):
|
||||
def test_no_weapon(self):
|
||||
self.collect_by_name(["Diamond Eye", "Wheel", "Gauge", "Energy Core Fragment"])
|
||||
self.assertBeatable(False)
|
||||
|
||||
def testNoRocket(self):
|
||||
def test_no_rocket(self):
|
||||
self.collect_by_name(["Gladiator Sword", "Diamond Eye", "Wheel", "Energy Core Fragment"])
|
||||
self.assertBeatable(False)
|
||||
|
||||
@@ -38,16 +38,16 @@ class TestShuffleGoal(SoETestBase):
|
||||
"energy_core": "shuffle",
|
||||
}
|
||||
|
||||
def testCore(self):
|
||||
def test_core(self):
|
||||
self.collect_by_name(["Gladiator Sword", "Diamond Eye", "Wheel", "Gauge"])
|
||||
self.assertBeatable(False)
|
||||
self.collect_by_name(["Energy Core"])
|
||||
self.assertBeatable(True)
|
||||
|
||||
def testNoWeapon(self):
|
||||
def test_no_weapon(self):
|
||||
self.collect_by_name(["Diamond Eye", "Wheel", "Gauge", "Energy Core"])
|
||||
self.assertBeatable(False)
|
||||
|
||||
def testNoRocket(self):
|
||||
def test_no_rocket(self):
|
||||
self.collect_by_name(["Gladiator Sword", "Diamond Eye", "Wheel", "Energy Core"])
|
||||
self.assertBeatable(False)
|
||||
|
||||
@@ -6,7 +6,7 @@ class OoBTest(SoETestBase):
|
||||
"""Tests that 'on' doesn't put out-of-bounds in logic. This is also the test base for OoB in logic."""
|
||||
options: typing.Dict[str, typing.Any] = {"out_of_bounds": "on"}
|
||||
|
||||
def testOoBAccess(self):
|
||||
def test_oob_access(self):
|
||||
in_logic = self.options["out_of_bounds"] == "logic"
|
||||
|
||||
# some locations that just need a weapon + OoB
|
||||
@@ -37,7 +37,7 @@ class OoBTest(SoETestBase):
|
||||
self.collect_by_name("Diamond Eye")
|
||||
self.assertLocationReachability(reachable=de_reachable, unreachable=de_unreachable, satisfied=in_logic)
|
||||
|
||||
def testOoBGoal(self):
|
||||
def test_oob_goal(self):
|
||||
# still need Energy Core with OoB if sequence breaks are not in logic
|
||||
for item in ["Gladiator Sword", "Diamond Eye", "Wheel", "Gauge"]:
|
||||
self.collect_by_name(item)
|
||||
|
||||
@@ -6,7 +6,7 @@ class SequenceBreaksTest(SoETestBase):
|
||||
"""Tests that 'on' doesn't put sequence breaks in logic. This is also the test base for in-logic."""
|
||||
options: typing.Dict[str, typing.Any] = {"sequence_breaks": "on"}
|
||||
|
||||
def testSequenceBreaksAccess(self):
|
||||
def test_sequence_breaks_access(self):
|
||||
in_logic = self.options["sequence_breaks"] == "logic"
|
||||
|
||||
# some locations that just need any weapon + sequence break
|
||||
@@ -30,7 +30,7 @@ class SequenceBreaksTest(SoETestBase):
|
||||
self.collect_by_name("Bronze Spear") # Escape now just needs either Megataur or Rimsala dead
|
||||
self.assertEqual(self.can_reach_location("Escape"), in_logic)
|
||||
|
||||
def testSequenceBreaksGoal(self):
|
||||
def test_sequence_breaks_goal(self):
|
||||
in_logic = self.options["sequence_breaks"] == "logic"
|
||||
|
||||
# don't need Energy Core with sequence breaks in logic
|
||||
|
||||
55
worlds/soe/test/test_traps.py
Normal file
55
worlds/soe/test/test_traps.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import typing
|
||||
from dataclasses import fields
|
||||
|
||||
from . import SoETestBase
|
||||
from ..options import SoEOptions
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from .. import SoEWorld
|
||||
|
||||
|
||||
class Bases:
|
||||
# class in class to avoid running tests for TrapTest class
|
||||
class TrapTestBase(SoETestBase):
|
||||
"""Test base for trap tests"""
|
||||
option_name_to_item_name = {
|
||||
# filtering by name here validates that there is no confusion between name and type
|
||||
field.name: field.type.item_name for field in fields(SoEOptions) if field.name.startswith("trap_chance_")
|
||||
}
|
||||
|
||||
def test_dataclass(self) -> None:
|
||||
"""Test that the dataclass helper property returns the expected sequence"""
|
||||
self.assertGreater(len(self.option_name_to_item_name), 0, "Expected more than 0 trap types")
|
||||
world: "SoEWorld" = typing.cast("SoEWorld", self.multiworld.worlds[1])
|
||||
item_name_to_rolled_option = {option.item_name: option for option in world.options.trap_chances}
|
||||
# compare that all fields are present - that is property in dataclass and selector code in test line up
|
||||
self.assertEqual(sorted(self.option_name_to_item_name.values()), sorted(item_name_to_rolled_option),
|
||||
"field names probably do not match field types")
|
||||
# sanity check that chances are correctly set and returned by property
|
||||
for option_name, item_name in self.option_name_to_item_name.items():
|
||||
self.assertEqual(item_name_to_rolled_option[item_name].value,
|
||||
self.options.get(option_name, item_name_to_rolled_option[item_name].default))
|
||||
|
||||
def test_trap_count(self) -> None:
|
||||
"""Test that total trap count is correct"""
|
||||
self.assertEqual(self.options["trap_count"], len(self.get_items_by_name(self.option_name_to_item_name.values())))
|
||||
|
||||
|
||||
class TestTrapAllZeroChance(Bases.TrapTestBase):
|
||||
"""Tests all zero chances still gives traps if trap_count is set."""
|
||||
options: typing.Dict[str, typing.Any] = {
|
||||
"trap_count": 1,
|
||||
**{name: 0 for name in Bases.TrapTestBase.option_name_to_item_name}
|
||||
}
|
||||
|
||||
|
||||
class TestTrapNoConfound(Bases.TrapTestBase):
|
||||
"""Tests that one zero chance does not give that trap."""
|
||||
options: typing.Dict[str, typing.Any] = {
|
||||
"trap_count": 99,
|
||||
"trap_chance_confound": 0,
|
||||
}
|
||||
|
||||
def test_no_confound_trap(self) -> None:
|
||||
self.assertEqual(self.option_name_to_item_name["trap_chance_confound"], "Confound Trap")
|
||||
self.assertEqual(len(self.get_items_by_name("Confound Trap")), 0)
|
||||
Reference in New Issue
Block a user