// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.IO; using System.Reflection; using Microsoft.Build.Framework; using Microsoft.Build.Tasks; using Microsoft.Build.Utilities; using Microsoft.Runtime.Hosting; using Microsoft.Build.Shared; using Xunit; namespace Microsoft.Build.UnitTests.AxTlbImp_Tests { sealed public class AxTlbBaseTask_Tests { /// /// Tests the /delaysign switch /// [Fact] public void DelaySign() { AxTlbBaseTask t = new ResolveComReference.AxImp(); Assert.False(t.DelaySign); // "DelaySign should be false by default" CommandLine.ValidateNoParameterStartsWith( t, @"/delaysign", false /* no response file */); t.DelaySign = true; Assert.True(t.DelaySign); // "DelaySign should be true" CommandLine.ValidateHasParameter( t, @"/delaysign", false /* no response file */); } /// /// Tests the /keycontainer: switch /// [Fact] public void KeyContainer() { if (!NativeMethodsShared.IsWindows) { return; // "Key container is not supported, except under Windows" } var t = new ResolveComReference.TlbImp(); t.TypeLibName = "FakeTlb.tlb"; string badParameterValue = "badKeyContainer"; string goodParameterValue = "myKeyContainer"; try { t.ToolPath = Path.GetTempPath(); Assert.Null(t.KeyContainer); // "KeyContainer should be null by default"); CommandLine.ValidateNoParameterStartsWith( t, @"/keycontainer:", false /* no response file */); t.KeyContainer = badParameterValue; Assert.Equal(badParameterValue, t.KeyContainer); // "New KeyContainer value should be set" CommandLine.ValidateHasParameter(t, @"/keycontainer:" + badParameterValue, false /* no response file */); Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.StrongNameUtils.NoKeyPairInContainer", t.KeyContainer); //ensure the key does not exist in the CSP StrongNameHelpers.StrongNameKeyDelete(goodParameterValue); IntPtr publicKeyBlob = IntPtr.Zero; int publicKeyBlobSize = 0; //add key to CSP if (StrongNameHelpers.StrongNameKeyGen(goodParameterValue, 1 /* leave key registered */, out publicKeyBlob, out publicKeyBlobSize) && publicKeyBlob != IntPtr.Zero) { StrongNameHelpers.StrongNameFreeBuffer(publicKeyBlob); t.KeyContainer = goodParameterValue; Assert.Equal(goodParameterValue, t.KeyContainer); // "New KeyContainer value should be set" CommandLine.ValidateHasParameter(t, @"/keycontainer:" + goodParameterValue, false /* no response file */); Utilities.ExecuteTaskAndVerifyLogDoesNotContainErrorFromResource(t, "AxTlbBaseTask.StrongNameUtils.NoKeyPairInContainer", t.KeyContainer); } else { Assert.True(false, "Key container could not be created (perhaps you are not running as admin)."); } } finally { //remove key from CSP StrongNameHelpers.StrongNameKeyDelete(goodParameterValue); // get rid of the generated temp file if (goodParameterValue != null) { File.Delete(goodParameterValue); } } } /// /// Tests the /keycontainer: switch with a space in the name /// [Fact] public void KeyContainerWithSpaces() { AxTlbBaseTask t = new ResolveComReference.AxImp(); string testParameterValue = @"my Key Container"; Assert.Null(t.KeyContainer); // "KeyContainer should be null by default" CommandLine.ValidateNoParameterStartsWith( t, @"/keycontainer:", false /* no response file */); t.KeyContainer = testParameterValue; Assert.Equal(testParameterValue, t.KeyContainer); // "New KeyContainer value should be set" CommandLine.ValidateHasParameter( t, @"/keycontainer:" + testParameterValue, false /* no response file */); } /// /// Tests the /keyfile: switch /// [Fact] public void KeyFile() { var t = new ResolveComReference.AxImp(); t.ActiveXControlName = "FakeControl.ocx"; string badParameterValue = "myKeyFile.key"; string goodParameterValue = null; try { goodParameterValue = FileUtilities.GetTemporaryFile(); t.ToolPath = Path.GetTempPath(); Assert.Null(t.KeyFile); // "KeyFile should be null by default" CommandLine.ValidateNoParameterStartsWith( t, @"/keyfile:", false /* no response file */); t.KeyFile = badParameterValue; Assert.Equal(badParameterValue, t.KeyFile); // "New KeyFile value should be set" CommandLine.ValidateHasParameter( t, @"/keyfile:" + badParameterValue, false /* no response file */); Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.InvalidKeyFileSpecified", t.KeyFile); t.KeyFile = goodParameterValue; Assert.Equal(goodParameterValue, t.KeyFile); // "New KeyFile value should be set" CommandLine.ValidateHasParameter( t, @"/keyfile:" + goodParameterValue, false /* no response file */); Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.StrongNameUtils.NoKeyPairInFile", t.KeyFile); } finally { if (goodParameterValue != null) { // get rid of the generated temp file File.Delete(goodParameterValue); } } } /// /// Tests the /keyfile: switch with a space in the filename /// [Fact] public void KeyFileWithSpaces() { AxTlbBaseTask t = new ResolveComReference.TlbImp(); string testParameterValue = @"C:\Program Files\myKeyFile.key"; Assert.Null(t.KeyFile); // "KeyFile should be null by default" CommandLine.ValidateNoParameterStartsWith( t, @"/keyfile:", false /* no response file */); t.KeyFile = testParameterValue; Assert.Equal(testParameterValue, t.KeyFile); // "New KeyFile value should be set" CommandLine.ValidateHasParameter( t, @"/keyfile:" + testParameterValue, false /* no response file */); } /// /// Tests the SdkToolsPath property: Should log an error if it's null or a bad path. /// [Fact] public void SdkToolsPath() { var t = new ResolveComReference.TlbImp(); t.TypeLibName = "FakeLibrary.tlb"; string badParameterValue = @"C:\Program Files\Microsoft Visual Studio 10.0\My Fake SDK Path"; string goodParameterValue = Path.GetTempPath(); bool taskPassed; Assert.Null(t.SdkToolsPath); // "SdkToolsPath should be null by default" Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", t.SdkToolsPath, t.ToolPath); t.SdkToolsPath = badParameterValue; Assert.Equal(badParameterValue, t.SdkToolsPath); // "New SdkToolsPath value should be set" Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", t.SdkToolsPath, t.ToolPath); MockEngine e = new MockEngine(); t.BuildEngine = e; t.SdkToolsPath = goodParameterValue; Assert.Equal(goodParameterValue, t.SdkToolsPath); // "New SdkToolsPath value should be set" taskPassed = t.Execute(); Assert.False(taskPassed); // "Task should still fail -- there are other things wrong with it." // but that particular error shouldn't be there anymore. string sdkToolsPathMessage = t.Log.FormatResourceString("AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", t.SdkToolsPath, t.ToolPath); string messageWithNoCode; string sdkToolsPathCode = t.Log.ExtractMessageCode(sdkToolsPathMessage, out messageWithNoCode); e.AssertLogDoesntContain(sdkToolsPathCode); } /// /// Tests the ToolPath property: Should log an error if it's null or a bad path. /// [Fact] public void ToolPath() { var t = new ResolveComReference.AxImp(); t.ActiveXControlName = "FakeControl.ocx"; string badParameterValue = @"C:\Program Files\Microsoft Visual Studio 10.0\My Fake SDK Path"; string goodParameterValue = Path.GetTempPath(); bool taskPassed; Assert.Null(t.ToolPath); // "ToolPath should be null by default" Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", t.SdkToolsPath, t.ToolPath); t.ToolPath = badParameterValue; Assert.Equal(badParameterValue, t.ToolPath); // "New ToolPath value should be set" Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", t.SdkToolsPath, t.ToolPath); MockEngine e = new MockEngine(); t.BuildEngine = e; t.ToolPath = goodParameterValue; Assert.Equal(goodParameterValue, t.ToolPath); // "New ToolPath value should be set" taskPassed = t.Execute(); Assert.False(taskPassed); // "Task should still fail -- there are other things wrong with it." // but that particular error shouldn't be there anymore. string toolPathMessage = t.Log.FormatResourceString("AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", t.SdkToolsPath, t.ToolPath); string messageWithNoCode; string toolPathCode = t.Log.ExtractMessageCode(toolPathMessage, out messageWithNoCode); e.AssertLogDoesntContain(toolPathCode); } /// /// Tests that strong name sign-related parameters are validated properly, causing the task /// to fail if they are incorrectly set up. /// [Fact] public void TaskFailsWhenImproperlySigned() { if (!NativeMethodsShared.IsWindows) { return; // "Key container is not supported, except under Windows" } var t = new ResolveComReference.TlbImp(); t.TypeLibName = "Blah.tlb"; string tempKeyContainer = null; string tempKeyFile = null; try { tempKeyContainer = FileUtilities.GetTemporaryFile(); tempKeyFile = FileUtilities.GetTemporaryFile(); t.ToolPath = Path.GetTempPath(); // DelaySign is passed without a KeyFile or a KeyContainer t.DelaySign = true; Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.CannotSpecifyDelaySignWithoutEitherKeyFileOrKeyContainer"); // KeyContainer and KeyFile are both passed in t.KeyContainer = tempKeyContainer; t.KeyFile = tempKeyFile; Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.CannotSpecifyBothKeyFileAndKeyContainer"); // All the inputs are correct, but the KeyContainer passed in is bad t.DelaySign = false; t.KeyContainer = tempKeyContainer; t.KeyFile = null; Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.StrongNameUtils.NoKeyPairInContainer", t.KeyContainer); // All the inputs are correct, but the KeyFile passed in is bad t.KeyContainer = null; t.KeyFile = tempKeyFile; Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.StrongNameUtils.NoKeyPairInFile", t.KeyFile); } finally { if (tempKeyContainer != null) { File.Delete(tempKeyContainer); } if (tempKeyFile != null) { File.Delete(tempKeyContainer); } } } } sealed internal class Utilities { /// /// Given an instance of an AxImp task, executes that task (assuming all necessary parameters /// have been set ahead of time) and verifies that the execution log contains the error /// corresponding to the resource name passed in. /// /// The task to execute and check /// The name of the resource string to check the log for /// Arguments needed to format the resource string properly internal static void ExecuteTaskAndVerifyLogContainsErrorFromResource(AxTlbBaseTask t, string errorResource, params object[] args) { MockEngine e = new MockEngine(); t.BuildEngine = e; bool taskPassed = t.Execute(); Assert.False(taskPassed); // "Task should have failed" VerifyLogContainsErrorFromResource(e, t.Log, errorResource, args); } /// /// Given a log and a resource string, acquires the text of that resource string and /// compares it to the log. Asserts if the log does not contain the desired string. /// /// The MockEngine that contains the log we're checking /// The TaskLoggingHelper that we use to load the string resource /// The name of the resource string to check the log for /// Arguments needed to format the resource string properly internal static void VerifyLogContainsErrorFromResource(MockEngine e, TaskLoggingHelper log, string errorResource, params object[] args) { string errorMessage = log.FormatResourceString(errorResource, args); e.AssertLogContains(errorMessage); } /// /// Given an instance of an AxImp task, executes that task (assuming all necessary parameters /// have been set ahead of time) and verifies that the execution log does not contain the error /// corresponding to the resource name passed in. /// /// The task to execute and check /// The name of the resource string to check the log for /// Arguments needed to format the resource string properly internal static void ExecuteTaskAndVerifyLogDoesNotContainErrorFromResource(AxTlbBaseTask t, string errorResource, params object[] args) { MockEngine e = new MockEngine(); t.BuildEngine = e; bool taskPassed = t.Execute(); VerifyLogDoesNotContainErrorFromResource(e, t.Log, errorResource, args); } /// /// Given a log and a resource string, acquires the text of that resource string and /// compares it to the log. Assert fails if the log contains the desired string. /// /// The MockEngine that contains the log we're checking /// The TaskLoggingHelper that we use to load the string resource /// The name of the resource string to check the log for /// Arguments needed to format the resource string properly internal static void VerifyLogDoesNotContainErrorFromResource(MockEngine e, TaskLoggingHelper log, string errorResource, params object[] args) { string errorMessage = log.FormatResourceString(errorResource, args); e.AssertLogDoesntContain(errorMessage); } } }