Skip to content

Commit c2c435f

Browse files
committed
Fixing function signature type resolution
1 parent 54a976a commit c2c435f

File tree

8 files changed

+117
-18
lines changed

8 files changed

+117
-18
lines changed

src/WebJobs.Script/Description/DotNet/Compilation/CSharp/CSharpCompilation.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public FunctionSignature GetEntryPointSignature(IFunctionEntryPointResolver entr
5353
bool hasLocalTypeReferences = HasLocalTypeReferences(entryPointReference);
5454
var functionParameters = entryPointReference.Parameters.Select(p => new FunctionParameter(p.Name, GetFullTypeName(p.Type), p.IsOptional, p.RefKind));
5555

56-
return new FunctionSignature(entryPointReference.ContainingType.Name, entryPointReference.Name,
56+
return new FunctionSignature(entryPointReference.ContainingType.ToDisplayString(), entryPointReference.Name,
5757
ImmutableArray.CreateRange(functionParameters.ToArray()), GetFullTypeName(entryPointReference.ReturnType), hasLocalTypeReferences);
5858
}
5959

src/WebJobs.Script/Description/DotNet/Compilation/FSharp/FSharpCompilation.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public FunctionSignature GetEntryPointSignature(IFunctionEntryPointResolver entr
6565
// For F#, we always set this to true for now.
6666
bool hasLocalTypeReference = true;
6767

68-
var signature = new FunctionSignature(entryPointReference.DeclaringType.Name, entryPointReference.Name,
68+
var signature = new FunctionSignature(entryPointReference.DeclaringType.FullName, entryPointReference.Name,
6969
parameters.ToImmutableArray(), entryPointReference.ReturnType.Name, hasLocalTypeReference);
7070

7171
return signature;

src/WebJobs.Script/Description/DotNet/Compilation/Raw/RawAssemblyCompilation.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public FunctionSignature GetEntryPointSignature(IFunctionEntryPointResolver entr
6060

6161
var functionParameters = method.GetParameters().Select(p => new FunctionParameter(p.Name, p.ParameterType.FullName, p.IsOptional, GetParameterRefKind(p)));
6262

63-
return new FunctionSignature(method.ReflectedType.Name, method.Name, ImmutableArray.CreateRange(functionParameters.ToArray()), method.ReturnType.Name, hasLocalTypeReference: false);
63+
return new FunctionSignature(method.ReflectedType.FullName, method.Name, ImmutableArray.CreateRange(functionParameters.ToArray()), method.ReturnType.Name, hasLocalTypeReference: false);
6464
}
6565

6666
private static RefKind GetParameterRefKind(ParameterInfo parameter)

src/WebJobs.Script/Description/DotNet/FunctionSignature.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public MethodInfo GetMethod(Assembly assembly)
5050
}
5151

5252
return assembly.DefinedTypes
53-
.FirstOrDefault(t => string.Compare(t.Name, ParentTypeName, StringComparison.Ordinal) == 0)
53+
.FirstOrDefault(t => string.Compare(t.FullName, ParentTypeName, StringComparison.Ordinal) == 0)
5454
?.GetMethod(MethodName);
5555
}
5656

test/WebJobs.Script.Tests.Integration/Properties/Resources.Designer.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/WebJobs.Script.Tests.Integration/Properties/Resources.resx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@
151151
<data name="DotNetFunctionJson" xml:space="preserve">
152152
<value>{{
153153
"scriptFile":"{0}",
154-
"entryPoint": "TestFunction.Function.Run",
154+
"entryPoint": "{1}",
155155
"bindings": [
156156
{{
157157
"type": "httpTrigger",
@@ -199,6 +199,22 @@ namespace TestFunction
199199
return "Hello";
200200
}
201201
}
202+
}
203+
204+
namespace Test.Function1
205+
{
206+
public class TestFunction
207+
{
208+
public static string TestMethod(HttpRequestMessage req) =&gt; "Function1";
209+
}
210+
}
211+
212+
namespace Test.Function2
213+
{
214+
public class TestFunction
215+
{
216+
public static string TestMethod(HttpRequestMessage req) =&gt; "Function2";
217+
}
202218
}</value>
203219
</data>
204220
</root>

test/WebJobs.Script.Tests.Integration/RawAssemblyEndToEndTests.cs

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Linq;
88
using System.Net;
99
using System.Net.Http;
10+
using System.Net.Http.Headers;
1011
using System.Threading;
1112
using System.Threading.Tasks;
1213
using Microsoft.Azure.WebJobs.Host;
@@ -26,28 +27,30 @@ public RawAssemblyEndToEndTests(TestFixture fixture) : base(fixture)
2627
[Fact]
2728
public async Task Invoking_DotNetFunction()
2829
{
29-
await InvokeDotNetFunction("DotNetFunction");
30+
await InvokeDotNetFunction("DotNetFunction", "Hello from .NET");
3031
}
3132

3233
[Fact]
3334
public async Task Invoking_DotNetFunctionShared()
3435
{
35-
await InvokeDotNetFunction("DotNetFunctionShared");
36+
await InvokeDotNetFunction("DotNetFunctionShared", "Hello from .NET");
3637
}
3738

38-
public async Task InvokeDotNetFunction(string functionName)
39+
public async Task InvokeDotNetFunction(string functionName, string expectedResult)
3940
{
4041
var request = new HttpRequestMessage(HttpMethod.Get, "http://functions/myfunc");
4142
Dictionary<string, object> arguments = new Dictionary<string, object>()
4243
{
4344
{ "req", request }
4445
};
46+
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
47+
request.SetConfiguration(Fixture.RequestConfiguration);
4548

4649
await Fixture.Host.CallAsync(functionName, arguments);
4750

4851
HttpResponseMessage response = (HttpResponseMessage)request.Properties[ScriptConstants.AzureFunctionsHttpResponseKey];
4952

50-
Assert.Equal("Hello from .NET", await response.Content.ReadAsStringAsync());
53+
Assert.Equal(expectedResult, await response.Content.ReadAsStringAsync());
5154
}
5255

5356
[Fact]
@@ -66,16 +69,27 @@ public void AssemblyChange_TriggersEnvironmentShutdown()
6669
Assert.True(eventSet, "Shutdown was not called when assembly changes were made.");
6770
}
6871

72+
[Fact]
73+
public async Task Invoke_WithSameTypeNames_InvokesExpectedMethod()
74+
{
75+
await InvokeDotNetFunction("Function1", "Function1");
76+
await InvokeDotNetFunction("Function2", "Function2");
77+
}
78+
6979
public class TestFixture : EndToEndTestFixture
7080
{
7181
private const string ScriptRoot = @"TestScripts\DotNet";
72-
private static readonly string FunctionPath;
82+
private static readonly string Function1Path;
83+
private static readonly string Function2Path;
84+
private static readonly string Function3Path;
7385
private static readonly string FunctionSharedPath;
7486
private static readonly string FunctionSharedBinPath;
7587

7688
static TestFixture()
7789
{
78-
FunctionPath = Path.Combine(ScriptRoot, "DotNetFunction");
90+
Function1Path = Path.Combine(ScriptRoot, "DotNetFunction");
91+
Function2Path = Path.Combine(ScriptRoot, "Function1");
92+
Function3Path = Path.Combine(ScriptRoot, "Function2");
7993
FunctionSharedPath = Path.Combine(ScriptRoot, "DotNetFunctionShared");
8094
FunctionSharedBinPath = Path.Combine(ScriptRoot, "DotNetFunctionSharedBin");
8195
CreateFunctionAssembly();
@@ -92,14 +106,18 @@ public override void Dispose()
92106
base.Dispose();
93107

94108
Task.WaitAll(
95-
FileUtility.DeleteDirectoryAsync(FunctionPath, true),
109+
FileUtility.DeleteDirectoryAsync(Function1Path, true),
110+
FileUtility.DeleteDirectoryAsync(Function2Path, true),
111+
FileUtility.DeleteDirectoryAsync(Function3Path, true),
96112
FileUtility.DeleteDirectoryAsync(FunctionSharedPath, true),
97113
FileUtility.DeleteDirectoryAsync(FunctionSharedBinPath, true));
98114
}
99115

100116
private static void CreateFunctionAssembly()
101117
{
102-
Directory.CreateDirectory(FunctionPath);
118+
Directory.CreateDirectory(Function1Path);
119+
Directory.CreateDirectory(Function2Path);
120+
Directory.CreateDirectory(Function3Path);
103121
Directory.CreateDirectory(FunctionSharedBinPath);
104122
Directory.CreateDirectory(FunctionSharedPath);
105123

@@ -112,17 +130,19 @@ private static void CreateFunctionAssembly()
112130
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location),
113131
MetadataReference.CreateFromFile(typeof(object).Assembly.Location));
114132

115-
compilation.Emit(Path.Combine(FunctionPath, "DotNetFunctionAssembly.dll"));
133+
compilation.Emit(Path.Combine(Function1Path, "DotNetFunctionAssembly.dll"));
116134
compilation.Emit(SharedAssemblyPath);
117135

118-
CreateFunctionMetadata(FunctionPath, "DotNetFunctionAssembly.dll");
136+
CreateFunctionMetadata(Function1Path, "DotNetFunctionAssembly.dll");
137+
CreateFunctionMetadata(Function2Path, $@"..\\{Path.GetFileName(FunctionSharedBinPath)}\\DotNetFunctionSharedAssembly.dll", "Test.Function1.TestFunction.TestMethod");
138+
CreateFunctionMetadata(Function3Path, $@"..\\{Path.GetFileName(FunctionSharedBinPath)}\\DotNetFunctionSharedAssembly.dll", "Test.Function2.TestFunction.TestMethod");
119139
CreateFunctionMetadata(FunctionSharedPath, $@"..\\{Path.GetFileName(FunctionSharedBinPath)}\\DotNetFunctionSharedAssembly.dll");
120140
}
121141

122-
private static void CreateFunctionMetadata(string path, string scriptFilePath)
142+
private static void CreateFunctionMetadata(string path, string scriptFilePath, string entrypoint = "TestFunction.Function.Run")
123143
{
124144
File.WriteAllText(Path.Combine(path, "function.json"),
125-
string.Format(Resources.DotNetFunctionJson, scriptFilePath));
145+
string.Format(Resources.DotNetFunctionJson, scriptFilePath, entrypoint));
126146
}
127147
}
128148
}

test/WebJobs.Script.Tests/Description/DotNet/CSharp/CSharpFunctionSignatureTests.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Collections.Generic;
6+
using System.Collections.Immutable;
7+
using System.IO;
58
using System.Linq;
9+
using System.Reflection;
10+
using System.Threading;
611
using Microsoft.Azure.WebJobs.Script.Description;
712
using Microsoft.CodeAnalysis;
813
using Microsoft.CodeAnalysis.CSharp;
14+
using WebJobs.Script.Tests;
915
using Xunit;
1016

1117
namespace Microsoft.Azure.WebJobs.Script.Tests
@@ -79,6 +85,63 @@ public static void Run(string id, out string output)
7985
Assert.NotEqual(signatures.Item1.GetHashCode(), signatures.Item2.GetHashCode());
8086
}
8187

88+
[Fact]
89+
public void GetMethod_ReturnsExpectedMethod()
90+
{
91+
var function1 = @"using System;
92+
namespace Test.Function1
93+
{
94+
public class Function
95+
{
96+
public static void Run(string identity, out string outputParam)
97+
{
98+
outputParam = nameof(Function1);
99+
}
100+
}
101+
}
102+
103+
namespace Test.Function2
104+
{
105+
public class Function
106+
{
107+
public static void Run(string identity, out string outputParam)
108+
{
109+
outputParam = nameof(Function2);
110+
}
111+
}
112+
}
113+
";
114+
115+
using (var path = new TempDirectory())
116+
{
117+
var tree = CSharpSyntaxTree.ParseText(function1);
118+
var references = new MetadataReference[] { MetadataReference.CreateFromFile(typeof(string).Assembly.Location) };
119+
120+
var compilation = CodeAnalysis.CSharp.CSharpCompilation.Create("TestAssembly", new[] { tree }, references: references)
121+
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
122+
123+
string assemblyPath = Path.Combine(path.Path, "TestAssembly.dll");
124+
compilation.Emit(assemblyPath);
125+
126+
Assembly assembly = Assembly.LoadFrom(assemblyPath);
127+
128+
var parameters = new List<FunctionParameter>
129+
{
130+
new FunctionParameter("identity", typeof(string).FullName, false, RefKind.None),
131+
new FunctionParameter("outputParam", typeof(string).FullName, false, RefKind.Out)
132+
};
133+
134+
var signature1 = new FunctionSignature("Test.Function1.Function", "Run", parameters.ToImmutableArray(), typeof(void).FullName, false);
135+
var signature2 = new FunctionSignature("Test.Function2.Function", "Run", parameters.ToImmutableArray(), typeof(void).FullName, false);
136+
137+
var method1 = signature1.GetMethod(assembly);
138+
var method2 = signature2.GetMethod(assembly);
139+
140+
Assert.Equal("Test.Function1.Function", method1.DeclaringType.FullName);
141+
Assert.Equal("Test.Function2.Function", method2.DeclaringType.FullName);
142+
}
143+
}
144+
82145
private Tuple<FunctionSignature, FunctionSignature> GetFunctionSignatures(string function1, string function2)
83146
{
84147
var tree1 = CSharpSyntaxTree.ParseText(function1, CSharpParseOptions.Default.WithKind(SourceCodeKind.Script));

0 commit comments

Comments
 (0)