mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 10:10:43 +00:00
被控端 - 修复 MTU 问题:夹 MSS + 路径 MTU 探测(公网可用率 48% -> 100%) - 复用 HTTP 连接(免掉每次新建 TLS 的 30% 握手失败) - 每分钟回头找局域网,自动切回快 400 倍的路径 - 画面心跳帧:静止时省流量,客户端仍知道流是活的 - 锁屏时给出可操作的提示 手表端 - 内置 GTS Root R4 / GlobalSign Root CA,修 SSLHandshakeException - 连接错误翻译成人话(证书/时间/解析/超时分别提示) - 经中转连上后用服务器报的地址刷新已配对记录 - 新增局域网连接页面、服务器延迟检测 服务器端 - 支持 chunked 请求体(修每 16 秒一次的连接错乱与大面积 502) - 网络调优脚本 net-tune.sh(开机自动应用) - 管理页:批量删除、网络体检、下载区 - 隧道改 HTTP/2 + 双连接 文档 - 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
181 lines
6.1 KiB
Python
181 lines
6.1 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""验证首次设置窗口(界面重做之后:整个程序只有这一个窗口)。
|
||
|
||
1. 全新安装 → 出现设置窗口:配对码输入框 + 自启/防火墙勾选框 + 按钮
|
||
2. 输入 6 位数字点确定 → 返回结果里带着码
|
||
3. 输入太短的码 → 被拒绝,窗口不关
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import shutil
|
||
import sys
|
||
import tempfile
|
||
import time
|
||
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 walk(widget):
|
||
yield widget
|
||
try:
|
||
for child in widget.winfo_children():
|
||
yield from walk(child)
|
||
except tk.TclError:
|
||
return
|
||
|
||
|
||
def inspect(window) -> dict:
|
||
"""看看窗口上有什么控件。"""
|
||
root = window.root
|
||
return {
|
||
"entries": [w for w in walk(root) if isinstance(w, tk.Entry)],
|
||
"checks": [w for w in walk(root) if isinstance(w, tk.Checkbutton)],
|
||
"buttons": [w for w in walk(root) if isinstance(w, tk.Button)],
|
||
"labels": [str(w.cget("text")) for w in walk(root)
|
||
if isinstance(w, tk.Label)],
|
||
}
|
||
|
||
|
||
def fill_and_confirm(window, code: str) -> bool:
|
||
parts = inspect(window)
|
||
if parts["entries"]:
|
||
entry = parts["entries"][0]
|
||
entry.delete(0, "end")
|
||
entry.insert(0, code)
|
||
for button in parts["buttons"]:
|
||
try:
|
||
if "确定" in str(button.cget("text")):
|
||
button.invoke()
|
||
return True
|
||
except tk.TclError:
|
||
continue
|
||
return False
|
||
|
||
|
||
def main() -> int:
|
||
from lanremote.config import Store
|
||
from lanremote.ui import SetupWindow
|
||
|
||
work = Path(tempfile.mkdtemp(prefix="lr_setup2_"))
|
||
|
||
print("一、全新安装:应该弹出设置窗口")
|
||
store = Store(work)
|
||
window = SetupWindow(store)
|
||
found: dict = {}
|
||
|
||
def ready(win):
|
||
root = win.root
|
||
# 多刷几轮,等布局稳定(刚建好时 winfo_width 还可能是初始值)
|
||
for _ in range(6):
|
||
root.update_idletasks()
|
||
root.update()
|
||
time.sleep(0.2)
|
||
|
||
parts = inspect(win)
|
||
found["entries"] = len(parts["entries"])
|
||
found["checks"] = len(parts["checks"])
|
||
found["buttons"] = len(parts["buttons"])
|
||
found["labels"] = parts["labels"]
|
||
|
||
children = root.winfo_children()
|
||
found["metrics"] = {
|
||
"width": root.winfo_width(),
|
||
"height": root.winfo_height(),
|
||
"children": len(children),
|
||
"mapped": sum(1 for c in children if c.winfo_ismapped()),
|
||
"reqheight": root.winfo_reqheight(),
|
||
"entry_mapped": any(e.winfo_ismapped() for e in parts["entries"]),
|
||
}
|
||
found["confirmed"] = fill_and_confirm(win, "789113")
|
||
|
||
result = window.show(on_ready=ready)
|
||
|
||
print(f" 输入框 {found.get('entries')} 个,"
|
||
f"勾选框 {found.get('checks')} 个,按钮 {found.get('buttons')} 个")
|
||
for text in found.get("labels", []):
|
||
if text.strip():
|
||
print(f" 文案: {text[:40]!r}")
|
||
|
||
# ---- 界面必须真的显示出来 ----
|
||
# 踩过的坑:_card() 返回了内层 frame,内层挂在从未 pack 的外层里,
|
||
# 于是两张卡片完全不显示,窗口只有 199px 高 —— 但控件都在,
|
||
# 光数控件个数查不出来。所以这里断言"够高 + 子控件是 mapped 的"。
|
||
metrics = found.get("metrics", {})
|
||
print(f" 窗口 {metrics.get('width')}x{metrics.get('height')},"
|
||
f"mapped 子控件 {metrics.get('mapped')}/{metrics.get('children')}")
|
||
check("设置窗口够高(卡片真的画出来了)",
|
||
(metrics.get("height") or 0) >= 400,
|
||
f"高度只有 {metrics.get('height')}(<400 说明卡片没显示)")
|
||
check("有多个子控件被真正映射(mapped)",
|
||
(metrics.get("mapped") or 0) >= 2, metrics)
|
||
check("配对码输入框是可见的",
|
||
metrics.get("entry_mapped") is True, metrics)
|
||
|
||
check("设置窗口有输入框", found.get("entries", 0) >= 1, str(found))
|
||
check("窗口上有勾选框(自启 / 防火墙)", found.get("checks", 0) >= 1,
|
||
f"{found.get('checks')} 个")
|
||
check("窗口上有按钮", found.get("buttons", 0) >= 2, f"{found.get('buttons')} 个")
|
||
check("点了确定", found.get("confirmed") is True)
|
||
|
||
check("返回了结果", isinstance(result, dict), str(result))
|
||
if isinstance(result, dict):
|
||
check("配对码是 789113", result.get("code") == "789113", str(result))
|
||
check("带上了自启选项", "autostart" in result, str(result))
|
||
check("带上了防火墙选项", "firewall" in result, str(result))
|
||
|
||
print()
|
||
print("二、太短的码要被拒绝")
|
||
bad_store = Store(Path(tempfile.mkdtemp(prefix="lr_bad_")))
|
||
bad = SetupWindow(bad_store)
|
||
state = {"tried_short": False}
|
||
|
||
def ready_bad(win):
|
||
state["tried_short"] = fill_and_confirm(win, "123")
|
||
def later():
|
||
try:
|
||
if win.root.winfo_exists():
|
||
fill_and_confirm(win, "123456")
|
||
except tk.TclError:
|
||
pass
|
||
|
||
win.root.after(400, later)
|
||
|
||
result_bad = bad.show(on_ready=ready_bad)
|
||
check("太短的码没有让窗口关闭(后来又输了对的)",
|
||
isinstance(result_bad, dict) and result_bad.get("code") == "123456",
|
||
str(result_bad))
|
||
|
||
print()
|
||
print("三、随机码建议是 6 位")
|
||
fresh = Store(Path(tempfile.mkdtemp(prefix="lr_fresh_")))
|
||
suggestion = fresh.new_pairing_code()
|
||
check("默认建议 6 位", len(suggestion) == 6, suggestion)
|
||
check("建议是纯数字", suggestion.isdigit(), suggestion)
|
||
|
||
shutil.rmtree(work, ignore_errors=True)
|
||
|
||
print()
|
||
print("=" * 62)
|
||
print(f"通过 {PASSED} 项,失败 {FAILED} 项")
|
||
return 1 if FAILED else 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|