Files
ft/CodexOutput/upserver/ReviewLifecycleTests

独立复审生命周期确定性复现

本目录只运行本地 .NET 宿主,不连接网络、不运行 Unity、不读写游戏存档。

修复后最新结果

2026-09-22:正常 4 项、原缺陷回归 4 项、扩展 41 项、Stage 停止边界 9 项,共 58 项通过,各命令退出 0。见 fixed-*-result.json 与 fixed-*.log。fixed-source-test-hashes.json 记录当前四生产文件及提取体、替身、断言 hash;下面修复前证据保持原样。

扩展测试直接使用 GetUI/NewEnterMap/OnCameraSpeedEvent 生产方法(async void 仅转换为可等待 Task),声音两级实际初始化/释放方法。Stage 使用实际 Start 前缀、完整 Stop/OnDestroy 和 base.StartAsync 的原异常过滤片段;未用复制的 guard 镜像替代真实方法。Stage 前缀在声音初始化后截断,末尾 Applied 仅作为「旧流程是否能继续」观察点。

Unity 对象等号、可控等待、逐资源引用和事件订阅是替身;真实引擎效果以主会话 Unity 结果为准。可用 --extended、--stage 运行扩展矩阵。修复前 regression-failing.log/baseline 未覆盖;修复后命令写 fixed-*。

generate.py 从实际生产文件提取方法。GetUI 唯一转换是 async void → public async Task,便于测试等待;SoundMgr.Init/Release、SoundService.InitAsync/Release 方法体不改。extraction.json 记录文件与方法 hash。替身依赖在 Stubs.cs,测试编排在 Program.cs;没有复制一套被测业务逻辑。

已保存结果(修复前)

  • normal.log:四项正常流程通过,退出码 0。
  • regression-failing.log:四项退出安全断言失败,退出码 1。
  • normal-result.json / regression-result.json:结构化副作用计数。
  • baseline/:修复前提取方法与结果快照,明确排除参与构建。
  • test-hashes.json:本次被测提取体、替身、断言的 hash。

重跑

从 C:/projects/ft 运行:

python CodexOutput/upserver/ReviewLifecycleTests/generate.py
$env:DOTNET_CLI_HOME='C:/projects/ft/CodexOutput/dotnet-home'
$env:APPDATA='C:/projects/ft/CodexOutput/dotnet-appdata'
dotnet run --project CodexOutput/upserver/ReviewLifecycleTests/ReviewLifecycleTests.csproj -- --normal
dotnet run --no-build --project CodexOutput/upserver/ReviewLifecycleTests/ReviewLifecycleTests.csproj -- --regression

修复前第二个命令预期退出码 1,是保存的缺陷证据,不能改成「预期失败等于验收通过」。修复后需重新提取源码、适配新增依赖,并使同样安全断言通过;不得覆写 baseline 证据。

限制

资源替身把操作完成与 Release 分开,允许已发出的 Task 完成,专门暴露生产代码缺失的生命周期检查。真实 Addressables 可能在释放后先抛出无效句柄/已销毁对象异常,因此这里证明的是退出后的控制流和所有权缺口,不声称真实引擎必然产生完全相同计数或异常。

修复前 GetUI 的 Destroyed 标识仅代表测试时序;旧生产方法从不读取它,测试同时实际替换容器,并观察随后对新解析服务的调用。修复后已补 Unity Object 等号替身,分别验证销毁、取消及容器替换,仍以 Unity 验证实际销毁语义。

SoundMgr 的测试先使底层声音服务初始化成功,暂停在其自身音频加载循环,以隔离该层缺口。SoundService 的两个内部等待窗口另行使用实际方法测试。正常对照排除「替身无条件报错」;不存在真实服务器或账号数据修改。