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.
140 lines
5.4 KiB
Python
140 lines
5.4 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""独立调试器窗口 + 调试日志协议 回归测试(离屏,临时文件,不碰真实 DB/日志)
|
||
运行: PYTHONIOENCODING=utf-8 QT_QPA_PLATFORM=offscreen python tests/test_debug_window.py
|
||
"""
|
||
import sys, os, re, tempfile
|
||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||
|
||
_TMPDIR = tempfile.mkdtemp(prefix="haocode_dbg_")
|
||
os.environ["HAOCODE_DEBUG_LOG"] = os.path.join(_TMPDIR, "debug_session.log")
|
||
os.environ["HAOCODE_DEBUG_CMD"] = os.path.join(_TMPDIR, "debug_window.cmd")
|
||
|
||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--disable-gpu"
|
||
|
||
from core import debug_log as dl
|
||
|
||
PASS, FAIL = 0, 0
|
||
|
||
|
||
def check(name, cond, extra=""):
|
||
global PASS, FAIL
|
||
if cond:
|
||
PASS += 1
|
||
print(f" PASS {name}")
|
||
else:
|
||
FAIL += 1
|
||
print(f" FAIL {name} {extra}")
|
||
|
||
|
||
def read_log():
|
||
if not os.path.exists(dl.DEBUG_LOG_PATH):
|
||
return ""
|
||
with open(dl.DEBUG_LOG_PATH, "r", encoding="utf-8") as f:
|
||
return f.read()
|
||
|
||
|
||
LINE_RE = re.compile(r"^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}\] "
|
||
r"\[(USER|AGENT|APP|SYS)\] .+$")
|
||
|
||
# ============ T1: debug_log 三方写入 + 行格式 ============
|
||
print("T1: debug_log 行格式与三方 TAG")
|
||
dl.debug_log("hello app", "APP")
|
||
dl.debug_log("代理注入一条备注", "AGENT")
|
||
dl.debug_log("用户观察到标签 40.5k", "USER")
|
||
lines = [l for l in read_log().splitlines() if l]
|
||
check("三行全部写入", len(lines) == 3, lines)
|
||
check("行格式 [ts] [TAG] msg",
|
||
all(LINE_RE.match(l) for l in lines), lines)
|
||
check("TAG 顺序 APP/AGENT/USER",
|
||
[re.search(r"\[(USER|AGENT|APP|SYS)\]", l).group(1) for l in lines]
|
||
== ["APP", "AGENT", "USER"])
|
||
|
||
# ============ T2: poll_debug_cmd 控制协议 ============
|
||
print("T2: poll_debug_cmd 消费语义")
|
||
with open(dl.DEBUG_CMD_PATH, "w", encoding="utf-8") as f:
|
||
f.write("show\n")
|
||
check("show 被识别", dl.poll_debug_cmd() == "show")
|
||
check("文件被消费(再读为 None)", dl.poll_debug_cmd() is None)
|
||
with open(dl.DEBUG_CMD_PATH, "w", encoding="utf-8") as f:
|
||
f.write(" HIDE ")
|
||
check("hide 大小写/空白容忍", dl.poll_debug_cmd() == "hide")
|
||
with open(dl.DEBUG_CMD_PATH, "w", encoding="utf-8") as f:
|
||
f.write("garbage")
|
||
check("非法内容忽略", dl.poll_debug_cmd() is None)
|
||
|
||
# ============ T3-T6: DebugWindow 行为(离屏) ============
|
||
print("T3: DebugWindow 用户输入 → [USER] 落盘")
|
||
from PyQt6.QtWidgets import QApplication
|
||
app = QApplication.instance() or QApplication(sys.argv)
|
||
import ui.views.debug_window as dw
|
||
# 应用日志 tab 指向临时文件(不读真实 diag.log)
|
||
_tmp_diag = os.path.join(_TMPDIR, "diag.log")
|
||
dw._APP_LOGS[:] = [("DIAG", _tmp_diag)]
|
||
|
||
win = dw.DebugWindow()
|
||
check("窗口标题独立", win.windowTitle() == "Haocode 调试器")
|
||
win._input.setText(" 发送后标签跳到 80k ")
|
||
win._on_submit()
|
||
check("输入框被清空", win._input.text() == "")
|
||
check("[USER] 已落盘且去首尾空白",
|
||
any(l.endswith("[USER] 发送后标签跳到 80k") for l in read_log().splitlines()))
|
||
|
||
print("T4: 首次 tick 记 [SYS] 开启事件")
|
||
win._tick()
|
||
check("[SYS] 调试窗口开启 已写入",
|
||
any("[SYS] 调试窗口开启" in l for l in read_log().splitlines()))
|
||
|
||
print("T5: 实时 tail 会话日志 + 应用日志")
|
||
dl.debug_log("tick 前注入的 APP 事件", "APP")
|
||
dl.debug_log("AGENT: 现在检查 compaction_diag.log", "AGENT")
|
||
win._tick()
|
||
txt = win._view_session.toPlainText()
|
||
check("会话视图含 APP 事件", "tick 前注入的 APP 事件" in txt, txt[-300:])
|
||
check("会话视图含 AGENT 注入", "AGENT: 现在检查 compaction_diag.log" in txt)
|
||
with open(_tmp_diag, "w", encoding="utf-8") as f:
|
||
f.write("[12:00:00.000] DIAG_TEST_LINE\n")
|
||
win._tick()
|
||
check("应用日志视图含 DIAG tail",
|
||
"DIAG_TEST_LINE" in win._view_app.toPlainText())
|
||
check("应用日志带文件头", "[DIAG]" in win._view_app.toPlainText())
|
||
|
||
print("T6: 暂停显示 + 截断重置")
|
||
win._chk_pause.setChecked(True) # 暂停
|
||
dl.debug_log("暂停期间的行不应上屏", "APP")
|
||
win._tick()
|
||
check("暂停期间不上屏", "暂停期间的行不应上屏"
|
||
not in win._view_session.toPlainText())
|
||
# 文件截断(模拟「清空会话日志」)→ 偏移重置,新行仍可读取
|
||
win._chk_pause.setChecked(False)
|
||
with open(dl.DEBUG_LOG_PATH, "w", encoding="utf-8") as f:
|
||
f.write("[2026-01-01 00:00:00.000] [SYS] 截断后新内容\n")
|
||
win._tick()
|
||
check("截断后偏移重置、新行上屏",
|
||
"截断后新内容" in win._view_session.toPlainText())
|
||
|
||
win.close()
|
||
|
||
# ============ T4: 调试窗口随程序启动(autostart_debug_window) ============
|
||
print("T4: 调试窗口随程序启动")
|
||
p = dl.DEBUG_CMD_PATH
|
||
if os.path.exists(p):
|
||
os.remove(p)
|
||
# 显式 false → 不写控制文件
|
||
check("T4.autostart=False 不写控制文件",
|
||
dl.autostart_debug_window({"debug_window_autostart": False}) is False
|
||
and not os.path.exists(p))
|
||
# 缺省(键不存在)→ 默认开
|
||
check("T4.缺省(无键)写入 show",
|
||
dl.autostart_debug_window({}) is True and os.path.exists(p))
|
||
check("T4.轮询消费 show",
|
||
dl.poll_debug_cmd() == "show" and not os.path.exists(p))
|
||
# 显式 true
|
||
check("T4.autostart=true 写入 show",
|
||
dl.autostart_debug_window({"debug_window_autostart": True}) is True)
|
||
check("T4.轮询再消费 show",
|
||
dl.poll_debug_cmd() == "show")
|
||
|
||
print(f"\n===== {PASS} PASS / {FAIL} FAIL =====")
|
||
sys.exit(1 if FAIL else 0)
|