From a62013b40cfc70bc3adb241659ca4b8027da5685 Mon Sep 17 00:00:00 2001 From: Konrad B Date: Mon, 9 Sep 2024 12:55:30 +0200 Subject: [PATCH 1/3] Issue-2691 - Add Verify to easier making source generators + upgrade libs to support IIncrementalGenerators + Add VisualBasic DLLs for test project required by SmartAnalyzers.RoslynTestKit reference (it's required to match DLL version) --- ...xtensions.Http.AspNetCore.Analyzers.csproj | 2 +- .../BindingTypeCodeRefactoringProvider.cs | 6 +- sdk/Sdk.Analyzers/Sdk.Analyzers.csproj | 4 +- sdk/Sdk.Generators/Sdk.Generators.csproj | 8 +- .../SdkTests.csproj | 4 +- .../Sdk.Analyzers.Tests.csproj | 2 + ...atedFunctionMetadataProvider.g.verified.cs | 62 ++++ ...atedFunctionMetadataProvider.g.verified.cs | 62 ++++ ...atedFunctionMetadataProvider.g.verified.cs | 75 ++++ ...atedFunctionMetadataProvider.g.verified.cs | 62 ++++ .../HttpTriggerTests.cs | 322 ++---------------- .../Helpers/AnalyzerConfigOptions.cs | 39 +++ .../SourceGeneratorVerifyExtensions.cs | 203 +++++++++++ .../{ => Helpers}/TestHelpers.cs | 22 +- test/Sdk.Generator.Tests/ModuleInitializer.cs | 14 + .../Sdk.Generator.Tests.csproj | 18 +- 16 files changed, 573 insertions(+), 332 deletions(-) create mode 100644 test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.BasicHttpFunctionWithNoResponse#GeneratedFunctionMetadataProvider.g.verified.cs create mode 100644 test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.GenerateSimpleHttpTriggerMetadataTest#GeneratedFunctionMetadataProvider.g.verified.cs create mode 100644 test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.NonStaticVoidOrTaskReturnType#GeneratedFunctionMetadataProvider.g.verified.cs create mode 100644 test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.ReturnTypeJustHttp#GeneratedFunctionMetadataProvider.g.verified.cs create mode 100644 test/Sdk.Generator.Tests/Helpers/AnalyzerConfigOptions.cs create mode 100644 test/Sdk.Generator.Tests/Helpers/SourceGeneratorVerifyExtensions.cs rename test/Sdk.Generator.Tests/{ => Helpers}/TestHelpers.cs (99%) create mode 100644 test/Sdk.Generator.Tests/ModuleInitializer.cs diff --git a/extensions/Worker.Extensions.Http.AspNetCore.Analyzers/src/Worker.Extensions.Http.AspNetCore.Analyzers.csproj b/extensions/Worker.Extensions.Http.AspNetCore.Analyzers/src/Worker.Extensions.Http.AspNetCore.Analyzers.csproj index 022514d9a..a5b713f84 100644 --- a/extensions/Worker.Extensions.Http.AspNetCore.Analyzers/src/Worker.Extensions.Http.AspNetCore.Analyzers.csproj +++ b/extensions/Worker.Extensions.Http.AspNetCore.Analyzers/src/Worker.Extensions.Http.AspNetCore.Analyzers.csproj @@ -22,7 +22,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/sdk/Sdk.Analyzers/BindingTypeCodeRefactoringProvider.cs b/sdk/Sdk.Analyzers/BindingTypeCodeRefactoringProvider.cs index 44a3de3f7..ecb791a32 100644 --- a/sdk/Sdk.Analyzers/BindingTypeCodeRefactoringProvider.cs +++ b/sdk/Sdk.Analyzers/BindingTypeCodeRefactoringProvider.cs @@ -1,19 +1,17 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System; -using System.Composition; using System.Collections.Generic; +using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.CodeRefactorings; - namespace Microsoft.Azure.Functions.Worker.Sdk.Analyzers { [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(BindingTypeCodeRefactoringProvider)), Shared] diff --git a/sdk/Sdk.Analyzers/Sdk.Analyzers.csproj b/sdk/Sdk.Analyzers/Sdk.Analyzers.csproj index c43f14f4e..8619ec767 100644 --- a/sdk/Sdk.Analyzers/Sdk.Analyzers.csproj +++ b/sdk/Sdk.Analyzers/Sdk.Analyzers.csproj @@ -19,11 +19,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/sdk/Sdk.Generators/Sdk.Generators.csproj b/sdk/Sdk.Generators/Sdk.Generators.csproj index fc001592e..476974bc7 100644 --- a/sdk/Sdk.Generators/Sdk.Generators.csproj +++ b/sdk/Sdk.Generators/Sdk.Generators.csproj @@ -17,13 +17,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/test/FunctionMetadataGeneratorTests/SdkTests.csproj b/test/FunctionMetadataGeneratorTests/SdkTests.csproj index fd40ace00..5780d4263 100644 --- a/test/FunctionMetadataGeneratorTests/SdkTests.csproj +++ b/test/FunctionMetadataGeneratorTests/SdkTests.csproj @@ -26,8 +26,8 @@ - - + + diff --git a/test/Sdk.Analyzers.Tests/Sdk.Analyzers.Tests.csproj b/test/Sdk.Analyzers.Tests/Sdk.Analyzers.Tests.csproj index b84dc015b..dc9402d2d 100644 --- a/test/Sdk.Analyzers.Tests/Sdk.Analyzers.Tests.csproj +++ b/test/Sdk.Analyzers.Tests/Sdk.Analyzers.Tests.csproj @@ -7,6 +7,8 @@ + + diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.BasicHttpFunctionWithNoResponse#GeneratedFunctionMetadataProvider.g.verified.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.BasicHttpFunctionWithNoResponse#GeneratedFunctionMetadataProvider.g.verified.cs new file mode 100644 index 000000000..1f1f77e41 --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.BasicHttpFunctionWithNoResponse#GeneratedFunctionMetadataProvider.g.verified.cs @@ -0,0 +1,62 @@ +//HintName: GeneratedFunctionMetadataProvider.g.cs +// +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace TestProject +{ + /// + /// Custom implementation that returns function metadata definitions for the current worker."/> + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class GeneratedFunctionMetadataProvider : IFunctionMetadataProvider + { + /// + public Task> GetFunctionMetadataAsync(string directory) + { + var metadataList = new List(); + var Function0RawBindings = new List(); + Function0RawBindings.Add(@"{""name"":""req"",""type"":""httpTrigger"",""direction"":""In"",""authLevel"":""Admin"",""methods"":[""get"",""post""],""route"":""/api2""}"); + Function0RawBindings.Add(@"{""name"":""$return"",""type"":""http"",""direction"":""Out""}"); + + var Function0 = new DefaultFunctionMetadata + { + Language = "dotnet-isolated", + Name = "HttpTrigger", + EntryPoint = "FunctionApp.HttpTriggerSimple.HttpTrigger", + RawBindings = Function0RawBindings, + ScriptFile = "TestProject.dll" + }; + metadataList.Add(Function0); + + return Task.FromResult(metadataList.ToImmutableArray()); + } + } + + /// + /// Extension methods to enable registration of the custom implementation generated for the current worker. + /// + public static class WorkerHostBuilderFunctionMetadataProviderExtension + { + /// + /// Adds the GeneratedFunctionMetadataProvider to the service collection. + /// During initialization, the worker will return generated function metadata instead of relying on the Azure Functions host for function indexing. + /// + public static IHostBuilder ConfigureGeneratedFunctionMetadataProvider(this IHostBuilder builder) + { + builder.ConfigureServices(s => + { + s.AddSingleton(); + }); + return builder; + } + } +} \ No newline at end of file diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.GenerateSimpleHttpTriggerMetadataTest#GeneratedFunctionMetadataProvider.g.verified.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.GenerateSimpleHttpTriggerMetadataTest#GeneratedFunctionMetadataProvider.g.verified.cs new file mode 100644 index 000000000..a5d59830d --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.GenerateSimpleHttpTriggerMetadataTest#GeneratedFunctionMetadataProvider.g.verified.cs @@ -0,0 +1,62 @@ +//HintName: GeneratedFunctionMetadataProvider.g.cs +// +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace TestProject +{ + /// + /// Custom implementation that returns function metadata definitions for the current worker."/> + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class GeneratedFunctionMetadataProvider : IFunctionMetadataProvider + { + /// + public Task> GetFunctionMetadataAsync(string directory) + { + var metadataList = new List(); + var Function0RawBindings = new List(); + Function0RawBindings.Add(@"{""name"":""req"",""type"":""httpTrigger"",""direction"":""In"",""authLevel"":""Anonymous"",""methods"":[""get"",""post""]}"); + Function0RawBindings.Add(@"{""name"":""$return"",""type"":""http"",""direction"":""Out""}"); + + var Function0 = new DefaultFunctionMetadata + { + Language = "dotnet-isolated", + Name = "HttpTriggerSimple", + EntryPoint = "FunctionApp.HttpTriggerSimple.Run", + RawBindings = Function0RawBindings, + ScriptFile = "TestProject.dll" + }; + metadataList.Add(Function0); + + return Task.FromResult(metadataList.ToImmutableArray()); + } + } + + /// + /// Extension methods to enable registration of the custom implementation generated for the current worker. + /// + public static class WorkerHostBuilderFunctionMetadataProviderExtension + { + /// + /// Adds the GeneratedFunctionMetadataProvider to the service collection. + /// During initialization, the worker will return generated function metadata instead of relying on the Azure Functions host for function indexing. + /// + public static IHostBuilder ConfigureGeneratedFunctionMetadataProvider(this IHostBuilder builder) + { + builder.ConfigureServices(s => + { + s.AddSingleton(); + }); + return builder; + } + } +} \ No newline at end of file diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.NonStaticVoidOrTaskReturnType#GeneratedFunctionMetadataProvider.g.verified.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.NonStaticVoidOrTaskReturnType#GeneratedFunctionMetadataProvider.g.verified.cs new file mode 100644 index 000000000..17fb19cef --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.NonStaticVoidOrTaskReturnType#GeneratedFunctionMetadataProvider.g.verified.cs @@ -0,0 +1,75 @@ +//HintName: GeneratedFunctionMetadataProvider.g.cs +// +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace MyCompany.MyProject.MyApp +{ + /// + /// Custom implementation that returns function metadata definitions for the current worker."/> + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class GeneratedFunctionMetadataProvider : IFunctionMetadataProvider + { + /// + public Task> GetFunctionMetadataAsync(string directory) + { + var metadataList = new List(); + var Function0RawBindings = new List(); + Function0RawBindings.Add(@"{""name"":""req"",""type"":""httpTrigger"",""direction"":""In"",""methods"":[""get""]}"); + Function0RawBindings.Add(@"{""name"":""$return"",""type"":""http"",""direction"":""Out""}"); + + var Function0 = new DefaultFunctionMetadata + { + Language = "dotnet-isolated", + Name = "Function1", + EntryPoint = "Foo.HttpTriggers.FunctionWithVoidReturnType", + RawBindings = Function0RawBindings, + ScriptFile = "TestProject.dll" + }; + metadataList.Add(Function0); + var Function1RawBindings = new List(); + Function1RawBindings.Add(@"{""name"":""req"",""type"":""httpTrigger"",""direction"":""In"",""methods"":[""get""]}"); + Function1RawBindings.Add(@"{""name"":""$return"",""type"":""http"",""direction"":""Out""}"); + + var Function1 = new DefaultFunctionMetadata + { + Language = "dotnet-isolated", + Name = "Function2", + EntryPoint = "Foo.HttpTriggers.FunctionWithTaskReturnType", + RawBindings = Function1RawBindings, + ScriptFile = "TestProject.dll" + }; + metadataList.Add(Function1); + + return Task.FromResult(metadataList.ToImmutableArray()); + } + } + + /// + /// Extension methods to enable registration of the custom implementation generated for the current worker. + /// + public static class WorkerHostBuilderFunctionMetadataProviderExtension + { + /// + /// Adds the GeneratedFunctionMetadataProvider to the service collection. + /// During initialization, the worker will return generated function metadata instead of relying on the Azure Functions host for function indexing. + /// + public static IHostBuilder ConfigureGeneratedFunctionMetadataProvider(this IHostBuilder builder) + { + builder.ConfigureServices(s => + { + s.AddSingleton(); + }); + return builder; + } + } +} \ No newline at end of file diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.ReturnTypeJustHttp#GeneratedFunctionMetadataProvider.g.verified.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.ReturnTypeJustHttp#GeneratedFunctionMetadataProvider.g.verified.cs new file mode 100644 index 000000000..c25e651c3 --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.ReturnTypeJustHttp#GeneratedFunctionMetadataProvider.g.verified.cs @@ -0,0 +1,62 @@ +//HintName: GeneratedFunctionMetadataProvider.g.cs +// +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace MyCompany.MyProject.MyApp +{ + /// + /// Custom implementation that returns function metadata definitions for the current worker."/> + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class GeneratedFunctionMetadataProvider : IFunctionMetadataProvider + { + /// + public Task> GetFunctionMetadataAsync(string directory) + { + var metadataList = new List(); + var Function0RawBindings = new List(); + Function0RawBindings.Add(@"{""name"":""req"",""type"":""httpTrigger"",""direction"":""In"",""methods"":[""get""],""dataType"":""String""}"); + Function0RawBindings.Add(@"{""name"":""httpResponseProp"",""type"":""http"",""direction"":""Out""}"); + + var Function0 = new DefaultFunctionMetadata + { + Language = "dotnet-isolated", + Name = "JustHtt", + EntryPoint = "Foo.HttpTriggerSimple.Justhtt", + RawBindings = Function0RawBindings, + ScriptFile = "TestProject.dll" + }; + metadataList.Add(Function0); + + return Task.FromResult(metadataList.ToImmutableArray()); + } + } + + /// + /// Extension methods to enable registration of the custom implementation generated for the current worker. + /// + public static class WorkerHostBuilderFunctionMetadataProviderExtension + { + /// + /// Adds the GeneratedFunctionMetadataProvider to the service collection. + /// During initialization, the worker will return generated function metadata instead of relying on the Azure Functions host for function indexing. + /// + public static IHostBuilder ConfigureGeneratedFunctionMetadataProvider(this IHostBuilder builder) + { + builder.ConfigureServices(s => + { + s.AddSingleton(); + }); + return builder; + } + } +} \ No newline at end of file diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs index b95db3252..ad8b1799a 100644 --- a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; +using Microsoft.Azure.Functions.SdkGeneratorTests.Helpers; using Microsoft.Azure.Functions.Worker.Sdk.Generators; using Microsoft.CodeAnalysis.CSharp; using Microsoft.Extensions.DependencyInjection; @@ -66,77 +67,11 @@ public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "g } """; - string expectedGeneratedFileName = $"GeneratedFunctionMetadataProvider.g.cs"; - string expectedOutput = """ - // - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Text.Json; - using System.Threading.Tasks; - using Microsoft.Azure.Functions.Worker; - using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; - - namespace TestProject - { - /// - /// Custom implementation that returns function metadata definitions for the current worker."/> - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class GeneratedFunctionMetadataProvider : IFunctionMetadataProvider - { - /// - public Task> GetFunctionMetadataAsync(string directory) - { - var metadataList = new List(); - var Function0RawBindings = new List(); - Function0RawBindings.Add(@"{""name"":""req"",""type"":""httpTrigger"",""direction"":""In"",""authLevel"":""Anonymous"",""methods"":[""get"",""post""]}"); - Function0RawBindings.Add(@"{""name"":""$return"",""type"":""http"",""direction"":""Out""}"); - - var Function0 = new DefaultFunctionMetadata - { - Language = "dotnet-isolated", - Name = "HttpTriggerSimple", - EntryPoint = "FunctionApp.HttpTriggerSimple.Run", - RawBindings = Function0RawBindings, - ScriptFile = "TestProject.dll" - }; - metadataList.Add(Function0); - - return Task.FromResult(metadataList.ToImmutableArray()); - } - } - - /// - /// Extension methods to enable registration of the custom implementation generated for the current worker. - /// - public static class WorkerHostBuilderFunctionMetadataProviderExtension - { - /// - /// Adds the GeneratedFunctionMetadataProvider to the service collection. - /// During initialization, the worker will return generated function metadata instead of relying on the Azure Functions host for function indexing. - /// - public static IHostBuilder ConfigureGeneratedFunctionMetadataProvider(this IHostBuilder builder) - { - builder.ConfigureServices(s => - { - s.AddSingleton(); - }); - return builder; - } - } - } - """; - - await TestHelpers.RunTestAsync( - _referencedExtensionAssemblies, - inputCode, - expectedGeneratedFileName, - expectedOutput, - languageVersion: languageVersion); + await new FunctionMetadataProviderGenerator() + .RunAndVerify( + inputCode, + _referencedExtensionAssemblies, + languageVersion: languageVersion); } [Theory] @@ -167,78 +102,11 @@ public static void HttpTrigger([HttpTrigger(AuthorizationLevel.Admin, "get", "po } """; - - string expectedGeneratedFileName = $"GeneratedFunctionMetadataProvider.g.cs"; - string expectedOutput = """ - // - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Text.Json; - using System.Threading.Tasks; - using Microsoft.Azure.Functions.Worker; - using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; - - namespace TestProject - { - /// - /// Custom implementation that returns function metadata definitions for the current worker."/> - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class GeneratedFunctionMetadataProvider : IFunctionMetadataProvider - { - /// - public Task> GetFunctionMetadataAsync(string directory) - { - var metadataList = new List(); - var Function0RawBindings = new List(); - Function0RawBindings.Add(@"{""name"":""req"",""type"":""httpTrigger"",""direction"":""In"",""authLevel"":""Admin"",""methods"":[""get"",""post""],""route"":""/api2""}"); - Function0RawBindings.Add(@"{""name"":""$return"",""type"":""http"",""direction"":""Out""}"); - - var Function0 = new DefaultFunctionMetadata - { - Language = "dotnet-isolated", - Name = "HttpTrigger", - EntryPoint = "FunctionApp.HttpTriggerSimple.HttpTrigger", - RawBindings = Function0RawBindings, - ScriptFile = "TestProject.dll" - }; - metadataList.Add(Function0); - - return Task.FromResult(metadataList.ToImmutableArray()); - } - } - - /// - /// Extension methods to enable registration of the custom implementation generated for the current worker. - /// - public static class WorkerHostBuilderFunctionMetadataProviderExtension - { - /// - /// Adds the GeneratedFunctionMetadataProvider to the service collection. - /// During initialization, the worker will return generated function metadata instead of relying on the Azure Functions host for function indexing. - /// - public static IHostBuilder ConfigureGeneratedFunctionMetadataProvider(this IHostBuilder builder) - { - builder.ConfigureServices(s => - { - s.AddSingleton(); - }); - return builder; - } - } - } - """; - - await TestHelpers.RunTestAsync( - _referencedExtensionAssemblies, - inputCode, - expectedGeneratedFileName, - expectedOutput, - languageVersion: languageVersion); + await new FunctionMetadataProviderGenerator() + .RunAndVerify( + inputCode, + _referencedExtensionAssemblies, + languageVersion: languageVersion); } [Theory] @@ -274,84 +142,18 @@ public class JustHttp } """; - - string expectedGeneratedFileName = $"GeneratedFunctionMetadataProvider.g.cs"; - string expectedOutput = """ - // - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Text.Json; - using System.Threading.Tasks; - using Microsoft.Azure.Functions.Worker; - using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; - - namespace MyCompany.MyProject.MyApp - { - /// - /// Custom implementation that returns function metadata definitions for the current worker."/> - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class GeneratedFunctionMetadataProvider : IFunctionMetadataProvider - { - /// - public Task> GetFunctionMetadataAsync(string directory) - { - var metadataList = new List(); - var Function0RawBindings = new List(); - Function0RawBindings.Add(@"{""name"":""req"",""type"":""httpTrigger"",""direction"":""In"",""methods"":[""get""],""dataType"":""String""}"); - Function0RawBindings.Add(@"{""name"":""httpResponseProp"",""type"":""http"",""direction"":""Out""}"); - - var Function0 = new DefaultFunctionMetadata - { - Language = "dotnet-isolated", - Name = "JustHtt", - EntryPoint = "Foo.HttpTriggerSimple.Justhtt", - RawBindings = Function0RawBindings, - ScriptFile = "TestProject.dll" - }; - metadataList.Add(Function0); - - return Task.FromResult(metadataList.ToImmutableArray()); - } - } - - /// - /// Extension methods to enable registration of the custom implementation generated for the current worker. - /// - public static class WorkerHostBuilderFunctionMetadataProviderExtension - { - /// - /// Adds the GeneratedFunctionMetadataProvider to the service collection. - /// During initialization, the worker will return generated function metadata instead of relying on the Azure Functions host for function indexing. - /// - public static IHostBuilder ConfigureGeneratedFunctionMetadataProvider(this IHostBuilder builder) - { - builder.ConfigureServices(s => - { - s.AddSingleton(); - }); - return builder; - } - } - } - """; // override the namespace value for generated types using msbuild property. var buildPropertiesDict = new Dictionary() { { Constants.BuildProperties.GeneratedCodeNamespace, "MyCompany.MyProject.MyApp"} }; - await TestHelpers.RunTestAsync( - _referencedExtensionAssemblies, - inputCode, - expectedGeneratedFileName, - expectedOutput, - buildPropertiesDictionary: buildPropertiesDict, - languageVersion: languageVersion); + await new FunctionMetadataProviderGenerator() + .RunAndVerify( + inputCode, + _referencedExtensionAssemblies, + languageVersion: languageVersion, + buildPropertiesDictionary: buildPropertiesDict); } [Theory] @@ -389,96 +191,18 @@ public Task FunctionWithTaskReturnType([HttpTrigger("get")] HttpRequestData req) } """; - string expectedGeneratedFileName = $"GeneratedFunctionMetadataProvider.g.cs"; - string expectedOutput = """" - // - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Text.Json; - using System.Threading.Tasks; - using Microsoft.Azure.Functions.Worker; - using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; - - namespace MyCompany.MyProject.MyApp - { - /// - /// Custom implementation that returns function metadata definitions for the current worker."/> - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class GeneratedFunctionMetadataProvider : IFunctionMetadataProvider - { - /// - public Task> GetFunctionMetadataAsync(string directory) - { - var metadataList = new List(); - var Function0RawBindings = new List(); - Function0RawBindings.Add(@"{""name"":""req"",""type"":""httpTrigger"",""direction"":""In"",""methods"":[""get""]}"); - Function0RawBindings.Add(@"{""name"":""$return"",""type"":""http"",""direction"":""Out""}"); - - var Function0 = new DefaultFunctionMetadata - { - Language = "dotnet-isolated", - Name = "Function1", - EntryPoint = "Foo.HttpTriggers.FunctionWithVoidReturnType", - RawBindings = Function0RawBindings, - ScriptFile = "TestProject.dll" - }; - metadataList.Add(Function0); - var Function1RawBindings = new List(); - Function1RawBindings.Add(@"{""name"":""req"",""type"":""httpTrigger"",""direction"":""In"",""methods"":[""get""]}"); - Function1RawBindings.Add(@"{""name"":""$return"",""type"":""http"",""direction"":""Out""}"); - - var Function1 = new DefaultFunctionMetadata - { - Language = "dotnet-isolated", - Name = "Function2", - EntryPoint = "Foo.HttpTriggers.FunctionWithTaskReturnType", - RawBindings = Function1RawBindings, - ScriptFile = "TestProject.dll" - }; - metadataList.Add(Function1); - - return Task.FromResult(metadataList.ToImmutableArray()); - } - } - - /// - /// Extension methods to enable registration of the custom implementation generated for the current worker. - /// - public static class WorkerHostBuilderFunctionMetadataProviderExtension - { - /// - /// Adds the GeneratedFunctionMetadataProvider to the service collection. - /// During initialization, the worker will return generated function metadata instead of relying on the Azure Functions host for function indexing. - /// - public static IHostBuilder ConfigureGeneratedFunctionMetadataProvider(this IHostBuilder builder) - { - builder.ConfigureServices(s => - { - s.AddSingleton(); - }); - return builder; - } - } - } - """"; // override the namespace value for generated types using msbuild property. var buildPropertiesDict = new Dictionary() { { Constants.BuildProperties.GeneratedCodeNamespace, "MyCompany.MyProject.MyApp"} }; - await TestHelpers.RunTestAsync( - _referencedExtensionAssemblies, - inputCode, - expectedGeneratedFileName, - expectedOutput, - buildPropertiesDictionary: buildPropertiesDict, - languageVersion: languageVersion); + await new FunctionMetadataProviderGenerator() + .RunAndVerify( + inputCode, + _referencedExtensionAssemblies, + languageVersion: languageVersion, + buildPropertiesDictionary: buildPropertiesDict); } } } diff --git a/test/Sdk.Generator.Tests/Helpers/AnalyzerConfigOptions.cs b/test/Sdk.Generator.Tests/Helpers/AnalyzerConfigOptions.cs new file mode 100644 index 000000000..8b0cb12b9 --- /dev/null +++ b/test/Sdk.Generator.Tests/Helpers/AnalyzerConfigOptions.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.Azure.Functions.SdkGeneratorTests.Helpers +{ + internal sealed class AnalyzerConfigOptions : AnalyzerConfigOptionsProvider + { + private readonly Options _options; + + public AnalyzerConfigOptions( + Dictionary options) + { + _options = new Options(options); + } + + public override CodeAnalysis.Diagnostics.AnalyzerConfigOptions GlobalOptions => _options; + + public override CodeAnalysis.Diagnostics.AnalyzerConfigOptions GetOptions(SyntaxTree tree) => _options; + public override CodeAnalysis.Diagnostics.AnalyzerConfigOptions GetOptions(AdditionalText textFile) => _options; + + private class Options : CodeAnalysis.Diagnostics.AnalyzerConfigOptions + { + private readonly Dictionary options; + + public Options( + Dictionary options) + { + this.options = options; + } + + public override bool TryGetValue(string key, [NotNullWhen(true)] out string? value) + { + return options.TryGetValue(key, out value); + } + } + } +} diff --git a/test/Sdk.Generator.Tests/Helpers/SourceGeneratorVerifyExtensions.cs b/test/Sdk.Generator.Tests/Helpers/SourceGeneratorVerifyExtensions.cs new file mode 100644 index 000000000..31d2e4f74 --- /dev/null +++ b/test/Sdk.Generator.Tests/Helpers/SourceGeneratorVerifyExtensions.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Core; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using VerifyXunit; +using Xunit; + +namespace Microsoft.Azure.Functions.SdkGeneratorTests.Helpers +{ + internal static class SourceGeneratorVerifyExtensions + { + private const LanguageVersion _defaultLanguageVersion = LanguageVersion.CSharp7_3; + + private static readonly string _dotNetAssemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location); + private static readonly TimeSpan _executionMaxTime = TimeSpan.FromSeconds(30); + + public static async Task RunAndVerify( + this IIncrementalGenerator sourceGenerator, + string inputSource, + IEnumerable? extensionAssemblyReferences = null, + IDictionary? buildPropertiesDictionary = null, + string? generatedCodeNamespace = null, + LanguageVersion? languageVersion = null, + bool runInsideAzureFunctionProject = true, + [CallerFilePath] string callerFileName = "", + [CallerMemberName] string callerName = "") + { + using var cts = new CancellationTokenSource(); +#if !DEBUG + cts.CancelAfter(_executionMaxTime); +#endif + + var compilation = CreateCompilation(inputSource, extensionAssemblyReferences, languageVersion); + + var config = CreateAnalyzerOptions( + buildPropertiesDictionary, + generatedCodeNamespace, + runInsideAzureFunctionProject); + + cts.Token.ThrowIfCancellationRequested(); + var driver = CSharpGeneratorDriver.Create(sourceGenerator) + .WithUpdatedAnalyzerConfigOptions(config); + + var generateResult = driver.RunGenerators(compilation, cts.Token); + + cts.Token.ThrowIfCancellationRequested(); + await VerifyGeneratedCode(generateResult, callerFileName, callerName); + } + + public static async Task RunAndVerify( + this ISourceGenerator sourceGenerator, + string inputSource, + IEnumerable? extensionAssemblyReferences = null, + IDictionary? buildPropertiesDictionary = null, + string? generatedCodeNamespace = null, + LanguageVersion? languageVersion = null, + bool runInsideAzureFunctionProject = true, + [CallerFilePath] string callerFileName = "", + [CallerMemberName] string callerName = "") + { + using var cts = new CancellationTokenSource(); +#if !DEBUG + cts.CancelAfter(_executionMaxTime); +#endif + + var compilation = CreateCompilation(inputSource, extensionAssemblyReferences, languageVersion); + + var config = CreateAnalyzerOptions( + buildPropertiesDictionary, + generatedCodeNamespace, + runInsideAzureFunctionProject); + + cts.Token.ThrowIfCancellationRequested(); + var driver = CSharpGeneratorDriver.Create(sourceGenerator) + .WithUpdatedAnalyzerConfigOptions(config); + + var generateResult = driver.RunGenerators(compilation, cts.Token); + + cts.Token.ThrowIfCancellationRequested(); + await VerifyGeneratedCode(generateResult, callerFileName, callerName); + } + + private static async Task VerifyGeneratedCode(GeneratorDriver generateResult, string callerFileName, string callerName) + { + var settings = Verifier + .Verify(generateResult) + .DisableRequireUniquePrefix(); + + if (!string.IsNullOrWhiteSpace(callerFileName)) + { + settings = settings + .UseDirectory(Path.GetDirectoryName(callerFileName)) + .UseFileName($"{Path.GetFileNameWithoutExtension(callerFileName)}.{callerName}"); + } + + await settings; + } + + private static AnalyzerConfigOptions CreateAnalyzerOptions( + IDictionary? buildPropertiesDictionary, + string? generatedCodeNamespace, + bool runInsideAzureFunctionProject) + { + var options = new Dictionary() + { + ["is_global"] = true.ToString(), + ["build_property.FunctionsEnableExecutorSourceGen"] = true.ToString(), + ["build_property.FunctionsEnableMetadataSourceGen"] = true.ToString(), + ["build_property.FunctionsGeneratedCodeNamespace"] = generatedCodeNamespace ?? "TestProject" + }; + + if (runInsideAzureFunctionProject) + { + options.Add("build_property.FunctionsExecutionModel", "isolated"); + } + + if (buildPropertiesDictionary is not null) + { + foreach (var pair in buildPropertiesDictionary) + { + options[pair.Key] = pair.Value; + } + } + + var config = new AnalyzerConfigOptions(options); + return config; + } + + private static CSharpCompilation CreateCompilation(string inputSource, IEnumerable? extensionAssemblyReferences, LanguageVersion? languageVersion) + { + var syntaxTree = CSharpSyntaxTree.ParseText( + inputSource, + new CSharpParseOptions(languageVersion ?? _defaultLanguageVersion)); + + var metadata = GetAllAssemblies((extensionAssemblyReferences ?? Array.Empty()) + .Concat(new[] + { + typeof(WorkerExtensionStartupAttribute).Assembly, + typeof(HttpTriggerAttribute).Assembly, + typeof(FunctionAttribute).Assembly + })) + .Distinct() + .Select(l => MetadataReference.CreateFromFile(l)) + .ToArray(); + + var compilation = CSharpCompilation.Create( + "TestProject", + new[] { syntaxTree }, + metadata); + + var errors = compilation.GetDiagnostics() + .Where(d => d.DefaultSeverity >= DiagnosticSeverity.Error + && !GetIgnoredErrors().Contains(d.Id)) + .ToArray(); + + Assert.Empty(errors); + return compilation; + } + + private static IEnumerable GetAllAssemblies( + IEnumerable assemblies) + { + foreach (var item in assemblies) + { + yield return item.Location; + + foreach (var nestedAssembly in GetAllAssemblies(item + .GetReferencedAssemblies() + .Select(x => Assembly.Load(x)))) + { + yield return nestedAssembly; + } + } + + foreach (var item in GetBaseCompilationAssemblies()) + { + yield return item; + } + } + + private static IEnumerable GetBaseCompilationAssemblies() + { + yield return Path.Combine(_dotNetAssemblyPath, "netstandard.dll"); + yield return Path.Combine(_dotNetAssemblyPath, "System.dll"); + yield return Path.Combine(_dotNetAssemblyPath, "System.Core.dll"); + yield return Path.Combine(_dotNetAssemblyPath, "System.Private.CoreLib.dll"); + yield return Path.Combine(_dotNetAssemblyPath, "System.Runtime.dll"); + } + + private static IEnumerable GetIgnoredErrors() + { + yield return "CS5001"; // Program does not contain a static 'Main' method suitable for an entry point + } + } +} diff --git a/test/Sdk.Generator.Tests/TestHelpers.cs b/test/Sdk.Generator.Tests/Helpers/TestHelpers.cs similarity index 99% rename from test/Sdk.Generator.Tests/TestHelpers.cs rename to test/Sdk.Generator.Tests/Helpers/TestHelpers.cs index 296bb8932..56bfc6d40 100644 --- a/test/Sdk.Generator.Tests/TestHelpers.cs +++ b/test/Sdk.Generator.Tests/Helpers/TestHelpers.cs @@ -1,22 +1,22 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Text; using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Versioning; using System.Text; using System.Threading.Tasks; +using Microsoft.Azure.Functions.Worker.Core; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Testing; -using System.IO; -using Microsoft.CodeAnalysis.CSharp; -using System.Collections.Immutable; using Microsoft.CodeAnalysis.Testing.Verifiers; -using Microsoft.Azure.Functions.Worker.Core; -using System.Reflection; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Versioning; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.Azure.Functions.SdkGeneratorTests { @@ -77,7 +77,6 @@ public static Task RunTestAsync( } } - test.TestState.AnalyzerConfigFiles.Add(("/.globalconfig", config)); foreach (var item in extensionAssemblyReferences) @@ -92,7 +91,6 @@ public static Task RunTestAsync( return test.RunAsync(); } - } // Mostly copy/pasted from the Microsoft Source Generators testing documentation diff --git a/test/Sdk.Generator.Tests/ModuleInitializer.cs b/test/Sdk.Generator.Tests/ModuleInitializer.cs new file mode 100644 index 000000000..2fdb16347 --- /dev/null +++ b/test/Sdk.Generator.Tests/ModuleInitializer.cs @@ -0,0 +1,14 @@ +using System.Runtime.CompilerServices; +using VerifyTests; + +namespace Microsoft.Azure.Functions.SdkGeneratorTests +{ + public static class ModuleInitializer + { + [ModuleInitializer] + public static void Init() + { + VerifySourceGenerators.Initialize(); + } + } +} diff --git a/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj b/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj index e30977b68..5e610aae9 100644 --- a/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj +++ b/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj @@ -15,23 +15,25 @@ - - - + + + - + + + - runtime; build; native; contentfiles; analyzers; buildtransitive - all + runtime; build; native; contentfiles; analyzers; buildtransitive + all - runtime; build; native; contentfiles; analyzers; buildtransitive - all + runtime; build; native; contentfiles; analyzers; buildtransitive + all From 3f98745f7cf0eb31d90f50a2454fd59203bf8fbf Mon Sep 17 00:00:00 2001 From: Konrad B Date: Wed, 11 Sep 2024 06:42:27 +0200 Subject: [PATCH 2/3] Issue-2691 - fix performance pitfall of loading related assemblies --- .../SourceGeneratorVerifyExtensions.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/test/Sdk.Generator.Tests/Helpers/SourceGeneratorVerifyExtensions.cs b/test/Sdk.Generator.Tests/Helpers/SourceGeneratorVerifyExtensions.cs index 31d2e4f74..889e9c832 100644 --- a/test/Sdk.Generator.Tests/Helpers/SourceGeneratorVerifyExtensions.cs +++ b/test/Sdk.Generator.Tests/Helpers/SourceGeneratorVerifyExtensions.cs @@ -137,8 +137,8 @@ private static AnalyzerConfigOptions CreateAnalyzerOptions( private static CSharpCompilation CreateCompilation(string inputSource, IEnumerable? extensionAssemblyReferences, LanguageVersion? languageVersion) { var syntaxTree = CSharpSyntaxTree.ParseText( - inputSource, - new CSharpParseOptions(languageVersion ?? _defaultLanguageVersion)); + inputSource, + new CSharpParseOptions(languageVersion ?? _defaultLanguageVersion)); var metadata = GetAllAssemblies((extensionAssemblyReferences ?? Array.Empty()) .Concat(new[] @@ -166,15 +166,22 @@ private static CSharpCompilation CreateCompilation(string inputSource, IEnumerab } private static IEnumerable GetAllAssemblies( - IEnumerable assemblies) + IEnumerable assemblies, + HashSet? alreadyConsumed = null) { + alreadyConsumed ??= new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var item in assemblies) { + if (!alreadyConsumed.Add(item.Location)) + { + continue; + } + yield return item.Location; foreach (var nestedAssembly in GetAllAssemblies(item .GetReferencedAssemblies() - .Select(x => Assembly.Load(x)))) + .Select(x => Assembly.Load(x)), alreadyConsumed)) { yield return nestedAssembly; } @@ -182,7 +189,10 @@ private static IEnumerable GetAllAssemblies( foreach (var item in GetBaseCompilationAssemblies()) { - yield return item; + if (alreadyConsumed.Add(item)) + { + yield return item; + } } } From eec96b5b7cddb311aa19e3b9739b3b2a1d0e4ec6 Mon Sep 17 00:00:00 2001 From: Konrad B Date: Wed, 11 Sep 2024 18:23:56 +0200 Subject: [PATCH 3/3] issue/2691 - convert more test to verify --- .editorconfig | 7 + ...ly#GeneratedFunctionExecutor.g.verified.cs | 60 ++ .../FunctionExecutor/DependentAssemblyTest.cs | 124 +-- ...ce#GeneratedFunctionExecutor.g.verified.cs | 64 ++ ...es#GeneratedFunctionExecutor.g.verified.cs | 84 ++ ...ng#GeneratedFunctionExecutor.g.verified.cs | 64 ++ ...on#GeneratedFunctionExecutor.g.verified.cs | 66 ++ ...ds#GeneratedFunctionExecutor.g.verified.cs | 94 +++ ...ed#GeneratedFunctionExecutor.g.verified.cs | 60 ++ .../FunctionExecutorGeneratorTests.cs | 795 ++++-------------- .../SourceGeneratorVerifyExtensions.cs | 4 +- 11 files changed, 702 insertions(+), 720 deletions(-) create mode 100644 test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.FunctionsFromDependentAssembly#GeneratedFunctionExecutor.g.verified.cs create mode 100644 test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.ClassWithSameNameAsNamespace#GeneratedFunctionExecutor.g.verified.cs create mode 100644 test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.FunctionsFromMultipleClasses#GeneratedFunctionExecutor.g.verified.cs create mode 100644 test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.FunctionsWithSameNameExceptForCasing#GeneratedFunctionExecutor.g.verified.cs create mode 100644 test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.MultipleFunctionsDependencyInjection#GeneratedFunctionExecutor.g.verified.cs create mode 100644 test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.StaticMethods#GeneratedFunctionExecutor.g.verified.cs create mode 100644 test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.VerifyAutoConfigureStartupTypeEmitted#GeneratedFunctionExecutor.g.verified.cs diff --git a/.editorconfig b/.editorconfig index 49de0d370..d153e3a58 100644 --- a/.editorconfig +++ b/.editorconfig @@ -56,6 +56,13 @@ dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_ dotnet_naming_symbols.constant_fields.applicable_kinds = field dotnet_naming_symbols.constant_fields.applicable_accessibilities = * dotnet_naming_symbols.constant_fields.required_modifiers = const +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private +dotnet_naming_style.prefix_underscore.capitalization = camel_case +dotnet_naming_style.prefix_underscore.required_prefix = _ +dotnet_naming_rule.private_fields_with_underscore.symbols = private_fields +dotnet_naming_rule.private_fields_with_underscore.style = prefix_underscore +dotnet_naming_rule.private_fields_with_underscore.severity = warning ############################### # C# Coding Conventions # ############################### diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.FunctionsFromDependentAssembly#GeneratedFunctionExecutor.g.verified.cs b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.FunctionsFromDependentAssembly#GeneratedFunctionExecutor.g.verified.cs new file mode 100644 index 000000000..4d2999c69 --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.FunctionsFromDependentAssembly#GeneratedFunctionExecutor.g.verified.cs @@ -0,0 +1,60 @@ +//HintName: GeneratedFunctionExecutor.g.cs +// +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Context.Features; +using Microsoft.Azure.Functions.Worker.Invocation; +namespace TestProject +{ + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor + { + private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; + private readonly Dictionary types = new Dictionary() + { + { "MyCompany.MyHttpTriggers", Type.GetType("MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") } + }; + + public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) + { + _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); + } + + /// + public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) + { + var inputBindingFeature = context.Features.Get(); + var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); + var inputArguments = inputBindingResult.Values; + + if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyHttpTriggers.Foo", StringComparison.Ordinal)) + { + var instanceType = types["MyCompany.MyHttpTriggers"]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; + context.GetInvocationResult().Value = i.Foo((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); + } + } + } + + /// + /// Extension methods to enable registration of the custom implementation generated for the current worker. + /// + public static class FunctionExecutorHostBuilderExtensions + { + /// + /// Configures an optimized function executor to the invocation pipeline. + /// + public static IHostBuilder ConfigureGeneratedFunctionExecutor(this IHostBuilder builder) + { + return builder.ConfigureServices(s => + { + s.AddSingleton(); + }); + } + } +} \ No newline at end of file diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs index ed925683d..2dd177a5b 100644 --- a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs +++ b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs @@ -3,6 +3,7 @@ using System.Reflection; using System.Threading.Tasks; +using Microsoft.Azure.Functions.SdkGeneratorTests.Helpers; using Microsoft.Azure.Functions.Worker.Sdk.Generators; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -41,112 +42,23 @@ public DependentAssemblyTest() [Fact] public async Task FunctionsFromDependentAssembly() { - const string inputSourceCode = """ - using System; - using Microsoft.Azure.Functions.Worker; - using Microsoft.Azure.Functions.Worker.Http; - namespace MyCompany - { - public class MyHttpTriggers - { - [Function("FunctionA")] - public HttpResponseData Foo([HttpTrigger(AuthorizationLevel.User, "get")] HttpRequestData r, FunctionContext c) - { - return r.CreateResponse(System.Net.HttpStatusCode.OK); - } - } - } - """; - var expected = $$""" - // - using System; - using System.Threading.Tasks; - using System.Collections.Generic; - using Microsoft.Extensions.Hosting; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Azure.Functions.Worker; - using Microsoft.Azure.Functions.Worker.Context.Features; - using Microsoft.Azure.Functions.Worker.Invocation; - namespace TestProject - { - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor - { - private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; - private Lazy _defaultExecutor; - private readonly Dictionary types = new Dictionary() - { - { "MyCompany.MyHttpTriggers", Type.GetType("MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") }, - { "DependentAssemblyWithFunctions.DependencyFunction", Type.GetType("DependentAssemblyWithFunctions.DependencyFunction, DependentAssemblyWithFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") }, - { "MyCompany.MyProduct.MyApp.HttpFunctions", Type.GetType("MyCompany.MyProduct.MyApp.HttpFunctions, DependentAssemblyWithFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") }, - { "MyCompany.MyProduct.MyApp.Foo.Bar", Type.GetType("MyCompany.MyProduct.MyApp.Foo.Bar, DependentAssemblyWithFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") } - }; - - public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) - { - _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); - } - - /// - public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) - { - var inputBindingFeature = context.Features.Get(); - var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); - var inputArguments = inputBindingResult.Values; - _defaultExecutor = new Lazy(() => CreateDefaultExecutorInstance(context)); - - if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyHttpTriggers.Foo", StringComparison.Ordinal)) - { - var instanceType = types["MyCompany.MyHttpTriggers"]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; - context.GetInvocationResult().Value = i.Foo((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); - } - else if (string.Equals(context.FunctionDefinition.EntryPoint, "DependentAssemblyWithFunctions.DependencyFunction.Run", StringComparison.Ordinal)) - { - var instanceType = types["DependentAssemblyWithFunctions.DependencyFunction"]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::DependentAssemblyWithFunctions.DependencyFunction; - context.GetInvocationResult().Value = i.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); - } - else if (string.Equals(context.FunctionDefinition.EntryPoint, "DependentAssemblyWithFunctions.InternalFunction.Run", StringComparison.Ordinal)) - { - await _defaultExecutor.Value.ExecuteAsync(context); - } - else if (string.Equals(context.FunctionDefinition.EntryPoint, "DependentAssemblyWithFunctions.StaticFunction.Run", StringComparison.Ordinal)) - { - context.GetInvocationResult().Value = global::DependentAssemblyWithFunctions.StaticFunction.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); - } - else if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyProduct.MyApp.HttpFunctions.Run", StringComparison.Ordinal)) - { - var instanceType = types["MyCompany.MyProduct.MyApp.HttpFunctions"]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyProduct.MyApp.HttpFunctions; - context.GetInvocationResult().Value = i.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); - } - else if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyProduct.MyApp.Foo.Bar.Run", StringComparison.Ordinal)) - { - var instanceType = types["MyCompany.MyProduct.MyApp.Foo.Bar"]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyProduct.MyApp.Foo.Bar; - context.GetInvocationResult().Value = i.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); - } - } - - private global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor CreateDefaultExecutorInstance(global::Microsoft.Azure.Functions.Worker.FunctionContext context) - { - var defaultExecutorFullName = "Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=1.18.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c"; - var defaultExecutorType = global::System.Type.GetType(defaultExecutorFullName); - - return ActivatorUtilities.CreateInstance(context.InstanceServices, defaultExecutorType) as global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor; - } - } - {{GetExpectedExtensionMethodCode()}} - } - """.Replace("'", "\""); - - await TestHelpers.RunTestAsync( - _referencedAssemblies, - inputSourceCode, - Constants.FileNames.GeneratedFunctionExecutor, - expected); + await new FunctionExecutorGenerator() + .RunAndVerify(""" + using System; + using Microsoft.Azure.Functions.Worker; + using Microsoft.Azure.Functions.Worker.Http; + namespace MyCompany + { + public class MyHttpTriggers + { + [Function("FunctionA")] + public HttpResponseData Foo([HttpTrigger(AuthorizationLevel.User, "get")] HttpRequestData r, FunctionContext c) + { + return r.CreateResponse(System.Net.HttpStatusCode.OK); + } + } + } + """); } } } diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.ClassWithSameNameAsNamespace#GeneratedFunctionExecutor.g.verified.cs b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.ClassWithSameNameAsNamespace#GeneratedFunctionExecutor.g.verified.cs new file mode 100644 index 000000000..eab6c7caf --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.ClassWithSameNameAsNamespace#GeneratedFunctionExecutor.g.verified.cs @@ -0,0 +1,64 @@ +//HintName: GeneratedFunctionExecutor.g.cs +// +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Context.Features; +using Microsoft.Azure.Functions.Worker.Invocation; +namespace TestProject +{ + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor + { + private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; + private readonly Dictionary types = new Dictionary() + { + { "TestProject.TestProject", Type.GetType("TestProject.TestProject, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") } + }; + + public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) + { + _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); + } + + /// + public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) + { + var inputBindingFeature = context.Features.Get(); + var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); + var inputArguments = inputBindingResult.Values; + + if (string.Equals(context.FunctionDefinition.EntryPoint, "TestProject.TestProject.Foo", StringComparison.Ordinal)) + { + var instanceType = types["TestProject.TestProject"]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::TestProject.TestProject; + context.GetInvocationResult().Value = i.Foo((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "TestProject.TestProject.FooStatic", StringComparison.Ordinal)) + { + context.GetInvocationResult().Value = global::TestProject.TestProject.FooStatic((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); + } + } + } + + /// + /// Extension methods to enable registration of the custom implementation generated for the current worker. + /// + public static class FunctionExecutorHostBuilderExtensions + { + /// + /// Configures an optimized function executor to the invocation pipeline. + /// + public static IHostBuilder ConfigureGeneratedFunctionExecutor(this IHostBuilder builder) + { + return builder.ConfigureServices(s => + { + s.AddSingleton(); + }); + } + } +} \ No newline at end of file diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.FunctionsFromMultipleClasses#GeneratedFunctionExecutor.g.verified.cs b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.FunctionsFromMultipleClasses#GeneratedFunctionExecutor.g.verified.cs new file mode 100644 index 000000000..b0efb2e76 --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.FunctionsFromMultipleClasses#GeneratedFunctionExecutor.g.verified.cs @@ -0,0 +1,84 @@ +//HintName: GeneratedFunctionExecutor.g.cs +// +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Context.Features; +using Microsoft.Azure.Functions.Worker.Invocation; +namespace TestProject +{ + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor + { + private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; + private readonly Dictionary types = new Dictionary() + { + { "MyCompany.MyHttpTriggers", Type.GetType("MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") }, + { "MyCompany.MyHttpTriggers2", Type.GetType("MyCompany.MyHttpTriggers2, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") }, + { "MyCompany.QueueTriggers", Type.GetType("MyCompany.QueueTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") } + }; + + public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) + { + _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); + } + + /// + public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) + { + var inputBindingFeature = context.Features.Get(); + var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); + var inputArguments = inputBindingResult.Values; + + if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyHttpTriggers.Foo", StringComparison.Ordinal)) + { + var instanceType = types["MyCompany.MyHttpTriggers"]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; + context.GetInvocationResult().Value = i.Foo((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyHttpTriggers2.Bar", StringComparison.Ordinal)) + { + var instanceType = types["MyCompany.MyHttpTriggers2"]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers2; + context.GetInvocationResult().Value = i.Bar((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.Foo.MyAsyncStaticMethod", StringComparison.Ordinal)) + { + context.GetInvocationResult().Value = await global::MyCompany.Foo.MyAsyncStaticMethod((string)inputArguments[0]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.QueueTriggers.Run", StringComparison.Ordinal)) + { + var instanceType = types["MyCompany.QueueTriggers"]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.QueueTriggers; + i.Run((global::Azure.Storage.Queues.Models.QueueMessage)inputArguments[0]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.QueueTriggers.Run2", StringComparison.Ordinal)) + { + var instanceType = types["MyCompany.QueueTriggers"]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.QueueTriggers; + i.Run2((string)inputArguments[0]); + } + } + } + + /// + /// Extension methods to enable registration of the custom implementation generated for the current worker. + /// + public static class FunctionExecutorHostBuilderExtensions + { + /// + /// Configures an optimized function executor to the invocation pipeline. + /// + public static IHostBuilder ConfigureGeneratedFunctionExecutor(this IHostBuilder builder) + { + return builder.ConfigureServices(s => + { + s.AddSingleton(); + }); + } + } +} \ No newline at end of file diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.FunctionsWithSameNameExceptForCasing#GeneratedFunctionExecutor.g.verified.cs b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.FunctionsWithSameNameExceptForCasing#GeneratedFunctionExecutor.g.verified.cs new file mode 100644 index 000000000..00abf81ac --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.FunctionsWithSameNameExceptForCasing#GeneratedFunctionExecutor.g.verified.cs @@ -0,0 +1,64 @@ +//HintName: GeneratedFunctionExecutor.g.cs +// +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Context.Features; +using Microsoft.Azure.Functions.Worker.Invocation; +namespace TestProject +{ + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor + { + private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; + private readonly Dictionary types = new Dictionary() + { + { "MyCompany.MyHttpTriggers", Type.GetType("MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") } + }; + + public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) + { + _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); + } + + /// + public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) + { + var inputBindingFeature = context.Features.Get(); + var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); + var inputArguments = inputBindingResult.Values; + + if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyHttpTriggers.Hello", StringComparison.Ordinal)) + { + var instanceType = types["MyCompany.MyHttpTriggers"]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; + context.GetInvocationResult().Value = i.Hello((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyHttpTriggers.HELLO", StringComparison.Ordinal)) + { + context.GetInvocationResult().Value = global::MyCompany.MyHttpTriggers.HELLO((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); + } + } + } + + /// + /// Extension methods to enable registration of the custom implementation generated for the current worker. + /// + public static class FunctionExecutorHostBuilderExtensions + { + /// + /// Configures an optimized function executor to the invocation pipeline. + /// + public static IHostBuilder ConfigureGeneratedFunctionExecutor(this IHostBuilder builder) + { + return builder.ConfigureServices(s => + { + s.AddSingleton(); + }); + } + } +} \ No newline at end of file diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.MultipleFunctionsDependencyInjection#GeneratedFunctionExecutor.g.verified.cs b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.MultipleFunctionsDependencyInjection#GeneratedFunctionExecutor.g.verified.cs new file mode 100644 index 000000000..c9456a35a --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.MultipleFunctionsDependencyInjection#GeneratedFunctionExecutor.g.verified.cs @@ -0,0 +1,66 @@ +//HintName: GeneratedFunctionExecutor.g.cs +// +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Context.Features; +using Microsoft.Azure.Functions.Worker.Invocation; +namespace MyCompany.MyProject.MyApp +{ + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor + { + private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; + private readonly Dictionary types = new Dictionary() + { + { "MyCompany.MyHttpTriggers", Type.GetType("MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") } + }; + + public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) + { + _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); + } + + /// + public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) + { + var inputBindingFeature = context.Features.Get(); + var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); + var inputArguments = inputBindingResult.Values; + + if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyHttpTriggers.Run1", StringComparison.Ordinal)) + { + var instanceType = types["MyCompany.MyHttpTriggers"]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; + context.GetInvocationResult().Value = i.Run1((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyHttpTriggers.Run2", StringComparison.Ordinal)) + { + var instanceType = types["MyCompany.MyHttpTriggers"]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; + context.GetInvocationResult().Value = i.Run2((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); + } + } + } + + /// + /// Extension methods to enable registration of the custom implementation generated for the current worker. + /// + public static class FunctionExecutorHostBuilderExtensions + { + /// + /// Configures an optimized function executor to the invocation pipeline. + /// + public static IHostBuilder ConfigureGeneratedFunctionExecutor(this IHostBuilder builder) + { + return builder.ConfigureServices(s => + { + s.AddSingleton(); + }); + } + } +} \ No newline at end of file diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.StaticMethods#GeneratedFunctionExecutor.g.verified.cs b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.StaticMethods#GeneratedFunctionExecutor.g.verified.cs new file mode 100644 index 000000000..2fa544b34 --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.StaticMethods#GeneratedFunctionExecutor.g.verified.cs @@ -0,0 +1,94 @@ +//HintName: GeneratedFunctionExecutor.g.cs +// +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Context.Features; +using Microsoft.Azure.Functions.Worker.Invocation; +namespace TestProject +{ + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor + { + private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; + + public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) + { + _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); + } + + /// + public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) + { + var inputBindingFeature = context.Features.Get(); + var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); + var inputArguments = inputBindingResult.Values; + + if (string.Equals(context.FunctionDefinition.EntryPoint, "FunctionApp26.MyQTriggers.MyTaskStaticMethod", StringComparison.Ordinal)) + { + await global::FunctionApp26.MyQTriggers.MyTaskStaticMethod((string)inputArguments[0]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "FunctionApp26.MyQTriggers.MyAsyncStaticMethod", StringComparison.Ordinal)) + { + context.GetInvocationResult().Value = await global::FunctionApp26.MyQTriggers.MyAsyncStaticMethod((string)inputArguments[0]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "FunctionApp26.MyQTriggers.MyVoidStaticMethod", StringComparison.Ordinal)) + { + global::FunctionApp26.MyQTriggers.MyVoidStaticMethod((string)inputArguments[0]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "FunctionApp26.MyQTriggers.MyAsyncStaticMethodWithReturn", StringComparison.Ordinal)) + { + context.GetInvocationResult().Value = await global::FunctionApp26.MyQTriggers.MyAsyncStaticMethodWithReturn((string)inputArguments[0], (string)inputArguments[1]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "FunctionApp26.MyQTriggers.MyValueTaskOfTStaticAsyncMethod", StringComparison.Ordinal)) + { + context.GetInvocationResult().Value = await global::FunctionApp26.MyQTriggers.MyValueTaskOfTStaticAsyncMethod((string)inputArguments[0]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "FunctionApp26.MyQTriggers.MyValueTaskStaticAsyncMethod2", StringComparison.Ordinal)) + { + await global::FunctionApp26.MyQTriggers.MyValueTaskStaticAsyncMethod2((string)inputArguments[0]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "FunctionApp26.BlobTriggers.Run", StringComparison.Ordinal)) + { + await global::FunctionApp26.BlobTriggers.Run((global::System.IO.Stream)inputArguments[0], (string)inputArguments[1]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "FunctionApp26.EventHubTriggers.Run1", StringComparison.Ordinal)) + { + global::FunctionApp26.EventHubTriggers.Run1((global::Azure.Messaging.EventHubs.EventData[])inputArguments[0]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "FunctionApp26.EventHubTriggers.Run2", StringComparison.Ordinal)) + { + context.GetInvocationResult().Value = global::FunctionApp26.EventHubTriggers.Run2((global::Azure.Messaging.EventHubs.EventData)inputArguments[0]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "FunctionApp26.EventHubTriggers.RunAsync1", StringComparison.Ordinal)) + { + await global::FunctionApp26.EventHubTriggers.RunAsync1((global::Azure.Messaging.EventHubs.EventData[])inputArguments[0]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "FunctionApp26.EventHubTriggers.RunAsync2", StringComparison.Ordinal)) + { + await global::FunctionApp26.EventHubTriggers.RunAsync2((global::Azure.Messaging.EventHubs.EventData[])inputArguments[0]); + } + } + } + + /// + /// Extension methods to enable registration of the custom implementation generated for the current worker. + /// + public static class FunctionExecutorHostBuilderExtensions + { + /// + /// Configures an optimized function executor to the invocation pipeline. + /// + public static IHostBuilder ConfigureGeneratedFunctionExecutor(this IHostBuilder builder) + { + return builder.ConfigureServices(s => + { + s.AddSingleton(); + }); + } + } +} \ No newline at end of file diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.VerifyAutoConfigureStartupTypeEmitted#GeneratedFunctionExecutor.g.verified.cs b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.VerifyAutoConfigureStartupTypeEmitted#GeneratedFunctionExecutor.g.verified.cs new file mode 100644 index 000000000..4d994df97 --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.VerifyAutoConfigureStartupTypeEmitted#GeneratedFunctionExecutor.g.verified.cs @@ -0,0 +1,60 @@ +//HintName: GeneratedFunctionExecutor.g.cs +// +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Context.Features; +using Microsoft.Azure.Functions.Worker.Invocation; +namespace TestProject +{ + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor + { + private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; + private readonly Dictionary types = new Dictionary() + { + { "MyCompany.MyHttpTriggers", Type.GetType("MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") } + }; + + public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) + { + _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); + } + + /// + public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) + { + var inputBindingFeature = context.Features.Get(); + var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); + var inputArguments = inputBindingResult.Values; + + if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyHttpTriggers.Run1", StringComparison.Ordinal)) + { + var instanceType = types["MyCompany.MyHttpTriggers"]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; + context.GetInvocationResult().Value = i.Run1((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); + } + } + } + + /// + /// Extension methods to enable registration of the custom implementation generated for the current worker. + /// + public static class FunctionExecutorHostBuilderExtensions + { + /// + /// Configures an optimized function executor to the invocation pipeline. + /// + public static IHostBuilder ConfigureGeneratedFunctionExecutor(this IHostBuilder builder) + { + return builder.ConfigureServices(s => + { + s.AddSingleton(); + }); + } + } +} \ No newline at end of file diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs index b9d5dcf50..eebb14ca0 100644 --- a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs +++ b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs @@ -1,11 +1,11 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System.Collections.Generic; -using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using Azure.Messaging.EventHubs; using Azure.Storage.Queues.Models; +using Microsoft.Azure.Functions.SdkGeneratorTests.Helpers; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Sdk.Generators; using Microsoft.CodeAnalysis.CSharp; @@ -19,23 +19,6 @@ namespace Microsoft.Azure.Functions.SdkGeneratorTests { public partial class FunctionExecutorGeneratorTests { - // A super set of assemblies we need for all tests in the file. - private readonly Assembly[] _referencedAssemblies = new[] - { - typeof(HttpTriggerAttribute).Assembly, - typeof(FunctionAttribute).Assembly, - typeof(QueueTriggerAttribute).Assembly, - typeof(EventHubTriggerAttribute).Assembly, - typeof(QueueMessage).Assembly, - typeof(EventData).Assembly, - typeof(BlobInputAttribute).Assembly, - typeof(ServiceProviderServiceExtensions).Assembly, - typeof(ILogger).Assembly, - typeof(IConfiguration).Assembly, - typeof(HostBuilder).Assembly, - typeof(IHostBuilder).Assembly - }; - [Theory] [InlineData(LanguageVersion.CSharp7_3)] [InlineData(LanguageVersion.CSharp8)] @@ -45,7 +28,8 @@ public partial class FunctionExecutorGeneratorTests [InlineData(LanguageVersion.Latest)] public async Task FunctionsFromMultipleClasses(LanguageVersion languageVersion) { - const string inputSourceCode = @" + await Test( + @" using System; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; @@ -103,81 +87,7 @@ public void Run2([QueueTrigger(""myqueue-items"")] string message) } } } -"; - var expectedOutput = $@"// -using System; -using System.Threading.Tasks; -using System.Collections.Generic; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Context.Features; -using Microsoft.Azure.Functions.Worker.Invocation; -namespace TestProject -{{ - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor - {{ - private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; - private readonly Dictionary types = new Dictionary() - {{ - {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"") }}, - {{ ""MyCompany.MyHttpTriggers2"", Type.GetType(""MyCompany.MyHttpTriggers2, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"") }}, - {{ ""MyCompany.QueueTriggers"", Type.GetType(""MyCompany.QueueTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"") }} - }}; - - public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) - {{ - _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); - }} - - /// - public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) - {{ - var inputBindingFeature = context.Features.Get(); - var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); - var inputArguments = inputBindingResult.Values; - - if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyHttpTriggers.Foo"", StringComparison.Ordinal)) - {{ - var instanceType = types[""MyCompany.MyHttpTriggers""]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; - context.GetInvocationResult().Value = i.Foo((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyHttpTriggers2.Bar"", StringComparison.Ordinal)) - {{ - var instanceType = types[""MyCompany.MyHttpTriggers2""]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers2; - context.GetInvocationResult().Value = i.Bar((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.Foo.MyAsyncStaticMethod"", StringComparison.Ordinal)) - {{ - context.GetInvocationResult().Value = await global::MyCompany.Foo.MyAsyncStaticMethod((string)inputArguments[0]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.QueueTriggers.Run"", StringComparison.Ordinal)) - {{ - var instanceType = types[""MyCompany.QueueTriggers""]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.QueueTriggers; - i.Run((global::Azure.Storage.Queues.Models.QueueMessage)inputArguments[0]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.QueueTriggers.Run2"", StringComparison.Ordinal)) - {{ - var instanceType = types[""MyCompany.QueueTriggers""]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.QueueTriggers; - i.Run2((string)inputArguments[0]); - }} - }} - }} -{GetExpectedExtensionMethodCode()} -}}".Replace("'", "\""); - - await TestHelpers.RunTestAsync( - _referencedAssemblies, - inputSourceCode, - Constants.FileNames.GeneratedFunctionExecutor, - expectedOutput, - languageVersion: languageVersion); +", languageVersion); } [Theory] @@ -189,7 +99,7 @@ public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunction [InlineData(LanguageVersion.Latest)] public async Task MultipleFunctionsDependencyInjection(LanguageVersion languageVersion) { - string inputSourceCode = @" + await Test(@" using System.Net; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; @@ -219,71 +129,8 @@ public HttpResponseData Run2([HttpTrigger(AuthorizationLevel.User, ""get"")] Htt private int Foo(int x) => x * x; } } -"; - - var expectedOutput = @$"// -using System; -using System.Threading.Tasks; -using System.Collections.Generic; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Context.Features; -using Microsoft.Azure.Functions.Worker.Invocation; -namespace MyCompany.MyProject.MyApp -{{ - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor - {{ - private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; - private readonly Dictionary types = new Dictionary() - {{ - {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"") }} - }}; - - public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) - {{ - _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); - }} - - /// - public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) - {{ - var inputBindingFeature = context.Features.Get(); - var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); - var inputArguments = inputBindingResult.Values; - - if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyHttpTriggers.Run1"", StringComparison.Ordinal)) - {{ - var instanceType = types[""MyCompany.MyHttpTriggers""]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; - context.GetInvocationResult().Value = i.Run1((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyHttpTriggers.Run2"", StringComparison.Ordinal)) - {{ - var instanceType = types[""MyCompany.MyHttpTriggers""]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; - context.GetInvocationResult().Value = i.Run2((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); - }} - }} - }} -{GetExpectedExtensionMethodCode()} -}}".Replace("'", "\""); - - // override the namespace value for generated types using msbuild property. - var buildPropertiesDict = new Dictionary() - { - { Constants.BuildProperties.GeneratedCodeNamespace, "MyCompany.MyProject.MyApp"} - }; - - await TestHelpers.RunTestAsync( - _referencedAssemblies, - inputSourceCode, - Constants.FileNames.GeneratedFunctionExecutor, - expectedOutput, - buildPropertiesDictionary: buildPropertiesDict, - languageVersion: languageVersion); +", languageVersion, +generatedCodeNamespace: "MyCompany.MyProject.MyApp"); } [Theory] @@ -295,163 +142,81 @@ await TestHelpers.RunTestAsync( [InlineData(LanguageVersion.Latest)] public async Task StaticMethods(LanguageVersion languageVersion) { - var inputSourceCode = @" -using System; -using System.Threading.Tasks; -using Microsoft.Azure.Functions.Worker; -using Azure.Messaging.EventHubs; -using System.IO; - -namespace FunctionApp26 -{ - public static class MyQTriggers - { - [Function(""ProcessOrder1"")] - public static Task MyTaskStaticMethod([QueueTrigger(""foo"")] string q) - { - return Task.CompletedTask; - } - [Function(""ProcessOrder2"")] - public static async Task MyAsyncStaticMethod([QueueTrigger(""foo"")] string q) => q; - - [Function(""ProcessOrder3"")] - public static void MyVoidStaticMethod([QueueTrigger(""foo"")] string q) - { - } - [Function(""ProcessOrder4"")] - public static async Task MyAsyncStaticMethodWithReturn( - [QueueTrigger(""foo"")] string q, - [BlobInput(""test-samples/sample1.txt"")] string myBlob) - { - return q.Length + myBlob.Length; - } - [Function(""ProcessOrder5"")] - public static async ValueTask MyValueTaskOfTStaticAsyncMethod([QueueTrigger(""foo"")] string q) - { - return q; - } - [Function(""ProcessOrder6"")] - public static ValueTask MyValueTaskStaticAsyncMethod2([QueueTrigger(""foo"")] string q) - { - return ValueTask.CompletedTask; - } - } - public class BlobTriggers - { - [Function(nameof(BlobTriggers))] - public static async Task Run([BlobTrigger(""items/{name}"", Connection = ""ConStr"")] Stream stream, string name) - { - using (var blobStreamReader = new StreamReader(stream)) - { - var content = await blobStreamReader.ReadToEndAsync(); - } - } - } - public class EventHubTriggers - { - [Function(""Run1"")] - public static void Run1([EventHubTrigger(""items"", Connection = ""EventHubConnection"")] EventData[] data) - { - } - [Function(nameof(Run2))] - [EventHubOutput(""dest"", Connection = ""EHConnection"")] - public static string Run2([EventHubTrigger(""queue"", Connection = ""EventHubConnection"", IsBatched = false)] EventData eventData) - { - return eventData.MessageId; - } - [Function(""RunAsync1"")] - public static Task RunAsync1([EventHubTrigger(""items"", Connection = ""Con"")] EventData[] data) - { - return Task.CompletedTask; - } - [Function(""RunAsync2"")] - public static async Task RunAsync2([EventHubTrigger(""items"", Connection = ""Con"")] EventData[] data) => await Task.Delay(10); - } -}".Replace("'", "\""); - var expectedOutput = @$"// -using System; -using System.Threading.Tasks; -using System.Collections.Generic; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Context.Features; -using Microsoft.Azure.Functions.Worker.Invocation; -namespace TestProject -{{ - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor - {{ - private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; - - public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) - {{ - _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); - }} - - /// - public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) - {{ - var inputBindingFeature = context.Features.Get(); - var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); - var inputArguments = inputBindingResult.Values; - - if (string.Equals(context.FunctionDefinition.EntryPoint, ""FunctionApp26.MyQTriggers.MyTaskStaticMethod"", StringComparison.Ordinal)) - {{ - await global::FunctionApp26.MyQTriggers.MyTaskStaticMethod((string)inputArguments[0]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""FunctionApp26.MyQTriggers.MyAsyncStaticMethod"", StringComparison.Ordinal)) - {{ - context.GetInvocationResult().Value = await global::FunctionApp26.MyQTriggers.MyAsyncStaticMethod((string)inputArguments[0]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""FunctionApp26.MyQTriggers.MyVoidStaticMethod"", StringComparison.Ordinal)) - {{ - global::FunctionApp26.MyQTriggers.MyVoidStaticMethod((string)inputArguments[0]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""FunctionApp26.MyQTriggers.MyAsyncStaticMethodWithReturn"", StringComparison.Ordinal)) - {{ - context.GetInvocationResult().Value = await global::FunctionApp26.MyQTriggers.MyAsyncStaticMethodWithReturn((string)inputArguments[0], (string)inputArguments[1]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""FunctionApp26.MyQTriggers.MyValueTaskOfTStaticAsyncMethod"", StringComparison.Ordinal)) - {{ - context.GetInvocationResult().Value = await global::FunctionApp26.MyQTriggers.MyValueTaskOfTStaticAsyncMethod((string)inputArguments[0]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""FunctionApp26.MyQTriggers.MyValueTaskStaticAsyncMethod2"", StringComparison.Ordinal)) - {{ - await global::FunctionApp26.MyQTriggers.MyValueTaskStaticAsyncMethod2((string)inputArguments[0]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""FunctionApp26.BlobTriggers.Run"", StringComparison.Ordinal)) - {{ - await global::FunctionApp26.BlobTriggers.Run((global::System.IO.Stream)inputArguments[0], (string)inputArguments[1]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""FunctionApp26.EventHubTriggers.Run1"", StringComparison.Ordinal)) - {{ - global::FunctionApp26.EventHubTriggers.Run1((global::Azure.Messaging.EventHubs.EventData[])inputArguments[0]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""FunctionApp26.EventHubTriggers.Run2"", StringComparison.Ordinal)) - {{ - context.GetInvocationResult().Value = global::FunctionApp26.EventHubTriggers.Run2((global::Azure.Messaging.EventHubs.EventData)inputArguments[0]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""FunctionApp26.EventHubTriggers.RunAsync1"", StringComparison.Ordinal)) - {{ - await global::FunctionApp26.EventHubTriggers.RunAsync1((global::Azure.Messaging.EventHubs.EventData[])inputArguments[0]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""FunctionApp26.EventHubTriggers.RunAsync2"", StringComparison.Ordinal)) - {{ - await global::FunctionApp26.EventHubTriggers.RunAsync2((global::Azure.Messaging.EventHubs.EventData[])inputArguments[0]); - }} - }} - }} -{GetExpectedExtensionMethodCode()} -}}".Replace("'", "\""); - - await TestHelpers.RunTestAsync( - _referencedAssemblies, - inputSourceCode, - Constants.FileNames.GeneratedFunctionExecutor, - expectedOutput, - languageVersion: languageVersion); + await Test(""" + using System; + using System.Threading.Tasks; + using Microsoft.Azure.Functions.Worker; + using Azure.Messaging.EventHubs; + using System.IO; + + namespace FunctionApp26 + { + public static class MyQTriggers + { + [Function("ProcessOrder1")] + public static Task MyTaskStaticMethod([QueueTrigger("foo")] string q) + { + return Task.CompletedTask; + } + [Function("ProcessOrder2")] + public static async Task MyAsyncStaticMethod([QueueTrigger("foo")] string q) => q; + + [Function("ProcessOrder3")] + public static void MyVoidStaticMethod([QueueTrigger("foo")] string q) + { + } + [Function("ProcessOrder4")] + public static async Task MyAsyncStaticMethodWithReturn( + [QueueTrigger("foo")] string q, + [BlobInput("test-samples/sample1.txt")] string myBlob) + { + return q.Length + myBlob.Length; + } + [Function("ProcessOrder5")] + public static async ValueTask MyValueTaskOfTStaticAsyncMethod([QueueTrigger("foo")] string q) + { + return q; + } + [Function("ProcessOrder6")] + public static ValueTask MyValueTaskStaticAsyncMethod2([QueueTrigger("foo")] string q) + { + return ValueTask.CompletedTask; + } + } + public class BlobTriggers + { + [Function(nameof(BlobTriggers))] + public static async Task Run([BlobTrigger("items/{name}", Connection = "ConStr")] Stream stream, string name) + { + using (var blobStreamReader = new StreamReader(stream)) + { + var content = await blobStreamReader.ReadToEndAsync(); + } + } + } + public class EventHubTriggers + { + [Function("Run1")] + public static void Run1([EventHubTrigger("items", Connection = "EventHubConnection")] EventData[] data) + { + } + [Function(nameof(Run2))] + [EventHubOutput("dest", Connection = "EHConnection")] + public static string Run2([EventHubTrigger("queue", Connection = "EventHubConnection", IsBatched = false)] EventData eventData) + { + return eventData.MessageId; + } + [Function("RunAsync1")] + public static Task RunAsync1([EventHubTrigger("items", Connection = "Con")] EventData[] data) + { + return Task.CompletedTask; + } + [Function("RunAsync2")] + public static async Task RunAsync2([EventHubTrigger("items", Connection = "Con")] EventData[] data) => await Task.Delay(10); + } + } + """, + languageVersion); } [Theory] @@ -469,82 +234,26 @@ public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunction [InlineData(false, LanguageVersion.Latest)] public async Task VerifyAutoConfigureStartupTypeEmitted(bool includeAutoStartupType, LanguageVersion languageVersion) { - string inputSourceCode = @" -using System.Net; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Http; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; - -namespace MyCompany -{ - public class MyHttpTriggers - { - [Function(""Function1"")] - public HttpResponseData Run1([HttpTrigger(AuthorizationLevel.User, ""get"")] HttpRequestData r) - { - return r.CreateResponse(System.Net.HttpStatusCode.OK); - } - } -} -"; - - var expectedOutput = @$"// -using System; -using System.Threading.Tasks; -using System.Collections.Generic; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Context.Features; -using Microsoft.Azure.Functions.Worker.Invocation; -namespace TestProject -{{ - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor - {{ - private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; - private readonly Dictionary types = new Dictionary() - {{ - {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"") }} - }}; - - public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) - {{ - _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); - }} - - /// - public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) - {{ - var inputBindingFeature = context.Features.Get(); - var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); - var inputArguments = inputBindingResult.Values; - - if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyHttpTriggers.Run1"", StringComparison.Ordinal)) - {{ - var instanceType = types[""MyCompany.MyHttpTriggers""]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; - context.GetInvocationResult().Value = i.Run1((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); - }} - }} - }} -{GetExpectedExtensionMethodCode(includeAutoStartupType: includeAutoStartupType)} -}}".Replace("'", "\""); - - var buildPropertiesDict = new Dictionary() - { - { Constants.BuildProperties.AutoRegisterGeneratedFunctionsExecutor, includeAutoStartupType.ToString()} - }; - - await TestHelpers.RunTestAsync( - _referencedAssemblies, - inputSourceCode, - Constants.FileNames.GeneratedFunctionExecutor, - expectedOutput, - buildPropertiesDictionary: buildPropertiesDict, - languageVersion: languageVersion); + await Test(""" + using System.Net; + using Microsoft.Azure.Functions.Worker; + using Microsoft.Azure.Functions.Worker.Http; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.Logging; + + namespace MyCompany + { + public class MyHttpTriggers + { + [Function("Function1")] + public HttpResponseData Run1([HttpTrigger(AuthorizationLevel.User, "get")] HttpRequestData r) + { + return r.CreateResponse(System.Net.HttpStatusCode.OK); + } + } + } + """, + languageVersion); } [Theory] @@ -556,86 +265,33 @@ await TestHelpers.RunTestAsync( [InlineData(LanguageVersion.Latest)] public async Task ClassWithSameNameAsNamespace(LanguageVersion languageVersion) { - const string inputSourceCode = @" -using System; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Azure.Storage.Queues.Models; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Http; -using Microsoft.Extensions.Logging; -namespace TestProject -{ - public class TestProject - { - [Function(""FunctionA"")] - public HttpResponseData Foo([HttpTrigger(AuthorizationLevel.User, ""get"")] HttpRequestData r, FunctionContext c) - { - return r.CreateResponse(System.Net.HttpStatusCode.OK); - } - - [Function(""FunctionB"")] - public static HttpResponseData FooStatic([HttpTrigger(AuthorizationLevel.User, ""get"")] HttpRequestData r, FunctionContext c) - { - return r.CreateResponse(System.Net.HttpStatusCode.OK); - } - } -} -"; - var expectedOutput = $@"// -using System; -using System.Threading.Tasks; -using System.Collections.Generic; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Context.Features; -using Microsoft.Azure.Functions.Worker.Invocation; -namespace TestProject -{{ - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor - {{ - private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; - private readonly Dictionary types = new Dictionary() - {{ - {{ ""TestProject.TestProject"", Type.GetType(""TestProject.TestProject, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"") }} - }}; - - public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) - {{ - _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); - }} - - /// - public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) - {{ - var inputBindingFeature = context.Features.Get(); - var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); - var inputArguments = inputBindingResult.Values; - - if (string.Equals(context.FunctionDefinition.EntryPoint, ""TestProject.TestProject.Foo"", StringComparison.Ordinal)) - {{ - var instanceType = types[""TestProject.TestProject""]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::TestProject.TestProject; - context.GetInvocationResult().Value = i.Foo((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""TestProject.TestProject.FooStatic"", StringComparison.Ordinal)) - {{ - context.GetInvocationResult().Value = global::TestProject.TestProject.FooStatic((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); - }} - }} - }} -{GetExpectedExtensionMethodCode()} -}}".Replace("'", "\""); - - await TestHelpers.RunTestAsync( - _referencedAssemblies, - inputSourceCode, - Constants.FileNames.GeneratedFunctionExecutor, - expectedOutput, - languageVersion: languageVersion); + await Test(""" + using System; + using System.Threading.Tasks; + using Microsoft.Extensions.Hosting; + using Azure.Storage.Queues.Models; + using Microsoft.Azure.Functions.Worker; + using Microsoft.Azure.Functions.Worker.Http; + using Microsoft.Extensions.Logging; + namespace TestProject + { + public class TestProject + { + [Function("FunctionA")] + public HttpResponseData Foo([HttpTrigger(AuthorizationLevel.User, "get")] HttpRequestData r, FunctionContext c) + { + return r.CreateResponse(System.Net.HttpStatusCode.OK); + } + + [Function("FunctionB")] + public static HttpResponseData FooStatic([HttpTrigger(AuthorizationLevel.User, "get")] HttpRequestData r, FunctionContext c) + { + return r.CreateResponse(System.Net.HttpStatusCode.OK); + } + } + } + """, + languageVersion); } [Theory] @@ -647,147 +303,62 @@ public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunction [InlineData(LanguageVersion.Latest)] public async Task FunctionsWithSameNameExceptForCasing(LanguageVersion languageVersion) { - const string inputSourceCode = @" -using System; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Azure.Storage.Queues.Models; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Http; -using Microsoft.Extensions.Logging; -namespace MyCompany -{ - public class MyHttpTriggers - { - [Function(""FunctionA"")] - public HttpResponseData Hello([HttpTrigger(AuthorizationLevel.User, ""get"")] HttpRequestData r, FunctionContext c) - { - return r.CreateResponse(System.Net.HttpStatusCode.OK); - } - - [Function(""FunctionB"")] - public static HttpResponseData HELLO([HttpTrigger(AuthorizationLevel.User, ""get"")] HttpRequestData r, FunctionContext c) - { - return r.CreateResponse(System.Net.HttpStatusCode.OK); - } - } -} -"; - var expectedOutput = $@"// -using System; -using System.Threading.Tasks; -using System.Collections.Generic; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Context.Features; -using Microsoft.Azure.Functions.Worker.Invocation; -namespace TestProject -{{ - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor - {{ - private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; - private readonly Dictionary types = new Dictionary() - {{ - {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"") }} - }}; - - public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) - {{ - _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); - }} - - /// - public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) - {{ - var inputBindingFeature = context.Features.Get(); - var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); - var inputArguments = inputBindingResult.Values; - - if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyHttpTriggers.Hello"", StringComparison.Ordinal)) - {{ - var instanceType = types[""MyCompany.MyHttpTriggers""]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; - context.GetInvocationResult().Value = i.Hello((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyHttpTriggers.HELLO"", StringComparison.Ordinal)) - {{ - context.GetInvocationResult().Value = global::MyCompany.MyHttpTriggers.HELLO((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); - }} - }} - }} -{GetExpectedExtensionMethodCode()} -}}".Replace("'", "\""); - - await TestHelpers.RunTestAsync( - _referencedAssemblies, - inputSourceCode, - Constants.FileNames.GeneratedFunctionExecutor, - expectedOutput, - languageVersion: languageVersion); + await Test(""" + using System; + using System.Threading.Tasks; + using Microsoft.Extensions.Hosting; + using Azure.Storage.Queues.Models; + using Microsoft.Azure.Functions.Worker; + using Microsoft.Azure.Functions.Worker.Http; + using Microsoft.Extensions.Logging; + namespace MyCompany + { + public class MyHttpTriggers + { + [Function("FunctionA")] + public HttpResponseData Hello([HttpTrigger(AuthorizationLevel.User, "get")] HttpRequestData r, FunctionContext c) + { + return r.CreateResponse(System.Net.HttpStatusCode.OK); + } + + [Function("FunctionB")] + public static HttpResponseData HELLO([HttpTrigger(AuthorizationLevel.User, "get")] HttpRequestData r, FunctionContext c) + { + return r.CreateResponse(System.Net.HttpStatusCode.OK); + } + } + } + """, + languageVersion); } - private static string GetExpectedExtensionMethodCode(bool includeAutoStartupType = false) + private async Task Test( + string sourceCode, + LanguageVersion languageVersion, + string? generatedCodeNamespace = null, + [CallerMemberName] string callerName = "") { - if (includeAutoStartupType) - { - return """ - - /// - /// Extension methods to enable registration of the custom implementation generated for the current worker. - /// - public static class FunctionExecutorHostBuilderExtensions - { - /// - /// Configures an optimized function executor to the invocation pipeline. - /// - public static IHostBuilder ConfigureGeneratedFunctionExecutor(this IHostBuilder builder) - { - return builder.ConfigureServices(s => - { - s.AddSingleton(); - }); - } - } - /// - /// Auto startup class to register the custom implementation generated for the current worker. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] - public class FunctionExecutorAutoStartup : global::Microsoft.Azure.Functions.Worker.IAutoConfigureStartup - { - /// - /// Configures the to use the custom implementation generated for the current worker. - /// - /// The instance to use for service registration. - public void Configure(IHostBuilder hostBuilder) - { - hostBuilder.ConfigureGeneratedFunctionExecutor(); - } - } - """; - } - - return """ - - /// - /// Extension methods to enable registration of the custom implementation generated for the current worker. - /// - public static class FunctionExecutorHostBuilderExtensions - { - /// - /// Configures an optimized function executor to the invocation pipeline. - /// - public static IHostBuilder ConfigureGeneratedFunctionExecutor(this IHostBuilder builder) - { - return builder.ConfigureServices(s => - { - s.AddSingleton(); - }); - } - } - """; + await new FunctionExecutorGenerator() + .RunAndVerify( + sourceCode, + new[] + { + typeof(HttpTriggerAttribute).Assembly, + typeof(FunctionAttribute).Assembly, + typeof(QueueTriggerAttribute).Assembly, + typeof(EventHubTriggerAttribute).Assembly, + typeof(QueueMessage).Assembly, + typeof(EventData).Assembly, + typeof(BlobInputAttribute).Assembly, + typeof(ServiceProviderServiceExtensions).Assembly, + typeof(ILogger).Assembly, + typeof(IConfiguration).Assembly, + typeof(HostBuilder).Assembly, + typeof(IHostBuilder).Assembly + }, + languageVersion: languageVersion, + generatedCodeNamespace: generatedCodeNamespace, + callerName: callerName); } } } diff --git a/test/Sdk.Generator.Tests/Helpers/SourceGeneratorVerifyExtensions.cs b/test/Sdk.Generator.Tests/Helpers/SourceGeneratorVerifyExtensions.cs index 889e9c832..e42cc2092 100644 --- a/test/Sdk.Generator.Tests/Helpers/SourceGeneratorVerifyExtensions.cs +++ b/test/Sdk.Generator.Tests/Helpers/SourceGeneratorVerifyExtensions.cs @@ -88,7 +88,7 @@ public static async Task RunAndVerify( await VerifyGeneratedCode(generateResult, callerFileName, callerName); } - private static async Task VerifyGeneratedCode(GeneratorDriver generateResult, string callerFileName, string callerName) + private static VerifyTests.SettingsTask VerifyGeneratedCode(GeneratorDriver generateResult, string callerFileName, string callerName) { var settings = Verifier .Verify(generateResult) @@ -101,7 +101,7 @@ private static async Task VerifyGeneratedCode(GeneratorDriver generateResult, st .UseFileName($"{Path.GetFileNameWithoutExtension(callerFileName)}.{callerName}"); } - await settings; + return settings; } private static AnalyzerConfigOptions CreateAnalyzerOptions(