未验证 提交 fc1cb19a 编写于 作者: J Jeremy Koritzinsky 提交者: GitHub

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

上级 4fb1b760
......@@ -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 -->
......
......@@ -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;
}
......
......@@ -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)),
......
......@@ -238,7 +238,7 @@ public BlockSyntax GenerateStubBody(int index, ImmutableArray<FunctionPointerUnm
{
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>)
......
......@@ -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
......@@ -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]
......@@ -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]
......@@ -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;
......@@ -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;
......@@ -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;
......@@ -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();
}
}
}
}
......@@ -126,7 +126,7 @@ partial interface INativeAPI
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")}}
""";
......@@ -277,7 +277,7 @@ partial interface IOtherComInterface
void MethodA();
}
{{GeneratedComInterface}}
partial interface IComInterface2 : IComInterface, IOtherComInterface
partial interface {|#0:IComInterface2|} : IComInterface, IOtherComInterface
{
void Method2();
}
......
......@@ -5,9 +5,11 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.Interop.UnitTests;
using Microsoft.CodeAnalysis.Testing;
using Xunit;
using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier<Microsoft.Interop.ComClassGenerator>;
namespace ComInterfaceGenerator.Unit.Tests
{
public class ComClassGeneratorOutputShape
......@@ -27,15 +29,8 @@ partial interface INativeAPI
[GeneratedComClass]
partial class C : INativeAPI {}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComClassGenerator());
TestUtils.AssertPostSourceGeneratorCompilation(newComp);
// We'll create one syntax tree for the new interface.
Assert.Equal(comp.SyntaxTrees.Count() + 1, newComp.SyntaxTrees.Count());
VerifyShape(newComp, "C");
await VerifySourceGeneratorAsync(source, "C");
}
[Fact]
......@@ -69,36 +64,56 @@ partial class E : C
{
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComClassGenerator());
TestUtils.AssertPostSourceGeneratorCompilation(newComp);
// We'll create one syntax tree per user-defined interface.
Assert.Equal(comp.SyntaxTrees.Count() + 3, newComp.SyntaxTrees.Count());
await VerifySourceGeneratorAsync(source, "C", "D", "E");
}
private static async Task VerifySourceGeneratorAsync(string source, params string[] typeNames)
{
GeneratedShapeTest test = new(typeNames)
{
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck
};
VerifyShape(newComp, "C");
VerifyShape(newComp, "D");
VerifyShape(newComp, "E");
await test.RunAsync();
}
private static void VerifyShape(Compilation comp, string userDefinedClassMetadataName)
class GeneratedShapeTest : VerifyCS.Test
{
INamedTypeSymbol? userDefinedClass = comp.Assembly.GetTypeByMetadataName(userDefinedClassMetadataName);
Assert.NotNull(userDefinedClass);
private readonly string[] _typeNames;
INamedTypeSymbol? comExposedClassAttribute = comp.GetTypeByMetadataName("System.Runtime.InteropServices.Marshalling.ComExposedClassAttribute`1");
public GeneratedShapeTest(params string[] typeNames)
:base(referenceAncillaryInterop: false)
{
_typeNames = typeNames;
}
Assert.NotNull(comExposedClassAttribute);
protected override void VerifyFinalCompilation(Compilation compilation)
{
// Generate one source file per attributed interface.
Assert.Equal(TestState.Sources.Count + _typeNames.Length, compilation.SyntaxTrees.Count());
Assert.All(_typeNames, name => VerifyShape(compilation, name));
}
AttributeData iUnknownDerivedAttribute = Assert.Single(
userDefinedClass.GetAttributes(),
attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass?.OriginalDefinition, comExposedClassAttribute));
private static void VerifyShape(Compilation comp, string userDefinedClassMetadataName)
{
INamedTypeSymbol? userDefinedClass = comp.Assembly.GetTypeByMetadataName(userDefinedClassMetadataName);
Assert.NotNull(userDefinedClass);
Assert.Collection(Assert.IsAssignableFrom<INamedTypeSymbol>(iUnknownDerivedAttribute.AttributeClass).TypeArguments,
infoType =>
{
Assert.True(Assert.IsAssignableFrom<INamedTypeSymbol>(infoType).IsFileLocal);
});
INamedTypeSymbol? comExposedClassAttribute = comp.GetTypeByMetadataName("System.Runtime.InteropServices.Marshalling.ComExposedClassAttribute`1");
Assert.NotNull(comExposedClassAttribute);
AttributeData iUnknownDerivedAttribute = Assert.Single(
userDefinedClass.GetAttributes(),
attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass?.OriginalDefinition, comExposedClassAttribute));
Assert.Collection(Assert.IsAssignableFrom<INamedTypeSymbol>(iUnknownDerivedAttribute.AttributeClass).TypeArguments,
infoType =>
{
Assert.True(Assert.IsAssignableFrom<INamedTypeSymbol>(infoType).IsFileLocal);
});
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
......@@ -27,6 +27,8 @@
Link="Verifiers\CSharpAnalyzerVerifier.cs"/>
<Compile Include="..\Common\Verifiers\CSharpCodeFixVerifier.cs"
Link="Verifiers\CSharpCodeFixVerifier.cs"/>
<Compile Include="..\Common\Verifiers\CSharpSourceGeneratorVerifier.cs"
Link="Verifiers\CSharpSourceGeneratorVerifier.cs"/>
<Compile Include="..\Common\Verifiers\CSharpVerifierHelper.cs"
Link="Verifiers\CSharpVerifierHelper.cs"/>
</ItemGroup>
......@@ -35,6 +37,7 @@
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(MicrosoftCodeAnalysisVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="$(CompilerPlatformTestingVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="$(CompilerPlatformTestingVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="$(CompilerPlatformTestingVersion)" />
</ItemGroup>
<ItemGroup>
......
......@@ -8,12 +8,16 @@
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.Interop.UnitTests;
using Xunit;
using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier<Microsoft.Interop.ComInterfaceGenerator>;
namespace ComInterfaceGenerator.Unit.Tests
{
public class ComInterfaceGeneratorOutputShape
......@@ -32,15 +36,8 @@ partial interface INativeAPI
void Method2();
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComInterfaceGenerator());
TestUtils.AssertPostSourceGeneratorCompilation(newComp);
// We'll create one syntax tree for the new interface.
Assert.Equal(comp.SyntaxTrees.Count() + 1, newComp.SyntaxTrees.Count());
VerifyShape(newComp, "INativeAPI");
await VerifySourceGeneratorAsync(source, "INativeAPI");
}
[Fact]
......@@ -63,16 +60,8 @@ partial interface J
void Method2();
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComInterfaceGenerator());
TestUtils.AssertPostSourceGeneratorCompilation(newComp);
// We'll create one syntax tree per user-defined interface.
Assert.Equal(comp.SyntaxTrees.Count() + 2, newComp.SyntaxTrees.Count());
VerifyShape(newComp, "I");
VerifyShape(newComp, "J");
await VerifySourceGeneratorAsync(source, "I", "J");
}
[Fact]
......@@ -99,17 +88,8 @@ partial interface J
void Method2();
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComInterfaceGenerator());
TestUtils.AssertPostSourceGeneratorCompilation(newComp);
// We'll create one syntax tree per user-defined interface.
Assert.Equal(comp.SyntaxTrees.Count() + 3, newComp.SyntaxTrees.Count());
VerifyShape(newComp, "I");
VerifyShape(newComp, "Empty");
VerifyShape(newComp, "J");
await VerifySourceGeneratorAsync(source, "I", "Empty", "J");
}
[Fact]
......@@ -132,47 +112,67 @@ partial interface J : I
void MethodB();
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComInterfaceGenerator());
TestUtils.AssertPostSourceGeneratorCompilation(newComp);
// We'll create one syntax tree per user-defined interface.
Assert.Equal(comp.SyntaxTrees.Count() + 2, newComp.SyntaxTrees.Count());
VerifyShape(newComp, "I");
VerifyShape(newComp, "J");
await VerifySourceGeneratorAsync(source, "I", "J");
}
private static void VerifyShape(Compilation comp, string userDefinedInterfaceMetadataName)
private static async Task VerifySourceGeneratorAsync(string source, params string[] typeNames)
{
INamedTypeSymbol? userDefinedInterface = comp.Assembly.GetTypeByMetadataName(userDefinedInterfaceMetadataName);
Assert.NotNull(userDefinedInterface);
INamedTypeSymbol? iUnknownDerivedAttributeType = comp.GetTypeByMetadataName("System.Runtime.InteropServices.Marshalling.IUnknownDerivedAttribute`2");
GeneratedShapeTest test = new(typeNames)
{
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck
};
Assert.NotNull(iUnknownDerivedAttributeType);
AttributeData iUnknownDerivedAttribute = Assert.Single(
userDefinedInterface.GetAttributes(),
attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass?.OriginalDefinition, iUnknownDerivedAttributeType));
Assert.Collection(Assert.IsAssignableFrom<INamedTypeSymbol>(iUnknownDerivedAttribute.AttributeClass).TypeArguments,
infoType =>
{
Assert.True(Assert.IsAssignableFrom<INamedTypeSymbol>(infoType).IsFileLocal);
},
implementationType =>
{
Assert.True(Assert.IsAssignableFrom<INamedTypeSymbol>(implementationType).IsFileLocal);
Assert.Contains(userDefinedInterface, implementationType.Interfaces, SymbolEqualityComparer.Default);
Assert.Contains(implementationType.GetAttributes(), attr => attr.AttributeClass?.ToDisplayString() == typeof(DynamicInterfaceCastableImplementationAttribute).FullName);
Assert.All(userDefinedInterface.GetMembers().OfType<IMethodSymbol>().Where(method => method.IsAbstract && !method.IsStatic),
method =>
{
Assert.NotNull(implementationType.FindImplementationForInterfaceMember(method));
});
});
await test.RunAsync();
}
class GeneratedShapeTest : VerifyCS.Test
{
private readonly string[] _typeNames;
public GeneratedShapeTest(params string[] typeNames)
: base(referenceAncillaryInterop: false)
{
_typeNames = typeNames;
}
protected override void VerifyFinalCompilation(Compilation compilation)
{
// Generate one source file per attributed interface.
Assert.Equal(TestState.Sources.Count + _typeNames.Length, compilation.SyntaxTrees.Count());
Assert.All(_typeNames, name => VerifyShape(compilation, name));
}
private static void VerifyShape(Compilation comp, string userDefinedInterfaceMetadataName)
{
INamedTypeSymbol? userDefinedInterface = comp.Assembly.GetTypeByMetadataName(userDefinedInterfaceMetadataName);
Assert.NotNull(userDefinedInterface);
INamedTypeSymbol? iUnknownDerivedAttributeType = comp.GetTypeByMetadataName("System.Runtime.InteropServices.Marshalling.IUnknownDerivedAttribute`2");
Assert.NotNull(iUnknownDerivedAttributeType);
AttributeData iUnknownDerivedAttribute = Assert.Single(
userDefinedInterface.GetAttributes(),
attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass?.OriginalDefinition, iUnknownDerivedAttributeType));
Assert.Collection(Assert.IsAssignableFrom<INamedTypeSymbol>(iUnknownDerivedAttribute.AttributeClass).TypeArguments,
infoType =>
{
Assert.True(Assert.IsAssignableFrom<INamedTypeSymbol>(infoType).IsFileLocal);
},
implementationType =>
{
Assert.True(Assert.IsAssignableFrom<INamedTypeSymbol>(implementationType).IsFileLocal);
Assert.Contains(userDefinedInterface, implementationType.Interfaces, SymbolEqualityComparer.Default);
Assert.Contains(implementationType.GetAttributes(), attr => attr.AttributeClass?.ToDisplayString() == typeof(DynamicInterfaceCastableImplementationAttribute).FullName);
Assert.All(userDefinedInterface.GetMembers().OfType<IMethodSymbol>().Where(method => method.IsAbstract && !method.IsStatic),
method =>
{
Assert.NotNull(implementationType.FindImplementationForInterfaceMember(method));
});
});
}
}
}
}
......@@ -11,9 +11,16 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.Interop.UnitTests;
using Xunit;
using System.Diagnostics;
using VerifyComInterfaceGenerator = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier<Microsoft.Interop.ComInterfaceGenerator>;
using Microsoft.Interop;
namespace ComInterfaceGenerator.Unit.Tests
{
public class CompileFails
......@@ -27,31 +34,106 @@ public static IEnumerable<object[]> ComInterfaceGeneratorSnippetsToCompile()
{
CodeSnippets codeSnippets = new(new GeneratedComInterfaceAttributeProvider());
// Inheriting from multiple GeneratedComInterface-marked interfaces.
yield return new object[] { ID(), codeSnippets.DerivedComInterfaceTypeMultipleComInterfaceBases, 1, 0 };
yield return new object[] { ID(), codeSnippets.DerivedComInterfaceTypeMultipleComInterfaceBases, new[] {
VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.MultipleComInterfaceBaseTypes)
.WithLocation(0)
.WithArguments("IComInterface2")
} };
}
[Theory]
[MemberData(nameof(ComInterfaceGeneratorSnippetsToCompile))]
public async Task ValidateComInterfaceGeneratorSnippets(string id, string source, int expectedGeneratorErrors, int expectedCompilerErrors)
public async Task ValidateComInterfaceGeneratorSnippets(string id, string source, DiagnosticResult[] expectedDiagnostics)
{
TestUtils.Use(id);
Compilation comp = await TestUtils.CreateCompilation(source);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.ComInterfaceGenerator());
// Verify the compilation failed with errors.
IEnumerable<Diagnostic> generatorErrors = generatorDiags.Where(d => d.Severity == DiagnosticSeverity.Error);
int generatorErrorCount = generatorErrors.Count();
Assert.True(
expectedGeneratorErrors == generatorErrorCount,
$"Expected {expectedGeneratorErrors} errors, but encountered {generatorErrorCount}. Errors: {string.Join(Environment.NewLine, generatorErrors.Select(d => d.ToString()))}");
IEnumerable<Diagnostic> compilerErrors = newComp.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error);
int compilerErrorCount = compilerErrors.Count();
Assert.True(
expectedCompilerErrors == compilerErrorCount,
$"Expected {expectedCompilerErrors} errors, but encountered {compilerErrorCount}. Errors: {string.Join(Environment.NewLine, compilerErrors.Select(d => d.ToString()))}");
await VerifyComInterfaceGenerator.VerifySourceGeneratorAsync(source, expectedDiagnostics);
}
private static IComInterfaceAttributeProvider GetAttributeProvider(GeneratorKind generator)
=> generator switch
{
GeneratorKind.VTableIndexStubGenerator => new VirtualMethodIndexAttributeProvider(),
GeneratorKind.ComInterfaceGenerator => new GeneratedComInterfaceAttributeProvider(),
_ => throw new UnreachableException(),
};
public static IEnumerable<object[]> InvalidUnmanagedToManagedCodeSnippetsToCompile(GeneratorKind generator)
{
CodeSnippets codeSnippets = new(GetAttributeProvider(generator));
// SafeHandles
yield return new object[] { ID(), codeSnippets.BasicParametersAndModifiers("Microsoft.Win32.SafeHandles.SafeFileHandle"), new[]
{
VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails).WithLocation(0).WithArguments("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' does not support it.", "Method"),
VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails).WithLocation(1).WithArguments("The specified parameter needs to be marshalled from unmanaged to managed, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' does not support it.", "value"),
// /0/Test0.cs(13,151): error SYSLIB1051: The type 'Microsoft.Win32.SafeHandles.SafeFileHandle' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter 'inValue'.
VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails).WithLocation(2).WithArguments("The specified parameter needs to be marshalled from unmanaged to managed, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' does not support it.", "inValue"),
// /0/Test0.cs(13,207): error SYSLIB1051: The type 'Microsoft.Win32.SafeHandles.SafeFileHandle' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter 'refValue'.
VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails).WithLocation(3).WithArguments("The specified parameter needs to be marshalled from managed to unmanaged and unmanaged to managed, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' does not support it.", "refValue"),
// /0/Test0.cs(13,264): error SYSLIB1051: The type 'Microsoft.Win32.SafeHandles.SafeFileHandle' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter 'outValue'.
VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails).WithLocation(4).WithArguments("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' does not support it.", "outValue"),
} };
// Marshallers with only support for their expected places in the signatures in
// ManagedToUnmanaged marshal modes.
DiagnosticResult invalidManagedToUnmanagedParameterDiagnostic = VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails)
.WithLocation(0)
.WithArguments("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type 'global::Marshaller' does not support it.", "value");
DiagnosticResult invalidUnmanagedToManagedParameterDiagnostic = VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails)
.WithLocation(0)
.WithArguments("The specified parameter needs to be marshalled from unmanaged to managed, but the marshaller type 'global::Marshaller' does not support it.", "value");
DiagnosticResult invalidReturnTypeDiagnostic = VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails)
.WithLocation(0)
.WithArguments("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type 'global::Marshaller' does not support it.", "Method");
CustomStructMarshallingCodeSnippets customStructMarshallingCodeSnippets = new(new CodeSnippets.Bidirectional(GetAttributeProvider(generator)));
yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateless.NativeToManagedOnlyOutParameter, new[] { invalidManagedToUnmanagedParameterDiagnostic } };
yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateless.NativeToManagedOnlyReturnValue, new[] { invalidReturnTypeDiagnostic } };
yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateless.ByValueInParameter, new[] { invalidUnmanagedToManagedParameterDiagnostic } };
yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateful.NativeToManagedOnlyOutParameter, new[] { invalidManagedToUnmanagedParameterDiagnostic } };
yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateful.NativeToManagedOnlyReturnValue, new[] { invalidReturnTypeDiagnostic } };
yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateful.ByValueInParameter, new[] { invalidUnmanagedToManagedParameterDiagnostic } };
}
public static IEnumerable<object[]> InvalidManagedToUnmanagedCodeSnippetsToCompile(GeneratorKind generator)
{
// Marshallers with only support for their expected places in the signatures in
// UnmanagedToManaged marshal modes.
CustomStructMarshallingCodeSnippets customStructMarshallingCodeSnippets = new(new CodeSnippets.Bidirectional(GetAttributeProvider(generator)));
yield return new[] { ID(), customStructMarshallingCodeSnippets.Stateless.NativeToManagedOnlyInParameter };
yield return new[] { ID(), customStructMarshallingCodeSnippets.Stateless.ByValueOutParameter };
yield return new[] { ID(), customStructMarshallingCodeSnippets.Stateful.NativeToManagedOnlyInParameter };
yield return new[] { ID(), customStructMarshallingCodeSnippets.Stateful.ByValueOutParameter };
}
[Theory]
[MemberData(nameof(InvalidUnmanagedToManagedCodeSnippetsToCompile), GeneratorKind.ComInterfaceGenerator)]
public async Task ValidateInvalidUnmanagedToManagedCodeSnippets(string id, string source, DiagnosticResult[] expectedDiagnostics)
{
_ = id;
VerifyComInterfaceGenerator.Test test = new(referenceAncillaryInterop: false)
{
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck,
// Our fallback mechanism for invalid code for unmanaged->managed stubs sometimes generates invalid code.
CompilerDiagnostics = CompilerDiagnostics.None,
};
test.ExpectedDiagnostics.AddRange(expectedDiagnostics);
await test.RunAsync();
}
[Theory]
[MemberData(nameof(InvalidManagedToUnmanagedCodeSnippetsToCompile), GeneratorKind.ComInterfaceGenerator)]
public async Task ValidateInvalidManagedToUnmanagedCodeSnippets(string id, string source)
{
_ = id;
DiagnosticResult expectedDiagnostic = VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails)
.WithLocation(0)
.WithArguments("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type 'global::Marshaller' does not support it.", "value");
await VerifyComInterfaceGenerator.VerifySourceGeneratorAsync(source, expectedDiagnostic);
}
}
}
......@@ -7,7 +7,7 @@
using Microsoft.Interop.Analyzers;
using Xunit;
using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpAnalyzerVerifier<Microsoft.Interop.Analyzers.GeneratedComInterfaceAttributeAnalyzer>;
using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpAnalyzerVerifier<Microsoft.Interop.Analyzers.GeneratedComInterfaceAttributeAnalyzer>;
namespace ComInterfaceGenerator.Unit.Tests
{
......
......@@ -67,7 +67,7 @@ partial interface INativeAPI
partial interface INativeAPI
{
{{AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}}
void Method({{typeName}} value);
void Method({{typeName}} {|#0:value|});
}
{{AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI")}}
""";
......@@ -85,7 +85,7 @@ partial interface INativeAPI
partial interface INativeAPI
{
{{AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}}
void Method({{modifier}} {{typeName}} value);
void Method({{modifier}} {{typeName}} {|#0:value|});
}
{{AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI")}}
""";
......@@ -100,7 +100,7 @@ partial interface INativeAPI
partial interface INativeAPI
{
{{AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}}
{{typeName}} Method();
{{typeName}} {|#0:Method|}();
}
{{AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI")}}
""";
......
......@@ -278,7 +278,7 @@ public string NestedMarshallerParametersAndModifiers(string elementType) => _pro
public string GenericCollectionMarshallingArityMismatch => _provider.BasicParameterByValue("TestCollection<int>", DisableRuntimeMarshalling)
+ """
[NativeMarshalling(typeof(Marshaller<,,>))]
[{|#10:NativeMarshalling(typeof(Marshaller<,,>))|}]
class TestCollection<T> {}
[CustomMarshaller(typeof(TestCollection<>), MarshalMode.Default, typeof(Marshaller<,,>))]
......
......@@ -18,7 +18,7 @@ public CustomStructMarshallingCodeSnippets(ICustomMarshallingSignatureTestProvid
private static readonly string UsingSystemRuntimeInteropServicesMarshalling = "using System.Runtime.InteropServices.Marshalling;";
public static string NonBlittableUserDefinedType(bool defineNativeMarshalling = true) => $$"""
{{(defineNativeMarshalling ? "[NativeMarshalling(typeof(Marshaller))]" : string.Empty)}}
{{(defineNativeMarshalling ? "[{|#10:NativeMarshalling(typeof(Marshaller))|}]" : string.Empty)}}
public struct S
{
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
......@@ -240,9 +240,9 @@ public struct Native { }
+ NonBlittableUserDefinedType()
+ Ref;
public string StackallocOnlyRefParameter => _provider.BasicParameterWithByRefModifier("ref", "S")
+ NonBlittableUserDefinedType()
+ InOutBuffer;
public string StackallocOnlyRefParameter => _provider.BasicParameterWithByRefModifier("ref", "S")
+ NonBlittableUserDefinedType()
+ InOutBuffer;
public string OptionalStackallocParametersAndModifiers => _provider.BasicParametersAndModifiers("S", UsingSystemRuntimeInteropServicesMarshalling)
+ NonBlittableUserDefinedType()
......
......@@ -5,8 +5,6 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.DotNet.XUnitExtensions;
using SourceGenerators.Tests;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
......
......@@ -8,7 +8,7 @@
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing;
namespace LibraryImportGenerator.UnitTests.Verifiers
namespace Microsoft.Interop.UnitTests.Verifiers
{
public static class CSharpAnalyzerVerifier<TAnalyzer>
where TAnalyzer : DiagnosticAnalyzer, new()
......
......@@ -14,9 +14,8 @@
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Testing.Verifiers;
using Microsoft.Interop.UnitTests;
namespace LibraryImportGenerator.UnitTests.Verifiers
namespace Microsoft.Interop.UnitTests.Verifiers
{
public static class CSharpCodeFixVerifier<TAnalyzer, TCodeFix>
where TAnalyzer : DiagnosticAnalyzer, new()
......@@ -120,45 +119,7 @@ public Test()
TestState.AdditionalReferences.AddRange(SourceGenerators.Tests.LiveReferencePack.GetMetadataReferences());
TestState.AdditionalReferences.Add(TestUtils.GetAncillaryReference());
SolutionTransforms.Add((solution, projectId) =>
{
var project = solution.GetProject(projectId)!;
var compilationOptions = project.CompilationOptions!;
var diagnosticOptions = compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings);
// Explicitly enable diagnostics that are not enabled by default
var enableAnalyzersOptions = new System.Collections.Generic.Dictionary<string, ReportDiagnostic>();
foreach (var analyzer in GetDiagnosticAnalyzers().ToImmutableArray())
{
foreach (var diagnostic in analyzer.SupportedDiagnostics)
{
if (diagnostic.IsEnabledByDefault)
continue;
// Map the default severity to the reporting behaviour.
// We cannot simply use ReportDiagnostic.Default here, as diagnostics that are not enabled by default
// are treated as suppressed (regardless of their default severity).
var report = diagnostic.DefaultSeverity switch
{
DiagnosticSeverity.Error => ReportDiagnostic.Error,
DiagnosticSeverity.Warning => ReportDiagnostic.Warn,
DiagnosticSeverity.Info => ReportDiagnostic.Info,
DiagnosticSeverity.Hidden => ReportDiagnostic.Hidden,
_ => ReportDiagnostic.Default
};
enableAnalyzersOptions.Add(diagnostic.Id, report);
}
}
compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(
compilationOptions.SpecificDiagnosticOptions
.SetItems(CSharpVerifierHelper.NullableWarnings)
.AddRange(enableAnalyzersOptions)
.AddRange(TestUtils.BindingRedirectWarnings));
solution = solution.WithProjectCompilationOptions(projectId, compilationOptions);
solution = solution.WithProjectParseOptions(projectId, ((CSharpParseOptions)project.ParseOptions!).WithLanguageVersion(LanguageVersion.Preview));
return solution;
});
SolutionTransforms.Add(CSharpVerifierHelper.GetAllDiagonsticsEnabledTransform(GetDiagnosticAnalyzers()));
}
protected override CompilationWithAnalyzers CreateCompilationWithAnalyzers(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, CancellationToken cancellationToken)
......@@ -187,6 +148,11 @@ protected override CompilationWithAnalyzers CreateCompilationWithAnalyzers(Compi
return true;
}));
}
protected override ParseOptions CreateParseOptions()
{
return new CSharpParseOptions(LanguageVersion.Preview, DocumentationMode.Diagnose);
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.CSharp.Testing.XUnit;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Testing.Verifiers;
namespace Microsoft.Interop.UnitTests.Verifiers
{
public static class CSharpSourceGeneratorVerifier<TSourceGenerator>
where TSourceGenerator : IIncrementalGenerator, new()
{
public static DiagnosticResult Diagnostic(string diagnosticId)
=> new DiagnosticResult(diagnosticId, DiagnosticSeverity.Error);
public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)
=> new DiagnosticResult(descriptor);
/// <summary>
/// Create a <see cref="DiagnosticResult"/> with the diagnostic message created with the provided arguments.
/// A <see cref="DiagnosticResult"/> with the <see cref="DiagnosticResult.Message"/> property set instead of just the <see cref="DiagnosticResult.MessageArguments"/> property
/// binds more strongly to the "correct" diagnostic as the test harness will match the diagnostic on the exact message instead of just on the message arguments.
/// </summary>
/// <param name="descriptor">The diagnostic descriptor</param>
/// <param name="arguments">The arguments to use to format the diagnostic message</param>
/// <returns>A <see cref="DiagnosticResult"/> with a <see cref="DiagnosticResult.Message"/> set with the <paramref name="descriptor"/>'s message format and the <paramref name="arguments"/>.</returns>
public static DiagnosticResult DiagnosticWithArguments(DiagnosticDescriptor descriptor, params object[] arguments)
{
// Generate the specific message here to ensure a stronger match with the correct diagnostic.
return Diagnostic(descriptor).WithMessage(string.Format(descriptor.MessageFormat.ToString(), arguments)).WithArguments(arguments);
}
public static async Task VerifySourceGeneratorAsync(string source, params DiagnosticResult[] expected)
{
var test = new Test(referenceAncillaryInterop: false)
{
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck
};
test.ExpectedDiagnostics.AddRange(expected);
await test.RunAsync(CancellationToken.None);
}
public static async Task VerifySourceGeneratorWithAncillaryInteropAsync(string source, params DiagnosticResult[] expected)
{
var test = new Test(referenceAncillaryInterop: true)
{
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck
};
test.ExpectedDiagnostics.AddRange(expected);
await test.RunAsync(CancellationToken.None);
}
public static async Task VerifySourceGeneratorAsync(string[] sources, params DiagnosticResult[] expected)
{
var test = new Test(referenceAncillaryInterop: false)
{
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck
};
foreach (var source in sources)
{
test.TestState.Sources.Add(source);
}
test.ExpectedDiagnostics.AddRange(expected);
await test.RunAsync(CancellationToken.None);
}
internal class Test : CSharpSourceGeneratorTest<TSourceGenerator, XUnitVerifier>
{
public Test(TestTargetFramework targetFramework)
{
if (targetFramework == TestTargetFramework.Net)
{
// Clear out the default reference assemblies. We explicitly add references from the live ref pack,
// so we don't want the Roslyn test infrastructure to resolve/add any default reference assemblies
ReferenceAssemblies = new ReferenceAssemblies(string.Empty);
TestState.AdditionalReferences.AddRange(SourceGenerators.Tests.LiveReferencePack.GetMetadataReferences());
}
else
{
ReferenceAssemblies = targetFramework switch
{
TestTargetFramework.Framework => ReferenceAssemblies.NetFramework.Net48.Default,
TestTargetFramework.Standard => ReferenceAssemblies.NetStandard.NetStandard21,
TestTargetFramework.Core => ReferenceAssemblies.NetCore.NetCoreApp31,
TestTargetFramework.Net6 => ReferenceAssemblies.Net.Net60,
_ => ReferenceAssemblies.Default
};
}
SolutionTransforms.Add(CSharpVerifierHelper.GetTargetFrameworkAnalyzerOptionsProviderTransform(targetFramework));
}
public Test(bool referenceAncillaryInterop)
:this(TestTargetFramework.Net)
{
if (referenceAncillaryInterop)
{
TestState.AdditionalReferences.Add(TestUtils.GetAncillaryReference());
}
SolutionTransforms.Add(CSharpVerifierHelper.GetAllDiagonsticsEnabledTransform(GetDiagnosticAnalyzers()));
}
protected override CompilationWithAnalyzers CreateCompilationWithAnalyzers(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, CancellationToken cancellationToken)
{
return new CompilationWithAnalyzers(
compilation,
analyzers,
new CompilationWithAnalyzersOptions(
options,
onAnalyzerException: null,
concurrentAnalysis: !Debugger.IsAttached,
logAnalyzerExecutionTime: true,
reportSuppressedDiagnostics: false,
analyzerExceptionFilter: ex =>
{
// We're hunting down a intermittent issue that causes NullReferenceExceptions deep in Roslyn. To ensure that we get an actionable dump, we're going to FailFast here to force a process dump.
if (ex is NullReferenceException)
{
// Break a debugger here so there's a chance to investigate if someone is already attached.
if (System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Break();
}
Environment.FailFast($"Encountered a NullReferenceException while running an analyzer. Taking the process down to get an actionable crash dump. Exception information:{ex.ToString()}");
}
return true;
}));
}
protected override ParseOptions CreateParseOptions()
{
return new CSharpParseOptions(LanguageVersion.Preview, DocumentationMode.Diagnose);
}
protected async override Task<(Compilation compilation, ImmutableArray<Diagnostic> generatorDiagnostics)> GetProjectCompilationAsync(Project project, IVerifier verifier, CancellationToken cancellationToken)
{
var (compilation, diagnostics) = await base.GetProjectCompilationAsync(project, verifier, cancellationToken);
VerifyFinalCompilation(compilation);
return (compilation, diagnostics);
}
/// <summary>
/// Verify any expected invariants on the final compilation after the source generators have been applied.
/// </summary>
/// <param name="compilation">The compilation.</param>
/// <remarks>
/// This function is useful for basic semantic testing of the generated code and can be used instead of verification testing of an exact match to the expected source output.
/// </remarks>
protected virtual void VerifyFinalCompilation(Compilation compilation)
{
}
}
}
}
......@@ -2,11 +2,15 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Text;
namespace LibraryImportGenerator.UnitTests.Verifiers
namespace Microsoft.Interop.UnitTests.Verifiers
{
internal static class CSharpVerifierHelper
{
......@@ -25,5 +29,88 @@ internal static class CSharpVerifierHelper
var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory);
return commandLineArguments.CompilationOptions.SpecificDiagnosticOptions;
}
internal static Func<Solution, ProjectId, Solution> GetAllDiagonsticsEnabledTransform(IEnumerable<DiagnosticAnalyzer> analyzers)
{
return (solution, projectId) =>
{
var project = solution.GetProject(projectId)!;
var compilationOptions = project.CompilationOptions!;
var diagnosticOptions = compilationOptions.SpecificDiagnosticOptions.SetItems(NullableWarnings);
// Explicitly enable diagnostics that are not enabled by default
var enableAnalyzersOptions = new Dictionary<string, ReportDiagnostic>();
foreach (var analyzer in analyzers)
{
foreach (var diagnostic in analyzer.SupportedDiagnostics)
{
if (diagnostic.IsEnabledByDefault)
continue;
// Map the default severity to the reporting behaviour.
// We cannot simply use ReportDiagnostic.Default here, as diagnostics that are not enabled by default
// are treated as suppressed (regardless of their default severity).
var report = diagnostic.DefaultSeverity switch
{
DiagnosticSeverity.Error => ReportDiagnostic.Error,
DiagnosticSeverity.Warning => ReportDiagnostic.Warn,
DiagnosticSeverity.Info => ReportDiagnostic.Info,
DiagnosticSeverity.Hidden => ReportDiagnostic.Hidden,
_ => ReportDiagnostic.Default
};
enableAnalyzersOptions.Add(diagnostic.Id, report);
}
}
compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(
compilationOptions.SpecificDiagnosticOptions
.SetItems(NullableWarnings)
.AddRange(enableAnalyzersOptions)
.AddRange(TestUtils.BindingRedirectWarnings));
solution = solution.WithProjectCompilationOptions(projectId, compilationOptions);
return solution;
};
}
internal static Func<Solution, ProjectId, Solution> GetTargetFrameworkAnalyzerOptionsProviderTransform(TestTargetFramework targetFramework)
{
return (solution, projectId) =>
{
var project = solution.GetProject(projectId)!;
string tfmEditorConfig = targetFramework switch
{
TestTargetFramework.Framework => """
is_global = true
build_property.TargetFrameworkIdentifier = .NETFramework
build_property.TargetFrameworkVersion = v4.8
""",
TestTargetFramework.Standard => """
is_global = true
build_property.TargetFrameworkIdentifier = .NETStandard
build_property.TargetFrameworkVersion = v2.0
""",
TestTargetFramework.Core => """
is_global = true
build_property.TargetFrameworkIdentifier = .NETCoreApp
build_property.TargetFrameworkVersion = v3.1
""",
TestTargetFramework.Net6 => """
is_global = true
build_property.TargetFrameworkIdentifier = .NETCoreApp
build_property.TargetFrameworkVersion = v6.0
""",
// Replicate the product case where we don't have these properties
// since we don't have a good mechanism to ship MSBuild files from dotnet/runtime
// in the SDK.
TestTargetFramework.Net => string.Empty,
_ => throw new System.Diagnostics.UnreachableException()
};
return solution.AddAnalyzerConfigDocument(
DocumentId.CreateNewId(projectId),
"TargetFrameworkConfig.editorconfig",
SourceText.From(tfmEditorConfig, encoding: System.Text.Encoding.UTF8),
filePath: "/TargetFrameworkConfig.editorconfig");
};
}
}
}
......@@ -4,28 +4,19 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing.Model;
using Microsoft.CodeAnalysis.Testing.Verifiers;
using Microsoft.Interop.Analyzers;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.Interop;
using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier<
LibraryImportGenerator.UnitTests.AddDisableRuntimeMarshallingAttributeFixerTests.MockAnalyzer,
Microsoft.Interop.Analyzers.AddDisableRuntimeMarshallingAttributeFixer>;
using Xunit;
using System.IO;
using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier<
Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer,
Microsoft.Interop.Analyzers.AddDisableRuntimeMarshallingAttributeFixer>;
namespace LibraryImportGenerator.UnitTests
{
[ActiveIssue("https://github.com/dotnet/runtime/issues/60650", TestRuntimes.Mono)]
public class AddDisableRuntimeMarshallingAttributeFixerTests
{
[Fact]
......@@ -38,7 +29,7 @@ public static async Task Adds_NewFile_With_Attribute()
partial class Foo
{
[LibraryImport("Foo")]
public static partial void {|CS8795:PInvoke|}(S {|#0:s|});
public static partial void PInvoke(S {|#0:s|});
}
[NativeMarshalling(typeof(Marshaller))]
......@@ -48,6 +39,7 @@ struct S
struct Native
{
public bool b;
}
[CustomMarshaller(typeof(S), MarshalMode.Default, typeof(Marshaller))]
......@@ -60,7 +52,9 @@ static class Marshaller
""";
var expectedPropertiesFile = "[assembly: System.Runtime.CompilerServices.DisableRuntimeMarshalling]" + Environment.NewLine;
var diagnostic = VerifyCS.Diagnostic(GeneratorDiagnostics.Ids.TypeNotSupported).WithLocation(0).WithArguments("S", "s");
var diagnostic = VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails)
.WithLocation(0)
.WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "s");
await VerifyCodeFixAsync(source, propertiesFile: null, expectedPropertiesFile, diagnostic);
}
......@@ -73,7 +67,7 @@ public static async Task Appends_Attribute_To_Existing_AssemblyInfo_File()
partial class Foo
{
[LibraryImport("Foo")]
public static partial void {|CS8795:PInvoke|}(S {|#0:s|});
public static partial void PInvoke(S {|#0:s|});
}
[NativeMarshalling(typeof(Marshaller))]
......@@ -83,6 +77,7 @@ struct S
struct Native
{
public bool b;
}
[CustomMarshaller(typeof(S), MarshalMode.Default, typeof(Marshaller))]
......@@ -106,19 +101,21 @@ static class Marshaller
""";
var diagnostic = VerifyCS.Diagnostic(GeneratorDiagnostics.Ids.TypeNotSupported).WithLocation(0).WithArguments("S", "s");
var diagnostic = VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails)
.WithLocation(0)
.WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "s");
await VerifyCodeFixAsync(source, propertiesFile, expectedPropertiesFile, diagnostic);
}
private static async Task VerifyCodeFixAsync(string source, string? propertiesFile, string? expectedPropertiesFile, DiagnosticResult diagnostic)
{
var test = new Test();
// We don't care about validating the settings for the MockAnalyzer and we're also hitting failures on Mono
// with this check in this case, so skip the check for now.
test.TestBehaviors = TestBehaviors.SkipGeneratedCodeCheck;
test.TestCode = source;
test.FixedCode = source;
test.BatchFixedCode = source;
var test = new Test
{
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck,
TestCode = source,
FixedCode = source,
BatchFixedCode = source
};
test.ExpectedDiagnostics.Add(diagnostic);
if (propertiesFile is not null)
{
......@@ -134,45 +131,15 @@ private static async Task VerifyCodeFixAsync(string source, string? propertiesFi
class Test : VerifyCS.Test
{
private static readonly ImmutableArray<Type> GeneratorTypes = ImmutableArray.Create(typeof(Microsoft.Interop.LibraryImportGenerator));
public const string FilePathPrefix = "/Project/";
protected override string DefaultFilePathPrefix => FilePathPrefix;
}
// The Roslyn SDK doesn't provide a good test harness for testing a code fix that triggers
// on a source-generator-introduced diagnostic. This analyzer does a decent enough job of triggering
// the specific diagnostic in the right place for us to test the code fix.
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class MockAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor AddDisableRuntimeMarshallingAttributeRule = GeneratorDiagnostics.ParameterTypeNotSupported;
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(AddDisableRuntimeMarshallingAttributeRule);
public override void Initialize(AnalysisContext context)
protected override IEnumerable<Type> GetSourceGenerators()
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.RegisterSymbolAction(context =>
{
var symbol = (IParameterSymbol)context.Symbol;
if (context.Symbol.ContainingAssembly.GetAttributes().Any(attr => attr.AttributeClass!.ToDisplayString() == TypeNames.System_Runtime_CompilerServices_DisableRuntimeMarshallingAttribute))
{
return;
}
if (symbol.ContainingSymbol is IMethodSymbol { IsStatic: true, IsPartialDefinition: true })
{
context.ReportDiagnostic(context.Symbol.CreateDiagnostic(
AddDisableRuntimeMarshallingAttributeRule,
ImmutableDictionary<string, string>.Empty
.Add(
GeneratorDiagnosticProperties.AddDisableRuntimeMarshallingAttribute,
GeneratorDiagnosticProperties.AddDisableRuntimeMarshallingAttribute),
symbol.Type.ToDisplayString(), symbol.Name));
}
}, SymbolKind.Parameter);
return GeneratorTypes;
}
}
}
......
......@@ -4,13 +4,14 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.Interop.UnitTests;
using SourceGenerators.Tests;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Xunit;
using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier<Microsoft.Interop.LibraryImportGenerator>;
namespace LibraryImportGenerator.UnitTests
{
public class AdditionalAttributesOnStub
......@@ -46,13 +47,7 @@ static class Marshaller
public static S ConvertToManaged(Native n) => default;
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.LibraryImportGenerator());
ITypeSymbol c = newComp.GetTypeByMetadataName("C")!;
IMethodSymbol stubMethod = c.GetMembers().OfType<IMethodSymbol>().Single(m => m.Name == "Method");
Assert.Contains(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(SkipLocalsInitAttribute).FullName);
await VerifySourceGeneratorAsync(source, "C", "Method", typeof(SkipLocalsInitAttribute).FullName, attributeAdded: true, TestTargetFramework.Net);
}
[Fact]
......@@ -66,13 +61,7 @@ partial class C
public static partial void Method();
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.LibraryImportGenerator());
ITypeSymbol c = newComp.GetTypeByMetadataName("C")!;
IMethodSymbol stubMethod = c.GetMembers().OfType<IMethodSymbol>().Single(m => m.Name == "Method");
Assert.DoesNotContain(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(SkipLocalsInitAttribute).FullName);
await VerifySourceGeneratorAsync(source, "C", "Method", typeof(SkipLocalsInitAttribute).FullName, attributeAdded: false, TestTargetFramework.Net);
}
[Fact]
......@@ -106,13 +95,7 @@ static class Marshaller
public static S ConvertToManaged(Native n) => default;
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.LibraryImportGenerator());
ITypeSymbol c = newComp.GetTypeByMetadataName("C")!;
IMethodSymbol stubMethod = c.GetMembers().OfType<IMethodSymbol>().Single(m => m.Name == "Method");
Assert.Contains(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(System.CodeDom.Compiler.GeneratedCodeAttribute).FullName);
await VerifySourceGeneratorAsync(source, "C", "Method", typeof(System.CodeDom.Compiler.GeneratedCodeAttribute).FullName, attributeAdded: true, TestTargetFramework.Net);
}
[Fact]
......@@ -126,13 +109,7 @@ partial class C
public static partial void Method();
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.LibraryImportGenerator());
ITypeSymbol c = newComp.GetTypeByMetadataName("C")!;
IMethodSymbol stubMethod = c.GetMembers().OfType<IMethodSymbol>().Single(m => m.Name == "Method");
Assert.DoesNotContain(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(System.CodeDom.Compiler.GeneratedCodeAttribute).FullName);
await VerifySourceGeneratorAsync(source, "C", "Method", typeof(System.CodeDom.Compiler.GeneratedCodeAttribute).FullName, attributeAdded: false, TestTargetFramework.Net);
}
public static IEnumerable<object[]> GetDownlevelTargetFrameworks()
......@@ -159,20 +136,7 @@ partial class C
public static partial bool Method();
}
""";
Compilation comp = await TestUtils.CreateCompilation(source, targetFramework);
Compilation newComp = TestUtils.RunGenerators(comp, new GlobalOptionsOnlyProvider(new TargetFrameworkConfigOptions(targetFramework)), out _, new Microsoft.Interop.LibraryImportGenerator());
ITypeSymbol c = newComp.GetTypeByMetadataName("C")!;
IMethodSymbol stubMethod = c.GetMembers().OfType<IMethodSymbol>().Single(m => m.Name == "Method");
if (expectSkipLocalsInit)
{
Assert.Contains(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(SkipLocalsInitAttribute).FullName);
}
else
{
Assert.DoesNotContain(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(SkipLocalsInitAttribute).FullName);
}
await VerifySourceGeneratorAsync(source, "C", "Method", typeof(SkipLocalsInitAttribute).FullName, attributeAdded: expectSkipLocalsInit, targetFramework);
}
[Fact]
......@@ -206,13 +170,7 @@ static class Marshaller
public static S ConvertToManaged(Native n) => default;
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.LibraryImportGenerator());
ITypeSymbol c = newComp.GetTypeByMetadataName("C")!;
IMethodSymbol stubMethod = c.GetMembers().OfType<IMethodSymbol>().Single(m => m.Name == "Method");
Assert.DoesNotContain(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(SkipLocalsInitAttribute).FullName);
await VerifySourceGeneratorAsync(source, "C", "Method", typeof(SkipLocalsInitAttribute).FullName, attributeAdded: false, TestTargetFramework.Net);
}
[Fact]
......@@ -246,13 +204,7 @@ static class Marshaller
public static S ConvertToManaged(Native n) => default;
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.LibraryImportGenerator());
ITypeSymbol c = newComp.GetTypeByMetadataName("C")!;
IMethodSymbol stubMethod = c.GetMembers().OfType<IMethodSymbol>().Single(m => m.Name == "Method");
Assert.DoesNotContain(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(SkipLocalsInitAttribute).FullName);
await VerifySourceGeneratorAsync(source, "C", "Method", typeof(SkipLocalsInitAttribute).FullName, attributeAdded: false, TestTargetFramework.Net);
}
[Fact]
......@@ -286,13 +238,49 @@ static class Marshaller
public static S ConvertToManaged(Native n) => default;
}
""";
Compilation comp = await TestUtils.CreateCompilation(source);
// Verify that we get no diagnostics from applying the attribute twice.
await VerifyCS.VerifySourceGeneratorAsync(source);
}
Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.LibraryImportGenerator());
private static Task VerifySourceGeneratorAsync(string source, string typeName, string methodName, string? attributeName, bool attributeAdded, TestTargetFramework targetFramework)
{
AttributeAddedTest test = new(typeName, methodName, attributeName, attributeAdded, targetFramework)
{
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck
};
return test.RunAsync();
}
class AttributeAddedTest : VerifyCS.Test
{
private readonly string _typeName;
private readonly string _methodName;
private readonly string? _attributeName;
private readonly bool _expectSkipLocalsInit;
ITypeSymbol c = newComp.GetTypeByMetadataName("C")!;
IMethodSymbol stubMethod = c.GetMembers().OfType<IMethodSymbol>().Single(m => m.Name == "Method");
Assert.DoesNotContain(newComp.GetDiagnostics(), d => d.Id != "CS0579"); // No duplicate attribute error
public AttributeAddedTest(string typeName, string methodName, string? attributeName, bool expectSkipLocalsInitOnMethod, TestTargetFramework targetFramework)
: base(targetFramework)
{
_typeName = typeName;
_methodName = methodName;
_attributeName = attributeName;
_expectSkipLocalsInit = expectSkipLocalsInitOnMethod;
}
protected override void VerifyFinalCompilation(Compilation compilation)
{
ITypeSymbol c = compilation.GetTypeByMetadataName(_typeName)!;
IMethodSymbol stubMethod = c.GetMembers().OfType<IMethodSymbol>().Single(m => m.Name == _methodName);
if (_expectSkipLocalsInit)
{
Assert.Contains(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == _attributeName);
}
else
{
Assert.DoesNotContain(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == _attributeName);
}
}
}
}
}
......@@ -14,6 +14,13 @@
using Xunit;
using SourceGenerators.Tests;
using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier<Microsoft.Interop.LibraryImportGenerator>;
using Microsoft.CodeAnalysis.Testing;
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis.Text;
using System.Text;
namespace LibraryImportGenerator.UnitTests
{
public class Compiles
......@@ -417,13 +424,8 @@ public static IEnumerable<object[]> CustomCollections()
public async Task ValidateSnippets(string id, string source)
{
TestUtils.Use(id);
Compilation comp = await TestUtils.CreateCompilation(source);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator());
Assert.Empty(generatorDiags);
TestUtils.AssertPostSourceGeneratorCompilation(newComp);
await VerifyCS.VerifySourceGeneratorAsync(source);
}
public static IEnumerable<object[]> CodeSnippetsToCompileWithPreprocessorSymbols()
......@@ -442,23 +444,37 @@ public static IEnumerable<object[]> CodeSnippetsToCompileWithPreprocessorSymbols
public async Task ValidateSnippetsWithPreprocessorDefinitions(string id, string source, IEnumerable<string> preprocessorSymbols)
{
TestUtils.Use(id);
Compilation comp = await TestUtils.CreateCompilation(source, preprocessorSymbols: preprocessorSymbols);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var test = new PreprocessorTest(preprocessorSymbols)
{
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck
};
var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator());
Assert.Empty(generatorDiags);
await test.RunAsync();
}
TestUtils.AssertPostSourceGeneratorCompilation(newComp);
private class PreprocessorTest : VerifyCS.Test
{
private readonly IEnumerable<string> _preprocessorSymbols;
public PreprocessorTest(IEnumerable<string> preprocessorSymbols)
:base(referenceAncillaryInterop: false)
{
_preprocessorSymbols = preprocessorSymbols;
}
protected override ParseOptions CreateParseOptions()
=> ((CSharpParseOptions)base.CreateParseOptions()).WithPreprocessorSymbols(_preprocessorSymbols);
}
public static IEnumerable<object[]> CodeSnippetsToValidateFallbackForwarder()
{
yield return new object[] { ID(), CodeSnippets.UserDefinedEntryPoint, TestTargetFramework.Net, true };
//yield return new object[] { ID(), CodeSnippets.UserDefinedEntryPoint, TestTargetFramework.Net, true };
// Confirm that all unsupported target frameworks can be generated.
{
string code = CodeSnippets.BasicParametersAndModifiers<byte>(CodeSnippets.LibraryImportAttributeDeclaration);
yield return new object[] { ID(), code, TestTargetFramework.Net6, false };
//yield return new object[] { ID(), code, TestTargetFramework.Net6, false };
yield return new object[] { ID(), code, TestTargetFramework.Core, false };
yield return new object[] { ID(), code, TestTargetFramework.Standard, false };
yield return new object[] { ID(), code, TestTargetFramework.Framework, false };
......@@ -498,31 +514,38 @@ public static IEnumerable<object[]> CodeSnippetsToValidateFallbackForwarder()
public async Task ValidateSnippetsFallbackForwarder(string id, string source, TestTargetFramework targetFramework, bool expectFallbackForwarder)
{
TestUtils.Use(id);
Compilation comp = await TestUtils.CreateCompilation(source, targetFramework);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(
comp,
new GlobalOptionsOnlyProvider(new TargetFrameworkConfigOptions(targetFramework)),
out var generatorDiags,
new Microsoft.Interop.LibraryImportGenerator());
var test = new FallbackForwarderTest(targetFramework, expectFallbackForwarder)
{
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck
};
Assert.Empty(generatorDiags);
await test.RunAsync();
}
TestUtils.AssertPostSourceGeneratorCompilation(newComp);
class FallbackForwarderTest : VerifyCS.Test
{
private readonly bool _expectFallbackForwarder;
// Verify that the forwarder generates the method as a DllImport.
SyntaxTree generatedCode = newComp.SyntaxTrees.Last();
SemanticModel model = newComp.GetSemanticModel(generatedCode);
var methods = generatedCode.GetRoot()
.DescendantNodes().OfType<MethodDeclarationSyntax>()
.ToList();
MethodDeclarationSyntax generatedMethod = Assert.Single(methods);
public FallbackForwarderTest(TestTargetFramework targetFramework, bool expectFallbackForwarder)
:base(targetFramework)
{
_expectFallbackForwarder = expectFallbackForwarder;
}
protected override void VerifyFinalCompilation(Compilation compilation)
{
SyntaxTree generatedCode = compilation.SyntaxTrees.Last();
SemanticModel model = compilation.GetSemanticModel(generatedCode);
var methods = generatedCode.GetRoot()
.DescendantNodes().OfType<MethodDeclarationSyntax>()
.ToList();
MethodDeclarationSyntax generatedMethod = Assert.Single(methods);
IMethodSymbol method = model.GetDeclaredSymbol(generatedMethod)!;
IMethodSymbol method = model.GetDeclaredSymbol(generatedMethod)!;
// If we expect fallback forwarder, then the DllImportData will not be null.
Assert.Equal(expectFallbackForwarder, method.GetDllImportData() is not null);
// If we expect fallback forwarder, then the DllImportData will not be null.
Assert.Equal(_expectFallbackForwarder, method.GetDllImportData() is not null);
}
}
public static IEnumerable<object[]> FullyBlittableSnippetsToCompile()
......@@ -536,26 +559,32 @@ public static IEnumerable<object[]> FullyBlittableSnippetsToCompile()
public async Task ValidateSnippetsWithBlittableAutoForwarding(string id, string source)
{
TestUtils.Use(id);
Compilation comp = await TestUtils.CreateCompilation(source);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(
comp,
out var generatorDiags,
new Microsoft.Interop.LibraryImportGenerator());
var test = new BlittableAutoForwarderTest()
{
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck
};
Assert.Empty(generatorDiags);
await test.RunAsync();
}
TestUtils.AssertPostSourceGeneratorCompilation(newComp);
class BlittableAutoForwarderTest : VerifyCS.Test
{
public BlittableAutoForwarderTest()
:base(referenceAncillaryInterop: false)
{
}
// Verify that the forwarder generates the method as a DllImport.
SyntaxTree generatedCode = newComp.SyntaxTrees.Last();
SemanticModel model = newComp.GetSemanticModel(generatedCode);
var methods = generatedCode.GetRoot()
.DescendantNodes().OfType<MethodDeclarationSyntax>()
.ToList();
protected override void VerifyFinalCompilation(Compilation compilation)
{
SyntaxTree generatedCode = compilation.SyntaxTrees.Last();
SemanticModel model = compilation.GetSemanticModel(generatedCode);
var methods = generatedCode.GetRoot()
.DescendantNodes().OfType<MethodDeclarationSyntax>()
.ToList();
Assert.All(methods, method => Assert.NotNull(model.GetDeclaredSymbol(method)!.GetDllImportData()));
Assert.All(methods, method => Assert.NotNull(model.GetDeclaredSymbol(method)!.GetDllImportData()));
}
}
public static IEnumerable<object[]> SnippetsWithBlittableTypesButNonBlittableDataToCompile()
......@@ -570,29 +599,34 @@ public static IEnumerable<object[]> SnippetsWithBlittableTypesButNonBlittableDat
public async Task ValidateSnippetsWithBlittableTypesButNonBlittableMetadataDoNotAutoForward(string id, string source)
{
TestUtils.Use(id);
Compilation comp = await TestUtils.CreateCompilation(source);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(
comp,
out var generatorDiags,
new Microsoft.Interop.LibraryImportGenerator());
Assert.Empty(generatorDiags);
var test = new NonBlittableNoAutoForwardTest()
{
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck
};
TestUtils.AssertPostSourceGeneratorCompilation(newComp);
await test.RunAsync();
}
// Verify that the generator generates stubs with inner DllImports for all methods.
SyntaxTree generatedCode = newComp.SyntaxTrees.Last();
SemanticModel model = newComp.GetSemanticModel(generatedCode);
int numStubMethods = generatedCode.GetRoot()
.DescendantNodes().OfType<MethodDeclarationSyntax>()
.Count();
int numInnerDllImports = generatedCode.GetRoot()
.DescendantNodes().OfType<LocalFunctionStatementSyntax>()
.Count();
class NonBlittableNoAutoForwardTest : VerifyCS.Test
{
public NonBlittableNoAutoForwardTest()
: base(referenceAncillaryInterop: false)
{
}
Assert.Equal(numStubMethods, numInnerDllImports);
protected override void VerifyFinalCompilation(Compilation compilation)
{
SyntaxTree generatedCode = compilation.SyntaxTrees.Last();
SemanticModel model = compilation.GetSemanticModel(generatedCode);
int numStubMethods = generatedCode.GetRoot()
.DescendantNodes().OfType<MethodDeclarationSyntax>()
.Count();
int numInnerDllImports = generatedCode.GetRoot()
.DescendantNodes().OfType<LocalFunctionStatementSyntax>()
.Count();
Assert.Equal(numStubMethods, numInnerDllImports);
}
}
public static IEnumerable<object[]> CodeSnippetsToCompileWithMarshalType()
......@@ -609,18 +643,21 @@ public static IEnumerable<object[]> CodeSnippetsToCompileWithMarshalType()
public async Task ValidateSnippetsWithMarshalType(string id, string source)
{
TestUtils.Use(id);
Compilation comp = await TestUtils.CreateCompilation(source);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(
comp,
new LibraryImportGeneratorOptionsProvider(TestTargetFramework.Net, useMarshalType: true, generateForwarders: false),
out var generatorDiags,
new Microsoft.Interop.LibraryImportGenerator());
Assert.Empty(generatorDiags);
TestUtils.AssertPostSourceGeneratorCompilation(newComp, "CS0117");
var test = new VerifyCS.Test(referenceAncillaryInterop: true)
{
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck
};
test.SolutionTransforms.Add((solution, projectId) =>
solution.AddAnalyzerConfigDocument(DocumentId.CreateNewId(projectId),
"UseMarshalType.editorconfig",
SourceText.From("""
is_global = true
build_property.LibraryImportGenerator_UseMarshalType = true
""",
Encoding.UTF8),
filePath: "/UseMarshalType.editorconfig"));
await test.RunAsync();
}
public static IEnumerable<object[]> CodeSnippetsToCompileMultipleSources()
......@@ -635,13 +672,16 @@ public static IEnumerable<object[]> CodeSnippetsToCompileMultipleSources()
public async Task ValidateSnippetsWithMultipleSources(string id, string[] sources)
{
TestUtils.Use(id);
Compilation comp = await TestUtils.CreateCompilation(sources);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator());
Assert.Empty(generatorDiags);
// To enable us to reuse snippets that have markup locations in our multiple-sources test, we'll strip out the markup locations.
// We need to do this as each snippet expects to be able to define all expected markup locations (starting from 0), so including multiple snippets
// results in multiple definitions for the same location (which doesn't work). Since we expect no diagnostics, we can strip out the locations.
await VerifyCS.VerifySourceGeneratorAsync(sources.Select(RemoveTestMarkup).ToArray());
}
TestUtils.AssertPostSourceGeneratorCompilation(newComp);
private static string RemoveTestMarkup(string sourceWithMarkup)
{
TestFileMarkupParser.GetSpans(sourceWithMarkup, out string sourceWithoutMarkup, out ImmutableArray<TextSpan> _);
return sourceWithoutMarkup;
}
public static IEnumerable<object[]> CodeSnippetsToVerifyNoTreesProduced()
......@@ -660,14 +700,29 @@ public class Basic { }
public async Task ValidateNoGeneratedOuptutForNoImport(string id, string source, TestTargetFramework framework)
{
TestUtils.Use(id);
Compilation comp = await TestUtils.CreateCompilation(source, framework, allowUnsafe: false);
TestUtils.AssertPreSourceGeneratorCompilation(comp);
var test = new NoChangeTest(framework)
{
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck
};
await test.RunAsync();
}
var newComp = TestUtils.RunGenerators(comp, new GlobalOptionsOnlyProvider(new TargetFrameworkConfigOptions(framework)), out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator());
Assert.Empty(generatorDiags);
class NoChangeTest : VerifyCS.Test
{
public NoChangeTest(TestTargetFramework framework)
:base(framework)
{
}
// Assert we didn't generate any syntax trees, even empty ones
Assert.Same(comp, newComp);
protected async override Task<(Compilation compilation, ImmutableArray<Diagnostic> generatorDiagnostics)> GetProjectCompilationAsync(Project project, IVerifier verifier, CancellationToken cancellationToken)
{
var originalCompilation = await project.GetCompilationAsync(cancellationToken);
var (newCompilation, diagnostics) = await base.GetProjectCompilationAsync(project, verifier, cancellationToken);
Assert.Same(originalCompilation, newCompilation);
return (newCompilation, diagnostics);
}
}
}
}
......@@ -10,7 +10,7 @@
using static Microsoft.Interop.Analyzers.ConvertToLibraryImportAnalyzer;
using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpAnalyzerVerifier<Microsoft.Interop.Analyzers.ConvertToLibraryImportAnalyzer>;
using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpAnalyzerVerifier<Microsoft.Interop.Analyzers.ConvertToLibraryImportAnalyzer>;
namespace LibraryImportGenerator.UnitTests
{
......
......@@ -9,9 +9,8 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.Interop.Analyzers;
using Xunit;
using static Microsoft.Interop.Analyzers.ConvertToLibraryImportFixer;
using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier<
using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier<
Microsoft.Interop.Analyzers.ConvertToLibraryImportAnalyzer,
Microsoft.Interop.Analyzers.ConvertToLibraryImportFixer>;
......
......@@ -8,7 +8,7 @@
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using LibraryImportGenerator.UnitTests.Verifiers;
using Microsoft.Interop.UnitTests.Verifiers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.Interop.Analyzers;
......
......@@ -8,7 +8,7 @@
using Xunit;
using static Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer;
using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier<
using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier<
Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer,
Microsoft.Interop.Analyzers.CustomMarshallerAttributeFixer>;
......
......@@ -9,7 +9,7 @@
using Xunit;
using static Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer;
using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier<
using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier<
Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer,
Microsoft.Interop.Analyzers.CustomMarshallerAttributeFixer>;
......
......@@ -10,7 +10,7 @@
using Xunit;
using static Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer;
using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier<
using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier<
Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer,
Microsoft.Interop.Analyzers.CustomMarshallerAttributeFixer>;
......
......@@ -9,7 +9,7 @@
using Xunit;
using static Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer;
using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier<
using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier<
Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer,
Microsoft.Interop.Analyzers.CustomMarshallerAttributeFixer>;
......
......@@ -9,7 +9,7 @@
using Xunit;
using static Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer;
using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier<
using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier<
Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer,
Microsoft.Interop.Analyzers.CustomMarshallerAttributeFixer>;
......
......@@ -3,12 +3,15 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Interop.UnitTests;
using Xunit;
using static Microsoft.Interop.LibraryImportGenerator;
......@@ -19,13 +22,10 @@ public class IncrementalGenerationTests
{
private static readonly GeneratorDriverOptions EnableIncrementalTrackingDriverOptions = new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true);
public const string RequiresIncrementalSyntaxTreeModifySupport = "The GeneratorDriver treats all SyntaxTree replace operations on a Compilation as an Add/Remove operation instead of a Modify operation"
+ ", so all cached results based on that input are thrown out. As a result, we cannot validate that unrelated changes within the same SyntaxTree do not cause regeneration.";
[Fact]
public async Task AddingNewUnrelatedType_DoesNotRegenerateSource()
{
string source = CodeSnippets.BasicParametersAndModifiers<int>();
string source = RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers<int>());
Compilation comp1 = await TestUtils.CreateCompilation(source);
......@@ -52,7 +52,7 @@ public async Task AppendingUnrelatedSource_DoesNotRegenerateSource()
string source = $$"""
namespace NS
{
{{CodeSnippets.BasicParametersAndModifiers<int>()}}
{{RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers<int>())}}
}
""";
......@@ -83,7 +83,7 @@ namespace NS
[Fact]
public async Task AddingFileWithNewLibraryImport_DoesNotRegenerateOriginalMethod()
{
string source = CodeSnippets.BasicParametersAndModifiers<int>();
string source = RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers<int>());
Compilation comp1 = await TestUtils.CreateCompilation(source);
......@@ -92,7 +92,7 @@ public async Task AddingFileWithNewLibraryImport_DoesNotRegenerateOriginalMethod
driver = driver.RunGenerators(comp1);
Compilation comp2 = comp1.AddSyntaxTrees(CSharpSyntaxTree.ParseText(CodeSnippets.MarshalAsParametersAndModifiers<bool>(UnmanagedType.I1), new CSharpParseOptions(LanguageVersion.Preview)));
Compilation comp2 = comp1.AddSyntaxTrees(CSharpSyntaxTree.ParseText(RemoveTestMarkup(CodeSnippets.MarshalAsParametersAndModifiers<bool>(UnmanagedType.I1)), new CSharpParseOptions(LanguageVersion.Preview)));
GeneratorDriver driver2 = driver.RunGenerators(comp2);
GeneratorRunResult runResult = driver2.GetRunResult().Results[0];
......@@ -113,14 +113,14 @@ public async Task AddingFileWithNewLibraryImport_DoesNotRegenerateOriginalMethod
[Fact]
public async Task ReplacingFileWithNewLibraryImport_DoesNotRegenerateStubsInOtherFiles()
{
Compilation comp1 = await TestUtils.CreateCompilation(new string[] { CodeSnippets.BasicParametersAndModifiers<int>(), CodeSnippets.MarshalAsParametersAndModifiers<bool>(UnmanagedType.I1) });
Compilation comp1 = await TestUtils.CreateCompilation(new string[] { RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers<int>()), RemoveTestMarkup(CodeSnippets.MarshalAsParametersAndModifiers<bool>(UnmanagedType.I1)) });
Microsoft.Interop.LibraryImportGenerator generator = new();
GeneratorDriver driver = TestUtils.CreateDriver(comp1, null, new[] { generator }, EnableIncrementalTrackingDriverOptions);
driver = driver.RunGenerators(comp1);
Compilation comp2 = comp1.ReplaceSyntaxTree(comp1.SyntaxTrees.First(), CSharpSyntaxTree.ParseText(CodeSnippets.BasicParametersAndModifiers<ulong>(), new CSharpParseOptions(LanguageVersion.Preview)));
Compilation comp2 = comp1.ReplaceSyntaxTree(comp1.SyntaxTrees.First(), CSharpSyntaxTree.ParseText(RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers<ulong>()), new CSharpParseOptions(LanguageVersion.Preview)));
GeneratorDriver driver2 = driver.RunGenerators(comp2);
GeneratorRunResult runResult = driver2.GetRunResult().Results[0];
......@@ -140,7 +140,7 @@ public async Task ReplacingFileWithNewLibraryImport_DoesNotRegenerateStubsInOthe
[Fact]
public async Task ChangingMarshallingStrategy_RegeneratesStub()
{
string stubSource = CodeSnippets.BasicParametersAndModifiers("CustomType", CodeSnippets.DisableRuntimeMarshalling);
string stubSource = RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers("CustomType", CodeSnippets.DisableRuntimeMarshalling));
string customTypeImpl1 = "struct CustomType { System.IntPtr handle; }";
......@@ -179,7 +179,7 @@ public async Task ChangingMarshallingStrategy_RegeneratesStub()
[Fact]
public async Task ChangingMarshallingAttributes_SameStrategy_DoesNotRegenerate()
{
string source = CodeSnippets.BasicParametersAndModifiers<int>();
string source = RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers<int>());
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(source, new CSharpParseOptions(LanguageVersion.Preview));
......@@ -192,7 +192,7 @@ public async Task ChangingMarshallingAttributes_SameStrategy_DoesNotRegenerate()
SyntaxTree newTree = syntaxTree.WithRootAndOptions(
SyntaxFactory.ParseCompilationUnit(
CodeSnippets.MarshalAsParametersAndModifiers<int>(System.Runtime.InteropServices.UnmanagedType.I4)),
RemoveTestMarkup(CodeSnippets.MarshalAsParametersAndModifiers<int>(System.Runtime.InteropServices.UnmanagedType.I4))),
syntaxTree.Options);
Compilation comp2 = comp1.ReplaceSyntaxTree(comp1.SyntaxTrees.First(), newTree);
......@@ -218,9 +218,9 @@ public async Task ChangingMarshallingAttributes_SameStrategy_DoesNotRegenerate()
public static IEnumerable<object[]> CompilationObjectLivenessSources()
{
// Basic stub
yield return new[] { CodeSnippets.BasicParametersAndModifiers<int>() };
yield return new[] { RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers<int>()) };
// Stub with custom string marshaller
yield return new[] { CodeSnippets.CustomStringMarshallingParametersAndModifiers<string>() };
yield return new[] { RemoveTestMarkup(CodeSnippets.CustomStringMarshallingParametersAndModifiers<string>()) };
}
// This test requires precise GC to ensure that we're accurately testing that we aren't
......@@ -271,5 +271,11 @@ public async Task GeneratorRun_WithNewCompilation_DoesNotKeepOldCompilationAlive
return (new WeakReference(comp2), driver2);
}
}
private static string RemoveTestMarkup(string sourceWithMarkup)
{
TestFileMarkupParser.GetSpans(sourceWithMarkup, out string sourceWithoutMarkup, out ImmutableArray<TextSpan> _);
return sourceWithoutMarkup;
}
}
}
......@@ -31,6 +31,8 @@
Link="Verifiers\CSharpAnalyzerVerifier.cs"/>
<Compile Include="..\Common\Verifiers\CSharpCodeFixVerifier.cs"
Link="Verifiers\CSharpCodeFixVerifier.cs"/>
<Compile Include="..\Common\Verifiers\CSharpSourceGeneratorVerifier.cs"
Link="Verifiers\CSharpSourceGeneratorVerifier.cs"/>
<Compile Include="..\Common\Verifiers\CSharpVerifierHelper.cs"
Link="Verifiers\CSharpVerifierHelper.cs"/>
</ItemGroup>
......@@ -39,6 +41,7 @@
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(MicrosoftCodeAnalysisVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="$(CompilerPlatformTestingVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="$(CompilerPlatformTestingVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="$(CompilerPlatformTestingVersion)" />
</ItemGroup>
<ItemGroup>
......
......@@ -8,7 +8,7 @@
using Xunit;
using static Microsoft.Interop.Analyzers.NativeMarshallingAttributeAnalyzer;
using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpAnalyzerVerifier<
using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpAnalyzerVerifier<
Microsoft.Interop.Analyzers.NativeMarshallingAttributeAnalyzer>;
namespace LibraryImportGenerator.UnitTests
......
......@@ -8,7 +8,7 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using LibraryImportGenerator.UnitTests.Verifiers;
using Microsoft.Interop.UnitTests.Verifiers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册