Open
[Feature] 视觉基线机制在每日跑批场景下的持久化与多版本隔离 #5
liu_wentaoAdd on  Oct 9
liu_wentao
liu_wentaoCollaborators
Oct 9 Create

背景

rnoh_test 的每日功能矩阵(launchd 定时任务,rn72/rn77/rn82/rn84/rn86 五版本串行)合入上游用例批次后,引入了 259 个元素级视觉断言(73 个用例文件声明 allowBaselineWrite,分布 ImageBackground / Transform / View 等类别)。

当前基线策略三态(command-base.ts → resolveBaselinePolicy):

  • use(默认):缺基线判红(expect-executor.ts:729-743"元素无基线可比对")
  • fill(--update-baseline):缺基线落盘且断言判绿
  • force(--force-baseline):覆盖并备份到 .history/

每日跑批场景下的四个结构性问题

  1. 基线按日期 ephemeral:TestRunner.resolveRunDirectories(test-runner.ts:447-490)把基线目录解析为 test-reports/<日期>/test-baseline/<平台>/<category>/。每晚新日期目录 → 基线永远"不存在" → 默认 use 策略下每晚确定性地贡献 65 个假红/版本(五版本 × 65 ≈ 325 条噪音),真实失败信号被淹没。

  2. 基线不进版本库:基线落在 test-reports/ 下且被其 gitignore 规则覆盖(visual-comparator.ts 注释已自述此点),无法评审式管理,"--update-baseline 落基线 → 评审 → 后续比对"的闭环走不通。

  3. 同日期多版本共享基线目录:五版本夜跑共用同一个 test-reports/<日期>/test-baseline/。若带 --update-baseline,先跑的 rn72 落基线,rn77~rn86 会拿 rn72 的基线比对 → 跨 RN 版本渲染差异产生新一轮假红;反之不带则每晚全红。

  4. CLI 未暴露基线目录:TestRunner 已支持 options.baselineDir(test-runner.ts:350),但 CLI 只有 --update-baseline / --force-baseline 两个开关,无法指定持久基线仓。

期望改进

  1. CLI 暴露 --baseline-dir <path>(内部机制已就绪,只差透出)
  2. 支持持久基线仓(如仓库内 app/tests/baselines/ 或任意外部路径),脱离 test-reports/<日期> 生命周期
  3. 基线目录支持按 RN 版本 / 设备分子目录的命名空间(多版本矩阵场景隔离,避免跨版本污染)
  4. 可选:配套基线入库的 git 工作流(.gitignore 例外规则 + 评审式更新;--force-baseline 的 .history/ 备份机制可复用为评审留痕)

环境数据

  • rnoh_test 每晚五版本 × 五设备矩阵,元素级视觉断言 259 个(73 文件)
  • 实测(2026-10-09 夜跑):每版本 65 个用例因基线缺失判红(rn77 266 总失败中 65 个、rn82 224/65、rn84 179/65、rn86 184/65)
  • 短期 workaround:跑批 runner 临时带 --update-baseline(视觉断言退化为"落盘即绿"的截图记录模式),等待上述机制完善后恢复真比对
likedislike
liu_wentaoliu_wentaoCollaborators
Oct 9 Added label: featuretriage
liu_wentao
liu_wentaoCollaborators
Oct 9 Comments:

补充验证一:当前不存在 app/tests/baselines/ 持久基线仓

有反馈提到「持久基线仓当前就在 app/tests/baselines/,testreport 会 copy 部分基线内容用于 html」。经代码核实(rnoh_test 子模块 d970d15 与 CPTF origin/main 250175b 双向验证):

  1. app/tests/baselines/ 目录不存在(0 文件),两个代码版本中均无该路径引用(git grep tests/baselines 零命中)
  2. 最接近的 baselines: { screenshotDir: 'docs/baselines/…' } 用例配置是死配置:expect-executor.ts:459 只消费 <name>_visual key 的 baselineData,screenshotDir key 全框架无人读取,docs/baselines/ 目录本身也不存在
  3. 实际存在的 test-baseline/ 目录装的是 UI 结构 dump(ui-<caseId>-*.json),属于 uiComparison(结构对比)通道:stream-comparison-queue.ts:539-575 收集双平台 baseline 文件做结构 diff,对比 HTML 消费——这是独立于元素像素基线的另一条通道
  4. 单平台 HTML 生成器对 baseline 零引用零拷贝;visual-comparator.ts 唯一的 copyFile(:635)是「当前截图→基线路径」的落基线动作,方向相反

结论:元素像素基线(visual 断言 + allowBaselineWrite)目前唯一的存储就是 test-reports/<日期>/test-baseline/<平台>/<category>/*.png,按日期 ephemeral,正文四个问题成立。

补充验证二:像素基线确实设备绑定,但支持而非否定持久仓提案

有反馈提到「基线数据没法跨设备,稍微有一点不一致整个像素比较就会差异比较大」。算法层核实(compareElementToBaseline 三档策略):

条件 算法 跨设备表现
宽高差 ≤1px 真·逐像素 RGB 比对 1px 偏移即内容整体错位,大面积超 tolerance
尺寸不一致 + equalize: true 等画布:左上角对齐平铺到并集画布、白底填充、不缩放,再逐像素 多出区域 = 白底 vs 真实内容,全部计为差异
尺寸不一致 + 无 equalize 3×3 网格平均色统计 尺寸无关但断言极弱

该论点成立——equalize 是「对齐平铺」而非「缩放归一」,通道内无 SSIM/偏移容忍带。且框架自身已内建设备绑定:test-runner.ts 多设备模式下基线目录带 harmony-<serial> 段(「天然按设备分开」)。实测两台夜跑设备(1280×2832 与 1216×2688/BRA-AL00)分辨率即不同,跨设备共享基线必然大面积假红。

但这恰好验证了提案第 3 条(按 RN 版本/设备分子目录)的设计要求——持久仓可行,前提是 per-device 命名空间。由此追加两点增强建议:

  1. 比对算法引入尺寸归一化(按 density 缩放到逻辑像素再比)+ 偏移容忍带/SSIM,降低对「同型号同分辨率」的硬依赖
  2. 正视维护成本与更现实的噪声源:基线矩阵 = N 设备 × M 版本 × 断言数;且 har 每日构建的渲染微差比跨设备更频繁——同设备下 0.98 相似度阈值能否扛住每日 har 变化,决定 nightly 像素回归的实际价值,建议在持久仓设计中一并考虑基线保鲜策略(如按 har buildTime 失效/重落)
likedislike
liu_wentao
liu_wentaoCollaborators
Oct 9 Comments:

修订:撤回「算法放宽」建议,确认双通道设计哲学

收到反馈:「其他的比较方案不行,差异太大了,无法发现问题」「test-baseline/ 的 ui-*.json 结构 dump 就是基线比对用的」。经重新评估,撤回上一条评论中「尺寸归一化 + 偏移容忍带/SSIM」的增强建议——该建议用检出能力换跨设备兼容性,方向错了:

  • 偏移容忍带会漏掉布局位移类真回归(位移本身就是要抓的 bug)
  • SSIM 对小面积色彩/抗锯齿变化不敏感(渲染回归的典型形态)
  • 密度归一化缩放存在重采样伪影,且不同密度下字体光栅化根本不同,归一不掉

结论与反馈一致:像素回归要保住信噪比,唯一可行配置是严格逐像素 + 同设备同版本;跨设备/跨端像素比对是死路。这也与「基线必须设备绑定」的既有设计(基线目录带 harmony-<serial> 段)自洽。

修正后的通道分工理解

通道 职责 生效前提
uiComparison(结构 dump,ui-<caseId>-*.json) 跨平台基线比对 compare 会话、双端在场(stream-comparison-queue 收集双端 dump 做结构 diff)
visual(元素像素断言 + allowBaselineWrite) 同端回归检测 持久基线 + 同设备同版本

两者互补不互替,本 issue 针对的是后者。

需要保留的边界说明

结构通道无法覆盖 harmony-only 夜跑场景:单平台运行时 uiComparison 步骤处于「单平台执行,等待跨平台对比数据」状态(仅捕获 dump,不发生比对),双端比对只在 compare 会话内发生。因此 nightly 中 65 个/版本的像素断言判红仍源于像素通道的 per-date 基线问题,本 issue 的核心诉求不变:

  1. CLI 透出 --baseline-dir(TestRunner.options.baselineDir 已就绪)
  2. 持久基线仓支持
  3. 基线按设备 × RN 版本分目录(严格逐像素 + 设备绑定的必然要求)

另补充一点:同设备同版本下还有每日 har 构建的渲染微差这一现实噪声源(我们的 har 天天更新),持久仓设计若落地,建议配套基线保鲜策略(如按 har buildTime 失效/重落),否则持久基线会被日常渲染漂移冲刷成噪音。

likedislike
liu_wentao
liu_wentaoCollaborators
Oct 9 Comments:

跨版本基线共享实证:命名空间可从「设备 × 版本」简化为「设备」单维度

针对「基线按设备 × 版本分目录」的提案第 3 条,做了同设备跨版本实测(2026-10-09,HUAWEI Mate 60 / 1280×2832,rn86 播种基线后其余版本 use 策略比对):

版本 blur-radius(6断言) border-width(8) transform-origin(2) view(2) 最低相似度 结果
rn84 6/6 8/8 2/2 2/2 99.98% 18/18 全过
rn82 6/6 8/8 2/2 2/2 99.96% 18/18 全过
rn77 6/6 8/8 2/2 2/2 100.00% 18/18 全过
rn72 5/6 8/8 0/2 2/2 94.00% 15/18,3 个已知分歧

结论:同设备下相邻版本渲染几乎逐像素一致(rn77~84 vs rn86 基线 ≥99.96%),仅 0.72 与 0.86 之间存在 transform/blur 的真实实现分歧(97.5%/94.0%/97.6%,已用断言级 knownIssue 标注)。因此持久基线仓的命名空间按「设备」单维度即可,无需版本维度——比原提案的矩阵小一个维度,维护成本显著降低。rn72 的例外属于断言级已知分歧,用 knownIssue 覆盖即可。

已在 rnoh_test 落地(作为该方案的消费侧验证)

nightly 版本顺序调整为 rn86 先行(带 --update-baseline 播种当日基线目录),rn84→82→77→72 后行(use 策略比对)。链路 matrix→run→cptf 已端到端验证。语义边界:这是当夜跨版本一致性检查(每晚基线现拍),rn86 自身的时序回归检测仍需本 issue 的持久仓 + --baseline-dir——两者互补,不冲突。

likedislike
cpf-thirdparty-manager
Oct 10 Comments:

本Issue主要用于开发协作,欢迎在此反馈问题或提出建议

likedislike