| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
修复(测试): 两条只在高负载下现形的偶发红——一条根因在库,一条断言写错 course_recolor_sheet_test.dart 在全量负载下约 1/15 概率红、qr_transfer_codec_test.dart 约 25% 概率红。两者都只在 CI 上才偶发、单独跑怎么都不现形,所以之前一直被当成 「环境问题」重跑掉。逐个挖到根因。 ## 一、二维码传输:随机性藏在库里,测试以为固定了种子就确定了 _simulateTransfer 用了固定 Random(42),看上去是确定性的 —— 但它固定的是 「打乱顺序」和「丢哪几帧」,**编码器吐出来的帧本身每次都不一样**。 fountain_codes-1.0.0 的 LTCodec.encode: final rng = Random(esiOrSeed); // 播了种 final degree = distribution.sample(); // 度数取自 RobustSoliton._random 而 RobustSoliton._random = random ?? Random() —— **默认无种子**,每 sample() 一次 前进一格。于是「同一 seed 的度数」取决于它之前被采样过几次,encode(0, seed) 对 同一个 seed 并不返回同样字节。我们两处 LTCodec(...) 都没往 random: 传东西。 **这不是传输正确性问题**:帧自带度数(Symbol.meta['degree']),接收端 submit 优先 读它,邻居也按 Random(seed) 推导,所以数据不会坏。代价只是同一个 seed 被生成两次 时白费一次扫描。 改法:给编码器与解码器各开一个可选 Random? 透传到 LTCodec,默认 null ⇒ 生产 行为逐字节不变;测试传 Random(42) ⇒ 相同调用顺序下可复现。注意这里给的是 「相同调用顺序可复现」而非「逐 seed 确定」—— 后者要改上游库,而帧自带度数说明 本仓不需要后者。 顺带**纠正一句写错的注释**:frameTextFor 上方原写「LT 编码对同一 seed 是确定性的, 因此可以预先生成未来几帧」,那句话是假的,已改成如实描述(预生成的价值在于把耗时 移出关键路径,不在字节稳定)。 回归钉三条,其中一条专门防「恒真的空断言」:同种子+同调用顺序 → 两个编码器逐字 相同;换种子 → 另一套帧;**不传种子 → 帧会变**(钉住根因本身还在,将来谁把 RobustSoliton 那个 Random() 换成有源的,前两条就都失去意义)。第三条只能断言 「5 组里至少有一组不同」——概率性断言不能反过来把偶发红引回来。 ⚠️ 载荷必须**不可压缩**:度数上限 min(k,100) 而 k 由压缩后长度决定。第一版用中文 短句,gzip 完 k≈1、每个种子都被钳成同一度数,于是第一条恒真、第二条恒假,护栏等于 没写。改用 2000 字节随机数据后两条才都对。**任何断言「帧内容」的测试都要先确认 k 够大。** ## 二、课表重新配色:拿一个可达的颜色当「变化已发生」的哨兵 原来等的是「没有一门课是 #E91E63」—— 而 #E91E63 就在正式色板里 (course_color_palette.dart:44 与 :139),多门课一起换色时换出来的配色完全可能 合法地包含它,于是这个哨兵**本来就可达**,等 60 轮等不到。 改成等「配色与换之前不同」。「换一批」的种子是 DateTime.now().microsecondsSinceEpoch(course_recolor_sheet.dart:112),配色本来 每次真随机,新判据方向正确。 > 两条的共同教训:**测试里拿一个「当前取值空间内可达」的值当不变量的判据,测的就 > 不是被测行为,而是那个值恰好没被取到。** 这类断言平时恒绿,只在概率边界上偶发, > 而且永远表现为「环境问题」。 验证:analyze --fatal-infos 0 问题;全量 flutter test 3051 通过 / 4 跳过 / 0 失败; 两个文件分别连跑 6 次与 10 次全绿(改前 8 次挂 2 次)。 | 8 天前 | |
合并二维码传输与资源功能 | 2 个月前 | |
合并二维码传输与资源功能 | 2 个月前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 8 天前 | ||
| 2 个月前 | ||
| 2 个月前 |