mock 框架架构
第0条(基线)本文件覆盖
core.lua、framework.lua、framework_engine.lua、framework_ui.lua、loader.lua、node_ps.lua、node_client.lua、tab_loader.lua、xml_ui_loader.lua、frame_driver.lua;一切数字以 20260918 磁盘实测为准,旧数引用禁止。
第一章 总则
第1条(定位)mock 为 AI 自动化代理设计的测试基础设施,禁止以人工点按调试为设计前提。人类只提供自然语言测试目标,AI 必须把目标翻译为精确、可断言、可运行的 Lua 测试用例。三项硬要求:
(一)结构化:tests/<system>/<aspect>/ 分类、run_<scene>_<aspect>_test.lua 命名、*_helpers.lua 共享基建。
(二)可追溯:断言回溯到文件符号、结构化 trace、固定种子可复现。
(三)断言明确:只断言可观察行为,失败必须打印实际值。
第2条(虚拟化策略)mock 只仿真边界,不模拟业务:framework.lua 仿真引擎对外接口,framework_ui.lua 仿真控件系统,loader.lua 加载真实业务代码。判定依据见 WHY_AND_HOW.md 第3条。
第3条(系统接入)每个接入系统必须定义节点集合与角色、真实代码、协议与装载方式;节点边界必须保留,禁止拍平------拍平将消灭到达顺序与竞态。麻将系统拓扑见 mahjong/ROUTE_SEMANTICS.md 第1条;其他系统按其接入定义执行。
第二章 核心机制
第4条(消息总线与虚拟时钟)Mock.Deliver(dst, proto, data, src, delay) 必须深拷贝 tData 并按到达时刻排序插入待投队列;Mock.Tick(nTargetMs) 处理全部到期消息(注入检查、handler pcall、状态追踪、trace)后推进时钟并触发到期定时器。
(一)时钟只允许单调推进:投递后必须 Mock.Tick(Mock.nClock + Δ)(Δ>0),禁止回退时钟。
(二)消息先于定时器:Tick 先处理到期消息再触发定时器;定时器回调内发出的 C2S 必须经下一次 Tick 处理。
(三)Mock.Flush(nMaxMs) 无界跳时:直接跳到队列首条消息到达时间,不受 nMaxMs 约束;"等待期"断言必须用 Mock.Tick 精确推进。
(四)深拷贝:测试侧构造数据不得被 handler 污染,handler 修改不得回灌测试。
(五)默认确定性:默认延迟 0、顺序投递;乱序与攻击由测试显式注入。
第5条(虚拟定时器)
(一)0 与负延迟必须强制为下一毫秒:防同步递归风暴;断言必须期待 nClock+1,不得期待 nClock+0。
(二)定时器必须携带 dwRoomID 并随 trace 输出。
(三)TimerRemoveByRoom(dwRoomID) 必须置死目标并输出 remove_by_room 事件、返回清除数;断言必须覆盖目标房全灭且他房存活。
第6条(多物理时钟折叠)真实系统每节点独立物理时钟;mock 必须折叠为单一虚拟时钟(Mock.nClock),由 GetCurrentTime()/GetMilliSecond() 返回。需要模拟因果偏差时必须用 Mock.Inject.Lag 注入到达延迟。
第7条(多客户端隔离)
(一)每个客户端必须独立 Lua env:env 的 __index 对 g_ 前缀返回 nil,使 g_tData = g_tData or {} 在 env 内新建,禁止多客户端共享 _G 状态。
(二)env 必须预置本客户端模块表空表,禁止 xxx = xxx or {} 解析到 _G 共享表。
(三)每个客户端必须独立控件树、独立 module(通信带客户端身份)、独立语音模块实例。
(四)节点命名 CLIENT_{dwID};module:CallClient(dwID, ...) 必须按 dwID 路由到对应节点。
第8条(控件树虚拟化)
(一)VirtualWindow(247 方法,20260918 实测)必须以纯 Lua 运行真实 UI 脚本:树操作、XML 构建(xml_ui_loader.lua 解析真实 UI XML 并按清单构建初始控件树,几何与可见性与声明同源)、文字、纹理、动画、位置、事件、列表全套方法。
(二)每个控件方法调用必须记录 control_log 事件(完整路径、方法、参数、几何、调用者)。
(三)SetWindowEvent 注册与 FireEvent 注入必须成对;ON_EVENT 期间必须注入全局 param(param.event=事件名)并在回调后还原 nil。
(四)音频必须仿真 Play2DSound、PlayBGM、音量与开关;Play2DSound 必须返回带 IsPlaying、Stop、Release 的对象,行为输出 audio 事件。
(五)动画 PlayPositionAnimate 必须以定时器驱动完成回调。
(六)断言快捷方式:Mock_AssertVisible、Mock_AssertText、Mock_AssertAlpha、Mock_Tap、Mock_ExportControlLog。
第9条(引擎语义精确项)
| 语义 | 要求 | 使用约束 |
|---|---|---|
| SetAlpha 单位 | 0.0-1.0 浮点,默认 1.0 | 断言必须容忍 0.001 误差 |
| ON_ACTUAL_SHOW/HIDE 级联 | 父隐藏级联整子树;父显示级联全部显示 | 默认关闭,Mock.cfg.bActualEvents=true 启用 |
| BringToTop | Z 序提升并触发 ON_BRING_TO_TOP | bActualEvents=true 时才触发事件 |
| ON_EVENT param | 事件期间注入 param,回调后还原 nil | 必须断言不泄漏 |
| 严格查找 | Mock.cfg.bStrictFind=true 时查找失败必须记录 FAIL |
全流程扫描测试必须开启 |
| 壳 root | _bShellRoot=true 的根窗口不参与可见性统计 |
统计可见控件数时必须排除 |
第10条(配置表加载)
(一)tab_loader.lua 必须真实读取配置表:第1行列名、第2行类型、第3行具体类型、第4行中文名、第5行起数据;Int32/Float32→number,Bool32→0/1,String→string。
(二)单元格转义必须递归处理且分割先于转义;Map、Array、Set 类型必须解析为 Lua 表;外层引号必须剥离。
(三).desc 必须按 LineSubValue 树解析(Name/Type/Value/Condition)并展开挂到行表。
(四)索引必须按 IndexTable 列逐级嵌套建立,按需缓存。
(五)规则注入优先级:测试注入的 g_tTestRules[szRuleName] 高于真实配置表。
(六)Enum.tab 与 t_ErrorCode.tab 默认不注入;显式开启时必须幂等补全,已有常量不覆盖。
第11条(故障注入)Mock.Inject 必须默认零注入(行为零变化);注入项必须穷举到达顺序并断言收敛:
| 注入 | API | 语义 |
|---|---|---|
| 丢包 | Inject.Drop |
后续 n 条消息丢弃;记 drop_injected |
| 延迟 | Inject.Lag |
强制投递延迟 |
| 节点崩溃 | Inject.NodeCrash/NodeRestore |
节点全丢并记录节点事件 |
| 快照 | Inject.Snapshot |
深拷贝,供一致性对比 |
| 对比 | Inject.Compare |
递归深比较,返回差异数组 |
| 清空 | Inject.Reset |
清空全部注入;Mock.Reset 不触碰注入 |
两种到达顺序产生两种终态即 BUG;扰动注入与收敛断言必须成对,禁止只注入不收敛。
第12条(行为跟踪)
(一)每个分布式交互必须生成结构化事件:投递、处理、定时器、状态变更、随机、配置读取、控件操作、音频。
(二)"谁在何时做了什么"必须由三字段保证:虚拟时间、by=文件:行号 函数(真实调用者)、参数与几何。
(三)观测四铁律:观测必须在投递与处理完成之后记录,禁止进入转发路径;观测不得修改数据包;观测必须可关闭且关闭时零行为变化;trace 数据不得参与路由判断。
(四)导出:Trace.Export()、Trace.ExportJSON(path)、Mock.cfg.bTrace。
第三章 测试分层
第13条(分层定义)测试必须按验证深度分四层,目录与分层一一对应:
| 层 | 测什么 | 断言风格 | 目录示例 |
|---|---|---|---|
| 单元 | 单个算法或函数 | 直调纯函数,输入到输出 | algorithm/、protocol/ |
| 开盒 | 真实函数的内部状态机 | 逐步调用,断言中间态与字段 | ps_openbox/、settle_amount/ |
| 集成 | 跨节点或跨模块全链路 | 驱动真实对局,断言终态与关键字段 | gameplay/、settle/、integration/ |
| 攻击 | 乱序、丢包、延迟、崩溃、重复 | 注入故障,断言不变量收敛与次数恰好 | sync_attack/、route/、client_order/ |
各层不可互相替代。规模(20260918 实测):tests/ 下 595 个 Lua 文件、70 个目录。
第14条(PIN 方法论)已确认产品 BUG 必须按"先钉后修"处理:按 PRD 正确行为写断言、跑出失败作为证据、修复产品代码、翻转断言至全绿。PIN 断言禁止为绿灯删除或放宽。
第四章 断言与报告
第15条(断言 API 与约定)
(一)API:Mock.Assert(失败收集)、Mock.Equals(失败信息必须含 expected 与 got)、Mock.True、Mock.False、Mock.Fail。
(二)失败信息必须打印实际值。
(三)每个测试必须以 Mock.Report("标题"); os.exit(Mock.nFail > 0 and 1 or 0) 结尾;Mock.Report 必须输出 ASSERT=n PASS=n FAIL=n 并打印全部历史失败。
(四)场景隔离:每场景必须 Mock.Reset()(清节点、消息、定时器、时钟并归档失败);测试之间必须独立进程。
(五)确定性:必须 Mock.Seed(n) 固定种子;随机与攻击测试必须可复现。
(六)trace 可用于断言:事件次数统计与状态迁移记录。
第16条(运行方式)运行方式只允许引用 mock/LINUX.md(Linux 唯一权威)与 mock/README.md 第10节(Windows 唯一权威);本文件禁止复制命令。退出码与工作目录纪律以 LINUX.md 为准。
第17条(语义文档索引)语义文档注册表以 README.md 第4条为准;本文件禁止重复维护索引。
第五章 新增测试程序
第18条(四步程序)
(一)读语义文档,确定可断言行为:每个行为必须对应相对时间因果不变量与文件符号证据;跨系统目标分别引用各系统文档;框架能力查 INTERFACE_MAP.md。
(二)选模板:在 tests/<system>/ 建目录,复制同类现有测试为模板,保持命名约定;骨架依次完成加载、节点初始化、输入构造、驱动、断言、报告。
(三)断言规则:只断言可观察行为且失败含实际值;必须覆盖正常、边界、错误路径;攻击类必须覆盖多种到达顺序;输入数据必须符合产品契约;固定种子;UI 去重语义测试必须先摘除 UI 事件桥接。
(四)验证与记录:单测失败数为零后必须跑同类回归;必须更新 .opencode/ 四份记忆文件;新测试必须登记到跑批脚本或目录 INDEX。
第六章 已知边界
第19条(已知限制)
| 限制 | 影响 |
|---|---|
| 单进程单线程同步分派 | 去重窗口折叠(测试侧摘除 UI 桥接规避);UI 自续定时器干扰(用类型注册表断言) |
| UI 全虚拟化 | 只断言控件显隐、几何、方法调用,禁止断言像素 |
| 网络物理层缺失 | 延迟、乱序、丢包为注入抽象,禁止断言协议字节 |
| 时钟单一化 | 只可测相对时间因果 |
| 配置表覆盖 | 缺失配置可能走兜底默认值 |
| 小游戏与外部系统 | 以 Include stub 加测试侧复刻;真实外部服务端逻辑不可测 |
| 性能与规模 | 不做真实并发与规模测试 |
| DB 与持久化 | 只验证内存态 WAL、token、落账语义,不测磁盘崩溃恢复 |
| node env 房间表 | 由测试侧管理(node env 跨场景持久为设计语义) |
| node_ps 路由缺口 | 缺 PlayServer2MatchCenter_PullResult 路由;Pull 测试必须自包装转发或补路由 |
第20条(边界红线)mock 测的是控制逻辑,禁止测引擎、网络、渲染、DB 底层实现;产品代码行为对错由断言裁决,mock 只保证能加载、能执行、可断言;测试必须按系统分类,共享基建必须放 *_helpers.lua,禁止复制粘贴;框架缺口记 [MOCK-GAP],产品 BUG 必须附文件符号证据,两者必须分开报告。