120 lines
3.8 KiB
Python
120 lines
3.8 KiB
Python
"""自动验证「环境准备」窗口的检测 → 确认 → 放行 流程(不需要人工点按钮)。
|
||
|
||
验证点:
|
||
1. 所有项最终都能检测完(不会卡在「检测中…」)
|
||
2. 全部就绪时,「进入程序」按钮变为可用
|
||
3. 调用 _on_confirm 后 proceeded=True(main() 才会继续启动服务)
|
||
4. 关闭窗口(_on_close)时 proceeded=False(main() 应当退出)
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
import time
|
||
import traceback
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parent.parent
|
||
sys.path.insert(0, str(ROOT))
|
||
|
||
import tkinter as tk # noqa: E402
|
||
|
||
from core.setup_window import SetupWindow # noqa: E402
|
||
|
||
FAILURES: list[str] = []
|
||
CALLBACK_ERRORS: list[str] = []
|
||
|
||
|
||
def check(name: str, ok: bool, detail: str = "") -> None:
|
||
print(f" [{'PASS' if ok else 'FAIL'}] {name}" + (f" — {detail}" if detail else ""))
|
||
if not ok:
|
||
FAILURES.append(name)
|
||
|
||
|
||
def pump(root: tk.Tk, seconds: float) -> None:
|
||
"""手动驱动 tkinter 事件循环(不进入 mainloop,便于断言)。"""
|
||
deadline = time.monotonic() + seconds
|
||
while time.monotonic() < deadline:
|
||
try:
|
||
root.update()
|
||
except tk.TclError as exc:
|
||
print(f" [pump] TclError 中断:{exc}")
|
||
return
|
||
time.sleep(0.02)
|
||
|
||
|
||
def main() -> int:
|
||
root = tk.Tk()
|
||
|
||
def _on_cb_error(exc_type, exc_value, exc_tb) -> None: # Tk 回调里的异常
|
||
text = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
|
||
CALLBACK_ERRORS.append(text)
|
||
print(f" [回调异常] {exc_value}")
|
||
|
||
root.report_callback_exception = _on_cb_error
|
||
|
||
win = SetupWindow(root, title="自检窗口")
|
||
# Apple 驱动检测要做 27015 协议往返,留足时间
|
||
pump(root, 25.0)
|
||
|
||
print(" 各项状态:")
|
||
for item in win.items:
|
||
st = win.state[item.key]
|
||
print(f" {item.key:16s} required={item.required} ok={st.get('ok')} "
|
||
f"busy={st.get('busy')} detail={str(st.get('detail'))[:46]}")
|
||
|
||
try:
|
||
exists = bool(root.winfo_exists())
|
||
except tk.TclError as exc:
|
||
exists = False
|
||
print(f" 窗口已不可用:{exc}")
|
||
print(f" 窗口存在:{exists} proceeded={win.proceeded}")
|
||
check("窗口在检测期间没有被意外销毁", exists)
|
||
check("Tk 回调没有抛异常", not CALLBACK_ERRORS,
|
||
f"{len(CALLBACK_ERRORS)} 个" if CALLBACK_ERRORS else "")
|
||
|
||
stuck = [i.key for i in win.items
|
||
if win.state[i.key].get("ok") is None and not win.state[i.key].get("busy")]
|
||
check("没有卡在「检测中…」的项", not stuck, str(stuck))
|
||
|
||
required = [i for i in win.items if i.required]
|
||
pending = [i.key for i in required if win.state[i.key].get("ok") is not True]
|
||
check("必需项全部就绪(本机环境)", not pending, str(pending))
|
||
|
||
try:
|
||
enabled = bool(win.confirm_button.instate(["!disabled"]))
|
||
detail = f"state={win.confirm_button.state()}"
|
||
except tk.TclError as exc:
|
||
enabled, detail = False, f"控件已失效:{exc}"
|
||
check("「进入程序」按钮可用", enabled, detail)
|
||
|
||
# 模拟点击「进入程序」
|
||
win._on_confirm()
|
||
check("点击后 proceeded=True", win.proceeded is True, f"proceeded={win.proceeded}")
|
||
try:
|
||
root.destroy()
|
||
except Exception:
|
||
pass
|
||
|
||
# 再测「关闭窗口」分支
|
||
root2 = tk.Tk()
|
||
win2 = SetupWindow(root2, title="自检窗口2")
|
||
pump(root2, 1.0)
|
||
win2._on_close()
|
||
check("关闭窗口时 proceeded=False", win2.proceeded is False,
|
||
f"proceeded={win2.proceeded}")
|
||
try:
|
||
root2.destroy()
|
||
except Exception:
|
||
pass
|
||
|
||
print()
|
||
if FAILURES:
|
||
print(f"失败 {len(FAILURES)} 项:{FAILURES}")
|
||
return 1
|
||
print("全部通过")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|