# -*- coding: utf-8 -*- """1) 布局核算补上「功能」分页(现在 7 页) 2) 新增功能注册表的测试 """ from __future__ import annotations import sys from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") LAYOUT = ROOT / "_tests" / "check_layout.py" LAYOUT_OLD = ''' "系统": ["锁屏", "睡眠", "关机", "重启", "取消定时关机", "定时关机", "设备诊断"], }''' LAYOUT_NEW = ''' "系统": ["锁屏", "睡眠", "关机", "重启", "取消定时关机", "定时关机", "设备诊断"], "功能": ["显示", "音量", "系统", "查看", "效率", "文件"], }''' TEST = '''# -*- coding: utf-8 -*- """验证电脑功能注册表。 功能是"电脑端声明、手表端调用"的结构,所以这里重点验证: * 声明出来的功能都能被调用(名字和实现对得上) * 读取类功能返回合理内容 * **安全边界**:文件操作不能越出用户自己的目录 * 边界是可配置的(这是给老师的实际需求留的口子) """ from __future__ import annotations import shutil import sys import tempfile import tkinter as tk from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "windows")) PASSED = 0 FAILED = 0 def check(name, ok, detail=""): global PASSED, FAILED if ok: PASSED += 1 print(f" [PASS] {name}") else: FAILED += 1 print(f" [FAIL] {name} {detail}") def main() -> int: from lanremote.actions import Actions from lanremote.config import Store from lanremote.features import Features tmp = Path(tempfile.mkdtemp()) store = Store(tmp) class FakeUI: root = tk.Tk() root.withdraw() def ui_call(self, fn, timeout=5.0): return fn() def screen_size(self): return (1920, 1080) def notify(self, *a, **k): pass def log(self, *a, **k): pass def show_message(self, *a, **k): pass actions = Actions(FakeUI(), store) features = Features(actions) print("一、声明与实现是否对得上") defs = features.defs() check("功能数量够多", len(defs) >= 30, f"{len(defs)} 个") print(f" 共 {len(defs)} 个功能") groups = {} for item in defs: groups.setdefault(item.get("group", "?"), 0) groups[item["group"]] += 1 for name, count in sorted(groups.items()): print(f" {name}: {count} 个") missing = [] for item in defs: if not hasattr(features, f"_do_{item['name']}"): missing.append(item["name"]) check("每个声明的功能都有实现", not missing, str(missing)) bad_kind = [i["name"] for i in defs if i.get("kind") not in ("toggle", "action", "value", "input")] check("kind 取值合法", not bad_kind, str(bad_kind)) need_input = [i for i in defs if i.get("kind") == "input"] check("需要输入的功能都标了参数名", all(i.get("key") for i in need_input), str([i["name"] for i in need_input if not i.get("key")])) print() print("二、读取类功能") reply_map = { "uptime": {}, "perf": {}, "disk": {}, "battery": {}, "network_speed": {}, "recent_files": {}, "temperature": {}, } for name in reply_map: try: text = features.run(name, {}) ok = isinstance(text, str) and len(text) > 0 except Exception as exc: # noqa: BLE001 text, ok = str(exc), False print(f" {name}: {str(text)[:60]}") check(f"{name} 能返回内容", ok) print() print("三、笔记 / 待办 / 收藏夹") features.run("note_add", {"text": "第一节课要讲第三章"}) text = features.run("note_list", {}) check("笔记能记能读", "第三章" in text, text[:80]) features.run("todo_add", {"text": "批改作业"}) text = features.run("todo_list", {}) check("待办能加能读", "批改作业" in text, text[:80]) features.run("clip_fav_add", {}) features.run("clip_fav_list", {}) print() print("四、安全边界(重点)") escapes = [ r"C:\\Windows\\System32", r"C:\\Windows\\System32\\drivers\\etc\\hosts", r"..\\..\\Windows", "/etc/passwd", "C:\\\\", ] for bad in escapes: try: resolved = features.resolve_in_roots(bad) check(f"拒绝越权路径 {bad}", False, f"竟然解析成 {resolved}") except ValueError: check(f"拒绝越权路径 {bad}", True) try: features.resolve_in_roots("Downloads") check("允许自己的目录(Downloads)", True) except ValueError as exc: check("允许自己的目录(Downloads)", False, str(exc)) # 删除功能只能删允许范围内的 try: features.run("delete_file", {"path": r"C:\\Windows\\notepad.exe"}) check("删除功能也不能越界", False, "竟然允许了") except ValueError: check("删除功能也不能越界", True) print() print("五、边界可配置") extra = tmp / "课件" extra.mkdir(parents=True, exist_ok=True) store.set("file_roots", str(extra)) roots = features.allowed_roots() check("额外目录能被加进白名单", any(str(extra) == str(r) for r in roots), str(roots[-3:])) try: resolved = features.resolve_in_roots(str(extra / "第一课.pptx")) check("新加的目录内可以访问", str(extra) in str(resolved)) except ValueError as exc: check("新加的目录内可以访问", False, str(exc)) store.set("extra_apps", "投影仪=C:\\\\Program Files\\\\App\\\\proj.exe") apps = features.extra_apps() check("自定义程序能配置", "投影仪" in apps, str(apps)) store.set("extra_commands", "重启投影=cmd /c restart-proj") cmds = features.extra_commands() check("自定义命令能配置", "重启投影" in cmds, str(cmds)) print() print("六、不存在/非法功能名") for bad in ("not_a_feature", "", "delete_everything"): try: features.run(bad, {}) check(f"拒绝未登记的功能 {bad!r}", False, "竟然执行了") except ValueError: check(f"拒绝未登记的功能 {bad!r}", True) shutil.rmtree(tmp, ignore_errors=True) print() print("=" * 62) print(f"通过 {PASSED} 项,失败 {FAILED} 项") return 1 if FAILED else 0 if __name__ == "__main__": sys.exit(main()) ''' def main() -> int: text = LAYOUT.read_text(encoding="utf-8") if LAYOUT_OLD not in text: print(" [失败] check_layout 锚点未匹配") return 1 text = text.replace(LAYOUT_OLD, LAYOUT_NEW, 1) LAYOUT.write_text(text, encoding="utf-8") print(" check_layout.py: 已加「功能」分页") target = ROOT / "_tests" / "test_features.py" target.write_text(TEST, encoding="utf-8") print(f" 已写 {target.name}") return 0 if __name__ == "__main__": sys.exit(main())