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 // </copyright> 5 // <summary>Tests for ProjectInstance internal members</summary> 6 //----------------------------------------------------------------------- 7 8 using System.Collections.Generic; 9 using Microsoft.Build.Execution; 10 using Microsoft.Build.Evaluation; 11 using Microsoft.Build.Framework; 12 using System.Collections; 13 using System; 14 using System.Diagnostics; 15 using Microsoft.Build.Construction; 16 using System.IO; 17 using System.Xml; 18 using System.Linq; 19 using Microsoft.Build.BackEnd; 20 using Microsoft.Build.Engine.UnitTests; 21 using Microsoft.Build.Shared; 22 using Microsoft.Build.UnitTests.BackEnd; 23 using Microsoft.Build.Utilities; 24 using Shouldly; 25 using Xunit; 26 using Xunit.Abstractions; 27 using static Microsoft.Build.Engine.UnitTests.TestComparers.ProjectInstanceModelTestComparers; 28 29 namespace Microsoft.Build.UnitTests.OM.Instance 30 { 31 /// <summary> 32 /// Tests for ProjectInstance internal members 33 /// </summary> 34 public class ProjectInstance_Internal_Tests 35 { 36 private readonly ITestOutputHelper _output; 37 ProjectInstance_Internal_Tests(ITestOutputHelper output)38 public ProjectInstance_Internal_Tests(ITestOutputHelper output) 39 { 40 _output = output; 41 } 42 43 /// <summary> 44 /// Read task registrations 45 /// </summary> 46 [Fact] GetTaskRegistrations()47 public void GetTaskRegistrations() 48 { 49 try 50 { 51 string projectFileContent = @" 52 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 53 <UsingTask TaskName='t0' AssemblyFile='af0'/> 54 <UsingTask TaskName='t1' AssemblyFile='af1a'/> 55 <ItemGroup> 56 <i Include='i0'/> 57 </ItemGroup> 58 <Import Project='{0}'/> 59 </Project>"; 60 61 string importContent = @" 62 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 63 <UsingTask TaskName='t1' AssemblyName='an1' Condition=""'$(p)'=='v'""/> 64 <UsingTask TaskName='t2' AssemblyName='an2' Condition=""'@(i)'=='i0'""/> 65 <UsingTask TaskName='t3' AssemblyFile='af' Condition='false'/> 66 <PropertyGroup> 67 <p>v</p> 68 </PropertyGroup> 69 </Project>"; 70 71 string importPath = ObjectModelHelpers.CreateFileInTempProjectDirectory("import.targets", importContent); 72 projectFileContent = String.Format(projectFileContent, importPath); 73 74 ProjectInstance project = new Project(ProjectRootElement.Create(XmlReader.Create(new StringReader(projectFileContent)))).CreateProjectInstance(); 75 76 Assert.Equal(3, project.TaskRegistry.TaskRegistrations.Count); 77 Assert.Equal(Path.Combine(Directory.GetCurrentDirectory(), "af0"), project.TaskRegistry.TaskRegistrations[new TaskRegistry.RegisteredTaskIdentity("t0", null)][0].TaskFactoryAssemblyLoadInfo.AssemblyFile); 78 Assert.Equal(Path.Combine(Directory.GetCurrentDirectory(), "af1a"), project.TaskRegistry.TaskRegistrations[new TaskRegistry.RegisteredTaskIdentity("t1", null)][0].TaskFactoryAssemblyLoadInfo.AssemblyFile); 79 Assert.Equal("an1", project.TaskRegistry.TaskRegistrations[new TaskRegistry.RegisteredTaskIdentity("t1", null)][1].TaskFactoryAssemblyLoadInfo.AssemblyName); 80 Assert.Equal("an2", project.TaskRegistry.TaskRegistrations[new TaskRegistry.RegisteredTaskIdentity("t2", null)][0].TaskFactoryAssemblyLoadInfo.AssemblyName); 81 } 82 finally 83 { 84 ObjectModelHelpers.DeleteTempProjectDirectory(); 85 } 86 } 87 88 /// <summary> 89 /// InitialTargets and DefaultTargets with imported projects. 90 /// DefaultTargets are not read from imported projects. 91 /// InitialTargets are gathered from imports depth-first. 92 /// </summary> 93 [Fact] InitialTargetsDefaultTargets()94 public void InitialTargetsDefaultTargets() 95 { 96 try 97 { 98 string projectFileContent = @" 99 <Project DefaultTargets='d0a;d0b' InitialTargets='i0a;i0b' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 100 <Import Project='{0}'/> 101 <Import Project='{1}'/> 102 </Project>"; 103 104 string import1Content = @" 105 <Project DefaultTargets='d1a;d1b' InitialTargets='i1a;i1b' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 106 <Import Project='{0}'/> 107 </Project>"; 108 109 string import2Content = @"<Project DefaultTargets='d2a;2db' InitialTargets='i2a;i2b' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'/>"; 110 111 string import3Content = @"<Project DefaultTargets='d3a;d3b' InitialTargets='i3a;i3b' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'/>"; 112 113 string import2Path = ObjectModelHelpers.CreateFileInTempProjectDirectory("import2.targets", import2Content); 114 string import3Path = ObjectModelHelpers.CreateFileInTempProjectDirectory("import3.targets", import3Content); 115 116 import1Content = String.Format(import1Content, import3Path); 117 string import1Path = ObjectModelHelpers.CreateFileInTempProjectDirectory("import1.targets", import1Content); 118 119 projectFileContent = String.Format(projectFileContent, import1Path, import2Path); 120 121 ProjectInstance project = new Project(ProjectRootElement.Create(XmlReader.Create(new StringReader(projectFileContent)))).CreateProjectInstance(); 122 123 Helpers.AssertListsValueEqual(new string[] { "d0a", "d0b" }, project.DefaultTargets); 124 Helpers.AssertListsValueEqual(new string[] { "i0a", "i0b", "i1a", "i1b", "i3a", "i3b", "i2a", "i2b" }, project.InitialTargets); 125 } 126 finally 127 { 128 ObjectModelHelpers.DeleteTempProjectDirectory(); 129 } 130 } 131 132 /// <summary> 133 /// InitialTargets and DefaultTargets with imported projects. 134 /// DefaultTargets are not read from imported projects. 135 /// InitialTargets are gathered from imports depth-first. 136 /// </summary> 137 [Fact] InitialTargetsDefaultTargetsEscaped()138 public void InitialTargetsDefaultTargetsEscaped() 139 { 140 try 141 { 142 string projectFileContent = @" 143 <Project DefaultTargets='d0a%3bd0b' InitialTargets='i0a%3bi0b' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 144 </Project>"; 145 146 ProjectInstance project = new Project(ProjectRootElement.Create(XmlReader.Create(new StringReader(projectFileContent)))).CreateProjectInstance(); 147 148 Helpers.AssertListsValueEqual(new string[] { "d0a;d0b" }, project.DefaultTargets); 149 Helpers.AssertListsValueEqual(new string[] { "i0a;i0b" }, project.InitialTargets); 150 } 151 finally 152 { 153 ObjectModelHelpers.DeleteTempProjectDirectory(); 154 } 155 } 156 157 /// <summary> 158 /// Read property group under target 159 /// </summary> 160 [Fact] GetPropertyGroupUnderTarget()161 public void GetPropertyGroupUnderTarget() 162 { 163 string content = @" 164 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 165 <Target Name='t'> 166 <PropertyGroup Condition='c1'> 167 <p1 Condition='c2'>v1</p1> 168 <p2/> 169 </PropertyGroup> 170 </Target> 171 </Project> 172 "; 173 174 ProjectInstance p = GetProjectInstance(content); 175 ProjectPropertyGroupTaskInstance propertyGroup = (ProjectPropertyGroupTaskInstance)(p.Targets["t"].Children[0]); 176 177 Assert.Equal("c1", propertyGroup.Condition); 178 179 List<ProjectPropertyGroupTaskPropertyInstance> properties = Helpers.MakeList(propertyGroup.Properties); 180 Assert.Equal(2, properties.Count); 181 182 Assert.Equal("c2", properties[0].Condition); 183 Assert.Equal("v1", properties[0].Value); 184 185 Assert.Equal(String.Empty, properties[1].Condition); 186 Assert.Equal(String.Empty, properties[1].Value); 187 } 188 189 /// <summary> 190 /// Read item group under target 191 /// </summary> 192 [Fact] GetItemGroupUnderTarget()193 public void GetItemGroupUnderTarget() 194 { 195 string content = @" 196 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 197 <Target Name='t'> 198 <ItemGroup Condition='c1'> 199 <i Include='i1' Exclude='e1' Condition='c2'> 200 <m Condition='c3'>m1</m> 201 <n>n1</n> 202 </i> 203 <j Remove='r1'/> 204 <k> 205 <o>o1</o> 206 </k> 207 </ItemGroup> 208 </Target> 209 </Project> 210 "; 211 212 ProjectInstance p = GetProjectInstance(content); 213 ProjectItemGroupTaskInstance itemGroup = (ProjectItemGroupTaskInstance)(p.Targets["t"].Children[0]); 214 215 Assert.Equal("c1", itemGroup.Condition); 216 217 List<ProjectItemGroupTaskItemInstance> items = Helpers.MakeList(itemGroup.Items); 218 Assert.Equal(3, items.Count); 219 220 Assert.Equal("i1", items[0].Include); 221 Assert.Equal("e1", items[0].Exclude); 222 Assert.Equal(String.Empty, items[0].Remove); 223 Assert.Equal("c2", items[0].Condition); 224 225 Assert.Equal(String.Empty, items[1].Include); 226 Assert.Equal(String.Empty, items[1].Exclude); 227 Assert.Equal("r1", items[1].Remove); 228 Assert.Equal(String.Empty, items[1].Condition); 229 230 Assert.Equal(String.Empty, items[2].Include); 231 Assert.Equal(String.Empty, items[2].Exclude); 232 Assert.Equal(String.Empty, items[2].Remove); 233 Assert.Equal(String.Empty, items[2].Condition); 234 235 List<ProjectItemGroupTaskMetadataInstance> metadata1 = Helpers.MakeList(items[0].Metadata); 236 List<ProjectItemGroupTaskMetadataInstance> metadata2 = Helpers.MakeList(items[1].Metadata); 237 List<ProjectItemGroupTaskMetadataInstance> metadata3 = Helpers.MakeList(items[2].Metadata); 238 239 Assert.Equal(2, metadata1.Count); 240 Assert.Equal(0, metadata2.Count); 241 Assert.Equal(1, metadata3.Count); 242 243 Assert.Equal("c3", metadata1[0].Condition); 244 Assert.Equal("m1", metadata1[0].Value); 245 Assert.Equal(String.Empty, metadata1[1].Condition); 246 Assert.Equal("n1", metadata1[1].Value); 247 248 Assert.Equal(String.Empty, metadata3[0].Condition); 249 Assert.Equal("o1", metadata3[0].Value); 250 } 251 252 /// <summary> 253 /// Task registry accessor 254 /// </summary> 255 [Fact] GetTaskRegistry()256 public void GetTaskRegistry() 257 { 258 ProjectInstance p = GetSampleProjectInstance(); 259 260 Assert.Equal(true, p.TaskRegistry != null); 261 } 262 263 /// <summary> 264 /// Global properties accessor 265 /// </summary> 266 [Fact] GetGlobalProperties()267 public void GetGlobalProperties() 268 { 269 ProjectInstance p = GetSampleProjectInstance(); 270 271 Assert.Equal("v1", p.GlobalPropertiesDictionary["g1"].EvaluatedValue); 272 Assert.Equal("v2", p.GlobalPropertiesDictionary["g2"].EvaluatedValue); 273 } 274 275 /// <summary> 276 /// ToolsVersion accessor 277 /// </summary> 278 [Fact] GetToolsVersion()279 public void GetToolsVersion() 280 { 281 ProjectInstance p = GetSampleProjectInstance(); 282 283 Assert.Equal(ObjectModelHelpers.MSBuildDefaultToolsVersion, p.Toolset.ToolsVersion); 284 } 285 286 [Fact] UsingExplicitToolsVersionShouldBeFalseWhenNoToolsetIsReferencedInProject()287 public void UsingExplicitToolsVersionShouldBeFalseWhenNoToolsetIsReferencedInProject() 288 { 289 var projectInstance = new ProjectInstance( 290 new ProjectRootElement( 291 XmlReader.Create(new StringReader("<Project></Project>")), ProjectCollection.GlobalProjectCollection.ProjectRootElementCache, false, false) 292 ); 293 294 projectInstance.UsingDifferentToolsVersionFromProjectFile.ShouldBeFalse(); 295 296 } 297 298 /// <summary> 299 /// Toolset data is cloned properly 300 /// </summary> 301 [Fact] CloneToolsetData()302 public void CloneToolsetData() 303 { 304 var projectCollection = new ProjectCollection(); 305 CreateMockToolsetIfNotExists("TESTTV", projectCollection); 306 ProjectInstance first = GetSampleProjectInstance(null, null, projectCollection, toolsVersion: "TESTTV"); 307 ProjectInstance second = first.DeepCopy(); 308 Assert.Equal(first.ToolsVersion, second.ToolsVersion); 309 Assert.Equal(first.ExplicitToolsVersion, second.ExplicitToolsVersion); 310 Assert.Equal(first.ExplicitToolsVersionSpecified, second.ExplicitToolsVersionSpecified); 311 } 312 313 /// <summary> 314 /// Test ProjectInstance's surfacing of the sub-toolset version 315 /// </summary> 316 [Fact] GetSubToolsetVersion()317 public void GetSubToolsetVersion() 318 { 319 string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion"); 320 321 try 322 { 323 Environment.SetEnvironmentVariable("VisualStudioVersion", null); 324 325 ProjectInstance p = GetSampleProjectInstance(null, null, new ProjectCollection()); 326 327 Assert.Equal(ObjectModelHelpers.MSBuildDefaultToolsVersion, p.Toolset.ToolsVersion); 328 Assert.Equal(p.Toolset.DefaultSubToolsetVersion, p.SubToolsetVersion); 329 330 if (p.Toolset.DefaultSubToolsetVersion == null) 331 { 332 Assert.Equal(MSBuildConstants.CurrentVisualStudioVersion, p.GetPropertyValue("VisualStudioVersion")); 333 } 334 else 335 { 336 Assert.Equal(p.Toolset.DefaultSubToolsetVersion, p.GetPropertyValue("VisualStudioVersion")); 337 } 338 } 339 finally 340 { 341 Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion); 342 } 343 } 344 345 /// <summary> 346 /// Test ProjectInstance's surfacing of the sub-toolset version when it is overridden by a value in the 347 /// environment 348 /// </summary> 349 [Fact] 350 [Trait("Category", "mono-osx-failing")] GetSubToolsetVersion_FromEnvironment()351 public void GetSubToolsetVersion_FromEnvironment() 352 { 353 string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion"); 354 355 try 356 { 357 Environment.SetEnvironmentVariable("VisualStudioVersion", "ABCD"); 358 359 ProjectInstance p = GetSampleProjectInstance(null, null, new ProjectCollection()); 360 361 Assert.Equal(ObjectModelHelpers.MSBuildDefaultToolsVersion, p.Toolset.ToolsVersion); 362 Assert.Equal("ABCD", p.SubToolsetVersion); 363 Assert.Equal("ABCD", p.GetPropertyValue("VisualStudioVersion")); 364 } 365 finally 366 { 367 Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion); 368 } 369 } 370 371 /// <summary> 372 /// Test ProjectInstance's surfacing of the sub-toolset version when it is overridden by a global property 373 /// </summary> 374 [Fact] GetSubToolsetVersion_FromProjectGlobalProperties()375 public void GetSubToolsetVersion_FromProjectGlobalProperties() 376 { 377 string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion"); 378 379 try 380 { 381 Environment.SetEnvironmentVariable("VisualStudioVersion", null); 382 383 IDictionary<string, string> globalProperties = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); 384 globalProperties.Add("VisualStudioVersion", "ABCDE"); 385 386 ProjectInstance p = GetSampleProjectInstance(null, globalProperties, new ProjectCollection()); 387 388 Assert.Equal(ObjectModelHelpers.MSBuildDefaultToolsVersion, p.Toolset.ToolsVersion); 389 Assert.Equal("ABCDE", p.SubToolsetVersion); 390 Assert.Equal("ABCDE", p.GetPropertyValue("VisualStudioVersion")); 391 } 392 finally 393 { 394 Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion); 395 } 396 } 397 398 /// <summary> 399 /// Verify that if a sub-toolset version is passed to the constructor, it all other heuristic methods for 400 /// getting the sub-toolset version. 401 /// </summary> 402 [Fact] GetSubToolsetVersion_FromConstructor()403 public void GetSubToolsetVersion_FromConstructor() 404 { 405 string originalVisualStudioVersion = Environment.GetEnvironmentVariable("VisualStudioVersion"); 406 407 try 408 { 409 Environment.SetEnvironmentVariable("VisualStudioVersion", "ABC"); 410 411 string projectContent = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 412 <Target Name='t'> 413 <Message Text='Hello'/> 414 </Target> 415 </Project>"; 416 417 ProjectRootElement xml = ProjectRootElement.Create(XmlReader.Create(new StringReader(projectContent))); 418 419 IDictionary<string, string> globalProperties = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); 420 globalProperties.Add("VisualStudioVersion", "ABCD"); 421 422 IDictionary<string, string> projectCollectionGlobalProperties = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); 423 projectCollectionGlobalProperties.Add("VisualStudioVersion", "ABCDE"); 424 425 ProjectInstance p = new ProjectInstance(xml, globalProperties, ObjectModelHelpers.MSBuildDefaultToolsVersion, "ABCDEF", new ProjectCollection(projectCollectionGlobalProperties)); 426 427 Assert.Equal(ObjectModelHelpers.MSBuildDefaultToolsVersion, p.Toolset.ToolsVersion); 428 Assert.Equal("ABCDEF", p.SubToolsetVersion); 429 Assert.Equal("ABCDEF", p.GetPropertyValue("VisualStudioVersion")); 430 } 431 finally 432 { 433 Environment.SetEnvironmentVariable("VisualStudioVersion", originalVisualStudioVersion); 434 } 435 } 436 437 /// <summary> 438 /// DefaultTargets accessor 439 /// </summary> 440 [Fact] GetDefaultTargets()441 public void GetDefaultTargets() 442 { 443 ProjectInstance p = GetSampleProjectInstance(); 444 445 Helpers.AssertListsValueEqual(new string[] { "dt" }, p.DefaultTargets); 446 } 447 448 /// <summary> 449 /// InitialTargets accessor 450 /// </summary> 451 [Fact] GetInitialTargets()452 public void GetInitialTargets() 453 { 454 ProjectInstance p = GetSampleProjectInstance(); 455 456 Helpers.AssertListsValueEqual(new string[] { "it" }, p.InitialTargets); 457 } 458 459 /// <summary> 460 /// Cloning project clones targets 461 /// </summary> 462 [Fact] CloneTargets()463 public void CloneTargets() 464 { 465 var hostServices = new HostServices(); 466 467 ProjectInstance first = GetSampleProjectInstance(hostServices); 468 ProjectInstance second = first.DeepCopy(); 469 470 // Targets, tasks are immutable so we can expect the same objects 471 Assert.True(Object.ReferenceEquals(first.Targets, second.Targets)); 472 Assert.True(Object.ReferenceEquals(first.Targets["t"], second.Targets["t"])); 473 474 var firstTasks = first.Targets["t"]; 475 var secondTasks = second.Targets["t"]; 476 477 Assert.True(Object.ReferenceEquals(firstTasks.Children[0], secondTasks.Children[0])); 478 } 479 480 /// <summary> 481 /// Cloning project copies task registry 482 /// </summary> 483 [Fact] CloneTaskRegistry()484 public void CloneTaskRegistry() 485 { 486 ProjectInstance first = GetSampleProjectInstance(); 487 ProjectInstance second = first.DeepCopy(); 488 489 // Task registry object should be immutable 490 Assert.Same(first.TaskRegistry, second.TaskRegistry); 491 } 492 493 /// <summary> 494 /// Cloning project copies global properties 495 /// </summary> 496 [Fact] CloneGlobalProperties()497 public void CloneGlobalProperties() 498 { 499 ProjectInstance first = GetSampleProjectInstance(); 500 ProjectInstance second = first.DeepCopy(); 501 502 Assert.Equal("v1", second.GlobalPropertiesDictionary["g1"].EvaluatedValue); 503 Assert.Equal("v2", second.GlobalPropertiesDictionary["g2"].EvaluatedValue); 504 } 505 506 /// <summary> 507 /// Cloning project copies default targets 508 /// </summary> 509 [Fact] CloneDefaultTargets()510 public void CloneDefaultTargets() 511 { 512 ProjectInstance first = GetSampleProjectInstance(); 513 ProjectInstance second = first.DeepCopy(); 514 515 Helpers.AssertListsValueEqual(new string[] { "dt" }, second.DefaultTargets); 516 } 517 518 /// <summary> 519 /// Cloning project copies initial targets 520 /// </summary> 521 [Fact] CloneInitialTargets()522 public void CloneInitialTargets() 523 { 524 ProjectInstance first = GetSampleProjectInstance(); 525 ProjectInstance second = first.DeepCopy(); 526 527 Helpers.AssertListsValueEqual(new string[] { "it" }, second.InitialTargets); 528 } 529 530 /// <summary> 531 /// Cloning project copies toolsversion 532 /// </summary> 533 [Fact] CloneToolsVersion()534 public void CloneToolsVersion() 535 { 536 ProjectInstance first = GetSampleProjectInstance(); 537 ProjectInstance second = first.DeepCopy(); 538 539 Assert.Equal(first.Toolset, second.Toolset); 540 } 541 542 /// <summary> 543 /// Cloning project copies toolsversion 544 /// </summary> 545 [Fact] CloneStateTranslation()546 public void CloneStateTranslation() 547 { 548 ProjectInstance first = GetSampleProjectInstance(); 549 first.TranslateEntireState = true; 550 551 ProjectInstance second = first.DeepCopy(); 552 553 Assert.Equal(true, second.TranslateEntireState); 554 } 555 556 /// <summary> 557 /// Tests building a simple project and verifying the log looks as expected. 558 /// </summary> 559 [Fact] Build()560 public void Build() 561 { 562 // Setting the current directory to the MSBuild running location. It *should* be this 563 // already, but if it's not some other test changed it and didn't change it back. If 564 // the directory does not include the reference dlls the compilation will fail. 565 Directory.SetCurrentDirectory(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory); 566 567 string projectFileContent = @" 568 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 569 <UsingTask TaskName='Microsoft.Build.Tasks.Message' AssemblyFile='$(MSBuildBinPath)\Microsoft.Build.Tasks.Core.dll'/> 570 <ItemGroup> 571 <i Include='i0'/> 572 </ItemGroup> 573 <Target Name='Build'> 574 <Message Text='Building...'/> 575 <Message Text='Completed!'/> 576 </Target> 577 </Project>"; 578 579 ProjectInstance projectInstance = GetProjectInstance(projectFileContent); 580 List<ILogger> loggers = new List<ILogger>(); 581 MockLogger mockLogger = new MockLogger(_output); 582 loggers.Add(mockLogger); 583 bool success = projectInstance.Build("Build", loggers); 584 585 Assert.True(success); 586 mockLogger.AssertLogContains(new string[] { "Building...", "Completed!" }); 587 } 588 589 [Theory] 590 [InlineData( 591 @" <Project> 592 </Project> 593 ")] 594 // Project with one of each direct child(indirect children trees are tested separately) 595 [InlineData( 596 @" <Project InitialTargets=`t1` DefaultTargets=`t2` ToolsVersion=`{0}`> 597 <UsingTask TaskName=`t1` AssemblyFile=`f1`/> 598 599 <ItemDefinitionGroup> 600 <i> 601 <n>n1</n> 602 </i> 603 </ItemDefinitionGroup> 604 605 <PropertyGroup> 606 <p1>v1</p1> 607 </PropertyGroup> 608 609 <ItemGroup> 610 <i Include='i0'/> 611 </ItemGroup> 612 613 <Target Name='t1'> 614 <t1/> 615 </Target> 616 617 <Target Name='t2' BeforeTargets=`t1`> 618 <t2/> 619 </Target> 620 621 <Target Name='t3' AfterTargets=`t2`> 622 <t3/> 623 </Target> 624 </Project> 625 ")] 626 // Project with at least two instances of each direct child. Tests that collections serialize well. 627 [InlineData( 628 @" <Project InitialTargets=`t1` DefaultTargets=`t2` ToolsVersion=`{0}`> 629 <UsingTask TaskName=`t1` AssemblyFile=`f1`/> 630 <UsingTask TaskName=`t2` AssemblyFile=`f2`/> 631 632 <ItemDefinitionGroup> 633 <i> 634 <n>n1</n> 635 </i> 636 </ItemDefinitionGroup> 637 638 <ItemDefinitionGroup> 639 <i2> 640 <n2>n2</n2> 641 </i2> 642 </ItemDefinitionGroup> 643 644 <PropertyGroup> 645 <p1>v1</p1> 646 </PropertyGroup> 647 648 <PropertyGroup> 649 <p2>v2</p2> 650 </PropertyGroup> 651 652 <ItemGroup> 653 <i Include='i1'/> 654 </ItemGroup> 655 656 <ItemGroup> 657 <i2 Include='i2'> 658 <m1 Condition=`1==1`>m1</m1> 659 <m2>m2</m2> 660 </i2> 661 </ItemGroup> 662 663 <Target Name='t1'> 664 <t1/> 665 </Target> 666 667 <Target Name='t2' BeforeTargets=`t1`> 668 <t2/> 669 </Target> 670 671 <Target Name='t3' AfterTargets=`t1`> 672 <t3/> 673 </Target> 674 675 <Target Name='t4' BeforeTargets=`t1`> 676 <t4/> 677 </Target> 678 679 <Target Name='t5' AfterTargets=`t1`> 680 <t5/> 681 </Target> 682 </Project> 683 ")] ProjectInstanceCanSerializeEntireStateViaTranslator(string projectContents)684 public void ProjectInstanceCanSerializeEntireStateViaTranslator(string projectContents) 685 { 686 projectContents = string.Format(projectContents, MSBuildConstants.CurrentToolsVersion); 687 688 var original = new ProjectInstance(ProjectRootElement.Create(XmlReader.Create(new StringReader(ObjectModelHelpers.CleanupFileContents(projectContents))))); 689 690 original.TranslateEntireState = true; 691 692 ((INodePacketTranslatable) original).Translate(TranslationHelpers.GetWriteTranslator()); 693 var copy = ProjectInstance.FactoryForDeserialization(TranslationHelpers.GetReadTranslator()); 694 695 Assert.Equal(original, copy, new ProjectInstanceComparer()); 696 } 697 ProjectInstanceFactory(string file, ProjectRootElement xml, ProjectCollection collection)698 public delegate ProjectInstance ProjectInstanceFactory(string file, ProjectRootElement xml, ProjectCollection collection); 699 ProjectInstanceHasEvaluationIdTestData()700 public static IEnumerable<object[]> ProjectInstanceHasEvaluationIdTestData() 701 { 702 // from file 703 yield return new ProjectInstanceFactory[] 704 { 705 (f, xml, c) => new ProjectInstance(f, null, null, c) 706 }; 707 708 // from Project 709 yield return new ProjectInstanceFactory[] 710 { 711 (f, xml, c) => new Project(f, null, null, c).CreateProjectInstance() 712 }; 713 714 // from DeepCopy 715 yield return new ProjectInstanceFactory[] 716 { 717 (f, xml, c) => new ProjectInstance(f, null, null, c).DeepCopy() 718 }; 719 720 // from ProjectRootElement 721 yield return new ProjectInstanceFactory[] 722 { 723 (f, xml, c) => new ProjectInstance(xml, null, null, c).DeepCopy() 724 }; 725 726 // from translated project instance 727 yield return new ProjectInstanceFactory[] 728 { 729 (f, xml, c) => 730 { 731 var pi = new ProjectInstance(f, null, null, c); 732 pi.AddItem("foo", "bar"); 733 pi.TranslateEntireState = true; 734 735 ((INodePacketTranslatable) pi).Translate(TranslationHelpers.GetWriteTranslator()); 736 var copy = ProjectInstance.FactoryForDeserialization(TranslationHelpers.GetReadTranslator()); 737 738 return copy; 739 } 740 }; 741 } 742 743 [Theory] 744 [MemberData(nameof(ProjectInstanceHasEvaluationIdTestData))] ProjectInstanceHasEvaluationId(ProjectInstanceFactory projectInstanceFactory)745 public void ProjectInstanceHasEvaluationId(ProjectInstanceFactory projectInstanceFactory) 746 { 747 using (var env = TestEnvironment.Create()) 748 { 749 var file = env.CreateFile().Path; 750 var projectCollection = env.CreateProjectCollection().Collection; 751 752 var xml = ProjectRootElement.Create(projectCollection); 753 xml.Save(file); 754 755 var projectInstance = projectInstanceFactory.Invoke(file, xml, projectCollection); 756 Assert.NotEqual(BuildEventContext.InvalidEvaluationId, projectInstance.EvaluationId); 757 } 758 } 759 760 /// <summary> 761 /// Create a ProjectInstance from provided project content 762 /// </summary> GetProjectInstance(string content)763 private static ProjectInstance GetProjectInstance(string content) 764 { 765 return GetProjectInstance(content, null); 766 } 767 768 /// <summary> 769 /// Create a ProjectInstance from provided project content and host services object 770 /// </summary> GetProjectInstance(string content, HostServices hostServices)771 private static ProjectInstance GetProjectInstance(string content, HostServices hostServices) 772 { 773 return GetProjectInstance(content, hostServices, null, null); 774 } 775 776 /// <summary> 777 /// Create a ProjectInstance from provided project content and host services object 778 /// </summary> GetProjectInstance(string content, HostServices hostServices, IDictionary<string, string> globalProperties, ProjectCollection projectCollection, string toolsVersion = null)779 private static ProjectInstance GetProjectInstance(string content, HostServices hostServices, IDictionary<string, string> globalProperties, ProjectCollection projectCollection, string toolsVersion = null) 780 { 781 XmlReader reader = XmlReader.Create(new StringReader(content)); 782 783 if (globalProperties == null) 784 { 785 // choose some interesting defaults if we weren't explicitly asked to use a set. 786 globalProperties = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); 787 globalProperties.Add("g1", "v1"); 788 globalProperties.Add("g2", "v2"); 789 } 790 791 Project project = new Project(reader, globalProperties, toolsVersion ?? ObjectModelHelpers.MSBuildDefaultToolsVersion, projectCollection ?? ProjectCollection.GlobalProjectCollection); 792 793 ProjectInstance instance = project.CreateProjectInstance(); 794 795 return instance; 796 } 797 798 /// <summary> 799 /// Create a ProjectInstance with some items and properties and targets 800 /// </summary> GetSampleProjectInstance()801 private static ProjectInstance GetSampleProjectInstance() 802 { 803 return GetSampleProjectInstance(null); 804 } 805 806 /// <summary> 807 /// Create a ProjectInstance with some items and properties and targets 808 /// </summary> GetSampleProjectInstance(HostServices hostServices)809 private static ProjectInstance GetSampleProjectInstance(HostServices hostServices) 810 { 811 return GetSampleProjectInstance(hostServices, null, null); 812 } 813 814 /// <summary> 815 /// Create a ProjectInstance with some items and properties and targets 816 /// </summary> GetSampleProjectInstance(HostServices hostServices, IDictionary<string, string> globalProperties, ProjectCollection projectCollection, string toolsVersion = null)817 private static ProjectInstance GetSampleProjectInstance(HostServices hostServices, IDictionary<string, string> globalProperties, ProjectCollection projectCollection, string toolsVersion = null) 818 { 819 string toolsVersionSubstring = toolsVersion != null ? "ToolsVersion=\"" + toolsVersion + "\" " : String.Empty; 820 string content = @" 821 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' InitialTargets='it' DefaultTargets='dt' " + toolsVersionSubstring + @"> 822 <PropertyGroup> 823 <p1>v1</p1> 824 <p2>v2</p2> 825 <p2>$(p2)X$(p)</p2> 826 </PropertyGroup> 827 <ItemGroup> 828 <i Include='i0'/> 829 <i Include='i1'> 830 <m>m1</m> 831 </i> 832 <i Include='$(p1)'/> 833 </ItemGroup> 834 <Target Name='t'> 835 <t1 a='a1' b='b1' ContinueOnError='coe' Condition='c'/> 836 <t2/> 837 </Target> 838 <Target Name='tt'/> 839 </Project> 840 "; 841 842 ProjectInstance p = GetProjectInstance(content, hostServices, globalProperties, projectCollection, toolsVersion); 843 844 return p; 845 } 846 847 /// <summary> 848 /// Creates a toolset with the given tools version if one does not already exist. 849 /// </summary> CreateMockToolsetIfNotExists(string toolsVersion, ProjectCollection projectCollection)850 private static void CreateMockToolsetIfNotExists(string toolsVersion, ProjectCollection projectCollection) 851 { 852 ProjectCollection pc = projectCollection; 853 if (!pc.Toolsets.Any(t => String.Equals(t.ToolsVersion, toolsVersion, StringComparison.OrdinalIgnoreCase))) 854 { 855 Toolset template = pc.Toolsets.First(t => String.Equals(t.ToolsVersion, pc.DefaultToolsVersion, StringComparison.OrdinalIgnoreCase)); 856 var toolset = new Toolset( 857 toolsVersion, 858 template.ToolsPath, 859 template.Properties.ToDictionary(p => p.Key, p => p.Value.EvaluatedValue), 860 pc, 861 null); 862 pc.AddToolset(toolset); 863 } 864 } 865 } 866 } 867