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

Add some more testing around shadowing, inheritance, and project boundaries. #86732

Merged
merged 2 commits into from
May 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Microsoft.CodeAnalysis.Testing;
using Microsoft.Interop;
using Microsoft.Interop.UnitTests;
using SourceGenerators.Tests;
using Xunit;

using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier<Microsoft.Interop.ComInterfaceGenerator>;
Expand All @@ -39,7 +40,7 @@ partial interface INativeAPI
}
""";

await VerifySourceGeneratorAsync(source, "INativeAPI");
await VerifyGeneratedTypeShapes(source, "INativeAPI");
}

[Fact]
Expand All @@ -65,7 +66,7 @@ partial interface J
}
""";

await VerifySourceGeneratorAsync(source, "I", "J");
await VerifyGeneratedTypeShapes(source, "I", "J");
}

[Fact]
Expand Down Expand Up @@ -96,7 +97,7 @@ partial interface J
}
""";

await VerifySourceGeneratorAsync(source, "I", "Empty", "J");
await VerifyGeneratedTypeShapes(source, "I", "Empty", "J");
}

[Fact]
Expand All @@ -122,7 +123,111 @@ partial interface J : I
}
""";

await VerifySourceGeneratorAsync(source, "I", "J");
await VerifyGeneratedTypeShapes(source, "I", "J");
}

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

[GeneratedComInterface]
[Guid("9D3FD745-3C90-4C10-B140-FAFB01E3541D")]
partial interface I
{
void Method();
void Method2();
}
[GeneratedComInterface]
[Guid("734AFCEC-8862-43CB-AB29-5A7954929E23")]
partial interface J : I
{
void MethodA();
void MethodB();
}
""";

var test = new VerifyCompilationTest<Microsoft.Interop.ComInterfaceGenerator>(false)
{
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck | TestBehaviors.SkipGeneratedCodeCheck,
CompilationVerifier = VerifyCompilation
};
await test.RunAsync();

static void VerifyCompilation(Compilation comp)
{
var j = comp.GetTypeByMetadataName("J");
Assert.NotNull(j);

var shadowingMethod = Assert.Single(j.GetMembers("Method"));
VerifyShadowingMethodShape(shadowingMethod);

shadowingMethod = Assert.Single(j.GetMembers("Method2"));
VerifyShadowingMethodShape(shadowingMethod);
}
}

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

[GeneratedComInterface]
[Guid("9D3FD745-3C90-4C10-B140-FAFB01E3541D")]
partial interface I
{
void Method();
}
[GeneratedComInterface]
[Guid("734AFCEC-8862-43CB-AB29-5A7954929E23")]
partial interface J : I
{
void Method2();
}
[GeneratedComInterface]
[Guid("0DB41042-0255-4CDD-B73A-9C5D5F31303D")]
partial interface K : J
{
void MethodA();
void MethodB();
}
""";

var test = new VerifyCompilationTest<Microsoft.Interop.ComInterfaceGenerator>(false)
{
TestCode = source,
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck | TestBehaviors.SkipGeneratedCodeCheck,
CompilationVerifier = VerifyCompilation
};
await test.RunAsync();

static void VerifyCompilation(Compilation comp)
{
var k = comp.GetTypeByMetadataName("K");
Assert.NotNull(k);

var shadowingMethod = Assert.Single(k.GetMembers("Method"));
VerifyShadowingMethodShape(shadowingMethod);

shadowingMethod = Assert.Single(k.GetMembers("Method2"));
Assert.False(shadowingMethod.IsAbstract);
Assert.True(shadowingMethod.IsVirtual);
VerifyShadowingMethodShape(shadowingMethod);
}
}

private static void VerifyShadowingMethodShape(ISymbol method)
{
Assert.False(method.IsAbstract);
Assert.True(method.IsVirtual);

var syntax = Assert.IsType<MethodDeclarationSyntax>(Assert.Single(method.DeclaringSyntaxReferences).GetSyntax());
Assert.Contains(syntax.Modifiers, token => token.IsKind(SyntaxKind.NewKeyword));
}

[Fact]
Expand Down Expand Up @@ -170,7 +275,7 @@ static void VerifyCompilation(Compilation comp)
}
}

private static async Task VerifySourceGeneratorAsync(string source, params string[] typeNames)
private static async Task VerifyGeneratedTypeShapes(string source, params string[] typeNames)
{
GeneratedShapeTest test = new(typeNames)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using VerifyComInterfaceGenerator = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier<Microsoft.Interop.ComInterfaceGenerator>;
using StringMarshalling = System.Runtime.InteropServices.StringMarshalling;
using System.Runtime.InteropServices.Marshalling;
using Microsoft.CodeAnalysis.CSharp;

namespace ComInterfaceGenerator.Unit.Tests
{
Expand Down Expand Up @@ -418,5 +419,75 @@ partial interface {|#0:IFoo2|}<T>

await VerifyComInterfaceGenerator.VerifySourceGeneratorAsync(source, expectedDiagnostic);
}

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

[GeneratedComInterface]
[Guid("9D3FD745-3C90-4C10-B140-FAFB01E3541D")]
public partial interface I
{
void Method();
}
""";

string source = $$"""
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;

[GeneratedComInterface]
[Guid("0DB41042-0255-4CDD-B73A-9C5D5F31303D")]
partial interface {|#0:J|} : I
{
void MethodA();
}
""";

var test = new VerifyComInterfaceGenerator.Test(referenceAncillaryInterop: false)
{
TestState =
{
Sources =
{
("Source.cs", source)
},
AdditionalProjects =
{
["Other"] =
{
Sources =
{
("Other.cs", additionalSource)
},
},
},
AdditionalProjectReferences =
{
"Other"
}
},
TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck | TestBehaviors.SkipGeneratedCodeCheck,
};
test.TestState.AdditionalProjects["Other"].AdditionalReferences.AddRange(test.TestState.AdditionalReferences);

test.ExpectedDiagnostics.Add(
VerifyComInterfaceGenerator
.Diagnostic(GeneratorDiagnostics.BaseInterfaceIsNotGenerated)
.WithLocation(0)
.WithArguments("J", "I"));

// The Roslyn SDK doesn't apply the compilation options from CreateCompilationOptions to AdditionalProjects-based projects.
test.SolutionTransforms.Add((sln, _) =>
{
var additionalProject = sln.Projects.First(proj => proj.Name == "Other");
return additionalProject.WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true)).Solution;
});
Comment on lines +484 to +488
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it's worth it to make a new Test subclass for tests with AdditionProjects?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I think doing it manually for the cases where we need it is okay. It's not a lot of packages and it's easy to mess with.

Also I'll probably fix this issue in the Roslyn-SDK at some point and then we can get rid of the solution transform.


await test.RunAsync();
}
}
}