mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-13 22:25:37 +08:00
refactor(source-generators-tests): 收口 ContextGetGeneratorTests 的 MA0051
- 重构 ContextGetGeneratorTests 的长测试方法为场景常量与验证 helper,保持生成输入和断言语义不变 - 补充测试类与 helper 的 XML 文档,并统一生成源码与诊断断言的复用路径 - 验证 GFramework.SourceGenerators.Tests Release build 与 ContextGetGeneratorTests 过滤测试通过
This commit is contained in:
parent
18c595a72f
commit
8cd492506d
@ -3,13 +3,18 @@ using GFramework.SourceGenerators.Tests.Core;
|
|||||||
|
|
||||||
namespace GFramework.SourceGenerators.Tests.Rule;
|
namespace GFramework.SourceGenerators.Tests.Rule;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证 <see cref="ContextGetGenerator" /> 在显式特性、<c>GetAll</c> 推断与诊断场景下的生成契约。
|
||||||
|
/// </summary>
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class ContextGetGeneratorTests
|
public class ContextGetGeneratorTests
|
||||||
{
|
{
|
||||||
[Test]
|
private const string InventoryPanelGeneratedFileName = "TestApp_InventoryPanel.ContextGet.g.cs";
|
||||||
public async Task Generates_Bindings_For_ContextAwareAttribute_Class()
|
private const string BattlePanelGeneratedFileName = "TestApp_BattlePanel.ContextGet.g.cs";
|
||||||
{
|
private const string GameplayHudGeneratedFileName = "TestApp_GameplayHud.ContextGet.g.cs";
|
||||||
var source = """
|
private const string StrategyHostGeneratedFileName = "TestApp_StrategyHost.ContextGet.g.cs";
|
||||||
|
|
||||||
|
private const string ContextAwareAttributeClassSource = """
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
@ -72,7 +77,7 @@ public class ContextGetGeneratorTests
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
const string expected = """
|
private const string ContextAwareAttributeClassExpected = """
|
||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
@ -91,16 +96,7 @@ public class ContextGetGeneratorTests
|
|||||||
|
|
||||||
""";
|
""";
|
||||||
|
|
||||||
await GeneratorTest<ContextGetGenerator>.RunAsync(
|
private const string FullyQualifiedFieldAttributesSource = """
|
||||||
source,
|
|
||||||
("TestApp_InventoryPanel.ContextGet.g.cs", expected));
|
|
||||||
Assert.Pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Generates_Bindings_For_Fully_Qualified_Field_Attributes()
|
|
||||||
{
|
|
||||||
var source = """
|
|
||||||
using System;
|
using System;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
@ -154,7 +150,7 @@ public class ContextGetGeneratorTests
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
const string expected = """
|
private const string FullyQualifiedFieldAttributesExpected = """
|
||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
@ -172,16 +168,7 @@ public class ContextGetGeneratorTests
|
|||||||
|
|
||||||
""";
|
""";
|
||||||
|
|
||||||
await GeneratorTest<ContextGetGenerator>.RunAsync(
|
private const string InferredGetAllClassSource = """
|
||||||
source,
|
|
||||||
("TestApp_InventoryPanel.ContextGet.g.cs", expected));
|
|
||||||
Assert.Pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Generates_Inferred_Bindings_For_GetAll_Class()
|
|
||||||
{
|
|
||||||
var source = """
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
@ -259,7 +246,7 @@ public class ContextGetGeneratorTests
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
const string expected = """
|
private const string InferredGetAllClassExpected = """
|
||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
@ -280,16 +267,7 @@ public class ContextGetGeneratorTests
|
|||||||
|
|
||||||
""";
|
""";
|
||||||
|
|
||||||
await GeneratorTest<ContextGetGenerator>.RunAsync(
|
private const string ExplicitServiceGetAllClassSource = """
|
||||||
source,
|
|
||||||
("TestApp_BattlePanel.ContextGet.g.cs", expected));
|
|
||||||
Assert.Pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Generates_Explicit_Service_Binding_For_GetAll_Class()
|
|
||||||
{
|
|
||||||
var source = """
|
|
||||||
using System;
|
using System;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
@ -352,7 +330,7 @@ public class ContextGetGeneratorTests
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
const string expected = """
|
private const string ExplicitServiceGetAllClassExpected = """
|
||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
@ -371,16 +349,7 @@ public class ContextGetGeneratorTests
|
|||||||
|
|
||||||
""";
|
""";
|
||||||
|
|
||||||
await GeneratorTest<ContextGetGenerator>.RunAsync(
|
private const string GeneratedInjectionMethodAlreadyExistsMarkupSource = """
|
||||||
source,
|
|
||||||
("TestApp_BattlePanel.ContextGet.g.cs", expected));
|
|
||||||
Assert.Pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Reports_Diagnostic_When_Generated_Injection_Method_Name_Already_Exists()
|
|
||||||
{
|
|
||||||
var source = """
|
|
||||||
using System;
|
using System;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
@ -438,27 +407,7 @@ public class ContextGetGeneratorTests
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
var test = new CSharpSourceGeneratorTest<ContextGetGenerator, DefaultVerifier>
|
private const string IgnoreConstFieldGetAllSource = """
|
||||||
{
|
|
||||||
TestState =
|
|
||||||
{
|
|
||||||
Sources = { source }
|
|
||||||
},
|
|
||||||
DisabledDiagnostics = { "GF_Common_Trace_001" },
|
|
||||||
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck
|
|
||||||
};
|
|
||||||
|
|
||||||
test.ExpectedDiagnostics.Add(new DiagnosticResult("GF_Common_Class_002", DiagnosticSeverity.Error)
|
|
||||||
.WithLocation(0)
|
|
||||||
.WithArguments("InventoryPanel", "__InjectContextBindings_Generated"));
|
|
||||||
|
|
||||||
await test.RunAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Ignores_NonInferable_Const_Field_For_GetAll_Class_Without_Diagnostic()
|
|
||||||
{
|
|
||||||
var source = """
|
|
||||||
using System;
|
using System;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
@ -514,7 +463,7 @@ public class ContextGetGeneratorTests
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
const string expected = """
|
private const string IgnoreConstFieldGetAllExpected = """
|
||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
@ -532,16 +481,7 @@ public class ContextGetGeneratorTests
|
|||||||
|
|
||||||
""";
|
""";
|
||||||
|
|
||||||
await GeneratorTest<ContextGetGenerator>.RunAsync(
|
private const string ReadonlyInferredFieldGetAllMarkupSource = """
|
||||||
source,
|
|
||||||
("TestApp_BattlePanel.ContextGet.g.cs", expected));
|
|
||||||
Assert.Pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Warns_And_Skips_Readonly_Inferred_Field_For_GetAll_Class()
|
|
||||||
{
|
|
||||||
var source = MarkupTestSource.Parse("""
|
|
||||||
using System;
|
using System;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
@ -597,9 +537,9 @@ public class ContextGetGeneratorTests
|
|||||||
private ICombatSystem _system = null!;
|
private ICombatSystem _system = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""");
|
""";
|
||||||
|
|
||||||
const string expected = """
|
private const string SkipInvalidGetAllFieldExpected = """
|
||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
@ -617,32 +557,7 @@ public class ContextGetGeneratorTests
|
|||||||
|
|
||||||
""";
|
""";
|
||||||
|
|
||||||
var test = new CSharpSourceGeneratorTest<ContextGetGenerator, DefaultVerifier>
|
private const string StaticInferredFieldGetAllMarkupSource = """
|
||||||
{
|
|
||||||
TestState =
|
|
||||||
{
|
|
||||||
Sources = { source.Source },
|
|
||||||
GeneratedSources =
|
|
||||||
{
|
|
||||||
(typeof(ContextGetGenerator), "TestApp_BattlePanel.ContextGet.g.cs", NormalizeLineEndings(expected))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
DisabledDiagnostics = { "GF_Common_Trace_001" }
|
|
||||||
};
|
|
||||||
|
|
||||||
test.ExpectedDiagnostics.Add(source.WithSpan(
|
|
||||||
new DiagnosticResult("GF_ContextGet_008", DiagnosticSeverity.Warning),
|
|
||||||
"0")
|
|
||||||
.WithArguments("_model"));
|
|
||||||
|
|
||||||
await test.RunAsync();
|
|
||||||
Assert.Pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Warns_And_Skips_Static_Inferred_Field_For_GetAll_Class()
|
|
||||||
{
|
|
||||||
var source = MarkupTestSource.Parse("""
|
|
||||||
using System;
|
using System;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
@ -698,60 +613,9 @@ public class ContextGetGeneratorTests
|
|||||||
private ICombatSystem _system = null!;
|
private ICombatSystem _system = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""");
|
|
||||||
|
|
||||||
const string expected = """
|
|
||||||
// <auto-generated />
|
|
||||||
#nullable enable
|
|
||||||
|
|
||||||
using GFramework.Core.Extensions;
|
|
||||||
|
|
||||||
namespace TestApp;
|
|
||||||
|
|
||||||
partial class BattlePanel
|
|
||||||
{
|
|
||||||
private void __InjectContextBindings_Generated()
|
|
||||||
{
|
|
||||||
_system = this.GetSystem<global::TestApp.ICombatSystem>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
""";
|
""";
|
||||||
|
|
||||||
var test = new CSharpSourceGeneratorTest<ContextGetGenerator, DefaultVerifier>
|
private const string SkipNullableServiceLikeFieldSource = """
|
||||||
{
|
|
||||||
TestState =
|
|
||||||
{
|
|
||||||
Sources = { source.Source },
|
|
||||||
GeneratedSources =
|
|
||||||
{
|
|
||||||
(typeof(ContextGetGenerator), "TestApp_BattlePanel.ContextGet.g.cs", NormalizeLineEndings(expected))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
DisabledDiagnostics = { "GF_Common_Trace_001" }
|
|
||||||
};
|
|
||||||
|
|
||||||
test.ExpectedDiagnostics.Add(source.WithSpan(
|
|
||||||
new DiagnosticResult("GF_ContextGet_007", DiagnosticSeverity.Warning),
|
|
||||||
"0")
|
|
||||||
.WithArguments("_model"));
|
|
||||||
|
|
||||||
await test.RunAsync();
|
|
||||||
Assert.Pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizeLineEndings(string content)
|
|
||||||
{
|
|
||||||
return content
|
|
||||||
.Replace("\r\n", "\n", StringComparison.Ordinal)
|
|
||||||
.Replace("\r", "\n", StringComparison.Ordinal)
|
|
||||||
.Replace("\n", Environment.NewLine, StringComparison.Ordinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Skips_Nullable_Service_Like_Field_For_ContextAware_GetAll_Class()
|
|
||||||
{
|
|
||||||
var source = """
|
|
||||||
using System;
|
using System;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
@ -815,7 +679,7 @@ public class ContextGetGeneratorTests
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
const string expected = """
|
private const string SkipNullableServiceLikeFieldExpected = """
|
||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
@ -834,16 +698,7 @@ public class ContextGetGeneratorTests
|
|||||||
|
|
||||||
""";
|
""";
|
||||||
|
|
||||||
await GeneratorTest<ContextGetGenerator>.RunAsync(
|
private const string IContextAwareClassSource = """
|
||||||
source,
|
|
||||||
("TestApp_GameplayHud.ContextGet.g.cs", expected));
|
|
||||||
Assert.Pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Generates_Bindings_For_IContextAware_Class()
|
|
||||||
{
|
|
||||||
var source = """
|
|
||||||
using System;
|
using System;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
@ -893,7 +748,7 @@ public class ContextGetGeneratorTests
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
const string expected = """
|
private const string IContextAwareClassExpected = """
|
||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
@ -911,16 +766,7 @@ public class ContextGetGeneratorTests
|
|||||||
|
|
||||||
""";
|
""";
|
||||||
|
|
||||||
await GeneratorTest<ContextGetGenerator>.RunAsync(
|
private const string NonContextAwareClassMarkupSource = """
|
||||||
source,
|
|
||||||
("TestApp_StrategyHost.ContextGet.g.cs", expected));
|
|
||||||
Assert.Pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Reports_Diagnostic_When_Class_Is_Not_ContextAware()
|
|
||||||
{
|
|
||||||
var source = MarkupTestSource.Parse("""
|
|
||||||
using System;
|
using System;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
@ -963,30 +809,9 @@ public class ContextGetGeneratorTests
|
|||||||
private IInventoryModel {|#0:_model|} = null!;
|
private IInventoryModel {|#0:_model|} = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""");
|
""";
|
||||||
|
|
||||||
var test = new CSharpSourceGeneratorTest<ContextGetGenerator, DefaultVerifier>
|
private const string GetModelsFieldNotIReadOnlyListMarkupSource = """
|
||||||
{
|
|
||||||
TestState =
|
|
||||||
{
|
|
||||||
Sources = { source.Source }
|
|
||||||
},
|
|
||||||
DisabledDiagnostics = { "GF_Common_Trace_001" }
|
|
||||||
};
|
|
||||||
|
|
||||||
test.ExpectedDiagnostics.Add(source.WithSpan(
|
|
||||||
new DiagnosticResult("GF_ContextGet_005", DiagnosticSeverity.Error),
|
|
||||||
"0")
|
|
||||||
.WithArguments("InventoryPanel"));
|
|
||||||
|
|
||||||
await test.RunAsync();
|
|
||||||
Assert.Pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Reports_Diagnostic_When_GetModels_Field_Is_Not_IReadOnlyList()
|
|
||||||
{
|
|
||||||
var source = MarkupTestSource.Parse("""
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
@ -1035,30 +860,9 @@ public class ContextGetGeneratorTests
|
|||||||
private List<IInventoryModel> {|#0:_models|} = new();
|
private List<IInventoryModel> {|#0:_models|} = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""");
|
""";
|
||||||
|
|
||||||
var test = new CSharpSourceGeneratorTest<ContextGetGenerator, DefaultVerifier>
|
private const string ReadonlyExplicitGetModelFieldMarkupSource = """
|
||||||
{
|
|
||||||
TestState =
|
|
||||||
{
|
|
||||||
Sources = { source.Source }
|
|
||||||
},
|
|
||||||
DisabledDiagnostics = { "GF_Common_Trace_001" }
|
|
||||||
};
|
|
||||||
|
|
||||||
test.ExpectedDiagnostics.Add(source.WithSpan(
|
|
||||||
new DiagnosticResult("GF_ContextGet_004", DiagnosticSeverity.Error),
|
|
||||||
"0")
|
|
||||||
.WithArguments("_models", "System.Collections.Generic.List<TestApp.IInventoryModel>", "GetModels"));
|
|
||||||
|
|
||||||
await test.RunAsync();
|
|
||||||
Assert.Pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Reports_Diagnostic_For_Readonly_Explicit_GetModel_Field()
|
|
||||||
{
|
|
||||||
var source = MarkupTestSource.Parse("""
|
|
||||||
using System;
|
using System;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
@ -1106,30 +910,9 @@ public class ContextGetGeneratorTests
|
|||||||
private readonly IInventoryModel {|#0:_model|} = null!;
|
private readonly IInventoryModel {|#0:_model|} = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""");
|
""";
|
||||||
|
|
||||||
var test = new CSharpSourceGeneratorTest<ContextGetGenerator, DefaultVerifier>
|
private const string StaticExplicitGetModelFieldMarkupSource = """
|
||||||
{
|
|
||||||
TestState =
|
|
||||||
{
|
|
||||||
Sources = { source.Source }
|
|
||||||
},
|
|
||||||
DisabledDiagnostics = { "GF_Common_Trace_001" }
|
|
||||||
};
|
|
||||||
|
|
||||||
test.ExpectedDiagnostics.Add(source.WithSpan(
|
|
||||||
new DiagnosticResult("GF_ContextGet_003", DiagnosticSeverity.Error),
|
|
||||||
"0")
|
|
||||||
.WithArguments("_model"));
|
|
||||||
|
|
||||||
await test.RunAsync();
|
|
||||||
Assert.Pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Reports_Diagnostic_For_Static_Explicit_GetModel_Field()
|
|
||||||
{
|
|
||||||
var source = MarkupTestSource.Parse("""
|
|
||||||
using System;
|
using System;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
@ -1177,30 +960,9 @@ public class ContextGetGeneratorTests
|
|||||||
private static IInventoryModel {|#0:_model|} = null!;
|
private static IInventoryModel {|#0:_model|} = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""");
|
""";
|
||||||
|
|
||||||
var test = new CSharpSourceGeneratorTest<ContextGetGenerator, DefaultVerifier>
|
private const string GetModelsAssignableSource = """
|
||||||
{
|
|
||||||
TestState =
|
|
||||||
{
|
|
||||||
Sources = { source.Source }
|
|
||||||
},
|
|
||||||
DisabledDiagnostics = { "GF_Common_Trace_001" }
|
|
||||||
};
|
|
||||||
|
|
||||||
test.ExpectedDiagnostics.Add(source.WithSpan(
|
|
||||||
new DiagnosticResult("GF_ContextGet_002", DiagnosticSeverity.Error),
|
|
||||||
"0")
|
|
||||||
.WithArguments("_model"));
|
|
||||||
|
|
||||||
await test.RunAsync();
|
|
||||||
Assert.Pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Generates_Bindings_For_GetModels_Field_Assignable_From_IReadOnlyList()
|
|
||||||
{
|
|
||||||
var source = """
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||||
@ -1251,7 +1013,7 @@ public class ContextGetGeneratorTests
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
const string expected = """
|
private const string GetModelsAssignableExpected = """
|
||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
@ -1269,9 +1031,328 @@ public class ContextGetGeneratorTests
|
|||||||
|
|
||||||
""";
|
""";
|
||||||
|
|
||||||
await GeneratorTest<ContextGetGenerator>.RunAsync(
|
/// <summary>
|
||||||
|
/// 验证 <c>[ContextAware]</c> 类上的显式字段特性会生成模型与服务绑定。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Generates_Bindings_For_ContextAwareAttribute_Class()
|
||||||
|
{
|
||||||
|
return VerifyGeneratedSourceAsync(
|
||||||
|
ContextAwareAttributeClassSource,
|
||||||
|
InventoryPanelGeneratedFileName,
|
||||||
|
ContextAwareAttributeClassExpected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证字段使用 fully-qualified 特性名时仍能生成绑定。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Generates_Bindings_For_Fully_Qualified_Field_Attributes()
|
||||||
|
{
|
||||||
|
return VerifyGeneratedSourceAsync(
|
||||||
|
FullyQualifiedFieldAttributesSource,
|
||||||
|
InventoryPanelGeneratedFileName,
|
||||||
|
FullyQualifiedFieldAttributesExpected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证 <c>GetAll</c> 会仅为可推断的 model、models、system 与 utility 字段生成绑定。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Generates_Inferred_Bindings_For_GetAll_Class()
|
||||||
|
{
|
||||||
|
return VerifyGeneratedSourceAsync(
|
||||||
|
InferredGetAllClassSource,
|
||||||
|
BattlePanelGeneratedFileName,
|
||||||
|
InferredGetAllClassExpected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证 <c>GetAll</c> 与显式 <c>[GetService]</c> 可以组合生成绑定。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Generates_Explicit_Service_Binding_For_GetAll_Class()
|
||||||
|
{
|
||||||
|
return VerifyGeneratedSourceAsync(
|
||||||
|
ExplicitServiceGetAllClassSource,
|
||||||
|
BattlePanelGeneratedFileName,
|
||||||
|
ExplicitServiceGetAllClassExpected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证目标类已声明注入方法时会报告冲突诊断。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Reports_Diagnostic_When_Generated_Injection_Method_Name_Already_Exists()
|
||||||
|
{
|
||||||
|
var source = MarkupTestSource.Parse(GeneratedInjectionMethodAlreadyExistsMarkupSource);
|
||||||
|
|
||||||
|
return VerifyDiagnosticAsync(
|
||||||
source,
|
source,
|
||||||
("TestApp_InventoryPanel.ContextGet.g.cs", expected));
|
CreateSingleSpanDiagnostic(
|
||||||
Assert.Pass();
|
source,
|
||||||
|
"GF_Common_Class_002",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
"InventoryPanel",
|
||||||
|
"__InjectContextBindings_Generated"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证 <c>GetAll</c> 会忽略不可推断的常量字段且不报告诊断。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Ignores_NonInferable_Const_Field_For_GetAll_Class_Without_Diagnostic()
|
||||||
|
{
|
||||||
|
return VerifyGeneratedSourceAsync(
|
||||||
|
IgnoreConstFieldGetAllSource,
|
||||||
|
BattlePanelGeneratedFileName,
|
||||||
|
IgnoreConstFieldGetAllExpected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证只读推断字段会被跳过并报告 warning,同时其他字段保持生成。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Warns_And_Skips_Readonly_Inferred_Field_For_GetAll_Class()
|
||||||
|
{
|
||||||
|
var source = MarkupTestSource.Parse(ReadonlyInferredFieldGetAllMarkupSource);
|
||||||
|
|
||||||
|
return VerifyDiagnosticAndGeneratedSourceAsync(
|
||||||
|
source,
|
||||||
|
BattlePanelGeneratedFileName,
|
||||||
|
SkipInvalidGetAllFieldExpected,
|
||||||
|
CreateSingleSpanDiagnostic(
|
||||||
|
source,
|
||||||
|
"GF_ContextGet_008",
|
||||||
|
DiagnosticSeverity.Warning,
|
||||||
|
"_model"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证静态推断字段会被跳过并报告 warning,同时其他字段保持生成。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Warns_And_Skips_Static_Inferred_Field_For_GetAll_Class()
|
||||||
|
{
|
||||||
|
var source = MarkupTestSource.Parse(StaticInferredFieldGetAllMarkupSource);
|
||||||
|
|
||||||
|
return VerifyDiagnosticAndGeneratedSourceAsync(
|
||||||
|
source,
|
||||||
|
BattlePanelGeneratedFileName,
|
||||||
|
SkipInvalidGetAllFieldExpected,
|
||||||
|
CreateSingleSpanDiagnostic(
|
||||||
|
source,
|
||||||
|
"GF_ContextGet_007",
|
||||||
|
DiagnosticSeverity.Warning,
|
||||||
|
"_model"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证 nullable 的服务样字段不会被 <c>GetAll</c> 误判为可注入字段。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Skips_Nullable_Service_Like_Field_For_ContextAware_GetAll_Class()
|
||||||
|
{
|
||||||
|
return VerifyGeneratedSourceAsync(
|
||||||
|
SkipNullableServiceLikeFieldSource,
|
||||||
|
GameplayHudGeneratedFileName,
|
||||||
|
SkipNullableServiceLikeFieldExpected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证实现 <c>IContextAware</c> 的类型无需 <c>[ContextAware]</c> 也能生成显式绑定。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Generates_Bindings_For_IContextAware_Class()
|
||||||
|
{
|
||||||
|
return VerifyGeneratedSourceAsync(
|
||||||
|
IContextAwareClassSource,
|
||||||
|
StrategyHostGeneratedFileName,
|
||||||
|
IContextAwareClassExpected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证缺少上下文感知契约的类型会报告错误诊断。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Reports_Diagnostic_When_Class_Is_Not_ContextAware()
|
||||||
|
{
|
||||||
|
var source = MarkupTestSource.Parse(NonContextAwareClassMarkupSource);
|
||||||
|
|
||||||
|
return VerifyDiagnosticAsync(
|
||||||
|
source,
|
||||||
|
CreateSingleSpanDiagnostic(
|
||||||
|
source,
|
||||||
|
"GF_ContextGet_005",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
"InventoryPanel"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证 <c>[GetModels]</c> 字段若不是可赋值自 <c>IReadOnlyList<T></c> 会报告错误。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Reports_Diagnostic_When_GetModels_Field_Is_Not_IReadOnlyList()
|
||||||
|
{
|
||||||
|
var source = MarkupTestSource.Parse(GetModelsFieldNotIReadOnlyListMarkupSource);
|
||||||
|
|
||||||
|
return VerifyDiagnosticAsync(
|
||||||
|
source,
|
||||||
|
CreateSingleSpanDiagnostic(
|
||||||
|
source,
|
||||||
|
"GF_ContextGet_004",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
"_models",
|
||||||
|
"System.Collections.Generic.List<TestApp.IInventoryModel>",
|
||||||
|
"GetModels"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证显式 <c>[GetModel]</c> 作用于只读字段时会报告错误。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Reports_Diagnostic_For_Readonly_Explicit_GetModel_Field()
|
||||||
|
{
|
||||||
|
var source = MarkupTestSource.Parse(ReadonlyExplicitGetModelFieldMarkupSource);
|
||||||
|
|
||||||
|
return VerifyDiagnosticAsync(
|
||||||
|
source,
|
||||||
|
CreateSingleSpanDiagnostic(
|
||||||
|
source,
|
||||||
|
"GF_ContextGet_003",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
"_model"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证显式 <c>[GetModel]</c> 作用于静态字段时会报告错误。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Reports_Diagnostic_For_Static_Explicit_GetModel_Field()
|
||||||
|
{
|
||||||
|
var source = MarkupTestSource.Parse(StaticExplicitGetModelFieldMarkupSource);
|
||||||
|
|
||||||
|
return VerifyDiagnosticAsync(
|
||||||
|
source,
|
||||||
|
CreateSingleSpanDiagnostic(
|
||||||
|
source,
|
||||||
|
"GF_ContextGet_002",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
"_model"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证 <c>[GetModels]</c> 字段可以赋值到更宽的可枚举接口上。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public Task Generates_Bindings_For_GetModels_Field_Assignable_From_IReadOnlyList()
|
||||||
|
{
|
||||||
|
return VerifyGeneratedSourceAsync(
|
||||||
|
GetModelsAssignableSource,
|
||||||
|
InventoryPanelGeneratedFileName,
|
||||||
|
GetModelsAssignableExpected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 运行单个生成源码断言,保持文件名与文本快照语义不变。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">测试输入源码。</param>
|
||||||
|
/// <param name="generatedFileName">期望生成文件名。</param>
|
||||||
|
/// <param name="expectedGeneratedSource">期望生成源码。</param>
|
||||||
|
/// <returns>表示测试执行的异步任务。</returns>
|
||||||
|
private static Task VerifyGeneratedSourceAsync(
|
||||||
|
string source,
|
||||||
|
string generatedFileName,
|
||||||
|
string expectedGeneratedSource)
|
||||||
|
{
|
||||||
|
return GeneratorTest<ContextGetGenerator>.RunAsync(
|
||||||
|
source,
|
||||||
|
(generatedFileName, expectedGeneratedSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 运行仅关注诊断输出的生成器测试。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">包含 markup span 的测试源码。</param>
|
||||||
|
/// <param name="expectedDiagnostic">期望诊断。</param>
|
||||||
|
/// <returns>表示测试执行的异步任务。</returns>
|
||||||
|
private static Task VerifyDiagnosticAsync(
|
||||||
|
MarkupTestSource source,
|
||||||
|
DiagnosticResult expectedDiagnostic)
|
||||||
|
{
|
||||||
|
var test = CreateGeneratorTest(source.Source);
|
||||||
|
test.ExpectedDiagnostics.Add(expectedDiagnostic);
|
||||||
|
return test.RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 运行同时断言诊断与部分生成输出的生成器测试。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">包含 markup span 的测试源码。</param>
|
||||||
|
/// <param name="generatedFileName">期望生成文件名。</param>
|
||||||
|
/// <param name="expectedGeneratedSource">期望生成源码。</param>
|
||||||
|
/// <param name="expectedDiagnostic">期望诊断。</param>
|
||||||
|
/// <returns>表示测试执行的异步任务。</returns>
|
||||||
|
private static Task VerifyDiagnosticAndGeneratedSourceAsync(
|
||||||
|
MarkupTestSource source,
|
||||||
|
string generatedFileName,
|
||||||
|
string expectedGeneratedSource,
|
||||||
|
DiagnosticResult expectedDiagnostic)
|
||||||
|
{
|
||||||
|
var test = CreateGeneratorTest(source.Source);
|
||||||
|
test.TestState.GeneratedSources.Add(
|
||||||
|
(typeof(ContextGetGenerator), generatedFileName, NormalizeLineEndings(expectedGeneratedSource)));
|
||||||
|
test.ExpectedDiagnostics.Add(expectedDiagnostic);
|
||||||
|
return test.RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为单一 markup span 场景构造诊断结果,统一保持定位键与参数组装方式。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">包含 markup span 的测试源码。</param>
|
||||||
|
/// <param name="diagnosticId">诊断 ID。</param>
|
||||||
|
/// <param name="severity">诊断严重级别。</param>
|
||||||
|
/// <param name="arguments">诊断参数。</param>
|
||||||
|
/// <returns>绑定到 markup key <c>0</c> 的期望诊断。</returns>
|
||||||
|
private static DiagnosticResult CreateSingleSpanDiagnostic(
|
||||||
|
MarkupTestSource source,
|
||||||
|
string diagnosticId,
|
||||||
|
DiagnosticSeverity severity,
|
||||||
|
params string[] arguments)
|
||||||
|
{
|
||||||
|
return source.WithSpan(
|
||||||
|
new DiagnosticResult(diagnosticId, severity),
|
||||||
|
"0")
|
||||||
|
.WithArguments(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建禁用 trace 诊断的通用源生成器测试实例,避免各场景重复样板配置。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">测试输入源码。</param>
|
||||||
|
/// <returns>配置完成的生成器测试对象。</returns>
|
||||||
|
private static CSharpSourceGeneratorTest<ContextGetGenerator, DefaultVerifier> CreateGeneratorTest(string source)
|
||||||
|
{
|
||||||
|
return new CSharpSourceGeneratorTest<ContextGetGenerator, DefaultVerifier>
|
||||||
|
{
|
||||||
|
TestState =
|
||||||
|
{
|
||||||
|
Sources = { source }
|
||||||
|
},
|
||||||
|
DisabledDiagnostics = { "GF_Common_Trace_001" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将手工声明的期望生成源码归一化到当前平台换行符,避免不同宿主上的伪差异。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="content">原始期望源码。</param>
|
||||||
|
/// <returns>已按当前平台换行符归一化的源码文本。</returns>
|
||||||
|
private static string NormalizeLineEndings(string content)
|
||||||
|
{
|
||||||
|
return content
|
||||||
|
.Replace("\r\n", "\n", StringComparison.Ordinal)
|
||||||
|
.Replace("\r", "\n", StringComparison.Ordinal)
|
||||||
|
.Replace("\n", Environment.NewLine, StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user