Skip to content

Commit f859a28

Browse files
committed
test: separate manual e2e tests and add one-to-one unit test structure
1 parent 9bc5c5b commit f859a28

212 files changed

Lines changed: 22012 additions & 26 deletions

File tree

Some content is hidden

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

autowsgr/ops/README.md

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
- cook
1616
- reward
1717

18-
每个模块只有少量功能,全部做 e2e 测试即可。测试脚本统一放在 `testing/ops/` 下。
18+
每个模块只有少量功能,全部做 e2e 测试即可。测试脚本统一放在 `tests/manual/ops/` 下。
1919

2020
| 模块 | 功能 | 状态 |
2121
|------|------|------|
@@ -36,31 +36,31 @@
3636
### startup 测试 ✅
3737

3838
```bash
39-
python testing/ops/startup.py
40-
python testing/ops/startup.py 127.0.0.1:16384
39+
python tests/ops/startup.py
40+
python tests/ops/startup.py 127.0.0.1:16384
4141
```
4242

4343
### normal_fight 测试 ✅
4444

4545
```bash
46-
python testing/ops/normal_fight.py
47-
python testing/ops/normal_fight.py 127.0.0.1:16384 3 # 指定设备和次数
48-
python testing/ops/normal_fight.py --plan examples/plans/normal_fight/7-46SS-all.yaml
46+
python tests/ops/normal_fight.py
47+
python tests/ops/normal_fight.py 127.0.0.1:16384 3 # 指定设备和次数
48+
python tests/ops/normal_fight.py --plan examples/plans/normal_fight/7-46SS-all.yaml
4949
```
5050

5151
### campaign 测试 ✅
5252

5353
```bash
54-
python testing/ops/campaign.py # 困难驱逐 x1
55-
python testing/ops/campaign.py 127.0.0.1:16384 困难航母 3 # 指定战役和次数
56-
python testing/ops/campaign.py "" 简单驱逐 2
54+
python tests/ops/campaign.py # 困难驱逐 x1
55+
python tests/ops/campaign.py 127.0.0.1:16384 困难航母 3 # 指定战役和次数
56+
python tests/ops/campaign.py "" 简单驱逐 2
5757
```
5858

5959
### expedition 测试 ✅
6060

6161
```bash
62-
python testing/ops/expedition.py
63-
python testing/ops/expedition.py 127.0.0.1:16384
62+
python tests/ops/expedition.py
63+
python tests/ops/expedition.py 127.0.0.1:16384
6464
```
6565

6666
### decisive 测试 ❌
@@ -70,24 +70,24 @@ python testing/ops/expedition.py 127.0.0.1:16384
7070
### exercise 测试 ✅
7171

7272
```bash
73-
python testing/ops/exercise.py --fleet 1
74-
python testing/ops/exercise.py --fleet 1 --rival 3
73+
python tests/ops/exercise.py --fleet 1
74+
python tests/ops/exercise.py --fleet 1 --rival 3
7575
```
7676

7777
### build 测试 ❌
7878

7979
```bash
80-
python testing/ops/build.py
81-
python testing/ops/build.py 127.0.0.1:16384
80+
python tests/ops/build.py
81+
python tests/ops/build.py 127.0.0.1:16384
8282
```
8383

8484
### repair 测试 ❌
8585

8686
#### 修复第一艘舰船 ✅
8787

8888
```bash
89-
python testing/ops/repair.py
90-
python testing/ops/repair.py 127.0.0.1:16384
89+
python tests/ops/repair.py
90+
python tests/ops/repair.py 127.0.0.1:16384
9191
```
9292

9393
#### 按照名称修复舰船 ❌
@@ -97,21 +97,21 @@ python testing/ops/repair.py 127.0.0.1:16384
9797
### destroy 测试 ✅
9898

9999
```bash
100-
python testing/ops/destroy.py
101-
python testing/ops/destroy.py 127.0.0.1:16384
100+
python tests/ops/destroy.py
101+
python tests/ops/destroy.py 127.0.0.1:16384
102102
```
103103

104104
### cook 测试 ✅
105105

106106
```bash
107-
python testing/ops/cook.py
108-
python testing/ops/cook.py 127.0.0.1:16384 2
107+
python tests/ops/cook.py
108+
python tests/ops/cook.py 127.0.0.1:16384 2
109109
```
110110

111111
### reward 测试 ✅
112112

113113
```bash
114-
python testing/ops/reward.py
115-
python testing/ops/reward.py 127.0.0.1:16384
116-
python testing/ops/reward.py 127.0.0.1:16384 --auto
114+
python tests/ops/reward.py
115+
python tests/ops/reward.py 127.0.0.1:16384
116+
python tests/ops/reward.py 127.0.0.1:16384 --auto
117117
```

docs/developer/test_pkg.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44

55
`test_pkg/` 是本地私有的视觉/OCR 回归测试集合,**不纳入版本控制**(已在 `.gitignore` 中排除),也不随 PyPI 包发布。
66

7-
它与 `testing/` 的分工:
7+
它与 `tests/` 的分工:
88

99
| 目录 | 作用 | 版本控制 | 框架 |
1010
|------|------|----------|------|
11-
| `testing/` | 单元测试、集成测试 || pytest |
11+
| `tests/` | 单元测试、集成测试 || pytest |
1212
| `test_pkg/` | 视觉/OCR 回归测试(含截图) | 否(本地私有) | 独立脚本 |
1313

1414
`test_pkg/` 中的测试图片来自游戏实机截图,体积较大且可能涉及版权,因此不适合入库。

tests/__init__.py

Whitespace-only changes.

tests/conftest.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""测试公共 fixtures。"""
2+
3+
from collections.abc import Callable
4+
from pathlib import Path
5+
6+
import pytest
7+
8+
9+
@pytest.fixture
10+
def fixtures_dir() -> Path:
11+
"""测试数据目录。"""
12+
return Path(__file__).parent / 'fixtures'
13+
14+
15+
@pytest.fixture
16+
def tmp_yaml(tmp_path: Path) -> Callable[[str, str], Path]:
17+
"""创建临时 YAML 文件的工厂 fixture。"""
18+
19+
def _factory(name: str, content: str) -> Path:
20+
p = tmp_path / name
21+
p.write_text(content, encoding='utf-8')
22+
return p
23+
24+
return _factory

tests/manual/__init__.py

Whitespace-only changes.

tests/manual/ops/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""游戏操作 (ops) 层的测试。
2+
3+
包含两类测试:
4+
5+
| 文件 | 类型 | 运行方式 | 需要真实设备 |
6+
|---|---|---|---|
7+
| `test_unit.py` | 单元测试(mock) | `pytest` | 否 |
8+
| `e2e.py` | 端到端测试(真实设备) | 直接执行 | **是** |
9+
"""

tests/manual/ops/_framework.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""ops 端到端测试公共工具。
2+
3+
提供 :func:`launch_for_test` 供各 ops 测试脚本调用,替代手动的
4+
``ConfigManager.load() → ADBController → GameContext → ensure_game_ready``
5+
流程,与生产环境的 :func:`autowsgr.scheduler.launcher.launch` 行为对齐。
6+
"""
7+
8+
from __future__ import annotations
9+
10+
from typing import TYPE_CHECKING
11+
12+
from autowsgr.context import GameContext
13+
14+
15+
if TYPE_CHECKING:
16+
from pathlib import Path
17+
18+
19+
def launch_for_test(
20+
serial: str | None,
21+
log_dir: Path,
22+
log_level: str = 'DEBUG',
23+
*,
24+
with_ocr: bool = False,
25+
) -> GameContext:
26+
"""为 ops 端到端测试准备就绪的 GameContext。
27+
28+
加载配置 → 覆盖日志目录/级别 → 连接设备 →
29+
构建 GameContext → ensure_game_ready → 返回就绪上下文。
30+
31+
与 :func:`autowsgr.scheduler.launcher.launch` 的区别:
32+
33+
* 使用测试专用的 *log_dir* 覆盖配置中的日志目录。
34+
* 默认 ``with_ocr=False`` 跳过 OCR 初始化(适用于不需要 OCR 的简单 ops)。
35+
36+
Parameters
37+
----------
38+
serial:
39+
ADB 设备序列号;为 ``None`` 时沿用配置文件或自动检测。
40+
log_dir:
41+
测试专用日志目录(覆盖配置文件 ``log.dir``)。
42+
log_level:
43+
日志级别,默认 ``"DEBUG"``。
44+
with_ocr:
45+
是否初始化 OCR 引擎(决战等需要 OCR 的场景置 ``True``)。
46+
47+
Returns
48+
-------
49+
GameContext
50+
``ctx.ctrl`` 已连接、游戏已在主页面。
51+
若 ``with_ocr=True`` 则 ``ctx.ocr`` 也已初始化。
52+
"""
53+
from autowsgr.infra.logger import setup_logger
54+
from autowsgr.scheduler.launcher import Launcher
55+
56+
launcher = Launcher()
57+
cfg = launcher.load_config()
58+
59+
# 以测试指定 log_dir 覆盖配置中的日志目录
60+
channels = cfg.log.effective_channels or None
61+
setup_logger(log_dir=log_dir, level=log_level, save_images=True, channels=channels)
62+
63+
# 以命令行指定的 serial 覆盖配置
64+
if serial is not None:
65+
new_emu = cfg.emulator.model_copy(update={'serial': serial})
66+
launcher.set_config(cfg.model_copy(update={'emulator': new_emu}))
67+
68+
launcher.connect()
69+
70+
if with_ocr:
71+
ctx = launcher.build_context()
72+
else:
73+
ctx = GameContext(ctrl=launcher.ctrl, config=launcher.config)
74+
launcher.ensure_ready(ctx)
75+
76+
return ctx

tests/manual/ops/build.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""建造收取 (collect_built_ships) 端到端测试。
2+
3+
用法::
4+
5+
python tests/manual/ops/build.py
6+
python tests/manual/ops/build.py 127.0.0.1:16384
7+
8+
无页面前置要求 — collect_built_ships() 内部通过 goto_page() 自动导航。
9+
"""
10+
11+
from __future__ import annotations
12+
13+
import sys
14+
from pathlib import Path
15+
16+
17+
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
18+
19+
try:
20+
if hasattr(sys.stdout, 'reconfigure'):
21+
sys.stdout.reconfigure(encoding='utf-8', errors='replace') # ty: ignore[call-non-callable]
22+
sys.stderr.reconfigure(encoding='utf-8', errors='replace') # ty: ignore[call-non-callable]
23+
except Exception: # noqa: S110
24+
pass
25+
from loguru import logger
26+
27+
from tests.manual.ops._framework import launch_for_test
28+
29+
30+
_STEPS = [
31+
'1. 连接设备',
32+
"2. 调用 collect_built_ships(ctrl, build_type='ship', allow_fast_build=False)",
33+
'3. 打印收取数量',
34+
]
35+
36+
37+
def main() -> None:
38+
serial = sys.argv[1] if len(sys.argv) > 1 else None
39+
40+
print('=' * 60)
41+
print(' 建造收取 (collect_built_ships) E2E 测试')
42+
print('=' * 60)
43+
print()
44+
print(' 测试步骤:')
45+
for s in _STEPS:
46+
print(f' {s}')
47+
print()
48+
input(' 按 Enter 开始运行...')
49+
print()
50+
51+
try:
52+
ctx = launch_for_test(serial, log_dir=Path('logs/e2e/build'))
53+
ctrl = ctx.ctrl
54+
logger.info(f'已连接: {ctrl.serial}')
55+
print(f' [OK] 已连接: {ctrl.serial}')
56+
57+
from autowsgr.ops.build import collect_built_ships
58+
59+
result = collect_built_ships(ctx, build_type='ship', allow_fast_build=False)
60+
logger.info(f'collect_built_ships() 返回: {result}')
61+
print(f' [OK] collect_built_ships() = {result} 艘')
62+
except Exception as exc:
63+
logger.opt(exception=True).error(f'测试失败: {exc}')
64+
print(f' [FAIL] {exc}')
65+
sys.exit(1)
66+
67+
ctrl.disconnect()
68+
print()
69+
print(' [OK] 建造收取测试通过')
70+
71+
72+
if __name__ == '__main__':
73+
main()

0 commit comments

Comments
 (0)