1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 
4 using System;
5 using System.IO;
6 using System.Reflection;
7 using System.Collections;
8 using Microsoft.Build.Evaluation;
9 using Microsoft.Build.Framework;
10 using Microsoft.Build.Tasks;
11 using Microsoft.Build.Utilities;
12 using System.Text.RegularExpressions;
13 
14 using Microsoft.Build.Shared;
15 using Shouldly;
16 using Xunit;
17 
18 namespace Microsoft.Build.UnitTests
19 {
20     sealed public class MSBuildTask_Tests : IDisposable
21     {
MSBuildTask_Tests()22         public MSBuildTask_Tests()
23         {
24             ProjectCollection.GlobalProjectCollection.UnloadAllProjects();
25         }
26 
Dispose()27         public void Dispose()
28         {
29             ProjectCollection.GlobalProjectCollection.UnloadAllProjects();
30         }
31 
32         /// <summary>
33         /// If we pass in an item spec that is over the max path but it can be normalized down to something under the max path, we should still work and not
34         /// throw a path too long exception
35         /// </summary>
36         [Fact]
ProjectItemSpecTooLong()37         public void ProjectItemSpecTooLong()
38         {
39             string currentDirectory = Directory.GetCurrentDirectory();
40             try
41             {
42                 Directory.SetCurrentDirectory(Path.GetTempPath());
43 
44                 string tempPath = Path.GetTempPath();
45 
46                 string tempProject = ObjectModelHelpers.CreateTempFileOnDisk(@"
47                 <Project DefaultTargets=`TargetA; TargetB; TargetC` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
48 
49                     <Target Name=`TargetA` Outputs=`a1.dll`/>
50                     <Target Name=`TargetB` Outputs=`b1.dll; b2.dll`/>
51                     <Target Name=`TargetC` Outputs=`@(C_Outputs)`>
52                         <CreateItem Include=`c1.dll` AdditionalMetadata=`MSBuildSourceProjectFile=birch; MSBuildSourceTargetName=oak`>
53                             <Output ItemName=`C_Outputs` TaskParameter=`Include`/>
54                         </CreateItem>
55                     </Target>
56                 </Project>
57                 ");
58 
59                 string fileName = Path.GetFileName(tempProject);
60 
61                 string projectFile1 = null;
62                 for (int i = 0; i < 250; i++)
63                 {
64                     projectFile1 += "..\\";
65                 }
66 
67                 int rootLength = Path.GetPathRoot(tempPath).Length;
68                 string tempPathNoRoot = tempPath.Substring(rootLength);
69 
70                 projectFile1 += Path.Combine(tempPathNoRoot, fileName);
71                 try
72                 {
73                     MSBuild msbuildTask = new MSBuild();
74                     msbuildTask.BuildEngine = new MockEngine();
75 
76                     msbuildTask.Projects = new ITaskItem[] { new TaskItem(projectFile1) };
77 
78                     bool success = msbuildTask.Execute();
79                     Assert.True(success); // "Build failed.  See 'Standard Out' tab for details."
80                 }
81                 finally
82                 {
83                     File.Delete(tempProject);
84                 }
85             }
86             finally
87             {
88                 Directory.SetCurrentDirectory(currentDirectory);
89             }
90         }
91 
92         /// <summary>
93         /// Ensure that the MSBuild task tags any output items with two pieces of metadata -- MSBuildSourceProjectFile and
94         /// MSBuildSourceTargetName  -- that give an indication of where the items came from.
95         /// </summary>
96         [Fact]
OutputItemsAreTaggedWithProjectFileAndTargetName()97         public void OutputItemsAreTaggedWithProjectFileAndTargetName()
98         {
99             string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
100                 <Project DefaultTargets=`TargetA; TargetB; TargetC` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
101 
102                     <Target Name=`TargetA` Outputs=`a1.dll`/>
103                     <Target Name=`TargetB` Outputs=`b1.dll; b2.dll`/>
104                     <Target Name=`TargetC` Outputs=`@(C_Outputs)`>
105                         <CreateItem Include=`c1.dll`>
106                             <Output ItemName=`C_Outputs` TaskParameter=`Include`/>
107                         </CreateItem>
108                     </Target>
109                 </Project>
110                 ");
111 
112             string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
113                 <Project DefaultTargets=`TargetG; TargetH` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
114                     <Target Name=`TargetG` Outputs=`g1.dll; g2.dll`/>
115                     <Target Name=`TargetH` Outputs=`h1.dll`/>
116                 </Project>
117                 ");
118 
119             try
120             {
121                 MSBuild msbuildTask = new MSBuild();
122                 msbuildTask.BuildEngine = new MockEngine();
123 
124                 msbuildTask.Projects = new ITaskItem[] { new TaskItem(projectFile1), new TaskItem(projectFile2) };
125 
126                 bool success = msbuildTask.Execute();
127                 Assert.True(success); // "Build failed.  See 'Standard Out' tab for details."
128 
129                 string expectedItemOutputs = string.Format(@"
130                     a1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetA
131                     b1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetB
132                     b2.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetB
133                     c1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetC
134                     g1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetG
135                     g2.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetG
136                     h1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetH
137                     ", projectFile1, projectFile2);
138 
139                 ObjectModelHelpers.AssertItemsMatch(expectedItemOutputs, msbuildTask.TargetOutputs, false /* order of items not enforced */);
140             }
141             finally
142             {
143                 File.Delete(projectFile1);
144                 File.Delete(projectFile2);
145             }
146         }
147 
148         /// <summary>
149         /// Ensures that it is possible to call the MSBuild task
150         /// with an empty Projects parameter, and it shouldn't error, and it shouldn't try to
151         /// build itself.
152         /// </summary>
153         [Fact]
EmptyProjectsParameterResultsInNoop()154         public void EmptyProjectsParameterResultsInNoop()
155         {
156             string projectContents = ObjectModelHelpers.CleanupFileContents(@"
157                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
158                     <Target Name=`t` >
159 	                    <MSBuild Projects=` @(empty) ` />
160                     </Target>
161                 </Project>
162                 ");
163 
164             MockLogger logger = new MockLogger();
165             Project project = ObjectModelHelpers.CreateInMemoryProject(projectContents, logger);
166 
167             bool success = project.Build();
168             Assert.True(success); // "Build failed.  See Standard Out tab for details"
169         }
170 
171         /// <summary>
172         /// Verifies that nonexistent projects aren't normally skipped
173         /// </summary>
174         [Fact]
NormallyDoNotSkipNonexistentProjects()175         public void NormallyDoNotSkipNonexistentProjects()
176         {
177             ObjectModelHelpers.DeleteTempProjectDirectory();
178             ObjectModelHelpers.CreateFileInTempProjectDirectory(
179                 "SkipNonexistentProjectsMain.csproj",
180                 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
181                     <Target Name=`t` >
182 	                    <MSBuild Projects=`this_project_does_not_exist.csproj` />
183                     </Target>
184                 </Project>
185                 ");
186 
187             MockLogger logger = ObjectModelHelpers.BuildTempProjectFileExpectFailure(@"SkipNonexistentProjectsMain.csproj");
188             Assert.True(logger.FullLog.Contains("MSB3202")); // project file not found
189         }
190 
191         /// <summary>
192         /// Verifies that nonexistent projects aren't normally skipped
193         /// </summary>
194         [Fact]
NormallyDoNotSkipNonexistentProjectsBuildInParallel()195         public void NormallyDoNotSkipNonexistentProjectsBuildInParallel()
196         {
197             ObjectModelHelpers.DeleteTempProjectDirectory();
198             ObjectModelHelpers.CreateFileInTempProjectDirectory(
199                 "SkipNonexistentProjectsMain.csproj",
200                 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
201                     <Target Name=`t` >
202 	                    <MSBuild Projects=`this_project_does_not_exist.csproj` BuildInParallel=`true`/>
203                     </Target>
204                 </Project>
205                 ");
206 
207             MockLogger logger = ObjectModelHelpers.BuildTempProjectFileExpectFailure(@"SkipNonexistentProjectsMain.csproj");
208             Assert.Equal(0, logger.WarningCount);
209             Assert.Equal(1, logger.ErrorCount);
210             Assert.True(logger.FullLog.Contains("MSB3202")); // project file not found
211         }
212 
213         /// <summary>
214         /// Verifies that nonexistent projects are skipped when requested
215         /// </summary>
216         [Fact]
SkipNonexistentProjects()217         public void SkipNonexistentProjects()
218         {
219             ObjectModelHelpers.DeleteTempProjectDirectory();
220             ObjectModelHelpers.CreateFileInTempProjectDirectory(
221                 "SkipNonexistentProjectsMain.csproj",
222                 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
223                     <Target Name=`t` >
224 	                    <MSBuild Projects=`this_project_does_not_exist.csproj;foo.csproj` SkipNonexistentProjects=`true` />
225                     </Target>
226                 </Project>
227                 ");
228 
229             ObjectModelHelpers.CreateFileInTempProjectDirectory(
230                 "foo.csproj",
231                 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
232                     <Target Name=`t` >
233 	                    <Message Text=`Hello from foo.csproj`/>
234                     </Target>
235                 </Project>
236                 ");
237 
238             MockLogger logger = ObjectModelHelpers.BuildTempProjectFileExpectSuccess(@"SkipNonexistentProjectsMain.csproj");
239 
240             logger.AssertLogContains("Hello from foo.csproj");
241             Assert.Equal(0, logger.WarningCount);
242             Assert.Equal(0, logger.ErrorCount);
243             Assert.True(logger.FullLog.Contains("this_project_does_not_exist.csproj")); // for the missing project
244             Assert.False(logger.FullLog.Contains("MSB3202")); // project file not found error
245         }
246 
247         /// <summary>
248         /// Verifies that nonexistent projects are skipped when requested when building in parallel.
249         /// DDB # 125831
250         /// </summary>
251         [Fact]
SkipNonexistentProjectsBuildingInParallel()252         public void SkipNonexistentProjectsBuildingInParallel()
253         {
254             ObjectModelHelpers.DeleteTempProjectDirectory();
255             ObjectModelHelpers.CreateFileInTempProjectDirectory(
256                 "SkipNonexistentProjectsMain.csproj",
257                 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
258                     <Target Name=`t` >
259 	                    <MSBuild Projects=`this_project_does_not_exist.csproj;foo.csproj` SkipNonexistentProjects=`true` BuildInParallel=`true` />
260                     </Target>
261                 </Project>
262                 ");
263 
264             ObjectModelHelpers.CreateFileInTempProjectDirectory(
265                 "foo.csproj",
266                 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
267                     <Target Name=`t` >
268 	                    <Message Text=`Hello from foo.csproj`/>
269                     </Target>
270                 </Project>
271                 ");
272 
273             MockLogger logger = ObjectModelHelpers.BuildTempProjectFileExpectSuccess(@"SkipNonexistentProjectsMain.csproj");
274 
275             logger.AssertLogContains("Hello from foo.csproj");
276             Assert.Equal(0, logger.WarningCount);
277             Assert.Equal(0, logger.ErrorCount);
278             Assert.True(logger.FullLog.Contains("this_project_does_not_exist.csproj")); // for the missing project
279             Assert.False(logger.FullLog.Contains("MSB3202")); // project file not found error
280         }
281 
282         [Fact]
LogErrorWhenBuildingVCProj()283         public void LogErrorWhenBuildingVCProj()
284         {
285             ObjectModelHelpers.DeleteTempProjectDirectory();
286             ObjectModelHelpers.CreateFileInTempProjectDirectory(
287                 "BuildingVCProjMain.csproj",
288                 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
289                     <Target Name=`t` >
290 	                    <MSBuild Projects=`blah.vcproj;foo.csproj` StopOnFirstFailure=`false` />
291                     </Target>
292                 </Project>
293                 ");
294 
295             ObjectModelHelpers.CreateFileInTempProjectDirectory(
296                 "foo.csproj",
297                 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
298                     <Target Name=`t` >
299 	                    <Message Text=`Hello from foo.csproj`/>
300                     </Target>
301                 </Project>
302                 ");
303 
304             ObjectModelHelpers.CreateFileInTempProjectDirectory(
305                 "blah.vcproj",
306                 @"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
307                     <NotWellFormedMSBuildFormatTag />
308                     <Target Name=`t` >
309 	                    <Message Text=`Hello from blah.vcproj`/>
310                     </Target>
311                 </Project>
312                 ");
313 
314             MockLogger logger = ObjectModelHelpers.BuildTempProjectFileExpectFailure(@"BuildingVCProjMain.csproj");
315 
316             logger.AssertLogContains("Hello from foo.csproj");
317             Assert.Equal(0, logger.WarningCount);
318             Assert.Equal(1, logger.ErrorCount);
319             Assert.True(logger.FullLog.Contains("MSB3204")); // upgrade to vcxproj needed
320         }
321 
322         /// <summary>
323         /// Calling the MSBuild task, passing in a property
324         /// in the Properties parameter that has a special character in its value, such as semicolon.
325         /// However, it's a situation where the project author doesn't have control over the
326         /// property value and so he can't escape it himself.
327         /// </summary>
328 #if RUNTIME_TYPE_NETCORE
329         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/259")]
330 #else
331         [Fact]
332 #endif
333         [Trait("Category", "mono-osx-failing")]
PropertyOverridesContainSemicolon()334         public void PropertyOverridesContainSemicolon()
335         {
336             ObjectModelHelpers.DeleteTempProjectDirectory();
337 
338             // -------------------------------------------------------
339             // ConsoleApplication1.csproj
340             // -------------------------------------------------------
341 
342             // Just a normal console application project.
343             ObjectModelHelpers.CreateFileInTempProjectDirectory(
344                 Path.Combine("bug'533'369", "Sub;Dir", "ConsoleApplication1", "ConsoleApplication1.csproj"), @"
345 
346                 <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
347                   <PropertyGroup>
348                     <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
349                     <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
350                     <OutputType>Exe</OutputType>
351                     <AssemblyName>ConsoleApplication1</AssemblyName>
352                   </PropertyGroup>
353                   <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' `>
354                     <DebugSymbols>true</DebugSymbols>
355                     <DebugType>full</DebugType>
356                     <Optimize>false</Optimize>
357                     <OutputPath>bin\Debug\</OutputPath>
358                   </PropertyGroup>
359                   <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Release|AnyCPU' `>
360                     <DebugType>pdbonly</DebugType>
361                     <Optimize>true</Optimize>
362                     <OutputPath>bin\Release\</OutputPath>
363                   </PropertyGroup>
364                   <ItemGroup>
365                     <Reference Include=`System` />
366                     <Reference Include=`System.Data` />
367                     <Reference Include=`System.Xml` />
368                   </ItemGroup>
369                   <ItemGroup>
370                     <Compile Include=`Program.cs` />
371                   </ItemGroup>
372                   <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
373                 </Project>
374                 ");
375 
376             // -------------------------------------------------------
377             // Program.cs
378             // -------------------------------------------------------
379 
380             // Just a normal console application project.
381             ObjectModelHelpers.CreateFileInTempProjectDirectory(
382                 Path.Combine("bug'533'369", "Sub;Dir", "ConsoleApplication1", "Program.cs"), @"
383                 using System;
384 
385                 namespace ConsoleApplication32
386                 {
387                     class Program
388                     {
389                         static void Main(string[] args)
390                         {
391                             Console.WriteLine(`Hello world`);
392                         }
393                     }
394                 }
395                 ");
396 
397 
398             // -------------------------------------------------------
399             // TeamBuild.proj
400             // -------------------------------------------------------
401             // Attempts to build the above ConsoleApplication1.csproj by calling the MSBuild task,
402             // and overriding the OutDir property.  However, the value being passed into OutDir
403             // is coming from another property which is produced by CreateProperty and has
404             // some special characters in it.
405             ObjectModelHelpers.CreateFileInTempProjectDirectory(
406                 Path.Combine("bug'533'369", "Sub;Dir", "TeamBuild.proj"), @"
407                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
408 
409                     <Target Name=`Build`>
410 
411                         <CreateProperty Value=`$(MSBuildProjectDirectory)\binaries\`>
412                             <Output PropertyName=`MasterOutDir` TaskParameter=`Value`/>
413                         </CreateProperty>
414 
415                         <MSBuild Projects=`ConsoleApplication1\ConsoleApplication1.csproj`
416                                  Properties=`OutDir=$(MasterOutDir)`
417                                  Targets=`Rebuild`/>
418                     </Target>
419 
420                 </Project>
421                 ");
422 
423             ObjectModelHelpers.BuildTempProjectFileExpectSuccess(Path.Combine("bug'533'369", "Sub;Dir", "TeamBuild.proj"));
424 
425             ObjectModelHelpers.AssertFileExistsInTempProjectDirectory(Path.Combine("bug'533'369", "Sub;Dir", "binaries", "ConsoleApplication1.exe"));
426         }
427 
428         /// <summary>
429         /// Check if passing different global properties via metadata works
430         /// </summary>
431         [Fact]
DifferentGlobalPropertiesWithDefault()432         public void DifferentGlobalPropertiesWithDefault()
433         {
434             string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
435                 <Project DefaultTargets=`TargetA; TargetB` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
436 
437                     <Target Name=`TargetA` Outputs=`a1.dll` Condition=`'$(MyProp)'=='0'`/>
438                     <Target Name=`TargetB` Outputs=`b1.dll` Condition=`'$(MyProp)'=='1'`/>
439 
440                 </Project>
441                 ");
442 
443             string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
444                 <Project DefaultTargets=`TargetG; TargetH` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
445                     <Target Name=`TargetG` Outputs=`g1.dll` Condition=`'$(MyProp)'=='0'` />
446                     <Target Name=`TargetH` Outputs=`h1.dll` Condition=`'$(MyProp)'=='1'` />
447                 </Project>
448                 ");
449 
450             try
451             {
452                 ITaskItem[] projects = new ITaskItem[]
453                 {
454                     new TaskItem(projectFile1),
455                     new TaskItem(projectFile1),
456                     new TaskItem(projectFile2),
457                     new TaskItem(projectFile2)
458                 };
459                 projects[1].SetMetadata("Properties", "MyProp=1");
460                 projects[3].SetMetadata("Properties", "MyProp=1");
461 
462                 MSBuild msbuildTask = new MSBuild();
463                 msbuildTask.BuildEngine = new MockEngine();
464                 msbuildTask.Projects = projects;
465                 msbuildTask.Properties = new string[] { "MyProp=0" };
466 
467                 bool success = msbuildTask.Execute();
468                 Assert.True(success); // "Build failed.  See 'Standard Out' tab for details."
469 
470                 string expectedItemOutputs = string.Format(@"
471                     a1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetA
472                     b1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetB
473                     g1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetG
474                     h1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetH
475                     ", projectFile1, projectFile2);
476 
477                 ObjectModelHelpers.AssertItemsMatch(expectedItemOutputs, msbuildTask.TargetOutputs, false /* order of items not enforced */);
478             }
479             finally
480             {
481                 File.Delete(projectFile1);
482                 File.Delete(projectFile2);
483             }
484         }
485 
486         /// <summary>
487         /// Check if passing different global properties via metadata works
488         /// </summary>
489         [Fact]
DifferentGlobalPropertiesWithoutDefault()490         public void DifferentGlobalPropertiesWithoutDefault()
491         {
492             string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
493                 <Project DefaultTargets=`TargetA; TargetB` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
494 
495                     <Target Name=`TargetA` Outputs=`a1.dll` Condition=`'$(MyProp)'=='0'`/>
496                     <Target Name=`TargetB` Outputs=`b1.dll` Condition=`'$(MyProp)'=='1'`/>
497 
498                 </Project>
499                 ");
500 
501             string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
502                 <Project DefaultTargets=`TargetG; TargetH` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
503                     <Target Name=`TargetG` Outputs=`g1.dll` Condition=`'$(MyProp)'=='0'` />
504                     <Target Name=`TargetH` Outputs=`h1.dll` Condition=`'$(MyProp)'=='1'` />
505                 </Project>
506                 ");
507 
508             try
509             {
510                 ITaskItem[] projects = new ITaskItem[]
511                 {
512                     new TaskItem(projectFile1),
513                     new TaskItem(projectFile1),
514                     new TaskItem(projectFile2),
515                     new TaskItem(projectFile2)
516                 };
517                 projects[1].SetMetadata("Properties", "MyProp=1");
518                 projects[3].SetMetadata("Properties", "MyProp=1");
519 
520                 MSBuild msbuildTask = new MSBuild();
521                 msbuildTask.BuildEngine = new MockEngine();
522 
523                 msbuildTask.Projects = projects;
524 
525                 bool success = msbuildTask.Execute();
526                 Assert.True(success); // "Build failed.  See 'Standard Out' tab for details."
527 
528                 string expectedItemOutputs = string.Format(@"
529                     b1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetB
530                     h1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetH
531                     ", projectFile1, projectFile2);
532 
533                 ObjectModelHelpers.AssertItemsMatch(expectedItemOutputs, msbuildTask.TargetOutputs, false /* order of items not enforced */);
534             }
535             finally
536             {
537                 File.Delete(projectFile1);
538                 File.Delete(projectFile2);
539             }
540         }
541 
542 
543         /// <summary>
544         /// Check if passing different global properties via metadata works
545         /// </summary>
546         [Fact]
DifferentGlobalPropertiesWithBlanks()547         public void DifferentGlobalPropertiesWithBlanks()
548         {
549             string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
550                 <Project DefaultTargets=`TargetA; TargetB` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
551 
552                     <Target Name=`TargetA` Outputs=`a1.dll` Condition=`'$(MyProp)'=='0'`/>
553                     <Target Name=`TargetB` Outputs=`b1.dll` Condition=`'$(MyProp)'=='1'`/>
554 
555                 </Project>
556                 ");
557 
558             string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
559                 <Project DefaultTargets=`TargetG; TargetH` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
560                     <Target Name=`TargetG` Outputs=`g1.dll` Condition=`'$(MyProp)'=='0'` />
561                     <Target Name=`TargetH` Outputs=`h1.dll` Condition=`'$(MyProp)'=='1'` />
562                 </Project>
563                 ");
564 
565             try
566             {
567                 ITaskItem[] projects = new ITaskItem[]
568                 {
569                     new TaskItem(projectFile1),
570                     new TaskItem(projectFile1),
571                     new TaskItem(projectFile2),
572                     new TaskItem(projectFile2)
573                 };
574                 projects[1].SetMetadata("Properties", "");
575                 projects[3].SetMetadata("Properties", "MyProp=1");
576 
577                 MSBuild msbuildTask = new MSBuild();
578                 msbuildTask.BuildEngine = new MockEngine();
579 
580                 msbuildTask.Projects = projects;
581 
582                 bool success = msbuildTask.Execute();
583                 Assert.True(success); // "Build failed.  See 'Standard Out' tab for details."
584 
585                 string expectedItemOutputs = string.Format(@"
586                     h1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetH
587                     ", projectFile2);
588 
589                 ObjectModelHelpers.AssertItemsMatch(expectedItemOutputs, msbuildTask.TargetOutputs, false /* order of items not enforced */);
590             }
591             finally
592             {
593                 File.Delete(projectFile1);
594                 File.Delete(projectFile2);
595             }
596         }
597 
598 
599         /// <summary>
600         /// Check if passing different global properties via metadata works
601         /// </summary>
602         [Fact]
DifferentGlobalPropertiesInvalid()603         public void DifferentGlobalPropertiesInvalid()
604         {
605             string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
606                 <Project DefaultTargets=`TargetA; TargetB` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
607 
608                     <Target Name=`TargetA` Outputs=`a1.dll` Condition=`'$(MyProp)'=='0'`/>
609                     <Target Name=`TargetB` Outputs=`b1.dll` Condition=`'$(MyProp)'=='1'`/>
610 
611                 </Project>
612                 ");
613 
614             string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
615                 <Project DefaultTargets=`TargetG; TargetH` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
616                     <Target Name=`TargetG` Outputs=`g1.dll` Condition=`'$(MyProp)'=='0'` />
617                     <Target Name=`TargetH` Outputs=`h1.dll` Condition=`'$(MyProp)'=='1'` />
618                 </Project>
619                 ");
620 
621             try
622             {
623                 ITaskItem[] projects = new ITaskItem[]
624                 {
625                     new TaskItem(projectFile1),
626                     new TaskItem(projectFile1),
627                     new TaskItem(projectFile2),
628                     new TaskItem(projectFile2)
629                 };
630                 projects[1].SetMetadata("Properties", "=1");
631                 projects[3].SetMetadata("Properties", "=;1");
632 
633                 MSBuild msbuildTask = new MSBuild();
634                 msbuildTask.BuildEngine = new MockEngine();
635 
636                 msbuildTask.Projects = projects;
637 
638                 bool success = msbuildTask.Execute();
639                 Assert.False(success); // "Build succeeded.  See 'Standard Out' tab for details."
640             }
641             finally
642             {
643                 File.Delete(projectFile1);
644                 File.Delete(projectFile2);
645             }
646         }
647 
648         /// <summary>
649         /// Check if passing additional global properties via metadata works
650         /// </summary>
651         [Fact]
DifferentAdditionalPropertiesWithDefault()652         public void DifferentAdditionalPropertiesWithDefault()
653         {
654             string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
655                 <Project DefaultTargets=`TargetA; TargetB` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
656 
657                     <Target Name=`TargetA` Outputs=`a1.dll` Condition=`'$(MyPropG)'=='1'`/>
658                     <Target Name=`TargetB` Outputs=`b1.dll` Condition=`'$(MyPropA)'=='1'`/>
659 
660                 </Project>
661                 ");
662 
663             string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
664                 <Project DefaultTargets=`TargetG; TargetH` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
665                     <Target Name=`TargetG` Outputs=`g1.dll` Condition=`'$(MyPropG)'=='1'` />
666                     <Target Name=`TargetH` Outputs=`h1.dll` Condition=`'$(MyPropA)'=='1'` />
667                 </Project>
668                 ");
669 
670             try
671             {
672                 ITaskItem[] projects = new ITaskItem[]
673                 {
674                     new TaskItem(projectFile1),
675                     new TaskItem(projectFile2)
676                 };
677                 projects[0].SetMetadata("AdditionalProperties", "MyPropA=1");
678                 projects[1].SetMetadata("AdditionalProperties", "MyPropA=0");
679 
680                 MSBuild msbuildTask = new MSBuild();
681                 msbuildTask.BuildEngine = new MockEngine();
682                 msbuildTask.Properties = new string[] { "MyPropG=1" };
683                 msbuildTask.Projects = projects;
684 
685                 bool success = msbuildTask.Execute();
686                 Assert.True(success); // "Build failed.  See 'Standard Out' tab for details."
687 
688                 string expectedItemOutputs = string.Format(@"
689                     a1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetA
690                     b1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetB
691                     g1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetG
692                     ", projectFile1, projectFile2);
693 
694                 ObjectModelHelpers.AssertItemsMatch(expectedItemOutputs, msbuildTask.TargetOutputs, false /* order of items not enforced */);
695             }
696             finally
697             {
698                 File.Delete(projectFile1);
699                 File.Delete(projectFile2);
700             }
701         }
702 
703 
704         /// <summary>
705         /// Check if passing additional global properties via metadata works
706         /// </summary>
707         [Fact]
DifferentAdditionalPropertiesWithGlobalProperties()708         public void DifferentAdditionalPropertiesWithGlobalProperties()
709         {
710             string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
711                 <Project DefaultTargets=`TargetA; TargetB` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
712 
713                     <Target Name=`TargetA` Outputs=`a1.dll` Condition=`'$(MyPropG)'=='0'`/>
714                     <Target Name=`TargetB` Outputs=`b1.dll` Condition=`'$(MyPropA)'=='1'`/>
715 
716                 </Project>
717                 ");
718 
719             string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
720                 <Project DefaultTargets=`TargetG; TargetH` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
721                     <Target Name=`TargetG` Outputs=`g1.dll` Condition=`'$(MyPropG)'=='0'` />
722                     <Target Name=`TargetH` Outputs=`h1.dll` Condition=`'$(MyPropA)'=='1'` />
723                 </Project>
724                 ");
725 
726             try
727             {
728                 ITaskItem[] projects = new ITaskItem[]
729                 {
730                     new TaskItem(projectFile1),
731                     new TaskItem(projectFile2)
732                 };
733 
734                 projects[0].SetMetadata("AdditionalProperties", "MyPropA=1");
735                 projects[1].SetMetadata("AdditionalProperties", "MyPropA=1");
736 
737                 projects[0].SetMetadata("Properties", "MyPropG=1");
738                 projects[1].SetMetadata("Properties", "MyPropG=0");
739 
740                 MSBuild msbuildTask = new MSBuild();
741                 msbuildTask.BuildEngine = new MockEngine();
742                 msbuildTask.Projects = projects;
743 
744                 bool success = msbuildTask.Execute();
745                 Assert.True(success); // "Build failed.  See 'Standard Out' tab for details."
746 
747                 string expectedItemOutputs = string.Format(@"
748                     b1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetB
749                     g1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetG
750                     h1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetH
751                     ", projectFile1, projectFile2);
752 
753                 ObjectModelHelpers.AssertItemsMatch(expectedItemOutputs, msbuildTask.TargetOutputs, false /* order of items not enforced */);
754             }
755             finally
756             {
757                 File.Delete(projectFile1);
758                 File.Delete(projectFile2);
759             }
760         }
761 
762 
763         /// <summary>
764         /// Check if passing additional global properties via metadata works
765         /// </summary>
766         [Fact]
DifferentAdditionalPropertiesWithoutDefault()767         public void DifferentAdditionalPropertiesWithoutDefault()
768         {
769             string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
770                 <Project DefaultTargets=`TargetA; TargetB` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
771 
772                     <Target Name=`TargetA` Outputs=`a1.dll` Condition=`'$(MyPropG)'=='1'`/>
773                     <Target Name=`TargetB` Outputs=`b1.dll` Condition=`'$(MyPropA)'=='1'`/>
774 
775                 </Project>
776                 ");
777 
778             string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
779                 <Project DefaultTargets=`TargetG; TargetH` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
780                     <Target Name=`TargetG` Outputs=`g1.dll` Condition=`'$(MyPropG)'=='1'` />
781                     <Target Name=`TargetH` Outputs=`h1.dll` Condition=`'$(MyPropA)'=='1'` />
782                 </Project>
783                 ");
784 
785             try
786             {
787                 ITaskItem[] projects = new ITaskItem[]
788                 {
789                     new TaskItem(projectFile1),
790                     new TaskItem(projectFile2)
791                 };
792 
793                 projects[0].SetMetadata("AdditionalProperties", "MyPropA=1");
794                 projects[1].SetMetadata("AdditionalProperties", "MyPropA=1");
795 
796                 MSBuild msbuildTask = new MSBuild();
797                 msbuildTask.BuildEngine = new MockEngine();
798                 msbuildTask.Projects = projects;
799 
800                 bool success = msbuildTask.Execute();
801                 Assert.True(success); // "Build failed.  See 'Standard Out' tab for details."
802 
803                 string expectedItemOutputs = string.Format(@"
804                     b1.dll : MSBuildSourceProjectFile={0} ; MSBuildSourceTargetName=TargetB
805                     h1.dll : MSBuildSourceProjectFile={1} ; MSBuildSourceTargetName=TargetH
806                     ", projectFile1, projectFile2);
807 
808                 ObjectModelHelpers.AssertItemsMatch(expectedItemOutputs, msbuildTask.TargetOutputs, false /* order of items not enforced */);
809             }
810             finally
811             {
812                 File.Delete(projectFile1);
813                 File.Delete(projectFile2);
814             }
815         }
816 
817         /// <summary>
818         /// Properties and Targets that use non-standard separation chars
819         /// </summary>
820         [Fact]
TargetsWithSeparationChars()821         public void TargetsWithSeparationChars()
822         {
823             string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
824                 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace` ToolsVersion=`msbuilddefaulttoolsversion`>
825                     <Target Name=`Clean` />
826                     <Target Name=`Build` />
827                     <Target Name=`BuildAgain` />
828                 </Project>
829                 ");
830 
831             string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
832                 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace` ToolsVersion=`msbuilddefaulttoolsversion`>
833                     <PropertyGroup>
834                         <Targets>Clean%3BBuild%3CBuildAgain</Targets>
835                     </PropertyGroup>
836 
837                     <ItemGroup>
838                         <ProjectFile Include=`" + projectFile1 + @"` />
839                     </ItemGroup>
840 
841                     <Target Name=`Build` Outputs=`$(SomeOutputs)`>
842                         <MSBuild Projects=`@(ProjectFile)` Targets=`$(Targets)` TargetAndPropertyListSeparators=`%3B;%3C` />
843                     </Target>
844                 </Project>
845                 ");
846 
847             try
848             {
849                 ITaskItem[] projects = new ITaskItem[]
850                 {
851                     new TaskItem(projectFile2)
852                 };
853 
854                 MSBuild msbuildTask = new MSBuild();
855                 msbuildTask.BuildEngine = new MockEngine();
856                 msbuildTask.Projects = projects;
857 
858                 bool success = msbuildTask.Execute();
859                 Assert.True(success); // "Build failed.  See 'Standard Out' tab for details."
860             }
861             finally
862             {
863                 File.Delete(projectFile1);
864                 File.Delete(projectFile2);
865             }
866         }
867 
868         /// <summary>
869         /// Verify stopOnFirstFailure with BuildInParallel override message are correctly logged
870         /// Also verify stop on first failure will not build the second project if the first one failed
871         /// The Aardvark tests which also test StopOnFirstFailure are at:
872         /// qa\md\wd\DTP\MSBuild\ShippingExtensions\ShippingTasks\MSBuild\_Tst\MSBuild.StopOnFirstFailure
873         /// </summary>
874         [Fact]
StopOnFirstFailureandBuildInParallelSingleNode()875         public void StopOnFirstFailureandBuildInParallelSingleNode()
876         {
877             string project1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
878                   <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
879                       <Target Name='msbuild'>
880                           <Error Text='Error'/>
881                       </Target>
882                   </Project>
883                   ");
884 
885             string project2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
886                    <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
887                        <Target Name='msbuild'>
888                            <Message Text='SecondProject'/>
889                        </Target>
890                     </Project>
891                   ");
892 
893             try
894             {
895                 ITaskItem[] projects = new ITaskItem[]
896                 {
897                     new TaskItem(project1), new TaskItem(project2)
898                 };
899 
900                 // Test the various combinations of BuildInParallel and StopOnFirstFailure when the msbuild task is told there are not multiple nodes
901                 // running in the system
902                 for (int i = 0; i < 4; i++)
903                 {
904                     MSBuild msbuildTask = new MSBuild();
905                     // By default IsMultipleNodesIs false
906                     MockEngine mockEngine = new MockEngine();
907                     mockEngine.IsRunningMultipleNodes = false;
908                     msbuildTask.BuildEngine = mockEngine;
909                     msbuildTask.Projects = projects;
910                     msbuildTask.Targets = new string[] { "msbuild" };
911                     // Make success true as the expected result is false
912                     bool success = true;
913                     switch (i)
914                     {
915                         case 0:
916                             // Verify setting BuildInParallel and StopOnFirstFailure to
917                             // true will cause the msbuild task to set BuildInParallel to false during the execute
918                             msbuildTask.BuildInParallel = true;
919                             msbuildTask.StopOnFirstFailure = true;
920                             success = msbuildTask.Execute();
921                             // Verify build did not build second project which has the message SecondProject
922                             mockEngine.AssertLogDoesntContain("SecondProject");
923                             // Verify the correct msbuild task messages are in the log
924                             Assert.False(msbuildTask.BuildInParallel); // "Iteration of 0 Expected BuildInParallel to be false"
925                             break;
926                         case 1:
927                             // Verify setting BuildInParallel to true and StopOnFirstFailure to
928                             // false will cause no change in BuildInParallel
929                             msbuildTask.BuildInParallel = true;
930                             msbuildTask.StopOnFirstFailure = false;
931                             success = msbuildTask.Execute();
932                             // Verify build did  build second project which has the message SecondProject
933                             mockEngine.AssertLogContains("SecondProject");
934                             // Verify the correct msbuild task messages are in the log
935                             Assert.True(msbuildTask.BuildInParallel); // "Iteration of 1 Expected BuildInParallel to be true"
936                             break;
937                         case 2:
938                             // Verify setting BuildInParallel to false and StopOnFirstFailure to
939                             // true will cause no change in BuildInParallel
940                             msbuildTask.BuildInParallel = false;
941                             msbuildTask.StopOnFirstFailure = true;
942                             success = msbuildTask.Execute();
943                             // Verify build did not build second project which has the message SecondProject
944                             mockEngine.AssertLogDoesntContain("SecondProject");
945                             // Verify the correct msbuild task messages are in the log
946                             Assert.False(msbuildTask.BuildInParallel); // "Iteration of 2 Expected BuildInParallel to be false"
947                             break;
948 
949                         case 3:
950                             // Verify setting BuildInParallel to false and StopOnFirstFailure to
951                             // false will cause no change in BuildInParallel
952                             msbuildTask.BuildInParallel = false;
953                             msbuildTask.StopOnFirstFailure = false;
954                             success = msbuildTask.Execute();
955                             // Verify build did build second project which has the message SecondProject
956                             mockEngine.AssertLogContains("SecondProject");
957                             // Verify the correct msbuild task messages are in the log
958                             Assert.False(msbuildTask.BuildInParallel); // "Iteration of 3 Expected BuildInParallel to be false"
959                             break;
960                     }
961                     // The build should fail as the first project has an error
962                     Assert.False(success, "Iteration of i " + i + " Build Succeeded.  See 'Standard Out' tab for details.");
963                 }
964             }
965             finally
966             {
967                 File.Delete(project1);
968                 File.Delete(project2);
969             }
970         }
971 
972 #if FEATURE_APPDOMAIN
973         /// <summary>
974         /// Verify stopOnFirstFailure with BuildInParallel override message are correctly logged when there are multiple nodes
975         /// </summary>
976         [Fact]
StopOnFirstFailureandBuildInParallelMultipleNode()977         public void StopOnFirstFailureandBuildInParallelMultipleNode()
978         {
979             string project1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
980                   <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
981                       <Target Name='msbuild'>
982                           <Error Text='Error'/>
983                       </Target>
984                   </Project>
985                   ");
986 
987             string project2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
988                    <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
989                        <Target Name='msbuild'>
990                            <Message Text='SecondProject'/>
991                        </Target>
992                     </Project>
993                   ");
994 
995             try
996             {
997                 ITaskItem[] projects = new ITaskItem[]
998                 {
999                     new TaskItem(project1), new TaskItem(project2)
1000                 };
1001 
1002                 // Test the various combinations of BuildInParallel and StopOnFirstFailure when the msbuild task is told there are multiple nodes
1003                 // running in the system
1004                 for (int i = 0; i < 4; i++)
1005                 {
1006                     MSBuild msbuildTask = new MSBuild();
1007                     MockEngine mockEngine = new MockEngine();
1008                     mockEngine.IsRunningMultipleNodes = true;
1009                     msbuildTask.BuildEngine = mockEngine;
1010                     msbuildTask.Projects = projects;
1011                     msbuildTask.Targets = new string[] { "msbuild" };
1012                     // Make success true as the expected result is false
1013                     bool success = true;
1014                     switch (i)
1015                     {
1016                         case 0:
1017                             // Verify setting BuildInParallel and StopOnFirstFailure to
1018                             // true will not cause the msbuild task to set BuildInParallel to false during the execute
1019                             msbuildTask.BuildInParallel = true;
1020                             msbuildTask.StopOnFirstFailure = true;
1021                             success = msbuildTask.Execute();
1022                             // Verify build did build second project which has the message SecondProject
1023                             mockEngine.AssertLogContains("SecondProject");
1024                             // Verify the correct msbuild task messages are in the log
1025                             Assert.True(msbuildTask.BuildInParallel); // "Iteration of 0 Expected BuildInParallel to be true"
1026                             break;
1027                         case 1:
1028                             // Verify setting BuildInParallel to true and StopOnFirstFailure to
1029                             // false will cause no change in BuildInParallel
1030                             msbuildTask.BuildInParallel = true;
1031                             msbuildTask.StopOnFirstFailure = false;
1032                             success = msbuildTask.Execute();
1033                             // Verify build did build second project which has the message SecondProject
1034                             mockEngine.AssertLogContains("SecondProject");
1035                             // Verify the correct msbuild task messages are in the log
1036                             Assert.True(msbuildTask.BuildInParallel); // "Iteration of 1 Expected BuildInParallel to be true"
1037                             break;
1038                         case 2:
1039                             // Verify setting BuildInParallel to false and StopOnFirstFailure to
1040                             // true will cause no change in BuildInParallel
1041                             msbuildTask.BuildInParallel = false;
1042                             msbuildTask.StopOnFirstFailure = true;
1043                             success = msbuildTask.Execute();
1044                             // Verify build did not build second project which has the message SecondProject
1045                             mockEngine.AssertLogDoesntContain("SecondProject");
1046                             // Verify the correct msbuild task messages are in the log
1047                             Assert.False(msbuildTask.BuildInParallel); // "Iteration of 2 Expected BuildInParallel to be false"
1048                             break;
1049 
1050                         case 3:
1051                             // Verify setting BuildInParallel to false and StopOnFirstFailure to
1052                             // false will cause no change in BuildInParallel
1053                             msbuildTask.BuildInParallel = false;
1054                             msbuildTask.StopOnFirstFailure = false;
1055                             success = msbuildTask.Execute();
1056                             // Verify build did build second project which has the message SecondProject
1057                             mockEngine.AssertLogContains("SecondProject");
1058                             // Verify the correct msbuild task messages are in the log
1059                             Assert.False(msbuildTask.BuildInParallel); // "Iteration of 3 Expected BuildInParallel to be false"
1060                             break;
1061                     }
1062                     // The build should fail as the first project has an error
1063                     Assert.False(success, "Iteration of i " + i + " Build Succeeded.  See 'Standard Out' tab for details.");
1064                 }
1065             }
1066             finally
1067             {
1068                 File.Delete(project1);
1069                 File.Delete(project2);
1070             }
1071         }
1072 #endif
1073 
1074         /// <summary>
1075         /// Test the skipping of the remaining projects. Verify the skip message is only displayed when there are projects to skip.
1076         /// </summary>
1077         [Fact]
SkipRemainingProjects()1078         public void SkipRemainingProjects()
1079         {
1080             string project1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
1081                   <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1082                       <Target Name='msbuild'>
1083                           <Error Text='Error'/>
1084                       </Target>
1085                   </Project>
1086                   ");
1087 
1088             string project2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
1089                    <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1090                        <Target Name='msbuild'>
1091                            <Message Text='SecondProject'/>
1092                        </Target>
1093                     </Project>
1094                   ");
1095 
1096             try
1097             {
1098                 // Test the case where there is only one project and it has an error
1099                 ITaskItem[] projects = new ITaskItem[]
1100                 {
1101                     new TaskItem(project1)
1102                 };
1103 
1104                 MSBuild msbuildTask = new MSBuild();
1105                 MockEngine mockEngine = new MockEngine();
1106                 mockEngine.IsRunningMultipleNodes = true;
1107                 msbuildTask.BuildEngine = mockEngine;
1108                 msbuildTask.Projects = projects;
1109                 msbuildTask.Targets = new string[] { "msbuild" };
1110                 msbuildTask.BuildInParallel = false;
1111                 msbuildTask.StopOnFirstFailure = true;
1112                 bool success = msbuildTask.Execute();
1113                 Assert.False(success); // "Build Succeeded.  See 'Standard Out' tab for details."
1114 
1115                 // Test the case where there are two projects and the last one has an error
1116                 projects = new ITaskItem[]
1117                 {
1118                     new TaskItem(project2), new TaskItem(project1)
1119                 };
1120 
1121                 msbuildTask = new MSBuild();
1122                 mockEngine = new MockEngine();
1123                 mockEngine.IsRunningMultipleNodes = true;
1124                 msbuildTask.BuildEngine = mockEngine;
1125                 msbuildTask.Projects = projects;
1126                 msbuildTask.Targets = new string[] { "msbuild" };
1127                 msbuildTask.BuildInParallel = false;
1128                 msbuildTask.StopOnFirstFailure = true;
1129                 success = msbuildTask.Execute();
1130                 Assert.False(success); // "Build Succeeded.  See 'Standard Out' tab for details."
1131             }
1132             finally
1133             {
1134                 File.Delete(project1);
1135                 File.Delete(project2);
1136             }
1137         }
1138 
1139         /// <summary>
1140         /// Verify the behavior of Target execution with StopOnFirstFailure
1141         /// </summary>
1142         [Fact]
TargetStopOnFirstFailureBuildInParallel()1143         public void TargetStopOnFirstFailureBuildInParallel()
1144         {
1145             string project1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
1146                    <Project xmlns='msbuildnamespace' ToolsVersion='msbuilddefaulttoolsversion'>
1147                    <Target Name='T1'>
1148                           <Message Text='Proj2 T1 message'/>
1149                       </Target>
1150                     <Target Name='T2'>
1151                           <Message Text='Proj2 T2 message'/>
1152                       </Target>
1153                     <Target Name='T3'>
1154                            <Error Text='Error'/>
1155                       </Target>
1156                     </Project>
1157                   ");
1158 
1159             try
1160             {
1161                 ITaskItem[] projects = new ITaskItem[]
1162                 {
1163                     new TaskItem(project1)
1164                 };
1165                 for (int i = 0; i < 6; i++)
1166                 {
1167                     // Test the case where the error is in the last target
1168                     MSBuild msbuildTask = new MSBuild();
1169                     MockEngine mockEngine = new MockEngine();
1170                     msbuildTask.BuildEngine = mockEngine;
1171                     msbuildTask.Projects = projects;
1172                     // Set to true as the expected result is false
1173                     bool success = true;
1174                     switch (i)
1175                     {
1176                         case 0:
1177                             // Test the case where the error is in the last project and RunEachTargetSeparately = true
1178                             msbuildTask.StopOnFirstFailure = true;
1179                             msbuildTask.RunEachTargetSeparately = true;
1180                             msbuildTask.Targets = new string[] { "T1", "T2", "T3" };
1181                             success = msbuildTask.Execute();
1182                             mockEngine.AssertLogContains("Proj2 T1 message");
1183                             mockEngine.AssertLogContains("Proj2 T2 message");
1184                             break;
1185                         case 1:
1186                             // Test the case where the error is in the second target out of 3.
1187                             msbuildTask.StopOnFirstFailure = true;
1188                             msbuildTask.RunEachTargetSeparately = true;
1189                             msbuildTask.Targets = new string[] { "T1", "T3", "T2" };
1190                             success = msbuildTask.Execute();
1191                             mockEngine.AssertLogContains("Proj2 T1 message");
1192                             mockEngine.AssertLogDoesntContain("Proj2 T2 message");
1193                             // The build should fail as the first project has an error
1194                             break;
1195                         case 2:
1196                             // Test case where error is in second last target but stopOnFirstFailure is false
1197                             msbuildTask.RunEachTargetSeparately = true;
1198                             msbuildTask.StopOnFirstFailure = false;
1199                             msbuildTask.Targets = new string[] { "T1", "T3", "T2" };
1200                             success = msbuildTask.Execute();
1201                             mockEngine.AssertLogContains("Proj2 T1 message");
1202                             mockEngine.AssertLogContains("Proj2 T2 message");
1203                             break;
1204                         // Test the cases where RunEachTargetSeparately is false. In these cases all of the targets should be submitted at once
1205                         case 3:
1206                             // Test the case where the error is in the last project and RunEachTargetSeparately = true
1207                             msbuildTask.StopOnFirstFailure = true;
1208                             msbuildTask.Targets = new string[] { "T1", "T2", "T3" };
1209                             success = msbuildTask.Execute();
1210                             mockEngine.AssertLogContains("Proj2 T1 message");
1211                             mockEngine.AssertLogContains("Proj2 T2 message");
1212                             // The build should fail as the first project has an error
1213                             break;
1214                         case 4:
1215                             // Test the case where the error is in the second target out of 3.
1216                             msbuildTask.StopOnFirstFailure = true;
1217                             msbuildTask.Targets = new string[] { "T1", "T3", "T2" };
1218                             success = msbuildTask.Execute();
1219                             mockEngine.AssertLogContains("Proj2 T1 message");
1220                             mockEngine.AssertLogDoesntContain("Proj2 T2 message");
1221                             // The build should fail as the first project has an error
1222                             break;
1223                         case 5:
1224                             // Test case where error is in second last target but stopOnFirstFailure is false
1225                             msbuildTask.StopOnFirstFailure = false;
1226                             msbuildTask.Targets = new string[] { "T1", "T3", "T2" };
1227                             success = msbuildTask.Execute();
1228                             mockEngine.AssertLogContains("Proj2 T1 message");
1229                             mockEngine.AssertLogDoesntContain("Proj2 T2 message");
1230                             break;
1231                     }
1232 
1233                     // The build should fail as the first project has an error
1234                     Assert.False(success, "Iteration of i:" + i + "Build Succeeded.  See 'Standard Out' tab for details.");
1235                 }
1236             }
1237             finally
1238             {
1239                 File.Delete(project1);
1240             }
1241         }
1242 
1243         /// <summary>
1244         /// Properties and Targets that use non-standard separation chars
1245         /// </summary>
1246         [Fact]
PropertiesWithSeparationChars()1247         public void PropertiesWithSeparationChars()
1248         {
1249             string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
1250                 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
1251                     <Target Name=`Build` Outputs=`|$(a)|$(b)|$(C)|$(D)|` />
1252                 </Project>
1253                 ");
1254 
1255             string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
1256                 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace` ToolsVersion=`msbuilddefaulttoolsversion`>
1257                     <PropertyGroup>
1258                         <AValues>a%3BA</AValues>
1259                         <BValues>b;B</BValues>
1260                         <CValues>c;C</CValues>
1261                         <DValues>d%3BD</DValues>
1262                     </PropertyGroup>
1263 
1264                     <ItemGroup>
1265                         <ProjectFile Include=`" + projectFile1 + @"`>
1266                             <AdditionalProperties>C=$(CValues)%3BD=$(DValues)</AdditionalProperties>
1267                         </ProjectFile>
1268                     </ItemGroup>
1269 
1270                     <Target Name=`Build` Outputs=`$(SomeOutputs)`>
1271                         <MSBuild Projects=`@(ProjectFile)` Targets=`Build` Properties=`a=$(AValues)%3Bb=$(BValues)` TargetAndPropertyListSeparators=`%3B`>
1272                             <Output TaskParameter=`TargetOutputs` PropertyName=`SomeOutputs`/>
1273                         </MSBuild>
1274                     </Target>
1275                 </Project>
1276                 ");
1277 
1278             try
1279             {
1280                 ITaskItem[] projects = new ITaskItem[]
1281                 {
1282                     new TaskItem(projectFile2)
1283                 };
1284 
1285                 MSBuild msbuildTask = new MSBuild();
1286                 msbuildTask.BuildEngine = new MockEngine();
1287                 msbuildTask.Projects = projects;
1288 
1289                 bool success = msbuildTask.Execute();
1290                 Assert.True(success); // "Build failed.  See 'Standard Out' tab for details."
1291 
1292                 Assert.Equal(5, msbuildTask.TargetOutputs.Length);
1293                 Assert.Equal("|a", msbuildTask.TargetOutputs[0].ItemSpec);
1294                 Assert.Equal("A|b", msbuildTask.TargetOutputs[1].ItemSpec);
1295                 Assert.Equal("B|c", msbuildTask.TargetOutputs[2].ItemSpec);
1296                 Assert.Equal("C|d", msbuildTask.TargetOutputs[3].ItemSpec);
1297                 Assert.Equal("D|", msbuildTask.TargetOutputs[4].ItemSpec);
1298             }
1299             finally
1300             {
1301                 File.Delete(projectFile1);
1302                 File.Delete(projectFile2);
1303             }
1304         }
1305 
1306         /// <summary>
1307         /// Orcas had a bug that if the target casing specified was not correct, we would still build it,
1308         /// but not return any target outputs!
1309         /// </summary>
1310         [Fact]
TargetNameIsCaseInsensitive()1311         public void TargetNameIsCaseInsensitive()
1312         {
1313             string projectFile1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
1314                 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace` ToolsVersion='msbuilddefaulttoolsversion'>
1315                     <Target Name=`bUiLd` Outputs=`foo.out` />
1316                 </Project>
1317                 ");
1318 
1319             string projectFile2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
1320                 <Project DefaultTargets=`t` xmlns=`msbuildnamespace` ToolsVersion=`msbuilddefaulttoolsversion`>
1321                     <Target Name=`t`>
1322                         <MSBuild Projects=`" + projectFile1 + @"` Targets=`BUILD`>
1323                             <Output TaskParameter=`TargetOutputs` ItemName=`out`/>
1324                         </MSBuild>
1325                         <Message Text=`[@(out)]`/>
1326                     </Target>
1327                 </Project>
1328                 ");
1329 
1330             try
1331             {
1332                 Project project = new Project(projectFile2);
1333                 MockLogger logger = new MockLogger();
1334 
1335                 project.Build(logger);
1336 
1337                 logger.AssertLogContains("[foo.out]");
1338             }
1339             finally
1340             {
1341                 File.Delete(projectFile1);
1342                 File.Delete(projectFile2);
1343             }
1344         }
1345 
1346         [Fact]
NonexistentTargetSkippedWhenSkipNonexistentTargetsSet()1347         public void NonexistentTargetSkippedWhenSkipNonexistentTargetsSet()
1348         {
1349             ObjectModelHelpers.DeleteTempProjectDirectory();
1350             ObjectModelHelpers.CreateFileInTempProjectDirectory(
1351                 "SkipNonexistentTargets.csproj",
1352                 @"<Project>
1353                     <Target Name=`t` >
1354 	                    <MSBuild Projects=`SkipNonexistentTargets.csproj` Targets=`t_nonexistent;t2` SkipNonexistentTargets=`true` />
1355                      </Target>
1356                     <Target Name=`t2`> <Message Text=`t2 executing` /> </Target>
1357                 </Project>
1358                 ");
1359 
1360             MockLogger logger = ObjectModelHelpers.BuildTempProjectFileExpectSuccess(@"SkipNonexistentTargets.csproj");
1361 
1362             // Target t2 should still execute
1363             logger.FullLog.ShouldContain("t2 executing");
1364             logger.WarningCount.ShouldBe(0);
1365             logger.ErrorCount.ShouldBe(0);
1366 
1367             // t_missing target should be skipped
1368             logger.FullLog.ShouldContain("Target \"t_nonexistent\" skipped");
1369         }
1370 
1371         [Fact]
NonexistentTargetFailsAfterSkipped()1372         public void NonexistentTargetFailsAfterSkipped()
1373         {
1374             ObjectModelHelpers.DeleteTempProjectDirectory();
1375             ObjectModelHelpers.CreateFileInTempProjectDirectory(
1376                 "SkipNonexistentTargets.csproj",
1377                 @"<Project>
1378                     <Target Name=`t` >
1379 	                    <MSBuild Projects=`SkipNonexistentTargets.csproj` Targets=`t_nonexistent` SkipNonexistentTargets=`true` />
1380                         <MSBuild Projects=`SkipNonexistentTargets.csproj` Targets=`t_nonexistent` SkipNonexistentTargets=`false` />
1381                      </Target>
1382                 </Project>
1383                 ");
1384 
1385             MockLogger logger = ObjectModelHelpers.BuildTempProjectFileExpectFailure(@"SkipNonexistentTargets.csproj");
1386 
1387             logger.WarningCount.ShouldBe(0);
1388             logger.ErrorCount.ShouldBe(1);
1389 
1390             // t_missing target should be skipped
1391             logger.FullLog.ShouldContain("Target \"t_nonexistent\" skipped");
1392 
1393             // 2nd invocation of t_missing should fail the build resulting in target not found error (MSB4057)
1394             logger.FullLog.ShouldContain("MSB4057");
1395         }
1396     }
1397 }
1398