# 测试 ## 分层 | 目标 | 依赖 | 说明 | |---|---|---| | `livecaption_core` | 无 Qt | 全部业务逻辑;单元测试只依赖它 | | `livecaption_models` | Qt6::Core / Network | 注册表、安装检查、sha256 校验、应用内下载 | | `livecaption_ui` | Qt6::Widgets | 悬浮窗、设置对话框、Qt 适配层 | | `live-caption` | ui + models | 悬浮窗程序 | | `live-caption-cli` | core + models + Qt6::Core | 命令行程序(**不链接 Qt Widgets**) | | `tst_*` | core + Qt6::Test | 单元测试 | ## 运行 ```bash cmake -S . -B build && cmake --build build -j ctest --test-dir build --output-on-failure # 也可以单独跑 build/tests/tst_xxx ``` 单元测试**不需要模型文件,也不需要显示环境**,秒级完成。目前 4 套: | 测试 | 覆盖 | |---|---| | `tst_streamingasr` | 流式策略:拼接、分段、已稳定/不稳定尾部、冷启动前缀 | | `tst_livepipeline` | 实时管线:静音过滤、不规则 chunk 不丢样本、静音分段 | | `tst_vad` | 能量 VAD(轻话音的检测) | | `tst_transcript` | 输出解析与语言名映射 | ## 为什么能这样测 关键是把推理后端抽象成 `IAsr`(`src/core/iasr.h`),测试里换成 `tests/fakeasr.h` 的 `FakeAsr`: - 可以**脚本化**每一次 `streamAppend` 返回什么文本; - 记录每次拿到的前缀、音频样本数、以及 `streamReset` 的调用次数; - `dropLastTokens` 用"空格分词"模拟词表,并保留原有格式。 另外 `LivePipeline` 刻意不带 Qt 依赖、并且把「送音频」拆成同步的 `process()`,所以测试可以直接喂音频,不必碰 PulseAudio。 ## 覆盖的回归用例 这些用例对应的都是开发过程中**实际踩到**的坑: | 测试 | 对应的坑 | |---|---| | `tst_streamingasr.keepsJoiningSpace` | 用 `trim()` 过的文本拼接,导致续写缺空格(`potatoesand`) | | `tst_streamingasr.segmentsAccumulateWithoutLosingEarlierText` | 多段拼接时只保留最后一个语言头之后的内容,前面正文丢失 | | `tst_streamingasr.committedDropsUnstableTail` | 已提交 / 不稳定尾部的划分 | | `tst_streamingasr.flushCommitsEverything` | 流结束时尾部被丢掉 | | `tst_streamingasr.coldStartHasNoPrefix` | 冷启动阶段不应回喂前缀 | | `tst_livepipeline.silenceIsNotForwarded` | 静音被送进模型导致幻觉(英文语音前冒出 `嗯。`) | | `tst_livepipeline.noSamplesDroppedAcrossIrregularChunks` | 工作线程丢样本(余数没接上),造成假静音与截断 | | `tst_livepipeline.silenceEndsSegment` | 静音收束分段 | | `tst_vad.quietSpeechIsDetected` | 噪声底用均值跟踪,把很轻的话音全判成静音 | | `tst_transcript.*` | 输出解析与语言名映射 | | `tst_transcript.isoCodeDisplayName` / `registryLanguagesAllHaveDisplayNames` | 设置里的语言提示用的是 ISO 码(`zh`/`uk`…),漏了显示名的话「识别语言」下拉会直接露出 `uk` 这种码 | 写测试的过程中还**抓到一个真 bug**:段恰好结束在 chunk 边界(没有待处理音频)时,`flush()` 只提交"已稳定"部分,最后几个词会被丢掉。已修(用完整文本 `lastRawFull_` 提交)。 ## 界面 / 集成测试钩子 界面与真实音频链路不做单元测试,但留有可脚本化的入口(全部选项见 [cli.md](cli.md)): ```bash # 设置对话框各条退出路径的冒烟测试(当初定位「点确定就退出」的崩溃就是靠它) for how in reject close accept ok apply; do ./build/live-caption --asr-model zipformer-zh-2025 --no-tray \ --settings-autoclose $how --quit-after 8 echo "$how → 退出码 $?" done # 界面渲染自截图(折叠态 + 展开态),不依赖外部截图工具 ./build/live-caption --asr-model zipformer-zh-2025 --no-tray \ --screenshot /tmp/win.png --screenshot-delay 6 ./build/live-caption --screenshot-settings /tmp/settings.png # 模型完整性与采集设备(不需要显示环境) ./build/live-caption-cli --verify-model zipformer-zh-2025 ./build/live-caption-cli --list-devices ``` 崩溃时用 `coredumpctl list` / `coredumpctl info ` 看调用栈。 ## 尚未覆盖 以下几类需要真模型或真实音频设备,属于集成测试,没放进单元测试: - **8s 编码切片对齐**:这是准确率的决定性因素(切 2s 会明显掉准确率),但必须用真模型跑才能验证。 - **增量 KV 复用的单步延迟**:需要真模型才能测。 - 采集链路(PulseAudio/PipeWire 的 monitor 源、设备枚举)。 - GUI 渲染的**像素级断言**(目前只有人工看截图)。 这些目前靠 `experiments/` 下的脚本与实机验证(见 `docs/phase0..3`)。 ## 后续可加 - 给需要模型的集成测试打 CTest label(如 `model`),默认跳过,用环境变量指模型路径时才跑。 - GUI 的渲染回归:已有的 `--screenshot` 自截图,可以断言面板像素 alpha(`(0,0,0,214)`),避免再次出现"样式表背景对自定义 QWidget 不生效 → 明明想半透明结果全透明"这类问题。 - 给 `ModelStore::verify` 加一个用临时文件构造损坏模型的单元测试(现在只在集成层面手工验证过)。