chore: import original project baseline

Import the pre-repair source tree as the history baseline.
Runtime data (data/), virtualenvs, bytecode caches and logs are
gitignored so local secrets and user state stay out of the repo.
This commit is contained in:
2026-09-17 16:40:01 +08:00
commit a7412824e0
124 changed files with 26747 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-
"""
WebView2 守卫单测 —— 锁死 T0 级事故不再复发
事故链(已发生):
每个 MainWindow()(含 offscreen 测试)→ get_environment()
→ 无条件 `taskkill /F /IM msedgewebview2.exe`
→ 把【正在运行的生产 app】的 WebView2 浏览器进程一并杀掉
→ 它的 controller 变 disposedset_bounds 报 0x8007139F
→ DOM 照渲染但视觉层永久空白(“选中会话不渲染核心内容”)
覆盖:
T1 无头环境(QT_QPA_PLATFORM=offscreen)→ 不启用 WebView2
T2 HAOCODE_FORCE_QTWEBENGINE=1 → 不启用
T3 桌面平台(QT_QPA_PLATFORM=windows)→ 允许
T4 单实例锁:持有者独占;【另一个进程】拿不到(跨进程互斥,是真守卫)
T5 offscreen 下 get_environment() 直接返回 None(根本不碰共享 profile
T6 同一进程重复 acquire 幂等返回 True(不会把自己锁死)
运行: PYTHONIOENCODING=utf-8 python tests/test_wv2_guard.py
"""
import os
import sys
import tempfile
import subprocess
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# 铁律:测试用独立锁文件,不得与正在运行的 app 争 data/app_instance.lock
# (否则断言会依赖“app 是否在跑”)
_LOCK_TMP = os.path.join(tempfile.gettempdir(), "haocode_wv2guard_%d.lock" % os.getpid())
os.environ["HAOCODE_INSTANCE_LOCK_FILE"] = _LOCK_TMP
if os.path.exists(_LOCK_TMP):
try:
os.remove(_LOCK_TMP)
except Exception:
pass
RESULTS = []
def check(name, cond, extra=""):
RESULTS.append((name, bool(cond)))
print(f"{'PASS' if cond else 'FAIL'} {name} {extra if not cond else ''}", flush=True)
import core.webview2 as W # noqa: E402
_ORIG = dict(os.environ)
try:
# ---------------- T1/T2/T3: 环境守卫 ----------------
os.environ["QT_QPA_PLATFORM"] = "offscreen"
os.environ.pop("HAOCODE_FORCE_QTWEBENGINE", None)
check("T1.1 offscreen → 不允许 WebView2", W._wv2_allowed_here() is False)
os.environ["QT_QPA_PLATFORM"] = "windows"
check("T1.2 windows → 允许 WebView2", W._wv2_allowed_here() is True)
for p in ("minimal", "minimalegl", "vnc", "wayland", "embedded", "offscreen"):
os.environ["QT_QPA_PLATFORM"] = p
if W._wv2_allowed_here() is not False:
check(f"T1.3 平台 {p} 应被拒绝", False, p)
break
else:
check("T1.3 非 windows 平台全部拒绝", True)
os.environ["QT_QPA_PLATFORM"] = "windows"
os.environ["HAOCODE_FORCE_QTWEBENGINE"] = "1"
check("T2.1 强制回退开关生效", W._wv2_allowed_here() is False)
os.environ.pop("HAOCODE_FORCE_QTWEBENGINE", None)
# ---------------- T4: 跨进程单实例互斥(核心) ----------------
os.environ["QT_QPA_PLATFORM"] = "windows"
W._INSTANCE_LOCK["fh"] = None # 从头开始,避免受本进程历史影响
first = W.acquire_instance_lock()
check("T4.1 首个 acquires 成功", first is True, str(first))
check("T4.2 同进程重复 acquire 幂等为 True",
W.acquire_instance_lock() is True)
code = ("import sys; sys.path.insert(0, r'%s');"
"import core.webview2 as W; print(W.acquire_instance_lock())"
% os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
r = subprocess.run([sys.executable, "-c", code], capture_output=True,
text=True, encoding="utf-8", errors="replace", timeout=60)
got = (r.stdout or "").strip().splitlines()
got = got[-1] if got else ""
check("T4.3 另一进程拿不到锁(互斥生效)", got == "False", f"stdout={got!r} err={(r.stderr or '')[:120]}")
# ---------------- T5: offscreen 下 get_environment 直接 None ----------------
os.environ["QT_QPA_PLATFORM"] = "offscreen"
W._env = None
env = W.get_environment(None)
check("T5.1 offscreen 下 get_environment → None(不碰共享 profile", env is None)
check("T5.2 被守卫拦下后 _env 仍为空", W._env is None)
# ---------------- T6: 已有实例在跑时 → 不启用 WebView2 ----------------
# 此时本进程仍持有锁;用子进程模拟“后来的实例”
code2 = ("import sys; sys.path.insert(0, r'%s');"
"import core.webview2 as W; print(W.get_environment(None) is None)"
% os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
r2 = subprocess.run([sys.executable, "-c", code2], capture_output=True,
text=True, encoding="utf-8", errors="replace",
env={**os.environ, "QT_QPA_PLATFORM": "windows"},
timeout=60)
out2 = (r2.stdout or "").strip().splitlines()
out2 = out2[-1] if out2 else ""
check("T6.1 已有实例持锁 → 后来的实例拿到 None(回落 QtWebEngine,不 taskkill",
out2 == "True", f"stdout={out2!r} err={(r2.stderr or '')[:160]}")
finally:
try:
fh = W._INSTANCE_LOCK.get("fh")
if fh is not None:
fh.close()
except Exception:
pass
try:
if os.path.exists(_LOCK_TMP):
os.remove(_LOCK_TMP)
except Exception:
pass
os.environ.clear()
os.environ.update(_ORIG)
failed = [n for n, ok in RESULTS if not ok]
print(f"\n===== {len(RESULTS) - len(failed)}/{len(RESULTS)} PASS =====", flush=True)
print("ALL PASS" if not failed else f"FAILED: {failed}", flush=True)
sys.exit(0 if not failed else 1)