Skip to content

Commit 3105320

Browse files
Test: check fields in world source manifest (#5558)
* Test: check game in world manifest * Update test/general/test_world_manifest.py Co-authored-by: Duck <[email protected]> * Test: rework finding expected manifest location * Test: fix doc comment * Test: fix wrong custom_worlds path in test_world_manifest Also simplifies the way we find ./worlds/. * Test: make test_world_manifest easier to extend * Test: check world_version in world manifest according to docs/apworld specification.md * Test: check no container version in source world manifest according what was added to docs/apworld specification.md in PR 5509 * Test: better assertion messages in test_world_manifest.py * Test: fix wording in world source manifest --------- Co-authored-by: Duck <[email protected]>
1 parent e8c8b0d commit 3105320

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"""Check world sources' manifest files"""
2+
3+
import json
4+
import unittest
5+
from pathlib import Path
6+
from typing import Any, ClassVar
7+
8+
import test
9+
from Utils import home_path, local_path
10+
from worlds.AutoWorld import AutoWorldRegister
11+
from ..param import classvar_matrix
12+
13+
14+
test_path = Path(test.__file__).parent
15+
worlds_paths = [
16+
Path(local_path("worlds")),
17+
Path(local_path("custom_worlds")),
18+
Path(home_path("worlds")),
19+
Path(home_path("custom_worlds")),
20+
]
21+
22+
# Only check source folders for now. Zip validation should probably be in the loader and/or installer.
23+
source_world_names = [
24+
k
25+
for k, v in AutoWorldRegister.world_types.items()
26+
if not v.zip_path and not Path(v.__file__).is_relative_to(test_path)
27+
]
28+
29+
30+
def get_source_world_manifest_path(game: str) -> Path | None:
31+
"""Get path of archipelago.json in the world's root folder from game name."""
32+
# TODO: add a feature to AutoWorld that makes this less annoying
33+
world_type = AutoWorldRegister.world_types[game]
34+
world_type_path = Path(world_type.__file__)
35+
for worlds_path in worlds_paths:
36+
if world_type_path.is_relative_to(worlds_path):
37+
world_root = worlds_path / world_type_path.relative_to(worlds_path).parents[0]
38+
manifest_path = world_root / "archipelago.json"
39+
return manifest_path if manifest_path.exists() else None
40+
assert False, f"{world_type_path} not found in any worlds path"
41+
42+
43+
# TODO: remove the filter once manifests are mandatory.
44+
@classvar_matrix(game=filter(get_source_world_manifest_path, source_world_names))
45+
class TestWorldManifest(unittest.TestCase):
46+
game: ClassVar[str]
47+
manifest: ClassVar[dict[str, Any]]
48+
49+
@classmethod
50+
def setUpClass(cls) -> None:
51+
world_type = AutoWorldRegister.world_types[cls.game]
52+
assert world_type.game == cls.game
53+
manifest_path = get_source_world_manifest_path(cls.game)
54+
assert manifest_path # make mypy happy
55+
with manifest_path.open("r", encoding="utf-8") as f:
56+
cls.manifest = json.load(f)
57+
58+
def test_game(self) -> None:
59+
"""Test that 'game' will be correctly defined when generating APWorld manifest from source."""
60+
self.assertIn(
61+
"game",
62+
self.manifest,
63+
f"archipelago.json manifest exists for {self.game} but does not contain 'game'",
64+
)
65+
self.assertEqual(
66+
self.manifest["game"],
67+
self.game,
68+
f"archipelago.json manifest for {self.game} specifies wrong game '{self.manifest['game']}'",
69+
)
70+
71+
def test_world_version(self) -> None:
72+
"""Test that world_version matches the requirements in apworld specification.md"""
73+
if "world_version" in self.manifest:
74+
world_version: str = self.manifest["world_version"]
75+
self.assertIsInstance(
76+
world_version,
77+
str,
78+
f"world_version in archipelago.json for '{self.game}' has to be string if provided.",
79+
)
80+
parts = world_version.split(".")
81+
self.assertEqual(
82+
len(parts),
83+
3,
84+
f"world_version in archipelago.json for '{self.game}' has to be in the form of 'major.minor.build'.",
85+
)
86+
for part in parts:
87+
self.assertTrue(
88+
part.isdigit(),
89+
f"world_version in archipelago.json for '{self.game}' may only contain numbers.",
90+
)
91+
92+
def test_no_container_version(self) -> None:
93+
self.assertNotIn(
94+
"version",
95+
self.manifest,
96+
f"archipelago.json for '{self.game}' must not define 'version', see apworld specification.md.",
97+
)
98+
self.assertNotIn(
99+
"compatible_version",
100+
self.manifest,
101+
f"archipelago.json for '{self.game}' must not define 'compatible_version', see apworld specification.md.",
102+
)

0 commit comments

Comments
 (0)