Files
vrsub/docs/testing.md
cat-shark f656ec98c5 feat: 媒体库字幕重生成统计脚本(数量/时长/旧字幕备份)
模型与管线切换后需要把媒体库里旧管线生成的字幕整批重跑。批量引擎按"视频旁
已有字幕即 SKIPPED"判定,重跑前必须先统计范围、再把旧字幕改名备份,否则建出来
的任务会把所有视频全部跳过。

- `scripts/plan_regenerate_subtitles.py`:扫描媒体库,按 CN 产物的最早 mtime 判定
  生成时间,统计待重生成的数量/时长/体积并输出 JSON 计划;`--backup-old [--apply]
  [--select]` 把旧字幕原地改名为 `<原名>.old-<日期>` 供批量重跑。脚本不依赖仓库,
  可拷到媒体库主机直接跑(CIFS 挂载下逐文件 ffprobe 太慢)。
- 测试覆盖真实 10 秒视频与真实字幕文件,含 ffprobe 时长探测、分类边界、
  备份幂等与选择清单解析。
2026-09-18 22:23:19 +08:00

14 KiB
Raw Permalink Blame History

测试

测试代码位于 tests/,运行方式:

uv run pytest                              # 全部测试
uv run pytest tests/nodes/test_whisper     # 只跑某个模块目录
uv run pytest tests/app/test_routers       # 只跑某组 API 测试
uv run pytest --cov=src --cov=nodes        # 需要覆盖率时手动追加

测试的规则要求写在 ../AGENTS.md:模块边界、 按模块组织、数据/过程/验证三段结构、功能覆盖优先、真实数据与真实生产代码。

目录结构与模块对应

测试按模块组织:被测代码的层级与模块名直接映射到 tests/ 下的目录。 一个模块 = 一个模块目录;单文件过长时在模块目录内部按行为拆文件。

tests/
├── nodes/                       # 对应 nodes/
│   ├── test_srt/                # SRT 解析/序列化(parsing + data/
│   ├── test_whisper/            # 转写:模型解析/分块/时间轴/幻觉清洗
│   ├── test_ass/                # SRT → VR 双目 ASS
│   ├── test_ffmpeg/             # ffmpeg 定位 + 提音(真实视频)
│   ├── test_frame_extract/      # 抽帧/crop/帧号排序(含 frames_boundary 回归)
│   ├── test_vlm/                # 单帧 OCR(真实图片 + Ollama 集成)
│   ├── test_subtitle_ocr/       # 逐帧 OCR 汇总、断点、暂停(14236 帧夹具)
│   ├── test_llm/                # 翻译:ID 对齐/重试/专名注入
│   ├── test_llm_filter/         # 规则层 + LLM 层(真实 OCR 回归数据)
│   ├── test_subtitle_cleanup/   # 幻觉删除 + 短呻吟过滤
│   ├── test_subtitle_correction/# 领域纠错(上下文/误听泛化)
│   ├── test_proper_nouns/       # 专名/隐语规则表
│   ├── test_adaptive_pool/      # 自适应并发额度
│   ├── test_vad_profiler/       # VAD 信号分析与评分
│   └── test_echo/               # 示例节点
├── app/                         # 对应 src/wov_app/
│   ├── test_db/                 # SQLite Repository
│   ├── test_scheduler/          # DAG 调度、断点、暂停
│   ├── test_batch/              # 批量扫描/放置/引擎
│   ├── test_maintenance/        # 孤儿清理
│   ├── test_registry/           # 节点注册表(唯一调用入口)
│   ├── test_seed/               # 内置工作流加载
│   ├── test_storage/            # 原子复制
│   ├── test_config/             # 环境变量与路径(子进程验证)
│   ├── test_logging/            # 日志配置
│   ├── test_main/               # 应用装配与生命周期
│   └── test_routers/            # 三组 APIapps / workflows / batch
├── sdk/test_models/             # 对应 src/wov_sdk/(协议数据模型)
├── web/test_crop/               # 对应 web/assets/(框选几何换算)
├── web/test_batch/              # 对应 web/assets/(批量页渲染)
├── scripts/test_fix_zombie_batch_jobs/  # 对应 scripts/(僵尸批量任务修复)
├── scripts/test_plan_regenerate_subtitles/  # 对应 scripts/(字幕重生成计划统计)
└── shared/                      # 跨模块公共设施
    ├── realdata_contract.py     # 真实数据契约与对齐量化
    ├── srt_entries.py           # 按秒解析 SRT
    ├── env_isolation.py         # 环境/临时目录隔离
    ├── data/alignment/          # 跨模块共享的真实素材 + 参考字幕
    ├── test_srt_entries/        # 公共设施自身的模块测试
    └── test_alignment/          # 真实语音时间对齐集成测试

规则要点(完整表述见 ../AGENTS.md):

  • 每个模块目录含 __init__.py,因此不同模块下的同名测试文件可共存。
  • 每个测试写成数据 → 测试过程 → 验证结果三段,可单独运行。
  • 不保留全局 conftest.py(已删除):隔离由模块自己的 fixture 完成 (tests/shared/env_isolation.py 或模块内 fixture)。
  • 测试过程调用真实生产代码(nodes/wov_appwov_sdk), 只自行构建测试数据并验证输出;mock 仅用于 I/O 边界与模型推理。

测试数据

测试媒体一次性准备后随模块目录入库,测试直接复用,禁止在执行时现场生成; 缺失时跳过而非现场生成。小体量数据(JSON 片段、期望输出)直接写在测试代码内。

素材 内容 归属模块
data/speech_60s.wav 真实语音(合法 16kHz 单声道 WAV) tests/nodes/test_whisper/
data/clip_with_audio.mp4 10 秒真实视频 + 真实语音音轨(提音用例) tests/nodes/test_ffmpeg/
data/subtitle_10s.mp4 烧录 SUB 001/SUB 002 的 10s 视频(无音轨,验证失败路径) tests/nodes/test_ffmpeg/tests/nodes/test_frame_extract/
data/frames_boundary/*.png 跨越 9999 帧边界的真实帧文件名(14236 帧事故回归) tests/nodes/test_frame_extract/
data/ocr_text.png / ocr_notext.png 有文字帧 / 无文字帧 tests/nodes/test_vlm/
data/test_real_hav_sub.png 真实视频字幕截图(期望识别出"还有没有什么困扰 或者奇怪的地方吗") tests/nodes/test_vlm/
data/ocr_srt_run_ac7f480a3ccb.srt 真实任务 1666 条 OCR 输出(规则层回归基线) tests/nodes/test_llm_filter/
data/frames_manifest_full.json + ocr_frames_full.json 真实任务 全部 14236 帧清单与逐帧 OCR 文本 tests/nodes/test_subtitle_ocr/
data/sample.reference.srt 真实参考字幕(5 条,含装饰行) tests/nodes/test_srt/
data/alignment/*.mp4 + *.reference.srt 真实音视频 + 人工校对参考字幕(大体积 mp4 已 gitignored tests/shared/data/alignment/
scripts/data/translate_eval/*.jsonl 翻译模型评测集(评测脚本专用) scripts/

真实模型集成测试

使用真实模型/服务 + 真实素材验证端到端行为;本地缺模型或素材时跳过, 有则必须执行,作为对替身单测的校准。

外部环境状态的两种处理(都不算代码缺陷):

状态 处理
未配置密钥、账户余额/配额、限流(HTTP 401/402/403/429)、服务不可达 tests/shared/llm_service.py 统一识别并跳过
GPU 显存不足 / CUDA OOM tests/shared/gpu_memory.py 运行时探测并跳过

gpu_memory 的两层防护:运行前按权重体量估算(实测 V2 权重 2.87GB → 峰值 增量约 4.1GB,系数 1.45)判断能否跑完;运行中若仍发生 CUDA OOM(临界卡上 的分配碎片化)则转为跳过。fits_with_margin() 进一步区分"显存充裕"与"临界": 充裕时走生产默认的分块路径,临界时退化为整段单次推理,避免整组跳过。

权重版本:测试只使用 V2 权重。V3 已全面停用(均改用 V2),即使留在 盘上也不得被测试使用;判据是 preprocessor_config.jsonfeature_size V2=80V3=128),不依赖目录名。

模块目录 覆盖内容
tests/nodes/test_whisper/ 真实 faster-whisper V2 模型端到端转写
tests/nodes/test_vlm/ 真实 Ollama glm-ocr(有文字帧 + 无文字帧)
tests/nodes/test_subtitle_ocr/ 真实 14236 帧清单重组装一致性
tests/nodes/test_llm/ 真实 LLM 翻译(专名不硬译)
tests/nodes/test_llm_filter/ 真实 OCR 输出规则层回归(保留 863 / 删除 803 基线)
tests/nodes/test_subtitle_correction/ 真实 LLM 误听泛化
tests/shared/test_alignment/ 真实语音转写 vs 人工参考字幕的时间对齐量化

功能模块覆盖清单

规则要求追求功能性代码覆盖率 100%、每个独立功能模块必须被测试覆盖 (不追求行覆盖率 100%,大模块内辅助函数由主功能用例顺带覆盖即可, 完整表述见 ../AGENTS.md)。 本表是新增/修改模块时的核对清单:新增独立功能模块必须同时补测试并更新本表

模块 职责 覆盖测试 状态
nodes/echo.py 示例回显节点 tests/nodes/test_echo/ 已覆盖
nodes/ffmpeg.py 提音与 ffmpeg 定位 tests/nodes/test_ffmpeg/(真实视频) 已覆盖
nodes/whisper.py 转写、分块、模型解析、清洗入口 tests/nodes/test_whisper/(含真实模型)+ tests/shared/test_alignment/ 已覆盖
nodes/llm.py LLM 翻译节点 tests/nodes/test_llm/(含真实 LLM 已覆盖
nodes/ass.py SRT → VR 双目 ASS tests/nodes/test_ass/ 已覆盖
nodes/vlm.py 单帧 OCROllama tests/nodes/test_vlm/(含真实模型) 已覆盖
nodes/frame_extract.py 抽帧与 crop tests/nodes/test_frame_extract/(真实视频 + 帧号边界) 已覆盖
nodes/subtitle_ocr.py 逐帧 OCR → 汇总 SRT、断点/暂停 tests/nodes/test_subtitle_ocr/14236 帧) 已覆盖
nodes/adaptive_pool.py 自适应并发线程池 tests/nodes/test_adaptive_pool/ 已覆盖
nodes/llm_filter.py 字幕规则/LLM 两级过滤 tests/nodes/test_llm_filter/ 已覆盖
nodes/subtitle_cleanup.py 幻觉与短呻吟清洗 tests/nodes/test_subtitle_cleanup/ 已覆盖
nodes/subtitle_correction.py 字幕领域纠错 tests/nodes/test_subtitle_correction/ 已覆盖
nodes/proper_nouns.py 专名/隐语规则表 tests/nodes/test_proper_nouns/ 已覆盖
nodes/srt.py SRT 解析/序列化/时间戳 tests/nodes/test_srt/ 已覆盖
nodes/vad_profiler.py VAD 信号分析与评分 tests/nodes/test_vad_profiler/ 已覆盖
wov_app/registry.py 节点注册表(唯一调用入口) tests/app/test_registry/ 已覆盖
wov_app/scheduler.py DAG 调度与断点续跑 tests/app/test_scheduler/ 已覆盖
wov_app/batch.py 文件夹批量引擎 tests/app/test_batch/ 已覆盖
wov_app/db.py SQLite Repository tests/app/test_db/ 已覆盖
wov_app/main.py 应用装配与生命周期 tests/app/test_main/ 已覆盖
wov_app/routers/apps.py 用户端 API tests/app/test_routers/test_apps_api.py 已覆盖
wov_app/routers/workflows.py 管理端工作流 API tests/app/test_routers/test_workflows_api.py 已覆盖
wov_app/routers/batch.py 批量任务 API tests/app/test_routers/test_batch_api.py 已覆盖
wov_app/seed.py 内置工作流加载 tests/app/test_seed/ 已覆盖
wov_app/maintenance.py 孤儿数据清理 tests/app/test_maintenance/ 已覆盖
wov_app/storage.py 原子复制 tests/app/test_storage/ 已覆盖
wov_app/config.py 路径与环境配置 tests/app/test_config/(子进程验证真实解析) 已覆盖
wov_app/logging.py 日志装配 tests/app/test_logging/ 已覆盖
wov_app/schemas.py 请求模型(Pydantic tests/app/test_main/schema 用例) 已覆盖
wov_sdk/models.py 协议数据模型 tests/sdk/test_models/ 已覆盖
web/assets/crop.js 框选几何换算 tests/web/test_crop/(真实 node 执行) 已覆盖
web/assets/batch.js 批量页明细/进度渲染 tests/web/test_batch/(真实 node 执行) 已覆盖
scripts/fix_zombie_batch_jobs.py 僵尸批量任务诊断与修复 tests/scripts/test_fix_zombie_batch_jobs/ 已覆盖
scripts/plan_regenerate_subtitles.py 媒体库字幕重生成计划统计(数量/时长/分类) tests/scripts/test_plan_regenerate_subtitles/ 已覆盖
tests/shared/srt_entries.py 按秒解析 SRT(测试公共设施) tests/shared/test_srt_entries/ 已覆盖
tests/shared/realdata_contract.py 真实数据契约与对齐量化 tests/shared/test_alignment/ 已覆盖
tests/shared/env_isolation.py 环境/临时目录隔离 tests/app/test_config 等间接覆盖 已覆盖(间接)

核对方式(查看某模块是否真被执行):

uv run pytest tests/ -q --cov=wov_app --cov-report=term-missing

重构历史(已完成)

按新规则对 tests/ 做了一次完整重写:旧平铺测试(40 个文件、约 10900 行) 已删除,旧内容仅作为参考与测试数据来源;现有测试全部按模块目录重写。 重构期间发现并修复的真实缺陷:

缺陷 发现方式 修复
parse_srt 在相邻条目缺少空行时把下一条时间轴吞进正文(静默错位) tests/nodes/test_srt/ 的红灯用例 正文行遇时间戳行即报错(nodes/srt.py
scheduler._file_size 只捕获 OSError,含 \\x00 的产物 URI 抛 ValueError 导致任务误判失败 tests/app/test_batch/ 端到端用例 同时捕获 ValueErrorsrc/wov_app/scheduler.py
生产节点 subtitle_correction 依赖测试包 tests.realdata_contract 解析 SRT 模块化拆分时发现 改用生产模块 nodes/srt.py 的严格解析器

相关文档