|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#if FEATURE_APPDOMAIN
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.BackEnd;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.Tasks;
using Microsoft.Build.Utilities;
using Shouldly;
using Xunit;
// TYPELIBATTR clashes with the one in InteropServices.
using TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR;
#nullable disable
namespace Microsoft.Build.UnitTests
{
public sealed class ResolveComReference_Tests
{
/// <summary>
/// Creates a valid task item that's modified later
/// </summary>
private TaskItem SetupTaskItem()
{
var item = new TaskItem();
item.SetMetadata(ComReferenceItemMetadataNames.guid, "{5C6D0C4D-D530-4B08-B22F-307CA6BFCB65}");
item.SetMetadata(ComReferenceItemMetadataNames.versionMajor, "1");
item.SetMetadata(ComReferenceItemMetadataNames.versionMinor, "0");
item.SetMetadata(ComReferenceItemMetadataNames.lcid, "0");
item.SetMetadata(ComReferenceItemMetadataNames.wrapperTool, "tlbimp");
return item;
}
private void AssertReference(ITaskItem item, bool valid, string attribute)
{
string missingOrInvalidAttribute;
Assert.Equal(ResolveComReference.VerifyReferenceMetadataForNameItem(item, out missingOrInvalidAttribute), valid);
Assert.Equal(missingOrInvalidAttribute, attribute);
}
private void AssertMetadataInitialized(ITaskItem item, string metadataName, string metadataValue)
{
Assert.Equal(item.GetMetadata(metadataName), metadataValue);
}
/// <summary>
/// Issue in this bug was an ArgumentNullException when ResolvedAssemblyReferences was null
/// </summary>
[Fact]
public void GetResolvedASsemblyReferenceSpecNotNull()
{
var task = new ResolveComReference();
Assert.NotNull(task.GetResolvedAssemblyReferenceItemSpecs());
}
[Fact]
public void TestSerializationAndDeserialization()
{
ResolveComReferenceCache cache = new("path1", "path2");
cache.componentTimestamps = new()
{
{ "first", DateTime.Now },
{ "second", DateTime.FromBinary(10000) },
};
ResolveComReferenceCache cache2 = null;
using (TestEnvironment env = TestEnvironment.Create())
{
TransientTestFile file = env.CreateFile();
cache.SerializeCache(file.Path, null);
cache2 = StateFileBase.DeserializeCache<ResolveComReferenceCache>(file.Path, null);
}
cache2.tlbImpLocation.ShouldBe(cache.tlbImpLocation);
cache2.axImpLocation.ShouldBe(cache.axImpLocation);
cache2.componentTimestamps.Count.ShouldBe(cache.componentTimestamps.Count);
cache2.componentTimestamps["second"].ShouldBe(cache.componentTimestamps["second"]);
}
/*
* Method: CheckComReferenceAttributeVerificationForNameItems
*
* Checks if verification of Com reference item metadata works properly
*/
[Fact]
public void CheckComReferenceMetadataVerificationForNameItems()
{
// valid item
TaskItem item = SetupTaskItem();
AssertReference(item, true, "");
// invalid guid
item = SetupTaskItem();
item.SetMetadata(ComReferenceItemMetadataNames.guid, "{I'm pretty sure this is not a valid guid}");
AssertReference(item, false, ComReferenceItemMetadataNames.guid);
// missing guid
item = SetupTaskItem();
item.RemoveMetadata(ComReferenceItemMetadataNames.guid);
AssertReference(item, false, ComReferenceItemMetadataNames.guid);
// invalid verMajor
item = SetupTaskItem();
item.SetMetadata(ComReferenceItemMetadataNames.versionMajor, "eleventy one");
AssertReference(item, false, ComReferenceItemMetadataNames.versionMajor);
// missing verMajor
item = SetupTaskItem();
item.RemoveMetadata(ComReferenceItemMetadataNames.versionMajor);
AssertReference(item, false, ComReferenceItemMetadataNames.versionMajor);
// invalid verMinor
item = SetupTaskItem();
item.SetMetadata(ComReferenceItemMetadataNames.versionMinor, "eleventy one");
AssertReference(item, false, ComReferenceItemMetadataNames.versionMinor);
// missing verMinor
item = SetupTaskItem();
item.RemoveMetadata(ComReferenceItemMetadataNames.versionMinor);
AssertReference(item, false, ComReferenceItemMetadataNames.versionMinor);
// invalid lcid
item = SetupTaskItem();
item.SetMetadata(ComReferenceItemMetadataNames.lcid, "Mars-us");
AssertReference(item, false, ComReferenceItemMetadataNames.lcid);
// missing lcid - it's optional, so this should work ok
item = SetupTaskItem();
item.RemoveMetadata(ComReferenceItemMetadataNames.lcid);
AssertReference(item, true, String.Empty);
// invalid tool
item = SetupTaskItem();
item.SetMetadata(ComReferenceItemMetadataNames.wrapperTool, "crowbar");
AssertReference(item, false, ComReferenceItemMetadataNames.wrapperTool);
// missing tool - it's optional, so this should work ok
item = SetupTaskItem();
item.RemoveMetadata(ComReferenceItemMetadataNames.wrapperTool);
AssertReference(item, true, String.Empty);
}
/*
* Method: CheckComReferenceAttributeInitializationForNameItems
*
* Checks if missing optional attributes for COM name references get initialized correctly
*/
[Fact]
public void CheckComReferenceMetadataInitializationForNameItems()
{
// missing lcid - should get initialized to 0
TaskItem item = SetupTaskItem();
item.RemoveMetadata(ComReferenceItemMetadataNames.lcid);
ResolveComReference.InitializeDefaultMetadataForNameItem(item);
AssertMetadataInitialized(item, ComReferenceItemMetadataNames.lcid, "0");
// existing lcid - should not get modified
item = SetupTaskItem();
item.SetMetadata(ComReferenceItemMetadataNames.lcid, "1033");
ResolveComReference.InitializeDefaultMetadataForNameItem(item);
AssertMetadataInitialized(item, ComReferenceItemMetadataNames.lcid, "1033");
// missing wrapperTool - should get initialized to tlbimp
item = SetupTaskItem();
item.RemoveMetadata(ComReferenceItemMetadataNames.wrapperTool);
ResolveComReference.InitializeDefaultMetadataForNameItem(item);
AssertMetadataInitialized(item, ComReferenceItemMetadataNames.wrapperTool, ComReferenceTypes.tlbimp);
// existing wrapperTool - should not get modified
item = SetupTaskItem();
item.SetMetadata(ComReferenceItemMetadataNames.wrapperTool, ComReferenceTypes.aximp);
ResolveComReference.InitializeDefaultMetadataForNameItem(item);
AssertMetadataInitialized(item, ComReferenceItemMetadataNames.wrapperTool, ComReferenceTypes.aximp);
}
/*
* Method: CheckComReferenceAttributeInitializationForFileItems
*
* Checks if missing optional attributes for COM file references get initialized correctly
*/
[Fact]
public void CheckComReferenceMetadataInitializationForFileItems()
{
// missing wrapperTool - should get initialized to tlbimp
TaskItem item = SetupTaskItem();
item.RemoveMetadata(ComReferenceItemMetadataNames.wrapperTool);
ResolveComReference.InitializeDefaultMetadataForFileItem(item);
AssertMetadataInitialized(item, ComReferenceItemMetadataNames.wrapperTool, ComReferenceTypes.tlbimp);
// existing wrapperTool - should not get modified
item = SetupTaskItem();
item.SetMetadata(ComReferenceItemMetadataNames.wrapperTool, ComReferenceTypes.aximp);
ResolveComReference.InitializeDefaultMetadataForFileItem(item);
AssertMetadataInitialized(item, ComReferenceItemMetadataNames.wrapperTool, ComReferenceTypes.aximp);
}
/// <summary>
/// Helper function for creating a COM reference task item instance
/// </summary>
private TaskItem CreateComReferenceTaskItem(string itemSpec, string guid, string vMajor, string vMinor, string lcid, string wrapperType, string embedInteropTypes)
{
var item = new TaskItem(itemSpec);
item.SetMetadata(ComReferenceItemMetadataNames.guid, guid);
item.SetMetadata(ComReferenceItemMetadataNames.versionMajor, vMajor);
item.SetMetadata(ComReferenceItemMetadataNames.versionMinor, vMinor);
item.SetMetadata(ComReferenceItemMetadataNames.lcid, lcid);
item.SetMetadata(ComReferenceItemMetadataNames.wrapperTool, wrapperType);
item.SetMetadata(ItemMetadataNames.embedInteropTypes, embedInteropTypes);
return item;
}
/// <summary>
/// Helper function for creating a COM reference task item instance
/// </summary>
private TaskItem CreateComReferenceTaskItem(string itemSpec, string guid, string vMajor, string vMinor, string lcid, string wrapperType)
{
return CreateComReferenceTaskItem(itemSpec, guid, vMajor, vMinor, lcid, wrapperType, String.Empty);
}
/// <summary>
/// Test the ResolveComReference.TaskItemToTypeLibAttr method
/// </summary>
[Fact]
public void CheckTaskItemToTypeLibAttr()
{
Guid refGuid = Guid.NewGuid();
TaskItem reference = CreateComReferenceTaskItem("ref", refGuid.ToString(), "11", "0", "1033", ComReferenceTypes.tlbimp);
TYPELIBATTR refAttr = ResolveComReference.TaskItemToTypeLibAttr(reference);
Assert.Equal(refGuid, refAttr.guid); // "incorrect guid"
Assert.Equal(11, refAttr.wMajorVerNum); // "incorrect version major"
Assert.Equal(0, refAttr.wMinorVerNum); // "incorrect version minor"
Assert.Equal(1033, refAttr.lcid); // "incorrect lcid"
}
/// <summary>
/// Helper function for creating a ComReferenceInfo object using an existing TaskInfo object and
/// typelib name/path. The type lib pointer will obviously not be initialized, so this object cannot
/// be used in any code that uses it.
/// </summary>
private ComReferenceInfo CreateComReferenceInfo(ITaskItem taskItem, string typeLibName, string typeLibPath)
{
var referenceInfo = new ComReferenceInfo();
referenceInfo.taskItem = taskItem;
referenceInfo.attr = ResolveComReference.TaskItemToTypeLibAttr(taskItem);
referenceInfo.typeLibName = typeLibName;
referenceInfo.fullTypeLibPath = typeLibPath;
referenceInfo.strippedTypeLibPath = typeLibPath;
referenceInfo.typeLibPointer = null;
return referenceInfo;
}
/// <summary>
/// Create a few test references for unit tests
/// </summary>
private void CreateTestReferences(
out ComReferenceInfo axRefInfo, out ComReferenceInfo tlbRefInfo, out ComReferenceInfo piaRefInfo,
out TYPELIBATTR axAttr, out TYPELIBATTR tlbAttr, out TYPELIBATTR piaAttr, out TYPELIBATTR notInProjectAttr)
{
// doing my part to deplete the worldwide guid reserves...
Guid axGuid = Guid.NewGuid();
Guid tlbGuid = Guid.NewGuid();
Guid piaGuid = Guid.NewGuid();
// create reference task items
TaskItem axTaskItem = CreateComReferenceTaskItem("axref", axGuid.ToString(), "1", "0", "1033", ComReferenceTypes.aximp);
TaskItem tlbTaskItem = CreateComReferenceTaskItem("tlbref", tlbGuid.ToString(), "5", "1", "0", ComReferenceTypes.tlbimp);
TaskItem piaTaskItem = CreateComReferenceTaskItem("piaref", piaGuid.ToString(), "999", "444", "123", ComReferenceTypes.primary);
// create reference infos
axRefInfo = CreateComReferenceInfo(axTaskItem, "AxRefLibName", "AxRefLibPath");
tlbRefInfo = CreateComReferenceInfo(tlbTaskItem, "TlbRefLibName", "TlbRefLibPath");
piaRefInfo = CreateComReferenceInfo(piaTaskItem, "PiaRefLibName", "PiaRefLibPath");
// get the references' typelib attributes
axAttr = ResolveComReference.TaskItemToTypeLibAttr(axTaskItem);
tlbAttr = ResolveComReference.TaskItemToTypeLibAttr(tlbTaskItem);
piaAttr = ResolveComReference.TaskItemToTypeLibAttr(piaTaskItem);
// create typelib attributes not matching any of the project refs
notInProjectAttr = new TYPELIBATTR();
notInProjectAttr.guid = tlbGuid;
notInProjectAttr.wMajorVerNum = 5;
notInProjectAttr.wMinorVerNum = 1;
notInProjectAttr.lcid = 1033;
}
/// <summary>
/// Unit test for the ResolveComReference.IsExistingProjectReference() method
/// </summary>
[Fact]
public void CheckIsExistingProjectReference()
{
TYPELIBATTR axAttr, tlbAttr, piaAttr, notInProjectAttr;
ComReferenceInfo axRefInfo, tlbRefInfo, piaRefInfo;
CreateTestReferences(out axRefInfo, out tlbRefInfo, out piaRefInfo,
out axAttr, out tlbAttr, out piaAttr, out notInProjectAttr);
var rcr = new ResolveComReference();
// populate the ResolveComReference's list of project references
rcr.allProjectRefs = new List<ComReferenceInfo>();
rcr.allProjectRefs.Add(axRefInfo);
rcr.allProjectRefs.Add(tlbRefInfo);
rcr.allProjectRefs.Add(piaRefInfo);
// find the Ax ref, matching with any type of reference - should NOT find it
bool retValue = rcr.IsExistingProjectReference(axAttr, null, out ComReferenceInfo referenceInfo);
Assert.True(!retValue && referenceInfo == null); // "ActiveX ref should NOT be found for any type of ref"
// find the Ax ref, matching with aximp types - should find it
retValue = rcr.IsExistingProjectReference(axAttr, ComReferenceTypes.aximp, out referenceInfo);
Assert.True(retValue && referenceInfo == axRefInfo); // "ActiveX ref should be found for aximp ref types"
// find the Ax ref, matching with tlbimp types - should NOT find it
retValue = rcr.IsExistingProjectReference(axAttr, ComReferenceTypes.tlbimp, out referenceInfo);
Assert.True(!retValue && referenceInfo == null); // "ActiveX ref should NOT be found for tlbimp ref types"
// find the Tlb ref, matching with any type of reference - should find it
retValue = rcr.IsExistingProjectReference(tlbAttr, null, out referenceInfo);
Assert.True(retValue && referenceInfo == tlbRefInfo); // "Tlb ref should be found for any type of ref"
// find the Tlb ref, matching with tlbimp types - should find it
retValue = rcr.IsExistingProjectReference(tlbAttr, ComReferenceTypes.tlbimp, out referenceInfo);
Assert.True(retValue && referenceInfo == tlbRefInfo); // "Tlb ref should be found for tlbimp ref types"
// find the Tlb ref, matching with pia types - should NOT find it
retValue = rcr.IsExistingProjectReference(tlbAttr, ComReferenceTypes.primary, out referenceInfo);
Assert.True(!retValue && referenceInfo == null); // "Tlb ref should NOT be found for primary ref types"
// find the Pia ref, matching with any type of reference - should find it
retValue = rcr.IsExistingProjectReference(piaAttr, null, out referenceInfo);
Assert.True(retValue && referenceInfo == piaRefInfo); // "Pia ref should be found for any type of ref"
// find the Pia ref, matching with pia types - should find it
retValue = rcr.IsExistingProjectReference(piaAttr, ComReferenceTypes.primary, out referenceInfo);
Assert.True(retValue && referenceInfo == piaRefInfo); // "Pia ref should be found for pia ref types"
// find the Pia ref, matching with pia types - should NOT find it
retValue = rcr.IsExistingProjectReference(piaAttr, ComReferenceTypes.aximp, out referenceInfo);
Assert.True(!retValue && referenceInfo == null); // "Pia ref should NOT be found for aximp ref types"
// try to find a non existing reference
retValue = rcr.IsExistingProjectReference(notInProjectAttr, null, out referenceInfo);
Assert.True(!retValue && referenceInfo == null); // "not in project ref should not be found"
}
/// <summary>
/// Unit test for the ResolveComReference.IsExistingDependencyReference() method
/// </summary>
[Fact]
public void CheckIsExistingDependencyReference()
{
TYPELIBATTR axAttr, tlbAttr, piaAttr, notInProjectAttr;
ComReferenceInfo axRefInfo, tlbRefInfo, piaRefInfo;
CreateTestReferences(out axRefInfo, out tlbRefInfo, out piaRefInfo,
out axAttr, out tlbAttr, out piaAttr, out notInProjectAttr);
var rcr = new ResolveComReference();
// populate the ResolveComReference's list of project references
rcr.allDependencyRefs = new List<ComReferenceInfo>();
rcr.allDependencyRefs.Add(axRefInfo);
rcr.allDependencyRefs.Add(tlbRefInfo);
rcr.allDependencyRefs.Add(piaRefInfo);
// find the Ax ref - should find it
bool retValue = rcr.IsExistingDependencyReference(axAttr, out ComReferenceInfo referenceInfo);
Assert.True(retValue && referenceInfo == axRefInfo); // "ActiveX ref should be found"
// find the Tlb ref - should find it
retValue = rcr.IsExistingDependencyReference(tlbAttr, out referenceInfo);
Assert.True(retValue && referenceInfo == tlbRefInfo); // "Tlb ref should be found"
// find the Pia ref - should find it
retValue = rcr.IsExistingDependencyReference(piaAttr, out referenceInfo);
Assert.True(retValue && referenceInfo == piaRefInfo); // "Pia ref should be found"
// try to find a non existing reference - should not find it
retValue = rcr.IsExistingDependencyReference(notInProjectAttr, out referenceInfo);
Assert.True(!retValue && referenceInfo == null); // "not in project ref should not be found"
// Now, try to resolve a non-existent ComAssemblyReference.
string path;
IComReferenceResolver resolver = (IComReferenceResolver)rcr;
Assert.False(resolver.ResolveComAssemblyReference("MyAssembly", out path));
Assert.Null(path);
}
/// <summary>
/// ResolveComReference automatically adds missing tlbimp references for aximp references.
/// This test verifies we actually create the missing references.
/// </summary>
[Fact]
public void CheckAddMissingTlbReference()
{
TYPELIBATTR axAttr, tlbAttr, piaAttr, notInProjectAttr;
ComReferenceInfo axRefInfo, tlbRefInfo, piaRefInfo;
CreateTestReferences(out axRefInfo, out tlbRefInfo, out piaRefInfo,
out axAttr, out tlbAttr, out piaAttr, out notInProjectAttr);
var rcr = new ResolveComReference();
rcr.BuildEngine = new MockEngine();
// populate the ResolveComReference's list of project references
rcr.allProjectRefs = new List<ComReferenceInfo>();
rcr.allProjectRefs.Add(axRefInfo);
rcr.allProjectRefs.Add(tlbRefInfo);
rcr.allProjectRefs.Add(piaRefInfo);
rcr.AddMissingTlbReferences();
Assert.Equal(4, rcr.allProjectRefs.Count); // "There should be four references now"
ComReferenceInfo newTlbInfo = (ComReferenceInfo)rcr.allProjectRefs[3];
Assert.Equal(axRefInfo.primaryOfAxImpRef, newTlbInfo); // "axRefInfo should hold back reference to tlbRefInfo"
Assert.True(ComReference.AreTypeLibAttrEqual(newTlbInfo.attr, axRefInfo.attr)); // "The added reference should have the same attributes as the Ax reference"
Assert.Equal(axRefInfo.typeLibName, newTlbInfo.typeLibName); // "The added reference should have the same type lib name as the Ax reference"
Assert.Equal(axRefInfo.strippedTypeLibPath, newTlbInfo.strippedTypeLibPath); // "The added reference should have the same type lib path as the Ax reference"
Assert.Equal(newTlbInfo.taskItem.ItemSpec, axRefInfo.taskItem.ItemSpec); // "The added reference should have the same task item spec as the Ax reference"
Assert.Equal(ComReferenceTypes.primaryortlbimp, newTlbInfo.taskItem.GetMetadata(ComReferenceItemMetadataNames.wrapperTool)); // "The added reference should have the tlbimp/primary wrapper tool"
rcr.AddMissingTlbReferences();
Assert.Equal(4, rcr.allProjectRefs.Count); // "There should still be four references"
}
[Fact]
public void BothKeyFileAndKeyContainer()
{
var rcr = new ResolveComReference();
var e = new MockEngine();
rcr.BuildEngine = e;
rcr.KeyFile = "foo";
rcr.KeyContainer = "bar";
Assert.False(rcr.Execute());
e.AssertLogContains("MSB3300");
}
[Fact]
public void DelaySignWithoutEitherKeyFileOrKeyContainer()
{
var rcr = new ResolveComReference();
var e = new MockEngine();
rcr.BuildEngine = e;
rcr.DelaySign = true;
Assert.False(rcr.Execute());
e.AssertLogContains("MSB3301");
}
/// <summary>
/// Test if assemblies located in the gac get their CopyLocal attribute set to False
/// </summary>
[Fact]
public void CheckSetCopyLocalToFalseOnEmbedInteropTypesAssemblies()
{
string gacPath = @"C:\windows\gac";
ResolveComReference rcr = new ResolveComReference();
rcr.BuildEngine = new MockEngine();
// the matrix of TargetFrameworkVersion values we are testing
string[] fxVersions =
{
"v2.0",
"v3.0",
"v3.5",
"v4.0"
};
for (int i = 0; i < fxVersions.Length; i++)
{
string fxVersion = fxVersions[i];
var taskItems = new List<ITaskItem>();
var nonGacNoPrivate = new TaskItem(@"C:\windows\gar\test1.dll");
nonGacNoPrivate.SetMetadata(ItemMetadataNames.embedInteropTypes, "true");
var gacNoPrivate = new TaskItem(@"C:\windows\gac\assembly1.dll");
gacNoPrivate.SetMetadata(ItemMetadataNames.embedInteropTypes, "true");
var nonGacPrivateFalse = new TaskItem(@"C:\windows\gar\test1.dll");
nonGacPrivateFalse.SetMetadata(ItemMetadataNames.privateMetadata, "false");
nonGacPrivateFalse.SetMetadata(ItemMetadataNames.embedInteropTypes, "true");
var gacPrivateFalse = new TaskItem(@"C:\windows\gac\assembly1.dll");
gacPrivateFalse.SetMetadata(ItemMetadataNames.privateMetadata, "false");
gacPrivateFalse.SetMetadata(ItemMetadataNames.embedInteropTypes, "true");
var nonGacPrivateTrue = new TaskItem(@"C:\windows\gar\test1.dll");
nonGacPrivateTrue.SetMetadata(ItemMetadataNames.privateMetadata, "true");
nonGacPrivateTrue.SetMetadata(ItemMetadataNames.embedInteropTypes, "true");
var gacPrivateTrue = new TaskItem(@"C:\windows\gac\assembly1.dll");
gacPrivateTrue.SetMetadata(ItemMetadataNames.privateMetadata, "true");
gacPrivateTrue.SetMetadata(ItemMetadataNames.embedInteropTypes, "true");
taskItems.Add(nonGacNoPrivate);
taskItems.Add(gacNoPrivate);
taskItems.Add(nonGacPrivateFalse);
taskItems.Add(gacPrivateFalse);
taskItems.Add(nonGacPrivateTrue);
taskItems.Add(gacPrivateTrue);
rcr.TargetFrameworkVersion = fxVersion;
rcr.SetFrameworkVersionFromString(rcr.TargetFrameworkVersion);
rcr.SetCopyLocalToFalseOnGacOrNoPIAAssemblies(taskItems, gacPath);
bool enabledNoPIA = false;
switch (fxVersion)
{
case "v4.0":
enabledNoPIA = true;
break;
default:
break;
}
// if Private is missing, by default GAC items are CopyLocal=false, non GAC CopyLocal=true
Assert.Equal(nonGacNoPrivate.GetMetadata(ItemMetadataNames.copyLocal), enabledNoPIA ? "false" : "true");
Assert.Equal(gacNoPrivate.GetMetadata(ItemMetadataNames.copyLocal), enabledNoPIA ? "false" : "false");
// if Private is set, it takes precedence
Assert.Equal(nonGacPrivateFalse.GetMetadata(ItemMetadataNames.copyLocal), enabledNoPIA ? "false" : "false");
Assert.Equal(gacPrivateFalse.GetMetadata(ItemMetadataNames.copyLocal), enabledNoPIA ? "false" : "false");
Assert.Equal(nonGacPrivateTrue.GetMetadata(ItemMetadataNames.copyLocal), enabledNoPIA ? "false" : "true");
Assert.Equal(gacPrivateTrue.GetMetadata(ItemMetadataNames.copyLocal), enabledNoPIA ? "false" : "true");
}
}
/// <summary>
/// Test if assemblies located in the gac get their CopyLocal attribute set to False
/// </summary>
[Fact]
public void CheckSetCopyLocalToFalseOnGacAssemblies()
{
string gacPath = @"C:\windows\gac";
var rcr = new ResolveComReference();
rcr.BuildEngine = new MockEngine();
var taskItems = new List<ITaskItem>();
TaskItem nonGacNoPrivate = new TaskItem(@"C:\windows\gar\test1.dll");
TaskItem gacNoPrivate = new TaskItem(@"C:\windows\gac\assembly1.dll");
TaskItem nonGacPrivateFalse = new TaskItem(@"C:\windows\gar\test1.dll");
nonGacPrivateFalse.SetMetadata(ItemMetadataNames.privateMetadata, "false");
TaskItem gacPrivateFalse = new TaskItem(@"C:\windows\gac\assembly1.dll");
gacPrivateFalse.SetMetadata(ItemMetadataNames.privateMetadata, "false");
TaskItem nonGacPrivateTrue = new TaskItem(@"C:\windows\gar\test1.dll");
nonGacPrivateTrue.SetMetadata(ItemMetadataNames.privateMetadata, "true");
TaskItem gacPrivateTrue = new TaskItem(@"C:\windows\gac\assembly1.dll");
gacPrivateTrue.SetMetadata(ItemMetadataNames.privateMetadata, "true");
taskItems.Add(nonGacNoPrivate);
taskItems.Add(gacNoPrivate);
taskItems.Add(nonGacPrivateFalse);
taskItems.Add(gacPrivateFalse);
taskItems.Add(nonGacPrivateTrue);
taskItems.Add(gacPrivateTrue);
rcr.SetCopyLocalToFalseOnGacOrNoPIAAssemblies(taskItems, gacPath);
// if Private is missing, by default GAC items are CopyLocal=false, non GAC CopyLocal=true
Assert.Equal("true", nonGacNoPrivate.GetMetadata(ItemMetadataNames.copyLocal)); // "Non Gac assembly, missing Private, should be TRUE"
Assert.Equal("false", gacNoPrivate.GetMetadata(ItemMetadataNames.copyLocal)); // "Gac assembly, missing Private, should be FALSE"
// if Private is set, it takes precedence
Assert.Equal("false", nonGacPrivateFalse.GetMetadata(ItemMetadataNames.copyLocal)); // "Non Gac assembly, Private false, should be FALSE"
Assert.Equal("false", gacPrivateFalse.GetMetadata(ItemMetadataNames.copyLocal)); // "Gac assembly, Private false, should be FALSE"
Assert.Equal("true", nonGacPrivateTrue.GetMetadata(ItemMetadataNames.copyLocal)); // "Non Gac assembly, Private true, should be TRUE"
Assert.Equal("true", gacPrivateTrue.GetMetadata(ItemMetadataNames.copyLocal)); // "Gac assembly, Private true, should be TRUE"
}
/// <summary>
/// Make sure the conflicting references are detected correctly
/// </summary>
[Fact]
public void TestCheckForConflictingReferences()
{
TYPELIBATTR axAttr, tlbAttr, piaAttr, notInProjectAttr;
ComReferenceInfo axRefInfo, tlbRefInfo, piaRefInfo;
CreateTestReferences(out axRefInfo, out tlbRefInfo, out piaRefInfo,
out axAttr, out tlbAttr, out piaAttr, out notInProjectAttr);
var rcr = new ResolveComReference();
rcr.BuildEngine = new MockEngine();
// populate the ResolveComReference's list of project references
rcr.allProjectRefs = new List<ComReferenceInfo>();
rcr.allProjectRefs.Add(axRefInfo);
rcr.allProjectRefs.Add(tlbRefInfo);
rcr.allProjectRefs.Add(piaRefInfo);
// no conflicts should be found with just the three initial refs
Assert.True(rcr.CheckForConflictingReferences());
Assert.Equal(3, rcr.allProjectRefs.Count);
// duplicate refs should not be treated as conflicts
ComReferenceInfo referenceInfo = new ComReferenceInfo(tlbRefInfo);
rcr.allProjectRefs.Add(referenceInfo);
referenceInfo = new ComReferenceInfo(axRefInfo);
rcr.allProjectRefs.Add(referenceInfo);
referenceInfo = new ComReferenceInfo(piaRefInfo);
rcr.allProjectRefs.Add(referenceInfo);
Assert.True(rcr.CheckForConflictingReferences());
Assert.Equal(6, rcr.allProjectRefs.Count);
// tlb and ax refs with same lib name but different attributes should be considered conflicting
// We don't care about typelib name conflicts for PIA refs, because we don't have to create wrappers for them
var conflictTlb = new ComReferenceInfo(tlbRefInfo);
conflictTlb.attr = notInProjectAttr;
rcr.allProjectRefs.Add(conflictTlb);
var conflictAx = new ComReferenceInfo(axRefInfo);
conflictAx.attr = notInProjectAttr;
rcr.allProjectRefs.Add(conflictAx);
var piaRef = new ComReferenceInfo(piaRefInfo);
piaRef.attr = notInProjectAttr;
rcr.allProjectRefs.Add(piaRef);
Assert.False(rcr.CheckForConflictingReferences());
// ... and conflicting references should have been removed
Assert.Equal(7, rcr.allProjectRefs.Count);
Assert.DoesNotContain(conflictTlb, rcr.allProjectRefs);
Assert.DoesNotContain(conflictAx, rcr.allProjectRefs);
Assert.Contains(piaRef, rcr.allProjectRefs);
}
/// <summary>
/// In order to make ResolveComReferences multitargetable, two properties, ExecuteAsTool
/// and SdkToolsPath were added. In order to have correct behavior when using pre-4.0
/// toolsversions, ExecuteAsTool must default to true, and the paths to the tools will be the
/// v3.5 path. It is difficult to verify the tool paths in a unit test, however, so
/// this was done by ad hoc testing and will be maintained by the dev suites.
/// </summary>
[Fact]
public void MultiTargetingDefaultSetCorrectly()
{
ResolveComReference t = new ResolveComReference();
Assert.True(t.ExecuteAsTool); // "ExecuteAsTool should default to true"
}
/// <summary>
/// When calling AxImp.exe directly, the runtime-callable wrapper needs to be
/// passed via the /rcw switch, so RCR needs to make sure that the ax reference knows about
/// its corresponding TLB wrapper.
/// </summary>
[Fact]
public void AxReferenceKnowsItsRCWCreateTlb()
{
CheckAxReferenceRCWTlbExists(RcwStyle.GenerateTlb /* have RCR create the TLB reference */, false /* don't include TLB version in the interop name */);
}
/// <summary>
/// When calling AxImp.exe directly, the runtime-callable wrapper needs to be
/// passed via the /rcw switch, so RCR needs to make sure that the ax reference knows about
/// its corresponding TLB wrapper.
/// </summary>
[Fact]
public void AxReferenceKnowsItsRCWCreateTlb_IncludeVersion()
{
CheckAxReferenceRCWTlbExists(RcwStyle.GenerateTlb /* have RCR create the TLB reference */, true /* include TLB version in the interop name */);
}
/// <summary>
/// When calling AxImp.exe directly, the runtime-callable wrapper needs to be
/// passed via the /rcw switch, so RCR needs to make sure that the ax reference knows about
/// its corresponding TLB wrapper.
/// </summary>
[Fact]
public void AxReferenceKnowsItsRCWTlbExists()
{
CheckAxReferenceRCWTlbExists(RcwStyle.PreexistingTlb /* pass in the TLB reference */, false /* don't include TLB version in the interop name */);
}
/// <summary>
/// When calling AxImp.exe directly, the runtime-callable wrapper needs to be
/// passed via the /rcw switch, so RCR needs to make sure that the ax reference knows about
/// its corresponding TLB wrapper.
///
/// Tests that still works when IncludeVersionInInteropName = true
/// </summary>
[Fact]
public void AxReferenceKnowsItsRCWTlbExists_IncludeVersion()
{
CheckAxReferenceRCWTlbExists(RcwStyle.PreexistingTlb /* pass in the TLB reference */, true /* include TLB version in the interop name */);
}
/// <summary>
/// When calling AxImp.exe directly, the runtime-callable wrapper needs to be
/// passed via the /rcw switch, so RCR needs to make sure that the ax reference knows about
/// its corresponding TLB wrapper.
/// </summary>
[Fact]
public void AxReferenceKnowsItsRCWPiaExists()
{
CheckAxReferenceRCWTlbExists(RcwStyle.PreexistingPia /* pass in the TLB reference */, false /* don't include version in the interop name */);
}
/// <summary>
/// When calling AxImp.exe directly, the runtime-callable wrapper needs to be
/// passed via the /rcw switch, so RCR needs to make sure that the ax reference knows about
/// its corresponding TLB wrapper.
///
/// Tests that still works when IncludeVersionInInteropName = true
/// </summary>
[Fact]
public void AxReferenceKnowsItsRCWPiaExists_IncludeVersion()
{
CheckAxReferenceRCWTlbExists(RcwStyle.PreexistingPia /* pass in the PIA reference */, true /* include version in the interop name */);
}
private enum RcwStyle
{
GenerateTlb,
PreexistingTlb,
PreexistingPia,
}
/// <summary>
/// Helper method that will new up an AX and matching TLB reference, and verify that the AX reference
/// sets its RCW appropriately.
/// </summary>
private void CheckAxReferenceRCWTlbExists(RcwStyle rcwStyle, bool includeVersionInInteropName)
{
Guid axGuid = Guid.NewGuid();
ComReferenceInfo tlbRefInfo;
var rcr = new ResolveComReference();
rcr.BuildEngine = new MockEngine();
rcr.IncludeVersionInInteropName = includeVersionInInteropName;
rcr.allProjectRefs = new List<ComReferenceInfo>();
TaskItem axTaskItem = CreateComReferenceTaskItem("ref", axGuid.ToString(), "1", "2", "1033", ComReferenceTypes.aximp);
ComReferenceInfo axRefInfo = CreateComReferenceInfo(axTaskItem, "RefLibName", "RefLibPath");
rcr.allProjectRefs.Add(axRefInfo);
switch (rcwStyle)
{
case RcwStyle.GenerateTlb: break;
case RcwStyle.PreexistingTlb:
{
TaskItem tlbTaskItem = CreateComReferenceTaskItem("ref", axGuid.ToString(), "1", "2", "1033", ComReferenceTypes.tlbimp, "true");
tlbRefInfo = CreateComReferenceInfo(tlbTaskItem, "RefLibName", "RefLibPath");
rcr.allProjectRefs.Add(tlbRefInfo);
break;
}
case RcwStyle.PreexistingPia:
{
TaskItem tlbTaskItem = CreateComReferenceTaskItem("ref", axGuid.ToString(), "1", "2", "1033", ComReferenceTypes.primary, "true");
tlbRefInfo = CreateComReferenceInfo(tlbTaskItem, "RefLibName", "RefLibPath");
rcr.allProjectRefs.Add(tlbRefInfo);
break;
}
}
rcr.AddMissingTlbReferences();
Assert.Equal(2, rcr.allProjectRefs.Count); // "Should be two references"
tlbRefInfo = rcr.allProjectRefs[1];
var embedInteropTypes = tlbRefInfo.taskItem.GetMetadata(ItemMetadataNames.embedInteropTypes);
Assert.Equal("false", embedInteropTypes); // "The tlb wrapper for the activex control should have EmbedInteropTypes=false not " + embedInteropTypes);
Assert.True(ComReference.AreTypeLibAttrEqual(tlbRefInfo.attr, axRefInfo.attr)); // "reference information should be the same"
Assert.Equal(TlbReference.GetWrapperFileName(
axRefInfo.taskItem.GetMetadata(ComReferenceItemMetadataNames.tlbReferenceName),
includeVersionInInteropName,
axRefInfo.attr.wMajorVerNum,
axRefInfo.attr.wMinorVerNum),
TlbReference.GetWrapperFileName(
tlbRefInfo.typeLibName,
includeVersionInInteropName,
tlbRefInfo.attr.wMajorVerNum,
tlbRefInfo.attr.wMinorVerNum)); // "Expected Ax reference's RCW name to match the new TLB"
}
}
}
#endif
|