|
// 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.Generic;
using System.Runtime.InteropServices.ComTypes;
using Microsoft.Build.Tasks;
using Xunit;
using COMException = System.Runtime.InteropServices.COMException;
#nullable disable
namespace Microsoft.Build.UnitTests
{
public class ComReferenceWalker_Tests
{
private static int MockReleaseComObject(object o)
{
return 0;
}
private void AssertDependenciesContainTypeLib(TYPELIBATTR[] dependencies, MockTypeLib typeLib, bool contains)
{
AssertDependenciesContainTypeLib("", dependencies, typeLib, contains);
}
private void AssertDependenciesContainTypeLib(string message, TYPELIBATTR[] dependencies, MockTypeLib typeLib, bool contains)
{
bool dependencyExists = false;
foreach (TYPELIBATTR attr in dependencies)
{
if (attr.guid == typeLib.Attributes.guid)
{
dependencyExists = true;
break;
}
}
Assert.Equal(contains, dependencyExists);
}
[Fact]
public void WalkTypeInfosInEmptyLibrary()
{
MockTypeLib typeLib = new MockTypeLib();
ComDependencyWalker walker = new ComDependencyWalker(new MarshalReleaseComObject(MockReleaseComObject));
walker.AnalyzeTypeLibrary(typeLib);
Assert.Equal(0, walker.GetDependencies().GetLength(0));
typeLib.AssertAllHandlesReleased();
}
private void CreateTwoTypeLibs(out MockTypeLib mainTypeLib, out MockTypeLib dependencyTypeLib)
{
mainTypeLib = new MockTypeLib();
mainTypeLib.AddTypeInfo(new MockTypeInfo());
dependencyTypeLib = new MockTypeLib();
dependencyTypeLib.AddTypeInfo(new MockTypeInfo());
}
private TYPELIBATTR[] RunDependencyWalker(MockTypeLib mainTypeLib, MockTypeLib dependencyTypeLib, bool dependencyShouldBePresent)
{
ComDependencyWalker walker = new ComDependencyWalker(new MarshalReleaseComObject(MockReleaseComObject));
walker.AnalyzeTypeLibrary(mainTypeLib);
TYPELIBATTR[] dependencies = walker.GetDependencies();
// types from the main type library should be in the dependency list
AssertDependenciesContainTypeLib(dependencies, mainTypeLib, true);
AssertDependenciesContainTypeLib(dependencies, dependencyTypeLib, dependencyShouldBePresent);
mainTypeLib.AssertAllHandlesReleased();
dependencyTypeLib.AssertAllHandlesReleased();
return dependencies;
}
/// <summary>
/// A type in the main type library implements an interface from a dependent type library
/// </summary>
[Fact]
public void ImplementedInterfaces()
{
MockTypeLib mainTypeLib, dependencyTypeLib;
CreateTwoTypeLibs(out mainTypeLib, out dependencyTypeLib);
mainTypeLib.ContainedTypeInfos[0].ImplementsInterface(dependencyTypeLib.ContainedTypeInfos[0]);
RunDependencyWalker(mainTypeLib, dependencyTypeLib, true);
}
[Fact]
public void DefinedVariableUDT()
{
MockTypeLib mainTypeLib, dependencyTypeLib;
CreateTwoTypeLibs(out mainTypeLib, out dependencyTypeLib);
mainTypeLib.ContainedTypeInfos[0].DefinesVariable(dependencyTypeLib.ContainedTypeInfos[0]);
RunDependencyWalker(mainTypeLib, dependencyTypeLib, true);
}
[Fact]
public void DefinedVariableUDTArray()
{
MockTypeLib mainTypeLib, dependencyTypeLib;
CreateTwoTypeLibs(out mainTypeLib, out dependencyTypeLib);
mainTypeLib.ContainedTypeInfos[0].DefinesVariable(new ArrayCompositeTypeInfo(dependencyTypeLib.ContainedTypeInfos[0]));
RunDependencyWalker(mainTypeLib, dependencyTypeLib, true);
}
[Fact]
public void DefinedVariableUDTPtr()
{
MockTypeLib mainTypeLib, dependencyTypeLib;
CreateTwoTypeLibs(out mainTypeLib, out dependencyTypeLib);
mainTypeLib.ContainedTypeInfos[0].DefinesVariable(new PtrCompositeTypeInfo(dependencyTypeLib.ContainedTypeInfos[0]));
RunDependencyWalker(mainTypeLib, dependencyTypeLib, true);
}
[Fact]
public void ThereAndBackAgain()
{
MockTypeLib mainTypeLib, dependencyTypeLib;
CreateTwoTypeLibs(out mainTypeLib, out dependencyTypeLib);
mainTypeLib.ContainedTypeInfos[0].DefinesVariable(new PtrCompositeTypeInfo(dependencyTypeLib.ContainedTypeInfos[0]));
dependencyTypeLib.ContainedTypeInfos[0].ImplementsInterface(mainTypeLib.ContainedTypeInfos[0]);
RunDependencyWalker(mainTypeLib, dependencyTypeLib, true);
}
[Fact]
public void ComplexComposition()
{
MockTypeLib mainTypeLib, dependencyTypeLib;
CreateTwoTypeLibs(out mainTypeLib, out dependencyTypeLib);
mainTypeLib.ContainedTypeInfos[0].DefinesVariable(
new ArrayCompositeTypeInfo(new ArrayCompositeTypeInfo(new PtrCompositeTypeInfo(
new PtrCompositeTypeInfo(new ArrayCompositeTypeInfo(new PtrCompositeTypeInfo(dependencyTypeLib.ContainedTypeInfos[0])))))));
RunDependencyWalker(mainTypeLib, dependencyTypeLib, true);
}
[Fact]
public void DefinedFunction()
{
MockTypeLib mainTypeLib, dependencyTypeLib1, dependencyTypeLib2, dependencyTypeLib3;
CreateTwoTypeLibs(out mainTypeLib, out dependencyTypeLib1);
CreateTwoTypeLibs(out dependencyTypeLib2, out dependencyTypeLib3);
mainTypeLib.ContainedTypeInfos[0].DefinesFunction(
new MockTypeInfo[] { dependencyTypeLib1.ContainedTypeInfos[0], dependencyTypeLib2.ContainedTypeInfos[0] },
dependencyTypeLib3.ContainedTypeInfos[0]);
TYPELIBATTR[] dependencies = RunDependencyWalker(mainTypeLib, dependencyTypeLib1, true);
AssertDependenciesContainTypeLib(dependencies, dependencyTypeLib2, true);
AssertDependenciesContainTypeLib(dependencies, dependencyTypeLib3, true);
dependencyTypeLib2.AssertAllHandlesReleased();
dependencyTypeLib3.AssertAllHandlesReleased();
}
[Fact]
public void IgnoreKnownOleTypes()
{
MockTypeLib mainTypeLib = new MockTypeLib();
mainTypeLib.AddTypeInfo(new MockTypeInfo());
MockTypeLib oleTypeLib = new MockTypeLib();
oleTypeLib.AddTypeInfo(new MockTypeInfo(NativeMethods.IID_IUnknown));
oleTypeLib.AddTypeInfo(new MockTypeInfo(NativeMethods.IID_IDispatch));
oleTypeLib.AddTypeInfo(new MockTypeInfo(NativeMethods.IID_IDispatchEx));
oleTypeLib.AddTypeInfo(new MockTypeInfo(NativeMethods.IID_IEnumVariant));
oleTypeLib.AddTypeInfo(new MockTypeInfo(NativeMethods.IID_ITypeInfo));
// We don't check for this type in the ComDependencyWalker, so it doesn't get counted as a known OLE type.
// It's too late in the Dev10 cycle to add it to shipping code without phenomenally good reason, but we should
// re-examine this in Dev11.
// oleTypeLib.AddTypeInfo(new MockTypeInfo(TYPEKIND.TKIND_ENUM));
foreach (MockTypeInfo typeInfo in oleTypeLib.ContainedTypeInfos)
{
mainTypeLib.ContainedTypeInfos[0].DefinesVariable(typeInfo);
}
RunDependencyWalker(mainTypeLib, oleTypeLib, false);
}
[Fact]
public void IgnoreGuidType()
{
MockTypeLib mainTypeLib = new MockTypeLib();
mainTypeLib.AddTypeInfo(new MockTypeInfo());
MockTypeLib oleTypeLib = new MockTypeLib(NativeMethods.IID_StdOle);
oleTypeLib.AddTypeInfo(new MockTypeInfo());
oleTypeLib.ContainedTypeInfos[0].TypeName = "GUID";
mainTypeLib.ContainedTypeInfos[0].DefinesVariable(oleTypeLib.ContainedTypeInfos[0]);
RunDependencyWalker(mainTypeLib, oleTypeLib, false);
}
[Fact]
public void IgnoreNetExportedTypeLibs()
{
MockTypeLib mainTypeLib, dependencyTypeLib;
CreateTwoTypeLibs(out mainTypeLib, out dependencyTypeLib);
mainTypeLib.ContainedTypeInfos[0].DefinesFunction(
new MockTypeInfo[] { dependencyTypeLib.ContainedTypeInfos[0] }, dependencyTypeLib.ContainedTypeInfos[0]);
dependencyTypeLib.ExportedFromComPlus = "1";
RunDependencyWalker(mainTypeLib, dependencyTypeLib, false);
}
/// <summary>
/// The main type lib is broken... don't expect any results, but make sure we don't throw.
/// </summary>
[Fact]
public void FaultInjectionMainLib()
{
// The primary test here is that we don't throw, which can't be explicitly expressed in NUnit...
// other asserts are secondary
foreach (MockTypeLibrariesFailurePoints failurePoint in Enum.GetValues(typeof(MockTypeLibrariesFailurePoints)))
{
MockTypeLib mainTypeLib = new MockTypeLib();
mainTypeLib.AddTypeInfo(new MockTypeInfo());
// Make it the StdOle lib to exercise the ITypeInfo.GetDocumentation failure point
MockTypeLib dependencyTypeLib = new MockTypeLib(NativeMethods.IID_StdOle);
dependencyTypeLib.AddTypeInfo(new MockTypeInfo());
COMException failureException = new COMException("unhandled exception in " + failurePoint.ToString());
mainTypeLib.InjectFailure(failurePoint, failureException);
dependencyTypeLib.InjectFailure(failurePoint, failureException);
mainTypeLib.ContainedTypeInfos[0].ImplementsInterface(dependencyTypeLib.ContainedTypeInfos[0]);
mainTypeLib.ContainedTypeInfos[0].DefinesVariable(dependencyTypeLib.ContainedTypeInfos[0]);
mainTypeLib.ContainedTypeInfos[0].DefinesFunction(
new MockTypeInfo[] { dependencyTypeLib.ContainedTypeInfos[0] }, dependencyTypeLib.ContainedTypeInfos[0]);
ComDependencyWalker walker = new ComDependencyWalker(new MarshalReleaseComObject(MockReleaseComObject));
walker.AnalyzeTypeLibrary(mainTypeLib);
Assert.Single(walker.EncounteredProblems); // "Test failed for failure point " + failurePoint.ToString()
Assert.Equal(failureException, walker.EncounteredProblems[0]); // "Test failed for failure point " + failurePoint.ToString()
mainTypeLib.AssertAllHandlesReleased();
dependencyTypeLib.AssertAllHandlesReleased();
}
}
private static void CreateFaultInjectionTypeLibs(MockTypeLibrariesFailurePoints failurePoint, out MockTypeLib mainTypeLib,
out MockTypeLib dependencyTypeLibGood1, out MockTypeLib dependencyTypeLibBad1,
out MockTypeLib dependencyTypeLibGood2, out MockTypeLib dependencyTypeLibBad2)
{
mainTypeLib = new MockTypeLib();
mainTypeLib.AddTypeInfo(new MockTypeInfo());
mainTypeLib.AddTypeInfo(new MockTypeInfo());
dependencyTypeLibGood1 = new MockTypeLib();
dependencyTypeLibGood1.AddTypeInfo(new MockTypeInfo());
// Make it the StdOle lib to exercise the ITypeInfo.GetDocumentation failure point
dependencyTypeLibBad1 = new MockTypeLib(NativeMethods.IID_StdOle);
dependencyTypeLibBad1.AddTypeInfo(new MockTypeInfo());
dependencyTypeLibGood2 = new MockTypeLib();
dependencyTypeLibGood2.AddTypeInfo(new MockTypeInfo());
// Make it the StdOle lib to exercise the ITypeInfo.GetDocumentation failure point
dependencyTypeLibBad2 = new MockTypeLib(NativeMethods.IID_StdOle);
dependencyTypeLibBad2.AddTypeInfo(new MockTypeInfo());
COMException failureException = new COMException("unhandled exception in " + failurePoint.ToString());
dependencyTypeLibBad1.InjectFailure(failurePoint, failureException);
dependencyTypeLibBad2.InjectFailure(failurePoint, failureException);
}
private void RunDependencyWalkerFaultInjection(MockTypeLibrariesFailurePoints failurePoint, MockTypeLib mainTypeLib, MockTypeLib dependencyTypeLibGood1, MockTypeLib dependencyTypeLibBad1, MockTypeLib dependencyTypeLibGood2, MockTypeLib dependencyTypeLibBad2)
{
ComDependencyWalker walker = new ComDependencyWalker(new MarshalReleaseComObject(MockReleaseComObject));
walker.AnalyzeTypeLibrary(mainTypeLib);
// Did the current failure point get hit for this test? If not then no point in checking anything
// The previous test (FaultInjectionMainLib) ensures that all defined failure points actually
// cause some sort of trouble
if (walker.EncounteredProblems.Count > 0)
{
TYPELIBATTR[] dependencies = walker.GetDependencies();
AssertDependenciesContainTypeLib("Test failed for failure point " + failurePoint.ToString(),
dependencies, mainTypeLib, true);
AssertDependenciesContainTypeLib("Test failed for failure point " + failurePoint.ToString(),
dependencies, dependencyTypeLibGood1, true);
AssertDependenciesContainTypeLib("Test failed for failure point " + failurePoint.ToString(),
dependencies, dependencyTypeLibGood2, true);
AssertDependenciesContainTypeLib("Test failed for failure point " + failurePoint.ToString(),
dependencies, dependencyTypeLibBad1, false);
AssertDependenciesContainTypeLib("Test failed for failure point " + failurePoint.ToString(),
dependencies, dependencyTypeLibBad2, false);
}
mainTypeLib.AssertAllHandlesReleased();
dependencyTypeLibGood1.AssertAllHandlesReleased();
dependencyTypeLibGood2.AssertAllHandlesReleased();
dependencyTypeLibBad1.AssertAllHandlesReleased();
dependencyTypeLibBad2.AssertAllHandlesReleased();
}
[Fact]
public void FullDependenciesWithIncrementalAnalysis()
{
MockTypeLib mainTypeLib1, mainTypeLib2, mainTypeLib3, dependencyTypeLib1, dependencyTypeLib2, dependencyTypeLib3;
CreateTwoTypeLibs(out mainTypeLib1, out dependencyTypeLib1);
CreateTwoTypeLibs(out mainTypeLib2, out dependencyTypeLib2);
CreateTwoTypeLibs(out mainTypeLib3, out dependencyTypeLib3);
mainTypeLib1.ContainedTypeInfos[0].DefinesVariable(dependencyTypeLib1.ContainedTypeInfos[0]);
mainTypeLib2.ContainedTypeInfos[0].DefinesVariable(dependencyTypeLib1.ContainedTypeInfos[0]);
mainTypeLib2.ContainedTypeInfos[0].DefinesVariable(dependencyTypeLib2.ContainedTypeInfos[0]);
mainTypeLib3.ContainedTypeInfos[0].DefinesVariable(dependencyTypeLib1.ContainedTypeInfos[0]);
mainTypeLib3.ContainedTypeInfos[0].DefinesVariable(dependencyTypeLib3.ContainedTypeInfos[0]);
ComDependencyWalker walker = new ComDependencyWalker(MockReleaseComObject);
walker.AnalyzeTypeLibrary(mainTypeLib1);
TYPELIBATTR[] dependencies = walker.GetDependencies();
ICollection<string> analyzedTypes = walker.GetAnalyzedTypeNames();
AssertDependenciesContainTypeLib(dependencies, dependencyTypeLib1, true);
AssertDependenciesContainTypeLib(dependencies, dependencyTypeLib2, false);
AssertDependenciesContainTypeLib(dependencies, dependencyTypeLib3, false);
Assert.Equal(2, analyzedTypes.Count);
walker.ClearDependencyList();
walker.AnalyzeTypeLibrary(mainTypeLib2);
dependencies = walker.GetDependencies();
analyzedTypes = walker.GetAnalyzedTypeNames();
AssertDependenciesContainTypeLib(dependencies, dependencyTypeLib1, true);
AssertDependenciesContainTypeLib(dependencies, dependencyTypeLib2, true);
AssertDependenciesContainTypeLib(dependencies, dependencyTypeLib3, false);
Assert.Equal(4, analyzedTypes.Count);
walker.ClearDependencyList();
walker.AnalyzeTypeLibrary(mainTypeLib3);
dependencies = walker.GetDependencies();
analyzedTypes = walker.GetAnalyzedTypeNames();
AssertDependenciesContainTypeLib(dependencies, dependencyTypeLib1, true);
AssertDependenciesContainTypeLib(dependencies, dependencyTypeLib2, false);
AssertDependenciesContainTypeLib(dependencies, dependencyTypeLib3, true);
Assert.Equal(6, analyzedTypes.Count);
}
}
}
|