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