Skip to content

Commit 65fb6db

Browse files
authored
Merge branch 'development' into checkout
2 parents 6d4197c + 7c45cca commit 65fb6db

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+1862
-967
lines changed

run_tuxemon.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,12 @@ def handle_fatal_error(e: Exception) -> None:
112112

113113
def launch_game(argv: list[str] | None = None) -> None:
114114
args = parse_args(argv)
115-
platform = "headless" if args.headless else "pygame"
116-
context = init_display(platform)
115+
116+
from tuxemon.platform import platform
117+
platform.init()
118+
119+
platform_mode = "headless" if args.headless else "pygame"
120+
context = init_display(platform_mode)
117121

118122
from tuxemon import main as tuxemon_main
119123

tests/tuxemon/test_event_condition_manager.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# SPDX-License-Identifier: GPL-3.0
22
# Copyright (c) 2014-2026 William Edwards <shadowapex@gmail.com>, Benjamin Bean <superman2k5@gmail.com>
3+
from dataclasses import dataclass
34
from unittest.mock import MagicMock, patch
45

56
import pytest
@@ -18,8 +19,12 @@ def mock_plugin_manager():
1819
@pytest.fixture
1920
def condition_manager(mock_plugin_manager):
2021

22+
@dataclass
2123
class DummyCondition(EventCondition):
2224
name = "char_at"
25+
a: str = ""
26+
b: int = 0
27+
c: str = ""
2328

2429
def test(self, session, condition_data):
2530
return True
@@ -35,6 +40,7 @@ def test_get_condition_found(condition_manager):
3540
mock_cond_data = MagicMock(spec=SpatialCondition)
3641
mock_cond_data.type = "char_at"
3742
mock_cond_data.operator = "is"
43+
mock_cond_data.parameters = []
3844
condition = condition_manager.get_condition(mock_cond_data)
3945
assert condition is not None
4046
assert condition.is_expected is True

tests/tuxemon/test_map_condition_boundary.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,13 @@
33
import pytest
44

55
from tuxemon.boundary import Dimensions, MapConditionBoundary
6-
from tuxemon.db import BoundingBox, Operator, SpatialCondition
6+
from tuxemon.db import BoundingBox
77

88

99
@pytest.fixture
1010
def make_boundary():
11-
def _make(box):
12-
condition = SpatialCondition(
13-
type="",
14-
parameters=[],
15-
box=box,
16-
operator=Operator.IS,
17-
name="unknown",
18-
)
19-
return MapConditionBoundary(condition)
11+
def _make(box: BoundingBox):
12+
return MapConditionBoundary(box)
2013

2114
return _make
2215

tests/tuxemon/test_platform.py

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# SPDX-License-Identifier: GPL-3.0
2+
# Copyright (c) 2014-2026 William Edwards <shadowapex@gmail.com>, Benjamin Bean <superman2k5@gmail.com>
3+
import sys
4+
import types
5+
from pathlib import Path
6+
7+
from tuxemon.platform import ASSET_ROOT, DummyMixer, Platform, PlatformError
8+
9+
10+
def fake_module(name, **attrs):
11+
module = types.ModuleType(name)
12+
for k, v in attrs.items():
13+
setattr(module, k, v)
14+
return module
15+
16+
17+
def test_detect_android(monkeypatch):
18+
android = fake_module("android", context=None)
19+
monkeypatch.setitem(sys.modules, "android", android)
20+
21+
p = Platform()
22+
p._detect_android()
23+
24+
assert p.android is android
25+
26+
27+
def test_detect_android_missing(monkeypatch):
28+
monkeypatch.delitem(sys.modules, "android", raising=False)
29+
30+
p = Platform()
31+
p._detect_android()
32+
33+
assert p.android is None
34+
35+
36+
def test_android_mixer(monkeypatch):
37+
android = fake_module("android", context=None)
38+
android_mixer = fake_module("android.mixer", music=None)
39+
40+
monkeypatch.setitem(sys.modules, "android", android)
41+
monkeypatch.setitem(sys.modules, "android.mixer", android_mixer)
42+
43+
p = Platform()
44+
p._detect_android()
45+
p._init_mixer()
46+
47+
assert p.mixer is android_mixer
48+
assert not p._pygame_mixer_in_use
49+
50+
51+
def test_android_mixer_missing(monkeypatch):
52+
android = fake_module("android", context=None)
53+
fake_pygame = fake_module("pygame")
54+
fake_pygame_mixer = fake_module("pygame.mixer", music=None)
55+
56+
monkeypatch.setitem(sys.modules, "android", android)
57+
monkeypatch.delitem(sys.modules, "android.mixer", raising=False)
58+
59+
fake_pygame.mixer = fake_pygame_mixer
60+
monkeypatch.setitem(sys.modules, "pygame", fake_pygame)
61+
monkeypatch.setitem(sys.modules, "pygame.mixer", fake_pygame_mixer)
62+
63+
p = Platform()
64+
p._detect_android()
65+
p._init_mixer()
66+
67+
assert p.mixer is fake_pygame_mixer
68+
assert p._pygame_mixer_in_use
69+
70+
71+
def test_pygame_mixer(monkeypatch):
72+
fake_pygame = fake_module("pygame")
73+
fake_pygame_mixer = fake_module("pygame.mixer", music=None)
74+
75+
fake_pygame.mixer = fake_pygame_mixer
76+
77+
monkeypatch.setitem(sys.modules, "pygame", fake_pygame)
78+
monkeypatch.setitem(sys.modules, "pygame.mixer", fake_pygame_mixer)
79+
80+
p = Platform()
81+
p._detect_android()
82+
p._init_mixer()
83+
84+
assert p.mixer is fake_pygame_mixer
85+
assert p._pygame_mixer_in_use
86+
87+
88+
def test_no_mixers(monkeypatch):
89+
monkeypatch.delitem(sys.modules, "android", raising=False)
90+
91+
fake_pygame = fake_module("pygame")
92+
fake_pygame.mixer = None
93+
94+
monkeypatch.setitem(sys.modules, "pygame", fake_pygame)
95+
monkeypatch.delitem(sys.modules, "pygame.mixer", raising=False)
96+
97+
p = Platform()
98+
p._detect_android()
99+
p._init_mixer()
100+
101+
assert isinstance(p.mixer, DummyMixer)
102+
103+
104+
def test_user_storage_desktop():
105+
p = Platform()
106+
p.android = None
107+
108+
user_dir = p.user_storage.user_dir()
109+
assert user_dir.name == ".tuxemon"
110+
111+
112+
def test_system_storage_desktop():
113+
p = Platform()
114+
p.android = None
115+
116+
dirs = p.system_storage.system_dirs()
117+
paths = [h.path for h in dirs if h.path is not None]
118+
assert any(str(p).endswith("tuxemon") for p in paths)
119+
120+
121+
def test_lazy_storage(monkeypatch):
122+
p = Platform()
123+
p.android = None
124+
storage1 = p.system_storage
125+
assert storage1.android is None
126+
127+
fake_android = fake_module("android", context=None)
128+
p.android = fake_android
129+
130+
storage2 = p.system_storage
131+
assert storage2 is storage1
132+
assert storage2.android is None
133+
134+
135+
def test_system_storage_android(monkeypatch, tmp_path):
136+
obb_path = tmp_path / "obb"
137+
obb_path.mkdir()
138+
139+
ctx = fake_module(
140+
"ctx",
141+
getObbDir=lambda: fake_module("obb", getPath=lambda: str(obb_path)),
142+
getAssets=lambda: object(),
143+
getExternalFilesDir=lambda arg: fake_module(
144+
"efd", getPath=lambda: "/tmp/ext"
145+
),
146+
)
147+
android = fake_module("android", context=ctx)
148+
149+
p = Platform()
150+
p.android = android
151+
152+
dirs = p.system_storage.system_dirs()
153+
assert any(h.asset == ASSET_ROOT for h in dirs)
154+
assert any(h.path == obb_path for h in dirs if h.path is not None)
155+
156+
157+
def test_user_storage_android(monkeypatch):
158+
ctx = fake_module(
159+
"ctx",
160+
getExternalFilesDir=lambda arg: fake_module(
161+
"efd", getPath=lambda: "/tmp/ext"
162+
),
163+
)
164+
android = fake_module("android", context=ctx)
165+
166+
p = Platform()
167+
p.android = android
168+
169+
assert p.user_storage.user_dir() == Path("/tmp/ext")
170+
171+
172+
def test_platform_error_on_invalid_android(monkeypatch):
173+
bad_android = fake_module("android")
174+
monkeypatch.setitem(sys.modules, "android", bad_android)
175+
176+
p = Platform()
177+
p._detect_android()
178+
179+
assert p.android is None

0 commit comments

Comments
 (0)