Files
tchMaterial-parser/tests/test_resource_search.py
肥宅水水呀 ac0a83e8ad refactor: 将单文件拆分为模块化包
原 src/tchmaterial_parser.py 已增长到 1608 行,其中 main() 占 541 行并声明
8 个 global,导致前面的函数反向依赖 main() 运行时才创建的对象;资源树部分
的 12 个闭包共享 8 个 nonlocal 变量;测试为取 3 个纯函数被迫拉起整个 GUI 栈。

现拆分为 15 个模块,最大 311 行,并保持原有的全局状态模型不变:每个全局
变量归属到拥有它的模块,由 app.py 在创建控件后通过 bind_* 函数写入。函数名、
参数、逻辑与注释均未改动,未启用的 fetch_lesson_list() 亦原样保留。

- 非 GUI 层:platform_utils、network、config、api、catalog、bookmarks、images
- 界面层:ui/runtime、theme、widgets、download_panel、token_window、resource_tree
- app.py 收容 main() 剩余部分,从 541 行降至 278 行且不再使用 global
- 资源树整体搬入 build_resource_tree(),闭包与 nonlocal 链保持原样
- 新增 runtime.scaled(),收敛全项目约 60 处 round(n * ui_scale)
- 入口改为包外的 src/main.py,spec 增加 pathex=['src']:PyInstaller 会把入口
  当作 __main__ 分析,包内脚本的相对导入在此情形下不成立
- resource_path() 上溯层级 +1;测试改为直接导入 catalog 与 images,不再连带
  拉起 tkinter/sv_ttk,headless CI 现在可以跑测试

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 16:00:45 +08:00

84 lines
3.0 KiB
Python

import unittest
from PIL import Image
from src.tchmaterial_parser.catalog import count_resource_items, filter_resource_items
from src.tchmaterial_parser.images import fit_cover_image
RESOURCE_ITEMS = {
"books": {
"display_name": "电子教材",
"children": {
"middle": {
"display_name": "初中",
"children": {
"chinese": {
"display_name": "语文",
"children": {
"grade7": {
"display_name": "七年级",
"children": {
"book-a": {
"display_name": "义务教育教科书 七年级上册",
},
"book-b": {
"display_name": "义务教育教科书 七年级下册",
},
},
},
},
},
"math": {
"display_name": "数学",
"children": {
"grade8": {
"display_name": "八年级",
"children": {
"book-c": {
"display_name": "义务教育教科书 八年级上册",
},
},
},
},
},
},
},
},
},
}
class ResourceSearchTest(unittest.TestCase):
def test_searches_across_the_full_category_path(self) -> None:
matches = filter_resource_items(RESOURCE_ITEMS, "语文 七年级 下册")
grade_items = matches["books"]["children"]["middle"]["children"]["chinese"]["children"]["grade7"]["children"]
self.assertEqual(list(grade_items), ["book-b"])
def test_category_search_keeps_all_matching_resources(self) -> None:
matches = filter_resource_items(RESOURCE_ITEMS, "数学")
self.assertEqual(count_resource_items(matches), 1)
def test_empty_query_returns_all_resources(self) -> None:
self.assertIs(filter_resource_items(RESOURCE_ITEMS, " "), RESOURCE_ITEMS)
self.assertEqual(count_resource_items(RESOURCE_ITEMS), 3)
def test_unknown_keyword_returns_an_empty_tree(self) -> None:
self.assertEqual(filter_resource_items(RESOURCE_ITEMS, "不存在"), {})
class CoverImageTest(unittest.TestCase):
def test_centers_portrait_cover_on_fixed_canvas(self) -> None:
image = Image.new("RGB", (100, 200), "white")
result = fit_cover_image(image, (80, 80))
self.assertEqual(result.size, (80, 80))
self.assertEqual(result.getbbox(), (20, 0, 60, 80))
if __name__ == "__main__":
unittest.main()