mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-29 16:44:28 +08:00
fix(generator): 解决源代码生成器中的换行符兼容性和集合类型候选检测问题
- 在测试中添加换行符标准化功能,确保跨平台测试一致性 - 修复ContextGetGenerator中集合类型候选检测逻辑,跳过无效类型参数 - 添加针对可空服务字段的单元测试用例 - 优化生成器对不同系统换行符的处理机制
This commit is contained in:
parent
4fb1da2da6
commit
535743e824
@ -1,7 +1,4 @@
|
|||||||
using Microsoft.CodeAnalysis.CSharp.Testing;
|
namespace GFramework.Godot.SourceGenerators.Tests.Core;
|
||||||
using Microsoft.CodeAnalysis.Testing;
|
|
||||||
|
|
||||||
namespace GFramework.Godot.SourceGenerators.Tests.Core;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 提供源代码生成器测试的通用功能。
|
/// 提供源代码生成器测试的通用功能。
|
||||||
@ -30,8 +27,21 @@ public static class GeneratorTest<TGenerator>
|
|||||||
|
|
||||||
foreach (var (filename, content) in generatedSources)
|
foreach (var (filename, content) in generatedSources)
|
||||||
test.TestState.GeneratedSources.Add(
|
test.TestState.GeneratedSources.Add(
|
||||||
(typeof(TGenerator), filename, content));
|
(typeof(TGenerator), filename, NormalizeLineEndings(content)));
|
||||||
|
|
||||||
await test.RunAsync();
|
await test.RunAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,7 +1,4 @@
|
|||||||
using Microsoft.CodeAnalysis.CSharp.Testing;
|
namespace GFramework.SourceGenerators.Tests.Core;
|
||||||
using Microsoft.CodeAnalysis.Testing;
|
|
||||||
|
|
||||||
namespace GFramework.SourceGenerators.Tests.Core;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 提供源代码生成器测试的通用功能
|
/// 提供源代码生成器测试的通用功能
|
||||||
@ -32,8 +29,21 @@ public static class GeneratorTest<TGenerator>
|
|||||||
// 添加期望的生成源文件到测试状态中
|
// 添加期望的生成源文件到测试状态中
|
||||||
foreach (var (filename, content) in generatedSources)
|
foreach (var (filename, content) in generatedSources)
|
||||||
test.TestState.GeneratedSources.Add(
|
test.TestState.GeneratedSources.Add(
|
||||||
(typeof(TGenerator), filename, content));
|
(typeof(TGenerator), filename, NormalizeLineEndings(content)));
|
||||||
|
|
||||||
await test.RunAsync();
|
await test.RunAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,9 +1,5 @@
|
|||||||
using GFramework.SourceGenerators.Rule;
|
using GFramework.SourceGenerators.Rule;
|
||||||
using GFramework.SourceGenerators.Tests.Core;
|
using GFramework.SourceGenerators.Tests.Core;
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp.Testing;
|
|
||||||
using Microsoft.CodeAnalysis.Testing;
|
|
||||||
using NUnit.Framework;
|
|
||||||
|
|
||||||
namespace GFramework.SourceGenerators.Tests.Rule;
|
namespace GFramework.SourceGenerators.Tests.Rule;
|
||||||
|
|
||||||
@ -381,6 +377,98 @@ public class ContextGetGeneratorTests
|
|||||||
Assert.Pass();
|
Assert.Pass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Skips_Nullable_Service_Like_Field_For_ContextAware_GetAll_Class()
|
||||||
|
{
|
||||||
|
var source = """
|
||||||
|
using System;
|
||||||
|
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||||
|
public sealed class ContextAwareAttribute : Attribute { }
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||||
|
public sealed class GetAllAttribute : Attribute { }
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace GFramework.Core.Abstractions.Rule
|
||||||
|
{
|
||||||
|
public interface IContextAware { }
|
||||||
|
}
|
||||||
|
|
||||||
|
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 GFramework.Core.Extensions
|
||||||
|
{
|
||||||
|
public static class ContextAwareServiceExtensions
|
||||||
|
{
|
||||||
|
public static T GetModel<T>(this object contextAware) => default!;
|
||||||
|
public static T GetSystem<T>(this object contextAware) => default!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Godot
|
||||||
|
{
|
||||||
|
public class Control { }
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace TestApp
|
||||||
|
{
|
||||||
|
public interface IGridModel : GFramework.Core.Abstractions.Model.IModel { }
|
||||||
|
public interface IRunLoopSystem : GFramework.Core.Abstractions.Systems.ISystem { }
|
||||||
|
public interface IUiPageBehavior { }
|
||||||
|
|
||||||
|
[ContextAware]
|
||||||
|
[GetAll]
|
||||||
|
public partial class GameplayHud : Godot.Control
|
||||||
|
{
|
||||||
|
private IGridModel _gridModel = null!;
|
||||||
|
private IUiPageBehavior? _page;
|
||||||
|
private IRunLoopSystem _runLoopSystem = null!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
const string expected = """
|
||||||
|
// <auto-generated />
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using GFramework.Core.Extensions;
|
||||||
|
|
||||||
|
namespace TestApp;
|
||||||
|
|
||||||
|
partial class GameplayHud
|
||||||
|
{
|
||||||
|
private void __InjectContextBindings_Generated()
|
||||||
|
{
|
||||||
|
_gridModel = this.GetModel<global::TestApp.IGridModel>();
|
||||||
|
_runLoopSystem = this.GetSystem<global::TestApp.IRunLoopSystem>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
await GeneratorTest<ContextGetGenerator>.RunAsync(
|
||||||
|
source,
|
||||||
|
("TestApp_GameplayHud.ContextGet.g.cs", expected));
|
||||||
|
Assert.Pass();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Generates_Bindings_For_IContextAware_Class()
|
public async Task Generates_Bindings_For_IContextAware_Class()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,13 +1,7 @@
|
|||||||
using System.Collections.Immutable;
|
|
||||||
using System.Text;
|
|
||||||
using GFramework.SourceGenerators.Common.Constants;
|
using GFramework.SourceGenerators.Common.Constants;
|
||||||
using GFramework.SourceGenerators.Common.Diagnostics;
|
using GFramework.SourceGenerators.Common.Diagnostics;
|
||||||
using GFramework.SourceGenerators.Common.Extensions;
|
|
||||||
using GFramework.SourceGenerators.Common.Info;
|
using GFramework.SourceGenerators.Common.Info;
|
||||||
using GFramework.SourceGenerators.Diagnostics;
|
using GFramework.SourceGenerators.Diagnostics;
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
||||||
|
|
||||||
namespace GFramework.SourceGenerators.Rule;
|
namespace GFramework.SourceGenerators.Rule;
|
||||||
|
|
||||||
@ -711,11 +705,12 @@ public sealed class ContextGetGenerator : IIncrementalGenerator
|
|||||||
if (readOnlyList is null || fieldType is not INamedTypeSymbol targetType)
|
if (readOnlyList is null || fieldType is not INamedTypeSymbol targetType)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var allTypeCandidates = EnumerateCollectionTypeCandidates(targetType)
|
foreach (var candidateType in EnumerateCollectionTypeCandidates(targetType))
|
||||||
.SelectMany(candidateType => candidateType.TypeArguments);
|
|
||||||
|
|
||||||
foreach (var candidateElementType in allTypeCandidates)
|
|
||||||
{
|
{
|
||||||
|
if (candidateType.TypeArguments.Length != 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var candidateElementType = candidateType.TypeArguments[0];
|
||||||
var expectedSourceType = readOnlyList.Construct(candidateElementType);
|
var expectedSourceType = readOnlyList.Construct(candidateElementType);
|
||||||
if (!expectedSourceType.IsAssignableTo(targetType))
|
if (!expectedSourceType.IsAssignableTo(targetType))
|
||||||
continue;
|
continue;
|
||||||
@ -724,7 +719,6 @@ public sealed class ContextGetGenerator : IIncrementalGenerator
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user