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
+280
View File
@@ -0,0 +1,280 @@
# -*- coding: utf-8 -*-
"""四项 UI 增强回归测试(真实 WebEngine 页面):
1. rAF 卡死模拟(AMD 核显/合成器不出帧)→ 40ms 兜底仍实时渲染流式正文
2. 深度思考默认收起 + .streaming-think 蓝色动画类;收尾后标签还原
3. bash chip 耗时徽章([exit 0] (1.2s)+ 超时徽章(命令超时(>120s))
4. 长结果:默认尾部 4000 字 + 展开按钮;点击展开全文/再点收起;
收起状态下 finish 后结果仍可见(修复「结束后展开为空」)
运行: python tests/smoke_live_guard.py
"""
import json
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
os.environ.setdefault("HAOCODE_RENDER", "software")
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--disable-gpu" # 绕过 AMD 核显 context lost
from PyQt6.QtWidgets import QApplication # noqa: E402
from PyQt6.QtCore import QTimer # noqa: E402
from ui.views.main_window import MainWindow # noqa: E402
app = QApplication(sys.argv)
window = None
results = []
def check(name, ok, detail=""):
print(f" {'PASS' if ok else 'FAIL'} {name}"
+ (f" [{detail}]" if detail and not ok else ""))
results.append(ok)
# Phase 0: rAF + 所有页面定时器全灭(极端节流)→
# 内容必须上屏:同步通道(token 内)+ Qt 看门狗(200msforceRenderNow
JS_PHASE_0A = r"""
(function() {
var out = {};
window.__realRAF = window.requestAnimationFrame;
window.__realCAF = window.cancelAnimationFrame;
window.__realTO = window.setTimeout;
window.__realCTO = window.clearTimeout;
window.__realSI = window.setInterval;
window.__realCSI = window.clearInterval;
window.__fakeRafId = 0;
window.__fakeTimerId = 1000;
window.requestAnimationFrame = function() { return ++window.__fakeRafId; };
window.cancelAnimationFrame = function(){};
window.setTimeout = function() { return ++window.__fakeTimerId; };
window.clearTimeout = function(){};
window.setInterval = function() { return ++window.__fakeTimerId; };
window.clearInterval = function(){};
var m0 = 'lg-sync-' + Date.now();
window.__lgM0 = m0;
createMessage(m0, 'assistant', '', 'LG');
appendReasoning(m0, 'sync think ');
appendToken(m0, 'sync text one ');
appendToken(m0, 'two');
var segs = document.querySelectorAll('#' + m0 + ' .md-segment');
var t = '';
for (var i = 0; i < segs.length; i++) t += segs[i].textContent || '';
out.textAtBurst = t;
return JSON.stringify(out);
})()
"""
JS_PHASE_0B = r"""
(function() {
var out = {};
var m0 = window.__lgM0;
var segs = document.querySelectorAll('#' + m0 + ' .md-segment');
var t = '';
for (var i = 0; i < segs.length; i++) t += segs[i].textContent || '';
var tcs = document.querySelectorAll('#' + m0 + ' .think-content');
var th = '';
for (var i = 0; i < tcs.length; i++) th += tcs[i].textContent || '';
out.text = t;
out.think = th;
// 恢复页面定时器
window.requestAnimationFrame = window.__realRAF;
window.cancelAnimationFrame = window.__realCAF;
window.setTimeout = window.__realTO;
window.clearTimeout = window.__realCTO;
window.setInterval = window.__realSI;
window.clearInterval = window.__realCSI;
finishMessage(m0);
return JSON.stringify(out);
})()
"""
# Phase A: 模拟 rAF 永不触发 + 流式 token;断言此刻 DOM 尚空(卡死生效)
JS_PHASE_A = r"""
(function() {
var out = {};
window.__realRAF = window.requestAnimationFrame;
window.__realCAF = window.cancelAnimationFrame;
// 模拟真实卡死:rAF 返回真实挂起 ID(truthy)但回调永不被调用
window.__fakeRafId = 0;
window.requestAnimationFrame = function(cb) { window.__rafCalls = (window.__rafCalls||0)+1; return ++window.__fakeRafId; };
window.cancelAnimationFrame = function(){};
var mid = 'lg-' + Date.now();
window.__lgMid = mid;
createMessage(mid, 'assistant', '', 'LG');
appendReasoning(mid, 'deep ');
appendReasoning(mid, 'thinking...');
appendToken(mid, 'live text one ');
appendToken(mid, 'two ');
appendToken(mid, 'three');
var segs = document.querySelectorAll('#' + mid + ' .md-segment');
var domLen = 0;
for (var i = 0; i < segs.length; i++)
domLen += (segs[i].textContent || '').length;
out.rafCalls = window.__rafCalls || 0;
out.domLenBeforeTimers = domLen;
// 思考块状态(默认收起 + 蓝色动画类)
var block = document.querySelector('#' + mid + ' .think-block');
out.thinkOpen = block ? block.open : null;
out.thinkStreaming = block ? block.classList.contains('streaming-think') : false;
out.thinkLabel = block ? (block.querySelector('.think-label')||{}).textContent : null;
return JSON.stringify(out);
})()
"""
# Phase Btimers 跑过之后): 兜底应已渲染正文 + 收尾 + 其余特性
JS_PHASE_B = r"""
(function() {
var out = {};
var mid = window.__lgMid;
var segs = document.querySelectorAll('#' + mid + ' .md-segment');
var domText = '';
for (var i = 0; i < segs.length; i++) domText += segs[i].textContent || '';
out.liveTextRendered = domText.indexOf('live text one two three') !== -1;
// 收尾:思考块标签还原 + 动画类移除
finishMessage(mid);
var block = document.querySelector('#' + mid + ' .think-block');
out.thinkLabelAfter = block ? (block.querySelector('.think-label')||{}).textContent : null;
out.thinkStreamingAfter = block ? block.classList.contains('streaming-think') : false;
// 恢复 rAF
window.requestAnimationFrame = window.__realRAF;
window.cancelAnimationFrame = window.__realCAF;
// ---- 耗时 / 超时徽章 ----
var mid2 = 'lg-timing-' + Date.now();
createMessage(mid2, 'assistant', '', 'LG');
toolExecutionStarted(mid2, 'call-ok', 'bash', JSON.stringify({command:'echo hi'}));
toolExecutionFinished(mid2, 'call-ok', 'bash', true, '$ echo hi\nhi\n[exit 0] (1.2s)');
toolExecutionStarted(mid2, 'call-tmo', 'bash', JSON.stringify({command:'sleep 999'}));
toolExecutionFinished(mid2, 'call-tmo', 'bash', false, '命令超时(>120s)已终止');
var w2 = document.getElementById(mid2);
var chipOk = w2.querySelector('[data-call-id="call-ok"]');
var chipTmo = w2.querySelector('[data-call-id="call-tmo"]');
out.durBadge = chipOk ? (chipOk.querySelector('.tool-chip-time')||{}).textContent : null;
out.tmoBadge = chipTmo ? (chipTmo.querySelector('.tool-chip-timeout')||{}).textContent : null;
// ---- 收起状态下 finish:结果必须已写入(展开可见) ----
var livePre = chipOk ? chipOk.querySelector('.tool-chip-live') : null;
out.resultWhileCollapsed = livePre ? (livePre.textContent || '').indexOf('hi') !== -1 : false;
// ---- 长结果:尾部预览 + 展开/收起 ----
var longRes = 'L'.repeat(1000) + ' MIDDLE-MARKER ' + 'R'.repeat(5000); // 6015 字,标记在头部(预览窗外)
toolExecutionStarted(mid2, 'call-long', 'read', JSON.stringify({path:'/big.txt'}));
toolExecutionFinished(mid2, 'call-long', 'read', true, longRes);
var chipLong = w2.querySelector('[data-call-id="call-long"]');
var pre = chipLong ? chipLong.querySelector('.tool-chip-live') : null;
var btn = chipLong ? chipLong.querySelector('.tool-expand-btn') : null;
out.longHasBtn = !!btn;
out.longPreviewLen = pre ? (pre.textContent || '').length : 0;
out.longPreviewIsTail = pre ? (pre.textContent || '').lastIndexOf('R') > (pre.textContent||'').lastIndexOf('MIDDLE-MARKER') : false;
out.longPreviewHasMarker = pre ? (pre.textContent || '').indexOf('MIDDLE-MARKER') !== -1 : true;
if (btn) {
btn.click();
out.expandedHasMarker = pre ? (pre.textContent || '').indexOf('MIDDLE-MARKER') !== -1 : false;
out.expandedLen = pre ? (pre.textContent || '').length : 0;
out.fullClass = pre ? pre.classList.contains('tool-chip-full') : false;
btn.click();
out.collapseBackLen = pre ? (pre.textContent || '').length : 0;
}
return JSON.stringify(out);
})()
"""
def step1():
global window
print("[step1] creating MainWindow...", flush=True)
window = MainWindow()
window.show()
print("[step1] shown, scheduling step2", flush=True)
QTimer.singleShot(3000, step2)
def step2():
print("[step2] phase 0: kill all page timers + burst tokens", flush=True)
# 注册为当前会话的活跃流 → Qt 看门狗(200ms)会对它 forceRenderNow
window.current_session_id = "fake-sync-sid"
window._active_streams["fake-sync-sid"] = {
"msg_id": None, "content": "pending", "reasoning": "",
"timeline": [], "tl_kind": None, "parent_id": None,
"branch_info": None, "worker": None, "previous_leaf_id": None,
}
window.browser.page().runJavaScript(JS_PHASE_0A, on_p0a)
def on_p0a(res):
a = json.loads(str(res))
print(f" phase0 burst 瞬间: text={a['textAtBurst']!r}")
# 把 JS 里生成的 mid 告诉看门狗(msg_id 需一致才会 forceRenderNow
# —— 简化:直接改 JS 端记录的 mid 对应的 buffer 即可,这里用 runJavaScript 对齐
window.browser.page().runJavaScript(
"window.__lgM0", lambda r: _align_and_wait(str(r)))
def _align_and_wait(mid):
mid = mid.strip().strip('"')
window._active_streams["fake-sync-sid"]["msg_id"] = mid
print("[phase0] 等待 Qt 看门狗渲染 (400ms) ...", flush=True)
QTimer.singleShot(400, on_p0b)
def on_p0b():
window.browser.page().runJavaScript(JS_PHASE_0B, on_p0)
def on_p0(res):
p = json.loads(str(res))
print(f" phase0 400ms 后: text={p['text']!r} think={p['think']!r}")
check("定时器全灭: burst 内思考同步上屏", "sync think" in p["think"], str(p))
check("定时器全灭: 正文经同步通道/看门狗上屏",
"sync text one two" in p["text"], str(p))
window._active_streams.pop("fake-sync-sid", None)
window.browser.page().runJavaScript(JS_PHASE_A, on_a)
def on_a(res):
print("[on_a] got result:", str(res)[:120], flush=True)
a = json.loads(str(res))
print(f" phaseA: rafCalls={a['rafCalls']} domLenBefore={a['domLenBeforeTimers']}")
# v7 起:首 token 插入前已同步渲染(:empty 布局失效修复的副作用——首字必现),
# 故此刻 DOM 可能已有首 token 内容;本断言只验证 rAF 被调用但从未触发。
check("rAF 被卡死(0 帧触发)", a["rafCalls"] > 0, str(a))
check("思考块默认收起", a["thinkOpen"] is False)
check("思考块带蓝色动画类 streaming-think", a["thinkStreaming"])
QTimer.singleShot(300, step3)
def step3():
window.browser.page().runJavaScript(JS_PHASE_B, on_b)
def on_b(res):
b = json.loads(str(res))
check("rAF 卡死时兜底仍实时渲染正文", b["liveTextRendered"], str(b))
check("收尾后思考标签还原为『已完成深度思考』",
b["thinkLabelAfter"] == "已完成深度思考", str(b["thinkLabelAfter"]))
check("收尾后移除 streaming-think 动画类", b["thinkStreamingAfter"] is False)
check("耗时徽章 ⏱ 1.2s", b["durBadge"] == "⏱ 1.2s", str(b["durBadge"]))
check("超时徽章 ⏱ 超时 120s", b["tmoBadge"] == "⏱ 超时 120s", str(b["tmoBadge"]))
check("收起状态下 finish 后结果仍可见(修复展开为空)", b["resultWhileCollapsed"])
check("长结果有展开按钮", b["longHasBtn"])
check("长结果默认只显示尾部 4002 字", b["longPreviewLen"] == 4002,
str(b["longPreviewLen"]))
check("尾部预览不含中段标记(标记在头部)", b["longPreviewIsTail"]
and b["longPreviewHasMarker"] is False)
check("点击展开全文(含中段标记 + full 类)",
b["expandedHasMarker"] and b["fullClass"]
and b["expandedLen"] > 5000, str(b))
check("再点收回到尾部预览", b["collapseBackLen"] == 4002,
str(b["collapseBackLen"]))
print(f"\n===== {'ALL PASS' if all(results) else 'HAS FAILURES'}: "
f"{sum(results)}/{len(results)} =====")
app.quit()
QTimer.singleShot(500, step1)
app.exec()