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 TargetBuilder with a mock task builder.</summary>
6 //-----------------------------------------------------------------------
7 
8 using System;
9 using System.Xml;
10 using System.Text;
11 using System.Collections;
12 using System.Collections.Generic;
13 using System.IO;
14 using System.Linq;
15 using System.Text.RegularExpressions;
16 using System.Threading;
17 using Microsoft.Build.Framework;
18 using Microsoft.Build.BackEnd;
19 using Microsoft.Build.Collections;
20 using Microsoft.Build.Evaluation;
21 using Microsoft.Build.Execution;
22 using Microsoft.Build.Shared;
23 
24 using ILoggingService = Microsoft.Build.BackEnd.Logging.ILoggingService;
25 using ProjectLoggingContext = Microsoft.Build.BackEnd.Logging.ProjectLoggingContext;
26 using NodeLoggingContext = Microsoft.Build.BackEnd.Logging.NodeLoggingContext;
27 using LegacyThreadingData = Microsoft.Build.Execution.LegacyThreadingData;
28 using System.Threading.Tasks;
29 using Microsoft.Build.BackEnd.SdkResolution;
30 using Microsoft.Build.Engine.UnitTests.BackEnd;
31 using Xunit;
32 
33 namespace Microsoft.Build.UnitTests.BackEnd
34 {
35     /// <summary>
36     /// This is the unit test for the TargetBuilder.  This particular test is confined to just using the
37     /// actual TargetBuilder, and uses a mock TaskBuilder on which TargetBuilder depends.
38     /// </summary>
39     public class TargetBuilder_Tests : IRequestBuilderCallback, IDisposable
40     {
41         /// <summary>
42         /// The component host.
43         /// </summary>
44         private MockHost _host;
45 
46         /// <summary>
47         /// A mock logger for scenario tests.
48         /// </summary>
49         private MockLogger _mockLogger;
50 
51         /// <summary>
52         /// The node request id counter
53         /// </summary>
54         private int _nodeRequestId;
55 
56         /// <summary>
57         /// Callback used to receive exceptions from loggers.  Unused here.
58         /// </summary>
59         /// <param name="e">The exception</param>
LoggingException(Exception e)60         public void LoggingException(Exception e)
61         {
62         }
63 
64         /// <summary>
65         /// Sets up to run tests.  Creates the host object.
66         /// </summary>
TargetBuilder_Tests()67         public TargetBuilder_Tests()
68         {
69             _nodeRequestId = 1;
70             _host = new MockHost();
71             _mockLogger = new MockLogger();
72             _host.OnLoggingThreadException += this.LoggingException;
73         }
74 
75         /// <summary>
76         /// Executed after all tests are run.
77         /// </summary>
Dispose()78         public void Dispose()
79         {
80             File.Delete("testProject.proj");
81             _mockLogger = null;
82             _host = null;
83         }
84 
85         /// <summary>
86         /// Runs the constructor.
87         /// </summary>
88         [Fact]
TestConstructor()89         public void TestConstructor()
90         {
91             TargetBuilder builder = new TargetBuilder();
92         }
93 
94         /// <summary>
95         /// Runs a "simple" build with no dependencies and no outputs.
96         /// </summary>
97         [Fact]
TestSimpleBuild()98         public void TestSimpleBuild()
99         {
100             ProjectInstance project = CreateTestProject();
101 
102             // The Empty target has no inputs or outputs.
103             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
104             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
105             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Empty" }), cache[1]);
106 
107             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
108             Assert.True(result.HasResultsForTarget("Empty"));
109             Assert.Equal(TargetResultCode.Success, result["Empty"].ResultCode);
110             Assert.Equal(0, result["Empty"].Items.Length);
111         }
112 
113         /// <summary>
114         /// Runs a build with a target which depends on one other target.
115         /// </summary>
116         [Fact]
TestDependencyBuild()117         public void TestDependencyBuild()
118         {
119             ProjectInstance project = CreateTestProject();
120 
121             // The Baz project depends on the Bar target.  Both should succeed.
122             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
123             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
124 
125             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Baz" }), cache[1]);
126             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
127 
128             // The result returned from the builder includes only those for the specified targets.
129             Assert.True(result.HasResultsForTarget("Baz"));
130             Assert.False(result.HasResultsForTarget("Bar"));
131             Assert.Equal(TargetResultCode.Success, result["Baz"].ResultCode);
132 
133             // The results cache should have ALL of the results.
134             IResultsCache resultsCache = (IResultsCache)_host.GetComponent(BuildComponentType.ResultsCache);
135             Assert.True(resultsCache.GetResultForRequest(entry.Request).HasResultsForTarget("Bar"));
136             Assert.Equal(TargetResultCode.Success, resultsCache.GetResultForRequest(entry.Request)["Bar"].ResultCode);
137         }
138 
139         /// <summary>
140         /// Tests a project with a dependency which will be skipped because its up-to-date.
141         /// </summary>
142         [Fact]
TestDependencyBuildWithSkip()143         public void TestDependencyBuildWithSkip()
144         {
145             ProjectInstance project = CreateTestProject();
146 
147             // DepSkip depends on Skip (which skips) but should succeed itself.
148             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
149             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
150             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "DepSkip" }), cache[1]);
151             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
152             Assert.True(result.HasResultsForTarget("DepSkip"));
153             Assert.False(result.HasResultsForTarget("Skip"));
154             Assert.Equal(TargetResultCode.Success, result["DepSkip"].ResultCode);
155 
156             IResultsCache resultsCache = (IResultsCache)_host.GetComponent(BuildComponentType.ResultsCache);
157             Assert.True(resultsCache.GetResultForRequest(entry.Request).HasResultsForTarget("SkipCondition"));
158             Assert.Equal(TargetResultCode.Skipped, resultsCache.GetResultForRequest(entry.Request)["SkipCondition"].ResultCode);
159         }
160 
161         /// <summary>
162         /// This test is currently ignored because the error tasks aren't implemented yet (due to needing the task builder.)
163         /// </summary>
164         [Fact]
TestDependencyBuildWithError()165         public void TestDependencyBuildWithError()
166         {
167             ProjectInstance project = CreateTestProject();
168 
169             // The DepError target builds Foo (which succeeds), Skip (which skips) and Error (which fails), and Baz2
170             // Baz2 should not run since it came after Error.
171             // Error tries to build Foo again as an error (which is already built) and Bar, which produces outputs.
172             // DepError builds Baz as an error, which produces outputs
173             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
174             MockTaskBuilder taskBuilder = (MockTaskBuilder)_host.GetComponent(BuildComponentType.TaskBuilder);
175             taskBuilder.FailTaskNumber = 3; // Succeed on Foo's one task, and Error's first task, and fail the second.
176 
177             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
178             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "DepError" }), cache[1]);
179             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
180             Assert.True(result.HasResultsForTarget("DepError"));
181             Assert.False(result.HasResultsForTarget("Foo"));
182             Assert.False(result.HasResultsForTarget("Skip"));
183             Assert.False(result.HasResultsForTarget("Error"));
184             Assert.False(result.HasResultsForTarget("Baz2"));
185             Assert.False(result.HasResultsForTarget("Bar"));
186             Assert.False(result.HasResultsForTarget("Baz"));
187 
188             IResultsCache resultsCache = (IResultsCache)_host.GetComponent(BuildComponentType.ResultsCache);
189 
190             Assert.True(resultsCache.GetResultForRequest(entry.Request).HasResultsForTarget("Foo"));
191             Assert.True(resultsCache.GetResultForRequest(entry.Request).HasResultsForTarget("Skip"));
192             Assert.True(resultsCache.GetResultForRequest(entry.Request).HasResultsForTarget("Error"));
193             Assert.False(resultsCache.GetResultForRequest(entry.Request).HasResultsForTarget("Baz2"));
194             Assert.True(resultsCache.GetResultForRequest(entry.Request).HasResultsForTarget("Bar"));
195             Assert.True(resultsCache.GetResultForRequest(entry.Request).HasResultsForTarget("Baz"));
196             Assert.Equal(TargetResultCode.Failure, resultsCache.GetResultForRequest(entry.Request)["DepError"].ResultCode);
197             Assert.Equal(TargetResultCode.Success, resultsCache.GetResultForRequest(entry.Request)["Foo"].ResultCode);
198             Assert.Equal(TargetResultCode.Success, resultsCache.GetResultForRequest(entry.Request)["Skip"].ResultCode);
199             Assert.Equal(TargetResultCode.Failure, resultsCache.GetResultForRequest(entry.Request)["Error"].ResultCode);
200             Assert.Equal(TargetResultCode.Success, resultsCache.GetResultForRequest(entry.Request)["Bar"].ResultCode);
201             Assert.Equal(TargetResultCode.Success, resultsCache.GetResultForRequest(entry.Request)["Baz"].ResultCode);
202         }
203 
204         /// <summary>
205         /// Ensure that skipped targets only infer outputs once
206         /// </summary>
207         [Fact]
SkippedTargetsShouldOnlyInferOutputsOnce()208         public void SkippedTargetsShouldOnlyInferOutputsOnce()
209         {
210             MockLogger logger = new MockLogger();
211 
212             string path = FileUtilities.GetTemporaryFile();
213 
214             Thread.Sleep(100);
215 
216             string content = String.Format
217                 (
218 @"
219 <Project ToolsVersion='msbuilddefaulttoolsversion' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
220 
221   <Target Name='Build' DependsOnTargets='GFA;GFT;DFTA;GAFT'>
222         <Message Text='Build: [@(Outs)]' />
223   </Target>
224 
225 
226   <Target Name='GFA' Inputs='{0}' Outputs='{0}'>
227         <Message Text='GFA' />
228         <CreateItem Include='GFA'>
229         	<Output TaskParameter='Include' ItemName='Outs' />
230         </CreateItem>
231   </Target>
232   <Target Name='GFT'  Inputs='{0}' Outputs='{0}'>
233         <CreateItem Include='GFT'>
234             <Output TaskParameter='Include' ItemName='Outs' />
235         </CreateItem>
236         <Message Text='GFT' />
237   </Target>
238   <Target Name='DFTA'  Inputs='{0}' Outputs='{0}'>
239         <CreateItem Include='DFTA'>
240             <Output TaskParameter='Include' ItemName='Outs' />
241         </CreateItem>
242         <Message Text='DFTA' />
243   </Target>
244   <Target Name='GAFT'  Inputs='{0}' Outputs='{0}' DependsOnTargets='DFTA'>
245         <CreateItem Include='GAFT'>
246             <Output TaskParameter='Include' ItemName='Outs' />
247         </CreateItem>
248         <Message Text='GAFT' />
249   </Target>
250 </Project>
251             ",
252              path
253              );
254 
255             Project p = new Project(XmlReader.Create(new StringReader(content)));
256             p.Build(new string[] { "Build" }, new ILogger[] { logger });
257 
258             // There should be no duplicates in the list - if there are, then skipped targets are being inferred multiple times
259             logger.AssertLogContains("[GFA;GFT;DFTA;GAFT]");
260 
261             File.Delete(path);
262         }
263 
264         /// <summary>
265         /// Test empty before targets
266         /// </summary>
267         [Fact]
TestLegacyCallTarget()268         public void TestLegacyCallTarget()
269         {
270             string projectBody = @"
271 <Target Name='Build'>
272     <CallTarget Targets='Foo;Goo'/>
273 </Target>
274 
275 <Target Name='Foo' DependsOnTargets='Foo2'>
276     <FooTarget/>
277 </Target>
278 
279 <Target Name='Goo'>
280     <GooTarget/>
281 </Target>
282 
283 <Target Name='Foo2'>
284     <Foo2Target/>
285 </Target>
286 ";
287 
288             ProjectInstance project = CreateTestProject(projectBody);
289 
290             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
291             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
292             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
293 
294             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
295             AssertTaskExecutionOrder(new string[] { "CallTarget", "Foo2Target", "FooTarget", "GooTarget" });
296         }
297 
298         /// <summary>
299         /// BeforeTargets specifies a missing target. Should not warn or error.
300         /// </summary>
301         [Fact]
TestBeforeTargetsMissing()302         public void TestBeforeTargetsMissing()
303         {
304             string content = @"
305 <Project DefaultTargets='t' xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
306 
307     <Target Name='t' BeforeTargets='x'>
308         <Message Text='[t]' />
309     </Target>
310 </Project>
311                 ";
312 
313             MockLogger log = Helpers.BuildProjectWithNewOMExpectSuccess(content);
314 
315             log.AssertLogContains("[t]");
316             log.AssertLogDoesntContain("MSB4057"); // missing target
317             log.AssertNoErrors();
318             log.AssertNoWarnings();
319         }
320 
321         /// <summary>
322         /// BeforeTargets specifies a missing target. Should not warn or error.
323         /// </summary>
324         [Fact]
TestBeforeTargetsMissingRunsOthers()325         public void TestBeforeTargetsMissingRunsOthers()
326         {
327             string content = @"
328 <Project DefaultTargets='a;c' xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
329 
330     <Target Name='t' BeforeTargets='a;b;c'>
331         <Message Text='[t]' />
332     </Target>
333 
334     <Target Name='a'>
335         <Message Text='[a]' />
336     </Target>
337 
338     <Target Name='c'>
339         <Message Text='[c]' />
340     </Target>
341 
342 </Project>
343                 ";
344 
345             MockLogger log = Helpers.BuildProjectWithNewOMExpectSuccess(content);
346 
347             log.AssertLogContains("[t]", "[a]", "[c]");
348             log.AssertLogDoesntContain("MSB4057"); // missing target
349             log.AssertNoErrors();
350             log.AssertNoWarnings();
351         }
352 
353         /// <summary>
354         /// AfterTargets specifies a missing target. Should not warn or error.
355         /// </summary>
356         [Fact]
TestAfterTargetsMissing()357         public void TestAfterTargetsMissing()
358         {
359             string content = @"
360 <Project DefaultTargets='t' xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
361 
362     <Target Name='t' AfterTargets='x'>
363         <Message Text='[t]' />
364     </Target>
365 </Project>
366                 ";
367 
368             MockLogger log = Helpers.BuildProjectWithNewOMExpectSuccess(content);
369 
370             log.AssertLogContains("[t]");
371             log.AssertLogDoesntContain("MSB4057"); // missing target
372             log.AssertNoErrors();
373             log.AssertNoWarnings();
374         }
375 
376         /// <summary>
377         /// AfterTargets specifies a missing target. Should not warn or error.
378         /// </summary>
379         [Fact]
TestAfterTargetsMissingRunsOthers()380         public void TestAfterTargetsMissingRunsOthers()
381         {
382             string content = @"
383 <Project DefaultTargets='a;c' xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
384 
385     <Target Name='t' AfterTargets='a;b'>
386         <Message Text='[t]' />
387     </Target>
388 
389     <Target Name='t2' AfterTargets='b;c'>
390         <Message Text='[t2]' />
391     </Target>
392 
393     <Target Name='a'>
394         <Message Text='[a]' />
395     </Target>
396 
397     <Target Name='c'>
398         <Message Text='[c]' />
399     </Target>
400 
401 </Project>
402                 ";
403 
404             MockLogger log = Helpers.BuildProjectWithNewOMExpectSuccess(content);
405 
406             log.AssertLogContains("[a]", "[t]", "[c]", "[t2]");
407             log.AssertLogDoesntContain("MSB4057"); // missing target
408             log.AssertNoErrors();
409             log.AssertNoWarnings();
410         }
411 
412         /// <summary>
413         /// Test empty before targets
414         /// </summary>
415         [Fact]
TestBeforeTargetsEmpty()416         public void TestBeforeTargetsEmpty()
417         {
418             string projectBody = @"
419 <Target Name='Build'>
420     <BuildTask/>
421 </Target>
422 
423 <Target Name='Before' BeforeTargets=''>
424     <BeforeTask/>
425 </Target>";
426 
427             ProjectInstance project = CreateTestProject(projectBody);
428 
429             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
430             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
431             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
432 
433             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
434             AssertTaskExecutionOrder(new string[] { "BuildTask" });
435         }
436 
437         /// <summary>
438         /// Test single before targets
439         /// </summary>
440         [Fact]
TestBeforeTargetsSingle()441         public void TestBeforeTargetsSingle()
442         {
443             string projectBody = @"
444 <Target Name='Build' Outputs='$(Test)'>
445     <BuildTask/>
446 </Target>
447 
448 <Target Name='Before' BeforeTargets='Build'>
449     <BeforeTask/>
450 </Target>";
451 
452             ProjectInstance project = CreateTestProject(projectBody);
453 
454             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
455             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
456             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
457 
458             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
459             AssertTaskExecutionOrder(new string[] { "BeforeTask", "BuildTask" });
460         }
461 
462         /// <summary>
463         /// Test single before targets on an escaped target
464         /// </summary>
465         [Fact]
TestBeforeTargetsEscaped()466         public void TestBeforeTargetsEscaped()
467         {
468             string projectBody = @"
469 <Target Name='Build;Me' Outputs='$(Test)'>
470     <BuildTask/>
471 </Target>
472 
473 <Target Name='Before' BeforeTargets='Build%3bMe'>
474     <BeforeTask/>
475 </Target>";
476 
477             ProjectInstance project = CreateTestProject(projectBody);
478 
479             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
480             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
481             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build;Me" }), cache[1]);
482 
483             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
484             AssertTaskExecutionOrder(new string[] { "BeforeTask", "BuildTask" });
485         }
486 
487         /// <summary>
488         /// Test single before targets
489         /// </summary>
490         [Fact]
TestBeforeTargetsSingleWithError()491         public void TestBeforeTargetsSingleWithError()
492         {
493             string projectBody = @"
494 <Target Name='Before' BeforeTargets='Build'>
495     <BeforeTask/>
496 </Target>
497 
498 <Target Name='Build'>
499     <BuildTask/>
500 </Target>
501 ";
502 
503             MockTaskBuilder taskBuilder = (MockTaskBuilder)_host.GetComponent(BuildComponentType.TaskBuilder);
504             taskBuilder.FailTaskNumber = 2; // Succeed on BeforeTask, fail on BuildTask
505 
506             ProjectInstance project = CreateTestProject(projectBody);
507 
508             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
509             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
510             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
511 
512             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
513             AssertTaskExecutionOrder(new string[] { "BeforeTask", "BuildTask" });
514         }
515 
516         /// <summary>
517         /// Test single before targets
518         /// </summary>
519         [Fact]
TestBeforeTargetsSingleWithErrorAndParent()520         public void TestBeforeTargetsSingleWithErrorAndParent()
521         {
522             string projectBody = @"
523 <Target Name='Before' BeforeTargets='Build'>
524     <BeforeTask/>
525 </Target>
526 
527 <Target Name='Build'>
528     <BuildTask/>
529     <OnError ExecuteTargets='ErrorTarget'/>
530 </Target>
531 
532 <Target Name='ErrorTarget'>
533     <Error/>
534 </Target>
535 ";
536 
537             MockTaskBuilder taskBuilder = (MockTaskBuilder)_host.GetComponent(BuildComponentType.TaskBuilder);
538             taskBuilder.FailTaskNumber = 2; // Succeed on BeforeTask, fail on BuildTask
539 
540             ProjectInstance project = CreateTestProject(projectBody);
541 
542             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
543             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
544             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
545 
546             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
547             AssertTaskExecutionOrder(new string[] { "BeforeTask", "BuildTask", "Error" });
548         }
549 
550         /// <summary>
551         /// Test multiple before targets
552         /// </summary>
553         [Fact]
TestBeforeTargetsWithTwoReferringToOne()554         public void TestBeforeTargetsWithTwoReferringToOne()
555         {
556             string projectBody = @"
557 <Target Name='Build' Outputs='$(Test)'>
558     <BuildTask/>
559 </Target>
560 
561 <Target Name='Before' BeforeTargets='Build'>
562     <BeforeTask/>
563 </Target>
564 
565 
566 <Target Name='Before2' BeforeTargets='Build'>
567     <BeforeTask2/>
568 </Target>
569 ";
570 
571             ProjectInstance project = CreateTestProject(projectBody);
572 
573             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
574             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
575             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
576 
577             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
578             AssertTaskExecutionOrder(new string[] { "BeforeTask", "BeforeTask2", "BuildTask" });
579         }
580 
581         /// <summary>
582         /// Test multiple before targets
583         /// </summary>
584         [Fact]
TestBeforeTargetsWithOneReferringToTwo()585         public void TestBeforeTargetsWithOneReferringToTwo()
586         {
587             string projectBody = @"
588 <Target Name='Build' Outputs='$(Test)'>
589     <BuildTask/>
590 </Target>
591 
592 <Target Name='Foo' Outputs='$(Test)'>
593     <FooTask/>
594 </Target>
595 
596 <Target Name='Before' BeforeTargets='Build;Foo'>
597     <BeforeTask/>
598 </Target>
599 ";
600 
601             ProjectInstance project = CreateTestProject(projectBody);
602 
603             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
604             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
605             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Foo" }), cache[1]);
606 
607             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
608             AssertTaskExecutionOrder(new string[] { "BeforeTask", "FooTask" });
609         }
610 
611         /// <summary>
612         /// Test before target on a skipped target
613         /// </summary>
614         [Fact]
TestBeforeTargetsSkip()615         public void TestBeforeTargetsSkip()
616         {
617             string projectBody = @"
618 <Target Name='Build' Condition=""'0'=='1'"">
619     <BuildTask/>
620 </Target>
621 
622 <Target Name='Before' BeforeTargets='Build'>
623     <BeforeTask/>
624 </Target>";
625 
626             ProjectInstance project = CreateTestProject(projectBody);
627 
628             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
629             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
630             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
631 
632             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
633             AssertTaskExecutionOrder(new string[] { "BeforeTask" });
634         }
635 
636         /// <summary>
637         /// Test before target on a skipped target
638         /// </summary>
639         [Fact]
TestBeforeTargetsDependencyOrdering()640         public void TestBeforeTargetsDependencyOrdering()
641         {
642             string projectBody = @"
643 <Target Name='Build' DependsOnTargets='BuildDep'>
644     <BuildTask/>
645 </Target>
646 
647 <Target Name='Before' DependsOnTargets='BeforeDep' BeforeTargets='Build'>
648     <BeforeTask/>
649 </Target>
650 
651 <Target Name='BuildDep'>
652     <BuildDepTask/>
653 </Target>
654 
655 <Target Name='BeforeDep'>
656     <BeforeDepTask/>
657 </Target>
658 
659 ";
660 
661             ProjectInstance project = CreateTestProject(projectBody);
662 
663             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
664             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
665             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
666 
667             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
668             AssertTaskExecutionOrder(new string[] { "BuildDepTask", "BeforeDepTask", "BeforeTask", "BuildTask" });
669         }
670 
671         /// <summary>
672         /// Test after target on a skipped target
673         /// </summary>
674         [Fact]
TestAfterTargetsEmpty()675         public void TestAfterTargetsEmpty()
676         {
677             string projectBody = @"
678 <Target Name='Build'>
679     <BuildTask/>
680 </Target>
681 
682 <Target Name='After' AfterTargets=''>
683     <AfterTask/>
684 </Target>";
685 
686             ProjectInstance project = CreateTestProject(projectBody);
687 
688             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
689             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
690             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
691 
692             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
693             AssertTaskExecutionOrder(new string[] { "BuildTask" });
694         }
695 
696         /// <summary>
697         /// Test after target on a skipped target
698         /// </summary>
699         [Fact]
TestAfterTargetsSkip()700         public void TestAfterTargetsSkip()
701         {
702             string projectBody = @"
703 <Target Name='Build' Condition=""'0'=='1'"">
704     <BuildTask/>
705 </Target>
706 
707 <Target Name='After' AfterTargets='Build'>
708     <AfterTask/>
709 </Target>";
710 
711             ProjectInstance project = CreateTestProject(projectBody);
712 
713             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
714             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
715             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
716 
717             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
718             AssertTaskExecutionOrder(new string[] { "AfterTask" });
719         }
720 
721         /// <summary>
722         /// Test single before targets
723         /// </summary>
724         [Fact]
TestAfterTargetsSingleWithError()725         public void TestAfterTargetsSingleWithError()
726         {
727             string projectBody = @"
728 <Target Name='Build'>
729     <BuildTask/>
730 </Target>
731 
732 <Target Name='After' AfterTargets='Build'>
733     <AfterTask/>
734 </Target>";
735 
736             MockTaskBuilder taskBuilder = (MockTaskBuilder)_host.GetComponent(BuildComponentType.TaskBuilder);
737             taskBuilder.FailTaskNumber = 1; // Fail on BuildTask
738 
739             ProjectInstance project = CreateTestProject(projectBody);
740 
741             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
742             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
743             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
744 
745             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
746             AssertTaskExecutionOrder(new string[] { "BuildTask" });
747         }
748 
749         /// <summary>
750         /// Test single before targets
751         /// </summary>
752         [Fact]
TestAfterTargetsSingleWithErrorAndParent()753         public void TestAfterTargetsSingleWithErrorAndParent()
754         {
755             string projectBody = @"
756 <Target Name='After' AfterTargets='Build'>
757     <AfterTask/>
758 </Target>
759 
760 <Target Name='Build'>
761     <BuildTask/>
762     <OnError ExecuteTargets='ErrorTarget'/>
763 </Target>
764 
765 <Target Name='ErrorTarget'>
766     <Error/>
767 </Target>
768 
769 <Target Name='ErrorTarget2'>
770     <Error2/>
771 </Target>
772 
773 <Target Name='PostBuild' DependsOnTargets='Build'>
774     <OnError ExecuteTargets='ErrorTarget2'/>
775 </Target>
776 ";
777 
778             MockTaskBuilder taskBuilder = (MockTaskBuilder)_host.GetComponent(BuildComponentType.TaskBuilder);
779             taskBuilder.FailTaskNumber = 2; // Succeed on BuildTask, fail on AfterTask
780 
781             ProjectInstance project = CreateTestProject(projectBody);
782 
783             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
784             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
785             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "PostBuild" }), cache[1]);
786 
787             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
788             AssertTaskExecutionOrder(new string[] { "BuildTask", "AfterTask", "Error2" });
789         }
790 
791         /// <summary>
792         /// Test after target on a normal target
793         /// </summary>
794         [Fact]
TestAfterTargetsSingle()795         public void TestAfterTargetsSingle()
796         {
797             string projectBody = @"
798 <Target Name='Build'>
799     <BuildTask/>
800 </Target>
801 
802 <Target Name='After' AfterTargets='Build'>
803     <AfterTask/>
804 </Target>";
805 
806             ProjectInstance project = CreateTestProject(projectBody);
807 
808             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
809             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
810             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
811 
812             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
813             AssertTaskExecutionOrder(new string[] { "BuildTask", "AfterTask" });
814         }
815 
816         /// <summary>
817         /// Test after target on a target name which needs escaping
818         /// </summary>
819         [Fact]
TestAfterTargetsEscaped()820         public void TestAfterTargetsEscaped()
821         {
822             string projectBody = @"
823 <Target Name='Build;Me'>
824     <BuildTask/>
825 </Target>
826 
827 <Target Name='After' AfterTargets='Build%3bMe'>
828     <AfterTask/>
829 </Target>";
830 
831             ProjectInstance project = CreateTestProject(projectBody);
832 
833             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
834             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
835             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build;Me" }), cache[1]);
836 
837             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
838             AssertTaskExecutionOrder(new string[] { "BuildTask", "AfterTask" });
839         }
840 
841         /// <summary>
842         /// Test after target on a skipped target
843         /// </summary>
844         [Fact]
TestAfterTargetsWithTwoReferringToOne()845         public void TestAfterTargetsWithTwoReferringToOne()
846         {
847             string projectBody = @"
848 <Target Name='Build'>
849     <BuildTask/>
850 </Target>
851 
852 <Target Name='After' AfterTargets='Build'>
853     <AfterTask/>
854 </Target>
855 
856 <Target Name='After2' AfterTargets='Build'>
857     <AfterTask2/>
858 </Target>
859 ";
860 
861             ProjectInstance project = CreateTestProject(projectBody);
862 
863             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
864             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
865             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
866 
867             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
868             AssertTaskExecutionOrder(new string[] { "BuildTask", "AfterTask", "AfterTask2" });
869         }
870 
871         /// <summary>
872         /// Test after target on a skipped target
873         /// </summary>
874         [Fact]
TestAfterTargetsWithOneReferringToTwo()875         public void TestAfterTargetsWithOneReferringToTwo()
876         {
877             string projectBody = @"
878 <Target Name='Build'>
879     <BuildTask/>
880 </Target>
881 
882 <Target Name='Foo'>
883     <FooTask/>
884 </Target>
885 
886 <Target Name='After' AfterTargets='Build;Foo'>
887     <AfterTask/>
888 </Target>
889 ";
890 
891             ProjectInstance project = CreateTestProject(projectBody);
892 
893             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
894             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
895             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Foo" }), cache[1]);
896 
897             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
898             AssertTaskExecutionOrder(new string[] { "FooTask", "AfterTask" });
899         }
900 
901         /// <summary>
902         /// Test after target on a skipped target
903         /// </summary>
904         [Fact]
TestAfterTargetsWithDependencyOrdering()905         public void TestAfterTargetsWithDependencyOrdering()
906         {
907             string projectBody = @"
908 <Target Name='Build' DependsOnTargets='BuildDep'>
909     <BuildTask/>
910 </Target>
911 
912 <Target Name='After' DependsOnTargets='AfterDep' AfterTargets='Build'>
913     <AfterTask/>
914 </Target>
915 
916 <Target Name='BuildDep'>
917     <BuildDepTask/>
918 </Target>
919 
920 <Target Name='AfterDep'>
921     <AfterDepTask/>
922 </Target>
923 ";
924 
925             ProjectInstance project = CreateTestProject(projectBody);
926 
927             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
928             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
929             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
930 
931             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
932             AssertTaskExecutionOrder(new string[] { "BuildDepTask", "BuildTask", "AfterDepTask", "AfterTask" });
933         }
934 
935         /// <summary>
936         /// Test a complex ordering with depends, before and after targets
937         /// </summary>
938         [Fact]
TestComplexOrdering()939         public void TestComplexOrdering()
940         {
941             string projectBody = @"
942 <Target Name='Build' DependsOnTargets='BuildDep'>
943     <BuildTask/>
944 </Target>
945 
946 <Target Name='Before' DependsOnTargets='BeforeDep' BeforeTargets='Build'>
947     <BeforeTask/>
948 </Target>
949 
950 <Target Name='After' DependsOnTargets='AfterDep' AfterTargets='Build'>
951     <AfterTask/>
952 </Target>
953 
954 <Target Name='BuildDep'>
955     <BuildDepTask/>
956 </Target>
957 
958 <Target Name='AfterDep' DependsOnTargets='AfterDepDep'>
959     <AfterDepTask/>
960 </Target>
961 
962 <Target Name='BeforeDep' DependsOnTargets='BeforeDepDep'>
963     <BeforeDepTask/>
964 </Target>
965 
966 <Target Name='BeforeDepDep'>
967     <BeforeDepDepTask/>
968 </Target>
969 
970 <Target Name='AfterDepDep'>
971     <AfterDepDepTask/>
972 </Target>
973 ";
974 
975             ProjectInstance project = CreateTestProject(projectBody);
976 
977             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
978             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
979             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
980 
981             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
982             AssertTaskExecutionOrder(new string[] { "BuildDepTask", "BeforeDepDepTask", "BeforeDepTask", "BeforeTask", "BuildTask", "AfterDepDepTask", "AfterDepTask", "AfterTask" });
983         }
984 
985         /// <summary>
986         /// Test a complex ordering with depends, before and after targets
987         /// </summary>
988         [Fact]
TestComplexOrdering2()989         public void TestComplexOrdering2()
990         {
991             string projectBody = @"
992 <Target Name='BuildDep'>
993     <BuildDepTask/>
994 </Target>
995 
996 <Target Name='BeforeDepDep'>
997     <BeforeDepDepTask/>
998 </Target>
999 
1000 <Target Name='BeforeBeforeDep' BeforeTargets='BeforeDep'>
1001     <BeforeBeforeDepTask/>
1002 </Target>
1003 
1004 <Target Name='AfterBeforeBeforeDep' AfterTargets='BeforeBeforeDep'>
1005     <AfterBeforeBeforeDepTask/>
1006 </Target>
1007 
1008 <Target Name='BeforeDep' DependsOnTargets='BeforeDepDep'>
1009     <BeforeDepTask/>
1010 </Target>
1011 
1012 <Target Name='Before' DependsOnTargets='BeforeDep' BeforeTargets='Build'>
1013     <BeforeTask/>
1014 </Target>
1015 
1016 <Target Name='AfterBeforeDepDep'>
1017     <AfterBeforeDepDepTask/>
1018 </Target>
1019 
1020 <Target Name='AfterBeforeDep' DependsOnTargets='AfterBeforeDepDep'>
1021     <AfterBeforeDepTask/>
1022 </Target>
1023 
1024 <Target Name='AfterBefore' DependsOnTargets='AfterBeforeDep' AfterTargets='Before'>
1025     <AfterBeforeTask/>
1026 </Target>
1027 
1028 <Target Name='Build' DependsOnTargets='BuildDep'>
1029     <BuildTask/>
1030 </Target>
1031 
1032 ";
1033 
1034             ProjectInstance project = CreateTestProject(projectBody);
1035 
1036             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
1037             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
1038             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
1039 
1040             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
1041             AssertTaskExecutionOrder(new string[] { "BuildDepTask", "BeforeDepDepTask", "BeforeBeforeDepTask", "AfterBeforeBeforeDepTask", "BeforeDepTask", "BeforeTask", "AfterBeforeDepDepTask", "AfterBeforeDepTask", "AfterBeforeTask", "BuildTask" });
1042         }
1043 
1044         /// <summary>
1045         /// Test a complex ordering with depends, before and after targets
1046         /// </summary>
1047         [Fact]
TestBeforeAndAfterWithErrorTargets()1048         public void TestBeforeAndAfterWithErrorTargets()
1049         {
1050             string projectBody = @"
1051 
1052 
1053 <Target Name='Build' >
1054     <BuildTask/>
1055     <OnError ExecuteTargets='ErrorTarget'/>
1056 </Target>
1057 
1058 <Target Name='ErrorTarget'>
1059     <ErrorTargetTask/>
1060 </Target>
1061 
1062 <Target Name='BeforeErrorTarget' BeforeTargets='ErrorTarget'>
1063     <BeforeErrorTargetTask/>
1064 </Target>
1065 
1066 <Target Name='AfterErrorTarget' AfterTargets='ErrorTarget'>
1067     <AfterErrorTargetTask/>
1068 </Target>
1069 
1070 ";
1071 
1072             MockTaskBuilder taskBuilder = (MockTaskBuilder)_host.GetComponent(BuildComponentType.TaskBuilder);
1073             taskBuilder.FailTaskNumber = 1; // Fail on BuildTask
1074 
1075             ProjectInstance project = CreateTestProject(projectBody);
1076 
1077             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
1078             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
1079             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
1080 
1081             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
1082             AssertTaskExecutionOrder(new string[] { "BuildTask", "BeforeErrorTargetTask", "ErrorTargetTask", "AfterErrorTargetTask" });
1083         }
1084 
1085         /// <summary>
1086         /// Test after target on a skipped target
1087         /// </summary>
1088         [Fact]
TestBeforeAndAfterOverrides()1089         public void TestBeforeAndAfterOverrides()
1090         {
1091             string projectBody = @"
1092 
1093 <Target Name='BuildDep'>
1094     <BuildDepTask/>
1095 </Target>
1096 
1097 <Target Name='Build' DependsOnTargets='BuildDep'>
1098     <BuildTask/>
1099 </Target>
1100 
1101 <Target Name='After' AfterTargets='Build'>
1102     <AfterTask/>
1103 </Target>
1104 
1105 <Target Name='After' AfterTargets='BuildDep'>
1106     <AfterTask/>
1107 </Target>
1108 
1109 <Target Name='Before' BeforeTargets='Build'>
1110     <BeforeTask/>
1111 </Target>
1112 
1113 <Target Name='Before' BeforeTargets='BuildDep'>
1114     <BeforeTask/>
1115 </Target>
1116 
1117 ";
1118 
1119             ProjectInstance project = CreateTestProject(projectBody);
1120 
1121             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
1122             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
1123             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
1124 
1125             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
1126             AssertTaskExecutionOrder(new string[] { "BeforeTask", "BuildDepTask", "AfterTask", "BuildTask" });
1127         }
1128 
1129         /// <summary>
1130         /// Test that if before and after targets skip, the main target still runs (bug 476908)
1131         /// </summary>
1132         [Fact]
TestSkippingBeforeAndAfterTargets()1133         public void TestSkippingBeforeAndAfterTargets()
1134         {
1135             string projectBody = @"
1136 <Target Name='Build'>
1137     <BuildTask/>
1138 </Target>
1139 
1140 <Target Name='Before' BeforeTargets='Build' Condition=""'0'=='1'"">
1141     <BeforeTask/>
1142 </Target>
1143 
1144 <Target Name='After' AfterTargets='Build' Condition=""'0'=='1'"">
1145     <AfterTask/>
1146 </Target>
1147 ";
1148 
1149             ProjectInstance project = CreateTestProject(projectBody);
1150 
1151             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
1152             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
1153             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
1154 
1155             BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
1156             AssertTaskExecutionOrder(new string[] { "BuildTask" });
1157         }
1158 
1159         /// <summary>
1160         /// Tests that a circular dependency within a CallTarget call correctly propagates the failure.  Bug 502570.
1161         /// </summary>
1162         [Fact]
TestCircularDependencyInCallTarget()1163         public void TestCircularDependencyInCallTarget()
1164         {
1165             string projectContents = @"
1166 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
1167     <Target Name=""t1"">
1168         <CallTarget Targets=""t3""/>
1169     </Target>
1170     <Target Name=""t2"" DependsOnTargets=""t1"">
1171     </Target>
1172     <Target Name=""t3"" DependsOnTargets=""t2"">
1173     </Target>
1174 </Project>
1175       ";
1176             StringReader reader = new StringReader(projectContents);
1177 #if FEATURE_XMLTEXTREADER
1178             Project project = new Project(new XmlTextReader(reader), null, null);
1179 #else
1180             Project project = new Project(XmlReader.Create(reader), null, null);
1181 #endif
1182             bool success = project.Build(_mockLogger);
1183             Assert.False(success);
1184         }
1185 
1186         /// <summary>
1187         /// Tests that cancel with no entries after building does not fail.
1188         /// </summary>
1189         [Fact]
TestCancelWithNoEntriesAfterBuild()1190         public void TestCancelWithNoEntriesAfterBuild()
1191         {
1192             string projectBody = @"
1193 <Target Name='Build'>
1194     <BuildTask/>
1195 </Target>
1196 ";
1197 
1198             ProjectInstance project = CreateTestProject(projectBody);
1199 
1200             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
1201             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
1202             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new string[] { "Build" }), cache[1]);
1203             using (CancellationTokenSource source = new CancellationTokenSource())
1204             {
1205                 BuildResult result = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), source.Token).Result;
1206                 AssertTaskExecutionOrder(new string[] { "BuildTask" });
1207 
1208                 // This simply should not fail.
1209                 source.Cancel();
1210             }
1211         }
1212 
1213         [Fact]
SkippedTargetWithFailedDependenciesStopsBuild()1214         public void SkippedTargetWithFailedDependenciesStopsBuild()
1215         {
1216             string projectContents = @"
1217   <Target Name=""Build"" DependsOnTargets=""ProduceError1;ProduceError2"" />
1218   <Target Name=""ProduceError1"" Condition=""false"" />
1219   <Target Name=""ProduceError2"">
1220     <ErrorTask2 />
1221   </Target>
1222   <Target Name=""_Error1"" BeforeTargets=""ProduceError1"">
1223     <ErrorTask1 />
1224   </Target>
1225 ";
1226 
1227             var project = CreateTestProject(projectContents, string.Empty, "Build");
1228             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
1229             MockTaskBuilder taskBuilder = (MockTaskBuilder)_host.GetComponent(BuildComponentType.TaskBuilder);
1230             // Fail the first task
1231             taskBuilder.FailTaskNumber = 1;
1232 
1233             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
1234             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new[] { "Build" }), cache[1]);
1235 
1236             var buildResult = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
1237 
1238             IResultsCache resultsCache = (IResultsCache)_host.GetComponent(BuildComponentType.ResultsCache);
1239             Assert.True(resultsCache.GetResultForRequest(entry.Request).HasResultsForTarget("Build"));
1240             Assert.True(resultsCache.GetResultForRequest(entry.Request).HasResultsForTarget("ProduceError1"));
1241             Assert.False(resultsCache.GetResultForRequest(entry.Request).HasResultsForTarget("ProduceError2"));
1242             Assert.True(resultsCache.GetResultForRequest(entry.Request).HasResultsForTarget("_Error1"));
1243 
1244             Assert.Equal(TargetResultCode.Failure, resultsCache.GetResultForRequest(entry.Request)["Build"].ResultCode);
1245             Assert.Equal(TargetResultCode.Skipped, resultsCache.GetResultForRequest(entry.Request)["ProduceError1"].ResultCode);
1246             Assert.Equal(TargetResultCode.Failure, resultsCache.GetResultForRequest(entry.Request)["_Error1"].ResultCode);
1247         }
1248 
1249         [Fact]
SkipNonexistentTargetsDoesNotExecuteOrCacheTargetResult()1250         public void SkipNonexistentTargetsDoesNotExecuteOrCacheTargetResult()
1251         {
1252             string projectContents = @"<Target Name=""Build"" />";
1253 
1254             var project = CreateTestProject(projectContents, string.Empty, "Build");
1255             TargetBuilder builder = (TargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder);
1256             MockTaskBuilder taskBuilder = (MockTaskBuilder)_host.GetComponent(BuildComponentType.TaskBuilder);
1257             taskBuilder.FailTaskNumber = 1;
1258 
1259             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
1260             BuildRequestEntry entry = new BuildRequestEntry(CreateNewBuildRequest(1, new[] { "NotFound" }, BuildRequestDataFlags.SkipNonexistentTargets), cache[1]);
1261             var buildResult = builder.BuildTargets(GetProjectLoggingContext(entry), entry, this, entry.Request.Targets.ToArray(), CreateStandardLookup(project), CancellationToken.None).Result;
1262 
1263             IResultsCache resultsCache = (IResultsCache)_host.GetComponent(BuildComponentType.ResultsCache);
1264 
1265             // This should throw because the results cache should not contain the results for the target that was not
1266             // executed. This is different than when a target is skipped due to condition not met where the result
1267             // would be in the cache. In this case we can't cache the result because it is only valid for the single
1268             // request.
1269             Assert.Throws<InternalErrorException>(() => resultsCache.GetResultForRequest(entry.Request));
1270         }
1271 
1272         #region IRequestBuilderCallback Members
1273 
1274         /// <summary>
1275         /// We have to have this interface, but it won't be used in this test because we aren't doing MSBuild callbacks.
1276         /// </summary>
1277         /// <param name="projectFiles">N/A</param>
1278         /// <param name="properties">N/A</param>
1279         /// <param name="toolsVersions">N/A</param>
1280         /// <param name="targets">N/A</param>
1281         /// <param name="waitForResults">N/A</param>
1282         /// <param name="skipNonexistentTargets">N/A</param>
1283         /// <returns>N/A</returns>
IRequestBuilderCallback.BuildProjects(string[] projectFiles, PropertyDictionary<ProjectPropertyInstance>[] properties, string[] toolsVersions, string[] targets, bool waitForResults, bool skipNonexistentTargets)1284         Task<BuildResult[]> IRequestBuilderCallback.BuildProjects(string[] projectFiles, PropertyDictionary<ProjectPropertyInstance>[] properties, string[] toolsVersions, string[] targets, bool waitForResults, bool skipNonexistentTargets)
1285         {
1286             throw new NotImplementedException();
1287         }
1288 
1289         /// <summary>
1290         /// Not implemented
1291         /// </summary>
IRequestBuilderCallback.BlockOnTargetInProgress(int blockingRequestId, string blockingTarget, BuildResult partialBuildResult)1292         Task IRequestBuilderCallback.BlockOnTargetInProgress(int blockingRequestId, string blockingTarget, BuildResult partialBuildResult)
1293         {
1294             throw new NotImplementedException();
1295         }
1296 
1297         /// <summary>
1298         /// Empty impl
1299         /// </summary>
IRequestBuilderCallback.Yield()1300         void IRequestBuilderCallback.Yield()
1301         {
1302         }
1303 
1304         /// <summary>
1305         /// Empty impl
1306         /// </summary>
IRequestBuilderCallback.Reacquire()1307         void IRequestBuilderCallback.Reacquire()
1308         {
1309         }
1310 
1311         /// <summary>
1312         /// Empty impl
1313         /// </summary>
IRequestBuilderCallback.EnterMSBuildCallbackState()1314         void IRequestBuilderCallback.EnterMSBuildCallbackState()
1315         {
1316         }
1317 
1318         /// <summary>
1319         /// Empty impl
1320         /// </summary>
IRequestBuilderCallback.ExitMSBuildCallbackState()1321         void IRequestBuilderCallback.ExitMSBuildCallbackState()
1322         {
1323         }
1324 
1325         #endregion
1326 
1327         /// <summary>
1328         /// Verifies the order in which tasks executed.
1329         /// </summary>
AssertTaskExecutionOrder(string[] tasks)1330         private void AssertTaskExecutionOrder(string[] tasks)
1331         {
1332             MockTaskBuilder mockBuilder = (MockTaskBuilder)_host.GetComponent(BuildComponentType.TaskBuilder);
1333 
1334             Assert.Equal(tasks.Length, mockBuilder.ExecutedTasks.Count);
1335 
1336             int currentTask = 0;
1337             foreach (ProjectTaskInstance task in mockBuilder.ExecutedTasks)
1338             {
1339                 Assert.True(String.Equals(task.Name, tasks[currentTask]));
1340                 currentTask++;
1341             }
1342         }
1343 
1344         /// <summary>
1345         /// Creates a new build request
1346         /// </summary>
CreateNewBuildRequest(int configurationId, string[] targets, BuildRequestDataFlags flags = BuildRequestDataFlags.None)1347         private BuildRequest CreateNewBuildRequest(int configurationId, string[] targets, BuildRequestDataFlags flags = BuildRequestDataFlags.None)
1348         {
1349             return new BuildRequest(1 /* submissionId */, _nodeRequestId++, configurationId, targets, null, BuildEventContext.Invalid, null, flags);
1350         }
1351 
1352         /// <summary>
1353         /// Creates a 'Lookup' used to deal with projects.
1354         /// </summary>
1355         /// <param name="project">The project for which to create the lookup</param>
1356         /// <returns>The lookup</returns>
CreateStandardLookup(ProjectInstance project)1357         private Lookup CreateStandardLookup(ProjectInstance project)
1358         {
1359             Lookup lookup = new Lookup(new ItemDictionary<ProjectItemInstance>(project.Items), new PropertyDictionary<ProjectPropertyInstance>(project.Properties), null);
1360             return lookup;
1361         }
1362 
1363         /// <summary>
1364         /// Creates a test project.
1365         /// </summary>
1366         /// <returns>The project.</returns>
CreateTestProject()1367         private ProjectInstance CreateTestProject()
1368         {
1369             string projectBodyContents = @"
1370                     <ItemGroup>
1371                         <Compile Include='b.cs' />
1372                         <Compile Include='c.cs' />
1373                     </ItemGroup>
1374 
1375                     <ItemGroup>
1376                         <Reference Include='System' />
1377                     </ItemGroup>
1378 
1379                     <Target Name='Empty' />
1380 
1381                     <Target Name='Skip' Inputs='testProject.proj' Outputs='testProject.proj' />
1382 
1383                     <Target Name='SkipCondition' Condition=""'true' == 'false'"" />
1384 
1385                     <Target Name='Error' >
1386                         <ErrorTask1 ContinueOnError='True'/>
1387                         <ErrorTask2 ContinueOnError='False'/>
1388                         <ErrorTask3 />
1389                         <OnError ExecuteTargets='Foo'/>
1390                         <OnError ExecuteTargets='Bar'/>
1391                     </Target>
1392 
1393                     <Target Name='DepError' DependsOnTargets='Foo;Skip;Error;Baz2'>
1394                         <OnError ExecuteTargets='Baz'/>
1395                     </Target>
1396 
1397                     <Target Name='Foo' Inputs='foo.cpp' Outputs='foo.o'>
1398                         <FooTask1/>
1399                     </Target>
1400 
1401                     <Target Name='Bar'>
1402                         <BarTask1/>
1403                     </Target>
1404 
1405                     <Target Name='Baz' DependsOnTargets='Bar'>
1406                         <BazTask1/>
1407                         <BazTask2/>
1408                     </Target>
1409 
1410                     <Target Name='Baz2' DependsOnTargets='Bar;Foo'>
1411                         <Baz2Task1/>
1412                         <Baz2Task2/>
1413                         <Baz2Task3/>
1414                     </Target>
1415 
1416                     <Target Name='DepSkip' DependsOnTargets='SkipCondition'>
1417                         <DepSkipTask1/>
1418                         <DepSkipTask2/>
1419                         <DepSkipTask3/>
1420                     </Target>
1421 
1422                     <Target Name='DepSkip2' DependsOnTargets='Skip' Inputs='testProject.proj' Outputs='testProject.proj'>
1423                         <DepSkipTask1/>
1424                         <DepSkipTask2/>
1425                         <DepSkipTask3/>
1426                     </Target>
1427                 ";
1428 
1429             return CreateTestProject(projectBodyContents);
1430         }
1431 
1432         /// <summary>
1433         /// Creates a test project.
1434         /// </summary>
CreateTestProject(string projectBodyContents)1435         private ProjectInstance CreateTestProject(string projectBodyContents)
1436         {
1437             return CreateTestProject(projectBodyContents, String.Empty, String.Empty);
1438         }
1439 
1440         /// <summary>
1441         /// Creates a test project.
1442         /// </summary>
CreateTestProject(string projectBodyContents, string initialTargets, string defaultTargets)1443         private ProjectInstance CreateTestProject(string projectBodyContents, string initialTargets, string defaultTargets)
1444         {
1445             string projectFileContents = String.Format("<Project ToolsVersion='msbuilddefaulttoolsversion' xmlns='http://schemas.microsoft.com/developer/msbuild/2003' InitialTargets='{0}' DefaultTargets='{1}'>{2}</Project>", initialTargets, defaultTargets, projectBodyContents);
1446 
1447             // retries to deal with occasional locking issues where the file can't be written to initially
1448             for (int retries = 0; retries < 5; retries++)
1449             {
1450                 try
1451                 {
1452                     File.Create("testProject.proj").Dispose();
1453                     break;
1454                 }
1455                 catch (Exception ex)
1456                 {
1457                     if (retries < 4)
1458                     {
1459                         Console.WriteLine(ex.ToString());
1460                     }
1461                     else
1462                     {
1463                         // All the retries have failed. We will now fail with the
1464                         // actual problem now instead of with some more difficult-to-understand
1465                         // issue later.
1466                         throw;
1467                     }
1468                 }
1469             }
1470 
1471             IConfigCache cache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache);
1472             BuildRequestConfiguration config = new BuildRequestConfiguration(1, new BuildRequestData("testFile", new Dictionary<string, string>(), "3.5", new string[0], null), "2.0");
1473             Project project = new Project(XmlReader.Create(new StringReader(projectFileContents)));
1474 
1475             config.Project = project.CreateProjectInstance();
1476             cache.AddConfiguration(config);
1477 
1478             return config.Project;
1479         }
1480 
1481         /// <summary>
1482         /// Creates a project logging context.
1483         /// </summary>
1484         /// <param name="entry">The entry on which to base the logging context.</param>
1485         /// <returns>The context</returns>
GetProjectLoggingContext(BuildRequestEntry entry)1486         private ProjectLoggingContext GetProjectLoggingContext(BuildRequestEntry entry)
1487         {
1488             return new ProjectLoggingContext(new NodeLoggingContext(_host, 1, false), entry, null);
1489         }
1490 
1491         /// <summary>
1492         /// The mock component host object.
1493         /// </summary>
1494         private class MockHost : MockLoggingService, IBuildComponentHost, IBuildComponent
1495         {
1496             #region IBuildComponentHost Members
1497 
1498             /// <summary>
1499             /// The config cache
1500             /// </summary>
1501             private IConfigCache _configCache;
1502 
1503             /// <summary>
1504             /// The logging service
1505             /// </summary>
1506             private ILoggingService _loggingService;
1507 
1508             /// <summary>
1509             /// The results cache
1510             /// </summary>
1511             private IResultsCache _resultsCache;
1512 
1513             /// <summary>
1514             /// The request builder
1515             /// </summary>
1516             private IRequestBuilder _requestBuilder;
1517 
1518             /// <summary>
1519             /// The mock task builder
1520             /// </summary>
1521             private ITaskBuilder _taskBuilder;
1522 
1523             /// <summary>
1524             /// The target builder
1525             /// </summary>
1526             private ITargetBuilder _targetBuilder;
1527 
1528             /// <summary>
1529             /// The build parameters
1530             /// </summary>
1531             private BuildParameters _buildParameters;
1532 
1533             /// <summary>
1534             /// Retrieves the LegacyThreadingData associated with a particular component host
1535             /// </summary>
1536             private LegacyThreadingData _legacyThreadingData;
1537 
1538             private ISdkResolverService _sdkResolverService;
1539 
1540             /// <summary>
1541             /// Constructor
1542             /// </summary>
MockHost()1543             public MockHost()
1544             {
1545                 _buildParameters = new BuildParameters();
1546                 _legacyThreadingData = new LegacyThreadingData();
1547 
1548                 _configCache = new ConfigCache();
1549                 ((IBuildComponent)_configCache).InitializeComponent(this);
1550 
1551                 _loggingService = this;
1552 
1553                 _resultsCache = new ResultsCache();
1554                 ((IBuildComponent)_resultsCache).InitializeComponent(this);
1555 
1556                 _requestBuilder = new RequestBuilder();
1557                 ((IBuildComponent)_requestBuilder).InitializeComponent(this);
1558 
1559                 _taskBuilder = new MockTaskBuilder();
1560                 ((IBuildComponent)_taskBuilder).InitializeComponent(this);
1561 
1562                 _targetBuilder = new TargetBuilder();
1563                 ((IBuildComponent)_targetBuilder).InitializeComponent(this);
1564 
1565                 _sdkResolverService = new MockSdkResolverService();
1566                 ((IBuildComponent)_sdkResolverService).InitializeComponent(this);
1567             }
1568 
1569             /// <summary>
1570             /// Returns the node logging service.  We don't distinguish here.
1571             /// </summary>
1572             /// <returns>The logging service.</returns>
1573             public ILoggingService LoggingService
1574             {
1575                 get
1576                 {
1577                     return _loggingService;
1578                 }
1579             }
1580 
1581             /// <summary>
1582             /// Retrieves the LegacyThreadingData associated with a particular component host
1583             /// </summary>
1584             LegacyThreadingData IBuildComponentHost.LegacyThreadingData
1585             {
1586                 get
1587                 {
1588                     return _legacyThreadingData;
1589                 }
1590             }
1591 
1592             /// <summary>
1593             /// Retrieves the name of the host.
1594             /// </summary>
1595             public string Name
1596             {
1597                 get
1598                 {
1599                     return "TargetBuilder_Tests.MockHost";
1600                 }
1601             }
1602 
1603             /// <summary>
1604             /// Returns the build parameters.
1605             /// </summary>
1606             public BuildParameters BuildParameters
1607             {
1608                 get
1609                 {
1610                     return _buildParameters;
1611                 }
1612             }
1613 
1614             /// <summary>
1615             /// Constructs and returns a component of the specified type.
1616             /// </summary>
1617             /// <param name="type">The type of component to return</param>
1618             /// <returns>The component</returns>
GetComponent(BuildComponentType type)1619             public IBuildComponent GetComponent(BuildComponentType type)
1620             {
1621                 switch (type)
1622                 {
1623                     case BuildComponentType.ConfigCache:
1624                         return (IBuildComponent)_configCache;
1625 
1626                     case BuildComponentType.LoggingService:
1627                         return (IBuildComponent)_loggingService;
1628 
1629                     case BuildComponentType.ResultsCache:
1630                         return (IBuildComponent)_resultsCache;
1631 
1632                     case BuildComponentType.RequestBuilder:
1633                         return (IBuildComponent)_requestBuilder;
1634 
1635                     case BuildComponentType.TaskBuilder:
1636                         return (IBuildComponent)_taskBuilder;
1637 
1638                     case BuildComponentType.TargetBuilder:
1639                         return (IBuildComponent)_targetBuilder;
1640 
1641                     case BuildComponentType.SdkResolverService:
1642                         return (IBuildComponent)_sdkResolverService;
1643 
1644                     default:
1645                         throw new ArgumentException("Unexpected type " + type);
1646                 }
1647             }
1648 
1649             /// <summary>
1650             /// Registers a component factory
1651             /// </summary>
RegisterFactory(BuildComponentType type, BuildComponentFactoryDelegate factory)1652             public void RegisterFactory(BuildComponentType type, BuildComponentFactoryDelegate factory)
1653             {
1654             }
1655 
1656             #endregion
1657 
1658             #region IBuildComponent Members
1659 
1660             /// <summary>
1661             /// Sets the component host
1662             /// </summary>
1663             /// <param name="host">The component host</param>
InitializeComponent(IBuildComponentHost host)1664             public void InitializeComponent(IBuildComponentHost host)
1665             {
1666                 throw new NotImplementedException();
1667             }
1668 
1669             /// <summary>
1670             /// Shuts down the component
1671             /// </summary>
ShutdownComponent()1672             public void ShutdownComponent()
1673             {
1674                 throw new NotImplementedException();
1675             }
1676 
1677             #endregion
1678         }
1679     }
1680 }
1681