# -*- coding: utf-8 -*- """修界面重做后的三个测试问题。 1. test_selfcheck.py / test_pair_once_flow.py 还在用 `--minimized` 启动。run_agent 现在没设置过配对码就弹设置窗口 (阻塞在 mainloop 上),所以起不来。改用 `--add-device`: 静默运行 + 临时开放配对,正好是这两个测试需要的。 2. test_image_modes.py 它自己调 root.update(),但 HeadlessUI 的泵线程**也在** update()。 Tk 不是线程安全的 —— 两个线程同时 update 会让泵线程挂掉, 于是 ui_call 永远等不到结果(界面响应超时)。 改成:测试不再碰 update(),几何读取统一走 ui_call 丢给泵线程。 """ from __future__ import annotations import sys from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") TESTS = ROOT / "_tests" FIXES = [ (TESTS / "test_selfcheck.py", '''[sys.executable, "run_agent.py", "--port", str(PORT), "--new-code"],''', '''# --add-device:静默运行 + 临时开放配对。 # 不能用默认启动方式 —— 没设置过配对码会弹设置窗口,卡住。 [sys.executable, "run_agent.py", "--port", str(PORT), "--new-code", "--add-device", "10"],''', "selfcheck 换启动方式"), (TESTS / "test_pair_once_flow.py", ''' proc = subprocess.Popen([sys.executable, str(AGENT), "--port", str(PORT), "--minimized"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) check("带窗口模式起来了", wait_port(PORT))''', ''' # --add-device:静默运行 + 临时开放配对(等价于"第一次配对"那段) proc = subprocess.Popen([sys.executable, str(AGENT), "--port", str(PORT), "--add-device", "10"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) check("被控端起来了", wait_port(PORT))''', "pair_once 换启动方式"), ] # ---- test_image_modes.py 的线程问题 ---- IMAGE_PUMP_OLD = '''def pump(root: tk.Tk, seconds: float) -> None: deadline = time.time() + seconds while time.time() < deadline: try: root.update() except tk.TclError: return time.sleep(0.02)''' IMAGE_PUMP_NEW = '''def pump(root, seconds: float) -> None: """等界面稳定。 注意:**不要**在这里调 root.update()。HeadlessUI 有自己的泵线程在 update(),而 Tk 不是线程安全的 —— 两个线程同时 update 会让泵线程 静默挂掉,之后 ui_call 就永远等不到结果。这里只需要等。 """ time.sleep(seconds)''' IMAGE_GEO_OLD = '''def geometry_of(win) -> tuple[int, int, int, int] | None: """返回窗口客户区的 (left, top, width, height)。"""''' IMAGE_GEO_NEW = '''def geometry_of_via(agent, win): """在界面线程上读窗口几何(跨线程读 Tk 同样不安全)。""" if win is None: return None def read(): try: if not win.winfo_exists(): return None win.update_idletasks() left = win.winfo_rootx() top = win.winfo_rooty() width = win.winfo_width() height = win.winfo_height() if width <= 1 or height <= 1: return None return (left, top, width, height) except tk.TclError: return None try: return agent.ui_call(read, timeout=5.0) except Exception: # noqa: BLE001 return None def geometry_of(win) -> tuple[int, int, int, int] | None: """返回窗口客户区的 (left, top, width, height)(兼容保留)。"""''' def main() -> int: ok = True for path, old, new, tag in FIXES: if not path.is_file(): print(f" [跳过] {tag} —— 文件不存在") ok = False continue text = path.read_text(encoding="utf-8") if old not in text: print(f" [跳过] {tag} —— 锚点没找到") ok = False continue path.write_text(text.replace(old, new, 1), encoding="utf-8") print(f" {tag}: OK") # image_modes:改 pump + 加跨线程几何读取 image = TESTS / "test_image_modes.py" text = image.read_text(encoding="utf-8") for old, new, tag in ((IMAGE_PUMP_OLD, IMAGE_PUMP_NEW, "pump 不再 update"), (IMAGE_GEO_OLD, IMAGE_GEO_NEW, "加 geometry_of_via")): if old not in text: print(f" [跳过] {tag} —— 锚点没找到") ok = False continue text = text.replace(old, new, 1) print(f" {tag}: OK") # 把所有 geometry_of(actions._image_window) 换成走界面线程 text = text.replace("geometry_of(actions._image_window)", "geometry_of_via(agent, actions._image_window)") print(" geometry_of 调用点已改为跨线程安全版本") image.write_text(text, encoding="utf-8") import ast for path in (TESTS / "test_selfcheck.py", TESTS / "test_pair_once_flow.py", image): try: ast.parse(path.read_text(encoding="utf-8")) print(f" {path.name}: 语法 OK") except SyntaxError as exc: print(f" {path.name}: 语法错误 {exc}") ok = False return 0 if ok else 1 if __name__ == "__main__": sys.exit(main())