diff --git a/GFramework.Game.Tests/Config/YamlConfigLoaderAllOfTests.cs b/GFramework.Game.Tests/Config/YamlConfigLoaderAllOfTests.cs index 09f0eebb..538d651b 100644 --- a/GFramework.Game.Tests/Config/YamlConfigLoaderAllOfTests.cs +++ b/GFramework.Game.Tests/Config/YamlConfigLoaderAllOfTests.cs @@ -90,7 +90,7 @@ public sealed class YamlConfigLoaderAllOfTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -124,7 +124,7 @@ public sealed class YamlConfigLoaderAllOfTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - await loader.LoadAsync(registry); + await loader.LoadAsync(registry).ConfigureAwait(false); var table = registry.GetTable("monster"); var reward = table.Get(1).Reward; @@ -165,7 +165,7 @@ public sealed class YamlConfigLoaderAllOfTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -210,7 +210,10 @@ public sealed class YamlConfigLoaderAllOfTests } """); - ArgumentNullException.ThrowIfNull(_rootPath); + if (_rootPath is null) + { + throw new InvalidOperationException("Root path is not initialized."); + } var loader = new YamlConfigLoader(_rootPath) .RegisterTable( @@ -220,7 +223,7 @@ public sealed class YamlConfigLoaderAllOfTests static config => config.Id); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -255,7 +258,7 @@ public sealed class YamlConfigLoaderAllOfTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -295,7 +298,7 @@ public sealed class YamlConfigLoaderAllOfTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -335,7 +338,7 @@ public sealed class YamlConfigLoaderAllOfTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -375,7 +378,7 @@ public sealed class YamlConfigLoaderAllOfTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -415,7 +418,7 @@ public sealed class YamlConfigLoaderAllOfTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -455,7 +458,7 @@ public sealed class YamlConfigLoaderAllOfTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -499,7 +502,7 @@ public sealed class YamlConfigLoaderAllOfTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { diff --git a/GFramework.Game.Tests/Config/YamlConfigLoaderDependentRequiredTests.cs b/GFramework.Game.Tests/Config/YamlConfigLoaderDependentRequiredTests.cs index d9978c61..9c5501a1 100644 --- a/GFramework.Game.Tests/Config/YamlConfigLoaderDependentRequiredTests.cs +++ b/GFramework.Game.Tests/Config/YamlConfigLoaderDependentRequiredTests.cs @@ -75,7 +75,7 @@ public sealed class YamlConfigLoaderDependentRequiredTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -124,7 +124,7 @@ public sealed class YamlConfigLoaderDependentRequiredTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - await loader.LoadAsync(registry); + await loader.LoadAsync(registry).ConfigureAwait(false); var table = registry.GetTable("monster"); Assert.That(table.Count, Is.EqualTo(1)); @@ -169,7 +169,7 @@ public sealed class YamlConfigLoaderDependentRequiredTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - await loader.LoadAsync(registry); + await loader.LoadAsync(registry).ConfigureAwait(false); var table = registry.GetTable("monster"); var reward = table.Get(1).Reward; @@ -217,7 +217,7 @@ public sealed class YamlConfigLoaderDependentRequiredTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -267,7 +267,7 @@ public sealed class YamlConfigLoaderDependentRequiredTests var loader = CreateCaseSensitiveRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -317,7 +317,7 @@ public sealed class YamlConfigLoaderDependentRequiredTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { diff --git a/GFramework.Game.Tests/Config/YamlConfigLoaderDependentSchemasTests.cs b/GFramework.Game.Tests/Config/YamlConfigLoaderDependentSchemasTests.cs index 66a1e1a9..009655db 100644 --- a/GFramework.Game.Tests/Config/YamlConfigLoaderDependentSchemasTests.cs +++ b/GFramework.Game.Tests/Config/YamlConfigLoaderDependentSchemasTests.cs @@ -74,7 +74,7 @@ public sealed class YamlConfigLoaderDependentSchemasTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -106,7 +106,7 @@ public sealed class YamlConfigLoaderDependentSchemasTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - await loader.LoadAsync(registry); + await loader.LoadAsync(registry).ConfigureAwait(false); var table = registry.GetTable("monster"); Assert.That(table.Count, Is.EqualTo(1)); @@ -133,7 +133,7 @@ public sealed class YamlConfigLoaderDependentSchemasTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - await loader.LoadAsync(registry); + await loader.LoadAsync(registry).ConfigureAwait(false); var table = registry.GetTable("monster"); var reward = table.Get(1).Reward; @@ -174,7 +174,7 @@ public sealed class YamlConfigLoaderDependentSchemasTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -220,7 +220,7 @@ public sealed class YamlConfigLoaderDependentSchemasTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -264,7 +264,7 @@ public sealed class YamlConfigLoaderDependentSchemasTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -283,7 +283,10 @@ public sealed class YamlConfigLoaderDependentSchemasTests /// 要写入的 YAML 或 schema 内容。 private void CreateConfigFile(string relativePath, string content) { - ArgumentNullException.ThrowIfNull(_rootPath); + if (_rootPath is null) + { + throw new InvalidOperationException("Root path is not initialized."); + } var filePath = Path.Combine(_rootPath, relativePath.Replace('/', Path.DirectorySeparatorChar)); var directoryPath = Path.GetDirectoryName(filePath); @@ -353,7 +356,10 @@ public sealed class YamlConfigLoaderDependentSchemasTests /// 已注册测试表与 schema 路径的加载器。 private YamlConfigLoader CreateMonsterRewardLoader() { - ArgumentNullException.ThrowIfNull(_rootPath); + if (_rootPath is null) + { + throw new InvalidOperationException("Root path is not initialized."); + } return new YamlConfigLoader(_rootPath) .RegisterTable( diff --git a/GFramework.Game.Tests/Config/YamlConfigLoaderEnumTests.cs b/GFramework.Game.Tests/Config/YamlConfigLoaderEnumTests.cs index d6253a8f..0edbb6e2 100644 --- a/GFramework.Game.Tests/Config/YamlConfigLoaderEnumTests.cs +++ b/GFramework.Game.Tests/Config/YamlConfigLoaderEnumTests.cs @@ -75,7 +75,7 @@ public class YamlConfigLoaderEnumTests var loader = CreateLoader(); var registry = new ConfigRegistry(); - await loader.LoadAsync(registry); + await loader.LoadAsync(registry).ConfigureAwait(false); var table = registry.GetTable("monster"); Assert.Multiple(() => @@ -127,7 +127,7 @@ public class YamlConfigLoaderEnumTests var loader = CreateLoader(); var registry = new ConfigRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -176,7 +176,7 @@ public class YamlConfigLoaderEnumTests var loader = CreateLoader(); var registry = new ConfigRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { diff --git a/GFramework.Game.Tests/Config/YamlConfigLoaderIfThenElseTests.cs b/GFramework.Game.Tests/Config/YamlConfigLoaderIfThenElseTests.cs index c73c3a56..071bc3e6 100644 --- a/GFramework.Game.Tests/Config/YamlConfigLoaderIfThenElseTests.cs +++ b/GFramework.Game.Tests/Config/YamlConfigLoaderIfThenElseTests.cs @@ -105,7 +105,7 @@ public sealed class YamlConfigLoaderIfThenElseTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -137,7 +137,7 @@ public sealed class YamlConfigLoaderIfThenElseTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - await loader.LoadAsync(registry); + await loader.LoadAsync(registry).ConfigureAwait(false); var table = registry.GetTable("monster"); var reward = table.Get(1).Reward; @@ -170,7 +170,7 @@ public sealed class YamlConfigLoaderIfThenElseTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -202,7 +202,7 @@ public sealed class YamlConfigLoaderIfThenElseTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - await loader.LoadAsync(registry); + await loader.LoadAsync(registry).ConfigureAwait(false); var table = registry.GetTable("monster"); var reward = table.Get(1).Reward; @@ -250,7 +250,10 @@ public sealed class YamlConfigLoaderIfThenElseTests } """); - ArgumentNullException.ThrowIfNull(_rootPath); + if (_rootPath is null) + { + throw new InvalidOperationException("Root path is not initialized."); + } var loader = new YamlConfigLoader(_rootPath) .RegisterTable( @@ -260,7 +263,7 @@ public sealed class YamlConfigLoaderIfThenElseTests static config => config.Id); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -301,7 +304,7 @@ public sealed class YamlConfigLoaderIfThenElseTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -342,7 +345,7 @@ public sealed class YamlConfigLoaderIfThenElseTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -390,7 +393,7 @@ public sealed class YamlConfigLoaderIfThenElseTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -409,7 +412,10 @@ public sealed class YamlConfigLoaderIfThenElseTests /// 配置文件内容。 private void CreateConfigFile(string relativePath, string content) { - ArgumentNullException.ThrowIfNull(_rootPath); + if (_rootPath is null) + { + throw new InvalidOperationException("Root path is not initialized."); + } var filePath = Path.Combine(_rootPath, relativePath.Replace('/', Path.DirectorySeparatorChar)); var directoryPath = Path.GetDirectoryName(filePath); @@ -496,7 +502,10 @@ public sealed class YamlConfigLoaderIfThenElseTests /// 已注册测试表与 schema 路径的加载器。 private YamlConfigLoader CreateMonsterRewardLoader() { - ArgumentNullException.ThrowIfNull(_rootPath); + if (_rootPath is null) + { + throw new InvalidOperationException("Root path is not initialized."); + } return new YamlConfigLoader(_rootPath) .RegisterTable( diff --git a/GFramework.Game.Tests/Config/YamlConfigLoaderNegationTests.cs b/GFramework.Game.Tests/Config/YamlConfigLoaderNegationTests.cs index 169a03bb..cc510b06 100644 --- a/GFramework.Game.Tests/Config/YamlConfigLoaderNegationTests.cs +++ b/GFramework.Game.Tests/Config/YamlConfigLoaderNegationTests.cs @@ -70,7 +70,7 @@ public sealed class YamlConfigLoaderNegationTests var loader = CreateMonsterLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -118,7 +118,7 @@ public sealed class YamlConfigLoaderNegationTests var loader = CreateMonsterLoader(); var registry = CreateRegistry(); - await loader.LoadAsync(registry); + await loader.LoadAsync(registry).ConfigureAwait(false); var table = registry.GetTable("monster"); @@ -172,7 +172,7 @@ public sealed class YamlConfigLoaderNegationTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { @@ -227,7 +227,7 @@ public sealed class YamlConfigLoaderNegationTests var loader = CreateMonsterRewardLoader(); var registry = CreateRegistry(); - await loader.LoadAsync(registry); + await loader.LoadAsync(registry).ConfigureAwait(false); var table = registry.GetTable("monster"); @@ -272,7 +272,7 @@ public sealed class YamlConfigLoaderNegationTests var loader = CreateMonsterLoader(); var registry = CreateRegistry(); - var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry)); + var exception = Assert.ThrowsAsync(async () => await loader.LoadAsync(registry).ConfigureAwait(false)); Assert.Multiple(() => { diff --git a/GFramework.Game.Tests/Config/YamlConfigSchemaValidatorTests.cs b/GFramework.Game.Tests/Config/YamlConfigSchemaValidatorTests.cs index 7a61a7e9..40b19abf 100644 --- a/GFramework.Game.Tests/Config/YamlConfigSchemaValidatorTests.cs +++ b/GFramework.Game.Tests/Config/YamlConfigSchemaValidatorTests.cs @@ -191,7 +191,10 @@ public sealed class YamlConfigSchemaValidatorTests string relativePath, string content) { - ArgumentNullException.ThrowIfNull(_rootPath); + if (_rootPath is null) + { + throw new InvalidOperationException("Root path is not initialized."); + } var fullPath = Path.Combine(_rootPath, relativePath.Replace('/', Path.DirectorySeparatorChar)); var directoryPath = Path.GetDirectoryName(fullPath); diff --git a/GFramework.Game.Tests/Config/YamlConfigTextValidatorTests.cs b/GFramework.Game.Tests/Config/YamlConfigTextValidatorTests.cs index 047b642c..483617c6 100644 --- a/GFramework.Game.Tests/Config/YamlConfigTextValidatorTests.cs +++ b/GFramework.Game.Tests/Config/YamlConfigTextValidatorTests.cs @@ -132,7 +132,7 @@ public sealed class YamlConfigTextValidatorTests "monster/generated.yaml", """ id: 1 - """)); + """).ConfigureAwait(false)); Assert.Multiple(() => { diff --git a/GFramework.Game.Tests/Data/PersistenceTestUtilities.cs b/GFramework.Game.Tests/Data/PersistenceTestUtilities.cs deleted file mode 100644 index 08dfb4e1..00000000 --- a/GFramework.Game.Tests/Data/PersistenceTestUtilities.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using GFramework.Game.Abstractions.Data; -using GFramework.Game.Abstractions.Enums; - -namespace GFramework.Game.Tests.Data; - -/// -/// 为持久化测试提供稳定的测试数据位置实现。 -/// -internal sealed class TestDataLocation : IDataLocation -{ - /// - /// 初始化测试数据位置。 - /// - /// 测试使用的存储键。 - /// 测试使用的存储类型。 - /// 测试使用的命名空间。 - /// 附加测试元数据。 - public TestDataLocation( - string key, - StorageKinds kinds = StorageKinds.Local, - string? namespaceValue = null, - IReadOnlyDictionary? metadata = null) - { - Key = key; - Kinds = kinds; - Namespace = namespaceValue; - Metadata = metadata; - } - - /// - /// 获取测试数据对应的存储键。 - /// - public string Key { get; } - - /// - /// 获取测试数据使用的存储类型。 - /// - public StorageKinds Kinds { get; } - - /// - /// 获取测试数据使用的命名空间。 - /// - public string? Namespace { get; } - - /// - /// 获取附加到测试位置上的元数据。 - /// - public IReadOnlyDictionary? Metadata { get; } -} - -/// -/// 为基础存档仓库测试提供的简单存档模型。 -/// -internal sealed class TestSaveData : IData -{ - /// - /// 获取或设置测试存档中的名称字段。 - /// - public string Name { get; set; } = string.Empty; -} - -/// -/// 为存档迁移测试提供的版本化存档模型。 -/// -internal sealed class TestVersionedSaveData : IVersionedData -{ - /// - /// 获取或设置测试存档中的名称字段。 - /// - public string Name { get; set; } = string.Empty; - - /// - /// 获取或设置测试存档中的等级字段。 - /// - public int Level { get; set; } - - /// - /// 获取或设置测试存档中的经验字段。 - /// - public int Experience { get; set; } - - /// - /// 获取或设置当前测试存档的版本号。 - /// - public int Version { get; set; } = 3; - - /// - /// 获取或设置测试存档的最后修改时间。 - /// - public DateTime LastModified { get; set; } = DateTime.UtcNow; -} - -/// -/// 为通用持久化测试提供的简单数据模型。 -/// -internal sealed class TestSimpleData : IData -{ - /// - /// 获取或设置测试数据中的整数值。 - /// - public int Value { get; set; } -} - -/// -/// 为批量持久化测试提供的另一种数据模型,用于验证运行时类型不会在接口路径上退化。 -/// -internal sealed class TestNamedData : IData -{ - /// - /// 获取或设置测试数据中的名称值。 - /// - public string Name { get; set; } = string.Empty; -} diff --git a/GFramework.Game.Tests/Data/PersistenceTests.cs b/GFramework.Game.Tests/Data/PersistenceTests.cs index 8813076b..f5549517 100644 --- a/GFramework.Game.Tests/Data/PersistenceTests.cs +++ b/GFramework.Game.Tests/Data/PersistenceTests.cs @@ -38,12 +38,12 @@ public class PersistenceTests using var storage = new FileStorage(root, new JsonSerializer(), ".json"); var saved = new TestSimpleData { Value = 5 }; - await storage.WriteAsync("folder/item", saved); + await storage.WriteAsync("folder/item", saved).ConfigureAwait(false); - var loaded = await storage.ReadAsync("folder/item"); + var loaded = await storage.ReadAsync("folder/item").ConfigureAwait(false); Assert.That(loaded.Value, Is.EqualTo(saved.Value)); - Assert.ThrowsAsync(async () => await storage.WriteAsync("../escape", new TestSimpleData())); + Assert.ThrowsAsync(async () => await storage.WriteAsync("../escape", new TestSimpleData()).ConfigureAwait(false)); } /// @@ -108,7 +108,7 @@ public class PersistenceTests .RegisterMigration(new TestSaveMigrationV2ToV3()); var loaded = await repository.LoadAsync(1); - var persisted = await storage.ReadAsync("saves/slot_1/save"); + var persisted = await storage.ReadAsync("saves/slot_1/save").ConfigureAwait(false); Assert.Multiple(() => { @@ -185,7 +185,7 @@ public class PersistenceTests var repository = new SaveRepository(storage, config) .RegisterMigration(new TestSaveMigrationV1ToV2()); - var exception = Assert.ThrowsAsync(async () => await repository.LoadAsync(1)); + var exception = Assert.ThrowsAsync(async () => await repository.LoadAsync(1).ConfigureAwait(false)); Assert.That(exception!.Message, Does.Contain("from version 2")); } @@ -218,8 +218,8 @@ public class PersistenceTests var repository = new SaveRepository(storage, config) .RegisterMigration(new TestSaveMigrationV1ToV2ReturningV3()); - var exception = Assert.ThrowsAsync(async () => await repository.LoadAsync(1)); - var persisted = await storage.ReadAsync("saves/slot_1/save"); + var exception = Assert.ThrowsAsync(async () => await repository.LoadAsync(1).ConfigureAwait(false)); + var persisted = await storage.ReadAsync("saves/slot_1/save").ConfigureAwait(false); Assert.Multiple(() => { @@ -270,7 +270,7 @@ public class PersistenceTests repository.RegisterMigration(new TestSaveMigrationV2ToV3()); continueMigration.Set(); - var exception = Assert.ThrowsAsync(async () => await loadTask); + var exception = Assert.ThrowsAsync(async () => await loadTask.ConfigureAwait(false)); var persisted = await storage.ReadAsync("saves/slot_1/save"); Assert.Multiple(() => diff --git a/GFramework.Game.Tests/Data/TestDataLocation.cs b/GFramework.Game.Tests/Data/TestDataLocation.cs new file mode 100644 index 00000000..b1cd6519 --- /dev/null +++ b/GFramework.Game.Tests/Data/TestDataLocation.cs @@ -0,0 +1,49 @@ +using GFramework.Game.Abstractions.Data; +using GFramework.Game.Abstractions.Enums; + +namespace GFramework.Game.Tests.Data; + +/// +/// 为持久化测试提供稳定的测试数据位置实现。 +/// +internal sealed class TestDataLocation : IDataLocation +{ + /// + /// 初始化测试数据位置。 + /// + /// 测试使用的存储键。 + /// 测试使用的存储类型。 + /// 测试使用的命名空间。 + /// 附加测试元数据。 + public TestDataLocation( + string key, + StorageKinds kinds = StorageKinds.Local, + string? namespaceValue = null, + IReadOnlyDictionary? metadata = null) + { + Key = key; + Kinds = kinds; + Namespace = namespaceValue; + Metadata = metadata; + } + + /// + /// 获取测试数据对应的存储键。 + /// + public string Key { get; } + + /// + /// 获取测试数据使用的存储类型。 + /// + public StorageKinds Kinds { get; } + + /// + /// 获取测试数据使用的命名空间。 + /// + public string? Namespace { get; } + + /// + /// 获取附加到测试位置上的元数据。 + /// + public IReadOnlyDictionary? Metadata { get; } +} diff --git a/GFramework.Game.Tests/Data/TestNamedData.cs b/GFramework.Game.Tests/Data/TestNamedData.cs new file mode 100644 index 00000000..4c355437 --- /dev/null +++ b/GFramework.Game.Tests/Data/TestNamedData.cs @@ -0,0 +1,14 @@ +using GFramework.Game.Abstractions.Data; + +namespace GFramework.Game.Tests.Data; + +/// +/// 为批量持久化测试提供的另一种数据模型,用于验证运行时类型不会在接口路径上退化。 +/// +internal sealed class TestNamedData : IData +{ + /// + /// 获取或设置测试数据中的名称值。 + /// + public string Name { get; set; } = string.Empty; +} diff --git a/GFramework.Game.Tests/Data/TestSaveData.cs b/GFramework.Game.Tests/Data/TestSaveData.cs new file mode 100644 index 00000000..4236088e --- /dev/null +++ b/GFramework.Game.Tests/Data/TestSaveData.cs @@ -0,0 +1,14 @@ +using GFramework.Game.Abstractions.Data; + +namespace GFramework.Game.Tests.Data; + +/// +/// 为基础存档仓库测试提供的简单存档模型。 +/// +internal sealed class TestSaveData : IData +{ + /// + /// 获取或设置测试存档中的名称字段。 + /// + public string Name { get; set; } = string.Empty; +} diff --git a/GFramework.Game.Tests/Data/TestSimpleData.cs b/GFramework.Game.Tests/Data/TestSimpleData.cs new file mode 100644 index 00000000..6af95297 --- /dev/null +++ b/GFramework.Game.Tests/Data/TestSimpleData.cs @@ -0,0 +1,14 @@ +using GFramework.Game.Abstractions.Data; + +namespace GFramework.Game.Tests.Data; + +/// +/// 为通用持久化测试提供的简单数据模型。 +/// +internal sealed class TestSimpleData : IData +{ + /// + /// 获取或设置测试数据中的整数值。 + /// + public int Value { get; set; } +} diff --git a/GFramework.Game.Tests/Data/TestVersionedSaveData.cs b/GFramework.Game.Tests/Data/TestVersionedSaveData.cs new file mode 100644 index 00000000..bc8e2773 --- /dev/null +++ b/GFramework.Game.Tests/Data/TestVersionedSaveData.cs @@ -0,0 +1,35 @@ +using System; +using GFramework.Game.Abstractions.Data; + +namespace GFramework.Game.Tests.Data; + +/// +/// 为存档迁移测试提供的版本化存档模型。 +/// +internal sealed class TestVersionedSaveData : IVersionedData +{ + /// + /// 获取或设置测试存档中的名称字段。 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 获取或设置测试存档中的等级字段。 + /// + public int Level { get; set; } + + /// + /// 获取或设置测试存档中的经验字段。 + /// + public int Experience { get; set; } + + /// + /// 获取或设置当前测试存档的版本号。 + /// + public int Version { get; set; } = 3; + + /// + /// 获取或设置测试存档的最后修改时间。 + /// + public DateTime LastModified { get; set; } = DateTime.UtcNow; +} 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 424cc2b6..5085564e 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 @@ -6,34 +6,35 @@ ## 当前恢复点 -- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-053` -- 当前阶段:`Phase 53` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-054` +- 当前阶段:`Phase 54` - 当前焦点: - - `2026-04-24` 本轮按 `$gframework-batch-boot 75` 重新建立当前分支相对 `origin/main` 的 batch 基线,并从整仓 `Release` build 里挑选低风险热点 - - `GFramework.Godot` 已完成一轮独立 warning 清理:`GodotYamlConfigEnvironment` 的目录枚举逻辑已拆分 helper,`AbstractArchitecture` / `SceneBehaviorBase` 中需要保留 Godot 同步上下文的 await 已显式改为 `.ConfigureAwait(true)` - - `GFramework.Godot.Tests` 已同步清理对应测试 warning:异步断言显式使用 `.ConfigureAwait(false)`,`RichTextMarkupTests` 中测试字典显式指定 `StringComparer.Ordinal` - - 当前代码批次相对 `origin/main` 的待提交 diff 为 `6` 个文件、`107` 行变更,远低于 `$gframework-batch-boot 75` 的主停止阈值;本轮在这里收口,是因为下一批候选将进入 `GFramework.Game` 等高基线模块,而不再是同等级低风险切片 + - `2026-04-24` 本轮继续按 `$gframework-batch-boot 75` 推进,切入 `GFramework.Game.Tests` 的低风险测试 warning,而不进入 `YamlConfigLoaderTests.cs` 等高上下文热点 + - 已完成 `PersistenceTestUtilities` 的单类型拆分,并在多组 YAML / persistence 测试中补齐 `.ConfigureAwait(false)` 与字段态显式状态检查 + - `GFramework.Game.Tests` 当前 `Release` build 已从本轮入口观测值 `116 warning(s)` 收敛到 `71 warning(s)`,且本轮 touched files 已不再出现在 warning 输出里 + - 当前工作树相对 `origin/main` 的累计 diff 已达到 `76` 个文件、`986` 行变更,超过 `$gframework-batch-boot 75` 的主停止阈值;本轮必须在提交后停止继续扩批 ## 当前活跃事实 - 之前记录的 plain `dotnet build` `0 Warning(s)` 属于增量构建假阴性,不能再作为 warning 检查真值 - 本轮直接执行仓库根目录 `dotnet clean` 仍在 `ValidateSolutionConfiguration` 阶段失败,输出未提供具体 error 文本 -- 本轮直接执行仓库根目录 `dotnet build GFramework.sln -c Release` 成功,并给出 `1122 warning(s)` 的当前整仓观测值 -- `GFramework.Godot/GFramework.Godot.csproj -c Release` 在本轮收尾验证中已达到 `0 Warning(s)`、`0 Error(s)` -- `GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release` 在串行复验中已达到 `0 Warning(s)`、`0 Error(s)` -- 本轮已验证 `dotnet test GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release --filter "FullyQualifiedName~AbstractArchitectureModuleInstallationTests|FullyQualifiedName~GodotYamlConfigLoaderTests|FullyQualifiedName~RichTextMarkupTests"`,结果为 `Passed: 15` -- `GFramework.Godot` 原先暴露的 `MA0051` 与 `MA0004` 热点都已清理完成;当前同域低风险切片基本耗尽 +- 本轮直接执行仓库根目录 `dotnet build GFramework.sln -c Release` 成功,并给出 `116 warning(s)` 的当前整仓入口观测值;其中低风险热点主要落在 `GFramework.Game.Tests` +- `dotnet build GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release` 在本轮收尾验证中为 `71 Warning(s)`、`0 Error(s)`;剩余 warning 已集中在未触碰的 `YamlConfigLoaderTests.cs`、`GeneratedConfigConsumerIntegrationTests.cs`、`GameConfigBootstrapTests.cs`、`ArchitectureConfigIntegrationTests.cs`、`JsonSerializerTests.cs` +- 本轮已验证 `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderIfThenElseTests|FullyQualifiedName~YamlConfigLoaderDependentSchemasTests|FullyQualifiedName~YamlConfigLoaderDependentRequiredTests|FullyQualifiedName~YamlConfigLoaderNegationTests|FullyQualifiedName~YamlConfigLoaderAllOfTests|FullyQualifiedName~YamlConfigLoaderEnumTests|FullyQualifiedName~YamlConfigTextValidatorTests|FullyQualifiedName~YamlConfigSchemaValidatorTests|FullyQualifiedName~PersistenceTests"`,结果为 `Passed: 63` +- `PersistenceTestUtilities.cs` 已拆分为 `TestDataLocation.cs`、`TestSaveData.cs`、`TestVersionedSaveData.cs`、`TestSimpleData.cs`、`TestNamedData.cs`,与仓库“一文件一主类型”风格对齐 ## 当前风险 - 如果后续继续依赖增量 `dotnet build`,容易再次把 warning 数量误判为 0 - 缓解措施:每轮 warning 检查前先执行 `dotnet clean`,再执行目标 `dotnet build` - 仓库根目录 `dotnet clean` 目前仍然无法给出新的 clean 基线 - - 缓解措施:若下一轮继续做整仓 warning reduction,先定位 `dotnet clean` 的 solution-level 失败原因,或明确继续沿用用户确认的 `1193 warning(s)` clean 基线与本轮 `1122 warning(s)` direct build 观测值 + - 缓解措施:若下一轮继续做整仓 warning reduction,先定位 `dotnet clean` 的 solution-level / project-level 失败原因,或明确继续沿用用户确认的 `1193 warning(s)` clean 基线与本轮 direct build 观测值 - 当前 worktree 仍存在未跟踪的 `.codex` 目录 - 缓解措施:提交当前批次时只暂存 analyzer-warning-reduction 相关源码与 `ai-plan` 文件,避免把工作目录辅助文件混入提交 -- 下一轮最明显的剩余热点将转入 `GFramework.Game` 等高 warning 基线模块 - - 缓解措施:恢复时先重新跑整仓 build 热点筛选,再决定是否接受更高上下文成本的 `GFramework.Game` 切片,或先排查 solution-level clean 失败原因 +- 当前批次已触发 `$gframework-batch-boot 75` 的主停止条件 + - 缓解措施:本轮提交后停止继续扩批;下一次继续前先评估是否需要基于更新后的 `origin/main` 重新选择基线,或切到新分支 / 新轮次处理剩余 `GFramework.Game.Tests` 热点 +- `GFramework.Game.Tests` 的剩余 warning 主要集中在大文件与集成测试文件 + - 缓解措施:后续若继续,优先把 `YamlConfigLoaderTests.cs` 单独作为一个高上下文切片处理,不要和其它 warning family 混批 ## 活跃文档 @@ -52,16 +53,15 @@ - `dotnet clean GFramework.sln -c Release` - 结果:失败;停在 solution `ValidateSolutionConfiguration`,`0 Warning(s)`、`0 Error(s)`,未输出更具体的 error 文本 - `dotnet build GFramework.sln -c Release` - - 结果:成功;`1122 Warning(s)`、`0 Error(s)` -- `dotnet build GFramework.Godot/GFramework.Godot.csproj -c Release` - - 结果:成功;`0 Warning(s)`、`0 Error(s)` -- `dotnet test GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release --filter "FullyQualifiedName~AbstractArchitectureModuleInstallationTests|FullyQualifiedName~GodotYamlConfigLoaderTests|FullyQualifiedName~RichTextMarkupTests"` - - 结果:成功;`Passed: 15`、`Failed: 0` -- `dotnet build GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release` - - 首次并行验证:成功;`1 Warning(s)`、`0 Error(s)`;`MSB3026` 来自与并行 `dotnet test` 竞争同一输出 DLL - - 串行复验:成功;`0 Warning(s)`、`0 Error(s)` + - 结果:成功;`116 Warning(s)`、`0 Error(s)` +- `dotnet clean GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release` + - 结果:失败;clean 阶段在 MSBuild 清理路径结束前返回 `0 Warning(s)`、`0 Error(s)`,未输出额外错误文本 +- `dotnet build GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release` + - 结果:成功;`71 Warning(s)`、`0 Error(s)` +- `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderIfThenElseTests|FullyQualifiedName~YamlConfigLoaderDependentSchemasTests|FullyQualifiedName~YamlConfigLoaderDependentRequiredTests|FullyQualifiedName~YamlConfigLoaderNegationTests|FullyQualifiedName~YamlConfigLoaderAllOfTests|FullyQualifiedName~YamlConfigLoaderEnumTests|FullyQualifiedName~YamlConfigTextValidatorTests|FullyQualifiedName~YamlConfigSchemaValidatorTests|FullyQualifiedName~PersistenceTests"` + - 结果:成功;`Passed: 63`、`Failed: 0` ## 下一步建议 -1. 提交当前 `GFramework.Godot` / `GFramework.Godot.Tests` warning 清理批次,并继续保持只纳入本 topic 相关文件 -2. 下一轮若继续使用 `$gframework-batch-boot 75`,先决定是优先排查 solution-level `dotnet clean` 失败,还是接受更高上下文成本进入 `GFramework.Game` warning 热点 +1. 提交当前 `GFramework.Game.Tests` warning 清理批次与 `RP-054` tracking 更新,然后停止当前 batch loop,因为 branch diff 已达 `76/75` +2. 下一轮若继续 warning reduction,应先决定是重新整理 `origin/main` 基线,还是单独开一个高上下文批次处理 `YamlConfigLoaderTests.cs` 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 e05a9c54..fcb8b35e 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 @@ -2,6 +2,36 @@ # Analyzer Warning Reduction 追踪 +## 2026-04-24 — RP-054 + +### 阶段:`GFramework.Game.Tests` 低风险测试 warning 批次(触发文件数停止阈值) + +- 触发背景: + - 用户要求“直接进入下一批”,继续沿 `$gframework-batch-boot 75` 自动推进 warning reduction + - 以 `origin/main` 为基线时,上一批提交后分支累计 diff 仍只有 `8` 个文件,足够再落一个独立批次 + - 重新执行 `dotnet clean GFramework.sln -c Release` 仍停在 `ValidateSolutionConfiguration`,因此继续以直接 `dotnet build GFramework.sln -c Release` 的输出挑选低风险热点 +- 主线程实施: + - 从整仓 `Release build` 的 `116 warning(s)` 入口观测值中,选择 `GFramework.Game.Tests` 的小型测试文件和 `PersistenceTestUtilities.cs` 作为当前批次,刻意避开 `YamlConfigLoaderTests.cs` 这类高上下文大文件 + - 在 `YamlConfigLoaderIfThenElseTests.cs`、`YamlConfigLoaderDependentSchemasTests.cs`、`YamlConfigLoaderDependentRequiredTests.cs`、`YamlConfigLoaderNegationTests.cs`、`YamlConfigLoaderAllOfTests.cs`、`YamlConfigLoaderEnumTests.cs`、`YamlConfigTextValidatorTests.cs`、`PersistenceTests.cs` 中补齐 `.ConfigureAwait(false)`,并把字段态 `_rootPath` 的 `ThrowIfNull` 改为显式 `InvalidOperationException` + - 将 `PersistenceTestUtilities.cs` 拆分为 `TestDataLocation.cs`、`TestSaveData.cs`、`TestVersionedSaveData.cs`、`TestSimpleData.cs`、`TestNamedData.cs`,消除 `MA0048` 并对齐仓库的一文件一主类型风格 + - 在 `YamlConfigSchemaValidatorTests.cs` 中把字段态 `_rootPath` 的校验改成显式状态异常,避免继续触发 `MA0015` +- 验证里程碑: + - `dotnet clean GFramework.sln -c Release` + - 结果:失败;停在 `ValidateSolutionConfiguration`,`0 Warning(s)`、`0 Error(s)` + - `dotnet build GFramework.sln -c Release` + - 结果:成功;`116 Warning(s)`、`0 Error(s)` + - `dotnet clean GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release` + - 结果:失败;clean 阶段提前结束,`0 Warning(s)`、`0 Error(s)` + - `dotnet build GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release` + - 第一轮批次后:成功;`80 Warning(s)`、`0 Error(s)` + - 收尾修正后:成功;`71 Warning(s)`、`0 Error(s)` + - `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderIfThenElseTests|FullyQualifiedName~YamlConfigLoaderDependentSchemasTests|FullyQualifiedName~YamlConfigLoaderDependentRequiredTests|FullyQualifiedName~YamlConfigLoaderNegationTests|FullyQualifiedName~YamlConfigLoaderAllOfTests|FullyQualifiedName~YamlConfigLoaderEnumTests|FullyQualifiedName~YamlConfigTextValidatorTests|FullyQualifiedName~YamlConfigSchemaValidatorTests|FullyQualifiedName~PersistenceTests"` + - 结果:成功;`Passed: 63`、`Failed: 0` +- 当前结论: + - `GFramework.Game.Tests` 本轮入口热点已从 `116 warning(s)` 收敛到 `71 warning(s)`,且本轮 touched files 不再出现在 warning 输出中 + - 当前工作树相对 `origin/main` 的累计 diff 已达到 `76` 个文件、`986` 行,超过 `$gframework-batch-boot 75` 的主停止阈值 + - 按批处理技能规则,本轮必须在提交当前批次后停止;剩余候选应在新一轮里单独评估,尤其是 `YamlConfigLoaderTests.cs` + ## 2026-04-24 — RP-053 ### 阶段:`GFramework.Godot` / `GFramework.Godot.Tests` 小批次 warning 清理