diff --git a/Refit.GeneratorTests/Fixture.cs b/Refit.GeneratorTests/Fixture.cs new file mode 100644 index 000000000..57f4331d7 --- /dev/null +++ b/Refit.GeneratorTests/Fixture.cs @@ -0,0 +1,95 @@ +using System.Reflection; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Refit.Generator; + +namespace Refit.GeneratorTests; + +public static class Fixture +{ + static readonly MetadataReference RefitAssembly = MetadataReference.CreateFromFile( + typeof(GetAttribute).Assembly.Location, + documentation: XmlDocumentationProvider.CreateFromFile( + Path.ChangeExtension(typeof(GetAttribute).Assembly.Location, ".xml") + ) + ); + + private static readonly Type[] ImportantAssemblies = { + typeof(Binder), + typeof(GetAttribute), + typeof(System.Reactive.Unit), + typeof(Enumerable), + typeof(Newtonsoft.Json.JsonConvert), + typeof(FactAttribute), + typeof(HttpContent), + typeof(Attribute) + }; + + private static Assembly[] AssemblyReferencesForCodegen => + AppDomain.CurrentDomain + .GetAssemblies() + .Concat(ImportantAssemblies.Select(x=>x.Assembly)) + .Distinct() + .Where(a => !a.IsDynamic) + .ToArray(); + + public static Task VerifyForBody(string body) + { + var source = + $$""" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Refit; + + namespace RefitGeneratorTest; + + public interface IGeneratedClient + { + {{body}} + } + """; + + return VerifyGenerator(source); + } + + private static CSharpCompilation CreateLibrary(params string[] source) + { + var references = new List(); + var assemblies = AssemblyReferencesForCodegen; + foreach (var assembly in assemblies) + { + if (!assembly.IsDynamic) + { + references.Add(MetadataReference.CreateFromFile(assembly.Location)); + } + } + + references.Add(RefitAssembly); + var compilation = CSharpCompilation.Create( + "compilation", + source.Select(s => CSharpSyntaxTree.ParseText(s)), + references, + new CSharpCompilationOptions(OutputKind.ConsoleApplication) + ); + + return compilation; + } + + private static Task VerifyGenerator(string source) + { + var compilation = CreateLibrary(source); + + var generator = new InterfaceStubGenerator(); + var driver = CSharpGeneratorDriver.Create(generator); + + var ranDriver = driver.RunGenerators(compilation); + var settings = new VerifySettings(); + var verify = VerifyXunit.Verifier.Verify(ranDriver, settings); + return verify.ToTask(); + } +} diff --git a/Refit.GeneratorTests/ModuleInitializer.cs b/Refit.GeneratorTests/ModuleInitializer.cs new file mode 100644 index 000000000..db1bea6a0 --- /dev/null +++ b/Refit.GeneratorTests/ModuleInitializer.cs @@ -0,0 +1,19 @@ +using System.Runtime.CompilerServices; +using VerifyTests.DiffPlex; + +namespace Refit.GeneratorTests; + +public static class ModuleInitializer +{ + // ModuleInitializer should only be used in apps +#pragma warning disable CA2255 + [ModuleInitializer] +#pragma warning restore CA2255 + public static void Init() + { + DerivePathInfo((file, _, type, method) => new(Path.Combine(Path.GetDirectoryName(file), "_snapshots"), type.Name, method.Name)); + + VerifySourceGenerators.Initialize(); + VerifyDiffPlex.Initialize(OutputType.Compact); + } +} diff --git a/Refit.GeneratorTests/Refit.GeneratorTests.csproj b/Refit.GeneratorTests/Refit.GeneratorTests.csproj new file mode 100644 index 000000000..12d4cd631 --- /dev/null +++ b/Refit.GeneratorTests/Refit.GeneratorTests.csproj @@ -0,0 +1,46 @@ + + + + + + net6.0;net8.0 + enable + enable + + false + true + $(NoWarn);CS1591;CA1819;CA2000;CA2007;CA1056;CA1707;CA1861;xUnit1031 + + + + + + + + + + + + + + + + + + + + + + + + + %(RecursiveDir)\resources\%(Filename)%(Extension) + Always + + + + + + + + diff --git a/Refit.GeneratorTests/ReturnTypeTests.cs b/Refit.GeneratorTests/ReturnTypeTests.cs new file mode 100644 index 000000000..1e9605b6e --- /dev/null +++ b/Refit.GeneratorTests/ReturnTypeTests.cs @@ -0,0 +1,25 @@ +namespace Refit.GeneratorTests; + +[UsesVerify] +public class ReturnTypeTests +{ + [Fact] + public Task GenericTaskShouldWork() + { + return Fixture.VerifyForBody( + """ + [Get("/users")] + Task Get(); + """); + } + + [Fact] + public Task VoidTaskShouldWork() + { + return Fixture.VerifyForBody( + """ + [Post("/users")] + Task Post(); + """); + } +} diff --git a/Refit.GeneratorTests/_snapshots/ReturnTypeTests.GenericTaskShouldWork#Generated.g.verified.cs b/Refit.GeneratorTests/_snapshots/ReturnTypeTests.GenericTaskShouldWork#Generated.g.verified.cs new file mode 100644 index 000000000..4470d5d71 --- /dev/null +++ b/Refit.GeneratorTests/_snapshots/ReturnTypeTests.GenericTaskShouldWork#Generated.g.verified.cs @@ -0,0 +1,24 @@ +//HintName: Generated.g.cs + +#pragma warning disable +namespace Refit.Implementation +{ + + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.Diagnostics.DebuggerNonUserCode] + [global::RefitInternalGenerated.PreserveAttribute] + [global::System.Reflection.Obfuscation(Exclude=true)] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal static partial class Generated + { +#if NET5_0_OR_GREATER + [System.Runtime.CompilerServices.ModuleInitializer] + [System.Diagnostics.CodeAnalysis.DynamicDependency(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All, typeof(global::Refit.Implementation.Generated))] + public static void Initialize() + { + } +#endif + } +} +#pragma warning restore diff --git a/Refit.GeneratorTests/_snapshots/ReturnTypeTests.GenericTaskShouldWork#IGeneratedClient.g.verified.cs b/Refit.GeneratorTests/_snapshots/ReturnTypeTests.GenericTaskShouldWork#IGeneratedClient.g.verified.cs new file mode 100644 index 000000000..19a9fc649 --- /dev/null +++ b/Refit.GeneratorTests/_snapshots/ReturnTypeTests.GenericTaskShouldWork#IGeneratedClient.g.verified.cs @@ -0,0 +1,66 @@ +//HintName: IGeneratedClient.g.cs +#nullable disable +#pragma warning disable +namespace Refit.Implementation +{ + + partial class Generated + { + + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.Diagnostics.DebuggerNonUserCode] + [global::RefitInternalGenerated.PreserveAttribute] + [global::System.Reflection.Obfuscation(Exclude=true)] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + partial class RefitGeneratorTestIGeneratedClient + : global::RefitGeneratorTest.IGeneratedClient + + { + /// + public global::System.Net.Http.HttpClient Client { get; } + readonly global::Refit.IRequestBuilder requestBuilder; + + /// + public RefitGeneratorTestIGeneratedClient(global::System.Net.Http.HttpClient client, global::Refit.IRequestBuilder requestBuilder) + { + Client = client; + this.requestBuilder = requestBuilder; + } + + + + /// + public async global::System.Threading.Tasks.Task Get() + { + var ______arguments = global::System.Array.Empty(); + var ______func = requestBuilder.BuildRestResultFuncForMethod("Get", global::System.Array.Empty() ); + try + { + return await ((global::System.Threading.Tasks.Task)______func(this.Client, ______arguments)).ConfigureAwait(false); + } + catch (global::System.Exception ______ex) + { + throw ______ex; + } + } + + /// + async global::System.Threading.Tasks.Task global::RefitGeneratorTest.IGeneratedClient.Get() + { + var ______arguments = global::System.Array.Empty(); + var ______func = requestBuilder.BuildRestResultFuncForMethod("Get", global::System.Array.Empty() ); + try + { + return await ((global::System.Threading.Tasks.Task)______func(this.Client, ______arguments)).ConfigureAwait(false); + } + catch (global::System.Exception ______ex) + { + throw ______ex; + } + } + } + } +} + +#pragma warning restore diff --git a/Refit.GeneratorTests/_snapshots/ReturnTypeTests.GenericTaskShouldWork#PreserveAttribute.g.verified.cs b/Refit.GeneratorTests/_snapshots/ReturnTypeTests.GenericTaskShouldWork#PreserveAttribute.g.verified.cs new file mode 100644 index 000000000..71b34929f --- /dev/null +++ b/Refit.GeneratorTests/_snapshots/ReturnTypeTests.GenericTaskShouldWork#PreserveAttribute.g.verified.cs @@ -0,0 +1,19 @@ +//HintName: PreserveAttribute.g.cs + +#pragma warning disable +namespace RefitInternalGenerated +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.AttributeUsage (global::System.AttributeTargets.Class | global::System.AttributeTargets.Struct | global::System.AttributeTargets.Enum | global::System.AttributeTargets.Constructor | global::System.AttributeTargets.Method | global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Event | global::System.AttributeTargets.Interface | global::System.AttributeTargets.Delegate)] + sealed class PreserveAttribute : global::System.Attribute + { + // + // Fields + // + public bool AllMembers; + + public bool Conditional; + } +} +#pragma warning restore diff --git a/Refit.GeneratorTests/_snapshots/ReturnTypeTests.VoidTaskShouldWork#Generated.g.verified.cs b/Refit.GeneratorTests/_snapshots/ReturnTypeTests.VoidTaskShouldWork#Generated.g.verified.cs new file mode 100644 index 000000000..4470d5d71 --- /dev/null +++ b/Refit.GeneratorTests/_snapshots/ReturnTypeTests.VoidTaskShouldWork#Generated.g.verified.cs @@ -0,0 +1,24 @@ +//HintName: Generated.g.cs + +#pragma warning disable +namespace Refit.Implementation +{ + + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.Diagnostics.DebuggerNonUserCode] + [global::RefitInternalGenerated.PreserveAttribute] + [global::System.Reflection.Obfuscation(Exclude=true)] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal static partial class Generated + { +#if NET5_0_OR_GREATER + [System.Runtime.CompilerServices.ModuleInitializer] + [System.Diagnostics.CodeAnalysis.DynamicDependency(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All, typeof(global::Refit.Implementation.Generated))] + public static void Initialize() + { + } +#endif + } +} +#pragma warning restore diff --git a/Refit.GeneratorTests/_snapshots/ReturnTypeTests.VoidTaskShouldWork#IGeneratedClient.g.verified.cs b/Refit.GeneratorTests/_snapshots/ReturnTypeTests.VoidTaskShouldWork#IGeneratedClient.g.verified.cs new file mode 100644 index 000000000..09c2eec91 --- /dev/null +++ b/Refit.GeneratorTests/_snapshots/ReturnTypeTests.VoidTaskShouldWork#IGeneratedClient.g.verified.cs @@ -0,0 +1,66 @@ +//HintName: IGeneratedClient.g.cs +#nullable disable +#pragma warning disable +namespace Refit.Implementation +{ + + partial class Generated + { + + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.Diagnostics.DebuggerNonUserCode] + [global::RefitInternalGenerated.PreserveAttribute] + [global::System.Reflection.Obfuscation(Exclude=true)] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + partial class RefitGeneratorTestIGeneratedClient + : global::RefitGeneratorTest.IGeneratedClient + + { + /// + public global::System.Net.Http.HttpClient Client { get; } + readonly global::Refit.IRequestBuilder requestBuilder; + + /// + public RefitGeneratorTestIGeneratedClient(global::System.Net.Http.HttpClient client, global::Refit.IRequestBuilder requestBuilder) + { + Client = client; + this.requestBuilder = requestBuilder; + } + + + + /// + public async global::System.Threading.Tasks.Task Post() + { + var ______arguments = global::System.Array.Empty(); + var ______func = requestBuilder.BuildRestResultFuncForMethod("Post", global::System.Array.Empty() ); + try + { + await ((global::System.Threading.Tasks.Task)______func(this.Client, ______arguments)).ConfigureAwait(false); + } + catch (global::System.Exception ______ex) + { + throw ______ex; + } + } + + /// + async global::System.Threading.Tasks.Task global::RefitGeneratorTest.IGeneratedClient.Post() + { + var ______arguments = global::System.Array.Empty(); + var ______func = requestBuilder.BuildRestResultFuncForMethod("Post", global::System.Array.Empty() ); + try + { + await ((global::System.Threading.Tasks.Task)______func(this.Client, ______arguments)).ConfigureAwait(false); + } + catch (global::System.Exception ______ex) + { + throw ______ex; + } + } + } + } +} + +#pragma warning restore diff --git a/Refit.GeneratorTests/_snapshots/ReturnTypeTests.VoidTaskShouldWork#PreserveAttribute.g.verified.cs b/Refit.GeneratorTests/_snapshots/ReturnTypeTests.VoidTaskShouldWork#PreserveAttribute.g.verified.cs new file mode 100644 index 000000000..71b34929f --- /dev/null +++ b/Refit.GeneratorTests/_snapshots/ReturnTypeTests.VoidTaskShouldWork#PreserveAttribute.g.verified.cs @@ -0,0 +1,19 @@ +//HintName: PreserveAttribute.g.cs + +#pragma warning disable +namespace RefitInternalGenerated +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.AttributeUsage (global::System.AttributeTargets.Class | global::System.AttributeTargets.Struct | global::System.AttributeTargets.Enum | global::System.AttributeTargets.Constructor | global::System.AttributeTargets.Method | global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Event | global::System.AttributeTargets.Interface | global::System.AttributeTargets.Delegate)] + sealed class PreserveAttribute : global::System.Attribute + { + // + // Fields + // + public bool AllMembers; + + public bool Conditional; + } +} +#pragma warning restore diff --git a/Refit.sln b/Refit.sln index 6edc2aa7b..fdeb099bb 100644 --- a/Refit.sln +++ b/Refit.sln @@ -34,6 +34,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Refit.Xml", "Refit.Xml\Refi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Refit.Benchmarks", "Refit.Benchmarks\Refit.Benchmarks.csproj", "{ABD72A27-9C30-481A-8303-D8F825A8FD47}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Refit.GeneratorTests", "Refit.GeneratorTests\Refit.GeneratorTests.csproj", "{CE7894EA-D411-494A-BA8B-1C231D45025D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -174,6 +176,22 @@ Global {ABD72A27-9C30-481A-8303-D8F825A8FD47}.Release|x64.Build.0 = Release|Any CPU {ABD72A27-9C30-481A-8303-D8F825A8FD47}.Release|x86.ActiveCfg = Release|Any CPU {ABD72A27-9C30-481A-8303-D8F825A8FD47}.Release|x86.Build.0 = Release|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Debug|ARM.ActiveCfg = Debug|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Debug|ARM.Build.0 = Debug|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Debug|x64.ActiveCfg = Debug|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Debug|x64.Build.0 = Debug|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Debug|x86.ActiveCfg = Debug|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Debug|x86.Build.0 = Debug|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Release|Any CPU.Build.0 = Release|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Release|ARM.ActiveCfg = Release|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Release|ARM.Build.0 = Release|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Release|x64.ActiveCfg = Release|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Release|x64.Build.0 = Release|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Release|x86.ActiveCfg = Release|Any CPU + {CE7894EA-D411-494A-BA8B-1C231D45025D}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -181,6 +199,7 @@ Global GlobalSection(NestedProjects) = preSolution {EB833B36-D3CA-4308-A776-8D574F2ADF64} = {0E99249A-FB80-4C60-8FD3-13820E853FF7} {ABD72A27-9C30-481A-8303-D8F825A8FD47} = {0E99249A-FB80-4C60-8FD3-13820E853FF7} + {CE7894EA-D411-494A-BA8B-1C231D45025D} = {0E99249A-FB80-4C60-8FD3-13820E853FF7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6E9C2873-AFF9-4D32-A784-1BA094814054}