[xbuild] Handle a repeat build of a target, when building through ..
[mono.git] / mcs / class / Microsoft.Build.Engine / Test / Microsoft.Build.BuildEngine / TargetTest.cs
1 //
2 // TargetTest.cs
3 //
4 // Authors:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 //   Andres G. Aragoneses (knocte@gmail.com)
7 //
8 // (C) 2006 Marek Sieradzki
9 // (C) 2012 Andres G. Aragoneses
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System;
30 using System.Collections;
31 using Microsoft.Build.BuildEngine;
32 using Microsoft.Build.Framework;
33 using Microsoft.Build.Utilities;
34 using MonoTests.Microsoft.Build.Tasks;
35 using NUnit.Framework;
36 using System.IO;
37 using System.Xml;
38
39 namespace MonoTests.Microsoft.Build.BuildEngine {
40         [TestFixture]
41         public class TargetTest {
42                 
43                 static bool isMono = Type.GetType ("Mono.Runtime", false) != null;
44                 Engine                  engine;
45                 Project                 project;
46                 
47                 [Test]
48                 public void TestFromXml1 ()
49                 {
50                         string documentString = @"
51                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
52                                         <Target Name='Target'>
53                                         </Target>
54                                 </Project>
55                         ";
56
57                         engine = new Engine (Consts.BinPath);
58
59                         project = engine.CreateNewProject ();
60                         project.LoadXml (documentString);
61
62                         Target[] t = new Target [1];
63                         project.Targets.CopyTo (t, 0);
64
65                         Assert.AreEqual (String.Empty, t [0].Condition, "A1");
66                         Assert.AreEqual (String.Empty, t [0].DependsOnTargets, "A2");
67                         Assert.IsFalse (t [0].IsImported, "A3");
68                         Assert.AreEqual ("Target", t [0].Name, "A4");
69                 }
70
71                 [Test]
72                 public void TestFromXml2 ()
73                 {
74                         string documentString = @"
75                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
76                                         <Target Name='Target' Condition='false' DependsOnTargets='X' >
77                                         </Target>
78                                 </Project>
79                         ";
80
81                         engine = new Engine (Consts.BinPath);
82
83                         project = engine.CreateNewProject ();
84                         project.LoadXml (documentString);
85
86                         Target[] t = new Target [1];
87                         project.Targets.CopyTo (t, 0);
88
89                         Assert.AreEqual ("false", t [0].Condition, "A1");
90                         Assert.AreEqual ("X", t [0].DependsOnTargets, "A2");
91
92                         t [0].Condition = "true";
93                         t [0].DependsOnTargets = "A;B";
94
95                         Assert.AreEqual ("true", t [0].Condition, "A3");
96                         Assert.AreEqual ("A;B", t [0].DependsOnTargets, "A4");
97                 }
98
99                 [Test]
100                 public void TestAddNewTask1 ()
101                 {
102                         string documentString = @"
103                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
104                                         <Target Name='Target' >
105                                         </Target>
106                                 </Project>
107                         ";
108
109                         engine = new Engine (Consts.BinPath);
110
111                         project = engine.CreateNewProject ();
112                         project.LoadXml (documentString);
113
114                         Target[] t = new Target [1];
115                         project.Targets.CopyTo (t, 0);
116
117                         BuildTask bt = t [0].AddNewTask ("Message");
118
119                         Assert.AreEqual ("Message", bt.Name, "A1");
120                 }
121
122                 [Test]
123                 [ExpectedException (typeof (ArgumentNullException))]
124                 public void TestAddNewTask2 ()
125                 {
126                         string documentString = @"
127                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
128                                         <Target Name='Target' >
129                                         </Target>
130                                 </Project>
131                         ";
132
133                         engine = new Engine (Consts.BinPath);
134
135                         project = engine.CreateNewProject ();
136                         project.LoadXml (documentString);
137
138                         Target[] t = new Target [1];
139                         project.Targets.CopyTo (t, 0);
140
141                         t [0].AddNewTask (null);
142                 }
143
144                 [Test]
145                 public void TestGetEnumerator ()
146                 {
147                         string documentString = @"
148                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
149                                         <Target Name='Target' >
150                                                 <Message Text='text' />
151                                                 <Warning Text='text' />
152                                         </Target>
153                                 </Project>
154                         ";
155
156                         engine = new Engine (Consts.BinPath);
157
158                         project = engine.CreateNewProject ();
159                         project.LoadXml (documentString);
160
161                         Target[] t = new Target [1];
162                         project.Targets.CopyTo (t, 0);
163
164                         IEnumerator e = t [0].GetEnumerator ();
165                         e.MoveNext ();
166                         Assert.AreEqual ("Message", ((BuildTask) e.Current).Name, "A1");
167                         e.MoveNext ();
168                         Assert.AreEqual ("Warning", ((BuildTask) e.Current).Name, "A2");
169                         Assert.IsFalse (e.MoveNext (), "A3");
170                 }
171
172                 [Test]
173                 public void TestOutOfRangeElementsOfTheEnumerator()
174                 {
175                         string documentString =
176                                 @"
177                                 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
178                                         <Target Name='A'>
179                                                 <Message Text='text' />
180                                         </Target>
181                                 </Project>";
182
183                         engine = new Engine (Consts.BinPath);
184
185                         project = engine.CreateNewProject ();
186                         project.LoadXml (documentString);
187
188                         Assert.IsFalse (project.Targets == null, "A1");
189                         Assert.AreEqual (1, project.Targets.Count, "A2");
190
191                         Target target = project.Targets ["A"];
192                         Assert.IsFalse (target == null, "A3");
193
194                         IEnumerator e = target.GetEnumerator ();
195
196                         bool thrown = false;
197                         try {
198                                 var name = ((BuildTask)e.Current).Name;
199                         } catch (InvalidOperationException) { // "Enumeration has not started. Call MoveNext"
200                                 thrown = true;
201                         }
202                         if (!thrown)
203                                 Assert.Fail ("A4: Should have thrown IOE");
204
205
206                         Assert.AreEqual (true, e.MoveNext (), "A5");
207                         Assert.AreEqual ("Message", ((BuildTask)e.Current).Name, "A6");
208                         Assert.AreEqual (false, e.MoveNext (), "A7");
209                         try {
210                                 var name = ((BuildTask) e.Current).Name;
211                         } catch (InvalidOperationException) { //"Enumeration already finished."
212                                 return;
213                         }
214                         Assert.Fail ("A8: Should have thrown IOE, because there's only one buidTask");
215                 }
216
217                 [Test]
218                 [ExpectedException (typeof (InvalidProjectFileException))]
219                 public void TestOnError1 ()
220                 {
221                         Engine engine;
222                         Project project;
223
224                         string documentString = @"
225                                 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
226                                         <Target Name='A'>
227                                                 <OnError ExecuteTargets='B' />
228                                                 <Error Text='text' />
229                                         </Target>
230                                 </Project>
231                         ";
232
233                         engine = new Engine (Consts.BinPath);
234                         project = engine.CreateNewProject ();
235                         project.LoadXml (documentString);
236                 }
237
238                 [Test]
239                 public void TestRemoveTask1 ()
240                 {
241                         string documentString = @"
242                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
243                                         <Target Name='Target' >
244                                                 <Message Text='text' />
245                                                 <Warning Text='text' />
246                                         </Target>
247                                 </Project>
248                         ";
249
250                         engine = new Engine (Consts.BinPath);
251
252                         project = engine.CreateNewProject ();
253                         project.LoadXml (documentString);
254
255                         Target[] t = new Target [1];
256                         project.Targets.CopyTo (t, 0);
257
258                         IEnumerator e = t [0].GetEnumerator ();
259                         e.MoveNext ();
260                         t [0].RemoveTask ((BuildTask) e.Current);
261                         e = t [0].GetEnumerator ();
262                         e.MoveNext ();
263                         Assert.AreEqual ("Warning", ((BuildTask) e.Current).Name, "A1");
264                         Assert.IsFalse (e.MoveNext (), "A2");
265                 }
266
267                 [Test]
268                 [ExpectedException (typeof (ArgumentNullException))]
269                 public void TestRemoveTask2 ()
270                 {
271                         string documentString = @"
272                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
273                                         <Target Name='Target' >
274                                         </Target>
275                                 </Project>
276                         ";
277
278                         engine = new Engine (Consts.BinPath);
279
280                         project = engine.CreateNewProject ();
281                         project.LoadXml (documentString);
282
283                         Target[] t = new Target [1];
284                         project.Targets.CopyTo (t, 0);
285                         t [0].RemoveTask (null);
286                 }
287
288                 [Test]
289                 public void TestRunTargetTwice ()
290                 {
291                         string documentString = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
292                         <Target Name=""Foo"">
293                                 <Message Text=""Foo ran""/>
294                         </Target>
295                         <Target Name=""Main"">
296                                 <MSBuild Projects=""$(MSBuildProjectFile)"" Targets=""Foo;Foo"" />
297                         </Target>
298
299                 </Project>";
300
301                         var filepath = Path.GetTempFileName ();
302                         try {
303                                 File.WriteAllText (filepath, documentString);
304
305                                 var engine = new Engine (Consts.BinPath);
306                                 var project = engine.CreateNewProject ();
307                                 project.Load (filepath);
308
309                                 var logger = new TestMessageLogger ();
310                                 engine.RegisterLogger (logger);
311
312                                 var result = project.Build ("Main");
313                                 if (!result) {
314                                         logger.DumpMessages ();
315                                         Assert.Fail ("Build failed, see the logs");
316                                 }
317
318                                 Assert.AreEqual(1, logger.NormalMessageCount, "Expected number of messages");
319                                 logger.CheckLoggedMessageHead ("Foo ran", "A1");
320
321                                 Assert.AreEqual(0, logger.NormalMessageCount, "Extra messages found");
322                                 Assert.AreEqual(0, logger.WarningMessageCount, "Extra warning messages found");
323
324                                 Assert.AreEqual(2, logger.TargetStarted, "TargetStarted count");
325                                 Assert.AreEqual(2, logger.TargetFinished, "TargetFinished count");
326
327                                 Assert.IsTrue (result);
328                         } finally {
329                                 File.Delete (filepath);
330                         }
331                 }
332
333                 [Test]
334                 public void TestTargetOutputs1 ()
335                 {
336                         Engine engine;
337                         Project project;
338
339                         string documentString = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
340                         <ItemGroup>
341                                 <fruit Include=""apple""/>
342                                 <fruit Include=""rhubarb""/>
343                                 <fruit Include=""apricot""/>
344                         </ItemGroup>
345
346                         <Target Name=""Main"">
347                                 <CallTarget Targets=""foo"">
348                                         <Output TaskParameter=""TargetOutputs"" ItemName=""AllOut""/>
349                                 </CallTarget>
350
351                                 <CallTarget Targets=""foo"">
352                                         <Output TaskParameter=""TargetOutputs"" ItemName=""AllOut""/>
353                                 </CallTarget>
354                                 <Message Text=""AllOut: @(AllOut)""/>
355                         </Target>
356
357                         <Target Name=""foo"" Outputs=""@(FooItem)"">
358                                 <Message Text=""foo called""/>
359                                 <CreateItem Include=""%(fruit.Identity)"">
360                                         <Output TaskParameter=""Include"" ItemName=""FooItem""/>
361                                 </CreateItem>
362                                 <Message Text=""FooItem: @(FooItem)""/>
363                         </Target>
364                 </Project>";
365
366                         engine = new Engine (Consts.BinPath);
367                         project = engine.CreateNewProject ();
368                         project.LoadXml (documentString);
369
370                         MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger =
371                                 new MonoTests.Microsoft.Build.Tasks.TestMessageLogger ();
372                         engine.RegisterLogger (logger);
373
374                         bool result = project.Build ("Main");
375                         if (!result) {
376                                 logger.DumpMessages ();
377                                 Assert.Fail ("Build failed");
378                         }
379
380                         try {
381                                 Assert.AreEqual (3, logger.NormalMessageCount, "Expected number of messages");
382                                 logger.CheckLoggedMessageHead ("foo called", "A1");
383                                 logger.CheckLoggedMessageHead ("FooItem: apple;rhubarb;apricot", "A2");
384                                 logger.CheckLoggedMessageHead ("AllOut: apple;rhubarb;apricot;apple;rhubarb;apricot", "A3");
385                                 Assert.AreEqual (0, logger.NormalMessageCount, "Extra messages found");
386
387                                 Assert.AreEqual (2, logger.TargetStarted, "TargetStarted count");
388                                 Assert.AreEqual (2, logger.TargetFinished, "TargetFinished count");
389                                 Assert.AreEqual (8, logger.TaskStarted, "TaskStarted count");
390                                 Assert.AreEqual (8, logger.TaskFinished, "TaskFinished count");
391
392                         } catch (AssertionException) {
393                                 logger.DumpMessages ();
394                                 throw;
395                         }
396                 }
397
398                 bool Build (string projectXml, ILogger logger)
399                 {
400                         if (!isMono) {
401                                 var reader = new StringReader (projectXml);
402                                 var xml = XmlReader.Create (reader);
403                                 return BuildOnDotNet (xml, logger);
404                         } else {
405                                 return BuildOnMono (projectXml, logger);
406                         }
407                 }
408
409                 bool BuildOnDotNet (XmlReader reader, ILogger logger)
410                 {
411                         var type = Type.GetType ("Microsoft.Build.Evaluation.ProjectCollection, Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
412
413                         var prop = type.GetProperty ("GlobalProjectCollection");
414                         var coll = prop.GetValue (null, null);
415                                 
416                         var loadProject = coll.GetType ().GetMethod (
417                                         "LoadProject", new Type[] { typeof (XmlReader), typeof (string) });
418                         var proj = loadProject.Invoke (coll, new object[] { reader, "4.0" });
419                                 
420                         var build = proj.GetType ().GetMethod ("Build", new Type[] { typeof (string), typeof (ILogger[]) });
421                         var ret = (bool)build.Invoke (proj, new object[] { "Main", new ILogger[] { logger }});
422                         return ret;
423                 }
424
425                 bool BuildOnMono (string projectXml, ILogger logger)
426                 {
427                         var engine = new Engine (Consts.BinPath);
428                         var project = engine.CreateNewProject ();
429                         project.LoadXml (projectXml);
430                         
431                         engine.RegisterLogger (logger);
432                         
433                         return project.Build ("Main");
434                 }
435
436                 TestMessageLogger CreateLogger (string projectXml)
437                 {
438                         var logger = new TestMessageLogger ();
439                         var result = Build (projectXml, logger);
440
441                         if (!result) {
442                                 logger.DumpMessages ();
443                                 Assert.Fail ("Build failed");
444                         }
445
446                         return logger;
447                 }
448
449                 void ItemGroupInsideTarget (string xml, params string[] messages)
450                 {
451                         ItemGroupInsideTarget (xml, 1, messages);
452                 }
453
454                 void ItemGroupInsideTarget (string xml, int expectedTargetCount, params string[] messages)
455                 {
456                         var logger = CreateLogger (xml);
457                         
458                         try
459                         {
460                                 Assert.AreEqual(messages.Length, logger.NormalMessageCount, "Expected number of messages");
461                                 for (int i = 0; i < messages.Length; i++)
462                                         logger.CheckLoggedMessageHead (messages [i], i.ToString ());
463                                 Assert.AreEqual(0, logger.NormalMessageCount, "Extra messages found");
464                                 Assert.AreEqual(0, logger.WarningMessageCount, "Extra warningmessages found");
465                                 
466                                 Assert.AreEqual(expectedTargetCount, logger.TargetStarted, "TargetStarted count");
467                                 Assert.AreEqual(expectedTargetCount, logger.TargetFinished, "TargetFinished count");
468                                 Assert.AreEqual(messages.Length, logger.TaskStarted, "TaskStarted count");
469                                 Assert.AreEqual(messages.Length, logger.TaskFinished, "TaskFinished count");
470                         }
471                         catch (AssertionException)
472                         {
473                                 logger.DumpMessages();
474                                 throw;
475                         }
476                 }
477
478                 [Test]
479                 public void BuildProjectWithItemGroupInsideTarget ()
480                 {
481                         ItemGroupInsideTarget (
482                                 @"<Project ToolsVersion=""4.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
483                                         <ItemGroup>
484                                         <fruit Include=""apple""/>
485                                                 <fruit Include=""apricot""/>
486                                         </ItemGroup>
487
488                                         <Target Name=""Main"">
489                                                 <ItemGroup>
490                                                         <fruit Include=""raspberry"" />
491                                                 </ItemGroup>
492                                                 <Message Text=""%(fruit.Identity)""/>
493                                         </Target>
494                                 </Project>", "apple", "apricot", "raspberry");
495                 }
496                 
497                 [Test]
498                 public void BuildProjectWithItemGroupInsideTarget2 ()
499                 {
500                         ItemGroupInsideTarget (
501                                 @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" ToolsVersion=""4.0"">
502                                         <ItemGroup>
503                                                 <A Include='1'>
504                                                         <Sub>Foo</Sub>
505                                                 </A>
506                                         </ItemGroup>
507                                         <PropertyGroup>
508                                                 <Foo>Bar</Foo>
509                                         </PropertyGroup>
510
511                                         <Target Name='Main'>
512                                                 <ItemGroup>
513                                                         <A Include='2'>
514                                                                 <Sub>$(Foo);Hello</Sub>
515                                                         </A>
516                                                 </ItemGroup>
517                                 
518                                                 <Message Text='@(A)' />
519                                                 <Message Text='%(A.Sub)' />
520                                         </Target>
521                                 </Project>", "1;2", "Foo", "Bar;Hello");
522                 }
523                 
524                 [Test]
525                 public void BuildProjectWithPropertyGroupInsideTarget ()
526                 {
527                         ItemGroupInsideTarget (
528                                 @"<Project ToolsVersion=""4.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
529                                         <PropertyGroup>
530                                                 <A>Foo</A>
531                                                 <B>Bar</B>
532                                         </PropertyGroup>
533
534                                         <Target Name=""Main"">
535                                                 <Message Text='$(A)' />
536                                                 <PropertyGroup>
537                                                         <A>$(B)</A>
538                                                 </PropertyGroup>
539                                                 <Message Text='$(A)' />
540                                         </Target>
541                                 </Project>", "Foo", "Bar");
542                 }
543
544                 [Test]
545                 public void BuildProjectWithPropertyGroupInsideTarget2 ()
546                 {
547                         ItemGroupInsideTarget (
548                                 @"<Project ToolsVersion=""4.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
549                                         <PropertyGroup>
550                                                 <A>Foo</A>
551                                                 <B>Bar</B>
552                                         </PropertyGroup>
553
554                                         <Target Name=""Main"">
555                                                 <Message Text='$(A)' />
556                                                 <PropertyGroup Condition='true'>
557                                                         <B Condition='false'>False</B>
558                                                 </PropertyGroup>
559                                                 <PropertyGroup Condition='true'>
560                                                         <A>$(B)</A>
561                                                 </PropertyGroup>
562                                                 <Message Text='$(A)' />
563                                                 <Message Text='$(B)' />
564                                                 <PropertyGroup>
565                                                         <A Condition='$(A) == $(B)'>Equal</A>
566                                                 </PropertyGroup>
567                                                 <Message Text='$(A)' />
568                                         </Target>
569                                 </Project>", "Foo", "Bar", "Bar", "Equal");
570                 }
571
572                 [Test]
573                 public void ItemGroupInsideTarget_ModifyMetadata ()
574                 {
575                         ItemGroupInsideTarget (
576                                 @"<Project ToolsVersion=""4.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
577                                         <ItemGroup>
578                                                 <Server Include='Server1'>
579                                                         <AdminContact>Mono</AdminContact>
580                                                 </Server>
581                                                 <Server Include='Server2'>
582                                                         <AdminContact>Mono</AdminContact>
583                                                 </Server>
584                                                 <Server Include='Server3'>
585                                                         <AdminContact>Root</AdminContact>
586                                                 </Server>
587                                         </ItemGroup>
588
589                                         <Target Name='Main'>
590                                                 <ItemGroup>
591                                                         <Server Condition=""'%(Server.AdminContact)' == 'Mono'"">
592                                                                 <AdminContact>Monkey</AdminContact>
593                                                         </Server>
594                                                 </ItemGroup>
595                                         
596                                                 <Message Text='%(Server.Identity) : %(Server.AdminContact)' />
597                                                 </Target>
598                                         </Project>", "Server1 : Monkey", "Server2 : Monkey", "Server3 : Root");
599                 }
600
601                 [Test]
602                 public void ItemGroupInsideTarget_RemoveItem ()
603                 {
604                         ItemGroupInsideTarget (
605                                 @"<Project ToolsVersion=""4.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
606                                         <ItemGroup>
607                                                 <Foo Include='A;B;C;D' />
608                                         </ItemGroup>
609
610                                         <Target Name='Main'>
611                                                 <ItemGroup>
612                                                         <Foo Remove='B' />
613                                                 </ItemGroup>
614
615                                                 <Message Text='@(Foo)' />
616                                         </Target>
617                                 </Project>", "A;C;D");
618                 }
619
620                 [Test]
621                 public void ItemGroupInsideTarget_DontKeepDuplicates ()
622                 {
623                         ItemGroupInsideTarget (
624                                 @"<Project ToolsVersion=""4.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
625                                         <ItemGroup>
626                                                 <Foo Include='A;B' />
627                                                 <Foo Include='C'>
628                                                         <Hello>World</Hello>
629                                                 </Foo>
630                                                 <Foo Include='D'>
631                                                         <Hello>Boston</Hello>
632                                                 </Foo>
633                                         </ItemGroup>
634
635                                         <Target Name='Main'>
636                                                 <ItemGroup>
637                                                         <Foo Include='B;C;D' KeepDuplicates='false'>
638                                                                 <Hello>Boston</Hello>
639                                                         </Foo>
640                                                 </ItemGroup>
641                                 
642                                                 <Message Text='@(Foo)' />
643                                         </Target>
644                                 </Project>", "A;B;C;D;B;C");
645                 }
646
647                 [Test]
648                 public void ItemGroupInsideTarget_RemoveMetadata ()
649                 {
650                         ItemGroupInsideTarget (
651                                 @"<Project ToolsVersion=""4.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
652                                         <ItemGroup>
653                                                 <Foo Include='A' />
654                                                 <Foo Include='B'>
655                                                         <Hello>World</Hello>
656                                                 </Foo>
657                                                 <Foo Include='C'>
658                                                         <Hello>Boston</Hello>
659                                                 </Foo>
660                                                 <Foo Include='D'>
661                                                         <Test>Monkey</Test>
662                                                 </Foo>
663                                         </ItemGroup>
664                                         <PropertyGroup>
665                                                 <Foo>Hello</Foo>
666                                         </PropertyGroup>
667
668                                         <Target Name='Main'>
669                                                 <ItemGroup>
670                                                         <Bar Include='@(Foo)' RemoveMetadata='$(Foo)' />
671                                                         <Bar Include='E'>
672                                                                 <Hello>Monkey</Hello>
673                                                         </Bar>
674                                                 </ItemGroup>
675                                 
676                                                 <Message Text='%(Bar.Identity)' Condition=""'%(Bar.Hello)' != ''""/>
677                                         </Target>
678                                 </Project>", "E");
679                 }
680
681                 [Test]
682                 public void ItemGroupInsideTarget_RemoveMetadata2 ()
683                 {
684                         ItemGroupInsideTarget (
685                                 @"<Project ToolsVersion=""4.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
686                                         <ItemGroup>
687                                                 <Foo Include='A' />
688                                                 <Foo Include='B'>
689                                                         <Hello>World</Hello>
690                                                 </Foo>
691                                                 <Foo Include='C'>
692                                                         <Hello>Boston</Hello>
693                                                 </Foo>
694                                                 <Foo Include='D'>
695                                                         <Test>Monkey</Test>
696                                                 </Foo>
697                                         </ItemGroup>
698                                         <PropertyGroup>
699                                         <Foo>Hello</Foo>
700                                         </PropertyGroup>
701
702                                         <Target Name='Main'>
703                                                 <ItemGroup>
704                                                         <Foo RemoveMetadata='$(Foo)' />
705                                                         <Foo Include='E'>
706                                                                 <Hello>Monkey</Hello>
707                                                         </Foo>
708                                                 </ItemGroup>
709                                 
710                                         <Message Text='%(Foo.Identity)' Condition=""'%(Foo.Hello)' != ''""/>
711                                         </Target>
712                                 </Project>", "E");
713                 }
714
715                 [Test]
716                 public void ItemGroupInsideTarget_KeepMetadata ()
717                 {
718                         ItemGroupInsideTarget (
719                                 @"<Project ToolsVersion=""4.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
720                                         <ItemGroup>
721                                                 <Foo Include='A' />
722                                                 <Foo Include='B'>
723                                                         <Hello>World</Hello>
724                                                 </Foo>
725                                                 <Foo Include='C'>
726                                                         <Hello>Boston</Hello>
727                                                 </Foo>
728                                                 <Foo Include='D'>
729                                                         <Test>Monkey</Test>
730                                                 </Foo>
731                                         </ItemGroup>
732
733                                         <Target Name='Main'>
734                                                 <ItemGroup>
735                                                         <Foo KeepMetadata='Test' />
736                                                         <Foo Include='E'>
737                                                                 <Hello>Monkey</Hello>
738                                                         </Foo>
739                                                 </ItemGroup>
740                                 
741                                                 <Message Text='%(Foo.Identity)' Condition=""'%(Foo.Test)' != ''""/>
742                                         </Target>
743                                 </Project>", "D");
744                 }
745
746                 [Test]
747                 public void ItemGroupInsideTarget_UpdateMetadata ()
748                 {
749                         ItemGroupInsideTarget (
750                                 @"<Project ToolsVersion=""4.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
751                                         <ItemGroup>
752                                                 <ProjectReference Include='xyz'/>
753                                         </ItemGroup>
754
755                                         <Target Name='Main' DependsOnTargets='CreateBar'>
756                                                 <Message Text='Before: $(Bar)'/>
757                                                 <ItemGroup>
758                                                         <ProjectReference>
759                                                                 <AdditionalProperties>A=b</AdditionalProperties>
760                                                         </ProjectReference>
761                                                 </ItemGroup>
762                                                 <Message Text='After: $(Bar)'/>
763                                         </Target>
764
765                                         <Target Name='CreateBar'>
766                                                 <PropertyGroup>
767                                                         <Bar>Bar01</Bar>
768                                                 </PropertyGroup>
769                                         </Target>
770                                 </Project>", 2, "Before: Bar01", "After: Bar01");
771                 }
772
773                 [Test]
774                 public void ItemGroupInsideTarget_Batching ()
775                 {
776                         ItemGroupInsideTarget (
777                                 @"<Project ToolsVersion=""4.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
778                                         <Target Name='Main'>
779                                                 <ItemGroup>
780                                                         <Foo Include='A;B' />
781                                                         <All Include='%(Foo.Identity)' />
782                                                 </ItemGroup>
783                                                 <Message Text='%(All.Identity)' />
784                                         </Target>
785                                 </Project>", "A", "B");
786                 }
787
788                 [Test]
789                 public void ItemGroupInsideTarget_Condition ()
790                 {
791                         ItemGroupInsideTarget (
792                                 @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" ToolsVersion=""4.0"">
793                                         <PropertyGroup>
794                                                 <Summer>true</Summer>
795                                         </PropertyGroup>
796                                         <ItemGroup>
797                                                 <Weather Include='Sun;Rain' />
798                                         </ItemGroup>
799                                 
800                                         <Target Name='Main'>
801                                                 <ItemGroup Condition=""'$(Summer)' != 'true'"">
802                                                         <Weather Include='Snow' />
803                                                 </ItemGroup>
804                                                 <Message Text='%(Weather.Identity)' />
805                                         </Target>
806                                 </Project>", "Sun", "Rain");
807                 }
808
809                 [Test]
810                 public void PropertyGroupInsideTarget_Condition ()
811                 {
812                         ItemGroupInsideTarget (
813                                 @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" ToolsVersion=""4.0"">
814                                         <ItemGroup>
815                                                 <Shells Include=""/bin/sh;/bin/bash;/bin/false"" />
816                                         </ItemGroup>
817
818                                         <Target Name='Main'>
819                                                 <PropertyGroup>
820                                                         <HasBash Condition=""'%(Shells.Filename)' == 'bash'"">true</HasBash>
821                                                 </PropertyGroup>
822
823                                                 <ItemGroup Condition=""'$(HasBash)' == 'true'"">
824                                                         <Weather Include='Rain' />
825                                                 </ItemGroup>
826                                                 <Message Text='%(Weather.Identity)' />
827                                         </Target>
828                                 </Project>", "Rain");
829                 }
830
831                 [Test]
832                 // Bug #14661
833                 public void ItemGroupInsideTarget_Expression_in_Metadata ()
834                 {
835                         ItemGroupInsideTarget (
836                         @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" ToolsVersion=""4.0"">
837                                 <ItemGroup>
838                                         <Foo Include='output1'>
839                                                 <Inputs>input1a;input1b</Inputs>
840                                         </Foo>
841                                         <Foo Include='output2'>
842                                                 <Inputs>input2a;input2b</Inputs>
843                                         </Foo>
844                                 </ItemGroup>
845
846                                 <Target Name='Main' DependsOnTargets='_PrepareItems' Inputs='@(_Foo)' Outputs='%(Result)'>
847                                         <Message Text='COMPILE: @(_Foo) - %(_Foo.Result)' />
848                                 </Target>
849
850                                 <Target Name='_PrepareItems'>
851                                         <ItemGroup>
852                                                 <_Foo Include='%(Foo.Inputs)'>
853                                                         <Result>%(Foo.Identity)</Result>
854                                                 </_Foo>
855                                         </ItemGroup>
856                                 </Target>
857                         </Project>",
858                         3, "COMPILE: input1a;input1b - output1", "COMPILE: input2a;input2b - output2");
859                 }
860
861
862                 [Test]
863                 public void TestTargetOutputsIncludingMetadata ()
864                 {
865                         Engine engine;
866                         Project project;
867
868                         string documentString = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
869                         <ItemGroup>
870                                 <fruit Include=""apple""><md>a</md></fruit>
871                                 <fruit Include=""rhubarb""><md>b</md></fruit>
872                                 <fruit Include=""apricot""><md>c</md></fruit>
873                         </ItemGroup>
874
875                         <Target Name=""Main"">
876                                 <CallTarget Targets=""foo"">
877                                         <Output TaskParameter=""TargetOutputs"" ItemName=""AllOut""/>
878                                 </CallTarget>
879
880                                 <CallTarget Targets=""foo"">
881                                         <Output TaskParameter=""TargetOutputs"" ItemName=""AllOut""/>
882                                 </CallTarget>
883                                 <Message Text=""AllOut: @(AllOut) metadata: %(AllOut.md)""/>
884                         </Target>
885
886                         <Target Name=""foo"" Outputs=""@(FooItem)"">
887                                 <Message Text=""foo called""/>
888                                 <CreateItem Include=""@(fruit)"">
889                                         <Output TaskParameter=""Include"" ItemName=""FooItem""/>
890                                 </CreateItem>
891                                 <Message Text=""FooItem: @(FooItem) metadata: %(FooItem.md)""/>
892                         </Target>
893                 </Project>";
894
895                         engine = new Engine (Consts.BinPath);
896                         project = engine.CreateNewProject ();
897                         project.LoadXml (documentString);
898
899                         MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger =
900                                 new MonoTests.Microsoft.Build.Tasks.TestMessageLogger ();
901                         engine.RegisterLogger (logger);
902
903                         bool result = project.Build ("Main");
904                         if (!result) {
905                                 logger.DumpMessages ();
906                                 Assert.Fail ("Build failed");
907                         }
908
909                         try {
910                                 logger.CheckLoggedMessageHead ("foo called", "A1");
911                                 logger.CheckLoggedMessageHead ("FooItem: apple metadata: a", "A2");
912                                 logger.CheckLoggedMessageHead ("FooItem: rhubarb metadata: b", "A3");
913                                 logger.CheckLoggedMessageHead ("FooItem: apricot metadata: c", "A4");
914
915                                 logger.CheckLoggedMessageHead ("AllOut: apple;apple metadata: a", "A5");
916                                 logger.CheckLoggedMessageHead ("AllOut: rhubarb;rhubarb metadata: b", "A6");
917                                 logger.CheckLoggedMessageHead ("AllOut: apricot;apricot metadata: c", "A7");
918
919                                 Assert.AreEqual (0, logger.NormalMessageCount, "Extra messages found");
920
921                                 Assert.AreEqual (2, logger.TargetStarted, "TargetStarted count");
922                                 Assert.AreEqual (2, logger.TargetFinished, "TargetFinished count");
923                                 Assert.AreEqual (10, logger.TaskStarted, "TaskStarted count");
924                                 Assert.AreEqual (10, logger.TaskFinished, "TaskFinished count");
925
926                         } catch (AssertionException) {
927                                 logger.DumpMessages ();
928                                 throw;
929                         }
930                 }
931
932                 [Test]
933                 public void TestOverridingTargets ()
934                 {
935                         Engine engine;
936                         Project project;
937
938                         string second = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
939                                 <Target Name='BeforeBuild'/>
940                                 <Target Name='AfterBuild'/>
941                                 <Target Name='Build' DependsOnTargets='BeforeBuild'>
942                                         <Message Text='Build executing'/>
943                                         <CallTarget Targets='AfterBuild'/>
944                                 </Target>
945                 </Project>";
946                         using (StreamWriter sw = new StreamWriter (Path.Combine ("Test", Path.Combine ("resources", "second.proj")))) {
947                                 sw.Write (second);
948                         }
949
950                         string documentString = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
951                                 <Target Name='AfterBuild'>
952                                         <Message Text='Overriding AfterBuild'/>
953                                 </Target>
954
955                                 <Import Project='Test/resources/second.proj'/>
956                                 <Target Name='BeforeBuild'>
957                                         <Message Text='Overriding BeforeBuild'/>
958                                 </Target>
959                 </Project>";
960
961                         engine = new Engine (Consts.BinPath);
962                         project = engine.CreateNewProject ();
963                         project.LoadXml (documentString);
964
965                         MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger =
966                                 new MonoTests.Microsoft.Build.Tasks.TestMessageLogger ();
967                         engine.RegisterLogger (logger);
968
969                         bool result = project.Build ("Build");
970                         if (!result) {
971                                 logger.DumpMessages ();
972                                 Assert.Fail ("Build failed");
973                         }
974
975                         logger.CheckLoggedMessageHead ("Overriding BeforeBuild", "A1");
976                         logger.CheckLoggedMessageHead ("Build executing", "A1");
977
978                         Assert.AreEqual (0, logger.NormalMessageCount, "Unexpected extra messages found");
979                 }
980
981                 [Test]
982                 [Category ("NotDotNet")]
983                 public void TestBeforeAndAfterTargets ()
984                 {
985                         Engine engine;
986                         Project project;
987
988                         string projectString = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" ToolsVersion=""4.0"">
989                           <Target Name=""DefaultBeforeTarget1"" BeforeTargets=""Default"">
990                             <Message Text=""Hello from DefaultBeforeTarget1""/>
991                           </Target>
992
993                           <Target Name=""DefaultBeforeTarget2"" BeforeTargets=""Default;Default;NonExistant"">
994                             <Message Text=""Hello from DefaultBeforeTarget2""/>
995                           </Target>
996
997
998                           <Target Name=""DefaultAfterTarget"" AfterTargets=""Default  ; Foo"">
999                             <Message Text=""Hello from DefaultAfterTarget""/>
1000                           </Target>
1001
1002                           <Target Name=""Default"" DependsOnTargets=""DefaultDependsOn"">
1003                             <Message Text=""Hello from Default""/>
1004                           </Target>
1005
1006                           <Target Name=""DefaultDependsOn"">
1007                             <Message Text=""Hello from DefaultDependsOn""/>
1008                           </Target>
1009                         </Project>";
1010
1011                         engine = new Engine ();
1012                         project = engine.CreateNewProject ();
1013                         project.LoadXml (projectString);
1014
1015                         MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger =
1016                                 new MonoTests.Microsoft.Build.Tasks.TestMessageLogger ();
1017                         engine.RegisterLogger (logger);
1018
1019                         if (!project.Build ("Default")) {
1020                                 logger.DumpMessages ();
1021                                 Assert.Fail ("Build failed");
1022                         }
1023
1024                         logger.CheckLoggedMessageHead ("Hello from DefaultDependsOn", "A1");
1025                         logger.CheckLoggedMessageHead ("Hello from DefaultBeforeTarget1", "A1");
1026                         logger.CheckLoggedMessageHead ("Hello from DefaultBeforeTarget2", "A1");
1027                         logger.CheckLoggedMessageHead ("Hello from Default", "A1");
1028                         logger.CheckLoggedMessageHead ("Hello from DefaultAfterTarget", "A1");
1029
1030                         Assert.AreEqual (0, logger.NormalMessageCount, "Unexpected messages found");
1031
1032                         //warnings for referencing unknown targets: NonExistant and Foo
1033                         Assert.AreEqual (2, logger.WarningsCount, "Expected warnings not raised");
1034                 }
1035
1036                 [Test]
1037                 public void TestTargetReturns ()
1038                 {
1039                         engine = new Engine (Consts.BinPath);
1040                         project = engine.CreateNewProject ();
1041                         project.Load (Path.Combine ("Test", Path.Combine ("resources", "TestReturns.csproj")));
1042
1043                         var logger = new TestMessageLogger ();
1044                         engine.RegisterLogger (logger);
1045
1046                         bool result = project.Build ("Main");
1047                         if (!result) {
1048                                 logger.DumpMessages ();
1049                                 Assert.Fail ("Build failed");
1050                         }
1051
1052                         logger.CheckLoggedMessageHead ("Result: Bar", "A1");
1053
1054                         Assert.AreEqual (0, logger.NormalMessageCount, "Unexpected extra messages found");
1055                 }
1056
1057         }
1058 }