Skip to content

Commit

Permalink
Share more boilerplate code in tests
Browse files Browse the repository at this point in the history
  • Loading branch information
AArnott committed Sep 14, 2023
1 parent 9904847 commit 72a737f
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 203 deletions.
90 changes: 21 additions & 69 deletions test/Microsoft.Windows.CsWin32.Tests/COMTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,15 @@ public COMTests(ITestOutputHelper logger)
public void FriendlyOverloadOfCOMInterfaceRemovesParameter()
{
const string ifaceName = "IEnumDebugPropertyInfo";
this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate(ifaceName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(ifaceName);
Assert.Contains(this.FindGeneratedMethod("Next"), m => m.ParameterList.Parameters.Count == 3 && m.ParameterList.Parameters[0].Modifiers.Any(SyntaxKind.ThisKeyword));
}

[Fact]
public void IDispatchDerivedInterface()
{
const string ifaceName = "IInkRectangle";
this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate(ifaceName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(ifaceName);
#pragma warning disable CS0618 // Type or member is obsolete
Assert.Contains(this.FindGeneratedType(ifaceName), t => t.BaseList is null && t.AttributeLists.Any(al => al.Attributes.Any(a => a.Name is IdentifierNameSyntax { Identifier: { ValueText: "InterfaceType" } } && a.ArgumentList?.Arguments[0].Expression is MemberAccessExpressionSyntax { Name: IdentifierNameSyntax { Identifier: { ValueText: nameof(ComInterfaceType.InterfaceIsIDispatch) } } })));
#pragma warning restore CS0618 // Type or member is obsolete
Expand All @@ -40,10 +34,7 @@ public void IDispatchDerivedInterface()
public void IInpectableDerivedInterface()
{
const string ifaceName = "IUserConsentVerifierInterop";
this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate(ifaceName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(ifaceName);
Assert.Contains(this.FindGeneratedType(ifaceName), t => t.BaseList is null && ((InterfaceDeclarationSyntax)t).Members.Count == 1 && t.AttributeLists.Any(al => al.Attributes.Any(a => a.Name is IdentifierNameSyntax { Identifier: { ValueText: "InterfaceType" } } && a.ArgumentList?.Arguments[0].Expression is MemberAccessExpressionSyntax { Name: IdentifierNameSyntax { Identifier: { ValueText: nameof(ComInterfaceType.InterfaceIsIInspectable) } } })));

// Make sure the WinRT marshaler was not brought in
Expand All @@ -55,9 +46,7 @@ public void COMPropertiesAreGeneratedAsInterfaceProperties(bool allowMarshaling)
{
const string ifaceName = "IADsClass";
this.generator = this.CreateGenerator(DefaultTestGeneratorOptions with { AllowMarshaling = allowMarshaling });
Assert.True(this.generator.TryGenerate(ifaceName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(ifaceName);
InterfaceDeclarationSyntax ifaceSyntax;
if (allowMarshaling)
{
Expand All @@ -83,9 +72,7 @@ public void COMPropertiesAreGeneratedAsInterfaceProperties_NonConsecutiveAccesso
{
const string ifaceName = "IUIAutomationProxyFactoryEntry";
this.generator = this.CreateGenerator(DefaultTestGeneratorOptions with { AllowMarshaling = allowMarshaling });
Assert.True(this.generator.TryGenerate(ifaceName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(ifaceName);
InterfaceDeclarationSyntax ifaceSyntax;
if (allowMarshaling)
{
Expand All @@ -107,9 +94,7 @@ public void COMPropertiesAreGeneratedAsStructProperties()
{
const string ifaceName = "IADsClass";
this.generator = this.CreateGenerator(DefaultTestGeneratorOptions with { AllowMarshaling = false });
Assert.True(this.generator.TryGenerate(ifaceName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(ifaceName);
StructDeclarationSyntax structSyntax = Assert.Single(this.FindGeneratedType(ifaceName).OfType<StructDeclarationSyntax>());

// Check a property where we expect just a getter.
Expand All @@ -126,9 +111,7 @@ public void COMPropertiesAreGeneratedAsStructProperties_NonConsecutiveAccessors(
{
const string ifaceName = "IUIAutomationProxyFactoryEntry";
this.generator = this.CreateGenerator(DefaultTestGeneratorOptions with { AllowMarshaling = false });
Assert.True(this.generator.TryGenerate(ifaceName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(ifaceName);
StructDeclarationSyntax structSyntax = Assert.Single(this.FindGeneratedType(ifaceName).OfType<StructDeclarationSyntax>());

// Check for a property where the interface declares the getter and setter in non-consecutive rows of the VMT.
Expand All @@ -151,19 +134,15 @@ public void COMPropertiesAreGeneratedAsStructProperties_NonConsecutiveAccessors(
public void COMPropertiesAreGeneratedAsStructProperties_NonConsecutiveAccessors_IPicture()
{
this.generator = this.CreateGenerator(DefaultTestGeneratorOptions with { AllowMarshaling = false });
Assert.True(this.generator.TryGenerate("IPicture", CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi("IPicture");
}

[Theory, PairwiseData]
public void COMPropertiesAreGeneratedAsMethodsWhenTheirReturnTypesDiffer([CombinatorialValues(0, 1, 2)] int marshaling)
{
const string ifaceName = "IHTMLImgElement";
this.generator = this.CreateGenerator(DefaultTestGeneratorOptions with { AllowMarshaling = marshaling > 0, ComInterop = new GeneratorOptions.ComInteropOptions { UseIntPtrForComOutPointers = marshaling == 1 } });
Assert.True(this.generator.TryGenerate(ifaceName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(ifaceName);
InterfaceDeclarationSyntax ifaceSyntax;
if (marshaling > 0)
{
Expand Down Expand Up @@ -208,10 +187,7 @@ public void WinRTInterfaceWithWinRTOutObjectUsesMarshaler()
const string WinRTInteropInterfaceName = "ICompositorDesktopInterop";
const string WinRTClassName = "Windows.UI.Composition.Desktop.DesktopWindowTarget";

this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate(WinRTInteropInterfaceName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(WinRTInteropInterfaceName);

InterfaceDeclarationSyntax interfaceDeclaration = (InterfaceDeclarationSyntax)Assert.Single(this.FindGeneratedType(WinRTInteropInterfaceName));
MethodDeclarationSyntax method = (MethodDeclarationSyntax)interfaceDeclaration.Members.First();
Expand Down Expand Up @@ -243,10 +219,7 @@ public void MethodWithHRParameter()
[Fact]
public void AssociatedEnumOnMethodParameters()
{
this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate("IShellFolderView", CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi("IShellFolderView");

InterfaceDeclarationSyntax ifaceSyntax = Assert.Single(this.FindGeneratedType("IShellFolderView").OfType<InterfaceDeclarationSyntax>());
MethodDeclarationSyntax methodSyntax = Assert.Single(ifaceSyntax.Members.OfType<MethodDeclarationSyntax>(), m => m.Identifier.ValueText == "Select");
Expand All @@ -266,9 +239,7 @@ public void InterestingUnmarshaledComInterfaces(
{
this.compilation = this.starterCompilations[tfm];
this.generator = this.CreateGenerator(DefaultTestGeneratorOptions with { AllowMarshaling = false });
Assert.True(this.generator.TryGenerate(api, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(api);
}

[Theory]
Expand All @@ -289,9 +260,7 @@ public void InterestingComInterfaces(
{
this.compilation = this.starterCompilations["net6.0"];
this.generator = this.CreateGenerator(DefaultTestGeneratorOptions with { AllowMarshaling = allowMarshaling });
Assert.True(this.generator.TryGenerate(api, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(api);
}

[Fact]
Expand All @@ -314,10 +283,7 @@ public void EnvironmentFailFast()
public void ComOutPtrTypedAsOutObject()
{
const string methodName = "CoCreateInstance";
this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate(methodName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(methodName);
Assert.Contains(this.FindGeneratedMethod(methodName), m => m.ParameterList.Parameters.Last() is { } last && last.Modifiers.Any(SyntaxKind.OutKeyword) && last.Type is PredefinedTypeSyntax { Keyword: { RawKind: (int)SyntaxKind.ObjectKeyword } });
}

Expand All @@ -326,9 +292,7 @@ public void ComOutPtrTypedAsIntPtr()
{
const string methodName = "CoCreateInstance";
this.generator = this.CreateGenerator(new GeneratorOptions { ComInterop = new() { UseIntPtrForComOutPointers = true } });
Assert.True(this.generator.TryGenerate(methodName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(methodName);
Assert.Contains(this.FindGeneratedMethod(methodName), m => m.ParameterList.Parameters.Last() is { } last && last.Modifiers.Any(SyntaxKind.OutKeyword) && last.Type is IdentifierNameSyntax { Identifier: { ValueText: "IntPtr" } });
}

Expand All @@ -338,9 +302,7 @@ public void NonCOMInterfaceReferences(bool allowMarshaling)
var options = DefaultTestGeneratorOptions with { AllowMarshaling = allowMarshaling };
this.generator = this.CreateGenerator(options);
const string methodName = "D3DCompile"; // A method whose signature references non-COM interface ID3DInclude
Assert.True(this.generator.TryGenerate(methodName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(methodName);

// The generated methods MUST reference the "interface" (which must actually be generated as a struct) by pointer.
Assert.Contains(this.FindGeneratedType("ID3DInclude"), t => t is StructDeclarationSyntax);
Expand All @@ -353,9 +315,7 @@ public void COMInterfaceWithSupportedOSPlatform(bool net60, bool allowMarshaling
this.compilation = this.starterCompilations[net60 ? "net6.0" : "netstandard2.0"];
const string typeName = "IInkCursors";
this.generator = this.CreateGenerator(DefaultTestGeneratorOptions with { AllowMarshaling = allowMarshaling });
Assert.True(this.generator.TryGenerateType(typeName));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(typeName);

var iface = this.FindGeneratedType(typeName).Single();

Expand All @@ -375,9 +335,7 @@ public void IStream_ProducesPopulateVTable()
this.compilation = this.starterCompilations["net6.0"];
const string typeName = "IStream";
this.generator = this.CreateGenerator(DefaultTestGeneratorOptions with { AllowMarshaling = false });
Assert.True(this.generator.TryGenerateType(typeName));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(typeName);
var iface = (StructDeclarationSyntax)this.FindGeneratedType(typeName).Single();
Assert.Single(iface.Members.OfType<MethodDeclarationSyntax>(), m => m.Identifier.ValueText == "PopulateVTable");
}
Expand All @@ -388,9 +346,7 @@ public void IPersistFile_DerivesFromIComIID()
this.compilation = this.starterCompilations["net7.0"];
const string typeName = "IPersistFile";
this.generator = this.CreateGenerator(DefaultTestGeneratorOptions with { AllowMarshaling = false });
Assert.True(this.generator.TryGenerateType(typeName));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(typeName);
var iface = (StructDeclarationSyntax)this.FindGeneratedType(typeName).Single();
Assert.NotNull(iface.BaseList);
Assert.Single(iface.BaseList.Types, bt => bt.Type.ToString().Contains("IComIID"));
Expand All @@ -401,9 +357,7 @@ public void ITypeNameBuilder_ToStringOverload(bool allowMarshaling)
{
const string typeName = "ITypeNameBuilder";
this.generator = this.CreateGenerator(DefaultTestGeneratorOptions with { AllowMarshaling = allowMarshaling });
Assert.True(this.generator.TryGenerateType(typeName));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(typeName);
}

[Theory]
Expand All @@ -417,9 +371,7 @@ public void COMInterfaceIIDInterfaceOnAppropriateTFMs(
this.compilation = this.starterCompilations[tfm];
this.parseOptions = this.parseOptions.WithLanguageVersion(langVersion);
this.generator = this.CreateGenerator(DefaultTestGeneratorOptions with { AllowMarshaling = allowMarshaling });
Assert.True(this.generator.TryGenerate(structName, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(structName);

BaseTypeDeclarationSyntax type = this.FindGeneratedType(structName).Single();
IEnumerable<BaseTypeSyntax> actual = type.BaseList?.Types ?? Enumerable.Empty<BaseTypeSyntax>();
Expand Down
5 changes: 1 addition & 4 deletions test/Microsoft.Windows.CsWin32.Tests/ConstantsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ public ConstantsTests(ITestOutputHelper logger)
public void InterestingConstants(string name)
{
this.compilation = this.compilation.WithOptions(this.compilation.Options.WithPlatform(Platform.X64));
this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate(name, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(name);
}
}
5 changes: 1 addition & 4 deletions test/Microsoft.Windows.CsWin32.Tests/DelegateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ public void InterestingDelegates(
{
AllowMarshaling = allowMarshaling,
};
this.generator = this.CreateGenerator(options);
Assert.True(this.generator.TryGenerate(name, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi(name);
}
}
38 changes: 11 additions & 27 deletions test/Microsoft.Windows.CsWin32.Tests/ExternMethodTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,22 @@ public ExternMethodTests(ITestOutputHelper logger)
[Fact]
public void AssociatedEnumOnParameter()
{
const string Method = "SHObjectProperties";
this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate(Method, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();

MethodDeclarationSyntax method = Assert.Single(this.FindGeneratedMethod(Method), IsOrContainsExternMethod);
MethodDeclarationSyntax method = Assert.Single(this.GenerateMethod("SHObjectProperties"), IsOrContainsExternMethod);
ParameterSyntax enumParam = method.ParameterList.Parameters[1];
Assert.Equal("SHOP_TYPE", Assert.IsType<QualifiedNameSyntax>(enumParam.Type).Right.Identifier.ValueText);
}

[Fact]
public void AssociatedEnumOnReturnValue()
{
const string Method = "PathCleanupSpec";
this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate(Method, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();

MethodDeclarationSyntax method = Assert.Single(this.FindGeneratedMethod(Method), IsOrContainsExternMethod);
MethodDeclarationSyntax method = Assert.Single(this.GenerateMethod("PathCleanupSpec"), IsOrContainsExternMethod);
Assert.Equal("PCS_RET", Assert.IsType<QualifiedNameSyntax>(method.ReturnType).Right.Identifier.ValueText);
}

[Fact]
public void AssociatedEnumOnParameterWithVoidReturn()
{
const string Method = "SHChangeNotify";
this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate(Method, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateMethod("SHChangeNotify");
}

/// <summary>
Expand All @@ -51,11 +35,7 @@ public void AssociatedEnumOnParameterWithVoidReturn()
[Fact]
public void WdkMethod_NtCreateFile()
{
const string Method = "NtCreateFile";
this.generator = this.CreateGenerator();
Assert.True(this.generator.TryGenerate(Method, CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateMethod("NtCreateFile");
}

[Theory, CombinatorialData]
Expand All @@ -65,9 +45,7 @@ public void SetLastError_ByMarshaling(
{
this.compilation = this.starterCompilations[tfm];
this.generator = this.CreateGenerator(DefaultTestGeneratorOptions with { AllowMarshaling = allowMarshaling });
Assert.True(this.generator.TryGenerate("GetVersionEx", CancellationToken.None));
this.CollectGeneratedCode(this.generator);
this.AssertNoDiagnostics();
this.GenerateApi("GetVersionEx");

bool expectMarshalingAttribute = allowMarshaling || tfm is "net472" or "netstandard2.0";
MethodDeclarationSyntax originalMethod = this.FindGeneratedMethod("GetVersionEx").Single(m => m.ParameterList.Parameters[0].Type is PointerTypeSyntax);
Expand All @@ -77,4 +55,10 @@ public void SetLastError_ByMarshaling(

static AttributeSyntax? FindDllImportAttribute(SyntaxList<AttributeListSyntax> attributeLists) => attributeLists.SelectMany(al => al.Attributes).FirstOrDefault(a => a.Name.ToString() == "DllImport");
}

private IEnumerable<MethodDeclarationSyntax> GenerateMethod(string methodName)
{
this.GenerateApi(methodName);
return this.FindGeneratedMethod(methodName);
}
}
Loading

0 comments on commit 72a737f

Please sign in to comment.