# -*- coding: utf-8 -*- """打「默认中转地址改成空白」之后的回归面。 重点: 1. 没配中转地址时,enable_relay() 的返回值对不对? 它先 self._relay = RelayLink(...) 再 start(), 而 start("") 会提前 return False(不起线程)—— 那么第二次调用 enable_relay() 时, `if self._relay is not None: return True` 会不会**谎报成功**? 2. 空白默认下,局域网监听是否照常起来(这是用户的核心用法)。 3. 手表端 DEFAULT_RELAY_URL="" 时,取地址的分支会不会拿到 null/空。 """ from __future__ import annotations import sys import threading import time from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "windows")) passed = 0 failed = 0 def check(name: str, ok: bool, detail: str = "") -> bool: global passed, failed if ok: passed += 1 print(" [PASS] " + name) else: failed += 1 print(" [FAIL] " + name + ((" —— " + str(detail)) if detail else "")) return ok class FakeStore: """最小可用的设置存储,只实现 relay 相关那几个键。""" def __init__(self, values=None): self._d = dict(values or {}) def get(self, key, default=None): return self._d.get(key, default) def set(self, key, value): self._d[key] = value def pairing_code(self): return self._d.get("pairing_code", "123456") def main() -> int: from lanremote import server as srv from lanremote.relay import RelayLink print("═══ 1. 默认中转地址确实是空 ═══") check("DEFAULT_RELAY_URL 是空白", srv.DEFAULT_RELAY_URL.strip() == "", repr(srv.DEFAULT_RELAY_URL)) print() print("═══ 2. RelayLink.start('') 的行为 ═══") store = FakeStore({"pairing_code": "123456"}) before = threading.active_count() link = RelayLink(store, log=lambda m: None) ret = link.start("") after = threading.active_count() check("start('') 返回 False", ret is False, ret) check("start('') 没有新建线程", after == before, f"{before} -> {after}") check("start('') 之后 candidates 是空的", not getattr(link, "candidates", None), link.candidates) print() print("═══ 3. enable_relay 连续调用两次的返回值 ═══") # 直接构造 Server 太麻烦(要起监听),所以复刻 enable_relay 的逻辑分支, # 用真实 RelayLink 走一遍,看返回值对不对。 store2 = FakeStore({"pairing_code": "123456", "relay_url": ""}) class Probe: def __init__(self): self._relay = None self._logs = [] def _log(self, msg): self._logs.append(str(msg)) def _alert(self, *a, **k): pass # ⚠ 这是 server.py 里 enable_relay 的**副本**。 # 副本的问题是:实现改了它不会跟着变,于是测试红了、 # 实现其实没问题(我修完真实现时这里就红了一次)。 # 所以下面第 3.5 节加了一条**直接读源码**的守卫兜底。 def enable_relay(self, url: str = "") -> bool: target = (url or "").strip() or str( store2.get("relay_url") or "") if not target.strip(): target = srv.DEFAULT_RELAY_URL if not target.strip(): self._log("没配中转地址,只走局域网" "(需要跨网络时在设置里填中转地址)") # 真实现:看**是否真的在跑**,不是"对象建过没有" if self._relay is not None: return bool(getattr(self._relay, "running", False)) self._relay = RelayLink(store2, log=self._log) return self._relay.start(target) p = Probe() r1 = p.enable_relay() r2 = p.enable_relay() print(f" 第 1 次 enable_relay() -> {r1}") print(f" 第 2 次 enable_relay() -> {r2}") print(f" 日志: {p._logs}") check("第 1 次调用返回 False(没有中转可连)", r1 is False, r1) if r2 is True: check("第 2 次调用也应返回 False(否则是谎报成功)", False, f"第 2 次返回 {r2} —— 但 relay 实际没在跑" f"(candidates={getattr(p._relay, 'candidates', None)})") print(" 🔴 复现:enable_relay() 第二次调用返回 True,") print(" 因为 self._relay 已经被赋值成 RelayLink 对象了,") print(" 而 `if self._relay is not None: return True` 没检查它是否真的在跑。") else: check("第 2 次调用也应返回 False", r2 is False, r2) print() print("═══ 4. 这个返回值会影响谁 ═══") import inspect src = inspect.getsource(srv) callers = [] for i, line in enumerate(src.splitlines(), 1): if "enable_relay" in line and "def " not in line: callers.append((i, line.strip())) if callers: print(" server.py 里调用 enable_relay 的地方:") for ln, line in callers: print(f" {ln}: {line}") else: print(" server.py 内部没有别的调用点(由外部/UI 触发)") # 找整个仓库里谁用了返回值 print() print(" 全仓库搜索 enable_relay 的调用点:") hits = 0 for py in (ROOT / "windows").rglob("*.py"): try: text = py.read_text(encoding="utf-8") except Exception: # noqa: BLE001 continue for i, line in enumerate(text.splitlines(), 1): if "enable_relay" in line: rel = py.relative_to(ROOT) print(f" {rel}:{i}: {line.strip()}") hits += 1 if not hits: print(" (没有别的调用点)") print() print("═══ 5. 空白默认下局域网发现仍可用吗 ═══") try: from lanremote import discovery check("discovery 模块能导入(不依赖 DEFAULT_RELAY_URL)", True) # 真的跑一次发现(会广播,2.5 秒超时) t0 = time.time() found = discovery.discover(timeout=2.0) dt = time.time() - t0 check("discover() 能跑完不抛异常", True, f"耗时 {dt:.1f}s") check("discover() 返回的是列表", isinstance(found, list), type(found)) print(f" · 发现到 {len(found)} 台设备(本机没有 agent 在跑," f"0 台是正常的)") except Exception as exc: # noqa: BLE001 check("discover() 能跑完不抛异常", False, f"{type(exc).__name__}: {exc}") print() print("=" * 62) print(f" 通过 {passed} 项,失败 {failed} 项") print("=" * 62) return 1 if failed else 0 if __name__ == "__main__": sys.exit(main())