From 320323972675fbeecdc23c776a3ff826206ceec6 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:01:06 +0800 Subject: [PATCH] =?UTF-8?q?fix(source-generators-tests):=20=E6=94=B6?= =?UTF-8?q?=E6=95=9B=20schema=20=E5=BF=AB=E7=85=A7=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E8=B6=85=E9=95=BF=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构 SchemaConfigGeneratorSnapshotTests 的 monster 场景输入与快照辅助逻辑,消除单个超长测试方法 - 更新 analyzer warning reduction 的 tracking 与 trace,记录 RP-033 基线和验证结果 --- .../SchemaConfigGeneratorSnapshotTests.cs | 324 +++++++++--------- .../analyzer-warning-reduction-tracking.md | 24 +- .../analyzer-warning-reduction-trace.md | 29 ++ 3 files changed, 218 insertions(+), 159 deletions(-) diff --git a/GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorSnapshotTests.cs b/GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorSnapshotTests.cs index 2d5cce4a..0d9414f6 100644 --- a/GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorSnapshotTests.cs +++ b/GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorSnapshotTests.cs @@ -8,171 +8,189 @@ namespace GFramework.SourceGenerators.Tests.Config; [TestFixture] public class SchemaConfigGeneratorSnapshotTests { + private const string RuntimeContractsSource = """ + using System; + using System.Collections.Generic; + + namespace GFramework.Game.Abstractions.Config + { + public interface IConfigTable + { + Type KeyType { get; } + Type ValueType { get; } + int Count { get; } + } + + public interface IConfigTable : IConfigTable + where TKey : notnull + { + TValue Get(TKey key); + bool TryGet(TKey key, out TValue? value); + bool ContainsKey(TKey key); + IReadOnlyCollection All(); + } + + public interface IConfigRegistry + { + IConfigTable GetTable(string name) + where TKey : notnull; + + bool TryGetTable(string name, out IConfigTable? table) + where TKey : notnull; + } + } + + namespace GFramework.Game.Config + { + public sealed class YamlConfigLoader + { + public YamlConfigLoader RegisterTable( + string tableName, + string relativePath, + string schemaRelativePath, + Func keySelector, + IEqualityComparer? comparer = null) + where TKey : notnull + { + return this; + } + } + } + """; + + private const string MonsterSchema = """ + { + "title": "Monster Config", + "description": "Represents one monster entry generated from schema metadata.", + "type": "object", + "minProperties": 4, + "maxProperties": 8, + "required": ["id", "name", "reward", "phases"], + "properties": { + "id": { + "type": "integer", + "description": "Unique monster identifier." + }, + "name": { + "type": "string", + "title": "Monster Name", + "description": "Localized monster display name.", + "x-gframework-index": true, + "minLength": 3, + "maxLength": 16, + "pattern": "^[A-Z][a-z]+$", + "default": "Slime", + "enum": ["Slime", "Goblin"] + }, + "hp": { + "type": "integer", + "const": 10, + "minimum": 1, + "maximum": 999, + "exclusiveMinimum": 0, + "exclusiveMaximum": 1000, + "multipleOf": 5, + "default": 10 + }, + "dropItems": { + "description": "Referenced drop ids.", + "type": "array", + "minItems": 1, + "maxItems": 3, + "minContains": 1, + "maxContains": 2, + "uniqueItems": true, + "contains": { + "type": "string", + "const": "potion" + }, + "items": { + "type": "string", + "minLength": 3, + "maxLength": 12, + "enum": ["potion", "slime_gel"] + }, + "default": ["potion"], + "x-gframework-ref-table": "item" + }, + "reward": { + "type": "object", + "description": "Reward payload.", + "minProperties": 2, + "maxProperties": 2, + "required": ["gold", "currency"], + "properties": { + "gold": { + "type": "integer", + "minimum": 0, + "default": 10 + }, + "currency": { + "type": "string", + "enum": ["coin", "gem"] + } + } + }, + "phases": { + "type": "array", + "description": "Encounter phases.", + "items": { + "type": "object", + "required": ["wave", "monsterId"], + "properties": { + "wave": { + "type": "integer" + }, + "monsterId": { + "type": "string", + "description": "Monster reference id.", + "minLength": 2, + "maxLength": 32, + "x-gframework-ref-table": "monster" + } + } + } + } + } + } + """; + /// - /// 验证一个最小 monster schema 能生成配置类型、表包装和注册辅助。 - /// + /// 验证一个最小 monster schema 能生成配置类型、表包装和注册辅助。 + /// [Test] public Task Snapshot_SchemaConfigGenerator() { - const string source = """ - using System; - using System.Collections.Generic; - - namespace GFramework.Game.Abstractions.Config - { - public interface IConfigTable - { - Type KeyType { get; } - Type ValueType { get; } - int Count { get; } - } - - public interface IConfigTable : IConfigTable - where TKey : notnull - { - TValue Get(TKey key); - bool TryGet(TKey key, out TValue? value); - bool ContainsKey(TKey key); - IReadOnlyCollection All(); - } - - public interface IConfigRegistry - { - IConfigTable GetTable(string name) - where TKey : notnull; - - bool TryGetTable(string name, out IConfigTable? table) - where TKey : notnull; - } - } - - namespace GFramework.Game.Config - { - public sealed class YamlConfigLoader - { - public YamlConfigLoader RegisterTable( - string tableName, - string relativePath, - string schemaRelativePath, - Func keySelector, - IEqualityComparer? comparer = null) - where TKey : notnull - { - return this; - } - } - } - """; - - const string schema = """ - { - "title": "Monster Config", - "description": "Represents one monster entry generated from schema metadata.", - "type": "object", - "minProperties": 4, - "maxProperties": 8, - "required": ["id", "name", "reward", "phases"], - "properties": { - "id": { - "type": "integer", - "description": "Unique monster identifier." - }, - "name": { - "type": "string", - "title": "Monster Name", - "description": "Localized monster display name.", - "x-gframework-index": true, - "minLength": 3, - "maxLength": 16, - "pattern": "^[A-Z][a-z]+$", - "default": "Slime", - "enum": ["Slime", "Goblin"] - }, - "hp": { - "type": "integer", - "const": 10, - "minimum": 1, - "maximum": 999, - "exclusiveMinimum": 0, - "exclusiveMaximum": 1000, - "multipleOf": 5, - "default": 10 - }, - "dropItems": { - "description": "Referenced drop ids.", - "type": "array", - "minItems": 1, - "maxItems": 3, - "minContains": 1, - "maxContains": 2, - "uniqueItems": true, - "contains": { - "type": "string", - "const": "potion" - }, - "items": { - "type": "string", - "minLength": 3, - "maxLength": 12, - "enum": ["potion", "slime_gel"] - }, - "default": ["potion"], - "x-gframework-ref-table": "item" - }, - "reward": { - "type": "object", - "description": "Reward payload.", - "minProperties": 2, - "maxProperties": 2, - "required": ["gold", "currency"], - "properties": { - "gold": { - "type": "integer", - "minimum": 0, - "default": 10 - }, - "currency": { - "type": "string", - "enum": ["coin", "gem"] - } - } - }, - "phases": { - "type": "array", - "description": "Encounter phases.", - "items": { - "type": "object", - "required": ["wave", "monsterId"], - "properties": { - "wave": { - "type": "integer" - }, - "monsterId": { - "type": "string", - "description": "Monster reference id.", - "minLength": 2, - "maxLength": 32, - "x-gframework-ref-table": "monster" - } - } - } - } - } - } - """; + var generatedSources = GenerateSourcesForMonsterSchema(); + var snapshotFolder = GetSchemaSnapshotFolder(); + return AssertAllSnapshotsAsync(generatedSources, snapshotFolder); + } + /// + /// 运行 monster schema 场景,并把生成结果转换为按 hint name 索引的字典。 + /// + /// 当前快照场景的全部生成文件内容。 + private static IReadOnlyDictionary GenerateSourcesForMonsterSchema() + { var result = SchemaGeneratorTestDriver.Run( - source, - ("monster.schema.json", schema)); + RuntimeContractsSource, + ("monster.schema.json", MonsterSchema)); - var generatedSources = result.Results + return result.Results .Single() .GeneratedSources .ToDictionary( static sourceResult => sourceResult.HintName, static sourceResult => sourceResult.SourceText.ToString(), StringComparer.Ordinal); + } + /// + /// 解析 schema 生成器快照目录,确保断言始终落在仓库内已提交的 snapshot 资产上。 + /// + /// schema 生成器快照目录的绝对路径。 + private static string GetSchemaSnapshotFolder() + { var snapshotFolder = Path.Combine( TestContext.CurrentContext.TestDirectory, "..", @@ -181,9 +199,7 @@ public class SchemaConfigGeneratorSnapshotTests "Config", "snapshots", "SchemaConfigGenerator"); - snapshotFolder = Path.GetFullPath(snapshotFolder); - - return AssertAllSnapshotsAsync(generatedSources, snapshotFolder); + return Path.GetFullPath(snapshotFolder); } /// diff --git a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md index 0f4721ad..e4e37da8 100644 --- a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md +++ b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md @@ -7,8 +7,8 @@ ## 当前恢复点 -- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-032` -- 当前阶段:`Phase 32` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-033` +- 当前阶段:`Phase 33` - 当前焦点: - 已完成 `GFramework.Core` 当前 `MA0016` / `MA0002` / `MA0015` / `MA0077` 低风险收口批次 - 已复核 `net10.0` 下的 `MA0158` 基线:`GFramework.Core` / `GFramework.Cqrs` 当前共有 `16` 个 object lock @@ -61,10 +61,15 @@ 将内联测试源码与期望快照抽到类级常量、补齐测试类 XML 文档,并将仅作转发的异步测试改为直接返回 `Task` - 当前 `GFramework.SourceGenerators.Tests` Release build 基线已从 `43` 条降到 `40` 条; `AutoRegisterModuleGeneratorTests.cs` 已不再出现在 `MA0051` 列表中 + - 已完成 `GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorSnapshotTests.cs` 的 `MA0051` 收口: + 将 monster 场景的运行时契约与 schema 输入提取为类级常量,并把生成结果与快照目录解析拆成小 helper,保持 + 生成文件名、快照目录和断言语义不变 + - 当前 `GFramework.SourceGenerators.Tests` Release build 基线已从 `40` 条降到 `39` 条; + `SchemaConfigGeneratorSnapshotTests.cs` 已不再出现在 `MA0051` 列表中 - `GFramework.Godot` 的 `Timing.cs` 已同步适配新事件签名,但当前 worktree 的 Godot restore 资产仍受 Windows fallback package folder 干扰,独立 build 需在修复资产后补跑 - 后续继续按 warning 类型和数量批处理,而不是回退到按单文件切片推进 - 下一轮默认继续拆分 `GFramework.SourceGenerators.Tests` 的 `MA0051` 热点,优先处理 - `GeneratorSnapshotTest` 或 `ContextGetGeneratorTests` + `GeneratorSnapshotTest` 或 `ContextRegistrationAnalyzerTests` - 单次 `boot` 的工作树改动上限控制在约 `100` 个文件以内,避免 recovery context 与 review 面同时失控 - 若任务边界互不冲突,允许使用不同模型的 subagent 并行处理不同 warning 类型或不同目录,但必须遵守显式 ownership @@ -104,6 +109,8 @@ `43` 条,并通过 focused snapshot tests 保持行为不变 - 已完成 `AutoRegisterModuleGeneratorTests` 的单文件 `MA0051` 收口;当前 `GFramework.SourceGenerators.Tests` Release build 基线已降到 `40` 条,并通过 focused generator tests 保持输出契约不变 +- 已完成 `SchemaConfigGeneratorSnapshotTests` 的单文件 `MA0051` 收口;当前 `GFramework.SourceGenerators.Tests` + Release build 基线已降到 `39` 条,并通过 focused snapshot test 保持生成输出契约不变 ## 当前活跃事实 @@ -146,6 +153,8 @@ 并用 focused schema generator tests 验证 50 个用例通过 - `RP-032` 已完成 `AutoRegisterModuleGeneratorTests` 的 3 个 `MA0051` 收口:通过提取类级常量承载测试源码与快照,保持 生成文件名、断言路径与源生成输出不变;`GFramework.SourceGenerators.Tests` warnings-only 基线由 `43` 降至 `40` +- `RP-033` 已完成 `SchemaConfigGeneratorSnapshotTests` 的 `MA0051` 收口:monster schema 运行时契约与 schema 输入已提取为 + 类级常量,生成结果映射与快照目录解析已拆为小 helper;`GFramework.SourceGenerators.Tests` warnings-only 基线由 `40` 降至 `39` - `RP-021` 使用 `$gframework-pr-review` 复核当前分支 PR #269 后,修复仍在本地成立的 4 个项:将 `CqrsHandlerRegistryGenerator` 拆分为职责清晰的 partial 文件、为 `ContextAwareGenerator` 生成字段增加稳定前缀并补上 `SetContextProvider` 的运行时 null 校验、为 `Option` 补齐 ``,并新增字段重名场景的生成器快照测试 @@ -362,13 +371,18 @@ - 结果:`43 Warning(s)`,`0 Error(s)`;`LoggerGeneratorSnapshotTests.cs` 已不再出现在 `MA0051` 列表中 - `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-build --disable-build-servers --filter FullyQualifiedName~LoggerGeneratorSnapshotTests -m:1 -p:RestoreFallbackFolders="" -nologo` - 结果:`6 Passed`,`0 Failed` +- `RP-033` 的验证结果: + - `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release -t:Rebuild --no-restore --disable-build-servers -m:1 -p:UseSharedCompilation=false -p:RestoreFallbackFolders="" -nologo -clp:"Summary;WarningsOnly"` + - 结果:`39 Warning(s)`,`0 Error(s)`;`SchemaConfigGeneratorSnapshotTests.cs` 已不再出现在 `MA0051` 列表中 + - `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-build --disable-build-servers --filter FullyQualifiedName~SchemaConfigGeneratorSnapshotTests -m:1 -p:RestoreFallbackFolders="" -nologo` + - 结果:`1 Passed`,`0 Failed` - active 跟踪文件只保留当前恢复点、活跃事实、风险与下一步,不再重复保存已完成阶段的长篇历史 ## 下一步 1. 若要继续该主题,先读 active tracking,再按需展开历史归档中的 warning 热点与验证记录 -2. 下一轮优先继续 `GFramework.SourceGenerators.Tests` 的 `MA0051` 收口,先在 `AutoRegisterModuleGeneratorTests`、 - `GeneratorSnapshotTest` 或 `ContextGetGeneratorTests` 中选择一个单写集推进,不再把已清零的 `MA0004` / `MA0048` 混回写集 +2. 下一轮优先继续 `GFramework.SourceGenerators.Tests` 的 `MA0051` 收口,先在 `GeneratorSnapshotTest`、 + `ContextRegistrationAnalyzerTests` 或 `ContextGetGeneratorTests` 中选择一个单写集推进,不再把已清零的 `MA0004` / `MA0048` 混回写集 3. 若改回推进 `MA0158`,先设计 `net8.0` / `net9.0` / `net10.0` 多 target 条件编译方案,不直接批量替换共享源码中的 `object` lock 4. 若后续继续改动 `GFramework.Godot`,先修复该项目的 Linux 侧 restore 资产,再补跑独立 build diff --git a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md index d2b8a5d5..8deaf556 100644 --- a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md +++ b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md @@ -1,5 +1,34 @@ # Analyzer Warning Reduction 追踪 +## 2026-04-23 — RP-033 + +### 阶段:`SchemaConfigGeneratorSnapshotTests.cs` `MA0051` 收口(RP-033) + +- 启动复核: + - 按 `gframework-boot` 流程恢复当前 worktree,读取 `AGENTS.md`、`.ai/environment/tools.ai.yaml`、 + `ai-plan/public/README.md` 与 active topic 跟踪文件,确认当前分支 `fix/analyzer-warning-reduction-batch` + 仍映射到 `analyzer-warning-reduction` + - 用 + `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release -t:Rebuild --no-restore --disable-build-servers -m:1 -p:UseSharedCompilation=false -p:RestoreFallbackFolders="" -nologo -clp:"Summary;WarningsOnly"` + 复核当前 `MA0051` 热点,确认 `SchemaConfigGeneratorSnapshotTests.cs` 仍保留 1 个超长方法,适合作为单文件低风险写集 +- 决策: + - 保持 monster schema 场景的输入源码、schema 文本、生成文件名与快照目录不变,只收敛测试方法长度 + - 沿用前几轮 snapshot test 的收口策略:提取类级常量承载大段 fixture 输入,再用小 helper 封装生成结果映射与快照目录解析 + - 同一测试项目的 build/test 继续采用串行验证;并行执行会在 WSL worktree 上制造瞬时输出缺失,导致 `MSB3030` / `CS0006` +- 实施调整: + - 为 `SchemaConfigGeneratorSnapshotTests` 新增 `RuntimeContractsSource` 与 `MonsterSchema` 类级常量,保留既有 monster 场景内容 + - 把生成结果字典构造拆到 `GenerateSourcesForMonsterSchema()`,把快照目录解析拆到 `GetSchemaSnapshotFolder()` + - 保持 `AssertAllSnapshotsAsync(...)`、快照文件名与断言流程不变,不改生成器逻辑和 snapshot 资产 +- 验证结果: + - `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release -t:Rebuild --no-restore --disable-build-servers -m:1 -p:UseSharedCompilation=false -p:RestoreFallbackFolders="" -nologo -clp:"Summary;WarningsOnly"` + - 结果:`39 Warning(s)`,`0 Error(s)`;`SchemaConfigGeneratorSnapshotTests.cs` 已不再出现在 `MA0051` 列表中 + - `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-build --disable-build-servers --filter FullyQualifiedName~SchemaConfigGeneratorSnapshotTests -m:1 -p:RestoreFallbackFolders="" -nologo` + - 结果:`1 Passed`,`0 Failed` +- 下一步建议: + - 若继续压缩 `GFramework.SourceGenerators.Tests` 的 `MA0051`,优先处理只剩单个超长方法的 `GeneratorSnapshotTest` 或 + `ContextRegistrationAnalyzerTests` + - 若希望继续按 warning 数量收敛,则回到 `ContextGetGeneratorTests.cs`,但需要接受更大的单文件写集 + ## 2026-04-23 — RP-032 ### 阶段:`AutoRegisterModuleGeneratorTests.cs` `MA0051` 收口(RP-032)