71 lines
2.8 KiB
Python
71 lines
2.8 KiB
Python
# coding: utf-8
|
||
"""收发日志上限与队列消费回归自检(无窗口, 离屏渲染)。
|
||
|
||
背景: v0.4.3 之前收发日志用 textCursor 手工裁剪, 其中 `cursor.Start` /
|
||
`cursor.BlockUnderCursor` 是实例级枚举访问 —— PySide6 6.11 移除了该形式,
|
||
日志一旦超过 500 行, 每次 _dlpc_log 追加后必抛 AttributeError; 且异常会
|
||
逃出 _poll_dlpc 的 drain 循环(旧 try 只捕获 queue.Empty), 每 tick 最多
|
||
消费到第一条 event, `_dlpc_log_q` 棘轮积压、日志裁剪与自动滚动全部失效,
|
||
电源自检/功率检测序列还会被异常打断在启动处(之后所有命令被"序列进行中"
|
||
拦截)。v0.4.3 改为 setMaximumBlockCount 环形裁剪 + 逐事件异常兜底。
|
||
|
||
本测试保证:
|
||
1. 连续追加 600 行日志不抛异常, document().blockCount() 环形封顶 500, 末行在;
|
||
2. 单条异常事件(模拟未来引入的坏数据)不中断 _poll_dlpc 本轮队列消费
|
||
—— 其后的事件仍全部入日志、队列清空。
|
||
|
||
运行: python selftest_log.py (依赖 PySide6-Fluent-Widgets 与 pyserial ——
|
||
串口卡构建后 txt_dlpc 才存在)
|
||
"""
|
||
import os
|
||
import sys
|
||
|
||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||
|
||
from PySide6.QtWidgets import QApplication
|
||
from qfluentwidgets import Theme, setTheme
|
||
|
||
import app_fluent
|
||
import dlpc_rs485
|
||
|
||
|
||
def check_cap(win):
|
||
for i in range(600):
|
||
win._dlpc_log(f"cap-line-{i}")
|
||
blocks = win.txt_dlpc.document().blockCount()
|
||
has_last = "cap-line-599" in win.txt_dlpc.toPlainText()
|
||
print(f" 追加 600 行: blockCount={blocks}(封顶 500), 末行在: {has_last}")
|
||
return blocks <= 500 and has_last
|
||
|
||
|
||
def check_drain_resilience(win):
|
||
win._timer.stop() # 排除 80ms 轮询定时器干扰
|
||
win._dlpc_log_q.put(("pdframe", "poison-not-a-frame")) # 该条分发必抛异常
|
||
win._dlpc_log_q.put(("event", "after-poison-1"))
|
||
win._dlpc_log_q.put(("event", "after-poison-2"))
|
||
win._poll_dlpc() # 单次手动驱动(不进事件循环)
|
||
left = win._dlpc_log_q.qsize()
|
||
text = win.txt_dlpc.toPlainText()
|
||
drained = left == 0 and "after-poison-1" in text and "after-poison-2" in text
|
||
print(f" 坏条目后消费不中断: 残留队列={left}, 后续事件全部写入: {drained}")
|
||
return drained
|
||
|
||
|
||
def main():
|
||
if not dlpc_rs485.HAS_SERIAL:
|
||
print("FAILED 缺少 pyserial: 串口卡未构建, txt_dlpc 不存在")
|
||
return 1
|
||
app = QApplication.instance() or QApplication(sys.argv)
|
||
setTheme(Theme.AUTO)
|
||
win = app_fluent.App(0)
|
||
passed = check_cap(win)
|
||
passed &= check_drain_resilience(win)
|
||
win.close()
|
||
print("ALL PASS" if passed else "FAILED")
|
||
return 0 if passed else 1
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|