152 lines
4.7 KiB
Python
152 lines
4.7 KiB
Python
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from core.basic.character import createCharacter
|
|
from core.basic.equipment import get_equipment
|
|
|
|
|
|
CHARACTER_ROOT = Path(__file__).resolve().parents[2] / "core" / "character" / "GF"
|
|
DEFAULT_ALTER = "GF.swordman_male.weapon_master"
|
|
RESULT_KEYS = {"uuid", "skills", "info", "suits", "buffer"}
|
|
EQUIP_PARTS = {
|
|
"武器",
|
|
"副武器",
|
|
"称号",
|
|
"上衣",
|
|
"头肩",
|
|
"下装",
|
|
"腰带",
|
|
"鞋",
|
|
"手镯",
|
|
"项链",
|
|
"戒指",
|
|
"辅助装备",
|
|
"魔法石",
|
|
"耳环",
|
|
}
|
|
KNOWN_BROKEN_ALTERS = {
|
|
|
|
}
|
|
|
|
|
|
def _discover_alters() -> list[str]:
|
|
alters = []
|
|
for path in sorted(CHARACTER_ROOT.rglob("*.py")):
|
|
if path.name == "__init__.py":
|
|
continue
|
|
text = path.read_text(encoding="utf-8")
|
|
if "classChange" not in text:
|
|
continue
|
|
relative = path.relative_to(CHARACTER_ROOT).with_suffix("")
|
|
alters.append("GF." + ".".join(relative.parts))
|
|
return alters
|
|
|
|
|
|
def _character_params():
|
|
params = []
|
|
for alter in _discover_alters():
|
|
marks = []
|
|
if alter in KNOWN_BROKEN_ALTERS:
|
|
marks.append(pytest.mark.xfail(reason=KNOWN_BROKEN_ALTERS[alter], strict=True))
|
|
params.append(pytest.param(alter, id=alter, marks=marks))
|
|
return params
|
|
|
|
|
|
def _equipment_part(equ) -> str | None:
|
|
if equ.itemType == "武器":
|
|
return "武器"
|
|
if equ.itemDetailType in EQUIP_PARTS:
|
|
return equ.itemDetailType
|
|
return None
|
|
|
|
|
|
def _equipment_params():
|
|
params = []
|
|
for equ in get_equipment("0").equs:
|
|
part = _equipment_part(equ)
|
|
if part is None:
|
|
continue
|
|
params.append(pytest.param(equ, part, id=f"{part}:{equ.id}:{equ.name}"))
|
|
return params
|
|
|
|
|
|
def _assert_calc_result_shape(result: dict):
|
|
assert isinstance(result, dict)
|
|
assert RESULT_KEYS.issubset(result)
|
|
assert isinstance(result["skills"], list)
|
|
assert isinstance(result["info"], list)
|
|
|
|
|
|
@pytest.mark.parametrize("alter", _character_params())
|
|
def test_all_gf_characters_can_calculate_with_minimal_config(alter, minimal_calc_config):
|
|
character = createCharacter(alter, "0")
|
|
|
|
assert character is not None, f"createCharacter returned None for alter={alter}"
|
|
_assert_calc_result_shape(character.calc(minimal_calc_config))
|
|
|
|
|
|
@pytest.mark.parametrize(("equ", "part"), _equipment_params())
|
|
def test_all_equipment_can_calculate_when_equipped_once(equ, part, minimal_calc_config):
|
|
config = minimal_calc_config.copy()
|
|
config["equips"] = {
|
|
part: value.copy()
|
|
for part, value in minimal_calc_config["equips"].items()
|
|
}
|
|
config["equips"][part] = {
|
|
**config["equips"][part],
|
|
"id": equ.id,
|
|
}
|
|
|
|
character = createCharacter(DEFAULT_ALTER, "0")
|
|
|
|
assert character is not None
|
|
_assert_calc_result_shape(character.calc(config))
|
|
|
|
|
|
@pytest.mark.parametrize('vp', [0, 1, 2])
|
|
@pytest.mark.parametrize('alter', _character_params())
|
|
def test_all_gf_learned_skill_results_ignore_configuration_order(alter, vp, minimal_calc_config):
|
|
"""Exercise real profession rules, including every VP-dependent effect."""
|
|
from copy import deepcopy
|
|
from random import Random
|
|
|
|
probe = createCharacter(alter, '0')
|
|
values = [
|
|
(str(skill.id), {'lv': skill.calculate_lv(), 'vp': vp, 'up': 0})
|
|
for skill in sorted(probe.skills, key=lambda item: item.id)
|
|
]
|
|
shuffled = values.copy()
|
|
Random(42).shuffle(shuffled)
|
|
results = []
|
|
for ordered in (values, values[::-1], shuffled):
|
|
config = deepcopy(minimal_calc_config)
|
|
config['skills'] = dict(ordered)
|
|
config['skillShortcuts'] = {'combos': [[] for _ in range(6)]}
|
|
result = createCharacter(alter, '0').calc(config)
|
|
result.pop('uuid')
|
|
results.append(result)
|
|
assert results[0] == results[1] == results[2]
|
|
|
|
|
|
@pytest.mark.parametrize('alter', _character_params())
|
|
def test_learned_skills_with_weapon_ignore_configuration_order(alter, minimal_calc_config):
|
|
from copy import deepcopy
|
|
|
|
probe = createCharacter(alter, '0')
|
|
weapon = next(
|
|
equ for equ in get_equipment('0').equs
|
|
if equ.itemType == '武器' and equ.itemDetailType in probe.武器选项
|
|
)
|
|
values = [(str(skill.id), {'lv': skill.calculate_lv(), 'vp': 2, 'up': 0}) for skill in probe.skills]
|
|
results = []
|
|
for ordered in (values, values[::-1]):
|
|
config = deepcopy(minimal_calc_config)
|
|
config['equips']['武器']['id'] = weapon.id
|
|
config['skills'] = dict(ordered)
|
|
config['skillShortcuts'] = {'combos': [[] for _ in range(6)]}
|
|
result = createCharacter(alter, '0').calc(config)
|
|
result.pop('uuid')
|
|
results.append(result)
|
|
assert results[0] == results[1]
|