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 <Project /> 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