Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Roslyn Source Generator Testing SDK to test interop source generators #84867

Merged
merged 19 commits into from
May 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
eb8e98a
Update references to Roslyn testing SDK and convert over our directed…
jkoritzinsky Apr 10, 2023
aab1da5
Add mechansim to test downlevel tfms with test harness and convert ou…
jkoritzinsky Apr 10, 2023
adfbff4
Change the AddDisableRuntimeMarshallingAttributeFixer test suite to u…
jkoritzinsky Apr 10, 2023
81cb761
Set up the correct options in an editorconfig for target framework ba…
jkoritzinsky Apr 11, 2023
d8027c2
Move over all tests other than CompileFails and the incremental testi…
jkoritzinsky Apr 11, 2023
c3418f7
Add the source generator test package to ComInterfaceGenerator.Unit.T…
jkoritzinsky Apr 11, 2023
2627e8d
Move over the small CompileFails theories.
jkoritzinsky Apr 12, 2023
1055a35
Add issue reference.
jkoritzinsky Apr 12, 2023
612432c
Move ComipleFails tests to the new Diagnostics test setup.
jkoritzinsky Apr 14, 2023
216de88
Convert over all no-diagnostic tests in the ComInterfaceGenerator tes…
jkoritzinsky Apr 17, 2023
44ee8af
Forward byref modifiers to the inner function pointer when using the …
jkoritzinsky Apr 17, 2023
374c917
Enable a set of directed tests for invalid marshal modes in the Compi…
jkoritzinsky Apr 18, 2023
dee4a06
Merge branch 'main' of github.com:dotnet/runtime into diagnostic-test…
jkoritzinsky Apr 18, 2023
625c6ac
Remove test markup from tests that don't use the unit testing apis
jkoritzinsky Apr 18, 2023
7cd2073
Fix multiple sources test.
jkoritzinsky Apr 18, 2023
c83364d
Add ActiveIssue for known failures on Mono that these tests now hit
jkoritzinsky Apr 21, 2023
a432e72
Merge branch 'main' of github.com:dotnet/runtime into diagnostic-test…
jkoritzinsky May 3, 2023
19b5c93
Undo change that shouldnt be in this branch
jkoritzinsky May 4, 2023
18a8bb0
Fix error messages in ComInterfaceGenerator tests.
jkoritzinsky May 4, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@
<GrpcToolsVersion>2.45.0</GrpcToolsVersion>
<!-- Uncomment to set a fixed version, else the latest is used -->
<!-- <SdkVersionForWorkloadTesting>8.0.100-alpha.1.23077.3</SdkVersionForWorkloadTesting> -->
<CompilerPlatformTestingVersion>1.1.2-beta1.22403.2</CompilerPlatformTestingVersion>
<CompilerPlatformTestingVersion>1.1.2-beta1.23205.1</CompilerPlatformTestingVersion>
<!-- Docs -->
<MicrosoftPrivateIntellisenseVersion>7.0.0-preview-20221010.1</MicrosoftPrivateIntellisenseVersion>
<!-- ILLink -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M
{
if (baseInterface is not null)
{
return Diagnostic.Create(GeneratorDiagnostics.MultipleComInterfaceBaseTypesAttribute, syntax.Identifier.GetLocation(), type.ToDisplayString());
return Diagnostic.Create(GeneratorDiagnostics.MultipleComInterfaceBaseTypes, syntax.Identifier.GetLocation(), type.ToDisplayString());
}
baseInterface = implemented;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public class Ids
isEnabledByDefault: true,
description: GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageDescription)));

public static readonly DiagnosticDescriptor MultipleComInterfaceBaseTypesAttribute =
public static readonly DiagnosticDescriptor MultipleComInterfaceBaseTypes =
new DiagnosticDescriptor(
Ids.MultipleComInterfaceBaseTypes,
GetResourceString(nameof(SR.MultipleComInterfaceBaseTypesTitle)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ private ParenthesizedExpressionSyntax CreateFunctionPointerExpression(
{
List<FunctionPointerParameterSyntax> functionPointerParameters = new();
var (paramList, retType, _) = _marshallers.GenerateTargetMethodSignatureData(_context);
functionPointerParameters.AddRange(paramList.Parameters.Select(p => FunctionPointerParameter(p.Type)));
functionPointerParameters.AddRange(paramList.Parameters.Select(p => FunctionPointerParameter(attributeLists: default, p.Modifiers, p.Type)));
functionPointerParameters.Add(FunctionPointerParameter(retType));

// ((delegate* unmanaged<...>)<untypedFunctionPointerExpression>)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.Interop.UnitTests;
using Microsoft.CodeAnalysis.Testing;
using Xunit;

using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier<Microsoft.Interop.VtableIndexStubGenerator>;

namespace ComInterfaceGenerator.Unit.Tests
{
public class CallingConventionForwarding
Expand All @@ -31,16 +31,12 @@ partial interface INativeAPI : IUnmanagedInterfaceType
void Method();
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
// Allow the Native nested type name to be missing in the pre-source-generator compilation
TestUtils.AssertPreSourceGeneratorCompilation(comp);

var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator());

var signature = await FindFunctionPointerInvocationSignature(newComp, "INativeAPI", "Method");

Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention);
Assert.Empty(signature.UnmanagedCallingConventionTypes);
await VerifySourceGeneratorAsync(source, "INativeAPI", "Method", (compilation, signature) =>
{
Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention);
Assert.Empty(signature.UnmanagedCallingConventionTypes);
});
}

[Fact]
Expand All @@ -59,16 +55,12 @@ partial interface INativeAPI : IUnmanagedInterfaceType
void Method();
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
// Allow the Native nested type name to be missing in the pre-source-generator compilation
TestUtils.AssertPreSourceGeneratorCompilation(comp);

var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator());

var signature = await FindFunctionPointerInvocationSignature(newComp, "INativeAPI", "Method");

Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention);
Assert.Equal(newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvSuppressGCTransition"), Assert.Single(signature.UnmanagedCallingConventionTypes), SymbolEqualityComparer.Default);
await VerifySourceGeneratorAsync(source, "INativeAPI", "Method", (newComp, signature) =>
{
Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention);
Assert.Equal(newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvSuppressGCTransition"), Assert.Single(signature.UnmanagedCallingConventionTypes), SymbolEqualityComparer.Default);
});
}

[Fact]
Expand All @@ -87,22 +79,19 @@ partial interface INativeAPI : IUnmanagedInterfaceType
void Method();
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
// Allow the Native nested type name to be missing in the pre-source-generator compilation
TestUtils.AssertPreSourceGeneratorCompilation(comp);

var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator());

var signature = await FindFunctionPointerInvocationSignature(newComp, "INativeAPI", "Method");

Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention);
Assert.Empty(signature.UnmanagedCallingConventionTypes);
await VerifySourceGeneratorAsync(source, "INativeAPI", "Method", (_, signature) =>
{
Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention);
Assert.Empty(signature.UnmanagedCallingConventionTypes);
});
}

[Fact]
public async Task SimpleUnmanagedCallConvAttributeForwarded()
{
string source = $$"""
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;

Expand All @@ -115,22 +104,19 @@ partial interface INativeAPI : IUnmanagedInterfaceType
void Method();
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
// Allow the Native nested type name to be missing in the pre-source-generator compilation
TestUtils.AssertPreSourceGeneratorCompilation(comp);

var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator());

var signature = await FindFunctionPointerInvocationSignature(newComp, "INativeAPI", "Method");

Assert.Equal(SignatureCallingConvention.CDecl, signature.CallingConvention);
Assert.Empty(signature.UnmanagedCallingConventionTypes);
await VerifySourceGeneratorAsync(source, "INativeAPI", "Method", (_, signature) =>
{
Assert.Equal(SignatureCallingConvention.CDecl, signature.CallingConvention);
Assert.Empty(signature.UnmanagedCallingConventionTypes);
});
}

[Fact]
public async Task ComplexUnmanagedCallConvAttributeForwarded()
{
string source = $$"""
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;

Expand All @@ -143,28 +129,25 @@ partial interface INativeAPI : IUnmanagedInterfaceType
void Method();
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
// Allow the Native nested type name to be missing in the pre-source-generator compilation
TestUtils.AssertPreSourceGeneratorCompilation(comp);

var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator());

var signature = await FindFunctionPointerInvocationSignature(newComp, "INativeAPI", "Method");

Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention);
Assert.Equal(new[]
await VerifySourceGeneratorAsync(source, "INativeAPI", "Method", (newComp, signature) =>
{
newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvCdecl"),
newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvMemberFunction"),
},
signature.UnmanagedCallingConventionTypes,
SymbolEqualityComparer.Default);
Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention);
Assert.Equal(new[]
{
newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvCdecl"),
newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvMemberFunction"),
},
signature.UnmanagedCallingConventionTypes,
SymbolEqualityComparer.Default);
});
}

[Fact]
public async Task ComplexUnmanagedCallConvAttributeWithSuppressGCTransitionForwarded()
{
string source = $$"""
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;

Expand All @@ -178,41 +161,67 @@ partial interface INativeAPI : IUnmanagedInterfaceType
void Method();
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
// Allow the Native nested type name to be missing in the pre-source-generator compilation
TestUtils.AssertPreSourceGeneratorCompilation(comp);

var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator());

var signature = await FindFunctionPointerInvocationSignature(newComp, "INativeAPI", "Method");
await VerifySourceGeneratorAsync(source, "INativeAPI", "Method", (newComp, signature) =>
{
Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention);
Assert.Equal(new[]
{
newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvSuppressGCTransition"),
newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvCdecl"),
newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvMemberFunction"),
},
signature.UnmanagedCallingConventionTypes,
SymbolEqualityComparer.Default);
});
}

Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention);
Assert.Equal(new[]
private static async Task VerifySourceGeneratorAsync(string source, string interfaceName, string methodName, Action<Compilation, IMethodSymbol> signatureValidator)
{
CallingConventionForwardingTest test = new(interfaceName, methodName, signatureValidator)
{
newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvSuppressGCTransition"),
newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvCdecl"),
newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvMemberFunction"),
},
signature.UnmanagedCallingConventionTypes,
SymbolEqualityComparer.Default);
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck
};

await test.RunAsync();
}

private static async Task<IMethodSymbol> FindFunctionPointerInvocationSignature(Compilation compilation, string userDefinedInterfaceName, string methodName)
class CallingConventionForwardingTest : VerifyCS.Test
{
INamedTypeSymbol? userDefinedInterface = compilation.Assembly.GetTypeByMetadataName(userDefinedInterfaceName);
Assert.NotNull(userDefinedInterface);
private readonly Action<Compilation, IMethodSymbol> _signatureValidator;
private readonly string _interfaceName;
private readonly string _methodName;

public CallingConventionForwardingTest(string interfaceName, string methodName, Action<Compilation, IMethodSymbol> signatureValidator)
: base(referenceAncillaryInterop: true)
{
_signatureValidator = signatureValidator;
_interfaceName = interfaceName;
_methodName = methodName;
}

protected override void VerifyFinalCompilation(Compilation compilation)
{
_signatureValidator(compilation, FindFunctionPointerInvocationSignature(compilation));
}
private IMethodSymbol FindFunctionPointerInvocationSignature(Compilation compilation)
{
INamedTypeSymbol? userDefinedInterface = compilation.Assembly.GetTypeByMetadataName(_interfaceName);
Assert.NotNull(userDefinedInterface);

INamedTypeSymbol generatedInterfaceImplementation = Assert.Single(userDefinedInterface.GetTypeMembers("Native"));
INamedTypeSymbol generatedInterfaceImplementation = Assert.Single(userDefinedInterface.GetTypeMembers("Native"));

IMethodSymbol methodImplementation = Assert.Single(generatedInterfaceImplementation.GetMembers($"global::{userDefinedInterfaceName}.{methodName}").OfType<IMethodSymbol>());
IMethodSymbol methodImplementation = Assert.Single(generatedInterfaceImplementation.GetMembers($"global::{_interfaceName}.{_methodName}").OfType<IMethodSymbol>());

SyntaxNode emittedImplementationSyntax = await methodImplementation.DeclaringSyntaxReferences[0].GetSyntaxAsync();
SyntaxNode emittedImplementationSyntax = methodImplementation.DeclaringSyntaxReferences[0].GetSyntax();

SemanticModel model = compilation.GetSemanticModel(emittedImplementationSyntax.SyntaxTree);
SemanticModel model = compilation.GetSemanticModel(emittedImplementationSyntax.SyntaxTree);

IOperation body = model.GetOperation(emittedImplementationSyntax)!;
IOperation body = model.GetOperation(emittedImplementationSyntax)!;

return Assert.Single(body.Descendants().OfType<IFunctionPointerInvocationOperation>()).GetFunctionPointerSignature();
return Assert.Single(body.Descendants().OfType<IFunctionPointerInvocationOperation>()).GetFunctionPointerSignature();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public string BasicParametersAndModifiers(string typeName, string methodModifier
partial interface INativeAPI
{
{{VirtualMethodIndex(0)}}
{{methodModifiers}} {{typeName}} Method({{typeName}} value, in {{typeName}} inValue, ref {{typeName}} refValue, out {{typeName}} outValue);
{{methodModifiers}} {{typeName}} {|#0:Method|}({{typeName}} {|#1:value|}, in {{typeName}} {|#2:inValue|}, ref {{typeName}} {|#3:refValue|}, out {{typeName}} {|#4:outValue|});
}
{{_attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI")}}
""";
Expand Down Expand Up @@ -277,7 +277,7 @@ partial interface IOtherComInterface
void MethodA();
}
{{GeneratedComInterface}}
partial interface IComInterface2 : IComInterface, IOtherComInterface
partial interface {|#0:IComInterface2|} : IComInterface, IOtherComInterface
{
void Method2();
}
Expand Down
Loading