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