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>Unit tests for the BuildManager object.</summary>
6 //-----------------------------------------------------------------------
7 
8 using System;
9 using System.CodeDom.Compiler;
10 using System.Collections;
11 using System.Collections.Generic;
12 using System.Diagnostics;
13 using System.Globalization;
14 using System.IO;
15 using System.Linq;
16 using System.Threading;
17 using System.Xml;
18 
19 using Microsoft.Build.BackEnd;
20 using Microsoft.Build.Collections;
21 using Microsoft.Build.Construction;
22 using Microsoft.Build.Engine.UnitTests;
23 using Microsoft.Build.Evaluation;
24 using Microsoft.Build.Execution;
25 using Microsoft.Build.Framework;
26 using Microsoft.Build.Logging;
27 using Microsoft.Build.Shared;
28 using Microsoft.Build.Utilities;
29 using Shouldly;
30 using Xunit;
31 using Xunit.Abstractions;
32 using static Microsoft.Build.UnitTests.ObjectModelHelpers;
33 
34 namespace Microsoft.Build.UnitTests.BackEnd
35 {
36     /// <summary>
37     /// The test fixture for the BuildManager
38     /// </summary>
39     public class BuildManager_Tests : IDisposable
40     {
41         /// <summary>
42         /// The mock logger for testing.
43         /// </summary>
44         private readonly MockLogger _logger;
45 
46         /// <summary>
47         /// The standard build manager for each test.
48         /// </summary>
49         private BuildManager _buildManager;
50 
51         /// <summary>
52         /// The build parameters.
53         /// </summary>
54         private readonly BuildParameters _parameters;
55 
56         /// <summary>
57         /// The project collection used.
58         /// </summary>
59         private readonly ProjectCollection _projectCollection;
60 
61         private readonly TestEnvironment _env;
62         private readonly ITestOutputHelper _output;
63 
64         /// <summary>
65         /// SetUp
66         /// </summary>
BuildManager_Tests(ITestOutputHelper output)67         public BuildManager_Tests(ITestOutputHelper output)
68         {
69             _output = output;
70             // Ensure that any previous tests which may have been using the default BuildManager do not conflict with us.
71             BuildManager.DefaultBuildManager.Dispose();
72 
73             _logger = new MockLogger(output);
74             _parameters = new BuildParameters
75             {
76                 ShutdownInProcNodeOnBuildFinish = true,
77                 Loggers = new ILogger[] { _logger },
78                 EnableNodeReuse = false
79             };
80             _buildManager = new BuildManager();
81             _projectCollection = new ProjectCollection();
82 
83             _env = TestEnvironment.Create(output);
84             _env.SetEnvironmentVariable("MSBUILDINPROCENVCHECK", "1");
85         }
86 
87         /// <summary>
88         /// TearDown
89         /// </summary>
Dispose()90         public void Dispose()
91         {
92             _buildManager.Dispose();
93             _projectCollection.Dispose();
94             _env.Dispose();
95         }
96 
97         /// <summary>
98         /// Check that we behave reasonably when passed a null ProjectCollection
99         /// </summary>
100         [Fact]
BuildParametersWithNullCollection()101         public void BuildParametersWithNullCollection()
102         {
103             Assert.Throws<ArgumentNullException>(() => { new BuildParameters(null); });
104         }
105 
106         /// <summary>
107         /// A simple successful build.
108         /// </summary>
109         [Fact]
SimpleBuild()110         public void SimpleBuild()
111         {
112             string contents = CleanupFileContents(@"
113 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
114 <PropertyGroup>
115        <InitialProperty1>InitialProperty1</InitialProperty1>
116        <InitialProperty2>InitialProperty2</InitialProperty2>
117        <InitialProperty3>InitialProperty3</InitialProperty3>
118 </PropertyGroup>
119  <Target Name='test'>
120 	<Message Text='[success]'/>
121  </Target>
122 </Project>
123 ");
124             BuildRequestData data = GetBuildRequestData(contents);
125             BuildResult result = _buildManager.Build(_parameters, data);
126             Assert.Equal(BuildResultCode.Success, result.OverallResult);
127             _logger.AssertLogContains("[success]");
128             Assert.Equal(1, _logger.ProjectStartedEvents.Count);
129 
130             ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0];
131             Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
132 
133             string propertyValue;
134             Assert.True(properties.TryGetValue("InitialProperty1", out propertyValue));
135             Assert.True(String.Equals(propertyValue, "InitialProperty1", StringComparison.OrdinalIgnoreCase));
136 
137             Assert.True(properties.TryGetValue("InitialProperty2", out propertyValue));
138             Assert.True(String.Equals(propertyValue, "InitialProperty2", StringComparison.OrdinalIgnoreCase));
139 
140             Assert.True(properties.TryGetValue("InitialProperty3", out propertyValue));
141             Assert.True(String.Equals(propertyValue, "InitialProperty3", StringComparison.OrdinalIgnoreCase));
142         }
143 
144 #if FEATURE_CODETASKFACTORY
145         /// <summary>
146         /// Verify that the environment between two msbuild calls to the same project are stored
147         /// so that on the next call we get access to them
148         /// </summary>
149         [Fact]
VerifyEnvironmentSavedBetweenCalls()150         public void VerifyEnvironmentSavedBetweenCalls()
151         {
152             string contents1 = CleanupFileContents(@"
153 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
154  <UsingTask TaskName='SetEnvv' TaskFactory='CodeTaskFactory' AssemblyFile='$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll' >
155                             <Task>
156                                 <Code Language='cs'>
157                                     System.Environment.SetEnvironmentVariable(""MOO"", ""When the dawn comes, tonight will be a memory too"");
158                                 </Code>
159                            </Task>
160 </UsingTask>
161                         <Target Name='SetEnv'>
162                             <SetEnvv/>
163                         </Target>
164                         <Target Name='Message1'>
165                             <Exec Command='echo What does a cat say : " + (NativeMethodsShared.IsWindows ? "%MOO%" : "$MOO") + @"' />
166                         </Target>
167 </Project>
168 ");
169 
170             var project = new Project(XmlReader.Create(new StringReader(contents1)), null, null, _projectCollection)
171             {
172                 FullPath = _env.CreateFile(".proj").Path
173             };
174 
175             project.Save();
176 
177 
178                 string contents2 = CleanupFileContents(@"
179 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
180         <Target Name='Build' >
181          <MSBuild Targets='SetEnv' Projects='" + project.FullPath + "'/>" +
182              "<MSBuild Targets='Message1' Projects='" + project.FullPath + "'/>" +
183             @"</Target>
184 </Project>
185 ");
186 
187             ProjectInstance instance = CreateProjectInstance(contents2, null, _projectCollection, true);
188             BuildRequestData data = new BuildRequestData(instance, new[] { "Build" }, _projectCollection.HostServices);
189 
190             BuildResult result = _buildManager.Build(_parameters, data);
191             Assert.Equal(BuildResultCode.Success, result.OverallResult);
192             _logger.AssertLogContains("What does a cat say : When the dawn comes, tonight will be a memory too");
193         }
194 #endif
195 
196         /// <summary>
197         /// Verify if idle nodes are shutdown when BuildManager.ShutdownAllNodes is evoked.
198         /// The final number of nodes has to be less or equal the number of nodes already in
199         /// the system before this method was called.
200         /// </summary>
201 #if RUNTIME_TYPE_NETCORE
202         [Theory(Skip = "https://github.com/Microsoft/msbuild/issues/1975")]
203 #elif MONO
204         [Theory(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
205 #else
206         [Theory(Skip = "https://github.com/Microsoft/msbuild/issues/2057")]
207         [InlineData(8, false)]
208 #endif
ShutdownNodesAfterParallelBuild(int numberOfParallelProjectsToBuild, bool enbaleDebugComm)209         public void ShutdownNodesAfterParallelBuild(int numberOfParallelProjectsToBuild, bool enbaleDebugComm)
210         {
211             // This test has previously been failing silently. With the addition of TestEnvironment the
212             // failure is now noticed (worker node is crashing with "Pipe is broken" exception. See #2057:
213             // https://github.com/Microsoft/msbuild/issues/2057
214             _env.ClearTestInvariants();
215 
216             // Communications debug log enabled, picked up by TestEnvironment
217             if (enbaleDebugComm) _env.SetEnvironmentVariable("MSBUILDDEBUGCOMM", "1");
218 
219             ProjectCollection projectCollection = new ProjectCollection();
220 
221             // Get number of MSBuild processes currently instantiated
222             int numberProcsOriginally = (new List<Process>(Process.GetProcessesByName("MSBuild"))).Count;
223             _output.WriteLine($"numberProcsOriginally = {numberProcsOriginally}");
224 
225             // Generate a theoretically unique directory to put our dummy projects in.
226             string shutdownProjectDirectory = Path.Combine(Path.GetTempPath(), String.Format(CultureInfo.InvariantCulture, "VSNodeShutdown_{0}_UnitTest", Process.GetCurrentProcess().Id));
227 
228             // Create the dummy projects we'll be "building" as our excuse to connect to and shut down
229             // all the nodes.
230             ProjectInstance rootProject = GenerateDummyProjects(shutdownProjectDirectory, numberOfParallelProjectsToBuild, projectCollection);
231 
232             // Build the projects.
233             var buildParameters = new BuildParameters(projectCollection)
234             {
235                 OnlyLogCriticalEvents = true,
236                 MaxNodeCount = numberOfParallelProjectsToBuild,
237                 EnableNodeReuse = true,
238                 DisableInProcNode = true,
239                 SaveOperatingEnvironment = false,
240                 Loggers = new List<ILogger> {new MockLogger(_output)}
241             };
242 
243             // Tell the build manager to not disturb process wide state
244 
245             BuildRequestData requestData = new BuildRequestData(rootProject, new[] { "Build" }, null);
246 
247             // Use a separate BuildManager for the node shutdown build, so that we don't have
248             // to worry about taking dependencies on whether or not the existing ones have already
249             // disappeared.
250             BuildManager shutdownManager = new BuildManager("IdleNodeShutdown");
251             shutdownManager.Build(buildParameters, requestData);
252 
253             // Number of nodes after the build has to be greater than the original number
254             int numberProcsAfterBuild = (new List<Process>(Process.GetProcessesByName("MSBuild"))).Count;
255             _output.WriteLine($"numberProcsAfterBuild = {numberProcsAfterBuild}");
256             Assert.True(numberProcsOriginally < numberProcsAfterBuild, $"Expected '{numberProcsOriginally}' < '{numberProcsAfterBuild}'");
257 
258             // Shutdown all nodes
259             shutdownManager.ShutdownAllNodes();
260 
261             // Wait until all processes shut down
262             Thread.Sleep(3000);
263 
264             // Number of nodes after the shutdown has to be smaller or equal the original number
265             int numberProcsAfterShutdown = (new List<Process>(Process.GetProcessesByName("MSBuild"))).Count;
266             _output.WriteLine($"numberProcsAfterShutdown = {numberProcsAfterShutdown}");
267             Assert.True(numberProcsAfterShutdown <= numberProcsOriginally);
268 
269             // Delete directory with the dummy project
270             if (Directory.Exists(shutdownProjectDirectory))
271             {
272                 FileUtilities.DeleteWithoutTrailingBackslash(shutdownProjectDirectory, true /* recursive delete */);
273             }
274         }
275 
276         /// <summary>
277         /// A simple successful build, out of process only.
278         /// </summary>
279 #if MONO
280         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
281 #else
282         [Fact]
283 #endif
SimpleBuildOutOfProcess()284         public void SimpleBuildOutOfProcess()
285         {
286             RunOutOfProcBuild(_ => _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1"));
287         }
288 
289         /// <summary>
290         /// A simple successful build, out of process only. Triggered by setting build parameters' DisableInProcNode to true.
291         /// </summary>
292 #if MONO
293         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
294 #else
295         [Fact]
296 #endif
DisableInProcNode()297         public void DisableInProcNode()
298         {
299             RunOutOfProcBuild(buildParameters => buildParameters.DisableInProcNode = true);
300         }
301 
302         /// <summary>
303         /// Runs a build and verifies it happens out of proc by checking the process ID.
304         /// </summary>
305         /// <param name="buildParametersModifier">Runs a test out of proc.</param>
RunOutOfProcBuild(Action<BuildParameters> buildParametersModifier)306         public void RunOutOfProcBuild(Action<BuildParameters> buildParametersModifier)
307         {
308             const string Contents = @"
309 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
310 <ItemGroup>
311        <InitialProperty Include='InitialProperty2'/>
312        <InitialProperty Include='InitialProperty3'/>
313 </ItemGroup>
314  <Target Name='test' Returns='@(InitialProperty)'>
315     <ItemGroup>
316        <InitialProperty Include='$([System.Diagnostics.Process]::GetCurrentProcess().Id)'/>
317     </ItemGroup>
318 	<Message Text='[success]'/>
319  </Target>
320 </Project>
321 ";
322 
323             // Need to set this env variable to enable Process.GetCurrentProcess().Id in the project file.
324             _env.SetEnvironmentVariable("MSBUILDENABLEALLPROPERTYFUNCTIONS", "1");
325 
326             Project project = CreateProject(CleanupFileContents(Contents), MSBuildDefaultToolsVersion, _projectCollection, false);
327 
328             BuildRequestData data = new BuildRequestData(project.CreateProjectInstance(), new string[0], _projectCollection.HostServices);
329             BuildParameters customparameters = new BuildParameters { EnableNodeReuse = false, Loggers = new ILogger[] { _logger } };
330             buildParametersModifier(customparameters);
331 
332             BuildResult result = _buildManager.Build(customparameters, data);
333             TargetResult targetresult = result.ResultsByTarget["test"];
334             ITaskItem[] item = targetresult.Items;
335 
336             Assert.Equal(BuildResultCode.Success, result.OverallResult);
337             Assert.Equal(3, item.Length);
338             int processId;
339             Assert.True(int.TryParse(item[2].ItemSpec, out processId), $"Process ID passed from the 'test' target is not a valid integer (actual is '{item[2].ItemSpec}')");
340             Assert.NotEqual(Process.GetCurrentProcess().Id, processId); // "Build is expected to be out-of-proc. In fact it was in-proc."
341         }
342 
343 #if MONO
344         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
345 #else
346         [Fact]
347 #endif
RequestedResultsAreSatisfied()348         public void RequestedResultsAreSatisfied()
349         {
350             const string contents = @"
351 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
352 <PropertyGroup>
353   <UnrequestedProperty>IsUnrequested</UnrequestedProperty>
354   <RequestedProperty>IsRequested</RequestedProperty>
355   <UpdatedProperty>Stale</UpdatedProperty>
356 </PropertyGroup>
357 <ItemGroup>
358   <AnItem Include='Item1' UnexpectedMetadatum='Unexpected' />
359   <AnItem Include='Item2'/>
360 </ItemGroup>
361 <Target Name='test' Returns='@(ItemWithMetadata)'>
362   <ItemGroup>
363     <AnItem Include='$([System.Diagnostics.Process]::GetCurrentProcess().Id)' />
364     <ItemWithMetadata Metadatum1='m1' Metadatum2='m2' Include='ItemFromTarget' />
365   </ItemGroup>
366   <PropertyGroup>
367     <NewProperty>FunValue</NewProperty>
368     <UpdatedProperty>Updated</UpdatedProperty>
369   </PropertyGroup>
370   <Message Text='[success]'/>
371 </Target>
372 
373 <Target Name='other' Returns='@(ItemWithMetadata)' DependsOnTargets='test' />
374 
375 </Project>
376 ";
377 
378             // Need to set this env variable to enable Process.GetCurrentProcess().Id in the project file.
379             _env.SetEnvironmentVariable("MSBUILDENABLEALLPROPERTYFUNCTIONS", "1");
380 
381             Project project = CreateProject(CleanupFileContents(contents), MSBuildDefaultToolsVersion,
382                 _projectCollection, false);
383 
384             var requestedProjectState = new RequestedProjectState
385             {
386                 ItemFilters = new Dictionary<string, List<string>>
387                 {
388                     {"AnItem", null},
389                     {"ItemWithMetadata", new List<string> {"Metadatum1"}},
390                 },
391                 PropertyFilters = new List<string> {"NewProperty", "RequestedProperty"},
392             };
393 
394             BuildRequestData data = new BuildRequestData(project.CreateProjectInstance(), new [] {"test", "other"},
395                 _projectCollection.HostServices, BuildRequestDataFlags.ProvideSubsetOfStateAfterBuild, null,
396                 requestedProjectState);
397             BuildParameters customparameters = new BuildParameters
398             {
399                 EnableNodeReuse = false,
400                 Loggers = new ILogger[] {_logger},
401                 DisableInProcNode = true,
402             };
403 
404             BuildResult result = _buildManager.Build(customparameters, data);
405 
406             result.OverallResult.ShouldBe(BuildResultCode.Success);
407 
408             result.ProjectStateAfterBuild.ShouldNotBeNull();
409 
410             result.ProjectStateAfterBuild.Properties.ShouldNotContain(p => p.Name == "UnrequestedProperty");
411 
412             result.ProjectStateAfterBuild.Properties.ShouldContain(p => p.Name == "NewProperty");
413             result.ProjectStateAfterBuild.GetPropertyValue("NewProperty").ShouldBe("FunValue");
414 
415             result.ProjectStateAfterBuild.Properties.ShouldContain(p => p.Name == "RequestedProperty");
416             result.ProjectStateAfterBuild.GetPropertyValue("RequestedProperty").ShouldBe("IsRequested");
417 
418             result.ProjectStateAfterBuild.Items.Count.ShouldBe(4);
419 
420             result.ProjectStateAfterBuild.GetItems("ItemWithMetadata").ShouldHaveSingleItem();
421             result.ProjectStateAfterBuild.GetItems("ItemWithMetadata").First().DirectMetadataCount.ShouldBe(1);
422             result.ProjectStateAfterBuild.GetItems("ItemWithMetadata").First().GetMetadataValue("Metadatum1")
423                 .ShouldBe("m1");
424             result.ProjectStateAfterBuild.GetItems("ItemWithMetadata").First().GetMetadataValue("Metadatum2")
425                 .ShouldBeNullOrEmpty();
426 
427             result.ProjectStateAfterBuild.GetItems("AnItem").Count.ShouldBe(3);
428             result.ProjectStateAfterBuild.GetItems("AnItem").ShouldContain(p => p.EvaluatedInclude == "Item2");
429 
430             result.ProjectStateAfterBuild.GetItemsByItemTypeAndEvaluatedInclude("AnItem", "Item1")
431                 .ShouldHaveSingleItem();
432             result.ProjectStateAfterBuild.GetItemsByItemTypeAndEvaluatedInclude("AnItem", "Item1").First()
433                 .GetMetadataValue("UnexpectedMetadatum").ShouldBe("Unexpected");
434         }
435 
436         /// <summary>
437         /// Make sure when we are doing an in-process build that even if the environment variable MSBUILDFORWARDPROPERTIESFROMCHILD is set that we still
438         /// get all of the initial properties.
439         /// </summary>
440         [Fact]
InProcForwardPropertiesFromChild()441         public void InProcForwardPropertiesFromChild()
442         {
443             string contents = CleanupFileContents(@"
444 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
445  <PropertyGroup>
446        <InitialProperty1>InitialProperty1</InitialProperty1>
447        <InitialProperty2>InitialProperty2</InitialProperty2>
448        <InitialProperty3>InitialProperty3</InitialProperty3>
449 </PropertyGroup>
450 <Target Name='test'>
451 	<Message Text='[success]'/>
452  </Target>
453 </Project>
454 ");
455 
456             _env.SetEnvironmentVariable("MSBuildForwardPropertiesFromChild", "InitialProperty2;IAMNOTREAL");
457             BuildRequestData data = GetBuildRequestData(contents);
458             BuildResult result = _buildManager.Build(_parameters, data);
459             Assert.Equal(BuildResultCode.Success, result.OverallResult);
460             _logger.AssertLogContains("[success]");
461             Assert.Equal(1, _logger.ProjectStartedEvents.Count);
462 
463             ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0];
464             Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
465 
466             string propertyValue;
467             Assert.True(properties.TryGetValue("InitialProperty1", out propertyValue));
468             Assert.True(String.Equals(propertyValue, "InitialProperty1", StringComparison.OrdinalIgnoreCase));
469 
470             Assert.True(properties.TryGetValue("InitialProperty2", out propertyValue));
471             Assert.True(String.Equals(propertyValue, "InitialProperty2", StringComparison.OrdinalIgnoreCase));
472 
473             Assert.True(properties.TryGetValue("InitialProperty3", out propertyValue));
474             Assert.True(String.Equals(propertyValue, "InitialProperty3", StringComparison.OrdinalIgnoreCase));
475         }
476 
477         /// <summary>
478         /// Make sure when we are doing an in-process build that even if the environment variable MsBuildForwardAllPropertiesFromChild is set that we still
479         /// get all of the initial properties.
480         /// </summary>
481         [Fact]
InProcMsBuildForwardAllPropertiesFromChild()482         public void InProcMsBuildForwardAllPropertiesFromChild()
483         {
484             string contents = CleanupFileContents(@"
485 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
486  <PropertyGroup>
487        <InitialProperty1>InitialProperty1</InitialProperty1>
488        <InitialProperty2>InitialProperty2</InitialProperty2>
489        <InitialProperty3>InitialProperty3</InitialProperty3>
490 </PropertyGroup>
491 <Target Name='test'>
492 	<Message Text='[success]'/>
493  </Target>
494 </Project>
495 ");
496             _env.SetEnvironmentVariable("MsBuildForwardAllPropertiesFromChild", "InitialProperty2;IAMNOTREAL");
497 
498             BuildRequestData data = GetBuildRequestData(contents);
499             BuildResult result = _buildManager.Build(_parameters, data);
500             Assert.Equal(BuildResultCode.Success, result.OverallResult);
501             _logger.AssertLogContains("[success]");
502             Assert.Equal(1, _logger.ProjectStartedEvents.Count);
503 
504             ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0];
505             Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
506 
507             string propertyValue = null;
508             Assert.True(properties.TryGetValue("InitialProperty1", out propertyValue));
509             Assert.True(String.Equals(propertyValue, "InitialProperty1", StringComparison.OrdinalIgnoreCase));
510 
511             Assert.True(properties.TryGetValue("InitialProperty2", out propertyValue));
512             Assert.True(String.Equals(propertyValue, "InitialProperty2", StringComparison.OrdinalIgnoreCase));
513 
514             Assert.True(properties.TryGetValue("InitialProperty3", out propertyValue));
515             Assert.True(String.Equals(propertyValue, "InitialProperty3", StringComparison.OrdinalIgnoreCase));
516         }
517 
518         /// <summary>
519         /// Make sure when we launch a child node and set MsBuildForwardAllPropertiesFromChild that we get all of our properties. This needs to happen
520         /// even if the msbuildforwardpropertiesfromchild is set to something.
521         /// </summary>
522         [Fact]
MsBuildForwardAllPropertiesFromChildLaunchChildNode()523         public void MsBuildForwardAllPropertiesFromChildLaunchChildNode()
524         {
525             string contents = CleanupFileContents(@"
526 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
527  <PropertyGroup>
528        <InitialProperty1>InitialProperty1</InitialProperty1>
529        <InitialProperty2>InitialProperty2</InitialProperty2>
530        <InitialProperty3>InitialProperty3</InitialProperty3>
531 </PropertyGroup>
532 <Target Name='test'>
533 	<Message Text='[success]'/>
534  </Target>
535 </Project>
536 ");
537 
538             _env.SetEnvironmentVariable("MsBuildForwardAllPropertiesFromChild", "InitialProperty2;IAMNOTREAL");
539             _env.SetEnvironmentVariable("MsBuildForwardPropertiesFromChild", "Something");
540 
541             var project = CreateProject(contents, null, _projectCollection, false);
542             var data = new BuildRequestData(project.FullPath, new Dictionary<string, string>(), MSBuildDefaultToolsVersion, new string[] { }, null);
543 
544             BuildResult result = _buildManager.Build(_parameters, data);
545             Assert.Equal(BuildResultCode.Success, result.OverallResult);
546             _logger.AssertLogContains("[success]");
547             Assert.Equal(1, _logger.ProjectStartedEvents.Count);
548 
549             ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0];
550             Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
551 
552             string propertyValue;
553             Assert.True(properties.TryGetValue("InitialProperty1", out propertyValue));
554             Assert.True(String.Equals(propertyValue, "InitialProperty1", StringComparison.OrdinalIgnoreCase));
555 
556             Assert.True(properties.TryGetValue("InitialProperty2", out propertyValue));
557             Assert.True(String.Equals(propertyValue, "InitialProperty2", StringComparison.OrdinalIgnoreCase));
558 
559             Assert.True(properties.TryGetValue("InitialProperty3", out propertyValue));
560             Assert.True(String.Equals(propertyValue, "InitialProperty3", StringComparison.OrdinalIgnoreCase));
561         }
562 
563         /// <summary>
564         /// Make sure when if the environment variable MsBuildForwardPropertiesFromChild is set to a value and
565         /// we launch a child node that we get only that value.
566         /// </summary>
567 #if RUNTIME_TYPE_NETCORE
568         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1976")]
569 #elif MONO
570         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
571 #else
572         [Fact]
573 #endif
OutOfProcNodeForwardCertainproperties()574         public void OutOfProcNodeForwardCertainproperties()
575         {
576             string contents = CleanupFileContents(@"
577 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
578  <PropertyGroup>
579        <InitialProperty1>InitialProperty1</InitialProperty1>
580        <InitialProperty2>InitialProperty2</InitialProperty2>
581        <InitialProperty3>InitialProperty3</InitialProperty3>
582 </PropertyGroup>
583 <Target Name='test'>
584 	<Message Text='[success]'/>
585  </Target>
586 </Project>
587 ");
588 
589             _env.SetEnvironmentVariable("MsBuildForwardPropertiesFromChild", "InitialProperty3;IAMNOTREAL");
590             _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1");
591 
592             var project = CreateProject(contents, null, _projectCollection, false);
593             var data = new BuildRequestData(project.FullPath, new Dictionary<string, string>(),
594                 MSBuildDefaultToolsVersion, new string[] { }, null);
595 
596             BuildResult result = _buildManager.Build(_parameters, data);
597             Assert.Equal(BuildResultCode.Success, result.OverallResult);
598             _logger.AssertLogContains("[success]");
599             Assert.Equal(1, _logger.ProjectStartedEvents.Count);
600 
601             ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0];
602             Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
603 
604             Assert.Equal(1, properties.Count);
605 
606             string propertyValue;
607             Assert.True(properties.TryGetValue("InitialProperty3", out propertyValue));
608             Assert.True(String.Equals(propertyValue, "InitialProperty3", StringComparison.OrdinalIgnoreCase));
609         }
610 
611         /// <summary>
612         /// Make sure when if the environment variable MsBuildForwardPropertiesFromChild is set to a value and
613         /// we launch a child node that we get only that value. Also, make sure that when a project is pulled from the results cache
614         /// and we have a list of properties to serialize that we do not crash. This is to prevent a regression of 826594
615         /// </summary>
616 #if RUNTIME_TYPE_NETCORE
617         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1976")]
618 #elif MONO
619         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
620 #else
621         [Fact]
622 #endif
OutOfProcNodeForwardCertainpropertiesAlsoGetResultsFromCache()623         public void OutOfProcNodeForwardCertainpropertiesAlsoGetResultsFromCache()
624         {
625             string tempProject = _env.CreateFile(".proj").Path;
626 
627             string contents = CleanupFileContents($@"
628 <Project ToolsVersion='msbuilddefaulttoolsversion' DefaultTargets='Build' xmlns='msbuildnamespace'>
629   <Target Name='Build'>
630        <MsBuild Projects='{tempProject}' Targets='BuildA'/>
631        <MsBuild Projects='{tempProject}' Targets='BuildA'/>
632   </Target>
633 </Project>
634 ");
635 
636             string projectContents = CleanupFileContents(@"
637 <Project ToolsVersion='msbuilddefaulttoolsversion' DefaultTargets='Build' xmlns='msbuildnamespace'>
638  <PropertyGroup>
639        <InitialProperty1>InitialProperty1</InitialProperty1>
640        <InitialProperty2>InitialProperty2</InitialProperty2>
641        <InitialProperty3>InitialProperty3</InitialProperty3>
642 </PropertyGroup>
643 <Target Name='BuildA'>
644        <Message Text='BuildA' Importance='High'/>
645        <Message Text='[success]'/>
646   </Target>
647 </Project>
648 ");
649 
650             File.WriteAllText(tempProject, projectContents);
651 
652             _env.SetEnvironmentVariable("MsBuildForwardPropertiesFromChild", "InitialProperty3;IAMNOTREAL");
653             _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1");
654 
655             var project = CreateProject(contents, null, _projectCollection, false);
656             var data = new BuildRequestData(project.FullPath, new Dictionary<string, string>(),
657                 MSBuildDefaultToolsVersion, new string[] { }, null);
658 
659             BuildResult result = _buildManager.Build(_parameters, data);
660             Assert.Equal(BuildResultCode.Success, result.OverallResult);
661             _logger.AssertLogContains("[success]");
662             Assert.Equal(3, _logger.ProjectStartedEvents.Count);
663 
664             ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[1];
665 
666             // After conversion to xunit, this test sometimes fails at this assertion.
667             // Related to shared state that the test touches that's getting handled
668             // differently in xunit?
669             Assert.NotNull(projectStartedEvent.Properties);
670 
671             Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
672 
673             Assert.NotNull(properties);
674             Assert.Equal(1, properties.Count);
675 
676             string propertyValue;
677             Assert.True(properties.TryGetValue("InitialProperty3", out propertyValue));
678             Assert.True(String.Equals(propertyValue, "InitialProperty3", StringComparison.OrdinalIgnoreCase));
679 
680             projectStartedEvent = _logger.ProjectStartedEvents[2];
681             Assert.Null(projectStartedEvent.Properties);
682         }
683 
684         /// <summary>
685         /// Make sure when if the environment variable MsBuildForwardPropertiesFromChild is set to empty and
686         /// we launch a child node that we get no properties
687         /// </summary>
688 #if MONO
689         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
690 #else
691         [Fact]
692 #endif
ForwardNoPropertiesLaunchChildNode()693         public void ForwardNoPropertiesLaunchChildNode()
694         {
695             string contents = CleanupFileContents(@"
696 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
697  <PropertyGroup>
698        <InitialProperty1>InitialProperty1</InitialProperty1>
699        <InitialProperty2>InitialProperty2</InitialProperty2>
700        <InitialProperty3>InitialProperty3</InitialProperty3>
701 </PropertyGroup>
702 <Target Name='test'>
703 	<Message Text='[success]'/>
704  </Target>
705 </Project>
706 ");
707 
708             _env.SetEnvironmentVariable("MsBuildForwardPropertiesFromChild", "");
709             _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1");
710 
711             var project = CreateProject(contents, null, _projectCollection, false);
712             var data = new BuildRequestData(project.FullPath, new Dictionary<string, string>(),
713                 MSBuildDefaultToolsVersion, new string[] { }, null);
714             BuildResult result = _buildManager.Build(_parameters, data);
715             Assert.Equal(BuildResultCode.Success, result.OverallResult);
716 
717             _logger.AssertLogContains("[success]");
718             Assert.Equal(1, _logger.ProjectStartedEvents.Count);
719 
720             ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0];
721             Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
722             Assert.Null(properties);
723         }
724 
725         /// <summary>
726         /// We want to pass the toolsets from the parent to the child nodes so that any custom toolsets
727         /// defined on the parent are also available on the child nodes for tasks which use the global project
728         /// collection
729         /// </summary>
730 #if RUNTIME_TYPE_NETCORE
731         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/933")]
732 #elif MONO
733         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
734 #else
735         [Fact]
736 #endif
VerifyCustomToolSetsPropagated()737         public void VerifyCustomToolSetsPropagated()
738         {
739             string netFrameworkDirectory = ToolLocationHelper.GetPathToDotNetFrameworkReferenceAssemblies(TargetDotNetFrameworkVersion.Version45);
740 
741             string contents = CleanupFileContents(@"
742 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
743 <UsingTask TaskName='VerifyGlobalProjectCollection' TaskFactory='CodeTaskFactory' AssemblyFile='$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll'>
744                         <Task>
745                             <Using Namespace='Microsoft.Build.Evaluation'/>
746                                <Reference Include='$(MSBuildToolsPath)\Microsoft.Build.dll'/>
747 <Code Type='Method'>
748  <![CDATA[
749 
750                                 public override bool Execute()
751                                 {
752                                     bool foundToolSet = false;
753                                     foreach(Toolset t in ProjectCollection.GlobalProjectCollection.Toolsets)
754                                     {
755                                         if(t.ToolsVersion.Equals(""CustomToolSet"", StringComparison.OrdinalIgnoreCase))
756                                         {
757                                             foundToolSet = true;
758                                             break;
759                                         }
760                                      }
761 
762                                     Log.LogMessage(MessageImportance.High, ""foundToolset:"" + foundToolSet.ToString());
763                                     return foundToolSet;
764                                 }
765   ]]>
766                             </Code>
767                          </Task>
768                          </UsingTask>
769                         <Target Name='Build'>
770                             <VerifyGlobalProjectCollection/>
771                         </Target>
772                     </Project>");
773 
774             _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1");
775 
776             ProjectCollection projectCollection = new ProjectCollection();
777             Toolset newToolSet = new Toolset("CustomToolSet", "c:\\SomePath", projectCollection, null);
778             projectCollection.AddToolset(newToolSet);
779 
780             var project = CreateProject(contents, null, projectCollection, false);
781             var data = new BuildRequestData(project.FullPath, new Dictionary<string, string>(),
782                 MSBuildDefaultToolsVersion, new string[] { }, null);
783 
784             BuildParameters customParameters = new BuildParameters(projectCollection);
785             customParameters.Loggers = new ILogger[] { _logger };
786             BuildResult result = _buildManager.Build(customParameters, data);
787             Assert.Equal(BuildResultCode.Success, result.OverallResult);
788         }
789 
790         /// <summary>
791         /// When a child node is launched by default we should not send any properties.
792         /// we launch a child node that we get no properties
793         /// </summary>
794 #if MONO
795         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
796 #else
797         [Fact]
798 #endif
ForwardNoPropertiesLaunchChildNodeDefault()799         public void ForwardNoPropertiesLaunchChildNodeDefault()
800         {
801             string contents = CleanupFileContents(@"
802 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
803  <PropertyGroup>
804        <InitialProperty1>InitialProperty1</InitialProperty1>
805        <InitialProperty2>InitialProperty2</InitialProperty2>
806        <InitialProperty3>InitialProperty3</InitialProperty3>
807 </PropertyGroup>
808 <Target Name='test'>
809 	<Message Text='[success]'/>
810  </Target>
811 </Project>
812 ");            _env.SetEnvironmentVariable("MsBuildForwardPropertiesFromChild", null);
813             _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1");
814 
815             var project = CreateProject(contents, null, _projectCollection, false);
816             var data = new BuildRequestData(project.FullPath, new Dictionary<string, string>(),
817                 MSBuildDefaultToolsVersion, new string[] { }, null);
818             BuildResult result = _buildManager.Build(_parameters, data);
819             Assert.Equal(BuildResultCode.Success, result.OverallResult);
820             _logger.AssertLogContains("[success]");
821             Assert.Equal(1, _logger.ProjectStartedEvents.Count);
822 
823             ProjectStartedEventArgs projectStartedEvent = _logger.ProjectStartedEvents[0];
824             Dictionary<string, string> properties = ExtractProjectStartedPropertyList(projectStartedEvent.Properties);
825             Assert.Null(properties);
826         }
827 
828         /// <summary>
829         /// A simple failing build.
830         /// </summary>
831         [Fact]
SimpleBuildWithFailure()832         public void SimpleBuildWithFailure()
833         {
834             string contents = CleanupFileContents(@"
835 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
836  <Target Name='test'>
837 	<Error Text='[fail]'/>
838  </Target>
839 </Project>
840 ");
841             BuildRequestData data = GetBuildRequestData(contents);
842             BuildResult result = _buildManager.Build(_parameters, data);
843             Assert.Equal(BuildResultCode.Failure, result.OverallResult);
844             _logger.AssertLogContains("[fail]");
845         }
846 
847         /// <summary>
848         /// A build with a message, error and warning, verify that
849         /// we only get errors, warnings, and project started and finished when OnlyLogCriticalEvents is true
850         /// </summary>
851         [Fact]
SimpleBuildWithFailureAndWarningOnlyLogCriticalEventsTrue()852         public void SimpleBuildWithFailureAndWarningOnlyLogCriticalEventsTrue()
853         {
854             string contents = CleanupFileContents(@"
855               <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
856                  <Target Name='test'>
857                      <Message Text='[Message]' Importance='high'/>
858                      <Warning Text='[warn]'/>
859                      <Error Text='[fail]'/>
860                 </Target>
861               </Project>
862             ");
863 
864             BuildRequestData data = GetBuildRequestData(contents);
865             _parameters.OnlyLogCriticalEvents = true;
866             BuildResult result = _buildManager.Build(_parameters, data);
867             Assert.Equal(BuildResultCode.Failure, result.OverallResult);
868             _logger.AssertLogContains("[fail]");
869             _logger.AssertLogContains("[warn]");
870             _logger.AssertLogDoesntContain("[message]");
871             Assert.Equal(1, _logger.BuildStartedEvents.Count);
872             Assert.Equal(1, _logger.BuildFinishedEvents.Count);
873             Assert.Equal(1, _logger.ProjectStartedEvents.Count);
874             Assert.Equal(1, _logger.ProjectFinishedEvents.Count);
875             Assert.Equal(0, _logger.TargetStartedEvents.Count);
876             Assert.Equal(0, _logger.TargetFinishedEvents.Count);
877             Assert.Equal(0, _logger.TaskStartedEvents.Count);
878             Assert.Equal(0, _logger.TaskFinishedEvents.Count);
879         }
880 
881         /// <summary>
882         /// A build with a message, error and warning, verify that
883         /// we only get errors, warnings, messages, task and target messages OnlyLogCriticalEvents is false
884         /// </summary>
885         [Fact]
SimpleBuildWithFailureAndWarningOnlyLogCriticalEventsFalse()886         public void SimpleBuildWithFailureAndWarningOnlyLogCriticalEventsFalse()
887         {
888             string contents = CleanupFileContents(@"
889               <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
890                  <Target Name='test'>
891                      <Message Text='[message]' Importance='high'/>
892                      <Warning Text='[warn]'/>
893                      <Error Text='[fail]'/>
894                 </Target>
895               </Project>
896             ");
897 
898             BuildRequestData data = GetBuildRequestData(contents);
899             _parameters.OnlyLogCriticalEvents = false;
900             BuildResult result = _buildManager.Build(_parameters, data);
901             Assert.Equal(BuildResultCode.Failure, result.OverallResult);
902             _logger.AssertLogContains("[fail]");
903             _logger.AssertLogContains("[warn]");
904             _logger.AssertLogContains("[message]");
905             Assert.Equal(1, _logger.BuildStartedEvents.Count);
906             Assert.Equal(1, _logger.BuildFinishedEvents.Count);
907             Assert.Equal(1, _logger.ProjectStartedEvents.Count);
908             Assert.Equal(1, _logger.ProjectFinishedEvents.Count);
909             Assert.Equal(1, _logger.TargetStartedEvents.Count);
910             Assert.Equal(1, _logger.TargetFinishedEvents.Count);
911             Assert.Equal(3, _logger.TaskStartedEvents.Count);
912             Assert.Equal(3, _logger.TaskFinishedEvents.Count);
913         }
914 
915         /// <summary>
916         /// Submitting a synchronous build request before calling BeginBuild yields an InvalidOperationException.
917         /// </summary>
918         [Fact]
BuildRequestWithoutBegin()919         public void BuildRequestWithoutBegin()
920         {
921             Assert.Throws<InvalidOperationException>(() =>
922                 {
923                     BuildRequestData data = new BuildRequestData("foo", new Dictionary<string, string>(), "2.0", new string[0], null);
924                     _buildManager.BuildRequest(data);
925                 }
926            );
927         }
928         /// <summary>
929         /// Pending a build request before calling BeginBuild yields an InvalidOperationException.
930         /// </summary>
931         [Fact]
PendBuildRequestWithoutBegin()932         public void PendBuildRequestWithoutBegin()
933         {
934             Assert.Throws<InvalidOperationException>(() =>
935                 {
936                     BuildRequestData data = new BuildRequestData("foo", new Dictionary<string, string>(), "2.0", new string[0], null);
937                     _buildManager.PendBuildRequest(data);
938                 }
939            );
940         }
941 
942         /// <summary>
943         /// Calling EndBuild before BeginBuild yields an InvalidOperationException.
944         /// </summary>
945         [Fact]
EndWithoutBegin()946         public void EndWithoutBegin()
947         {
948             Assert.Throws<InvalidOperationException>(() =>
949                 {
950                     _buildManager.EndBuild();
951                 }
952             );
953         }
954 
955         [Fact]
DisposeAfterUse()956         public void DisposeAfterUse()
957         {
958             string contents = CleanupFileContents(@"
959 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
960 </Project>
961 ");
962             var project = CreateProject(contents, null, _projectCollection, false);
963             var globalProperties = new Dictionary<string, string>();
964             var targets = new string[0];
965             var brd = new BuildRequestData(project.FullPath, globalProperties, null, targets, new HostServices());
966             using (var bm = new BuildManager())
967             {
968                 bm.Build(new BuildParameters(), brd);
969             }
970         }
971 
972         [Fact]
DisposeWithoutUse()973         public void DisposeWithoutUse()
974         {
975             var bm = new BuildManager();
976             bm.Dispose();
977         }
978 
979         /// <summary>
980         /// Calling BeginBuild after BeginBuild has already been called yields an InvalidOperationException.
981         /// </summary>
982         [Fact]
OverlappingBegin()983         public void OverlappingBegin()
984         {
985             try
986             {
987                 _buildManager.BeginBuild(new BuildParameters());
988                 Assert.Throws<InvalidOperationException>(() => _buildManager.BeginBuild(new BuildParameters()));
989             }
990             finally
991             {
992                 // Call EndBuild to get us back into a state that approximates reasonable
993                 _buildManager.EndBuild();
994             }
995         }
996 
997         /// <summary>
998         /// Starting and ending a build without submitting any requests is valid.
999         /// </summary>
1000         [Fact]
EmptyBuild()1001         public void EmptyBuild()
1002         {
1003             _buildManager.BeginBuild(_parameters);
1004             _buildManager.EndBuild();
1005 
1006             Assert.Equal(0, _logger.ErrorCount);
1007             Assert.Equal(0, _logger.WarningCount);
1008         }
1009 
1010         /// <summary>
1011         /// Calling EndBuild after it has already been called yields an InvalidOperationException.
1012         /// </summary>
1013         [Fact]
ExtraEnds()1014         public void ExtraEnds()
1015         {
1016             Assert.Throws<InvalidOperationException>(() =>
1017             {
1018                 _buildManager.BeginBuild(new BuildParameters());
1019                 _buildManager.EndBuild();
1020                 _buildManager.EndBuild();
1021             }
1022            );
1023         }
1024         /// <summary>
1025         /// Pending a request after EndBuild has been called yields an InvalidOperationException.
1026         /// </summary>
1027         [Fact]
PendBuildRequestAfterEnd()1028         public void PendBuildRequestAfterEnd()
1029         {
1030             Assert.Throws<InvalidOperationException>(() =>
1031             {
1032                 BuildRequestData data = new BuildRequestData("foo", new Dictionary<string, string>(), "2.0", new string[0], null);
1033                 _buildManager.BeginBuild(new BuildParameters());
1034                 _buildManager.EndBuild();
1035 
1036                 _buildManager.PendBuildRequest(data);
1037             }
1038            );
1039         }
1040 
1041         /// <summary>
1042         /// Attempting a synchronous build when a build is in progress yields an InvalidOperationException.
1043         /// </summary>
1044         [Fact]
BuildDuringBuild()1045         public void BuildDuringBuild()
1046         {
1047             try
1048             {
1049                 BuildRequestData data =
1050                     new BuildRequestData("foo", new Dictionary<string, string>(), "2.0", new string[0], null);
1051                 _buildManager.BeginBuild(new BuildParameters());
1052 
1053                 Assert.Throws<InvalidOperationException>(() => { _buildManager.Build(new BuildParameters(), data); });
1054             }
1055             finally
1056             {
1057                 // Call EndBuild to get us back into a state that approximates reasonable
1058                 _buildManager.EndBuild();
1059             }
1060         }
1061 
1062         /// <summary>
1063         /// A sequential build.
1064         /// </summary>
1065         [Fact]
EndBuildBlocks()1066         public void EndBuildBlocks()
1067         {
1068             string contents = CleanupFileContents(@"
1069 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1070  <Target Name='test'>
1071     <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(1)) + @"'/>
1072 	<Message Text='[success 1]'/>
1073  </Target>
1074 </Project>
1075 ");
1076 
1077             BuildRequestData data = GetBuildRequestData(contents);
1078             _buildManager.BeginBuild(_parameters);
1079             BuildSubmission submission1 = _buildManager.PendBuildRequest(data);
1080             submission1.ExecuteAsync(null, null);
1081             Assert.False(submission1.IsCompleted);
1082             _buildManager.EndBuild();
1083             Assert.True(submission1.IsCompleted);
1084             _logger.AssertLogContains("[success 1]");
1085         }
1086 
1087         /// <summary>
1088         /// Validate that EndBuild can be called during a submission completion callback.
1089         /// </summary>
1090         [Fact]
EndBuildCalledWithinSubmissionCallback()1091         public void EndBuildCalledWithinSubmissionCallback()
1092         {
1093             string contents = CleanupFileContents(@"
1094 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1095  <Target Name='test'>
1096 	<Message Text='[success 1]'/>
1097  </Target>
1098 </Project>
1099 ");
1100 
1101             BuildRequestData data = GetBuildRequestData(contents);
1102             _buildManager.BeginBuild(_parameters);
1103             BuildSubmission submission1 = _buildManager.PendBuildRequest(data);
1104             AutoResetEvent callbackFinished = new AutoResetEvent(false);
1105             submission1.ExecuteAsync(submission =>
1106             {
1107                 _buildManager.EndBuild();
1108                 callbackFinished.Set();
1109             }, null);
1110 
1111             // Wait for the build to finish
1112             Assert.True(callbackFinished.WaitOne(5000)); // "Build is hung."
1113 
1114             // EndBuild should now have been called, so invoking it again should give us an invalid operation error.
1115             Assert.Throws<InvalidOperationException>(() => _buildManager.EndBuild());
1116         }
1117 
1118         /// <summary>
1119         /// A sequential build.
1120         /// </summary>
1121         [Fact]
SequentialBuild()1122         public void SequentialBuild()
1123         {
1124             string contents = CleanupFileContents(@"
1125 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1126  <Target Name='test'>
1127 	<Message Text='[success 1]'/>
1128  </Target>
1129 </Project>
1130 ");
1131 
1132             string contents2 = CleanupFileContents(@"
1133 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1134  <Target Name='test'>
1135 	<Message Text='[success 2]'/>
1136  </Target>
1137 </Project>
1138 ");
1139 
1140             BuildRequestData data = GetBuildRequestData(contents);
1141             BuildRequestData data2 = GetBuildRequestData(contents2);
1142             _buildManager.BeginBuild(_parameters);
1143             BuildResult result = _buildManager.BuildRequest(data);
1144             Assert.Equal(BuildResultCode.Success, result.OverallResult);
1145 
1146             BuildResult result2 = _buildManager.BuildRequest(data2);
1147             Assert.Equal(BuildResultCode.Success, result2.OverallResult);
1148             _buildManager.EndBuild();
1149 
1150             _logger.AssertLogContains("[success 1]");
1151             _logger.AssertLogContains("[success 2]");
1152         }
1153 
1154         /// <summary>
1155         /// A sequential build.
1156         /// </summary>
1157         [Fact]
OverlappingBuildSubmissions()1158         public void OverlappingBuildSubmissions()
1159         {
1160             string contents = CleanupFileContents(@"
1161 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1162  <Target Name='test'>
1163     <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(500)) + @"'/>
1164 	<Message Text='[success 1]'/>
1165  </Target>
1166 </Project>
1167 ");
1168 
1169             string contents2 = CleanupFileContents(@"
1170 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1171  <Target Name='test'>
1172 	<Message Text='[success 2]'/>
1173  </Target>
1174 </Project>
1175 ");
1176 
1177             BuildRequestData data = GetBuildRequestData(contents);
1178             BuildRequestData data2 = GetBuildRequestData(contents2);
1179             _buildManager.BeginBuild(_parameters);
1180             BuildSubmission submission1 = _buildManager.PendBuildRequest(data);
1181             submission1.ExecuteAsync(null, null);
1182             BuildResult result2 = _buildManager.BuildRequest(data2);
1183             submission1.WaitHandle.WaitOne();
1184             BuildResult result = submission1.BuildResult;
1185             _buildManager.EndBuild();
1186 
1187             Assert.Equal(BuildResultCode.Success, result2.OverallResult);
1188             Assert.Equal(BuildResultCode.Success, result.OverallResult);
1189 
1190             _logger.AssertLogContains("[success 1]");
1191             _logger.AssertLogContains("[success 2]");
1192         }
1193 
1194         /// <summary>
1195         /// If two overlapping submissions are executed against the same project, with at least one
1196         /// target involved that skipped, make sure that the second one successfully completes
1197         /// (retrieved from the cache).
1198         /// </summary>
1199         [Fact]
OverlappingIdenticalBuildSubmissions()1200         public void OverlappingIdenticalBuildSubmissions()
1201         {
1202             string contents = CleanupFileContents(@"
1203 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1204  <Target Name='test' Condition='false' />
1205 </Project>
1206 ");
1207 
1208             BuildRequestData data = GetBuildRequestData(contents);
1209             BuildRequestData data2 = new BuildRequestData(data.ProjectInstance, data.TargetNames.ToArray(), data.HostServices);
1210 
1211             _buildManager.BeginBuild(_parameters);
1212             BuildSubmission submission1 = _buildManager.PendBuildRequest(data);
1213             BuildSubmission submission2 = _buildManager.PendBuildRequest(data2);
1214 
1215             submission2.ExecuteAsync(null, null);
1216             submission1.ExecuteAsync(null, null);
1217 
1218             submission1.WaitHandle.WaitOne();
1219             submission2.WaitHandle.WaitOne();
1220 
1221             _buildManager.EndBuild();
1222 
1223             Assert.Equal(BuildResultCode.Success, submission1.BuildResult.OverallResult);
1224             Assert.Equal(BuildResultCode.Success, submission2.BuildResult.OverallResult);
1225         }
1226 
1227         /// <summary>
1228         /// With two overlapping submissions, the first of which skips a target and the second
1229         /// of which should not, ensure that the second submission does not, in fact, skip
1230         /// the target.  (E.g. despite the fact that the target results are in the cache already
1231         /// as 'skipped', ensure that we retry execution in case conditions have changed.)
1232         /// </summary>
1233         [Fact]
OverlappingBuildSubmissions_OnlyOneSucceeds()1234         public void OverlappingBuildSubmissions_OnlyOneSucceeds()
1235         {
1236             string contents = CleanupFileContents(@"
1237 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1238  <Target Name='A' DependsOnTargets='SetProp;MaySkip;UnsetProp' />
1239 
1240  <Target Name='SetProp'>
1241   <PropertyGroup>
1242    <ShouldSkip>true</ShouldSkip>
1243   </PropertyGroup>
1244  </Target>
1245 
1246  <Target Name='MaySkip' Condition='!$(ShouldSkip)'>
1247   <Error Text='[ERROR]' />
1248  </Target>
1249 
1250  <Target Name='UnsetProp'>
1251   <PropertyGroup>
1252    <ShouldSkip>false</ShouldSkip>
1253   </PropertyGroup>
1254  </Target>
1255 
1256 </Project>
1257 ");
1258 
1259             BuildRequestData data = GetBuildRequestData(contents, new[] { "A" });
1260             BuildRequestData data2 = new BuildRequestData(data.ProjectInstance, new[] { "MaySkip" }, data.HostServices);
1261 
1262             _buildManager.BeginBuild(_parameters);
1263             BuildSubmission submission1 = _buildManager.PendBuildRequest(data);
1264             BuildSubmission submission2 = _buildManager.PendBuildRequest(data2);
1265 
1266             submission1.ExecuteAsync(null, null);
1267             submission2.ExecuteAsync(null, null);
1268 
1269             submission1.WaitHandle.WaitOne();
1270             submission2.WaitHandle.WaitOne();
1271 
1272             _buildManager.EndBuild();
1273 
1274             Assert.Equal(BuildResultCode.Success, submission1.BuildResult.OverallResult);
1275             Assert.Equal(BuildResultCode.Failure, submission2.BuildResult.OverallResult);
1276         }
1277 
1278         /// <summary>
1279         /// Calling EndBuild with an unexecuted submission.
1280         /// </summary>
1281         [Fact]
EndWithUnexecutedSubmission()1282         public void EndWithUnexecutedSubmission()
1283         {
1284             string contents = CleanupFileContents(@"
1285 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1286  <Target Name='test'>
1287 	<Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(20)) + @"'/>
1288     <Message Text='[fail]'/>
1289  </Target>
1290 </Project>
1291 ");
1292             BuildRequestData data = GetBuildRequestData(contents, new string[] { }, MSBuildDefaultToolsVersion);
1293             _buildManager.BeginBuild(_parameters);
1294             _buildManager.PendBuildRequest(data);
1295             _buildManager.EndBuild();
1296         }
1297 
1298         /// <summary>
1299         /// A canceled build with a submission which is not executed yet.
1300         /// </summary>
1301         [Fact]
CancelledBuildWithUnexecutedSubmission()1302         public void CancelledBuildWithUnexecutedSubmission()
1303         {
1304             string contents = CleanupFileContents(@"
1305 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1306  <Target Name='test'>
1307 	<Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(20)) + @"'/>
1308     <Message Text='[fail]'/>
1309  </Target>
1310 </Project>
1311 ");
1312             BuildRequestData data = GetBuildRequestData(contents, new string[] { }, MSBuildDefaultToolsVersion);
1313             _buildManager.BeginBuild(_parameters);
1314             _buildManager.PendBuildRequest(data);
1315             _buildManager.CancelAllSubmissions();
1316             _buildManager.EndBuild();
1317         }
1318 
1319         /// <summary>
1320         /// A canceled build
1321         /// </summary>
1322         [Fact]
1323         [Trait("Category", "mono-osx-failing")]
CancelledBuild()1324         public void CancelledBuild()
1325         {
1326             string contents = CleanupFileContents(@"
1327 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1328  <Target Name='test'>
1329 	<Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(60)) + @"'/>
1330     <Message Text='[fail]'/>
1331  </Target>
1332 </Project>
1333 ");
1334             BuildRequestData data = GetBuildRequestData(contents, new string[] { }, MSBuildDefaultToolsVersion);
1335             _buildManager.BeginBuild(_parameters);
1336             BuildSubmission asyncResult = _buildManager.PendBuildRequest(data);
1337 
1338             asyncResult.ExecuteAsync(null, null);
1339             _buildManager.CancelAllSubmissions();
1340             asyncResult.WaitHandle.WaitOne();
1341             BuildResult result = asyncResult.BuildResult;
1342             _buildManager.EndBuild();
1343 
1344             Assert.Equal(BuildResultCode.Failure, result.OverallResult); // "Build should have failed."
1345             _logger.AssertLogDoesntContain("[fail]");
1346         }
1347 
1348         /// <summary>
1349         /// A canceled build which waits for the task to get started before canceling.  Because it is a 2.0 task, we should
1350         /// wait until the task finishes normally (cancellation not supported.)
1351         /// </summary>
1352         [Fact]
CancelledBuildWithDelay20()1353         public void CancelledBuildWithDelay20()
1354         {
1355             if (FrameworkLocationHelper.PathToDotNetFrameworkV20 == null) return;
1356 
1357             string contents = CleanupFileContents(@"
1358 <Project xmlns='msbuildnamespace' ToolsVersion='2.0'>
1359  <Target Name='test'>
1360 	<Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(5)) + @"'/>
1361     <Message Text='[fail]'/>
1362  </Target>
1363 </Project>
1364 ");
1365             BuildRequestData data = GetBuildRequestData(contents);
1366             _buildManager.BeginBuild(_parameters);
1367             BuildSubmission asyncResult = _buildManager.PendBuildRequest(data);
1368             asyncResult.ExecuteAsync(null, null);
1369 
1370             Thread.Sleep(500);
1371             _buildManager.CancelAllSubmissions();
1372             asyncResult.WaitHandle.WaitOne();
1373             BuildResult result = asyncResult.BuildResult;
1374             _buildManager.EndBuild();
1375 
1376             Assert.Equal(BuildResultCode.Failure, result.OverallResult); // "Build should have failed."
1377             _logger.AssertLogDoesntContain("[fail]");
1378         }
1379 
1380 #if FEATURE_TASKHOST
1381         /// <summary>
1382         /// A canceled build which waits for the task to get started before canceling.  Because it is a 2.0 task, we should
1383         /// wait until the task finishes normally (cancellation not supported.)
1384         /// </summary>
1385         [Fact]
1386         [Trait("Category", "mono-osx-failing")]
CancelledBuildInTaskHostWithDelay20()1387         public void CancelledBuildInTaskHostWithDelay20()
1388         {
1389             if (FrameworkLocationHelper.PathToDotNetFrameworkV20 == null) return;
1390 
1391             string contents = CleanupFileContents(@"
1392 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1393  <UsingTask TaskName='Microsoft.Build.Tasks.Exec' AssemblyName='Microsoft.Build.Tasks.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' TaskFactory='TaskHostFactory' />
1394  <Target Name='test'>
1395 	<Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(10)) + @"'/>
1396     <Message Text='[fail]'/>
1397  </Target>
1398 </Project>
1399 ");
1400             BuildRequestData data = GetBuildRequestData(contents, new string[] { }, MSBuildDefaultToolsVersion);
1401             _buildManager.BeginBuild(_parameters);
1402             BuildSubmission asyncResult = _buildManager.PendBuildRequest(data);
1403             asyncResult.ExecuteAsync(null, null);
1404 
1405             Thread.Sleep(500);
1406             _buildManager.CancelAllSubmissions();
1407             asyncResult.WaitHandle.WaitOne();
1408             BuildResult result = asyncResult.BuildResult;
1409             _buildManager.EndBuild();
1410 
1411             Assert.Equal(BuildResultCode.Failure, result.OverallResult); // "Build should have failed."
1412             _logger.AssertLogDoesntContain("[fail]");
1413 
1414             // Task host should not have exited prematurely
1415             _logger.AssertLogDoesntContain("MSB4217");
1416         }
1417 #endif
1418 
1419         /// <summary>
1420         /// A canceled build which waits for the task to get started before canceling.  Because it is a 12.. task, we should
1421         /// cancel the task and exit out after a short period wherein we wait for the task to exit cleanly.
1422         /// </summary>
1423         [Fact]
1424         [Trait("Category", "mono-osx-failing")]
CancelledBuildWithDelay40()1425         public void CancelledBuildWithDelay40()
1426         {
1427             string contents = CleanupFileContents(@"
1428 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1429  <Target Name='test'>
1430 	<Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(10)) + @"'/>
1431     <Message Text='[fail]'/>
1432  </Target>
1433 </Project>
1434 ");
1435             BuildRequestData data = GetBuildRequestData(contents, new string[] { }, MSBuildDefaultToolsVersion);
1436             _buildManager.BeginBuild(_parameters);
1437             BuildSubmission asyncResult = _buildManager.PendBuildRequest(data);
1438             asyncResult.ExecuteAsync(null, null);
1439 
1440             Thread.Sleep(500);
1441             _buildManager.CancelAllSubmissions();
1442             asyncResult.WaitHandle.WaitOne();
1443             BuildResult result = asyncResult.BuildResult;
1444             _buildManager.EndBuild();
1445 
1446             Assert.Equal(BuildResultCode.Failure, result.OverallResult); // "Build should have failed."
1447             _logger.AssertLogDoesntContain("[fail]");
1448         }
1449 
1450 #if FEATURE_TASKHOST
1451         /// <summary>
1452         /// A canceled build which waits for the task to get started before canceling.  Because it is a 12.0 task, we should
1453         /// cancel the task and exit out after a short period wherein we wait for the task to exit cleanly.
1454         /// </summary>
1455         [Fact]
1456         [Trait("Category", "mono-osx-failing")]
CancelledBuildInTaskHostWithDelay40()1457         public void CancelledBuildInTaskHostWithDelay40()
1458         {
1459             string contents = CleanupFileContents(@"
1460 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1461  <UsingTask TaskName='Microsoft.Build.Tasks.Exec' AssemblyName='Microsoft.Build.Tasks.Core, Version=msbuildassemblyversion, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' TaskFactory='TaskHostFactory' />
1462  <Target Name='test'>
1463 	<Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(10)) + @"'/>
1464     <Message Text='[fail]'/>
1465  </Target>
1466 </Project>
1467 ");
1468             BuildRequestData data = GetBuildRequestData(contents, new string[] { }, MSBuildDefaultToolsVersion);
1469             _buildManager.BeginBuild(_parameters);
1470             BuildSubmission asyncResult = _buildManager.PendBuildRequest(data);
1471             asyncResult.ExecuteAsync(null, null);
1472 
1473             Thread.Sleep(500);
1474             _buildManager.CancelAllSubmissions();
1475             asyncResult.WaitHandle.WaitOne();
1476             BuildResult result = asyncResult.BuildResult;
1477             _buildManager.EndBuild();
1478 
1479             Assert.Equal(BuildResultCode.Failure, result.OverallResult); // "Build should have failed."
1480             _logger.AssertLogDoesntContain("[fail]");
1481 
1482             // Task host should not have exited prematurely
1483             _logger.AssertLogDoesntContain("MSB4217");
1484         }
1485 #endif
1486 
1487         /// <summary>
1488         /// This test verifies that builds of the same project instance in sequence are permitted.
1489         /// </summary>
1490         [Fact]
SequentialBuildsOfTheSameProjectAllowed()1491         public void SequentialBuildsOfTheSameProjectAllowed()
1492         {
1493             string contents = CleanupFileContents(@"
1494 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1495  <Target Name='target1'>
1496     <Message Text='text'/>
1497  </Target>
1498  <Target Name='target2'>
1499     <Message Text='text'/>
1500  </Target>
1501 </Project>
1502 ");
1503             Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true);
1504             ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project);
1505             _buildManager.BeginBuild(_parameters);
1506             BuildResult result1 = _buildManager.BuildRequest(new BuildRequestData(instance, new[] {"target1"}));
1507             BuildResult result2 = _buildManager.BuildRequest(new BuildRequestData(instance, new[] {"target2"}));
1508             _buildManager.EndBuild();
1509 
1510             Assert.Equal(BuildResultCode.Success, result1.OverallResult);
1511             Assert.True(result1.HasResultsForTarget("target1")); // "Results for target1 missing"
1512             Assert.Equal(BuildResultCode.Success, result2.OverallResult);
1513             Assert.True(result2.HasResultsForTarget("target2")); // "Results for target2 missing"
1514         }
1515 
1516         /// <summary>
1517         /// This test verifies that overlapping builds of the same project are allowed.
1518         /// </summary>
1519         [Fact]
OverlappingBuildsOfTheSameProjectDifferentTargetsAreAllowed()1520         public void OverlappingBuildsOfTheSameProjectDifferentTargetsAreAllowed()
1521         {
1522             string contents = CleanupFileContents(@"
1523 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1524  <Target Name='target1'>
1525     <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(3)) + @"'/>
1526     <Message Text='text'/>
1527  </Target>
1528  <Target Name='target2'>
1529     <Message Text='text'/>
1530  </Target>
1531 </Project>
1532 ");
1533 
1534             Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true);
1535             ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project);
1536             _buildManager.BeginBuild(_parameters);
1537 
1538             BuildSubmission submission =_buildManager.PendBuildRequest(new BuildRequestData(instance, new[] {"target1"}));
1539             submission.ExecuteAsync(null, null);
1540             BuildResult result2 =_buildManager.BuildRequest(new BuildRequestData(project.CreateProjectInstance(), new[] {"target2"}));
1541 
1542             submission.WaitHandle.WaitOne();
1543             var result1 = submission.BuildResult;
1544 
1545             Assert.Equal(BuildResultCode.Success, result1.OverallResult);
1546             Assert.True(result1.HasResultsForTarget("target1")); // "Results for target1 missing"
1547             Assert.Equal(BuildResultCode.Success, result2.OverallResult);
1548             Assert.True(result2.HasResultsForTarget("target2")); // "Results for target2 missing"
1549             _buildManager.EndBuild();
1550         }
1551 
1552         /// <summary>
1553         /// This test verifies that overlapping builds of the same project are allowed.
1554         /// </summary>
1555         [Fact]
OverlappingBuildsOfTheSameProjectSameTargetsAreAllowed()1556         public void OverlappingBuildsOfTheSameProjectSameTargetsAreAllowed()
1557         {
1558             string contents = CleanupFileContents(@"
1559 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1560  <Target Name='target1'>
1561     <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromSeconds(3)) + @"'/>
1562     <Message Text='text'/>
1563  </Target>
1564  <Target Name='target2'>
1565     <Message Text='text'/>
1566  </Target>
1567 </Project>
1568 ");
1569             Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true);
1570             ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project);
1571             _buildManager.BeginBuild(_parameters);
1572 
1573             BuildSubmission submission = _buildManager.PendBuildRequest(new BuildRequestData(instance, new[] {"target1"}));
1574             submission.ExecuteAsync(null, null);
1575             BuildResult result2 = _buildManager.BuildRequest(new BuildRequestData(project.CreateProjectInstance(), new[] {"target1"}));
1576             submission.WaitHandle.WaitOne();
1577             var result1 = submission.BuildResult;
1578 
1579             Assert.Equal(BuildResultCode.Success, result1.OverallResult);
1580             Assert.True(result1.HasResultsForTarget("target1")); // "Results for target1 missing"
1581             Assert.Equal(BuildResultCode.Success, result2.OverallResult);
1582             Assert.True(result2.HasResultsForTarget("target1")); // "Results for target1 (second call) missing"
1583             _buildManager.EndBuild();
1584         }
1585 
1586         /// <summary>
1587         /// This test verifies that the out-of-proc node won't lock the directory containing the target project.
1588         /// </summary>
1589 #if RUNTIME_TYPE_NETCORE
1590         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/933")]
1591 #else
1592         [Fact]
1593 #endif
OutOfProcNodeDoesntLockWorkingDirectory()1594         public void OutOfProcNodeDoesntLockWorkingDirectory()
1595         {
1596             string contents = CleanupFileContents(@"
1597 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1598  <Target Name='test'>
1599     <Message Text='[success]'/>
1600  </Target>
1601 </Project>
1602 ");
1603 
1604             var projectFolder = _env.CreateFolder();
1605             string projectFile = _env.CreateFile(projectFolder, ".proj").Path;
1606 
1607             File.WriteAllText(projectFile, contents);
1608             _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1");
1609             BuildRequestData data = new BuildRequestData(projectFile, new Dictionary<string, string>(), MSBuildDefaultToolsVersion, new string[] { }, null);
1610             _buildManager.Build(_parameters, data);
1611         }
1612 
1613         /// <summary>
1614         /// Retrieving a ProjectInstance from the BuildManager stores it in the cache
1615         /// </summary>
1616         [Fact]
ProjectInstanceStoredInCache()1617         public void ProjectInstanceStoredInCache()
1618         {
1619             string contents = CleanupFileContents(@"
1620 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1621  <Target Name='test'>
1622     <Message Text='text'/>
1623  </Target>
1624 </Project>
1625 ");
1626             Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true);
1627             ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project);
1628             ProjectInstance instance2 = _buildManager.GetProjectInstanceForBuild(project);
1629 
1630             Assert.Equal(instance, instance2); // "Instances don't match"
1631         }
1632 
1633         /// <summary>
1634         /// Retrieving a ProjectInstance from the BuildManager after a build.
1635         /// </summary>
1636         [Fact]
ProjectInstanceRetrievedAfterBuildMatchesSourceProject()1637         public void ProjectInstanceRetrievedAfterBuildMatchesSourceProject()
1638         {
1639             string contents = CleanupFileContents(@"
1640 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1641  <Target Name='test'>
1642     <PropertyGroup>
1643         <Foo>bar</Foo>
1644     </PropertyGroup>
1645 	<Message Text='[success]'/>
1646  </Target>
1647 </Project>
1648 ");
1649             IBuildComponentHost host = _buildManager;
1650             host.GetComponent(BuildComponentType.ConfigCache);
1651             BuildRequestData data = GetBuildRequestData(contents);
1652             _buildManager.Build(_parameters, data);
1653 
1654             Project project = _projectCollection.LoadProject(data.ProjectFullPath);
1655             ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project);
1656             Assert.Equal(instance.GetPropertyValue("Foo"), "bar");
1657         }
1658 
1659         /// <summary>
1660         /// Retrieving a ProjectInstance after resetting the cache clears the instances.
1661         /// </summary>
1662         [Fact]
ResetCacheClearsInstances()1663         public void ResetCacheClearsInstances()
1664         {
1665             string contents = CleanupFileContents(@"
1666 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1667  <Target Name='test'>
1668     <PropertyGroup>
1669         <Foo>bar</Foo>
1670     </PropertyGroup>
1671 	<Message Text='[success]'/>
1672  </Target>
1673 </Project>
1674 ");
1675             IBuildComponentHost host = _buildManager;
1676             host.GetComponent(BuildComponentType.ConfigCache);
1677             BuildRequestData data = GetBuildRequestData(contents);
1678             _buildManager.Build(_parameters, data);
1679 
1680             Project project = _projectCollection.LoadProject(data.ProjectFullPath);
1681             ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project);
1682             Assert.Equal("bar", instance.GetPropertyValue("Foo"));
1683 
1684             _buildManager.BeginBuild(_parameters);
1685             _buildManager.EndBuild();
1686 
1687             instance = _buildManager.GetProjectInstanceForBuild(project);
1688             Assert.Null(instance.GetProperty("Foo"));
1689         }
1690 
1691         /// <summary>
1692         /// Retrieving a ProjectInstance after another build without resetting the cache keeps the existing instance
1693         /// </summary>
1694         [Fact]
DisablingCacheResetKeepsInstance()1695         public void DisablingCacheResetKeepsInstance()
1696         {
1697             string contents = CleanupFileContents(@"
1698 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1699  <Target Name='test'>
1700     <PropertyGroup>
1701         <Foo>bar</Foo>
1702     </PropertyGroup>
1703 	<Message Text='[success]'/>
1704  </Target>
1705 </Project>
1706 ");
1707             IBuildComponentHost host = _buildManager;
1708             host.GetComponent(BuildComponentType.ConfigCache);
1709             BuildRequestData data = GetBuildRequestData(contents);
1710             _buildManager.Build(_parameters, data);
1711 
1712             Project project = _projectCollection.LoadProject(data.ProjectFullPath);
1713             ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project);
1714             Assert.Equal(instance.GetPropertyValue("Foo"), "bar");
1715 
1716             _logger.ClearLog();
1717             _parameters.ResetCaches = false;
1718             _buildManager.BeginBuild(_parameters);
1719             _buildManager.BuildRequest(data);
1720             _buildManager.EndBuild();
1721 
1722             // We should have built the same instance, with the same results, so the target will be skipped.
1723             string skippedMessage = ResourceUtilities.FormatResourceString("TargetAlreadyCompleteSuccess", "test");
1724             Assert.Equal(true, _logger.FullLog.Contains(skippedMessage));
1725 
1726             ProjectInstance instance2 = _buildManager.GetProjectInstanceForBuild(project);
1727             Assert.Equal(instance, instance2); // "Instances are not the same"
1728         }
1729 
1730         /// <summary>
1731         /// Retrieving a ProjectInstance after another build without resetting the cache keeps the existing instance
1732         /// </summary>
1733         [Fact]
GhostProjectRootElementCache()1734         public void GhostProjectRootElementCache()
1735         {
1736             string p2pProject = _env.CreateFile(".Project2.proj").Path;
1737 
1738             string contents1 = CleanupFileContents($@"
1739 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1740  <Target Name='test'>
1741     <Msbuild Projects='{p2pProject}'>
1742       <Output TaskParameter='TargetOutputs' ItemName='P2pOutput'/>
1743     </Msbuild>
1744 
1745      <Message Text='Value:@(P2pOutput)' Importance='high'/>
1746  </Target>
1747 </Project>
1748 ");
1749 
1750             string contents2 = CleanupFileContents(@"
1751 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1752     <PropertyGroup>
1753         <Bar Condition=""'$(Bar)' == ''"">Baz</Bar>
1754     </PropertyGroup>
1755 
1756 <Target Name='test' Returns='$(Bar)'/>
1757 </Project>
1758 ");
1759             IBuildComponentHost host = _buildManager;
1760             host.GetComponent(BuildComponentType.ConfigCache);
1761 
1762             // Create Project 1
1763             ProjectInstance projectInstance = CreateProjectInstance(contents1, null, _projectCollection, false);
1764             BuildRequestData data = new BuildRequestData(projectInstance, new string[0]);
1765 
1766             _logger.ClearLog();
1767 
1768             // Write the second project to disk and load it into its own project collection
1769             ProjectCollection projectCollection2 = new ProjectCollection();
1770             File.WriteAllText(p2pProject, contents2);
1771 
1772             Project project2 = projectCollection2.LoadProject(p2pProject);
1773 
1774             _parameters.ResetCaches = false;
1775 
1776             // Build the first project to make sure we get the expected default values out for the p2p call.
1777             _parameters.ProjectRootElementCache = _projectCollection.ProjectRootElementCache;
1778             _buildManager.BeginBuild(_parameters);
1779             _buildManager.BuildRequest(data);
1780             _buildManager.EndBuild();
1781 
1782             _logger.AssertLogContains("Value:Baz");
1783             _logger.ClearLog();
1784 
1785             // Modify the property in the second project and save it to disk.
1786             project2.SetProperty("Bar", "FOO");
1787             project2.Save();
1788 
1789             // Create a new build.
1790             ProjectInstance projectInstance2 = CreateProjectInstance(contents1, null, _projectCollection, false);
1791             BuildRequestData data2 = new BuildRequestData(projectInstance2, new string[0]);
1792 
1793             // Build again.
1794             _parameters.ResetCaches = false;
1795             _buildManager.BeginBuild(_parameters);
1796             _buildManager.BuildRequest(data2);
1797             _buildManager.EndBuild();
1798             _logger.AssertLogContains("Value:FOO");
1799         }
1800 
1801         /// <summary>
1802         /// Verifies that explicitly loaded projects' imports are all marked as also explicitly loaded.
1803         /// </summary>
1804         [Fact]
VerifyImportedProjectRootElementsInheritExplicitLoadFlag()1805         public void VerifyImportedProjectRootElementsInheritExplicitLoadFlag()
1806         {
1807             string contents1 = CleanupFileContents(@"
1808 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1809  <Import Project='{0}' />
1810  <Target Name='test' />
1811 </Project>
1812 ");
1813 
1814             string contents2 = CleanupFileContents(@"
1815 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1816     <PropertyGroup>
1817         <ImportedProperty>ImportedValue</ImportedProperty>
1818     </PropertyGroup>
1819 </Project>
1820 ");
1821 
1822             string importedProjectPath = _env.CreateFile(".proj").Path;
1823             string rootProjectPath = _env.CreateFile(".proj").Path;
1824 
1825             File.WriteAllText(importedProjectPath, contents2);
1826             File.WriteAllText(rootProjectPath, String.Format(CultureInfo.InvariantCulture, contents1, importedProjectPath));
1827 
1828             var projectCollection = new ProjectCollection();
1829 
1830             // Run a simple build just to prove that nothing is left in the cache.
1831             BuildRequestData data = new BuildRequestData(rootProjectPath, ReadOnlyEmptyDictionary<string, string>.Instance, null, new[] { "test" }, null);
1832             _parameters.ResetCaches = true;
1833             _parameters.ProjectRootElementCache = projectCollection.ProjectRootElementCache;
1834             _buildManager.BeginBuild(_parameters);
1835             _buildManager.BuildRequest(data);
1836             _buildManager.EndBuild();
1837             _buildManager.ResetCaches();
1838 
1839             // The semantic of TryOpen is to only retrieve the PRE if it is already in the weak cache.
1840             Assert.Null(ProjectRootElement.TryOpen(rootProjectPath, projectCollection)); // "The built project shouldn't be in the cache anymore."
1841             Assert.Null(ProjectRootElement.TryOpen(importedProjectPath, projectCollection)); // "The built project's import shouldn't be in the cache anymore."
1842 
1843             Project project = projectCollection.LoadProject(rootProjectPath);
1844             ProjectRootElement preRoot, preImported;
1845             Assert.NotNull(preRoot = ProjectRootElement.TryOpen(rootProjectPath, projectCollection)); // "The root project file should be in the weak cache."
1846             Assert.NotNull(preImported = ProjectRootElement.TryOpen(importedProjectPath, projectCollection)); // "The imported project file should be in the weak cache."
1847             Assert.True(preRoot.IsExplicitlyLoaded);
1848             Assert.True(preImported.IsExplicitlyLoaded);
1849 
1850             // Run a simple build just to prove that it doesn't impact what is in the cache.
1851             data = new BuildRequestData(rootProjectPath, ReadOnlyEmptyDictionary<string, string>.Instance, null, new[] { "test" }, null);
1852             _parameters.ResetCaches = true;
1853             _parameters.ProjectRootElementCache = projectCollection.ProjectRootElementCache;
1854             _buildManager.BeginBuild(_parameters);
1855             _buildManager.BuildRequest(data);
1856             _buildManager.EndBuild();
1857             _buildManager.ResetCaches();
1858 
1859             // Now make sure they are still in the weak cache.  Since they were loaded explicitly before the build, the build shouldn't have unloaded them from the cache.
1860             Assert.Same(preRoot, ProjectRootElement.TryOpen(rootProjectPath, projectCollection)); // "The root project file should be in the weak cache after a build."
1861             Assert.Same(preImported, ProjectRootElement.TryOpen(importedProjectPath, projectCollection)); // "The imported project file should be in the weak cache after a build."
1862             Assert.True(preRoot.IsExplicitlyLoaded);
1863             Assert.True(preImported.IsExplicitlyLoaded);
1864 
1865             projectCollection.UnloadProject(project);
1866             projectCollection.UnloadAllProjects();
1867             Assert.Null(ProjectRootElement.TryOpen(rootProjectPath, projectCollection)); // "The unloaded project shouldn't be in the cache anymore."
1868             Assert.Null(ProjectRootElement.TryOpen(importedProjectPath, projectCollection)); // "The unloaded project's import shouldn't be in the cache anymore."
1869         }
1870 
1871         /// <summary>
1872         /// Verify that using a second BuildManager doesn't cause the system to crash.
1873         /// </summary>
1874         [Fact]
Regress251333()1875         public void Regress251333()
1876         {
1877             string contents = CleanupFileContents(@"
1878 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1879 <PropertyGroup>
1880        <InitialProperty1>InitialProperty1</InitialProperty1>
1881        <InitialProperty2>InitialProperty2</InitialProperty2>
1882        <InitialProperty3>InitialProperty3</InitialProperty3>
1883 </PropertyGroup>
1884  <Target Name='test'>
1885 	<Message Text='[success]'/>
1886  </Target>
1887 </Project>
1888 ");
1889 
1890             // First a normal build
1891             BuildRequestData data = GetBuildRequestData(contents);
1892             BuildResult result = _buildManager.Build(_parameters, data);
1893             Assert.Equal(result.OverallResult, BuildResultCode.Success);
1894 
1895             // Now a build using a different build manager.
1896             using (BuildManager newBuildManager = new BuildManager())
1897             {
1898                 GetBuildRequestData(contents);
1899                 BuildResult result2 = newBuildManager.Build(_parameters, data);
1900                 Assert.Equal(result2.OverallResult, BuildResultCode.Success);
1901             }
1902         }
1903 
1904         /// <summary>
1905         /// Verify that disabling the in-proc node doesn't cause projects which don't require it to fail.
1906         /// </summary>
1907 #if MONO
1908         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
1909 #else
1910         [Fact]
1911 #endif
Regress239661()1912         public void Regress239661()
1913         {
1914             string contents = CleanupFileContents(@"
1915 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1916 <PropertyGroup>
1917        <InitialProperty1>InitialProperty1</InitialProperty1>
1918        <InitialProperty2>InitialProperty2</InitialProperty2>
1919        <InitialProperty3>InitialProperty3</InitialProperty3>
1920 </PropertyGroup>
1921  <Target Name='test'>
1922 	<Message Text='[success]'/>
1923  </Target>
1924 </Project>
1925 ");
1926 
1927             string fileName = _env.CreateFile(".proj").Path;
1928             File.WriteAllText(fileName, contents);
1929             BuildRequestData data = new BuildRequestData(fileName, _projectCollection.GlobalProperties, MSBuildDefaultToolsVersion, new string[0], null);
1930             _parameters.DisableInProcNode = true;
1931             BuildResult result = _buildManager.Build(_parameters, data);
1932             Assert.Equal(BuildResultCode.Success, result.OverallResult);
1933             _logger.AssertLogContains("[success]");
1934         }
1935 
1936         /// <summary>
1937         /// Verify that disabling the in-proc node when a project requires it will cause the build to fail, but not crash.
1938         /// </summary>
1939         [Fact]
Regress239661_NodeUnavailable()1940         public void Regress239661_NodeUnavailable()
1941         {
1942             string contents = CleanupFileContents(@"
1943 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1944 <PropertyGroup>
1945        <InitialProperty1>InitialProperty1</InitialProperty1>
1946        <InitialProperty2>InitialProperty2</InitialProperty2>
1947        <InitialProperty3>InitialProperty3</InitialProperty3>
1948 </PropertyGroup>
1949  <Target Name='test'>
1950 	<Message Text='[success]'/>
1951  </Target>
1952 </Project>
1953 ");
1954             BuildRequestData data = GetBuildRequestData(contents);
1955             _parameters.DisableInProcNode = true;
1956 
1957             // Require that this project build on the in-proc node, which will not be available.
1958             data.HostServices.SetNodeAffinity(data.ProjectFullPath, NodeAffinity.InProc);
1959             BuildResult result = _buildManager.Build(_parameters, data);
1960             Assert.Equal(BuildResultCode.Failure, result.OverallResult);
1961             _logger.AssertLogDoesntContain("[success]");
1962             _logger.AssertLogContains("MSB4223");
1963         }
1964 
1965         /// <summary>
1966         /// Ensures that properties and items are transferred to the out-of-proc node when an instance is used to start the build.
1967         /// </summary>
1968 #if MONO
1969         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
1970 #else
1971         [Fact]
1972 #endif
ProjectInstanceTransfersToOOPNode()1973         public void ProjectInstanceTransfersToOOPNode()
1974         {
1975             string contents = CleanupFileContents(@"
1976 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1977  <PropertyGroup>
1978    <DeleteMe>deleteme</DeleteMe>
1979    <Unmodified>unmodified</Unmodified>
1980    <VirtualProp>original</VirtualProp>
1981  </PropertyGroup>
1982 <ItemGroup>
1983   <Foo Include='foo'/>
1984   <Foo2 Include='foo2'/>
1985 </ItemGroup>
1986  <Target Name='test'>
1987    <Message Text='[$(DeleteMe)]'/>
1988    <Message Text='[$(Unmodified)]'/>
1989    <Message Text='[$(VirtualProp)]'/>
1990    <Message Text='[$(NewProp)]'/>
1991    <Message Text='[@(Foo)]'/>
1992    <Message Text='[@(Foo2)]'/>
1993    <Message Text='[@(Baz)]'/>
1994  </Target>
1995 </Project>
1996 ");
1997 
1998             string fileName = _env.CreateFile(".proj").Path;
1999             File.WriteAllText(fileName, contents);
2000             Project project = new Project(fileName);
2001             ProjectInstance instance = project.CreateProjectInstance();
2002             instance.RemoveProperty("DeleteMe");
2003             instance.SetProperty("VirtualProp", "overridden");
2004             instance.SetProperty("NewProp", "new");
2005             instance.AddItem("Baz", "baz");
2006             instance.AddItem("Foo2", "foo21");
2007             foreach (var item in instance.Items)
2008             {
2009                 if (item.EvaluatedInclude == "foo")
2010                 {
2011                     instance.RemoveItem(item);
2012                     break;
2013                 }
2014             }
2015 
2016             BuildRequestData data = new BuildRequestData(instance, new string[0]);
2017 
2018             // Force this to build out-of-proc
2019             _parameters.DisableInProcNode = true;
2020             _buildManager.Build(_parameters, data);
2021             _logger.AssertLogDoesntContain("[deleteme]");
2022             _logger.AssertLogContains("[overridden]");
2023             _logger.AssertLogContains("[unmodified]");
2024             _logger.AssertLogContains("[new]");
2025             _logger.AssertLogDoesntContain("[foo]");
2026             _logger.AssertLogContains("[foo2;foo21]");
2027             _logger.AssertLogContains("[baz]");
2028         }
2029 
2030         /// <summary>
2031         /// Ensures that a limited set of properties are transferred from a project instance to an OOP node.
2032         /// </summary>
2033 #if MONO
2034         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
2035 #else
2036         [Fact]
2037 #endif
ProjectInstanceLimitedTransferToOOPNode()2038         public void ProjectInstanceLimitedTransferToOOPNode()
2039         {
2040             string contents = CleanupFileContents(@"
2041 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
2042  <PropertyGroup>
2043    <Unmodified>unmodified</Unmodified>
2044    <VirtualProp>original</VirtualProp>
2045  </PropertyGroup>
2046  <Target Name='test'>
2047    <Message Text='[$(Unmodified)]'/>
2048    <Message Text='[$(VirtualProp)]'/>
2049  </Target>
2050 </Project>
2051 ");
2052 
2053             string fileName = _env.CreateFile(".proj").Path;
2054             File.WriteAllText(fileName, contents);
2055             Project project = new Project(fileName);
2056             ProjectInstance instance = project.CreateProjectInstance();
2057             instance.SetProperty("VirtualProp", "overridden");
2058             instance.SetProperty("Unmodified", "changed");
2059 
2060             BuildRequestData data = new BuildRequestData(instance, new string[0], null, BuildRequestDataFlags.None, new string[] { "VirtualProp" });
2061 
2062             // Force this to build out-of-proc
2063             _parameters.DisableInProcNode = true;
2064             _buildManager.Build(_parameters, data);
2065             _logger.AssertLogContains("[overridden]");
2066             _logger.AssertLogContains("[unmodified]");
2067             _logger.AssertLogDoesntContain("[changed]");
2068         }
2069 
2070         /// <summary>
2071         /// Tests that cache files are created as expected and their lifetime is controlled appropriately.
2072         /// </summary>
2073         [Fact]
2074         [Trait("Category", "netcore-osx-failing")]
2075         [Trait("Category", "netcore-linux-failing")]
2076         [Trait("Category", "mono-osx-failing")]
CacheLifetime()2077         public void CacheLifetime()
2078         {
2079 
2080             FileUtilities.ClearCacheDirectory();
2081 
2082             _env.SetEnvironmentVariable("MSBUILDDEBUGFORCECACHING", "1");
2083             string outerBuildCacheDirectory;
2084             string innerBuildCacheDirectory;
2085 
2086             // Do a build with one build manager.
2087             using (var outerBuildManager = new BuildManager())
2088             {
2089                 outerBuildCacheDirectory = BuildAndCheckCache(outerBuildManager, new string[] { });
2090 
2091                 // Do another build with a second build manager while the first still exists.  Since both BuildManagers
2092                 // share a process-wide cache directory, we want to verify that they don't stomp on each other, either
2093                 // by accidentally sharing results, or by clearing them away.
2094                 using (var innerBuildManager = new BuildManager())
2095                 {
2096                     innerBuildCacheDirectory = BuildAndCheckCache(innerBuildManager, new string[] { outerBuildCacheDirectory });
2097 
2098                     // Force the cache for this build manager (and only this build manager) to be cleared.  It should leave
2099                     // behind the results from the other one.
2100                     innerBuildManager.ResetCaches();
2101                 }
2102 
2103                 Assert.False(Directory.Exists(innerBuildCacheDirectory)); // "Inner build cache directory still exists after inner build manager was disposed."
2104                 Assert.True(Directory.Exists(outerBuildCacheDirectory)); // "Outer build cache directory doesn't exist after inner build manager was disposed."
2105 
2106                 // Force the cache for this build manager to be cleared.
2107                 outerBuildManager.ResetCaches();
2108             }
2109 
2110             Assert.False(Directory.Exists(outerBuildCacheDirectory)); // "Outer build cache directory still exists after outer build manager was disposed."
2111         }
2112 
2113         /// <summary>
2114         /// If there's a P2P that otherwise succeeds, but has an AfterTarget that errors out, the
2115         /// overall build result -- and thus the return value of the MSBuild task -- should reflect
2116         /// that failure.
2117         /// </summary>
2118         [Fact]
FailedAfterTargetInP2PShouldCauseOverallBuildFailure()2119         public void FailedAfterTargetInP2PShouldCauseOverallBuildFailure()
2120         {
2121             var projA = _env.CreateFile(".proj").Path;
2122             var projB = _env.CreateFile(".proj").Path;
2123 
2124             string contentsA = @"
2125 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2126   <Target Name=`Build`>
2127     <MSBuild Projects=`" + projB + @"` />
2128 
2129     <Warning Text=`We shouldn't reach here.` />
2130   </Target>
2131 </Project>
2132 ";
2133 
2134             string contentsB = @"
2135 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2136   <Target Name=`Build`>
2137     <Message Text=`Build` />
2138   </Target>
2139 
2140   <Target Name=`Error` AfterTargets=`Build`>
2141     <Error Text=`Error!` />
2142   </Target>
2143 </Project>
2144 ";
2145 
2146             File.WriteAllText(projA, CleanupFileContents(contentsA));
2147             File.WriteAllText(projB, CleanupFileContents(contentsB));
2148 
2149             _buildManager.BeginBuild(_parameters);
2150             BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null, new[] { "Build" }, new HostServices());
2151             BuildResult result = _buildManager.PendBuildRequest(data).Execute();
2152 
2153             Assert.Equal(BuildResultCode.Failure, result.OverallResult);
2154             _logger.AssertNoWarnings();
2155             _buildManager.EndBuild();
2156         }
2157 
2158         /// <summary>
2159         /// If there's a P2P that otherwise succeeds, but has an AfterTarget that errors out, the
2160         /// overall build result -- and thus the return value of the MSBuild task -- should reflect
2161         /// that failure.  Specifically tests where there are multiple entrypoint targets with
2162         /// AfterTargets, only one of which fails.
2163         /// </summary>
2164         [Fact]
FailedAfterTargetInP2PShouldCauseOverallBuildFailure_MultipleEntrypoints()2165         public void FailedAfterTargetInP2PShouldCauseOverallBuildFailure_MultipleEntrypoints()
2166         {
2167             var projA = _env.CreateFile(".proj").Path;
2168             var projB = _env.CreateFile(".proj").Path;
2169 
2170             string contentsA = @"
2171 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2172   <Target Name=`Build`>
2173     <MSBuild Projects=`" + projB + @"` Targets=`Build;Build2` />
2174 
2175     <Warning Text=`We shouldn't reach here.` />
2176   </Target>
2177 </Project>
2178 ";
2179 
2180             string contentsB = @"
2181 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2182   <Target Name=`Build`>
2183     <Message Text=`[Build]` />
2184   </Target>
2185 
2186   <Target Name=`Build2`>
2187     <Message Text=`[Build2]` />
2188   </Target>
2189 
2190   <Target Name=`AT1` AfterTargets=`Build`>
2191     <Message Text=`[AT1]` />
2192   </Target>
2193 
2194   <Target Name=`AT2` AfterTargets=`Build2`>
2195     <Message Text=`[AT2]` />
2196   </Target>
2197 
2198   <Target Name=`Error` AfterTargets=`Build2`>
2199     <Error Text=`Error!` />
2200   </Target>
2201 </Project>
2202 ";
2203 
2204             File.WriteAllText(projA, CleanupFileContents(contentsA));
2205             File.WriteAllText(projB, CleanupFileContents(contentsB));
2206 
2207             _buildManager.BeginBuild(_parameters);
2208             BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null, new[] { "Build" }, new HostServices());
2209             BuildResult result = _buildManager.PendBuildRequest(data).Execute();
2210 
2211             Assert.Equal(BuildResultCode.Failure, result.OverallResult);
2212             _logger.AssertNoWarnings();
2213             _logger.AssertLogContains("[Build]");
2214             _logger.AssertLogContains("[Build2]");
2215             _logger.AssertLogContains("[AT1]");
2216             _logger.AssertLogContains("[AT2]");
2217 
2218             _buildManager.EndBuild();
2219         }
2220 
2221         /// <summary>
2222         /// If there's a P2P that otherwise succeeds, but has an AfterTarget that errors out, the
2223         /// overall build result -- and thus the return value of the MSBuild task -- should reflect
2224         /// that failure. This should also be true if the AfterTarget is an AfterTarget of the
2225         /// entrypoint target.
2226         /// </summary>
2227         [Fact]
FailedNestedAfterTargetInP2PShouldCauseOverallBuildFailure()2228         public void FailedNestedAfterTargetInP2PShouldCauseOverallBuildFailure()
2229         {
2230             var projA = _env.CreateFile(".proj").Path;
2231             var projB = _env.CreateFile(".proj").Path;
2232 
2233             string contentsA = @"
2234 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2235   <Target Name=`Build`>
2236     <MSBuild Projects=`" + projB + @"` />
2237 
2238     <Warning Text=`We shouldn't reach here.` />
2239   </Target>
2240 </Project>
2241 ";
2242 
2243             string contentsB = @"
2244 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2245   <Target Name=`Build`>
2246     <Message Text=`Build` />
2247   </Target>
2248 
2249   <Target Name=`Target1` AfterTargets=`Build`>
2250     <Message Text=`Target1` />
2251   </Target>
2252 
2253   <Target Name=`Error` AfterTargets=`Target1`>
2254     <Error Text=`Error!` />
2255   </Target>
2256 </Project>
2257 ";
2258 
2259             File.WriteAllText(projA, CleanupFileContents(contentsA));
2260             File.WriteAllText(projB, CleanupFileContents(contentsB));
2261 
2262             _buildManager.BeginBuild(_parameters);
2263             BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null, new[] { "Build" }, new HostServices());
2264             BuildResult result = _buildManager.PendBuildRequest(data).Execute();
2265 
2266             Assert.Equal(BuildResultCode.Failure, result.OverallResult);
2267             _logger.AssertNoWarnings();
2268             _buildManager.EndBuild();
2269         }
2270 
2271         /// <summary>
2272         /// If a project is called into twice, with two different entrypoint targets that
2273         /// depend on non-overlapping sets of targets, and the first fails, the second
2274         /// should not inherit that failure if all the targets it calls succeed.
2275         /// </summary>
2276         [Fact]
NonOverlappingEnusingTrypointTargetsShouldNotInfluenceEachOthersResults()2277         public void NonOverlappingEnusingTrypointTargetsShouldNotInfluenceEachOthersResults()
2278         {
2279             var projA = _env.CreateFile(".proj").Path;
2280             var projB = _env.CreateFile(".proj").Path;
2281 
2282             string contentsA = @"
2283 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2284   <Target Name=`Build`>
2285 
2286    <Message Text=`The next MSBuild call should FAIL, but the build will continue.` />
2287    <MSBuild Projects=`" + projB + @"` Targets=`Build` ContinueOnError=`true` />
2288    <Message Text=`The next MSBuild call should SUCCEED without error.` />
2289    <MSBuild Projects=`" + projB + @"` Targets=`GetTargetPath` />
2290   </Target>
2291 </Project>
2292 ";
2293 
2294             string contentsB = @"
2295 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2296   <Target Name=`Build`>
2297     <Error Text=`Forced error in Build` />
2298   </Target>
2299 
2300 
2301   <Target Name=`GetTargetPath`>
2302     <Message Text=`Success` />
2303   </Target>
2304 </Project>
2305 ";
2306 
2307             File.WriteAllText(projA, CleanupFileContents(contentsA));
2308             File.WriteAllText(projB, CleanupFileContents(contentsB));
2309 
2310             _buildManager.BeginBuild(_parameters);
2311             BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null, new[] { "Build" }, new HostServices());
2312             BuildResult result = _buildManager.PendBuildRequest(data).Execute();
2313 
2314             Assert.Equal(BuildResultCode.Success, result.OverallResult);
2315             Assert.Equal(1, _logger.ErrorCount);
2316             _buildManager.EndBuild();
2317         }
2318 
2319         /// <summary>
2320         /// In a situation where we have two requests calling into the same project, with different entry point
2321         /// targets, one of which depends on "A;B", the other of which depends on "B", which has a dependency of
2322         /// its own on "A", that we still properly build.
2323         /// </summary>
2324 #if RUNTIME_TYPE_NETCORE
2325         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/933")]
2326 #elif MONO
2327         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
2328 #else
2329         [Fact]
2330 #endif
Regress473114()2331         public void Regress473114()
2332         {
2333             var projA = _env.CreateFile(".proj").Path;
2334             var projB = _env.CreateFile(".proj").Path;
2335             var projC = _env.CreateFile(".proj").Path;
2336             var projD = _env.CreateFile(".proj").Path;
2337 
2338             string contentsA = @"<?xml version='1.0' encoding='utf-8'?>
2339 <Project ToolsVersion='4.0' DefaultTargets='Build' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
2340   <ItemGroup>
2341     <ProjectReference Include='" + projD + @"' />
2342     <ProjectReference Include='" + projC + @"' />
2343     <ProjectReference Include='" + projB + @"' />
2344   </ItemGroup>
2345 
2346   <Target Name='Build'>
2347     <MSBuild Projects='@(ProjectReference)' BuildInParallel='true' />
2348   </Target>
2349 </Project>
2350 ";
2351 
2352             string contentsB = @"<?xml version='1.0' encoding='utf-8'?>
2353 <Project ToolsVersion='4.0' DefaultTargets='CallsGenerateImpLib' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
2354   <Target Name='CallsGenerateImpLib'>
2355     <MSBuild Projects='" + projC + @"' Targets='GenerateImpLib' BuildInParallel='true' />
2356   </Target>
2357 
2358 </Project>
2359 ";
2360 
2361             string contentsC = @"<?xml version='1.0' encoding='utf-8'?>
2362 <Project ToolsVersion='4.0' DefaultTargets='Default' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
2363   <Target Name='Default' DependsOnTargets='ResolveReferences;BuildCompile'>
2364     <Message Text='Executed Default' />
2365   </Target>
2366 
2367   <Target Name='GenerateImpLib' DependsOnTargets='BuildCompile'>
2368     <Message Text='Executed GenerateImpLib' />
2369   </Target>
2370 
2371   <Target Name='ResolveReferences'>
2372     <MSBuild Projects='" + projD + @"' Targets='BuildSlower' BuildInParallel='true' />
2373   </Target>
2374 
2375   <Target Name='BuildCompile' DependsOnTargets='ResolveReferences'>
2376     <Message Text='Executed BuildCompile' />
2377   </Target>
2378 
2379 </Project>
2380 ";
2381 
2382             string contentsD = @"<?xml version='1.0' encoding='utf-8'?>
2383 <Project ToolsVersion='4.0' DefaultTargets='Build' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
2384   <Target Name='Build'>
2385     <Message Text='In d.proj' />
2386   </Target>
2387 
2388   <Target Name='BuildSlower'>
2389     <Exec Command='" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(500)) + @"' />
2390   </Target>
2391 </Project>
2392 ";
2393 
2394             File.WriteAllText(projA, contentsA);
2395             File.WriteAllText(projB, contentsB);
2396             File.WriteAllText(projC, contentsC);
2397             File.WriteAllText(projD, contentsD);
2398 
2399             _parameters.MaxNodeCount = 3;
2400             _parameters.EnableNodeReuse = false;
2401             _buildManager.BeginBuild(_parameters);
2402             BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), "4.0", new[] { "Build" }, new HostServices());
2403             BuildResult result = _buildManager.PendBuildRequest(data).Execute();
2404 
2405             Assert.Equal(BuildResultCode.Success, result.OverallResult);
2406 
2407             _buildManager.EndBuild();
2408         }
2409 
2410         /// <summary>
2411         /// If two requests are made for the same project, and they call in with
2412         /// just the right timing such that:
2413         /// - request 1 builds for a while, reaches a P2P, and blocks
2414         /// - request 2 starts building, skips for a while, reaches the above P2P, and
2415         ///   blocks waiting for request 1's results
2416         /// - request 1 resumes building, errors, and exits
2417         /// - request 2 resumes building
2418         ///
2419         /// Then request 2 should end up exiting in the same fashion.
2420         ///
2421         /// This simple test verifies that if there are two error targets in a row, the
2422         /// second request will bail out where the first request did, as though it had
2423         /// executed the target, rather than skipping and continuing.
2424         /// </summary>
2425 #if MONO
2426         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
2427 #else
2428         [Fact]
2429 #endif
VerifyMultipleRequestForSameProjectWithErrors_Simple()2430         public void VerifyMultipleRequestForSameProjectWithErrors_Simple()
2431         {
2432             var projA = _env.CreateFile(".proj").Path;
2433             var projB = _env.CreateFile(".proj").Path;
2434             var projC = _env.CreateFile(".proj").Path;
2435 
2436             string contentsA = $@"
2437 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2438   <Target Name=`Build`>
2439     <MSBuild Projects=`{projB};{projC};{projB}` BuildInParallel=`true` />
2440   </Target>
2441 </Project>";
2442 
2443             string contentsB = $@"
2444 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2445   <Target Name=`Build` DependsOnTargets=`CallMSBuild;Error1;Error2` />
2446   <Target Name=`CallMSBuild`>
2447     <MSBuild Projects=`{projC}` Targets=`Sleep` BuildInParallel=`true` />
2448   </Target>
2449   <Target Name=`Error1`>
2450     <Error Text=`Error 1` />
2451   </Target>
2452   <Target Name=`Error2`>
2453     <Error Text=`Error 2` />
2454   </Target>
2455 </Project>";
2456 
2457             string contentsC = @"
2458 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2459 
2460   <Target Name=`Build`>
2461     <Message Text=`foo` />
2462   </Target>
2463 
2464   <Target Name=`Sleep`>
2465     <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(500)) + @"` />
2466   </Target>
2467 </Project>";
2468 
2469             File.WriteAllText(projA, CleanupFileContents(contentsA));
2470             File.WriteAllText(projB, CleanupFileContents(contentsB));
2471             File.WriteAllText(projC, CleanupFileContents(contentsC));
2472 
2473             _parameters.MaxNodeCount = 2;
2474             _parameters.EnableNodeReuse = false;
2475             _buildManager.BeginBuild(_parameters);
2476             BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null,
2477                 new[] {"Build"}, new HostServices());
2478             BuildResult result = _buildManager.PendBuildRequest(data).Execute();
2479 
2480             Assert.Equal(BuildResultCode.Failure, result.OverallResult);
2481 
2482             // We should never get to Error2, because it's supposed to execute after Error1, which failed.
2483             _logger.AssertLogDoesntContain("Error 2");
2484 
2485             // We should, however, end up skipping Error1 on the second call to B.
2486             string skippedMessage =
2487                 ResourceUtilities.FormatResourceString("TargetAlreadyCompleteFailure", "Error1");
2488             _logger.AssertLogContains(skippedMessage);
2489             _buildManager.EndBuild();
2490         }
2491 
2492         /// <summary>
2493         /// If two requests are made for the same project, and they call in with
2494         /// just the right timing such that:
2495         /// - request 1 builds for a while, reaches a P2P, and blocks
2496         /// - request 2 starts building, skips for a while, reaches the above P2P, and
2497         ///   blocks waiting for request 1's results
2498         /// - request 1 resumes building, errors, and exits
2499         /// - request 2 resumes building
2500         ///
2501         /// Then request 2 should end up exiting in the same fashion.
2502         ///
2503         /// This simple test verifies that if there are two error targets in a row, and the
2504         /// first has a chain of OnError targets, the OnError targets will all execute as
2505         /// expected in the first request, but be skipped by the second (since if it's "skipping
2506         /// unsuccessful", it can assume that all other OnError targets have also already been run)
2507         /// </summary>
2508 #if MONO
2509         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
2510 #else
2511         [Fact]
2512 #endif
VerifyMultipleRequestForSameProjectWithErrors_OnErrorChain()2513         public void VerifyMultipleRequestForSameProjectWithErrors_OnErrorChain()
2514         {
2515             var projA = _env.CreateFile(".proj").Path;
2516             var projB = _env.CreateFile(".proj").Path;
2517             var projC = _env.CreateFile(".proj").Path;
2518 
2519             string contentsA = @"
2520 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2521   <Target Name=`Build`>
2522     <MSBuild Projects=`" + String.Join(";", projB, projC, projB) + @"` BuildInParallel=`true` />
2523   </Target>
2524 </Project>
2525 ";
2526 
2527             string contentsB = @"
2528 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2529   <Target Name=`Build` DependsOnTargets=`CallMSBuild;Error1;Error2` />
2530   <Target Name=`CallMSBuild`>
2531     <MSBuild Projects=`" + projC + @"` Targets=`Sleep` BuildInParallel=`true` />
2532   </Target>
2533   <Target Name=`Error1`>
2534     <Error Text=`Error 1` />
2535     <OnError ExecuteTargets=`Target2;Target3` />
2536   </Target>
2537   <Target Name=`Error2`>
2538     <Error Text=`Error 2` />
2539   </Target>
2540 
2541   <Target Name=`Target2`>
2542     <Error Text=`Error in Target2` />
2543     <OnError ExecuteTargets=`Target4` />
2544   </Target>
2545 
2546   <Target Name=`Target3`>
2547     <Message Text=`Target 3` />
2548   </Target>
2549 
2550   <Target Name=`Target4`>
2551     <Message Text=`Target 4` />
2552   </Target>
2553 
2554 </Project>
2555 ";
2556 
2557             string contentsC = @"
2558 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2559 
2560   <Target Name=`Build`>
2561     <Message Text=`foo` />
2562   </Target>
2563 
2564   <Target Name=`Sleep`>
2565     <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(500)) + @"` />
2566   </Target>
2567 </Project>
2568 ";
2569 
2570             File.WriteAllText(projA, CleanupFileContents(contentsA));
2571             File.WriteAllText(projB, CleanupFileContents(contentsB));
2572             File.WriteAllText(projC, CleanupFileContents(contentsC));
2573 
2574             _parameters.MaxNodeCount = 2;
2575             _parameters.EnableNodeReuse = false;
2576             _buildManager.BeginBuild(_parameters);
2577             BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null,
2578                 new[] {"Build"}, new HostServices());
2579             BuildResult result = _buildManager.PendBuildRequest(data).Execute();
2580 
2581             Assert.Equal(BuildResultCode.Failure, result.OverallResult);
2582 
2583             // We should never get to Error2, because it's supposed to execute after Error1, which failed.
2584             _logger.AssertLogDoesntContain("Error 2");
2585 
2586             // We should, however, get to Target2, Target3, and Target4, since they're part of the OnError
2587             // chain for Error1
2588             _logger.AssertLogContains("Error in Target2");
2589             _logger.AssertLogContains("Target 3");
2590             _logger.AssertLogContains("Target 4");
2591 
2592             // We should end up skipping Error1 on the second call to B.
2593             string skippedMessage1 =
2594                 ResourceUtilities.FormatResourceString("TargetAlreadyCompleteFailure", "Error1");
2595             _logger.AssertLogContains(skippedMessage1);
2596 
2597             // We shouldn't, however, see skip messages for the OnError targets
2598             string skippedMessage2 =
2599                 ResourceUtilities.FormatResourceString("TargetAlreadyCompleteFailure", "Target2");
2600             _logger.AssertLogDoesntContain(skippedMessage2);
2601 
2602             string skippedMessage3 =
2603                 ResourceUtilities.FormatResourceString("TargetAlreadyCompleteSuccess", "Target3");
2604             _logger.AssertLogDoesntContain(skippedMessage3);
2605 
2606             string skippedMessage4 =
2607                 ResourceUtilities.FormatResourceString("TargetAlreadyCompleteSuccess", "Target4");
2608             _logger.AssertLogDoesntContain(skippedMessage4);
2609             _buildManager.EndBuild();
2610         }
2611 
2612         /// <summary>
2613         /// If two requests are made for the same project, and they call in with
2614         /// just the right timing such that:
2615         /// - request 1 builds for a while, reaches a P2P, and blocks
2616         /// - request 2 starts building, skips for a while, reaches the above P2P, and
2617         ///   blocks waiting for request 1's results
2618         /// - request 1 resumes building, errors, and exits
2619         /// - request 2 resumes building
2620         ///
2621         /// Then request 2 should end up exiting in the same fashion.
2622         ///
2623         /// This simple test verifies that if there are two error targets in a row, AND
2624         /// they're marked as ContinueOnError=ErrorAndContinue, then we won't bail, but
2625         /// will continue executing (on the first request) or skipping (on the second)
2626         /// </summary>
2627 #if MONO
2628         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
2629 #else
2630         [Fact]
2631 #endif
VerifyMultipleRequestForSameProjectWithErrors_ErrorAndContinue()2632         public void VerifyMultipleRequestForSameProjectWithErrors_ErrorAndContinue()
2633         {
2634             var projA = _env.CreateFile(".proj").Path;
2635             var projB = _env.CreateFile(".proj").Path;
2636             var projC = _env.CreateFile(".proj").Path;
2637 
2638             string contentsA = @"
2639 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2640   <Target Name=`Build`>
2641     <MSBuild Projects=`" + String.Join(";", projB, projC, projB) + @"` BuildInParallel=`true` />
2642   </Target>
2643 </Project>
2644 ";
2645 
2646             string contentsB = @"
2647 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2648   <Target Name=`Build` DependsOnTargets=`CallMSBuild;Error1;Error2` />
2649   <Target Name=`CallMSBuild`>
2650     <MSBuild Projects=`" + projC + @"` Targets=`Sleep` BuildInParallel=`true` />
2651   </Target>
2652   <Target Name=`Error1`>
2653     <Error Text=`Error 1` ContinueOnError=`ErrorAndContinue` />
2654   </Target>
2655   <Target Name=`Error2`>
2656     <Error Text=`Error 2` />
2657   </Target>
2658 </Project>
2659 ";
2660 
2661             string contentsC = @"
2662 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2663 
2664   <Target Name=`Build`>
2665     <Message Text=`foo` />
2666   </Target>
2667 
2668   <Target Name=`Sleep`>
2669     <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(500)) + @"` />
2670   </Target>
2671 </Project>
2672 ";
2673 
2674             File.WriteAllText(projA, CleanupFileContents(contentsA));
2675             File.WriteAllText(projB, CleanupFileContents(contentsB));
2676             File.WriteAllText(projC, CleanupFileContents(contentsC));
2677 
2678             _parameters.MaxNodeCount = 2;
2679             _parameters.EnableNodeReuse = false;
2680             _buildManager.BeginBuild(_parameters);
2681             BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null,
2682                 new[] {"Build"}, new HostServices());
2683             BuildResult result = _buildManager.PendBuildRequest(data).Execute();
2684 
2685             Assert.Equal(BuildResultCode.Failure, result.OverallResult);
2686 
2687             // We should see both Error1 and Error2
2688             _logger.AssertLogContains("Error 1");
2689             _logger.AssertLogContains("Error 2");
2690 
2691             // We should also end up skipping them both.
2692             string skippedMessage1 =
2693                 ResourceUtilities.FormatResourceString("TargetAlreadyCompleteFailure", "Error1");
2694             _logger.AssertLogContains(skippedMessage1);
2695 
2696             string skippedMessage2 =
2697                 ResourceUtilities.FormatResourceString("TargetAlreadyCompleteFailure", "Error2");
2698             _logger.AssertLogContains(skippedMessage2);
2699 
2700             _buildManager.EndBuild();
2701         }
2702 
2703         /// <summary>
2704         /// If two requests are made for the same project, and they call in with
2705         /// just the right timing such that:
2706         /// - request 1 builds for a while, reaches a P2P, and blocks
2707         /// - request 2 starts building, skips for a while, reaches the above P2P, and
2708         ///   blocks waiting for request 1's results
2709         /// - request 1 resumes building, errors, and exits
2710         /// - request 2 resumes building
2711         ///
2712         /// Then request 2 should end up exiting in the same fashion.
2713         ///
2714         /// This test verifies that if the errors are in AfterTargets, we still
2715         /// exit as though the target that those targets run after has already run.
2716         /// </summary>
2717 #if MONO
2718         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")]
2719 #else
2720         [Fact]
2721 #endif
VerifyMultipleRequestForSameProjectWithErrors_AfterTargets()2722         public void VerifyMultipleRequestForSameProjectWithErrors_AfterTargets()
2723         {
2724             var projA = _env.CreateFile(".proj").Path;
2725             var projB = _env.CreateFile(".proj").Path;
2726             var projC = _env.CreateFile(".proj").Path;
2727 
2728             string contentsA = @"
2729 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2730   <Target Name=`Build`>
2731     <MSBuild Projects=`" + String.Join(";", projB, projC, projB) + @"` BuildInParallel=`true` />
2732   </Target>
2733 </Project>
2734 ";
2735 
2736             string contentsB = @"
2737 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2738   <Target Name=`Build` DependsOnTargets=`CallMSBuild` />
2739   <Target Name=`CallMSBuild`>
2740     <MSBuild Projects=`" + projC + @"` Targets=`Sleep` BuildInParallel=`true` />
2741   </Target>
2742   <Target Name=`Error1` AfterTargets=`CallMSBuild`>
2743     <Error Text=`Error 1` />
2744   </Target>
2745   <Target Name=`Error2` AfterTargets=`CallMSBuild`>
2746     <Error Text=`Error 2` />
2747   </Target>
2748 </Project>
2749 ";
2750 
2751             string contentsC = @"
2752 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2753 
2754   <Target Name=`Build`>
2755     <Message Text=`foo` />
2756   </Target>
2757 
2758   <Target Name=`Sleep`>
2759     <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(500)) + @"` />
2760   </Target>
2761 </Project>
2762 ";
2763 
2764             File.WriteAllText(projA, CleanupFileContents(contentsA));
2765             File.WriteAllText(projB, CleanupFileContents(contentsB));
2766             File.WriteAllText(projC, CleanupFileContents(contentsC));
2767 
2768             _parameters.MaxNodeCount = 2;
2769             _parameters.EnableNodeReuse = false;
2770             _buildManager.BeginBuild(_parameters);
2771             BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null,
2772                 new[] {"Build"}, new HostServices());
2773             BuildResult result = _buildManager.PendBuildRequest(data).Execute();
2774 
2775             Assert.Equal(BuildResultCode.Failure, result.OverallResult);
2776 
2777             // We should never get to Error2, because we should never run its AfterTarget, after
2778             // the AfterTarget with Error1 failed
2779             _logger.AssertLogDoesntContain("Error 2");
2780 
2781             _buildManager.EndBuild();
2782         }
2783 
2784         /// <summary>
2785         /// Related to the two tests above, if two requests are made for the same project, but
2786         /// for different entry targets, and a target fails in the first request, if the second
2787         /// request also runs that target, its skip-unsuccessful should behave in the same
2788         /// way as if the target had actually errored.
2789         /// </summary>
2790         [Fact]
VerifyMultipleRequestForSameProjectWithErrors_DifferentEntrypoints()2791         public void VerifyMultipleRequestForSameProjectWithErrors_DifferentEntrypoints()
2792         {
2793             var projA = _env.CreateFile(".proj").Path;
2794             var projB = _env.CreateFile(".proj").Path;
2795 
2796             string contentsA = @"
2797 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2798   <ItemGroup>
2799     <PR Include=`" + projB + @"`>
2800       <Targets>Build</Targets>
2801     </PR>
2802     <PR Include=`" + projB + @"`>
2803       <Targets>Build2</Targets>
2804     </PR>
2805   </ItemGroup>
2806 
2807   <Target Name=`Build`>
2808     <MSBuild Projects=`@(PR)` Targets=`%(PR.Targets)` ContinueOnError=`true` />
2809   </Target>
2810 </Project>
2811 ";
2812 
2813             string contentsB = @"
2814 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2815   <Target Name=`Build` DependsOnTargets=`Target1;Error1`>
2816     <Message Text=`[Build]` />
2817   </Target>
2818   <Target Name=`Build2` DependsOnTargets=`Target2;Error1;Error2`>
2819     <Message Text=`[Build2]` />
2820   </Target>
2821   <Target Name=`Target1`>
2822     <Message Text=`[Target1]` />
2823   </Target>
2824   <Target Name=`Target2`>
2825     <Message Text=`[Target2]` />
2826   </Target>
2827   <Target Name=`Error1`>
2828     <Error Text=`[Error1]` />
2829   </Target>
2830   <Target Name=`Error2`>
2831     <Error Text=`[Error2]` />
2832   </Target>
2833 </Project>
2834 ";
2835 
2836             File.WriteAllText(projA, CleanupFileContents(contentsA));
2837             File.WriteAllText(projB, CleanupFileContents(contentsB));
2838 
2839             _buildManager.BeginBuild(_parameters);
2840             BuildRequestData data = new BuildRequestData(projA, new Dictionary<string, string>(), null,
2841                 new[] {"Build"}, new HostServices());
2842             BuildResult result = _buildManager.PendBuildRequest(data).Execute();
2843 
2844             Assert.Equal(BuildResultCode.Success, result.OverallResult);
2845 
2846             // We should never get to Error2, because it's only ever executed in the second
2847             // request after Error1, which should skip-unsuccessful and exit
2848             _logger.AssertLogDoesntContain("[Error2]");
2849 
2850             _buildManager.EndBuild();
2851         }
2852 
2853         /// <summary>
2854         /// Verify that we can submit multiple simultaneous submissions with
2855         /// legacy threading mode active and successfully build.
2856         /// </summary>
2857         [Fact]
TestSimultaneousSubmissionsWithLegacyThreadingData()2858         public void TestSimultaneousSubmissionsWithLegacyThreadingData()
2859         {
2860             string projectContent = @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2861     <Target Name=`Build`>
2862         <!-- Wait 200 ms -->
2863         <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(200)) + @"` />
2864     </Target>
2865 
2866 </Project>
2867 ";
2868             var projectPath1 = _env.CreateFile(".proj").Path;
2869             File.WriteAllText(projectPath1, CleanupFileContents(projectContent));
2870 
2871             Project project1 = new Project(projectPath1);
2872 
2873             var projectPath2 = _env.CreateFile(".proj").Path;
2874             File.WriteAllText(projectPath2, CleanupFileContents(projectContent));
2875 
2876             Project project2 = new Project(projectPath2);
2877 
2878             ConsoleLogger cl = new ConsoleLogger();
2879             BuildParameters buildParameters = new BuildParameters(ProjectCollection.GlobalProjectCollection);
2880             buildParameters.Loggers = new ILogger[] { cl };
2881             buildParameters.LegacyThreadingSemantics = true;
2882             BuildManager.DefaultBuildManager.BeginBuild(buildParameters);
2883 
2884             AutoResetEvent project1DoneEvent = new AutoResetEvent(false);
2885             ThreadPool.QueueUserWorkItem(delegate
2886             {
2887                 ProjectInstance pi = BuildManager.DefaultBuildManager.GetProjectInstanceForBuild(project1);
2888 
2889                 BuildRequestData requestData = new BuildRequestData(pi, new[] { "Build" });
2890                 BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData);
2891                 BuildResult br = submission.Execute();
2892                 project1DoneEvent.Set();
2893             });
2894 
2895             AutoResetEvent project2DoneEvent = new AutoResetEvent(false);
2896             ThreadPool.QueueUserWorkItem(delegate
2897             {
2898                 ProjectInstance pi = BuildManager.DefaultBuildManager.GetProjectInstanceForBuild(project2);
2899                 BuildRequestData requestData = new BuildRequestData(pi, new[] { "Build" });
2900                 BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData);
2901                 BuildResult br = submission.Execute();
2902                 project2DoneEvent.Set();
2903             });
2904 
2905             project1DoneEvent.WaitOne();
2906             project2DoneEvent.WaitOne();
2907 
2908             BuildManager.DefaultBuildManager.EndBuild();
2909         }
2910 
2911         /// <summary>
2912         /// Verify that we can submit multiple simultaneous submissions with
2913         /// legacy threading mode active and successfully build, and that one of those
2914         /// submissions can P2P to the other.
2915         /// </summary>
2916         [Fact]
TestSimultaneousSubmissionsWithLegacyThreadingData_P2P()2917         public void TestSimultaneousSubmissionsWithLegacyThreadingData_P2P()
2918         {
2919             string projectContent1 = @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2920     <Target Name=`CopyRunEnvironmentFiles`>
2921         <!-- Wait 100 ms -->
2922         <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(100)) + @"` />
2923     </Target>
2924 
2925     <Target Name=`MyConsoleTarget`>
2926         <!-- Wait 100 ms -->
2927         <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(100)) + @"` />
2928     </Target>
2929 
2930 </Project>
2931 ";
2932 
2933             var projectPath1 = _env.CreateFile(".proj").Path;
2934             File.WriteAllText(projectPath1, CleanupFileContents(projectContent1));
2935 
2936             Project project1 = new Project(projectPath1);
2937 
2938             string projectContent2 = @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2939     <Target Name=`MSDeployPublish` />
2940 
2941     <Target Name=`DoStuff` AfterTargets=`MSDeployPublish`>
2942         <MSBuild Projects=`" + projectPath1 + @"` Targets=`MyConsoleTarget` />
2943     </Target>
2944 
2945 </Project>
2946 ";
2947 
2948             var projectPath2 = _env.CreateFile(".proj").Path;
2949             File.WriteAllText(projectPath2, CleanupFileContents(projectContent2));
2950 
2951             Project project2 = new Project(projectPath2);
2952 
2953             ConsoleLogger cl = new ConsoleLogger();
2954             BuildParameters buildParameters = new BuildParameters(ProjectCollection.GlobalProjectCollection);
2955             buildParameters.Loggers = new ILogger[] { cl };
2956             buildParameters.LegacyThreadingSemantics = true;
2957             BuildManager.DefaultBuildManager.BeginBuild(buildParameters);
2958 
2959             AutoResetEvent project1DoneEvent = new AutoResetEvent(false);
2960             ThreadPool.QueueUserWorkItem(delegate
2961             {
2962                 // need to kick off project 2 first so that it project 1 can get submitted before the P2P happens
2963                 ProjectInstance pi = BuildManager.DefaultBuildManager.GetProjectInstanceForBuild(project2);
2964 
2965                 BuildRequestData requestData = new BuildRequestData(pi, new[] { "MSDeployPublish" });
2966                 BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData);
2967                 BuildResult br = submission.Execute();
2968                 Assert.Equal(BuildResultCode.Success, br.OverallResult);
2969                 project1DoneEvent.Set();
2970             });
2971 
2972             AutoResetEvent project2DoneEvent = new AutoResetEvent(false);
2973             ThreadPool.QueueUserWorkItem(delegate
2974             {
2975                 ProjectInstance pi = BuildManager.DefaultBuildManager.GetProjectInstanceForBuild(project1);
2976                 BuildRequestData requestData = new BuildRequestData(pi, new string[] { "CopyRunEnvironmentFiles" });
2977                 BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData);
2978                 BuildResult br = submission.Execute();
2979                 Assert.Equal(BuildResultCode.Success, br.OverallResult);
2980                 project2DoneEvent.Set();
2981             });
2982 
2983             project1DoneEvent.WaitOne();
2984             project2DoneEvent.WaitOne();
2985 
2986             BuildManager.DefaultBuildManager.EndBuild();
2987         }
2988 
2989         /// <summary>
2990         /// Verify that we can submit multiple simultaneous submissions with
2991         /// legacy threading mode active and successfully build, and that one of those
2992         /// submissions can P2P to the other.
2993         ///
2994         /// A variation of the above test, where multiple nodes are available, so the
2995         /// submissions aren't restricted to running strictly serially by the single in-proc
2996         /// node.
2997         /// </summary>
2998 #if MONO
2999         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1245")]
3000 #else
3001         [Fact]
3002 #endif
TestSimultaneousSubmissionsWithLegacyThreadingData_P2P_MP()3003         public void TestSimultaneousSubmissionsWithLegacyThreadingData_P2P_MP()
3004         {
3005             string projectContent1 = @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3006     <Target Name=`CopyRunEnvironmentFiles`>
3007         <!-- Wait 100 ms -->
3008         <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(100)) + @"` />
3009     </Target>
3010 
3011     <Target Name=`MyConsoleTarget`>
3012         <!-- Wait 100 ms -->
3013         <Exec Command=`" + Helpers.GetSleepCommand(TimeSpan.FromMilliseconds(100)) + @"` />
3014     </Target>
3015 
3016 </Project>
3017 ";
3018 
3019             var projectPath1 = _env.CreateFile(".proj").Path;
3020             File.WriteAllText(projectPath1, CleanupFileContents(projectContent1));
3021 
3022             Project project1 = new Project(projectPath1);
3023 
3024             string projectContent2 = @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3025     <Target Name=`MSDeployPublish` />
3026 
3027     <Target Name=`DoStuff` AfterTargets=`MSDeployPublish`>
3028         <MSBuild Projects=`" + projectPath1 + @"` Targets=`MyConsoleTarget` />
3029     </Target>
3030 
3031 </Project>
3032 ";
3033 
3034             var projectPath2 = _env.CreateFile(".proj").Path;
3035             File.WriteAllText(projectPath2, CleanupFileContents(projectContent2));
3036 
3037             Project project2 = new Project(projectPath2);
3038 
3039             ConsoleLogger cl = new ConsoleLogger();
3040             BuildParameters buildParameters = new BuildParameters(ProjectCollection.GlobalProjectCollection);
3041             buildParameters.Loggers = new ILogger[] { cl };
3042             buildParameters.LegacyThreadingSemantics = true;
3043             buildParameters.MaxNodeCount = 2;
3044             buildParameters.EnableNodeReuse = false;
3045             BuildManager.DefaultBuildManager.BeginBuild(buildParameters);
3046 
3047             AutoResetEvent project1DoneEvent = new AutoResetEvent(false);
3048             ThreadPool.QueueUserWorkItem(delegate
3049             {
3050                 // need to kick off project 2 first so that it project 1 can get submitted before the P2P happens
3051                 ProjectInstance pi = BuildManager.DefaultBuildManager.GetProjectInstanceForBuild(project2);
3052 
3053                 BuildRequestData requestData = new BuildRequestData(pi, new string[] { "MSDeployPublish" });
3054                 BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData);
3055                 BuildResult br = submission.Execute();
3056                 Assert.Equal(BuildResultCode.Success, br.OverallResult);
3057                 project1DoneEvent.Set();
3058             });
3059 
3060             AutoResetEvent project2DoneEvent = new AutoResetEvent(false);
3061             ThreadPool.QueueUserWorkItem(delegate
3062             {
3063                 ProjectInstance pi = BuildManager.DefaultBuildManager.GetProjectInstanceForBuild(project1);
3064                 BuildRequestData requestData = new BuildRequestData(pi, new string[] { "CopyRunEnvironmentFiles" });
3065                 BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData);
3066                 BuildResult br = submission.Execute();
3067                 Assert.Equal(BuildResultCode.Success, br.OverallResult);
3068                 project2DoneEvent.Set();
3069             });
3070 
3071             project1DoneEvent.WaitOne();
3072             project2DoneEvent.WaitOne();
3073 
3074             BuildManager.DefaultBuildManager.EndBuild();
3075         }
3076 
3077         /// <summary>
3078         /// Ensures that properties and items are transferred from an out-of-proc project to an in-proc project.
3079         /// </summary>
3080         /// <remarks>
3081         /// This differs from transferring a project instance to an out-of-proc node because in this case the project
3082         /// was loaded by MSBuild, not supplied directly by the user.
3083         /// </remarks>
3084         [Fact]
3085         [Trait("Category", "mono-osx-failing")]
Regress265010()3086         public void Regress265010()
3087         {
3088             string contents = CleanupFileContents(@"
3089 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
3090  <PropertyGroup>
3091    <Prop>BaseValue</Prop>
3092  </PropertyGroup>
3093 <ItemGroup>
3094   <Item Include='BaseItem'/>
3095 </ItemGroup>
3096  <Target Name='BaseTest'>
3097    <Message Text='[$(Prop)]'/>
3098    <Message Text='[@(Item)]'/>
3099     <PropertyGroup>
3100         <Prop>NewValue</Prop>
3101     </PropertyGroup>
3102     <ItemGroup>
3103         <Item Include='NewItem'/>
3104     </ItemGroup>
3105  </Target>
3106 
3107  <Target Name='MovedTest'>
3108    <Message Text='[$(Prop)]'/>
3109    <Message Text='[@(Item)]'/>
3110  </Target>
3111 
3112 </Project>
3113 ");
3114 
3115             string fileName = _env.CreateFile(".proj").Path;
3116             File.WriteAllText(fileName, contents);
3117             _buildManager.BeginBuild(_parameters);
3118 
3119             HostServices services = new HostServices();
3120             services.SetNodeAffinity(fileName, NodeAffinity.OutOfProc);
3121             BuildRequestData data = new BuildRequestData(fileName, new Dictionary<string, string>(), MSBuildDefaultToolsVersion, new[] { "BaseTest" }, services);
3122             _buildManager.PendBuildRequest(data).Execute();
3123             _logger.AssertLogContains("[BaseValue]");
3124             _logger.AssertLogContains("[BaseItem]");
3125             _logger.ClearLog();
3126 
3127             _parameters.ResetCaches = false;
3128             services.SetNodeAffinity(fileName, NodeAffinity.InProc);
3129             data = new BuildRequestData(fileName, new Dictionary<string, string>(), MSBuildDefaultToolsVersion, new[] { "MovedTest" }, services);
3130             _buildManager.PendBuildRequest(data).Execute();
3131             _logger.AssertLogContains("[NewValue]");
3132             _logger.AssertLogContains("[BaseItem;NewItem]");
3133             _logger.AssertLogDoesntContain("[BaseValue]");
3134 
3135             _buildManager.EndBuild();
3136         }
3137 
3138         /// <summary>
3139         /// Verifies that all warnings are treated as errors and that the overall build result is a failure.
3140         /// </summary>
3141         [Fact]
WarningsAreTreatedAsErrorsAll()3142         public void WarningsAreTreatedAsErrorsAll()
3143         {
3144             string contents = CleanupFileContents(@"
3145 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
3146  <Target Name='target1'>
3147     <Warning Text='This warning should be treated as an error' Code='ABC123'/>
3148     <Warning Text='This warning should NOT be treated as an error' />
3149  </Target>
3150 </Project>
3151 ");
3152             _parameters.WarningsAsErrors = new HashSet<string>();
3153 
3154             Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true);
3155             ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project);
3156             _buildManager.BeginBuild(_parameters);
3157             BuildResult result1 = _buildManager.BuildRequest(new BuildRequestData(instance, new[] { "target1" }));
3158             _buildManager.EndBuild();
3159 
3160             Assert.Equal(0, _logger.WarningCount);
3161             Assert.Equal(2, _logger.ErrorCount);
3162 
3163             Assert.Equal(BuildResultCode.Failure, result1.OverallResult);
3164             Assert.True(result1.HasResultsForTarget("target1"));
3165         }
3166 
3167         /// <summary>
3168         /// Verifies that only the specified warnings are treated as errors and that the overall build result is a failure.
3169         /// </summary>
3170         [Fact]
WarningsAreTreatedAsErrorsSpecific()3171         public void WarningsAreTreatedAsErrorsSpecific()
3172         {
3173             string contents = CleanupFileContents(@"
3174 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
3175  <Target Name='target1'>
3176     <Warning Text='This warning should be treated as an error' Code='ABC123'/>
3177     <Warning Text='This warning should NOT be treated as an error' Code='NA123' />
3178     <Warning Text='This warning should NOT be treated as an error' />
3179  </Target>
3180 </Project>
3181 ");
3182             _parameters.WarningsAsErrors = new HashSet<string> { "ABC123" };
3183 
3184             Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true);
3185             ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project);
3186             _buildManager.BeginBuild(_parameters);
3187             BuildResult result1 = _buildManager.BuildRequest(new BuildRequestData(instance, new string[] { "target1" }));
3188             _buildManager.EndBuild();
3189 
3190             Assert.Equal(2, _logger.WarningCount);
3191             Assert.Equal(1, _logger.ErrorCount);
3192 
3193             Assert.Equal(BuildResultCode.Failure, result1.OverallResult);
3194             Assert.True(result1.HasResultsForTarget("target1"));
3195         }
3196 
3197         /// <summary>
3198         /// Verifies that when building targets which emit warnings, they still show as succeeding but the overall build result is a failure.
3199         /// </summary>
3200         [Fact]
WarningsAreTreatedAsErrorsButTargetsStillSucceed()3201         public void WarningsAreTreatedAsErrorsButTargetsStillSucceed()
3202         {
3203             string contents = CleanupFileContents(@"
3204 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
3205 <Target Name='target1'>
3206     <Message Text='text'/>
3207  </Target>
3208  <Target Name='target2'>
3209     <Warning Text='This warning should be treated as an error' Code='ABC123'/>
3210  </Target>
3211 </Project>
3212 ");
3213             _parameters.WarningsAsErrors = new HashSet<string> { "ABC123" };
3214 
3215             Project project = CreateProject(contents, MSBuildDefaultToolsVersion, _projectCollection, true);
3216             ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project);
3217             _buildManager.BeginBuild(_parameters);
3218             BuildResult buildResult = _buildManager.BuildRequest(new BuildRequestData(instance, new string[] { "target1", "target2" }));
3219             _buildManager.EndBuild();
3220 
3221             Assert.Equal(0, _logger.WarningCount);
3222             Assert.Equal(1, _logger.ErrorCount);
3223 
3224             Assert.Equal(BuildResultCode.Failure, buildResult.OverallResult);
3225             Assert.True(buildResult.HasResultsForTarget("target1"));
3226             Assert.True(buildResult.HasResultsForTarget("target2"));
3227             // The two targets should still show as success because they don't know their warning was changed to an error
3228             // Logging a warning as an error does not change execution, only the final result of the build
3229             Assert.Equal(TargetResultCode.Success, buildResult.ResultsByTarget["target1"].ResultCode);
3230             Assert.Equal(TargetResultCode.Success, buildResult.ResultsByTarget["target2"].ResultCode);
3231         }
3232 
3233         /// <summary>
3234         /// Helper for cache tests.  Builds a project and verifies the right cache files are created.
3235         /// </summary>
BuildAndCheckCache(BuildManager localBuildManager, IEnumerable<string> exceptCacheDirectories)3236         private string BuildAndCheckCache(BuildManager localBuildManager, IEnumerable<string> exceptCacheDirectories)
3237         {
3238             string contents = CleanupFileContents(@"
3239 <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
3240  <Target Name='One' Outputs='one.txt'>
3241  </Target>
3242 
3243  <Target Name='Two' Outputs='two.txt'>
3244  </Target>
3245 
3246  <Target Name='Three' Outputs='three.txt'>
3247  </Target>
3248 </Project>
3249 ");
3250             string fileName = Path.GetTempFileName();
3251             File.WriteAllText(fileName, contents);
3252 
3253             string cacheDirectory = FileUtilities.GetCacheDirectory();
3254 
3255             BuildParameters parameters = new BuildParameters();
3256             localBuildManager.BeginBuild(parameters);
3257             try
3258             {
3259                 var services = new HostServices();
3260                 BuildRequestData data = new BuildRequestData(fileName, new Dictionary<string, string>(), MSBuildDefaultToolsVersion, new[] { "One", "Two", "Three" }, services);
3261                 var result = localBuildManager.PendBuildRequest(data).Execute();
3262                 Assert.Equal(result.OverallResult, BuildResultCode.Success); // "Test project failed to build correctly."
3263             }
3264             finally
3265             {
3266                 localBuildManager.EndBuild();
3267             }
3268 
3269             // Ensure that we got the cache files we expected.  There should be one set of results in there, once we exclude
3270             // any of the specified directories from previous builds in the same test.
3271             string directory = Directory.EnumerateDirectories(cacheDirectory).Except(exceptCacheDirectories).First();
3272 
3273             // Within this directory should be a set of target results files, one for each of the targets we invoked.
3274             var resultsFiles = Directory.EnumerateFiles(directory).Select(Path.GetFileName);
3275 
3276             Assert.Equal(3, resultsFiles.Count());
3277             Assert.True(resultsFiles.Contains("One.cache"));
3278             Assert.True(resultsFiles.Contains("Two.cache"));
3279             Assert.True(resultsFiles.Contains("Three.cache"));
3280 
3281             // Return the cache directory created for this build.
3282             return directory;
3283         }
3284 
3285         /// <summary>
3286         /// Extract a string dictionary from the property enumeration on a project started event.
3287         /// </summary>
ExtractProjectStartedPropertyList(IEnumerable properties)3288         private Dictionary<string, string> ExtractProjectStartedPropertyList(IEnumerable properties)
3289         {
3290             // Gather a sorted list of all the properties.
3291             return properties?.Cast<DictionaryEntry>()
3292                 .ToDictionary(prop => (string) prop.Key, prop => (string) prop.Value, StringComparer.OrdinalIgnoreCase);
3293         }
3294 
3295         /// <summary>
3296         /// Retrieves a BuildRequestData using the specified contents, default targets and an empty project collection.
3297         /// </summary>
GetBuildRequestData(string projectContents)3298         private BuildRequestData GetBuildRequestData(string projectContents)
3299         {
3300             return GetBuildRequestData(projectContents, new string[] { });
3301         }
3302 
3303         /// <summary>
3304         /// Retrieves a BuildRequestData using the specified contents, targets and project collection.
3305         /// </summary>
GetBuildRequestData(string projectContents, string[] targets, string toolsVersion = null)3306         private BuildRequestData GetBuildRequestData(string projectContents, string[] targets, string toolsVersion = null)
3307         {
3308             BuildRequestData data = new BuildRequestData(
3309                 CreateProjectInstance(projectContents, toolsVersion, _projectCollection, true), targets,
3310                 _projectCollection.HostServices);
3311 
3312             return data;
3313         }
3314 
3315         /// <summary>
3316         /// Retrieve a ProjectInstance evaluated with the specified contents using the specified projectCollection
3317         /// </summary>
CreateProjectInstance(string contents, string toolsVersion, ProjectCollection projectCollection, bool deleteTempProject)3318         private ProjectInstance CreateProjectInstance(string contents, string toolsVersion, ProjectCollection projectCollection, bool deleteTempProject)
3319         {
3320             Project project = CreateProject(contents, toolsVersion, projectCollection, deleteTempProject);
3321             return project.CreateProjectInstance();
3322         }
3323 
3324         /// <summary>
3325         /// Retrieve a Project with the specified contents using the specified projectCollection
3326         /// </summary>
CreateProject(string contents, string toolsVersion, ProjectCollection projectCollection, bool deleteTempProject)3327         private Project CreateProject(string contents, string toolsVersion, ProjectCollection projectCollection, bool deleteTempProject)
3328         {
3329             Project project = new Project(XmlReader.Create(new StringReader(contents)), null, toolsVersion, projectCollection)
3330             {
3331                 FullPath = _env.CreateFile().Path
3332             };
3333 
3334             if (!deleteTempProject)
3335             {
3336                 project.Save();
3337             }
3338 
3339             if (deleteTempProject)
3340             {
3341                 File.Delete(project.FullPath);
3342             }
3343 
3344             return project;
3345         }
3346 
3347         /// <summary>
3348         /// Generate dummy projects
3349         /// </summary>
GenerateDummyProjects(string shutdownProjectDirectory, int parallelProjectCount, ProjectCollection projectCollection)3350         private ProjectInstance GenerateDummyProjects(string shutdownProjectDirectory, int parallelProjectCount, ProjectCollection projectCollection)
3351         {
3352             Directory.CreateDirectory(shutdownProjectDirectory);
3353 
3354             // Generate the project.  It will have the following format.  Setting the AdditionalProperties
3355             // causes the projects to be built to be separate configs, which allows us to build the same project
3356             // a bunch of times in parallel.
3357             //
3358             // <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'/>
3359             //   <ItemGroup>
3360             //     <ProjectReference Include="RootProjectName.proj">
3361             //       <AdditionalProperties>p={incremented value}</AdditionalProperties>
3362             //     </ProjectReference>
3363             //     ...
3364             //   </ItemGroup>
3365             //
3366             //   <Target Name="Build">
3367             //     <MSBuild Projects="@(ProjectReference) Targets="ChildBuild" BuildInParallel="true" />
3368             //   </Target>
3369             //
3370             //   <Target Name="ChildBuild" />
3371             // </Project>
3372             string rootProjectPath = Path.Combine(shutdownProjectDirectory, String.Format(CultureInfo.InvariantCulture, "RootProj_{0}.proj", Guid.NewGuid().ToString("N")));
3373             ProjectRootElement rootProject = ProjectRootElement.Create(rootProjectPath, projectCollection);
3374 
3375             ProjectTargetElement buildTarget = rootProject.AddTarget("Build");
3376             ProjectTaskElement buildTask = buildTarget.AddTask("MSBuild");
3377             buildTask.SetParameter("Projects", "@(ProjectReference)");
3378             buildTask.SetParameter("BuildInParallel", "true");
3379             buildTask.SetParameter("Targets", "ChildBuild");
3380 
3381             rootProject.AddTarget("ChildBuild");
3382 
3383             IDictionary<string, string> metadata = new Dictionary<string, string>(1);
3384             for (int i = 0; i < parallelProjectCount; i++)
3385             {
3386                 // Add the ProjectReference item for this actual config.
3387                 metadata["AdditionalProperties"] = String.Format(CultureInfo.InvariantCulture, "p={0}", i);
3388                 rootProject.AddItem("ProjectReference", rootProjectPath, metadata);
3389             }
3390 
3391             rootProject.Save();
3392             return new ProjectInstance(rootProject);
3393         }
3394 
3395         [Fact]
3396         [Trait("Category", "mono-osx-failing")] // out-of-proc nodes not working on mono yet
ShouldBuildMutatedProjectInstanceWhoseProjectWasPreviouslyBuiltAsAP2PDependency()3397         public void ShouldBuildMutatedProjectInstanceWhoseProjectWasPreviouslyBuiltAsAP2PDependency()
3398         {
3399             var mainProjectContents =
3400 @"<Project>
3401 
3402   <Target Name=""BuildOther"" Outputs=""@(ReturnValue)"">
3403     <MSBuild Projects=""{0}"" Targets=""Foo"">
3404       <Output TaskParameter=""TargetOutputs"" ItemName=""ReturnValue"" />
3405     </MSBuild>
3406   </Target>
3407 
3408 </Project>";
3409 
3410 
3411             var p2pProjectContents =
3412 @"<Project>
3413 
3414   <PropertyGroup>
3415     <P>InitialValue</P>
3416   </PropertyGroup>
3417 
3418   <Target Name=""Foo"" Outputs=""$(P)""/>
3419 
3420 </Project>";
3421 
3422             using (var env = TestEnvironment.Create())
3423             using (var collection = new ProjectCollection())
3424             using (var manager = new BuildManager())
3425             {
3426                 try
3427                 {
3428                     var testFiles = env.CreateTestProjectWithFiles(string.Empty, new[] { "p2p", "main" });
3429                     var p2pProjectPath = testFiles.CreatedFiles[0];
3430                     File.WriteAllText(p2pProjectPath, p2pProjectContents);
3431 
3432                     var mainRootElement = ProjectRootElement.Create(XmlReader.Create(new StringReader(string.Format(mainProjectContents, p2pProjectPath))), collection);
3433 
3434                     mainRootElement.FullPath = testFiles.CreatedFiles[1];
3435                     mainRootElement.Save();
3436 
3437                     // build p2p project as a real p2p dependency of some other project. This loads the p2p into msbuild's caches
3438 
3439                     var mainProject = new Project(mainRootElement, new Dictionary<string, string>(), MSBuildConstants.CurrentToolsVersion, collection);
3440                     var mainInstance = mainProject.CreateProjectInstance(ProjectInstanceSettings.Immutable).DeepCopy(isImmutable: false);
3441 
3442                     Assert.Equal(0, mainInstance.GlobalProperties.Count);
3443 
3444                     var request = new BuildRequestData(mainInstance, new[] {"BuildOther"});
3445 
3446                     var parameters = new BuildParameters
3447                     {
3448                         DisableInProcNode = true,
3449                         EnableNodeReuse = false,
3450                     };
3451 
3452                     manager.BeginBuild(parameters);
3453 
3454                     var submission = manager.PendBuildRequest(request);
3455 
3456                     var results = submission.Execute();
3457                     Assert.Equal(BuildResultCode.Success, results.OverallResult);
3458                     Assert.Equal("InitialValue", results.ResultsByTarget["BuildOther"].Items.First().ItemSpec);
3459 
3460                     // build p2p directly via mutated ProjectInstances based of the same Project.
3461                     // This should rebuild and the result should reflect the in-memory changes and not reuse stale cache info
3462 
3463                     var p2pProject = new Project(p2pProjectPath, new Dictionary<string, string>(), MSBuildConstants.CurrentToolsVersion, collection);
3464 
3465                     for (var i = 0; i < 2; i++)
3466                     {
3467                         var p2pInstance = p2pProject.CreateProjectInstance(ProjectInstanceSettings.Immutable).DeepCopy(isImmutable: false);
3468 
3469                         var newPropertyValue = $"NewValue_{i}";
3470 
3471                         p2pInstance.SetProperty("P", newPropertyValue);
3472 
3473                         request = new BuildRequestData(p2pInstance, new[] {"Foo"});
3474                         submission = manager.PendBuildRequest(request);
3475                         results = submission.Execute();
3476 
3477                         Assert.Equal(0, p2pInstance.GlobalProperties.Count);
3478 
3479                         Assert.Equal(BuildResultCode.Success, results.OverallResult);
3480                         Assert.Equal(newPropertyValue, results.ResultsByTarget["Foo"].Items.First().ItemSpec);
3481                     }
3482                 }
3483                 finally
3484                 {
3485                     manager.EndBuild();
3486                 }
3487             }
3488         }
3489 
3490         [Fact]
3491         [Trait("Category", "mono-osx-failing")] // out-of-proc nodes not working on mono yet
OutOfProcFileBasedP2PBuildSucceeds()3492         public void OutOfProcFileBasedP2PBuildSucceeds()
3493         {
3494             var mainProject =
3495                 @"<Project>
3496 
3497   <Target Name=`MainTarget` Returns=`foo;@(P2PReturnValue)`>
3498     <MSBuild Projects=`{0}` Targets=`P2PTarget`>
3499       <Output TaskParameter=`TargetOutputs` ItemName=`P2PReturnValue` />
3500     </MSBuild>
3501   </Target>
3502 
3503 </Project>";
3504 
3505             var p2pProject =
3506                 @"<Project>
3507 
3508   <Target Name=`P2PTarget` Returns=`bar`>
3509     <Message Text=`Bar` Importance=`High` />
3510   </Target>
3511 
3512 </Project>";
3513             var testFiles = _env.CreateTestProjectWithFiles(string.Empty, new[] {"main", "p2p"}, string.Empty);
3514 
3515             var buildParameters = new BuildParameters(_projectCollection)
3516             {
3517                 DisableInProcNode = true,
3518                 EnableNodeReuse = false,
3519                 Loggers = new ILogger[] {_logger}
3520             };
3521 
3522             _buildManager.BeginBuild(buildParameters);
3523 
3524             try
3525             {
3526                 var p2pProjectPath = testFiles.CreatedFiles[1];
3527                 var cleanedUpP2pContents = CleanupFileContents(p2pProject);
3528                 File.WriteAllText(p2pProjectPath, cleanedUpP2pContents);
3529 
3530                 var mainProjectPath = testFiles.CreatedFiles[0];
3531                 var cleanedUpMainContents = CleanupFileContents(string.Format(mainProject, p2pProjectPath));
3532                 File.WriteAllText(mainProjectPath, cleanedUpMainContents);
3533 
3534 
3535                 var buildRequestData = new BuildRequestData(
3536                     mainProjectPath,
3537                     new Dictionary<string, string>(),
3538                     MSBuildConstants.CurrentToolsVersion,
3539                     new[] {"MainTarget"},
3540                     null
3541                 );
3542 
3543                 var submission = _buildManager.PendBuildRequest(buildRequestData);
3544 
3545                 var result = submission.Execute();
3546 
3547                 Assert.Equal(BuildResultCode.Success, result.OverallResult);
3548                 Assert.Equal("foo;bar",
3549                     string.Join(";", result.ResultsByTarget["MainTarget"].Items.Select(i => i.ItemSpec)));
3550             }
3551             finally
3552             {
3553                 _buildManager.EndBuild();
3554             }
3555         }
3556 
3557         /// When a ProjectInstance based BuildRequestData is built out of proc, the node should
3558         /// not reload it from disk but instead fully utilize the entire translate project instance state
3559         /// to do the build
3560         [Theory]
3561         [InlineData(false)]
3562         [InlineData(true)]
3563         [Trait("Category", "mono-osx-failing")] // out-of-proc nodes not working on mono yet
OutOfProcProjectInstanceBasedBuildDoesNotReloadFromDisk(bool shouldSerializeEntireState)3564         public void OutOfProcProjectInstanceBasedBuildDoesNotReloadFromDisk(bool shouldSerializeEntireState)
3565         {
3566             var mainProject =
3567                 @"<Project>
3568   <PropertyGroup>
3569     <ImportIt>true</ImportIt>
3570   </PropertyGroup>
3571 
3572   <Import Project=""{0}"" Condition=""'$(ImportIt)' == 'true'""/>
3573 
3574 </Project>";
3575 
3576             var importProject =
3577                 @"<Project>
3578   <Target Name=""Foo"">
3579     <Message Text=""Bar"" Importance=""High"" />
3580   </Target>
3581 </Project>";
3582 
3583             var testFiles = _env.CreateTestProjectWithFiles(string.Empty, new[] {"main", "import"}, string.Empty);
3584 
3585             try
3586             {
3587                 var importPath = testFiles.CreatedFiles[1];
3588                 File.WriteAllText(importPath, CleanupFileContents(importProject));
3589 
3590                 var root = ProjectRootElement.Create(
3591                     XmlReader.Create(new StringReader(string.Format(mainProject, importPath))), _projectCollection);
3592                 root.FullPath = Path.GetTempFileName();
3593                 root.Save();
3594 
3595                 // build a project which runs a target from an imported file
3596 
3597                 var project = new Project(root, new Dictionary<string, string>(), MSBuildConstants.CurrentToolsVersion,
3598                     _projectCollection);
3599                 var instance = project.CreateProjectInstance(ProjectInstanceSettings.Immutable).DeepCopy(false);
3600 
3601                 instance.TranslateEntireState = shouldSerializeEntireState;
3602 
3603                 var request = new BuildRequestData(instance, new[] {"Foo"});
3604 
3605 
3606                 var parameters = new BuildParameters(_projectCollection)
3607                 {
3608                     DisableInProcNode = true,
3609                     EnableNodeReuse = false,
3610                     Loggers = new ILogger[] {_logger}
3611                 };
3612 
3613                 _buildManager.BeginBuild(parameters);
3614 
3615 
3616                 var submission = _buildManager.PendBuildRequest(request);
3617 
3618                 var results = submission.Execute();
3619 
3620                 Assert.True(results.OverallResult == BuildResultCode.Success);
3621 
3622                 // reset caches to ensure nothing is reused
3623 
3624                 _buildManager.EndBuild();
3625                 _buildManager.ResetCaches();
3626 
3627                 // mutate the file on disk such that the import (containing the target to get executed)
3628                 // is no longer imported
3629 
3630                 project.SetProperty("ImportIt", "false");
3631                 project.Save();
3632 
3633                 // Build the initial project instance again.
3634                 // The project instance is not in sync with the file anymore, making it an in-memory build:
3635                 // the file does not contain the target Foo, but the project instance does
3636                 // Building the stale project instance should still succeed when the entire state is translated: MSBuild should use the
3637                 // in-memory state to build and not reload from disk.
3638 
3639                 _buildManager.BeginBuild(parameters);
3640                 request = new BuildRequestData(instance, new[] {"Foo"}, null,
3641                     BuildRequestDataFlags.ReplaceExistingProjectInstance);
3642                 submission = _buildManager.PendBuildRequest(request);
3643 
3644 
3645                 results = submission.Execute();
3646 
3647                 if (shouldSerializeEntireState)
3648                 {
3649                     Assert.Equal(BuildResultCode.Success, results.OverallResult);
3650                 }
3651                 else
3652                 {
3653                     Assert.Equal(BuildResultCode.Failure, results.OverallResult);
3654                     Assert.Contains("The target \"Foo\" does not exist in the project", _logger.FullLog,
3655                         StringComparison.OrdinalIgnoreCase);
3656                 }
3657             }
3658             finally
3659             {
3660                 _buildManager.EndBuild();
3661             }
3662         }
3663 
3664         [Fact]
3665         [Trait("Category", "mono-osx-failing")] // out-of-proc nodes not working on mono yet
OutOfProcEvaluationIdsUnique()3666         public void OutOfProcEvaluationIdsUnique()
3667         {
3668             var mainProject =
3669                 @"<Project>
3670 
3671   <Target Name=`MainTarget`>
3672     <MSBuild Projects=`{0};{1}` Targets=`DummyTarget` />
3673   </Target>
3674 
3675 </Project>";
3676 
3677             var childProject =
3678                 @"<Project>
3679 
3680   <Target Name=`DummyTarget`>
3681     <Message Text=`Bar` Importance=`High` />
3682   </Target>
3683 
3684 </Project>";
3685 
3686             var testFiles = _env.CreateTestProjectWithFiles(string.Empty, new[] { "main", "child1", "child2" }, string.Empty);
3687 
3688             var buildParameters = new BuildParameters(_projectCollection)
3689             {
3690                 DisableInProcNode = true,
3691                 EnableNodeReuse = false,
3692                 Loggers = new ILogger[] { _logger }
3693             };
3694 
3695             _buildManager.BeginBuild(buildParameters);
3696 
3697             try
3698             {
3699                 var child1ProjectPath = testFiles.CreatedFiles[1];
3700                 var child2ProjectPath = testFiles.CreatedFiles[2];
3701                 var cleanedUpChildContents = CleanupFileContents(childProject);
3702                 File.WriteAllText(child1ProjectPath, cleanedUpChildContents);
3703                 File.WriteAllText(child2ProjectPath, cleanedUpChildContents);
3704 
3705                 var mainProjectPath = testFiles.CreatedFiles[0];
3706                 var cleanedUpMainContents = CleanupFileContents(string.Format(mainProject, child1ProjectPath, child2ProjectPath));
3707                 File.WriteAllText(mainProjectPath, cleanedUpMainContents);
3708 
3709                 var buildRequestData = new BuildRequestData(
3710                     mainProjectPath,
3711                     new Dictionary<string, string>(),
3712                     MSBuildConstants.CurrentToolsVersion,
3713                     new[] { "MainTarget" },
3714                     null
3715                 );
3716 
3717                 var submission = _buildManager.PendBuildRequest(buildRequestData);
3718 
3719                 var result = submission.Execute();
3720 
3721                 Assert.Equal(BuildResultCode.Success, result.OverallResult);
3722                 Assert.True(_logger.AllBuildEvents.OfType<ProjectEvaluationStartedEventArgs>().GroupBy(args => args.BuildEventContext.EvaluationId).All(g => g.Count() == 1));
3723             }
3724             finally
3725             {
3726                 _buildManager.EndBuild();
3727             }
3728         }
3729 
3730         /// <summary>
3731         /// Regression test for https://github.com/Microsoft/msbuild/issues/3047
3732         /// </summary>
3733         [Fact]
3734         [Trait("Category", "mono-osx-failing")] // out-of-proc nodes not working on mono yet
MultiProcReentrantProjectWithCallTargetDoesNotFail()3735         public void MultiProcReentrantProjectWithCallTargetDoesNotFail()
3736         {
3737             var a =
3738                 @"<Project>
3739                      <Target Name=`EntryTarget`>
3740                          <MSBuild Projects=`b;c` BuildInParallel=`true` />
3741                      </Target>
3742                  </Project>".Cleanup();
3743 
3744             var b =
3745                 @"<Project>
3746                      <Target Name=`BTarget`>
3747                          <MSBuild Projects=`reentrant` Targets=`BuildGenerateSources` BuildInParallel=`true` />
3748                      </Target>
3749                  </Project>".Cleanup();
3750 
3751             var c =
3752                 $@"<Project>
3753                      <Target Name=`CTarget`>
3754                          <Exec Command=`{Helpers.GetSleepCommand(TimeSpan.FromSeconds(1))}` />
3755                          <MSBuild Projects=`reentrant` Targets=`BuildGenerated` BuildInParallel=`true` />
3756                      </Target>
3757                  </Project>".Cleanup();
3758 
3759             var delay =
3760                 $@"<Project>
3761                      <Target Name=`Delay`>
3762                          <Exec Command=`{Helpers.GetSleepCommand(TimeSpan.FromSeconds(2))}` />
3763                      </Target>
3764                  </Project>".Cleanup();
3765 
3766             var reentrant =
3767                 $@"<Project DefaultTargets=`Build`>
3768                      <Target Name=`BuildGenerateSources` DependsOnTargets=`_Get;Build`></Target>
3769                      <Target Name=`BuildGenerated`>
3770                          <CallTarget Targets=`Build` />
3771                      </Target>
3772                      <Target Name=`Build`>
3773                          <CallTarget Targets=`_Get` />
3774                      </Target>
3775                      <Target Name=`_Get`>
3776                          <MSBuild Projects=`delay` BuildInParallel=`true` />
3777                          <Exec Command=`{Helpers.GetSleepCommand(TimeSpan.FromSeconds(5))}` YieldDuringToolExecution=`true` StandardOutputImportance=`low` />
3778                      </Target>
3779                  </Project>".Cleanup();
3780 
3781             using (var env = TestEnvironment.Create(_output))
3782             {
3783                 var entryFile = env.CreateFile(nameof(a), a).Path;
3784                 env.CreateFile(nameof(b), b);
3785                 env.CreateFile(nameof(c), c);
3786                 env.CreateFile(nameof(delay), delay);
3787                 env.CreateFile(nameof(reentrant), reentrant);
3788 
3789                 var mockLogger = new MockLogger(_output);
3790 
3791                 var buildParameters = new BuildParameters()
3792                 {
3793                     DisableInProcNode = true,
3794                     MaxNodeCount = Environment.ProcessorCount,
3795                     EnableNodeReuse = false,
3796                     Loggers = new List<ILogger>()
3797                     {
3798                         mockLogger
3799                     }
3800                 };
3801 
3802                 var buildRequestData = new BuildRequestData(entryFile, new Dictionary<string, string>(), MSBuildDefaultToolsVersion, new[]{ "EntryTarget" }, null);
3803 
3804                 var result = _buildManager.Build(buildParameters, buildRequestData);
3805 
3806                 result.OverallResult.ShouldBe(BuildResultCode.Success);
3807             }
3808         }
3809     }
3810 }
3811