[P1][CR-T3/T4] 测试可移植性与稳定性:硬编码家目录、依赖本地 MPQ、墙钟性能断言偶发失败、全量耗时长 #536
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
问题描述
AGENTS.md§5 要求每次改动都满足"npx vitest run全部通过(0 failed)"。但在基线e475c2e上直接实跑全量测试,暴露出四类工程问题:/usr/local/google/home/taodao/d2-data直接写死在src/、19 个测试文件和 20 个脚本里。换一台没有这个目录的机器(包括 Gitea CI runner),285 个调用getSharedDataRegistry()/getSharedMountedArchives()的测试会直接ENOENT崩溃,另有约 32 个带skipIf的测试文件被静默跳过。Math.random与文件句柄未关闭:5 个压力测试用无种子Math.random;MPQfileSource从不关闭FileHandle,触发 Node 的Closing file descriptor ... on garbage collection警告。证据
T3:硬编码路径与本地 MPQ 依赖
getSharedMountedArchives(baseDir = '/usr/local/google/home/taodao/d2-data')(data-registry.ts:1541)getSharedDataRegistry(baseDir = '/usr/local/google/home/taodao/d2-data')(data-registry.ts:1569)baseDir.startsWith('/usr/local') ? 'samples/d2' : baseDir(data-registry.ts:1548、data-registry.ts:1574)来绕过这个默认值。tests/**/*.test.ts和 20 个scripts/*.ts硬编码了同一个家目录路径;285 个测试文件直接调用getSharedDataRegistry()/getSharedMountedArchives()且不传参,也没有skipIf。skipIf现状:42 个测试文件含有skipIf(共 404 处),其中 9 个机械转换文件写的是死代码const isSkip = false && !fs.existsSync('samples/d2')(如 collision-orientation.test.ts:31),其余约 32 个文件在缺少本地 MPQ 或烘包产物时会静默跳过,不会在总结里醒目提示。has_actions: false,最近 15 个main提交上的 commit status 数量均为 0——也就是说 CI 实际上没有在跑;一旦启用 runner,上面 285 个测试就会因找不到/usr/local/google/home/taodao/d2-data直接失败。T4:不稳定断言、慢测试与资源泄漏
墙钟时间与 GC 堆断言(受并发 worker 抢占和 GC 时机影响):
expect(elapsedMs).toBeLessThan(500)expect(elapsed).toBeLessThan(1000)expect(elapsed).toBeLessThan(2500)(注释里已写明是为 362 文件并发放宽的阈值)expect(duration).toBeLessThan(2000)expect(elapsed).toBeLessThan(100)expect(heapDiffMb).toBeLessThan(15)耗时最长的测试文件(来自基线全量实跑):
tests/skills/nec/necro-assets-adversarial-stress.test.ts:316s(其中单条用例遍历烘焙与校验耗时 296s)tests/challenger-m1-stress.test.ts:181stests/full-spectrum-drop-parity.test.ts:125s测试内使用
Math.random:tests/blizzard-challenger-stress.test.ts、tests/challenger-cold-armors-stress.test.ts、tests/skills/nec/adv-pnb-adversarial-challenger.test.ts、tests/skills/pal/adv-pal-challenger-stress.test.ts、tests/skills/sor/adversarial-challenger-sor.test.ts在生成随机输入时用了Math.random,一旦偶发失败无法复现。文件描述符泄漏:
fileSource(file-source.ts:16-37)open(path, 'r')后返回的MpqSource没有close()方法,句柄一直活到进程结束或被 GC 回收,测试过程中打印Warning: Closing file descriptor ... on garbage collection。根因
/usr/local/google/home/taodao/d2-data,所有后续测试和脚本都顺手复制了这个绝对路径,从未在干净环境或启用了 runner 的 CI 上跑过。tests/**/*.test.ts入口里(vitest.config.ts:4-6),并用墙钟耗时代替复杂度/操作数断言。修复指南
src/config/data-dir.ts(或放在 Node 侧 helper),唯一解析顺序:process.env.D2_DATA_DIR→ 仓库根下的samples/d2(若存在且含完整 MPQ);找不到时抛出带清晰指引的错误(指明需要设置D2_DATA_DIR=/path/to/mpq);/usr/local/google/home/taodao/...字面量,以及data-registry.ts里baseDir.startsWith('/usr/local')的���判;AGENTS.md§5 与docs/LOCAL_DATA.md中写明D2_DATA_DIR约定。test:unitvstest:stress/test:mpq):vitest.config.ts中使用projects(或拆成vitest.unit.config.ts与vitest.stress.config.ts):npm run test:unit:不依赖完整商业 MPQ(只用仓库自带的最小文本表导出或合成 fixture)、不含*-stress.test.ts,目标在 30–60 秒内跑完,任何机器检出即可运行;npm run test:stress:包含*-stress.test.ts、full-spectrum-*、necro-assets-adversarial-stress等重型遍历测试;npm test:在配置了D2_DATA_DIR的开发机或自托管 CI runner 上跑完整套件;const isSkip = false && ...这类假skipIf;对真正依赖外部工具(如ORACLE_BIN)的测试,在 CI 日志末尾显式汇总跳过数量,避免"全跳过也算绿灯"。2500、爆炸数4400、活跃投射物上限<= 25、视线步数上限),把expect(elapsedMs).toBeLessThan(...)和expect(heapDiffMb).toBeLessThan(...)移到单独的npm run bench(vitest bench)脚本中,不在并发 worker 下做硬门禁。Math.random全部替换为固定种子的D2Rng(例如new D2Rng(0x113c_beef));MpqSource/MpqArchive/MountedArchives增加可选的close(): Promise<void>,底层调用handle.close();在afterAll或进程退出钩子中关闭共享归档,消除 fd GC 警告。验收标准
src/、tests/、scripts/)搜索/usr/local/google/home/taodao结果为 0;通过D2_DATA_DIR指定数据目录时全量测试正常通过。toBeLessThan墙钟耗时或heapUsed断言;连续在满载并发下运行npx vitest run3 次,均为 0 failed。npm run test:unit在未设置D2_DATA_DIR的干净环境下可独立运行并在 60 秒内全绿通过;重型压力测试归入npm run test:stress。tests/中Math.random出现次数为 0;运行测试不再出现Closing file descriptor ... on garbage collection警告。npm run typecheck0 error;npx vitest run全部通过。相关
Math.random。