mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-13 06:04:30 +08:00
refactor(source-generators-tests): 拆分 ContextRegistrationAnalyzerTests 结构
- 重构 ContextRegistrationAnalyzerTests 的场景源码常量,保持 analyzer 输入与 markup span 不变 - 补充测试方法与辅助 helper 文档,并统一诊断断言路径以收口 MA0051
This commit is contained in:
parent
cebdbdbe9b
commit
18c595a72f
@ -132,462 +132,545 @@ public sealed class ContextRegistrationAnalyzerTests
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
|
// Keep scenario fixtures at class scope so MA0051 reduction does not change analyzer inputs or markup spans.
|
||||||
|
private const string MissingFieldInjectedModelRegistrationSource = """
|
||||||
|
namespace TestApp
|
||||||
|
{
|
||||||
|
using GFramework.Core.Abstractions.Model;
|
||||||
|
using GFramework.Core.Abstractions.Systems;
|
||||||
|
using GFramework.Core.Architectures;
|
||||||
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
public interface IInventoryModel : IModel { }
|
||||||
|
|
||||||
|
public sealed class InventoryPanelSystem : ISystem
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IInventoryModel {|#0:_model|} = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void OnInitialize()
|
||||||
|
{
|
||||||
|
RegisterSystem(new InventoryPanelSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
private const string RegisteredFieldInjectedModelSource = """
|
||||||
|
namespace TestApp
|
||||||
|
{
|
||||||
|
using GFramework.Core.Abstractions.Model;
|
||||||
|
using GFramework.Core.Abstractions.Systems;
|
||||||
|
using GFramework.Core.Architectures;
|
||||||
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
public interface IInventoryModel : IModel { }
|
||||||
|
|
||||||
|
public sealed class InventoryModel : IInventoryModel { }
|
||||||
|
|
||||||
|
public sealed class InventoryPanelSystem : ISystem
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IInventoryModel _model = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void OnInitialize()
|
||||||
|
{
|
||||||
|
RegisterModel(new InventoryModel());
|
||||||
|
RegisterSystem(new InventoryPanelSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
private const string MissingHandWrittenGetSystemRegistrationSource = """
|
||||||
|
namespace TestApp
|
||||||
|
{
|
||||||
|
using GFramework.Core.Abstractions.Systems;
|
||||||
|
using GFramework.Core.Abstractions.Utility;
|
||||||
|
using GFramework.Core.Architectures;
|
||||||
|
using GFramework.Core.Extensions;
|
||||||
|
|
||||||
|
public interface ICombatSystem : ISystem { }
|
||||||
|
|
||||||
|
public sealed class UiUtility : IUtility
|
||||||
|
{
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
{|#0:this.GetSystem<ICombatSystem>()|};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void OnInitialize()
|
||||||
|
{
|
||||||
|
RegisterUtility(new UiUtility());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
private const string ModuleProvidedModelRegistrationSource = """
|
||||||
|
namespace TestApp
|
||||||
|
{
|
||||||
|
using GFramework.Core.Abstractions.Architectures;
|
||||||
|
using GFramework.Core.Abstractions.Model;
|
||||||
|
using GFramework.Core.Abstractions.Systems;
|
||||||
|
using GFramework.Core.Architectures;
|
||||||
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
public interface IInventoryModel : IModel { }
|
||||||
|
|
||||||
|
public sealed class InventoryModel : IInventoryModel { }
|
||||||
|
|
||||||
|
public sealed class InventoryPanelSystem : ISystem
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IInventoryModel _model = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class InventoryModule : IArchitectureModule
|
||||||
|
{
|
||||||
|
public void Install(IArchitecture architecture)
|
||||||
|
{
|
||||||
|
architecture.RegisterModel(new InventoryModel());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void OnInitialize()
|
||||||
|
{
|
||||||
|
InstallModule(new InventoryModule());
|
||||||
|
RegisterSystem(new InventoryPanelSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
private const string AmbiguousOwningArchitectureSource = """
|
||||||
|
namespace TestApp
|
||||||
|
{
|
||||||
|
using GFramework.Core.Abstractions.Model;
|
||||||
|
using GFramework.Core.Abstractions.Systems;
|
||||||
|
using GFramework.Core.Architectures;
|
||||||
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
public interface IInventoryModel : IModel { }
|
||||||
|
|
||||||
|
public sealed class InventoryPanelSystem : ISystem
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IInventoryModel _model = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class FirstArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void OnInitialize()
|
||||||
|
{
|
||||||
|
RegisterSystem(new InventoryPanelSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class SecondArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void OnInitialize()
|
||||||
|
{
|
||||||
|
RegisterSystem(new InventoryPanelSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
private const string MissingGetUtilitiesRegistrationSource = """
|
||||||
|
namespace TestApp
|
||||||
|
{
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using GFramework.Core.Abstractions.Systems;
|
||||||
|
using GFramework.Core.Abstractions.Utility;
|
||||||
|
using GFramework.Core.Architectures;
|
||||||
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
public interface IInventoryUtility : IUtility { }
|
||||||
|
|
||||||
|
public sealed class InventoryPanelSystem : ISystem
|
||||||
|
{
|
||||||
|
[GetUtilities]
|
||||||
|
private IReadOnlyList<IInventoryUtility> {|#0:_utilities|} = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void OnInitialize()
|
||||||
|
{
|
||||||
|
RegisterSystem(new InventoryPanelSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
private const string DerivedArchitectureVirtualHelperRegistrationSource = """
|
||||||
|
namespace TestApp
|
||||||
|
{
|
||||||
|
using GFramework.Core.Abstractions.Model;
|
||||||
|
using GFramework.Core.Abstractions.Systems;
|
||||||
|
using GFramework.Core.Architectures;
|
||||||
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
public interface IInventoryModel : IModel { }
|
||||||
|
|
||||||
|
public sealed class InventoryModel : IInventoryModel { }
|
||||||
|
|
||||||
|
public abstract class ArchitectureBase : Architecture
|
||||||
|
{
|
||||||
|
protected override void OnInitialize()
|
||||||
|
{
|
||||||
|
RegisterComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void RegisterComponents()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class InventoryPanelSystem : ISystem
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IInventoryModel _model = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class GameArchitecture : ArchitectureBase
|
||||||
|
{
|
||||||
|
protected override void RegisterComponents()
|
||||||
|
{
|
||||||
|
RegisterModel(new InventoryModel());
|
||||||
|
RegisterSystem(new InventoryPanelSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
private const string DerivedModuleVirtualHelperRegistrationSource = """
|
||||||
|
namespace TestApp
|
||||||
|
{
|
||||||
|
using GFramework.Core.Abstractions.Architectures;
|
||||||
|
using GFramework.Core.Abstractions.Model;
|
||||||
|
using GFramework.Core.Abstractions.Systems;
|
||||||
|
using GFramework.Core.Architectures;
|
||||||
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
public interface IInventoryModel : IModel { }
|
||||||
|
|
||||||
|
public sealed class InventoryModel : IInventoryModel { }
|
||||||
|
|
||||||
|
public abstract class ModuleBase : IArchitectureModule
|
||||||
|
{
|
||||||
|
public void Install(IArchitecture architecture)
|
||||||
|
{
|
||||||
|
RegisterComponents(architecture);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void RegisterComponents(IArchitecture architecture)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class DerivedInventoryModule : ModuleBase
|
||||||
|
{
|
||||||
|
protected override void RegisterComponents(IArchitecture architecture)
|
||||||
|
{
|
||||||
|
architecture.RegisterModel(new InventoryModel());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class InventoryPanelSystem : ISystem
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IInventoryModel _model = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void OnInitialize()
|
||||||
|
{
|
||||||
|
InstallModule(new DerivedInventoryModule());
|
||||||
|
RegisterSystem(new InventoryPanelSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
private const string DerivedArchitectureBaseHelperCallSource = """
|
||||||
|
namespace TestApp
|
||||||
|
{
|
||||||
|
using GFramework.Core.Abstractions.Model;
|
||||||
|
using GFramework.Core.Abstractions.Systems;
|
||||||
|
using GFramework.Core.Architectures;
|
||||||
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
public interface IInventoryModel : IModel { }
|
||||||
|
|
||||||
|
public sealed class InventoryModel : IInventoryModel { }
|
||||||
|
|
||||||
|
public sealed class InventoryPanelSystem : ISystem
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IInventoryModel {|#0:_model|} = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class ArchitectureBase : Architecture
|
||||||
|
{
|
||||||
|
protected virtual void RegisterComponents()
|
||||||
|
{
|
||||||
|
RegisterSystem(new InventoryPanelSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class GameArchitecture : ArchitectureBase
|
||||||
|
{
|
||||||
|
protected override void OnInitialize()
|
||||||
|
{
|
||||||
|
base.RegisterComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void RegisterComponents()
|
||||||
|
{
|
||||||
|
RegisterModel(new InventoryModel());
|
||||||
|
RegisterSystem(new InventoryPanelSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
private const string DerivedModuleBaseHelperCallSource = """
|
||||||
|
namespace TestApp
|
||||||
|
{
|
||||||
|
using GFramework.Core.Abstractions.Architectures;
|
||||||
|
using GFramework.Core.Abstractions.Model;
|
||||||
|
using GFramework.Core.Abstractions.Systems;
|
||||||
|
using GFramework.Core.Architectures;
|
||||||
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
public interface IInventoryModel : IModel { }
|
||||||
|
|
||||||
|
public sealed class InventoryModel : IInventoryModel { }
|
||||||
|
|
||||||
|
public sealed class InventoryPanelSystem : ISystem
|
||||||
|
{
|
||||||
|
[GetModel]
|
||||||
|
private IInventoryModel {|#0:_model|} = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class ModuleBase : IArchitectureModule
|
||||||
|
{
|
||||||
|
public virtual void Install(IArchitecture architecture)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void RegisterComponents(IArchitecture architecture)
|
||||||
|
{
|
||||||
|
architecture.RegisterSystem(new InventoryPanelSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class DerivedInventoryModule : ModuleBase
|
||||||
|
{
|
||||||
|
public override void Install(IArchitecture architecture)
|
||||||
|
{
|
||||||
|
base.RegisterComponents(architecture);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void RegisterComponents(IArchitecture architecture)
|
||||||
|
{
|
||||||
|
architecture.RegisterModel(new InventoryModel());
|
||||||
|
architecture.RegisterSystem(new InventoryPanelSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class GameArchitecture : Architecture
|
||||||
|
{
|
||||||
|
protected override void OnInitialize()
|
||||||
|
{
|
||||||
|
InstallModule(new DerivedInventoryModule());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证字段注入模型未注册时会报告缺失注册告警。
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Reports_Warning_When_FieldInjectedModel_Is_Not_Registered()
|
public Task Reports_Warning_When_FieldInjectedModel_Is_Not_Registered()
|
||||||
{
|
{
|
||||||
var markup = MarkupTestSource.Parse(
|
return RunWarningScenarioAsync(
|
||||||
Wrap("""
|
MissingFieldInjectedModelRegistrationSource,
|
||||||
namespace TestApp
|
CreateContextRegistrationWarning(
|
||||||
{
|
"GF_ContextRegistration_001",
|
||||||
using GFramework.Core.Abstractions.Model;
|
"IInventoryModel",
|
||||||
using GFramework.Core.Abstractions.Systems;
|
"InventoryPanelSystem",
|
||||||
using GFramework.Core.Architectures;
|
"GameArchitecture"));
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
|
||||||
|
|
||||||
public interface IInventoryModel : IModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryPanelSystem : ISystem
|
|
||||||
{
|
|
||||||
[GetModel]
|
|
||||||
private IInventoryModel {|#0:_model|} = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class GameArchitecture : Architecture
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""));
|
|
||||||
|
|
||||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
|
||||||
markup.Source,
|
|
||||||
markup.WithSpan(
|
|
||||||
new DiagnosticResult("GF_ContextRegistration_001", DiagnosticSeverity.Warning)
|
|
||||||
.WithArguments("IInventoryModel", "InventoryPanelSystem", "GameArchitecture"),
|
|
||||||
"0"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证字段注入模型已注册时不会产生误报。
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Does_Not_Report_When_FieldInjectedModel_Is_Registered()
|
public Task Does_Not_Report_When_FieldInjectedModel_Is_Registered()
|
||||||
{
|
{
|
||||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
return RunNoDiagnosticScenarioAsync(RegisteredFieldInjectedModelSource);
|
||||||
Wrap("""
|
|
||||||
namespace TestApp
|
|
||||||
{
|
|
||||||
using GFramework.Core.Abstractions.Model;
|
|
||||||
using GFramework.Core.Abstractions.Systems;
|
|
||||||
using GFramework.Core.Architectures;
|
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
|
||||||
|
|
||||||
public interface IInventoryModel : IModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryModel : IInventoryModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryPanelSystem : ISystem
|
|
||||||
{
|
|
||||||
[GetModel]
|
|
||||||
private IInventoryModel _model = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class GameArchitecture : Architecture
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
RegisterModel(new InventoryModel());
|
|
||||||
RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证手写扩展方法访问未注册 System 时会报告缺失注册告警。
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Reports_Warning_When_HandWrittenGetSystem_Call_Has_No_Registration()
|
public Task Reports_Warning_When_HandWrittenGetSystem_Call_Has_No_Registration()
|
||||||
{
|
{
|
||||||
var markup = MarkupTestSource.Parse(
|
return RunWarningScenarioAsync(
|
||||||
Wrap("""
|
MissingHandWrittenGetSystemRegistrationSource,
|
||||||
namespace TestApp
|
CreateContextRegistrationWarning(
|
||||||
{
|
"GF_ContextRegistration_002",
|
||||||
using GFramework.Core.Abstractions.Systems;
|
"ICombatSystem",
|
||||||
using GFramework.Core.Abstractions.Utility;
|
"UiUtility",
|
||||||
using GFramework.Core.Architectures;
|
"GameArchitecture"));
|
||||||
using GFramework.Core.Extensions;
|
|
||||||
|
|
||||||
public interface ICombatSystem : ISystem { }
|
|
||||||
|
|
||||||
public sealed class UiUtility : IUtility
|
|
||||||
{
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
{|#0:this.GetSystem<ICombatSystem>()|};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class GameArchitecture : Architecture
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
RegisterUtility(new UiUtility());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""));
|
|
||||||
|
|
||||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
|
||||||
markup.Source,
|
|
||||||
markup.WithSpan(
|
|
||||||
new DiagnosticResult("GF_ContextRegistration_002", DiagnosticSeverity.Warning)
|
|
||||||
.WithArguments("ICombatSystem", "UiUtility", "GameArchitecture"),
|
|
||||||
"0"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证模块安装链路提供注册时分析器会把该注册视为有效来源。
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Does_Not_Report_When_Registration_Comes_From_Installed_Module()
|
public Task Does_Not_Report_When_Registration_Comes_From_Installed_Module()
|
||||||
{
|
{
|
||||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
return RunNoDiagnosticScenarioAsync(ModuleProvidedModelRegistrationSource);
|
||||||
Wrap("""
|
|
||||||
namespace TestApp
|
|
||||||
{
|
|
||||||
using GFramework.Core.Abstractions.Architectures;
|
|
||||||
using GFramework.Core.Abstractions.Model;
|
|
||||||
using GFramework.Core.Abstractions.Systems;
|
|
||||||
using GFramework.Core.Architectures;
|
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
|
||||||
|
|
||||||
public interface IInventoryModel : IModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryModel : IInventoryModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryPanelSystem : ISystem
|
|
||||||
{
|
|
||||||
[GetModel]
|
|
||||||
private IInventoryModel _model = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class InventoryModule : IArchitectureModule
|
|
||||||
{
|
|
||||||
public void Install(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
architecture.RegisterModel(new InventoryModel());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class GameArchitecture : Architecture
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
InstallModule(new InventoryModule());
|
|
||||||
RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证无法唯一推导所属 Architecture 时分析器保持静默以避免误报。
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Does_Not_Report_When_Owning_Architecture_Cannot_Be_Uniquely_Determined()
|
public Task Does_Not_Report_When_Owning_Architecture_Cannot_Be_Uniquely_Determined()
|
||||||
{
|
{
|
||||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
return RunNoDiagnosticScenarioAsync(AmbiguousOwningArchitectureSource);
|
||||||
Wrap("""
|
|
||||||
namespace TestApp
|
|
||||||
{
|
|
||||||
using GFramework.Core.Abstractions.Model;
|
|
||||||
using GFramework.Core.Abstractions.Systems;
|
|
||||||
using GFramework.Core.Architectures;
|
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
|
||||||
|
|
||||||
public interface IInventoryModel : IModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryPanelSystem : ISystem
|
|
||||||
{
|
|
||||||
[GetModel]
|
|
||||||
private IInventoryModel _model = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class FirstArchitecture : Architecture
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class SecondArchitecture : Architecture
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证集合注入 Utility 缺失注册时仍会报告对应告警。
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Reports_Warning_When_GetUtilities_Field_Has_No_Registered_Utility()
|
public Task Reports_Warning_When_GetUtilities_Field_Has_No_Registered_Utility()
|
||||||
{
|
{
|
||||||
var markup = MarkupTestSource.Parse(
|
return RunWarningScenarioAsync(
|
||||||
Wrap("""
|
MissingGetUtilitiesRegistrationSource,
|
||||||
namespace TestApp
|
CreateContextRegistrationWarning(
|
||||||
{
|
"GF_ContextRegistration_003",
|
||||||
using System.Collections.Generic;
|
"IInventoryUtility",
|
||||||
using GFramework.Core.Abstractions.Systems;
|
"InventoryPanelSystem",
|
||||||
using GFramework.Core.Abstractions.Utility;
|
"GameArchitecture"));
|
||||||
using GFramework.Core.Architectures;
|
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
|
||||||
|
|
||||||
public interface IInventoryUtility : IUtility { }
|
|
||||||
|
|
||||||
public sealed class InventoryPanelSystem : ISystem
|
|
||||||
{
|
|
||||||
[GetUtilities]
|
|
||||||
private IReadOnlyList<IInventoryUtility> {|#0:_utilities|} = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class GameArchitecture : Architecture
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""));
|
|
||||||
|
|
||||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
|
||||||
markup.Source,
|
|
||||||
markup.WithSpan(
|
|
||||||
new DiagnosticResult("GF_ContextRegistration_003", DiagnosticSeverity.Warning)
|
|
||||||
.WithArguments("IInventoryUtility", "InventoryPanelSystem", "GameArchitecture"),
|
|
||||||
"0"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证基类初始化经由虚方法分派到派生实现时,派生注册仍会被识别。
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public async Task
|
public Task
|
||||||
Does_Not_Report_When_Inherited_OnInitialize_Calls_Virtual_Helper_Overridden_In_Derived_Architecture()
|
Does_Not_Report_When_Inherited_OnInitialize_Calls_Virtual_Helper_Overridden_In_Derived_Architecture()
|
||||||
{
|
{
|
||||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
return RunNoDiagnosticScenarioAsync(DerivedArchitectureVirtualHelperRegistrationSource);
|
||||||
Wrap("""
|
|
||||||
namespace TestApp
|
|
||||||
{
|
|
||||||
using GFramework.Core.Abstractions.Model;
|
|
||||||
using GFramework.Core.Abstractions.Systems;
|
|
||||||
using GFramework.Core.Architectures;
|
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
|
||||||
|
|
||||||
public interface IInventoryModel : IModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryModel : IInventoryModel { }
|
|
||||||
|
|
||||||
public abstract class ArchitectureBase : Architecture
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
RegisterComponents();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void RegisterComponents()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class InventoryPanelSystem : ISystem
|
|
||||||
{
|
|
||||||
[GetModel]
|
|
||||||
private IInventoryModel _model = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class GameArchitecture : ArchitectureBase
|
|
||||||
{
|
|
||||||
protected override void RegisterComponents()
|
|
||||||
{
|
|
||||||
RegisterModel(new InventoryModel());
|
|
||||||
RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证模块基类通过虚方法转发注册时,派生模块的注册依然会被识别。
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Does_Not_Report_When_Inherited_Module_Install_Calls_Virtual_Helper_Overridden_In_Derived_Module()
|
public Task Does_Not_Report_When_Inherited_Module_Install_Calls_Virtual_Helper_Overridden_In_Derived_Module()
|
||||||
{
|
{
|
||||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
return RunNoDiagnosticScenarioAsync(DerivedModuleVirtualHelperRegistrationSource);
|
||||||
Wrap("""
|
|
||||||
namespace TestApp
|
|
||||||
{
|
|
||||||
using GFramework.Core.Abstractions.Architectures;
|
|
||||||
using GFramework.Core.Abstractions.Model;
|
|
||||||
using GFramework.Core.Abstractions.Systems;
|
|
||||||
using GFramework.Core.Architectures;
|
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
|
||||||
|
|
||||||
public interface IInventoryModel : IModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryModel : IInventoryModel { }
|
|
||||||
|
|
||||||
public abstract class ModuleBase : IArchitectureModule
|
|
||||||
{
|
|
||||||
public void Install(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
RegisterComponents(architecture);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void RegisterComponents(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class DerivedInventoryModule : ModuleBase
|
|
||||||
{
|
|
||||||
protected override void RegisterComponents(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
architecture.RegisterModel(new InventoryModel());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class InventoryPanelSystem : ISystem
|
|
||||||
{
|
|
||||||
[GetModel]
|
|
||||||
private IInventoryModel _model = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class GameArchitecture : Architecture
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
InstallModule(new DerivedInventoryModule());
|
|
||||||
RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证显式调用基类 helper 时,分析器按基类实际执行的注册路径发出告警。
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Reports_Warning_When_Derived_Architecture_Explicitly_Calls_Base_Helper()
|
public Task Reports_Warning_When_Derived_Architecture_Explicitly_Calls_Base_Helper()
|
||||||
{
|
{
|
||||||
var markup = MarkupTestSource.Parse(
|
return RunWarningScenarioAsync(
|
||||||
Wrap("""
|
DerivedArchitectureBaseHelperCallSource,
|
||||||
namespace TestApp
|
CreateContextRegistrationWarning(
|
||||||
{
|
"GF_ContextRegistration_001",
|
||||||
using GFramework.Core.Abstractions.Model;
|
"IInventoryModel",
|
||||||
using GFramework.Core.Abstractions.Systems;
|
"InventoryPanelSystem",
|
||||||
using GFramework.Core.Architectures;
|
"GameArchitecture"));
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
}
|
||||||
|
|
||||||
public interface IInventoryModel : IModel { }
|
/// <summary>
|
||||||
|
/// 验证模块显式调用基类 helper 时,分析器按实际执行的安装路径发出告警。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Reports_Warning_When_Derived_Module_Explicitly_Calls_Base_Helper()
|
||||||
|
{
|
||||||
|
return RunWarningScenarioAsync(
|
||||||
|
DerivedModuleBaseHelperCallSource,
|
||||||
|
CreateContextRegistrationWarning(
|
||||||
|
"GF_ContextRegistration_001",
|
||||||
|
"IInventoryModel",
|
||||||
|
"InventoryPanelSystem",
|
||||||
|
"GameArchitecture"));
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class InventoryModel : IInventoryModel { }
|
/// <summary>
|
||||||
|
/// 运行包含诊断标记的 analyzer 场景,并把预期诊断绑定到统一的 `#0` span。
|
||||||
public sealed class InventoryPanelSystem : ISystem
|
/// </summary>
|
||||||
{
|
/// <param name="source">不含公共前导代码的测试源码。</param>
|
||||||
[GetModel]
|
/// <param name="expectedDiagnostic">需要命中的预期诊断。</param>
|
||||||
private IInventoryModel {|#0:_model|} = null!;
|
/// <returns>代表 analyzer 验证流程的异步任务。</returns>
|
||||||
}
|
private static Task RunWarningScenarioAsync(string source, DiagnosticResult expectedDiagnostic)
|
||||||
|
{
|
||||||
public abstract class ArchitectureBase : Architecture
|
MarkupTestSource markup = MarkupTestSource.Parse(Wrap(source));
|
||||||
{
|
return AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
||||||
protected virtual void RegisterComponents()
|
|
||||||
{
|
|
||||||
RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class GameArchitecture : ArchitectureBase
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
base.RegisterComponents();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void RegisterComponents()
|
|
||||||
{
|
|
||||||
RegisterModel(new InventoryModel());
|
|
||||||
RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""));
|
|
||||||
|
|
||||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
|
||||||
markup.Source,
|
markup.Source,
|
||||||
markup.WithSpan(
|
markup.WithSpan(expectedDiagnostic, "0"));
|
||||||
new DiagnosticResult("GF_ContextRegistration_001", DiagnosticSeverity.Warning)
|
|
||||||
.WithArguments("IInventoryModel", "InventoryPanelSystem", "GameArchitecture"),
|
|
||||||
"0"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
/// <summary>
|
||||||
public async Task Reports_Warning_When_Derived_Module_Explicitly_Calls_Base_Helper()
|
/// 运行不应产生诊断的 analyzer 场景。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">不含公共前导代码的测试源码。</param>
|
||||||
|
/// <returns>代表 analyzer 验证流程的异步任务。</returns>
|
||||||
|
private static Task RunNoDiagnosticScenarioAsync(string source)
|
||||||
{
|
{
|
||||||
var markup = MarkupTestSource.Parse(
|
return AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(Wrap(source));
|
||||||
Wrap("""
|
|
||||||
namespace TestApp
|
|
||||||
{
|
|
||||||
using GFramework.Core.Abstractions.Architectures;
|
|
||||||
using GFramework.Core.Abstractions.Model;
|
|
||||||
using GFramework.Core.Abstractions.Systems;
|
|
||||||
using GFramework.Core.Architectures;
|
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
|
||||||
|
|
||||||
public interface IInventoryModel : IModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryModel : IInventoryModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryPanelSystem : ISystem
|
|
||||||
{
|
|
||||||
[GetModel]
|
|
||||||
private IInventoryModel {|#0:_model|} = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class ModuleBase : IArchitectureModule
|
|
||||||
{
|
|
||||||
public virtual void Install(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void RegisterComponents(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
architecture.RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class DerivedInventoryModule : ModuleBase
|
|
||||||
{
|
|
||||||
public override void Install(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
base.RegisterComponents(architecture);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void RegisterComponents(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
architecture.RegisterModel(new InventoryModel());
|
|
||||||
architecture.RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class GameArchitecture : Architecture
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
InstallModule(new DerivedInventoryModule());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""));
|
|
||||||
|
|
||||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
|
||||||
markup.Source,
|
|
||||||
markup.WithSpan(
|
|
||||||
new DiagnosticResult("GF_ContextRegistration_001", DiagnosticSeverity.Warning)
|
|
||||||
.WithArguments("IInventoryModel", "InventoryPanelSystem", "GameArchitecture"),
|
|
||||||
"0"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造 Context 注册分析器的统一预期诊断,以保持断言参数顺序稳定。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="diagnosticId">预期诊断 ID。</param>
|
||||||
|
/// <param name="serviceType">缺失注册的服务或依赖类型。</param>
|
||||||
|
/// <param name="ownerType">触发访问的拥有者类型。</param>
|
||||||
|
/// <param name="architectureType">推导出的所属 Architecture 类型。</param>
|
||||||
|
/// <returns>配置好参数的预期诊断结果。</returns>
|
||||||
|
private static DiagnosticResult CreateContextRegistrationWarning(
|
||||||
|
string diagnosticId,
|
||||||
|
string serviceType,
|
||||||
|
string ownerType,
|
||||||
|
string architectureType)
|
||||||
|
{
|
||||||
|
return new DiagnosticResult(diagnosticId, DiagnosticSeverity.Warning)
|
||||||
|
.WithArguments(serviceType, ownerType, architectureType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将公共测试前导代码与具体场景源码拼接为完整编译单元。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">具体测试场景源码。</param>
|
||||||
|
/// <returns>包含公共前导代码的完整源码文本。</returns>
|
||||||
private static string Wrap(string source)
|
private static string Wrap(string source)
|
||||||
{
|
{
|
||||||
return $"{TestPreamble}{Environment.NewLine}{Environment.NewLine}{source}";
|
return $"{TestPreamble}{Environment.NewLine}{Environment.NewLine}{source}";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user