using GFramework.SourceGenerators.Rule; 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; [TestFixture] public class ContextGetGeneratorTests { [Test] public async Task Generates_Bindings_For_ContextAwareAttribute_Class() { var source = """ using System; using System.Collections.Generic; using GFramework.SourceGenerators.Abstractions.Rule; namespace GFramework.SourceGenerators.Abstractions.Rule { [AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class ContextAwareAttribute : Attribute { } [AttributeUsage(AttributeTargets.Field, Inherited = false)] public sealed class GetModelAttribute : Attribute { } [AttributeUsage(AttributeTargets.Field, Inherited = false)] public sealed class GetServicesAttribute : 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(this object contextAware) => default!; public static IReadOnlyList GetServices(this object contextAware) => default!; } } namespace TestApp { public interface IInventoryModel : GFramework.Core.Abstractions.Model.IModel { } public interface IInventoryStrategy { } [ContextAware] public partial class InventoryPanel { [GetModel] private IInventoryModel _model = null!; [GetServices] private IReadOnlyList _strategies = null!; } } """; const string expected = """ // #nullable enable using GFramework.Core.Extensions; namespace TestApp; partial class InventoryPanel { private void __InjectContextBindings_Generated() { _model = this.GetModel(); _strategies = this.GetServices(); } } """; await GeneratorTest.RunAsync( 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.Collections.Generic; using GFramework.SourceGenerators.Abstractions.Rule; namespace GFramework.SourceGenerators.Abstractions.Rule { [AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class GetAllAttribute : Attribute { } } namespace GFramework.Core.Abstractions.Rule { public interface IContextAware { } } namespace GFramework.Core.Abstractions.Architectures { public interface IArchitectureContext { } } namespace GFramework.Core.Rule { public abstract class ContextAwareBase : GFramework.Core.Abstractions.Rule.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(this object contextAware) => default!; public static IReadOnlyList GetModels(this object contextAware) => default!; public static T GetSystem(this object contextAware) => default!; public static T GetUtility(this object contextAware) => default!; public static T GetService(this object contextAware) => default!; } } namespace Godot { public class Node { } } namespace TestApp { public interface IInventoryModel : GFramework.Core.Abstractions.Model.IModel { } public interface ICombatSystem : GFramework.Core.Abstractions.Systems.ISystem { } public interface IUiUtility : GFramework.Core.Abstractions.Utility.IUtility { } public interface IStrategy { } [GetAll] public partial class BattlePanel : GFramework.Core.Rule.ContextAwareBase { private IInventoryModel _model = null!; private IReadOnlyList _models = null!; private ICombatSystem _system = null!; private IUiUtility _utility = null!; private IStrategy _service = null!; private Godot.Node _node = null!; } } """; const string expected = """ // #nullable enable using GFramework.Core.Extensions; namespace TestApp; partial class BattlePanel { private void __InjectContextBindings_Generated() { _model = this.GetModel(); _models = this.GetModels(); _system = this.GetSystem(); _utility = this.GetUtility(); _service = this.GetService(); } } """; await GeneratorTest.RunAsync( source, ("TestApp_BattlePanel.ContextGet.g.cs", expected)); Assert.Pass(); } [Test] public async Task Generates_Bindings_For_IContextAware_Class() { var source = """ using System; using GFramework.SourceGenerators.Abstractions.Rule; namespace GFramework.SourceGenerators.Abstractions.Rule { [AttributeUsage(AttributeTargets.Field, Inherited = false)] public sealed class GetServiceAttribute : 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 GetService(this object contextAware) => default!; } } namespace TestApp { public interface IStrategy { } public partial class StrategyHost : GFramework.Core.Abstractions.Rule.IContextAware { [GetService] private IStrategy _strategy = null!; } } """; const string expected = """ // #nullable enable using GFramework.Core.Extensions; namespace TestApp; partial class StrategyHost { private void __InjectContextBindings_Generated() { _strategy = this.GetService(); } } """; await GeneratorTest.RunAsync( source, ("TestApp_StrategyHost.ContextGet.g.cs", expected)); Assert.Pass(); } [Test] public async Task Reports_Diagnostic_When_Class_Is_Not_ContextAware() { var source = """ using System; using GFramework.SourceGenerators.Abstractions.Rule; namespace GFramework.SourceGenerators.Abstractions.Rule { [AttributeUsage(AttributeTargets.Field, Inherited = false)] public sealed class GetModelAttribute : Attribute { } } 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(this object contextAware) => default!; } } namespace TestApp { public interface IInventoryModel : GFramework.Core.Abstractions.Model.IModel { } public partial class InventoryPanel { [GetModel] private IInventoryModel _model = null!; } } """; var test = new CSharpSourceGeneratorTest { TestState = { Sources = { source } }, DisabledDiagnostics = { "GF_Common_Trace_001" } }; test.ExpectedDiagnostics.Add(new DiagnosticResult("GF_ContextGet_005", DiagnosticSeverity.Error) .WithSpan(31, 30, 31, 45) .WithArguments("InventoryPanel")); await test.RunAsync(); Assert.Pass(); } [Test] public async Task Reports_Diagnostic_When_GetModels_Field_Is_Not_IReadOnlyList() { var source = """ using System; using System.Collections.Generic; using GFramework.SourceGenerators.Abstractions.Rule; namespace GFramework.SourceGenerators.Abstractions.Rule { [AttributeUsage(AttributeTargets.Field, Inherited = false)] public sealed class GetModelsAttribute : 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 IReadOnlyList GetModels(this object contextAware) => default!; } } namespace TestApp { public interface IInventoryModel : GFramework.Core.Abstractions.Model.IModel { } public partial class InventoryPanel : GFramework.Core.Abstractions.Rule.IContextAware { [GetModels] private List _models = new(); } } """; var test = new CSharpSourceGeneratorTest { TestState = { Sources = { source } }, DisabledDiagnostics = { "GF_Common_Trace_001" } }; test.ExpectedDiagnostics.Add(new DiagnosticResult("GF_ContextGet_004", DiagnosticSeverity.Error) .WithSpan(40, 40, 40, 47) .WithArguments("_models", "System.Collections.Generic.List", "GetModels")); await test.RunAsync(); Assert.Pass(); } }