mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-07 00:39:00 +08:00
refactor(source-generators-tests): 收敛自动注册模块测试告警
- 重构 AutoRegisterModuleGeneratorTests 的内联测试源码与快照常量,降低 MA0051 方法长度并保持生成断言不变 - 更新 analyzer warning reduction 的 tracking 与 trace,记录 RP-032 的验证结果和下一步恢复点
This commit is contained in:
parent
31ae1460a9
commit
9d251ab1f8
@ -4,109 +4,323 @@ using GFramework.SourceGenerators.Tests.Core;
|
||||
namespace GFramework.SourceGenerators.Tests.Architectures;
|
||||
|
||||
[TestFixture]
|
||||
/// <summary>
|
||||
/// 验证 <see cref="AutoRegisterModuleGenerator" /> 在模块自动注册场景下的生成契约与输出顺序。
|
||||
/// </summary>
|
||||
public class AutoRegisterModuleGeneratorTests
|
||||
{
|
||||
private const string AttributeOrderSource = """
|
||||
using System;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Architectures
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class AutoRegisterModuleAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterModelAttribute : Attribute
|
||||
{
|
||||
public RegisterModelAttribute(Type modelType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterSystemAttribute : Attribute
|
||||
{
|
||||
public RegisterSystemAttribute(Type systemType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterUtilityAttribute : Attribute
|
||||
{
|
||||
public RegisterUtilityAttribute(Type utilityType) { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Architectures
|
||||
{
|
||||
public interface IArchitecture
|
||||
{
|
||||
T RegisterModel<T>(T model) where T : GFramework.Core.Abstractions.Model.IModel;
|
||||
T RegisterSystem<T>(T system) where T : GFramework.Core.Abstractions.Systems.ISystem;
|
||||
T RegisterUtility<T>(T utility) where T : GFramework.Core.Abstractions.Utility.IUtility;
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Model
|
||||
{
|
||||
public interface IModel { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Systems
|
||||
{
|
||||
public interface ISystem { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Utility
|
||||
{
|
||||
public interface IUtility { }
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Abstractions.Utility;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
public sealed class PlayerModel : IModel { }
|
||||
public sealed class CombatSystem : ISystem { }
|
||||
public sealed class AudioUtility : IUtility { }
|
||||
|
||||
[AutoRegisterModule]
|
||||
[RegisterSystem(typeof(CombatSystem))]
|
||||
[RegisterModel(typeof(PlayerModel))]
|
||||
[RegisterUtility(typeof(AudioUtility))]
|
||||
public partial class GameplayModule
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
private const string AttributeOrderExpected = """
|
||||
// <auto-generated />
|
||||
#nullable enable
|
||||
|
||||
namespace TestApp;
|
||||
|
||||
partial class GameplayModule
|
||||
{
|
||||
public void Install(global::GFramework.Core.Abstractions.Architectures.IArchitecture architecture)
|
||||
{
|
||||
architecture.RegisterSystem(new global::TestApp.CombatSystem());
|
||||
architecture.RegisterModel(new global::TestApp.PlayerModel());
|
||||
architecture.RegisterUtility(new global::TestApp.AudioUtility());
|
||||
}
|
||||
}
|
||||
|
||||
""";
|
||||
|
||||
private const string DeterministicOrderCommonSource = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Architectures
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class AutoRegisterModuleAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterModelAttribute : Attribute
|
||||
{
|
||||
public RegisterModelAttribute(Type modelType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterSystemAttribute : Attribute
|
||||
{
|
||||
public RegisterSystemAttribute(Type systemType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterUtilityAttribute : Attribute
|
||||
{
|
||||
public RegisterUtilityAttribute(Type utilityType) { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Architectures
|
||||
{
|
||||
public interface IArchitecture
|
||||
{
|
||||
T RegisterModel<T>(T model) where T : GFramework.Core.Abstractions.Model.IModel;
|
||||
T RegisterSystem<T>(T system) where T : GFramework.Core.Abstractions.Systems.ISystem;
|
||||
T RegisterUtility<T>(T utility) where T : GFramework.Core.Abstractions.Utility.IUtility;
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Model
|
||||
{
|
||||
public interface IModel { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Systems
|
||||
{
|
||||
public interface ISystem { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Utility
|
||||
{
|
||||
public interface IUtility { }
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Abstractions.Utility;
|
||||
|
||||
public sealed class PlayerModel : IModel { }
|
||||
public sealed class CombatSystem : ISystem { }
|
||||
public sealed class AudioUtility : IUtility { }
|
||||
}
|
||||
""";
|
||||
|
||||
private const string DeterministicOrderPartASource = """
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
// Padding ensures this attribute lives later in the file than the attributes in PartB.
|
||||
// The generator should still place it first because PartA sorts before PartB.
|
||||
// padding 01
|
||||
// padding 02
|
||||
// padding 03
|
||||
// padding 04
|
||||
// padding 05
|
||||
// padding 06
|
||||
// padding 07
|
||||
// padding 08
|
||||
// padding 09
|
||||
// padding 10
|
||||
[AutoRegisterModule]
|
||||
[RegisterUtility(typeof(AudioUtility))]
|
||||
public partial class GameplayModule
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
private const string DeterministicOrderPartBSource = """
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
[RegisterSystem(typeof(CombatSystem))]
|
||||
[RegisterModel(typeof(PlayerModel))]
|
||||
public partial class GameplayModule
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
private const string DeterministicOrderExpected = """
|
||||
// <auto-generated />
|
||||
#nullable enable
|
||||
|
||||
namespace TestApp;
|
||||
|
||||
partial class GameplayModule
|
||||
{
|
||||
public void Install(global::GFramework.Core.Abstractions.Architectures.IArchitecture architecture)
|
||||
{
|
||||
architecture.RegisterUtility(new global::TestApp.AudioUtility());
|
||||
architecture.RegisterSystem(new global::TestApp.CombatSystem());
|
||||
architecture.RegisterModel(new global::TestApp.PlayerModel());
|
||||
}
|
||||
}
|
||||
|
||||
""";
|
||||
|
||||
private const string TypeConstraintSource = """
|
||||
#nullable enable
|
||||
using System;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Architectures
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class AutoRegisterModuleAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterModelAttribute : Attribute
|
||||
{
|
||||
public RegisterModelAttribute(Type modelType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterSystemAttribute : Attribute
|
||||
{
|
||||
public RegisterSystemAttribute(Type systemType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterUtilityAttribute : Attribute
|
||||
{
|
||||
public RegisterUtilityAttribute(Type utilityType) { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Architectures
|
||||
{
|
||||
public interface IArchitecture
|
||||
{
|
||||
T RegisterModel<T>(T model) where T : GFramework.Core.Abstractions.Model.IModel;
|
||||
T RegisterSystem<T>(T system) where T : GFramework.Core.Abstractions.Systems.ISystem;
|
||||
T RegisterUtility<T>(T utility) where T : GFramework.Core.Abstractions.Utility.IUtility;
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Model
|
||||
{
|
||||
public interface IModel { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Systems
|
||||
{
|
||||
public interface ISystem { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Utility
|
||||
{
|
||||
public interface IUtility { }
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
public sealed class PlayerModel : IModel { }
|
||||
|
||||
[AutoRegisterModule]
|
||||
[RegisterModel(typeof(PlayerModel))]
|
||||
public partial class GameplayModule<TNullableRef, TNotNull, TUnmanaged>
|
||||
where TNullableRef : class?
|
||||
where TNotNull : notnull
|
||||
where TUnmanaged : unmanaged
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
private const string TypeConstraintExpected = """
|
||||
// <auto-generated />
|
||||
#nullable enable
|
||||
|
||||
namespace TestApp;
|
||||
|
||||
partial class GameplayModule<TNullableRef, TNotNull, TUnmanaged>
|
||||
where TNullableRef : class?
|
||||
where TNotNull : notnull
|
||||
where TUnmanaged : unmanaged
|
||||
{
|
||||
public void Install(global::GFramework.Core.Abstractions.Architectures.IArchitecture architecture)
|
||||
{
|
||||
architecture.RegisterModel(new global::TestApp.PlayerModel());
|
||||
}
|
||||
}
|
||||
|
||||
""";
|
||||
|
||||
/// <summary>
|
||||
/// 验证同一声明上的注册特性会按照源码中的书写顺序生成安装代码。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Generates_Module_Install_Method_In_Attribute_Order()
|
||||
public Task Generates_Module_Install_Method_In_Attribute_Order()
|
||||
{
|
||||
const string source = """
|
||||
using System;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Architectures
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class AutoRegisterModuleAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterModelAttribute : Attribute
|
||||
{
|
||||
public RegisterModelAttribute(Type modelType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterSystemAttribute : Attribute
|
||||
{
|
||||
public RegisterSystemAttribute(Type systemType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterUtilityAttribute : Attribute
|
||||
{
|
||||
public RegisterUtilityAttribute(Type utilityType) { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Architectures
|
||||
{
|
||||
public interface IArchitecture
|
||||
{
|
||||
T RegisterModel<T>(T model) where T : GFramework.Core.Abstractions.Model.IModel;
|
||||
T RegisterSystem<T>(T system) where T : GFramework.Core.Abstractions.Systems.ISystem;
|
||||
T RegisterUtility<T>(T utility) where T : GFramework.Core.Abstractions.Utility.IUtility;
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Model
|
||||
{
|
||||
public interface IModel { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Systems
|
||||
{
|
||||
public interface ISystem { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Utility
|
||||
{
|
||||
public interface IUtility { }
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Abstractions.Utility;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
public sealed class PlayerModel : IModel { }
|
||||
public sealed class CombatSystem : ISystem { }
|
||||
public sealed class AudioUtility : IUtility { }
|
||||
|
||||
[AutoRegisterModule]
|
||||
[RegisterSystem(typeof(CombatSystem))]
|
||||
[RegisterModel(typeof(PlayerModel))]
|
||||
[RegisterUtility(typeof(AudioUtility))]
|
||||
public partial class GameplayModule
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
const string expected = """
|
||||
// <auto-generated />
|
||||
#nullable enable
|
||||
|
||||
namespace TestApp;
|
||||
|
||||
partial class GameplayModule
|
||||
{
|
||||
public void Install(global::GFramework.Core.Abstractions.Architectures.IArchitecture architecture)
|
||||
{
|
||||
architecture.RegisterSystem(new global::TestApp.CombatSystem());
|
||||
architecture.RegisterModel(new global::TestApp.PlayerModel());
|
||||
architecture.RegisterUtility(new global::TestApp.AudioUtility());
|
||||
}
|
||||
}
|
||||
|
||||
""";
|
||||
|
||||
await GeneratorTest<AutoRegisterModuleGenerator>.RunAsync(
|
||||
source,
|
||||
("TestApp_GameplayModule.AutoRegisterModule.g.cs", expected));
|
||||
return GeneratorTest<AutoRegisterModuleGenerator>.RunAsync(
|
||||
AttributeOrderSource,
|
||||
("TestApp_GameplayModule.AutoRegisterModule.g.cs", AttributeOrderExpected));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -115,140 +329,20 @@ public class AutoRegisterModuleGeneratorTests
|
||||
[Test]
|
||||
public async Task Generates_Module_Install_Method_In_Deterministic_Order_Across_Partial_Declarations()
|
||||
{
|
||||
const string commonSource = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Architectures
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class AutoRegisterModuleAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterModelAttribute : Attribute
|
||||
{
|
||||
public RegisterModelAttribute(Type modelType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterSystemAttribute : Attribute
|
||||
{
|
||||
public RegisterSystemAttribute(Type systemType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterUtilityAttribute : Attribute
|
||||
{
|
||||
public RegisterUtilityAttribute(Type utilityType) { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Architectures
|
||||
{
|
||||
public interface IArchitecture
|
||||
{
|
||||
T RegisterModel<T>(T model) where T : GFramework.Core.Abstractions.Model.IModel;
|
||||
T RegisterSystem<T>(T system) where T : GFramework.Core.Abstractions.Systems.ISystem;
|
||||
T RegisterUtility<T>(T utility) where T : GFramework.Core.Abstractions.Utility.IUtility;
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Model
|
||||
{
|
||||
public interface IModel { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Systems
|
||||
{
|
||||
public interface ISystem { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Utility
|
||||
{
|
||||
public interface IUtility { }
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Abstractions.Utility;
|
||||
|
||||
public sealed class PlayerModel : IModel { }
|
||||
public sealed class CombatSystem : ISystem { }
|
||||
public sealed class AudioUtility : IUtility { }
|
||||
}
|
||||
""";
|
||||
|
||||
const string partASource = """
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
// Padding ensures this attribute lives later in the file than the attributes in PartB.
|
||||
// The generator should still place it first because PartA sorts before PartB.
|
||||
// padding 01
|
||||
// padding 02
|
||||
// padding 03
|
||||
// padding 04
|
||||
// padding 05
|
||||
// padding 06
|
||||
// padding 07
|
||||
// padding 08
|
||||
// padding 09
|
||||
// padding 10
|
||||
[AutoRegisterModule]
|
||||
[RegisterUtility(typeof(AudioUtility))]
|
||||
public partial class GameplayModule
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
const string partBSource = """
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
[RegisterSystem(typeof(CombatSystem))]
|
||||
[RegisterModel(typeof(PlayerModel))]
|
||||
public partial class GameplayModule
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
const string expected = """
|
||||
// <auto-generated />
|
||||
#nullable enable
|
||||
|
||||
namespace TestApp;
|
||||
|
||||
partial class GameplayModule
|
||||
{
|
||||
public void Install(global::GFramework.Core.Abstractions.Architectures.IArchitecture architecture)
|
||||
{
|
||||
architecture.RegisterUtility(new global::TestApp.AudioUtility());
|
||||
architecture.RegisterSystem(new global::TestApp.CombatSystem());
|
||||
architecture.RegisterModel(new global::TestApp.PlayerModel());
|
||||
}
|
||||
}
|
||||
|
||||
""";
|
||||
|
||||
var test = new CSharpSourceGeneratorTest<AutoRegisterModuleGenerator, DefaultVerifier>
|
||||
{
|
||||
TestState =
|
||||
{
|
||||
Sources =
|
||||
{
|
||||
("Common.cs", commonSource),
|
||||
("GameplayModule.PartA.cs", partASource),
|
||||
("GameplayModule.PartB.cs", partBSource)
|
||||
("Common.cs", DeterministicOrderCommonSource),
|
||||
("GameplayModule.PartA.cs", DeterministicOrderPartASource),
|
||||
("GameplayModule.PartB.cs", DeterministicOrderPartBSource)
|
||||
},
|
||||
GeneratedSources =
|
||||
{
|
||||
(typeof(AutoRegisterModuleGenerator), "TestApp_GameplayModule.AutoRegisterModule.g.cs",
|
||||
NormalizeLineEndings(expected))
|
||||
NormalizeLineEndings(DeterministicOrderExpected))
|
||||
}
|
||||
},
|
||||
DisabledDiagnostics = { "GF_Common_Trace_001" }
|
||||
@ -261,102 +355,11 @@ public class AutoRegisterModuleGeneratorTests
|
||||
/// 验证生成器会保留可空引用、notnull 与 unmanaged 约束。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Generates_Type_Constraints_For_NullableReference_NotNull_And_Unmanaged()
|
||||
public Task Generates_Type_Constraints_For_NullableReference_NotNull_And_Unmanaged()
|
||||
{
|
||||
const string source = """
|
||||
#nullable enable
|
||||
using System;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Architectures
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class AutoRegisterModuleAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterModelAttribute : Attribute
|
||||
{
|
||||
public RegisterModelAttribute(Type modelType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterSystemAttribute : Attribute
|
||||
{
|
||||
public RegisterSystemAttribute(Type systemType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class RegisterUtilityAttribute : Attribute
|
||||
{
|
||||
public RegisterUtilityAttribute(Type utilityType) { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Architectures
|
||||
{
|
||||
public interface IArchitecture
|
||||
{
|
||||
T RegisterModel<T>(T model) where T : GFramework.Core.Abstractions.Model.IModel;
|
||||
T RegisterSystem<T>(T system) where T : GFramework.Core.Abstractions.Systems.ISystem;
|
||||
T RegisterUtility<T>(T utility) where T : GFramework.Core.Abstractions.Utility.IUtility;
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Model
|
||||
{
|
||||
public interface IModel { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Systems
|
||||
{
|
||||
public interface ISystem { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Utility
|
||||
{
|
||||
public interface IUtility { }
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
public sealed class PlayerModel : IModel { }
|
||||
|
||||
[AutoRegisterModule]
|
||||
[RegisterModel(typeof(PlayerModel))]
|
||||
public partial class GameplayModule<TNullableRef, TNotNull, TUnmanaged>
|
||||
where TNullableRef : class?
|
||||
where TNotNull : notnull
|
||||
where TUnmanaged : unmanaged
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
const string expected = """
|
||||
// <auto-generated />
|
||||
#nullable enable
|
||||
|
||||
namespace TestApp;
|
||||
|
||||
partial class GameplayModule<TNullableRef, TNotNull, TUnmanaged>
|
||||
where TNullableRef : class?
|
||||
where TNotNull : notnull
|
||||
where TUnmanaged : unmanaged
|
||||
{
|
||||
public void Install(global::GFramework.Core.Abstractions.Architectures.IArchitecture architecture)
|
||||
{
|
||||
architecture.RegisterModel(new global::TestApp.PlayerModel());
|
||||
}
|
||||
}
|
||||
|
||||
""";
|
||||
|
||||
await GeneratorTest<AutoRegisterModuleGenerator>.RunAsync(
|
||||
source,
|
||||
("TestApp_GameplayModule.AutoRegisterModule.g.cs", expected));
|
||||
return GeneratorTest<AutoRegisterModuleGenerator>.RunAsync(
|
||||
TypeConstraintSource,
|
||||
("TestApp_GameplayModule.AutoRegisterModule.g.cs", TypeConstraintExpected));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
|
||||
## 当前恢复点
|
||||
|
||||
- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-031`
|
||||
- 当前阶段:`Phase 31`
|
||||
- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-032`
|
||||
- 当前阶段:`Phase 32`
|
||||
- 当前焦点:
|
||||
- 已完成 `GFramework.Core` 当前 `MA0016` / `MA0002` / `MA0015` / `MA0077` 低风险收口批次
|
||||
- 已复核 `net10.0` 下的 `MA0158` 基线:`GFramework.Core` / `GFramework.Cqrs` 当前共有 `16` 个 object lock
|
||||
@ -57,10 +57,14 @@
|
||||
场景已收敛为模板化 helper,保留原有快照目录与生成器输入语义不变
|
||||
- 当前 `GFramework.SourceGenerators.Tests` Release build 基线已从 `49` 条降到 `43` 条;`LoggerGeneratorSnapshotTests.cs`
|
||||
已不再出现在 `MA0051` 列表中
|
||||
- 已完成 `GFramework.SourceGenerators.Tests/Architectures/AutoRegisterModuleGeneratorTests.cs` 的 `MA0051` 收口:
|
||||
将内联测试源码与期望快照抽到类级常量、补齐测试类 XML 文档,并将仅作转发的异步测试改为直接返回 `Task`
|
||||
- 当前 `GFramework.SourceGenerators.Tests` Release build 基线已从 `43` 条降到 `40` 条;
|
||||
`AutoRegisterModuleGeneratorTests.cs` 已不再出现在 `MA0051` 列表中
|
||||
- `GFramework.Godot` 的 `Timing.cs` 已同步适配新事件签名,但当前 worktree 的 Godot restore 资产仍受 Windows fallback package folder 干扰,独立 build 需在修复资产后补跑
|
||||
- 后续继续按 warning 类型和数量批处理,而不是回退到按单文件切片推进
|
||||
- 下一轮默认继续拆分 `GFramework.SourceGenerators.Tests` 的 `MA0051` 热点,优先处理
|
||||
`AutoRegisterModuleGeneratorTests`、`GeneratorSnapshotTest` 或 `ContextGetGeneratorTests`
|
||||
`GeneratorSnapshotTest` 或 `ContextGetGeneratorTests`
|
||||
- 单次 `boot` 的工作树改动上限控制在约 `100` 个文件以内,避免 recovery context 与 review 面同时失控
|
||||
- 若任务边界互不冲突,允许使用不同模型的 subagent 并行处理不同 warning 类型或不同目录,但必须遵守显式 ownership
|
||||
|
||||
@ -98,6 +102,8 @@
|
||||
全部收敛为 `MA0051`
|
||||
- 已完成 `LoggerGeneratorSnapshotTests` 的单文件 `MA0051` 收口;当前 `GFramework.SourceGenerators.Tests` Release build 基线已降到
|
||||
`43` 条,并通过 focused snapshot tests 保持行为不变
|
||||
- 已完成 `AutoRegisterModuleGeneratorTests` 的单文件 `MA0051` 收口;当前 `GFramework.SourceGenerators.Tests` Release build 基线已降到
|
||||
`40` 条,并通过 focused generator tests 保持输出契约不变
|
||||
|
||||
## 当前活跃事实
|
||||
|
||||
@ -138,6 +144,8 @@
|
||||
通过 schema 类型比较 helper 与显式 `StringComparison.Ordinal` 清空当前项目的 `MA0006`
|
||||
- `RP-020` 继续拆分 `SchemaConfigGenerator.cs` 的 `MA0051` 热点,将当前项目 warnings-only 基线从 `19` 条降到 `9` 条,
|
||||
并用 focused schema generator tests 验证 50 个用例通过
|
||||
- `RP-032` 已完成 `AutoRegisterModuleGeneratorTests` 的 3 个 `MA0051` 收口:通过提取类级常量承载测试源码与快照,保持
|
||||
生成文件名、断言路径与源生成输出不变;`GFramework.SourceGenerators.Tests` warnings-only 基线由 `43` 降至 `40`
|
||||
- `RP-021` 使用 `$gframework-pr-review` 复核当前分支 PR #269 后,修复仍在本地成立的 4 个项:将
|
||||
`CqrsHandlerRegistryGenerator` 拆分为职责清晰的 partial 文件、为 `ContextAwareGenerator` 生成字段增加稳定前缀并补上
|
||||
`SetContextProvider` 的运行时 null 校验、为 `Option<T>` 补齐 `<remarks>`,并新增字段重名场景的生成器快照测试
|
||||
|
||||
@ -1,5 +1,34 @@
|
||||
# Analyzer Warning Reduction 追踪
|
||||
|
||||
## 2026-04-23 — RP-032
|
||||
|
||||
### 阶段:`AutoRegisterModuleGeneratorTests.cs` `MA0051` 收口(RP-032)
|
||||
|
||||
- 启动复核:
|
||||
- 按 `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` 热点,确认 `AutoRegisterModuleGeneratorTests.cs` 仍有 `3` 个超长方法,适合作为单文件低风险写集
|
||||
- 决策:
|
||||
- 保持 `AutoRegisterModuleGeneratorTests` 的测试输入、生成文件名、快照文本与断言结构不变,只收敛方法长度
|
||||
- 采用“提取类级常量承载大段测试源码与期望输出”的方式,避免引入新的共享 helper 或改变场景组装顺序
|
||||
- 验证阶段改为串行执行 build/test;避免和同项目并行运行时拿到不完整 `bin/Release` 输出
|
||||
- 实施调整:
|
||||
- 为 `AutoRegisterModuleGeneratorTests` 补齐测试类 XML 文档
|
||||
- 将 3 个长测试方法中的源码与期望快照提取为类级 `const string`,保留原有生成文件名与断言目标
|
||||
- 将仅转发 `GeneratorTest.RunAsync(...)` 的两个异步测试改为直接返回 `Task`
|
||||
- 验证结果:
|
||||
- `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"`
|
||||
- 结果:`40 Warning(s)`,`0 Error(s)`;`AutoRegisterModuleGeneratorTests.cs` 已不再出现在 `MA0051` 列表中
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore --disable-build-servers --filter FullyQualifiedName~AutoRegisterModuleGeneratorTests -m:1 -p:RestoreFallbackFolders="" -nologo`
|
||||
- 结果:`3 Passed`,`0 Failed`
|
||||
- 下一步建议:
|
||||
- 若继续压缩 `GFramework.SourceGenerators.Tests` 的 `MA0051`,优先处理仅剩单个超长方法的
|
||||
`GFramework.SourceGenerators.Tests/Core/GeneratorSnapshotTest.cs`
|
||||
- 若希望单次继续多降几条 warning,则改选 `ContextGetGeneratorTests.cs`,但需要接受更大的单文件写集
|
||||
|
||||
## 2026-04-23 — RP-031
|
||||
|
||||
### 阶段:`LoggerGeneratorSnapshotTests.cs` `MA0051` 收口(RP-031)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user