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.Collections.Generic;
6 using System.IO;
7 using System.Reflection;
8 using System.Text;
9 using System.Xml;
10 
11 using NUnit.Framework;
12 
13 using Microsoft.Build.Framework;
14 using Microsoft.Build.BuildEngine;
15 using Microsoft.Build.BuildEngine.Shared;
16 using System.Collections;
17 
18 namespace Microsoft.Build.UnitTests
19 {
20     [TestFixture]
21     public class EngineProxy_Tests
22     {
23         EngineProxy engineProxy;
24         EngineProxy engineProxy2;
25         MockTaskExecutionModule taskExecutionModule;
26         MockTaskExecutionModule taskExecutionModule2;
27         Engine engine;
28         string project1 = "Project1";
29         string project2 = "Project2";
30 
31         [SetUp]
SetUp()32         public void SetUp()
33         {
34             // Whole bunch of setup code.
35             XmlElement taskNode = new XmlDocument().CreateElement("MockTask");
36             LoadedType taskClass = new LoadedType(typeof(MockTask), new AssemblyLoadInfo(typeof(MockTask).Assembly.FullName, null));
37             engine = new Engine(@"c:\");
38             Project project = new Project(engine);
39             EngineCallback engineCallback = new EngineCallback(engine);
40             taskExecutionModule = new MockTaskExecutionModule(engineCallback);
41             int handleId = engineCallback.CreateTaskContext(project, null, null, taskNode, EngineCallback.inProcNode, new BuildEventContext(BuildEventContext.InvalidNodeId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTaskId));
42             TaskEngine taskEngine = new TaskEngine
43                                 (
44                                     taskNode,
45                                     null, /* host object */
46                                     "In Memory",
47                                     project.FullFileName,
48                                     engine.LoggingServices,
49                                     handleId,
50                                     taskExecutionModule,
51                                     null
52                                 );
53             taskEngine.TaskClass = taskClass;
54 
55             engineProxy = new EngineProxy(taskExecutionModule, handleId, project.FullFileName, project.FullFileName, engine.LoggingServices, null);
56             taskExecutionModule2 = new MockTaskExecutionModule(engineCallback, TaskExecutionModule.TaskExecutionModuleMode.MultiProcFullNodeMode);
57             engineProxy2 = new EngineProxy(taskExecutionModule2, handleId, project.FullFileName, project.FullFileName, engine.LoggingServices, null);
58 
59         }
60 
61         [TearDown]
TearDownAttribute()62         public void TearDownAttribute()
63         {
64             engine.Shutdown();
65             engineProxy = null;
66             engineProxy2 = null;
67             engine = null;
68         }
69 
70 
71         /// <summary>
72         /// Class which implements a simple custom build error
73         /// </summary>
74         [Serializable]
75         internal class MyCustomBuildErrorEventArgs : BuildErrorEventArgs
76         {
77 
MyCustomBuildErrorEventArgs( string message )78             internal MyCustomBuildErrorEventArgs
79                 (
80                 string message
81                 )
82                 : base(null, null, null, 0, 0, 0, 0, message, null, null)
83             {
84             }
85 
86             internal string FXCopRule
87             {
88                 get
89                 {
90                     return fxcopRule;
91                 }
92                 set
93                 {
94                     fxcopRule = value;
95                 }
96             }
97 
98             private string fxcopRule;
99         }
100 
101         /// <summary>
102         /// Custom logger which will be used for testing
103         /// </summary>
104         internal class MyCustomLogger : ILogger
105         {
106             public LoggerVerbosity Verbosity
107             {
108                 get
109                 {
110                     return LoggerVerbosity.Normal;
111                 }
112                 set
113                 {
114                 }
115             }
116 
117             public string Parameters
118             {
119                 get
120                 {
121                     return String.Empty;
122                 }
123                 set
124                 {
125                 }
126             }
127 
Initialize(IEventSource eventSource)128             public void Initialize(IEventSource eventSource)
129             {
130                 eventSource.ErrorRaised += new BuildErrorEventHandler(MyCustomErrorHandler);
131                 eventSource.WarningRaised += new BuildWarningEventHandler(MyCustomWarningHandler);
132                 eventSource.MessageRaised += new BuildMessageEventHandler(MyCustomMessageHandler);
133                 eventSource.CustomEventRaised += new CustomBuildEventHandler(MyCustomBuildHandler);
134                 eventSource.AnyEventRaised += new AnyEventHandler(eventSource_AnyEventRaised);
135             }
136 
eventSource_AnyEventRaised(object sender, BuildEventArgs e)137             void eventSource_AnyEventRaised(object sender, BuildEventArgs e)
138             {
139                 if (e.Message != null)
140                 {
141                     Console.Out.WriteLine("AnyEvent:"+e.Message.ToString());
142                 }
143             }
144 
Shutdown()145             public void Shutdown()
146             {
147             }
148 
MyCustomErrorHandler(object s, BuildErrorEventArgs e)149             internal void MyCustomErrorHandler(object s, BuildErrorEventArgs e)
150             {
151                 numberOfError++;
152                 this.lastError = e;
153                 if (e.Message != null)
154                 {
155                     Console.Out.WriteLine("CustomError:"+e.Message.ToString());
156                 }
157             }
158 
MyCustomWarningHandler(object s, BuildWarningEventArgs e)159             internal void MyCustomWarningHandler(object s, BuildWarningEventArgs e)
160             {
161                 numberOfWarning++;
162                 this.lastWarning = e;
163                 if (e.Message != null)
164                 {
165                     Console.Out.WriteLine("CustomWarning:" + e.Message.ToString());
166                 }
167             }
168 
MyCustomMessageHandler(object s, BuildMessageEventArgs e)169             internal void MyCustomMessageHandler(object s, BuildMessageEventArgs e)
170             {
171                 numberOfMessage++;
172                 this.lastMessage = e;
173                 if (e.Message != null)
174                 {
175                     Console.Out.WriteLine("CustomMessage:" + e.Message.ToString());
176                 }
177             }
178 
MyCustomBuildHandler(object s, CustomBuildEventArgs e)179             internal void MyCustomBuildHandler(object s, CustomBuildEventArgs e)
180             {
181                 numberOfCustom++;
182                 this.lastCustom = e;
183                 if (e.Message != null)
184                 {
185                     Console.Out.WriteLine("CustomEvent:" + e.Message.ToString());
186                 }
187             }
188             internal BuildErrorEventArgs lastError = null;
189             internal BuildWarningEventArgs lastWarning = null;
190             internal BuildMessageEventArgs lastMessage = null;
191             internal CustomBuildEventArgs lastCustom = null;
192             internal int numberOfError = 0;
193             internal int numberOfWarning =0;
194             internal int numberOfMessage = 0;
195             internal int numberOfCustom = 0;
196         }
197 
198         /// <summary>
199         /// Makes sure that if a task tries to log a custom error event that subclasses our own
200         /// BuildErrorEventArgs, that the subclass makes it all the way to the logger.  In other
201         /// words, the engine should not try to read data out of the event args and construct
202         /// its own.  Bug VSWhidbey 440801.
203         /// </summary>
204         [Test]
CustomBuildErrorEventIsPreserved()205         public void CustomBuildErrorEventIsPreserved()
206         {
207 
208             MyCustomLogger myLogger = new MyCustomLogger();
209             engine.RegisterLogger(myLogger);
210             // Create a custom build event args that derives from MSBuild's BuildErrorEventArgs.
211             // Set a custom field on this event (FXCopRule).
212             MyCustomBuildErrorEventArgs fxcopError = new MyCustomBuildErrorEventArgs("Your code is lame.");
213             fxcopError.FXCopRule = "CodeLamenessViolation";
214 
215             // Log the custom event args.  (Pretend that the task actually did this.)
216             engineProxy.LogErrorEvent(fxcopError);
217             engine.LoggingServices.ProcessPostedLoggingEvents();
218 
219             // Make sure our custom logger received the actual custom event and not some fake.
220             Assertion.Assert("Expected Custom Error Event", myLogger.lastError is MyCustomBuildErrorEventArgs);
221 
222             // Make sure the special fields in the custom event match what we originally logged.
223             fxcopError = myLogger.lastError as MyCustomBuildErrorEventArgs;
224             Assertion.AssertEquals("Your code is lame.", fxcopError.Message);
225             Assertion.AssertEquals("CodeLamenessViolation", fxcopError.FXCopRule);
226         }
227 
228         /// <summary>
229         /// Test that error events are correctly logged
230         /// </summary>
231         [Test]
TestLogErrorEvent()232         public void TestLogErrorEvent()
233         {
234 
235             MyCustomLogger myLogger = new MyCustomLogger();
236             engine.RegisterLogger(myLogger);
237 
238             engineProxy.UpdateContinueOnError(false);
239             // Log the custom event args.  (Pretend that the task actually did this.)
240             engineProxy.LogErrorEvent(new BuildErrorEventArgs("SubCategory", "code", null, 0, 1, 2, 3, "message", "Help", "Sender"));
241             engine.LoggingServices.ProcessPostedLoggingEvents();
242 
243             // Make sure our custom logger received the actual custom event and not some fake.
244             Assertion.Assert("Expected Error Event", myLogger.lastError is BuildErrorEventArgs);
245             Assertion.Assert("Expected line number to be 0", myLogger.lastError.LineNumber == 0);
246 
247 
248             engineProxy.UpdateContinueOnError(true);
249             // Log the custom event args.  (Pretend that the task actually did this.)
250             engineProxy.LogErrorEvent(new BuildErrorEventArgs("SubCategory", "code", null, 0, 1, 2, 3, "message", "Help", "Sender"));
251             engine.LoggingServices.ProcessPostedLoggingEvents();
252 
253             // Make sure our custom logger received the actual custom event and not some fake.
254             Assertion.Assert("Expected Warning Event", myLogger.lastWarning is BuildWarningEventArgs);
255             Assertion.Assert("Expected line number to be 0", myLogger.lastWarning.LineNumber == 0);
256         }
257 
258         /// <summary>
259         /// Test that a null error event will cause an exception
260         /// </summary>
261         [Test]
262         [ExpectedException(typeof(ArgumentNullException))]
TestLogErrorEventNull()263         public void TestLogErrorEventNull()
264         {
265             MyCustomLogger myLogger = new MyCustomLogger();
266             engine.RegisterLogger(myLogger);
267             engineProxy.UpdateContinueOnError(true);
268             engineProxy.LogErrorEvent(null);
269             engine.LoggingServices.ProcessPostedLoggingEvents();
270         }
271 
272         /// <summary>
273         /// Test that a null error event will cause an exception
274         /// </summary>
275         [Test]
276         [ExpectedException(typeof(ArgumentNullException))]
TestLogErrorEventNull2()277         public void TestLogErrorEventNull2()
278         {
279             MyCustomLogger myLogger = new MyCustomLogger();
280             engine.RegisterLogger(myLogger);
281             engineProxy.UpdateContinueOnError(false);
282             engineProxy.LogErrorEvent(null);
283             engine.LoggingServices.ProcessPostedLoggingEvents();
284         }
285         /// <summary>
286         /// Test that warnings are logged properly
287         /// </summary>
288         [Test]
TestLogWarningEvent()289         public void TestLogWarningEvent()
290         {
291 
292             MyCustomLogger myLogger = new MyCustomLogger();
293             engine.RegisterLogger(myLogger);
294 
295             // Log the custom event args.  (Pretend that the task actually did this.)
296             engineProxy.LogWarningEvent(new BuildWarningEventArgs("SubCategory", "code", null, 0, 1, 2, 3, "message", "Help", "Sender"));
297             engine.LoggingServices.ProcessPostedLoggingEvents();
298 
299             // Make sure our custom logger received the actual custom event and not some fake.
300             Assertion.Assert("Expected Warning Event", myLogger.lastWarning is BuildWarningEventArgs);
301             Assertion.Assert("Expected line number to be 0", myLogger.lastWarning.LineNumber == 0);
302         }
303 
304         /// <summary>
305         /// Test that messages are logged properly
306         /// </summary>
307         [Test]
TestLogMessageEvent()308         public void TestLogMessageEvent()
309         {
310             MyCustomLogger myLogger = new MyCustomLogger();
311             engine.RegisterLogger(myLogger);
312 
313             // Log the custom event args.  (Pretend that the task actually did this.)
314             engineProxy.LogMessageEvent(new BuildMessageEventArgs("message", "HelpKeyword", "senderName", MessageImportance.High));
315             engine.LoggingServices.ProcessPostedLoggingEvents();
316 
317             // Make sure our custom logger received the actual custom event and not some fake.
318             Assertion.Assert("Expected Message Event", myLogger.lastMessage is BuildMessageEventArgs);
319             Assertion.Assert("Expected Message importance to be high", myLogger.lastMessage.Importance == MessageImportance.High);
320         }
321         [Serializable]
322         class MyCustomBuildEventArgs : CustomBuildEventArgs
323         {
MyCustomBuildEventArgs()324             public MyCustomBuildEventArgs() : base() { }
MyCustomBuildEventArgs(string message)325             public MyCustomBuildEventArgs(string message) : base(message, "HelpKeyword", "SenderName") { }
326         }
327         /// <summary>
328         /// Test that custom events are logged properly
329         /// </summary>
330         [Test]
TestLogCustomEvent()331         public void TestLogCustomEvent()
332         {
333             MyCustomLogger myLogger = new MyCustomLogger();
334             engine.RegisterLogger(myLogger);
335 
336             // Log the custom event args.  (Pretend that the task actually did this.)
337             engineProxy.LogCustomEvent(new MyCustomBuildEventArgs("testCustomBuildEvent"));
338             engine.LoggingServices.ProcessPostedLoggingEvents();
339 
340             // Make sure our custom logger received the actual custom event and not some fake.
341             Assertion.Assert("Expected custom build Event", myLogger.lastCustom is CustomBuildEventArgs);
342             Assertion.AssertEquals("testCustomBuildEvent", myLogger.lastCustom.Message);
343         }
344         /// <summary>
345         /// Test the building of a single project file
346         /// </summary>
347         [Test]
BuildProjectFile()348         public void BuildProjectFile()
349         {
350             string[] targets;
351             MyCustomLogger myLogger = new MyCustomLogger();
352             engine.RegisterLogger(myLogger);
353             Dictionary<string, string> globalProperties = new Dictionary<string, string>();
354             targets = new string[1];
355             targets[0] = "Build";
356             Assert.IsTrue(engineProxy.BuildProjectFile(project1, targets, null, new Dictionary<object, object>()), "Expected Build 2 to work");
357         }
358         /// <summary>
359         /// Test the building of multiple files in parallel
360         /// </summary>
361         [Test]
BuildProjectFilesInParallel()362         public void BuildProjectFilesInParallel()
363         {
364             string[] targets;
365             string[] projects;
366             Dictionary<string, string> globalProperties = new Dictionary<string, string>();
367             targets = new string[1];
368             targets[0] = "Build";
369             projects = new string[2];
370             projects[0] = project2;
371             projects[1] = project1;
372             Dictionary<object, object>[] dictionaryList = new Dictionary<object, object>[2];
373             dictionaryList[0] = new Dictionary<object, object>();
374             dictionaryList[1] = new Dictionary<object, object>();
375             Dictionary<string, string>[] globalPropertiesArray = new Dictionary<string, string>[2];
376             globalProperties.Add("MyGlobalProp", "SomePropertyText");
377             globalPropertiesArray[0] = globalProperties;
378             globalPropertiesArray[1] = globalProperties;
379             string[] toolVersions = new string[] { null, null };
380             Assert.IsTrue(engineProxy.BuildProjectFilesInParallel(projects, targets, globalPropertiesArray, dictionaryList, toolVersions, false, false));
381         }
382 
383         [Test]
ContinueOnErrorShouldConvertErrorsToWarnings()384         public void ContinueOnErrorShouldConvertErrorsToWarnings()
385         {
386             MockLogger logger = ObjectModelHelpers.BuildProjectExpectSuccess(@"
387 
388                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
389 
390                     <Target Name=`Build`>
391 
392                         <Copy SourceFiles=`RandomNonExistentSourceFile.123456789`
393                               DestinationFiles=`foo`
394                               ContinueOnError=`true` />
395 
396                     </Target>
397 
398                 </Project>
399 
400                 ");
401 
402             Assertion.AssertEquals("Expected zero errors", 0, logger.ErrorCount);
403             Assertion.AssertEquals("Expected one warning", 1, logger.WarningCount);
404         }
405         /// <summary>
406         /// Check that the properties are correctly set and retreived
407         /// </summary>
408         [Test]
Properties()409         public void Properties()
410         {
411             Assert.IsTrue(engineProxy.LineNumberOfTaskNode == 0, "Expected LineNumberOfTaskNode to be 0");
412             Assert.IsTrue(engineProxy.ColumnNumberOfTaskNode == 0, "Expected ColumnNumberOfTaskNode to be 0");
413             Assert.IsTrue(string.Compare(engineProxy.ProjectFileOfTaskNode, string.Empty, StringComparison.OrdinalIgnoreCase) == 0, "Expected ProjectFileOfTaskNode to be empty");
414         }
415 
416         /// <summary>
417         /// Verify IsRunningMultipleNodes
418         /// </summary>
419         [Test]
IsRunningMultipleNodes()420         public void IsRunningMultipleNodes()
421         {
422             // Verify TEM is running singleProc mode before we can test to make sure EngineProxy is correctly using the value
423             Assertion.Assert("Expected TEM to be running singleProcMode", taskExecutionModule.GetExecutionModuleMode() == TaskExecutionModule.TaskExecutionModuleMode.SingleProcMode);
424             Assertion.Assert("Expected EngineProxy for TEM running in singleProc mode to return false for IsRunningMultipleNodes", engineProxy.IsRunningMultipleNodes == false);
425 
426             // Verify TEM is running MultiProc mode before we can test to make sure EngineProxy is correctly using the value
427             TaskExecutionModule.TaskExecutionModuleMode moduleMode = taskExecutionModule2.GetExecutionModuleMode();
428             Assertion.Assert("Expected TEM to be not be running SingleProcMode",moduleMode != TaskExecutionModule.TaskExecutionModuleMode.SingleProcMode);
429             Assertion.Assert("Expected EngineProxy for TEM running in MultiProc mode to return true for IsRunningMultipleNodes", engineProxy2.IsRunningMultipleNodes);
430         }
431 
432         #region ToolsVersion tests
433 
ToolsVersionTestHelper(string parentProjectToolsVersionInProject, string parentProjectToolsVersionOverride, string toolsVersionPassedToEngineProxy)434         private ITaskItem ToolsVersionTestHelper(string parentProjectToolsVersionInProject,
435                                                  string parentProjectToolsVersionOverride,
436                                                  string toolsVersionPassedToEngineProxy)
437         {
438             Engine engine = new Engine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath));
439             engine.AddToolset(new Toolset("44.0", "someToolsPath"));
440             engine.AddToolset(new Toolset("55.0", "anotherToolsPath"));
441             engine.AddToolset(new Toolset("66.0", "yetanotherToolsPath"));
442 
443             // The child project declares its ToolsVersion
444             string childProjectFullPath = ObjectModelHelpers.CreateFileInTempProjectDirectory("child.proj", @"
445                       <Project ToolsVersion='44.0' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
446                           <ItemGroup>
447                               <ToolsVersionItem Include='$(MSBuildToolsVersion)'>
448                                   <ToolsPath>$(MSBuildToolsPath)</ToolsPath>
449                                   <BinPath>$(MSBuildBinPath)</BinPath>
450                               </ToolsVersionItem>
451                           </ItemGroup>
452                           <Target Name='Build' Outputs='@(ToolsVersionItem)' />
453                       </Project>
454                       ");
455 
456             // The parent project doesn't declare its ToolsVersion, and its ToolsVersion is not overridden
457             string parentProjectContent = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'";
458             if (parentProjectToolsVersionInProject != null)
459             {
460                 parentProjectContent += String.Format(" ToolsVersion=\"{0}\"", parentProjectToolsVersionInProject);
461             }
462             parentProjectContent += "/>";
463 
464             Project parentProject =
465                 ObjectModelHelpers.CreateInMemoryProject(engine, parentProjectContent, null);
466 
467             if (parentProjectToolsVersionOverride != null)
468             {
469                 parentProject.ToolsVersion = parentProjectToolsVersionOverride;
470             }
471 
472             Dictionary<string, ITaskItem[]> targetOutputs =
473                 new Dictionary<string, ITaskItem[]>(StringComparer.OrdinalIgnoreCase);
474 
475             EngineProxy engineProxy = CreateEngineProxyWithDummyTaskEngine(engine, parentProject);
476             bool success = engineProxy.BuildProjectFile
477                 (childProjectFullPath, null, null, targetOutputs, toolsVersionPassedToEngineProxy);
478 
479             ITaskItem toolsVersionItem = targetOutputs["Build"][0];
480 
481             Assertion.Assert("Expected a successful build!", success);
482 
483             return toolsVersionItem;
484         }
485 
486         /// <summary>
487         /// Basic test using the toolsVersion to override the child's toolsVersion.
488         /// </summary>
489         [Test]
ToolsVersionCanBeOverriddenUsingBuildProjectFile()490         public void ToolsVersionCanBeOverriddenUsingBuildProjectFile()
491         {
492             ITaskItem toolsVersionItem = ToolsVersionTestHelper(null, null, "55.0");
493 
494             Assertion.AssertEquals("55.0", toolsVersionItem.ItemSpec);
495             Assertion.Assert("ToolsPath should've been 'anotherToolsPath'.",
496                              0 == String.Compare(toolsVersionItem.GetMetadata("ToolsPath"), "anotherToolsPath", StringComparison.OrdinalIgnoreCase));
497             Assertion.Assert("BinPath should've been 'anotherToolsPath'.",
498                              0 == String.Compare(toolsVersionItem.GetMetadata("BinPath"), "anotherToolsPath", StringComparison.OrdinalIgnoreCase));
499         }
500 
501         /// <summary>
502         /// If toolsVersion is not passed to BuildProjectFile, and the parent project
503         /// is building with a toolsVersion override, then the child project should
504         /// inherit that toolsVersion override.
505         /// </summary>
506         [Test]
UseParentProjectToolsVersionOverrideIfNotOverriddenByTask()507         public void UseParentProjectToolsVersionOverrideIfNotOverriddenByTask()
508         {
509             ITaskItem toolsVersionItem = ToolsVersionTestHelper(null, "55.0", null);
510 
511             Assertion.AssertEquals("55.0", toolsVersionItem.ItemSpec);
512             Assertion.Assert("ToolsPath should've been 'anotherToolsPath'.",
513                              0 == String.Compare(toolsVersionItem.GetMetadata("ToolsPath"), "anotherToolsPath", StringComparison.OrdinalIgnoreCase));
514             Assertion.Assert("BinPath should've been 'anotherToolsPath'.",
515                              0 == String.Compare(toolsVersionItem.GetMetadata("BinPath"), "anotherToolsPath", StringComparison.OrdinalIgnoreCase));
516 
517         }
518 
519         /// <summary>
520         /// If a toolsVersion override was previously specified on the command line or
521         /// a prior call to the MSBuild task, the toolsVersion specified on this
522         /// task call should still take highest precedence.
523         /// </summary>
524         [Test]
ToolsVersionFromTaskWinsEvenIfParentProjectUsingOverride()525         public void ToolsVersionFromTaskWinsEvenIfParentProjectUsingOverride()
526         {
527             ITaskItem toolsVersionItem = ToolsVersionTestHelper(null, "66.0", "55.0");
528 
529             Assertion.AssertEquals("55.0", toolsVersionItem.ItemSpec);
530             Assertion.Assert("ToolsPath should've been 'anotherToolsPath'.",
531                              0 == String.Compare(toolsVersionItem.GetMetadata("ToolsPath"), "anotherToolsPath", StringComparison.OrdinalIgnoreCase));
532             Assertion.Assert("BinPath should've been 'anotherToolsPath'.",
533                              0 == String.Compare(toolsVersionItem.GetMetadata("BinPath"), "anotherToolsPath", StringComparison.OrdinalIgnoreCase));
534 
535         }
536 
537         /// <summary>
538         /// If no override was specified previously or here on BuildProjectFile, should use the
539         /// value from the &lt;Project /&gt; element of the child.
540         /// This is probably adequately tested elsewhere, but it doesn't hurt to cover here as well.
541         /// </summary>
542         [Test]
ToolsVersionFromProjectElementUsedIfNoOverride()543         public void ToolsVersionFromProjectElementUsedIfNoOverride()
544         {
545             ITaskItem toolsVersionItem = ToolsVersionTestHelper("66.0", null, null);
546 
547             Assertion.AssertEquals("44.0", toolsVersionItem.ItemSpec);
548             Assertion.Assert("ToolsPath should've been 'someToolsPath'.",
549                              0 == String.Compare(toolsVersionItem.GetMetadata("ToolsPath"), "someToolsPath", StringComparison.OrdinalIgnoreCase));
550             Assertion.Assert("BinPath should've been 'someToolsPath'.",
551                              0 == String.Compare(toolsVersionItem.GetMetadata("BinPath"), "someToolsPath", StringComparison.OrdinalIgnoreCase));
552 
553         }
554 
555         /// <summary>
556         /// The toolsVersion override should be honored even when building a solution file.
557         /// </summary>
558         [Test]
ToolsVersionRespectedWhenBuildingASolution()559         public void ToolsVersionRespectedWhenBuildingASolution()
560         {
561             ObjectModelHelpers.DeleteTempProjectDirectory();
562 
563             Engine engine = new Engine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath));
564 
565             // We've intentionally assigned the correct BinPath value to the tools version '3.5' since later
566             // we will request the engine build the solution with that tools version, and the engine will
567             // need to locate the default tasks. If the override we're going to specify isn't in fact applied,
568             // the engine will try to load the tasks from the bogus toolpath, and that will fail the solution build,
569             // so this test will fail.
570             engine.AddToolset(new Toolset("3.5", engine.BinPath));
571             engine.AddToolset(new Toolset("2.0", "anotherToolsPath"));
572 
573             string solutionFileContents =
574                 @"
575                 Microsoft Visual Studio Solution File, Format Version 9.00
576                 # Visual Studio 2005
577                 Global
578                     GlobalSection(SolutionConfigurationPlatforms) = preSolution
579                         Debug|AnyCPU = Debug|AnyCPU
580                         Release|AnyCPU = Release|AnyCPU
581                     EndGlobalSection
582                     GlobalSection(SolutionProperties) = preSolution
583                         HideSolutionNode = FALSE
584                     EndGlobalSection
585                 EndGlobal
586                 ";
587 
588             string solutionFullPath = ObjectModelHelpers.CreateFileInTempProjectDirectory("ConsoleApplication1.sln", solutionFileContents);
589 
590             // The parent project doesn't declare its ToolsVersion, and its ToolsVersion is not overridden
591             string parentProjectContent = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
592             Project parentProject =
593                 ObjectModelHelpers.CreateInMemoryProject(engine, parentProjectContent, null);
594 
595             EngineProxy engineProxy = CreateEngineProxyWithDummyTaskEngine(engine, parentProject);
596             bool success = engineProxy.BuildProjectFile
597                 (solutionFullPath, null, null, null, "3.5");
598 
599             Assertion.Assert("Expected a successful build!", success);
600         }
601 
602         /// <summary>
603         /// If the project we've been asked to build has the same full path, global properties,
604         /// and toolsVersion as those of another loaded Project, we should simply pass this
605         /// call along to that loaded Project object.
606         /// </summary>
607         [Test]
608         [Ignore("undone")]
UseSameProjectObjectIfChildIsEquivalent()609         public void UseSameProjectObjectIfChildIsEquivalent()
610         {
611             // UNDONE -- need new msbuild task
612             //            Engine engine = new Engine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath));
613             //            engine.AddToolset(new Toolset("44.0", "someToolsPath");
614             //            engine.AddToolset(new Toolset("55.0", "anotherToolsPath");
615             //            engine.AddToolset(new Toolset("66.0", "yetnotherToolsPath");
616 
617             //            string childProjectFullPath = ObjectModelHelpers.CreateFileInTempProjectDirectory("child.proj", @"
618             //                      <Project ToolsVersion='44.0' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
619             //                          <UsingTask TaskName='CreateItem' AssemblyName='Microsoft.Build.Tasks.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'/>
620             //                          <Target Name='BuildTarget'>
621             //                              <CreateItem Include='BuildTargetRan'>
622             //                                  <Output TaskParameter='Include' ItemName='BuildTargetRan'/>
623             //                              </CreateItem>
624             //                          </Target>
625             //                          <Target Name='OtherTarget' Outputs='@(BuildTargetRan)' />
626             //                      </Project>
627             //                      ");
628 
629             //            Project parentProject = new Project(engine, "55.0");
630 
631             //            parentProject.GlobalProperties.SetProperty("foo", "bar");
632             //            Hashtable propertiesTable = new Hashtable();
633             //            propertiesTable.Add("foo", "bar");
634 
635             //            Dictionary<string, ITaskItem[]> targetOutputs =
636             //                new Dictionary<string, ITaskItem[]>(StringComparer.OrdinalIgnoreCase);
637 
638             //            // Now build through engineProxy, passing the same set of global properties in
639             //            EngineProxy engineProxy = CreateEngineProxyWithDummyTaskEngine(engine, parentProject);
640             //            bool success = engineProxy.BuildProjectFile
641             //                (childProjectFullPath, new string[] { "BuildTarget" }, propertiesTable, targetOutputs, "55.0");
642             //            Assertion.Assert("Expected a successful build!", success);
643             //            success = engineProxy.BuildProjectFile
644             //                (childProjectFullPath, new string[] { "OtherTarget" }, propertiesTable, targetOutputs, "55.0");
645             //            Assertion.Assert("Expected a successful build!", success);
646 
647             //            Assertion.AssertEquals(1, targetOutputs["OtherTarget"].Length);
648             //            ITaskItem toolsVersionItem = targetOutputs["OtherTarget"][0];
649             //            Assertion.AssertEquals("BuildTargetRan", toolsVersionItem.ItemSpec);
650         }
651 
652         /// <summary>
653         /// If the project we've been asked to build has different full path, global properties,
654         /// and toolsVersion as those of other loaded Projects, we should create a new project object.
655         /// </summary>
656         [Test]
657         [Ignore("undone")]
UseDifferentProjectObjectIfChildIsNotEquivalent()658         public void UseDifferentProjectObjectIfChildIsNotEquivalent()
659         {
660             // UNDONE
661         }
662 
663         /// <summary>
664         /// If the project we've been asked to build has the same full path, global properties,
665         /// and toolsVersion as those of another loaded Project, we should simply pass this
666         /// call along to that loaded Project object.
667         /// </summary>
668         [Test]
669         [Ignore("undone")]
UseSameProjectObjectIfUsingCallTarget()670         public void UseSameProjectObjectIfUsingCallTarget()
671         {
672             // UNDONE
673         }
674 
CreateEngineProxyWithDummyTaskEngine(Engine e, Project p)675         internal EngineProxy CreateEngineProxyWithDummyTaskEngine(Engine e, Project p)
676         {
677             // UNDONE need a real handle Id and a real TEM pointer
678             XmlElement taskNode = new XmlDocument().CreateElement("MockTask");
679 
680             BuildRequest request = new BuildRequest(EngineCallback.invalidEngineHandle, "mockproject", null, (BuildPropertyGroup)null, null, -1, false, false);
681             ProjectBuildState context = new ProjectBuildState(request, new ArrayList(), new BuildEventContext(0, 0, 0, 0));
682             int handleId = e.EngineCallback.CreateTaskContext(p, null, context, taskNode, 0, new BuildEventContext(0, 0, 0, 0));
683             TaskEngine taskEngine = new TaskEngine
684                                 (
685                                     taskNode,
686                                     null, /* host object */
687                                     p.FullFileName,
688                                     p.FullFileName,
689                                     e.LoggingServices,
690                                     handleId,
691                                     e.NodeManager.TaskExecutionModule,
692                                     new BuildEventContext(0, 0, 0, 0)
693                                 );
694             e.Scheduler.Initialize(new INodeDescription[] { null });
695             return new EngineProxy(e.NodeManager.TaskExecutionModule, handleId, p.FullFileName, p.FullFileName, e.LoggingServices, new BuildEventContext(0, 0, 0, 0));
696         }
697 
698         #endregion
699 
700         #region SerializationTests
701 
702         internal class CustomEvent : CustomBuildEventArgs
703         {
CustomEvent()704             internal CustomEvent() { }
705         }
706 
707         internal class CustomMessageEvent : BuildMessageEventArgs
708         {
CustomMessageEvent()709             internal CustomMessageEvent() { }
710         }
711 
712         internal class CustomErrorEvent : BuildErrorEventArgs
713         {
CustomErrorEvent()714             internal CustomErrorEvent() { }
715         }
716 
717         internal class CustomWarningEvent : BuildWarningEventArgs
718         {
CustomWarningEvent()719             internal CustomWarningEvent() { }
720         }
721 
722         [Test]
TestCustomEventException()723         public void TestCustomEventException()
724         {
725             MyCustomLogger myLogger = new MyCustomLogger();
726             engine.RegisterLogger(myLogger);
727             engineProxy2.LogCustomEvent(new CustomEvent());
728             engine.LoggingServices.ProcessPostedLoggingEvents();
729             Assertion.AssertNull("Expected customEvent to be null", myLogger.lastCustom);
730             Assertion.AssertNotNull("Expected WarningEvent Not to be null", myLogger.lastWarning);
731             Assertion.AssertEquals(1, myLogger.numberOfWarning);
732             Assertion.AssertEquals(0, myLogger.numberOfCustom);
733             myLogger = null;
734 
735             myLogger = new MyCustomLogger();
736             engine.RegisterLogger(myLogger);
737             engineProxy.LogCustomEvent(new CustomEvent());
738             engine.LoggingServices.ProcessPostedLoggingEvents();
739             Assertion.AssertNotNull("Expected customEvent to Not be null", myLogger.lastCustom);
740             Assertion.AssertNull("Expected WarningEvent to be null", myLogger.lastWarning);
741             Assertion.AssertEquals(0, myLogger.numberOfWarning);
742             Assertion.AssertEquals(1, myLogger.numberOfCustom);
743             myLogger = null;
744         }
745 
746         [Test]
TestCustomErrorEventException()747         public void TestCustomErrorEventException()
748         {
749             MyCustomLogger myLogger = new MyCustomLogger();
750             engine.RegisterLogger(myLogger);
751             engineProxy2.LogErrorEvent(new CustomErrorEvent());
752             engine.LoggingServices.ProcessPostedLoggingEvents();
753             Assertion.AssertNull("Expected customErrorEvent to be null", myLogger.lastError);
754             Assertion.AssertNotNull("Expected WarningEvent Not to be null", myLogger.lastWarning);
755             Assertion.AssertEquals(1, myLogger.numberOfWarning);
756             Assertion.AssertEquals(0, myLogger.numberOfError);
757             myLogger = null;
758 
759             myLogger = new MyCustomLogger();
760             engine.RegisterLogger(myLogger);
761             engineProxy.LogErrorEvent(new CustomErrorEvent());
762             engine.LoggingServices.ProcessPostedLoggingEvents();
763             Assertion.AssertNotNull("Expected customErrorEvent to not be null", myLogger.lastError);
764             Assertion.AssertNull("Expected WarningEvent to be null", myLogger.lastWarning);
765             Assertion.AssertEquals(1, myLogger.numberOfError);
766             Assertion.AssertEquals(0, myLogger.numberOfWarning);
767             myLogger = null;
768         }
769         [Test]
TestCustomWarningEventException()770         public void TestCustomWarningEventException()
771         {
772             MyCustomLogger myLogger = new MyCustomLogger();
773             engine.RegisterLogger(myLogger);
774             engineProxy2.LogWarningEvent(new CustomWarningEvent());
775             engine.LoggingServices.ProcessPostedLoggingEvents();
776             Assertion.AssertNotNull("Expected WarningEvent Not to be null", myLogger.lastWarning);
777             Assertion.AssertEquals(1, myLogger.numberOfWarning);
778             myLogger = null;
779 
780             myLogger = new MyCustomLogger();
781             engine.RegisterLogger(myLogger);
782             engineProxy.LogWarningEvent(new CustomWarningEvent());
783             engine.LoggingServices.ProcessPostedLoggingEvents();
784             Assertion.AssertNotNull("Expected WarningEvent Not to be null", myLogger.lastWarning);
785             Assertion.AssertEquals(1, myLogger.numberOfWarning);
786             myLogger = null;
787         }
788         [Test]
TestCustomMessageEventException()789         public void TestCustomMessageEventException()
790         {
791             MyCustomLogger myLogger = new MyCustomLogger();
792             engine.RegisterLogger(myLogger);
793             engineProxy2.LogMessageEvent(new CustomMessageEvent());
794             engine.LoggingServices.ProcessPostedLoggingEvents();
795             Assertion.AssertNull("Expected customMessageEvent to be null", myLogger.lastMessage);
796             Assertion.AssertNotNull("Expected WarningEvent Not to be null", myLogger.lastWarning);
797             Assertion.AssertEquals(0, myLogger.numberOfMessage);
798             Assertion.AssertEquals(1, myLogger.numberOfWarning);
799             myLogger = null;
800 
801             myLogger = new MyCustomLogger();
802             engine.RegisterLogger(myLogger);
803             engineProxy.LogMessageEvent(new CustomMessageEvent());
804             engine.LoggingServices.ProcessPostedLoggingEvents();
805             Assertion.AssertNotNull("Expected customMessageEvent to not be null", myLogger.lastMessage);
806             Assertion.AssertNull("Expected WarningEvent to be null", myLogger.lastWarning);
807             Assertion.AssertEquals(1, myLogger.numberOfMessage);
808             Assertion.AssertEquals(0, myLogger.numberOfWarning);
809             myLogger = null;
810         }
811         #endregion
812     }
813 }
814