1 // Copyright (c) Microsoft. All rights reserved. 2 // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 4 using System; 5 using System.IO; 6 using System.Reflection; 7 using Microsoft.Build.Framework; 8 using Microsoft.Build.Tasks; 9 using Microsoft.Build.Utilities; 10 using Microsoft.Runtime.Hosting; 11 using Microsoft.Build.Shared; 12 using Xunit; 13 14 namespace Microsoft.Build.UnitTests.AxTlbImp_Tests 15 { 16 sealed public class AxTlbBaseTask_Tests 17 { 18 /// <summary> 19 /// Tests the /delaysign switch 20 /// </summary> 21 [Fact] DelaySign()22 public void DelaySign() 23 { 24 AxTlbBaseTask t = new ResolveComReference.AxImp(); 25 26 Assert.False(t.DelaySign); // "DelaySign should be false by default" 27 CommandLine.ValidateNoParameterStartsWith( 28 t, 29 @"/delaysign", 30 false /* no response file */); 31 32 t.DelaySign = true; 33 Assert.True(t.DelaySign); // "DelaySign should be true" 34 CommandLine.ValidateHasParameter( 35 t, 36 @"/delaysign", 37 false /* no response file */); 38 } 39 40 /// <summary> 41 /// Tests the /keycontainer: switch 42 /// </summary> 43 [Fact] KeyContainer()44 public void KeyContainer() 45 { 46 if (!NativeMethodsShared.IsWindows) 47 { 48 return; // "Key container is not supported, except under Windows" 49 } 50 51 var t = new ResolveComReference.TlbImp(); 52 t.TypeLibName = "FakeTlb.tlb"; 53 string badParameterValue = "badKeyContainer"; 54 string goodParameterValue = "myKeyContainer"; 55 56 try 57 { 58 t.ToolPath = Path.GetTempPath(); 59 60 Assert.Null(t.KeyContainer); // "KeyContainer should be null by default"); 61 CommandLine.ValidateNoParameterStartsWith( 62 t, 63 @"/keycontainer:", 64 false /* no response file */); 65 66 t.KeyContainer = badParameterValue; 67 Assert.Equal(badParameterValue, t.KeyContainer); // "New KeyContainer value should be set" 68 CommandLine.ValidateHasParameter(t, @"/keycontainer:" + badParameterValue, false /* no response file */); 69 Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.StrongNameUtils.NoKeyPairInContainer", t.KeyContainer); 70 //ensure the key does not exist in the CSP 71 StrongNameHelpers.StrongNameKeyDelete(goodParameterValue); 72 73 IntPtr publicKeyBlob = IntPtr.Zero; 74 int publicKeyBlobSize = 0; 75 76 //add key to CSP 77 if (StrongNameHelpers.StrongNameKeyGen(goodParameterValue, 1 /* leave key registered */, out publicKeyBlob, out publicKeyBlobSize) && publicKeyBlob != IntPtr.Zero) 78 { 79 StrongNameHelpers.StrongNameFreeBuffer(publicKeyBlob); 80 81 t.KeyContainer = goodParameterValue; 82 Assert.Equal(goodParameterValue, t.KeyContainer); // "New KeyContainer value should be set" 83 CommandLine.ValidateHasParameter(t, @"/keycontainer:" + goodParameterValue, false /* no response file */); 84 Utilities.ExecuteTaskAndVerifyLogDoesNotContainErrorFromResource(t, "AxTlbBaseTask.StrongNameUtils.NoKeyPairInContainer", t.KeyContainer); 85 } 86 else 87 { 88 Assert.True(false, "Key container could not be created (perhaps you are not running as admin)."); 89 } 90 } 91 finally 92 { 93 //remove key from CSP 94 StrongNameHelpers.StrongNameKeyDelete(goodParameterValue); 95 96 // get rid of the generated temp file 97 if (goodParameterValue != null) 98 { 99 File.Delete(goodParameterValue); 100 } 101 } 102 } 103 104 /// <summary> 105 /// Tests the /keycontainer: switch with a space in the name 106 /// </summary> 107 [Fact] KeyContainerWithSpaces()108 public void KeyContainerWithSpaces() 109 { 110 AxTlbBaseTask t = new ResolveComReference.AxImp(); 111 string testParameterValue = @"my Key Container"; 112 113 Assert.Null(t.KeyContainer); // "KeyContainer should be null by default" 114 CommandLine.ValidateNoParameterStartsWith( 115 t, 116 @"/keycontainer:", 117 false /* no response file */); 118 119 t.KeyContainer = testParameterValue; 120 Assert.Equal(testParameterValue, t.KeyContainer); // "New KeyContainer value should be set" 121 CommandLine.ValidateHasParameter( 122 t, 123 @"/keycontainer:" + testParameterValue, 124 false /* no response file */); 125 } 126 127 /// <summary> 128 /// Tests the /keyfile: switch 129 /// </summary> 130 [Fact] KeyFile()131 public void KeyFile() 132 { 133 var t = new ResolveComReference.AxImp(); 134 t.ActiveXControlName = "FakeControl.ocx"; 135 string badParameterValue = "myKeyFile.key"; 136 string goodParameterValue = null; 137 138 try 139 { 140 goodParameterValue = FileUtilities.GetTemporaryFile(); 141 t.ToolPath = Path.GetTempPath(); 142 143 Assert.Null(t.KeyFile); // "KeyFile should be null by default" 144 CommandLine.ValidateNoParameterStartsWith( 145 t, 146 @"/keyfile:", 147 false /* no response file */); 148 149 t.KeyFile = badParameterValue; 150 Assert.Equal(badParameterValue, t.KeyFile); // "New KeyFile value should be set" 151 CommandLine.ValidateHasParameter( 152 t, 153 @"/keyfile:" + badParameterValue, 154 false /* no response file */); 155 Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.InvalidKeyFileSpecified", t.KeyFile); 156 157 t.KeyFile = goodParameterValue; 158 Assert.Equal(goodParameterValue, t.KeyFile); // "New KeyFile value should be set" 159 CommandLine.ValidateHasParameter( 160 t, 161 @"/keyfile:" + goodParameterValue, 162 false /* no response file */); 163 Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.StrongNameUtils.NoKeyPairInFile", t.KeyFile); 164 } 165 finally 166 { 167 if (goodParameterValue != null) 168 { 169 // get rid of the generated temp file 170 File.Delete(goodParameterValue); 171 } 172 } 173 } 174 175 /// <summary> 176 /// Tests the /keyfile: switch with a space in the filename 177 /// </summary> 178 [Fact] KeyFileWithSpaces()179 public void KeyFileWithSpaces() 180 { 181 AxTlbBaseTask t = new ResolveComReference.TlbImp(); 182 string testParameterValue = @"C:\Program Files\myKeyFile.key"; 183 184 Assert.Null(t.KeyFile); // "KeyFile should be null by default" 185 CommandLine.ValidateNoParameterStartsWith( 186 t, 187 @"/keyfile:", 188 false /* no response file */); 189 190 t.KeyFile = testParameterValue; 191 Assert.Equal(testParameterValue, t.KeyFile); // "New KeyFile value should be set" 192 CommandLine.ValidateHasParameter( 193 t, 194 @"/keyfile:" + testParameterValue, 195 false /* no response file */); 196 } 197 198 /// <summary> 199 /// Tests the SdkToolsPath property: Should log an error if it's null or a bad path. 200 /// </summary> 201 [Fact] SdkToolsPath()202 public void SdkToolsPath() 203 { 204 var t = new ResolveComReference.TlbImp(); 205 t.TypeLibName = "FakeLibrary.tlb"; 206 string badParameterValue = @"C:\Program Files\Microsoft Visual Studio 10.0\My Fake SDK Path"; 207 string goodParameterValue = Path.GetTempPath(); 208 bool taskPassed; 209 210 Assert.Null(t.SdkToolsPath); // "SdkToolsPath should be null by default" 211 Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", t.SdkToolsPath, t.ToolPath); 212 213 t.SdkToolsPath = badParameterValue; 214 Assert.Equal(badParameterValue, t.SdkToolsPath); // "New SdkToolsPath value should be set" 215 Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", t.SdkToolsPath, t.ToolPath); 216 217 MockEngine e = new MockEngine(); 218 t.BuildEngine = e; 219 t.SdkToolsPath = goodParameterValue; 220 221 Assert.Equal(goodParameterValue, t.SdkToolsPath); // "New SdkToolsPath value should be set" 222 taskPassed = t.Execute(); 223 Assert.False(taskPassed); // "Task should still fail -- there are other things wrong with it." 224 225 // but that particular error shouldn't be there anymore. 226 string sdkToolsPathMessage = t.Log.FormatResourceString("AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", t.SdkToolsPath, t.ToolPath); 227 string messageWithNoCode; 228 string sdkToolsPathCode = t.Log.ExtractMessageCode(sdkToolsPathMessage, out messageWithNoCode); 229 e.AssertLogDoesntContain(sdkToolsPathCode); 230 } 231 232 /// <summary> 233 /// Tests the ToolPath property: Should log an error if it's null or a bad path. 234 /// </summary> 235 [Fact] ToolPath()236 public void ToolPath() 237 { 238 var t = new ResolveComReference.AxImp(); 239 t.ActiveXControlName = "FakeControl.ocx"; 240 string badParameterValue = @"C:\Program Files\Microsoft Visual Studio 10.0\My Fake SDK Path"; 241 string goodParameterValue = Path.GetTempPath(); 242 bool taskPassed; 243 244 Assert.Null(t.ToolPath); // "ToolPath should be null by default" 245 Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", t.SdkToolsPath, t.ToolPath); 246 247 t.ToolPath = badParameterValue; 248 Assert.Equal(badParameterValue, t.ToolPath); // "New ToolPath value should be set" 249 Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", t.SdkToolsPath, t.ToolPath); 250 251 MockEngine e = new MockEngine(); 252 t.BuildEngine = e; 253 t.ToolPath = goodParameterValue; 254 255 Assert.Equal(goodParameterValue, t.ToolPath); // "New ToolPath value should be set" 256 taskPassed = t.Execute(); 257 Assert.False(taskPassed); // "Task should still fail -- there are other things wrong with it." 258 259 // but that particular error shouldn't be there anymore. 260 string toolPathMessage = t.Log.FormatResourceString("AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", t.SdkToolsPath, t.ToolPath); 261 string messageWithNoCode; 262 string toolPathCode = t.Log.ExtractMessageCode(toolPathMessage, out messageWithNoCode); 263 e.AssertLogDoesntContain(toolPathCode); 264 } 265 266 /// <summary> 267 /// Tests that strong name sign-related parameters are validated properly, causing the task 268 /// to fail if they are incorrectly set up. 269 /// </summary> 270 [Fact] TaskFailsWhenImproperlySigned()271 public void TaskFailsWhenImproperlySigned() 272 { 273 if (!NativeMethodsShared.IsWindows) 274 { 275 return; // "Key container is not supported, except under Windows" 276 } 277 278 var t = new ResolveComReference.TlbImp(); 279 t.TypeLibName = "Blah.tlb"; 280 string tempKeyContainer = null; 281 string tempKeyFile = null; 282 283 try 284 { 285 tempKeyContainer = FileUtilities.GetTemporaryFile(); 286 tempKeyFile = FileUtilities.GetTemporaryFile(); 287 t.ToolPath = Path.GetTempPath(); 288 289 // DelaySign is passed without a KeyFile or a KeyContainer 290 t.DelaySign = true; 291 Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.CannotSpecifyDelaySignWithoutEitherKeyFileOrKeyContainer"); 292 293 // KeyContainer and KeyFile are both passed in 294 t.KeyContainer = tempKeyContainer; 295 t.KeyFile = tempKeyFile; 296 Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.CannotSpecifyBothKeyFileAndKeyContainer"); 297 298 // All the inputs are correct, but the KeyContainer passed in is bad 299 t.DelaySign = false; 300 t.KeyContainer = tempKeyContainer; 301 t.KeyFile = null; 302 Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.StrongNameUtils.NoKeyPairInContainer", t.KeyContainer); 303 304 // All the inputs are correct, but the KeyFile passed in is bad 305 t.KeyContainer = null; 306 t.KeyFile = tempKeyFile; 307 Utilities.ExecuteTaskAndVerifyLogContainsErrorFromResource(t, "AxTlbBaseTask.StrongNameUtils.NoKeyPairInFile", t.KeyFile); 308 } 309 finally 310 { 311 if (tempKeyContainer != null) 312 { 313 File.Delete(tempKeyContainer); 314 } 315 if (tempKeyFile != null) 316 { 317 File.Delete(tempKeyContainer); 318 } 319 } 320 } 321 } 322 323 sealed internal class Utilities 324 { 325 /// <summary> 326 /// Given an instance of an AxImp task, executes that task (assuming all necessary parameters 327 /// have been set ahead of time) and verifies that the execution log contains the error 328 /// corresponding to the resource name passed in. 329 /// </summary> 330 /// <param name="t">The task to execute and check</param> 331 /// <param name="errorResource">The name of the resource string to check the log for</param> 332 /// <param name="args">Arguments needed to format the resource string properly</param> ExecuteTaskAndVerifyLogContainsErrorFromResource(AxTlbBaseTask t, string errorResource, params object[] args)333 internal static void ExecuteTaskAndVerifyLogContainsErrorFromResource(AxTlbBaseTask t, string errorResource, params object[] args) 334 { 335 MockEngine e = new MockEngine(); 336 t.BuildEngine = e; 337 338 bool taskPassed = t.Execute(); 339 Assert.False(taskPassed); // "Task should have failed" 340 341 VerifyLogContainsErrorFromResource(e, t.Log, errorResource, args); 342 } 343 344 /// <summary> 345 /// Given a log and a resource string, acquires the text of that resource string and 346 /// compares it to the log. Asserts if the log does not contain the desired string. 347 /// </summary> 348 /// <param name="e">The MockEngine that contains the log we're checking</param> 349 /// <param name="log">The TaskLoggingHelper that we use to load the string resource</param> 350 /// <param name="errorResource">The name of the resource string to check the log for</param> 351 /// <param name="args">Arguments needed to format the resource string properly</param> VerifyLogContainsErrorFromResource(MockEngine e, TaskLoggingHelper log, string errorResource, params object[] args)352 internal static void VerifyLogContainsErrorFromResource(MockEngine e, TaskLoggingHelper log, string errorResource, params object[] args) 353 { 354 string errorMessage = log.FormatResourceString(errorResource, args); 355 e.AssertLogContains(errorMessage); 356 } 357 358 /// <summary> 359 /// Given an instance of an AxImp task, executes that task (assuming all necessary parameters 360 /// have been set ahead of time) and verifies that the execution log does not contain the error 361 /// corresponding to the resource name passed in. 362 /// </summary> 363 /// <param name="t">The task to execute and check</param> 364 /// <param name="errorResource">The name of the resource string to check the log for</param> 365 /// <param name="args">Arguments needed to format the resource string properly</param> ExecuteTaskAndVerifyLogDoesNotContainErrorFromResource(AxTlbBaseTask t, string errorResource, params object[] args)366 internal static void ExecuteTaskAndVerifyLogDoesNotContainErrorFromResource(AxTlbBaseTask t, string errorResource, params object[] args) 367 { 368 MockEngine e = new MockEngine(); 369 t.BuildEngine = e; 370 371 bool taskPassed = t.Execute(); 372 373 VerifyLogDoesNotContainErrorFromResource(e, t.Log, errorResource, args); 374 } 375 376 /// <summary> 377 /// Given a log and a resource string, acquires the text of that resource string and 378 /// compares it to the log. Assert fails if the log contains the desired string. 379 /// </summary> 380 /// <param name="e">The MockEngine that contains the log we're checking</param> 381 /// <param name="log">The TaskLoggingHelper that we use to load the string resource</param> 382 /// <param name="errorResource">The name of the resource string to check the log for</param> 383 /// <param name="args">Arguments needed to format the resource string properly</param> VerifyLogDoesNotContainErrorFromResource(MockEngine e, TaskLoggingHelper log, string errorResource, params object[] args)384 internal static void VerifyLogDoesNotContainErrorFromResource(MockEngine e, TaskLoggingHelper log, string errorResource, params object[] args) 385 { 386 string errorMessage = log.FormatResourceString(errorResource, args); 387 e.AssertLogDoesntContain(errorMessage); 388 } 389 } 390 } 391