1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 
4 using System;
5 using System.IO;
6 using System.Resources;
7 using System.Reflection;
8 using System.Collections;
9 using System.Xml;
10 using System.Text;
11 using System.Globalization;
12 
13 using NUnit.Framework;
14 
15 using Microsoft.Build.Framework;
16 using Microsoft.Build.BuildEngine;
17 using Microsoft.Build.BuildEngine.Shared;
18 using Microsoft.Win32;
19 using System.Configuration;
20 using System.Diagnostics;
21 
22 namespace Microsoft.Build.UnitTests.Project_Tests
23 {
24     [TestFixture]
25     public class AddItem
26     {
27         /// <summary>
28         /// This loads an existing project, and uses the MSBuild object model to
29         /// add a new item (Type="Compile" Include="c.cs") to the project.  Then
30         /// it compares the final project XML to make sure the item was added in
31         /// the correct place.
32         /// </summary>
33         /// <param name="originalProjectContents"></param>
34         /// <param name="newExpectedProjectContents"></param>
35         /// <param name="newItemType"></param>
36         /// <param name="newItemInclude"></param>
37         /// <returns></returns>
38         /// <owner>RGoel</owner>
AddNewItemHelper( string originalProjectContents, string newExpectedProjectContents, string newItemType, string newItemInclude )39         internal static BuildItem AddNewItemHelper
40             (
41             string originalProjectContents,
42             string newExpectedProjectContents,
43             string newItemType,
44             string newItemInclude
45             )
46         {
47             Project project = ObjectModelHelpers.CreateInMemoryProject(originalProjectContents);
48 
49             // The project shouldn't be marked dirty yet.
50             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
51 
52             // Add a new item (Type="Compile", Include="c.cs") to the project using
53             // the object model.
54             BuildItem newItem = project.AddNewItem(newItemType, newItemInclude);
55 
56             // The project should be marked dirty now.
57             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
58 
59             ObjectModelHelpers.CompareProjectContents(project, newExpectedProjectContents);
60 
61             return newItem;
62         }
63 
64         /// <summary>
65         /// This loads an existing project that contains items within <ItemGroup>s.
66         /// It then uses the MSBuild object model to
67         /// add a new item to the project.  Then it compares the final project
68         /// XML to make sure the item was added in the correct place.
69         /// </summary>
70         /// <owner>RGoel</owner>
71         [Test]
AddNewItemToExistingItemGroup()72         public void AddNewItemToExistingItemGroup()
73         {
74             // ************************************
75             //               BEFORE
76             // ************************************
77             string projectOriginalContents = @"
78                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
79 
80                     <ItemGroup>
81                         <Compile Include=`b.cs` />
82                         <Compile Include=`c.cs` />
83                     </ItemGroup>
84 
85                     <ItemGroup>
86                         <Reference Include=`System` />
87                     </ItemGroup>
88 
89                     <Target Name=`Build` />
90 
91                 </Project>
92                 ";
93 
94 
95             // ************************************
96             //               AFTER
97             // ************************************
98             string projectNewExpectedContents = @"
99                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
100 
101                     <ItemGroup>
102                         <Compile Include=`a.cs` />
103                         <Compile Include=`b.cs` />
104                         <Compile Include=`c.cs` />
105                     </ItemGroup>
106 
107                     <ItemGroup>
108                         <Reference Include=`System` />
109                     </ItemGroup>
110 
111                     <Target Name=`Build` />
112 
113                 </Project>
114                 ";
115 
116             AddNewItemHelper(projectOriginalContents, projectNewExpectedContents,
117                 "Compile", "a.cs");
118         }
119 
120         /// <summary>
121         /// This tests that the project is correctly marked as dirty when we
122         /// add a new item to the project.
123         /// </summary>
124         /// <owner>RGoel</owner>
125         [Test]
AddNewItemToNewItemGroup()126         public void AddNewItemToNewItemGroup()
127         {
128             // ************************************
129             //               BEFORE
130             // ************************************
131             string projectOriginalContents = @"
132                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
133 
134                     <PropertyGroup>
135                         <WarningLevel>4</WarningLevel>
136                     </PropertyGroup>
137 
138                     <ItemGroup>
139                         <Compile Include=`a.cs`>
140                             <HintPath>hint</HintPath>
141                         </Compile>
142                         <Compile Include=`b.cs` />
143                     </ItemGroup>
144 
145                     <Target Name=`Build` />
146 
147                 </Project>
148                 ";
149 
150 
151             // ************************************
152             //               AFTER
153             // ************************************
154             string projectNewExpectedContents = @"
155                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
156 
157                     <PropertyGroup>
158                         <WarningLevel>4</WarningLevel>
159                     </PropertyGroup>
160 
161                     <ItemGroup>
162                         <Compile Include=`a.cs`>
163                             <HintPath>hint</HintPath>
164                         </Compile>
165                         <Compile Include=`b.cs` />
166                     </ItemGroup>
167 
168                     <ItemGroup>
169                         <Resource Include=`strings.resx` />
170                     </ItemGroup>
171 
172                     <Target Name=`Build` />
173 
174                 </Project>
175                 ";
176 
177             AddNewItemHelper(projectOriginalContents, projectNewExpectedContents,
178                 "Resource", "strings.resx");
179         }
180 
181         /// <summary>
182         /// This loads an existing project that did not contain any items previously.
183         /// It then uses the MSBuild object model to
184         /// add a new item to the project.  Then it compares the final project
185         /// XML to make sure the item was added in the correct place.
186         /// </summary>
187         /// <owner>RGoel</owner>
188         [Test]
AddNewItemWithNoPreviousItemGroup()189         public void AddNewItemWithNoPreviousItemGroup()
190         {
191             // ************************************
192             //               BEFORE
193             // ************************************
194             string projectOriginalContents = @"
195                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
196 
197                     <PropertyGroup>
198                         <Foo>bar</Foo>
199                     </PropertyGroup>
200 
201                     <Target Name=`Build` />
202 
203                 </Project>
204                 ";
205 
206 
207             // ************************************
208             //               AFTER
209             // ************************************
210             string projectNewExpectedContents = @"
211                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
212 
213                     <PropertyGroup>
214                         <Foo>bar</Foo>
215                     </PropertyGroup>
216 
217                     <ItemGroup>
218                         <Compile Include=`c.cs` />
219                     </ItemGroup>
220 
221                     <Target Name=`Build` />
222 
223                 </Project>
224                 ";
225 
226             AddNewItemHelper(projectOriginalContents, projectNewExpectedContents,
227                 "Compile", "c.cs");
228         }
229 
230         /// <summary>
231         /// This adds a new item into the project, and then immediately queries
232         /// the item for an evaluated attribute, which of course doesn't exist yet.
233         /// We're testing to make sure we don't throw an exception in this case.
234         /// </summary>
235         /// <owner>RGoel</owner>
236         [Test]
AddNewItemAndQueryForNonExistentMetadata()237         public void AddNewItemAndQueryForNonExistentMetadata()
238         {
239             // ************************************
240             //               BEFORE
241             // ************************************
242             string projectOriginalContents = @"
243                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
244                 </Project>
245                 ";
246 
247 
248             // ************************************
249             //               AFTER
250             // ************************************
251             string projectNewExpectedContents = @"
252                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
253 
254                     <ItemGroup>
255                         <Compile Include=`foo.cs` />
256                     </ItemGroup>
257 
258                 </Project>
259                 ";
260 
261             BuildItem newItem = AddNewItemHelper(projectOriginalContents,
262                 projectNewExpectedContents, "Compile", "foo.cs");
263 
264             string hintPath = newItem.GetEvaluatedMetadata("HintPath");
265 
266             Assertion.AssertEquals(String.Empty, hintPath);
267         }
268 
269         /// <summary>
270         /// Add a new item of the same name and include path of an item that already
271         /// exists in the project.  Current behavior is that we add the duplicated item,
272         /// although there's no great reason for this.  If we wanted, we could have
273         /// made it so that adding a dup results in a no-op to the project file.
274         /// </summary>
275         /// <owner>RGoel</owner>
276         [Test]
AddNewDuplicate()277         public void AddNewDuplicate()
278         {
279             // ************************************
280             //               BEFORE
281             // ************************************
282             string projectOriginalContents = @"
283                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
284                     <ItemGroup>
285                         <MyWildCard Include=`foo.weirdo`/>
286                     </ItemGroup>
287                 </Project>
288                 ";
289 
290 
291             // ************************************
292             //               AFTER
293             // ************************************
294             string projectNewExpectedContents = @"
295                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
296                     <ItemGroup>
297                         <MyWildCard Include=`foo.weirdo`/>
298                         <MyWildCard Include=`foo.weirdo`/>
299                     </ItemGroup>
300                 </Project>
301                 ";
302 
303             BuildItem newItem = AddNewItemHelper(projectOriginalContents,
304                 projectNewExpectedContents, "MyWildCard", "foo.weirdo");
305         }
306 
307         /// <summary>
308         /// If user tries to add a new item that has the same item name as an existing
309         /// wildcarded item, but the wildcard won't pick up the new file, then we
310         /// of course have to add the new item.
311         /// </summary>
312         /// <owner>RGoel</owner>
313         [Test]
AddNewItemThatDoesntMatchWildcard()314         public void AddNewItemThatDoesntMatchWildcard()
315         {
316             // ************************************
317             //               BEFORE
318             // ************************************
319             string projectOriginalContents = @"
320                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
321                     <ItemGroup>
322                         <MyWildCard Include=`*.weirdo`/>
323                     </ItemGroup>
324                 </Project>
325                 ";
326 
327 
328             // ************************************
329             //               AFTER
330             // ************************************
331             string projectNewExpectedContents = @"
332                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
333                     <ItemGroup>
334                         <MyWildCard Include=`*.weirdo`/>
335                         <MyWildCard Include=`foo.txt`/>
336                     </ItemGroup>
337                 </Project>
338                 ";
339 
340             BuildItem newItem = AddNewItemHelper(projectOriginalContents,
341                 projectNewExpectedContents, "MyWildCard", "foo.txt");
342         }
343 
344         /// <summary>
345         /// In order to match a new item with a wildcard already in the project,
346         /// they of course have to have the same name.  If the item names differ,
347         /// then we just add the new item.
348         /// </summary>
349         /// <owner>RGoel</owner>
350         [Test]
AddNewItemThatMatchesWildcardWithDifferentItemName()351         public void AddNewItemThatMatchesWildcardWithDifferentItemName()
352         {
353             // ************************************
354             //               BEFORE
355             // ************************************
356             string projectOriginalContents = @"
357                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
358                     <ItemGroup>
359                         <MyWildCard Include=`*.weirdo`/>
360                     </ItemGroup>
361                 </Project>
362                 ";
363 
364 
365             // ************************************
366             //               AFTER
367             // ************************************
368             string projectNewExpectedContents = @"
369                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
370                     <ItemGroup>
371                         <MyWildCard Include=`*.weirdo`/>
372                     </ItemGroup>
373                     <ItemGroup>
374                         <MyNewItemName Include=`foo.weirdo`/>
375                     </ItemGroup>
376                 </Project>
377                 ";
378 
379             BuildItem newItem = AddNewItemHelper(projectOriginalContents,
380                 projectNewExpectedContents, "MyNewItemName", "foo.weirdo");
381         }
382 
383         /// <summary>
384         /// When the wildcarded item already in the project file has a Condition
385         /// on it, we don't try to match with it when a user tries to add a new
386         /// item to the project.
387         /// </summary>
388         /// <owner>RGoel</owner>
389         [Test]
AddNewItemThatMatchesWildcardWithCondition()390         public void AddNewItemThatMatchesWildcardWithCondition()
391         {
392             // ************************************
393             //               BEFORE
394             // ************************************
395             string projectOriginalContents = @"
396                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
397                     <ItemGroup>
398                         <MyWildCard Include=`*.weirdo` Condition=`'1'=='1'`/>
399                     </ItemGroup>
400                 </Project>
401                 ";
402 
403 
404             // ************************************
405             //               AFTER
406             // ************************************
407             string projectNewExpectedContents = @"
408                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
409                     <ItemGroup>
410                         <MyWildCard Include=`*.weirdo` Condition=`'1'=='1'`/>
411                         <MyWildCard Include=`foo.weirdo`/>
412                     </ItemGroup>
413                 </Project>
414                 ";
415 
416             BuildItem newItem = AddNewItemHelper(projectOriginalContents,
417                 projectNewExpectedContents, "MyWildCard", "foo.weirdo");
418         }
419 
420         /// <summary>
421         /// When the wildcarded item already in the project file has a Exclude
422         /// on it, we don't try to match with it when a user tries to add a new
423         /// item to the project.
424         /// </summary>
425         /// <owner>RGoel</owner>
426         [Test]
AddNewItemThatMatchesWildcardWithExclude()427         public void AddNewItemThatMatchesWildcardWithExclude()
428         {
429             // ************************************
430             //               BEFORE
431             // ************************************
432             string projectOriginalContents = @"
433                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
434                     <ItemGroup>
435                         <MyWildCard Include=`*.weirdo` Exclude=`bar.weirdo`/>
436                     </ItemGroup>
437                 </Project>
438                 ";
439 
440 
441             // ************************************
442             //               AFTER
443             // ************************************
444             string projectNewExpectedContents = @"
445                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
446                     <ItemGroup>
447                         <MyWildCard Include=`*.weirdo` Exclude=`bar.weirdo`/>
448                         <MyWildCard Include=`foo.weirdo`/>
449                     </ItemGroup>
450                 </Project>
451                 ";
452 
453             BuildItem newItem = AddNewItemHelper(projectOriginalContents,
454                 projectNewExpectedContents, "MyWildCard", "foo.weirdo");
455         }
456 
457         /// <summary>
458         /// There's a wildcard in the project already, and the user tries to add an item
459         /// that matches that wildcard.  In this case, we don't touch the project at all.
460         /// </summary>
461         /// <owner>RGoel</owner>
462         [Test]
AddNewItemThatMatchesWildcard()463         public void AddNewItemThatMatchesWildcard()
464         {
465             // ************************************
466             //               BEFORE
467             // ************************************
468             string projectOriginalContents = @"
469                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
470                     <ItemGroup>
471                         <MyWildCard Include=`*.weirdo`/>
472                     </ItemGroup>
473                 </Project>
474                 ";
475 
476 
477             // ************************************
478             //               AFTER
479             // ************************************
480             string projectNewExpectedContents = @"
481                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
482                     <ItemGroup>
483                         <MyWildCard Include=`*.weirdo`/>
484                     </ItemGroup>
485                 </Project>
486                 ";
487 
488             BuildItem newItem = AddNewItemHelper(projectOriginalContents,
489                 projectNewExpectedContents, "MyWildCard", "foo.weirdo");
490 
491             Assertion.AssertEquals("Newly added item should have correct ItemName", "MyWildCard", newItem.Name);
492             Assertion.AssertEquals("Newly added item should have correct Include", "*.weirdo", newItem.Include);
493             Assertion.AssertEquals("Newly added item should have correct FinalItemSpec", "foo.weirdo", newItem.FinalItemSpecEscaped);
494         }
495 
496         /// <summary>
497         /// There's a complicated recursive wildcard in the project already, and the user tries to add an item
498         /// that matches that wildcard.  In this case, we don't touch the project at all.
499         /// </summary>
500         /// <owner>RGoel</owner>
501         [Test]
AddNewItemThatMatchesComplicatedWildcard()502         public void AddNewItemThatMatchesComplicatedWildcard()
503         {
504             // ************************************
505             //               BEFORE
506             // ************************************
507             string projectOriginalContents = @"
508                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
509                     <ItemGroup>
510                         <MyWildCard Include=`c:\subdir1\**\subdir2\**\*.we?rdo`/>
511                     </ItemGroup>
512                 </Project>
513                 ";
514 
515 
516             // ************************************
517             //               AFTER
518             // ************************************
519             string projectNewExpectedContents = @"
520                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
521                     <ItemGroup>
522                         <MyWildCard Include=`c:\subdir1\**\subdir2\**\*.we?rdo`/>
523                     </ItemGroup>
524                 </Project>
525                 ";
526 
527             BuildItem newItem = AddNewItemHelper(projectOriginalContents,
528                 projectNewExpectedContents, "MyWildCard", @"c:\subdir1\xmake\engine\subdir2\items\foo.weirdo");
529 
530             Assertion.AssertEquals("Newly added item should have correct ItemName", "MyWildCard", newItem.Name);
531             Assertion.AssertEquals("Newly added item should have correct Include", @"c:\subdir1\**\subdir2\**\*.we?rdo", newItem.Include);
532             Assertion.AssertEquals("Newly added item should have correct FinalItemSpec", @"c:\subdir1\xmake\engine\subdir2\items\foo.weirdo", newItem.FinalItemSpecEscaped);
533         }
534 
535         /// <summary>
536         /// There's a complicated recursive wildcard in the project already, and the user tries to add an item
537         /// that matches that wildcard.  In this case, we don't touch the project at all.
538         /// </summary>
539         /// <owner>RGoel</owner>
540         [Test]
AddNewItemThatDoesntMatchComplicatedWildcard()541         public void AddNewItemThatDoesntMatchComplicatedWildcard()
542         {
543             // ************************************
544             //               BEFORE
545             // ************************************
546             string projectOriginalContents = @"
547                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
548                     <ItemGroup>
549                         <MyWildCard Include=`c:\subdir1\**\subdir2\**\*.we?rdo`/>
550                     </ItemGroup>
551                 </Project>
552                 ";
553 
554 
555             // ************************************
556             //               AFTER
557             // ************************************
558             string projectNewExpectedContents = @"
559                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
560                     <ItemGroup>
561                         <MyWildCard Include=`c:\subdir1\**\subdir2\**\*.we?rdo`/>
562                         <MyWildCard Include=`c:\subdir1\xmake\engine\items\foo.weirdo`/>
563                     </ItemGroup>
564                 </Project>
565                 ";
566 
567             BuildItem newItem = AddNewItemHelper(projectOriginalContents,
568                 projectNewExpectedContents, "MyWildCard", @"c:\subdir1\xmake\engine\items\foo.weirdo");
569 
570             Assertion.AssertEquals("Newly added item should have correct ItemName", "MyWildCard", newItem.Name);
571             Assertion.AssertEquals("Newly added item should have correct Include", @"c:\subdir1\xmake\engine\items\foo.weirdo", newItem.Include);
572             Assertion.AssertEquals("Newly added item should have correct FinalItemSpec", @"c:\subdir1\xmake\engine\items\foo.weirdo", newItem.FinalItemSpecEscaped);
573         }
574 
575         /// <summary>
576         /// There's a wildcard in the project already, and the user tries to add an item
577         /// that matches that wildcard.  In this case, we don't touch the project at all.
578         /// Then take the item that you got back from Project.AddNewItem and try and modify
579         /// its metadata.
580         /// </summary>
581         /// <owner>RGoel</owner>
582         [Test]
AddNewItemThatMatchesWildcardAndThenModifyIt()583         public void AddNewItemThatMatchesWildcardAndThenModifyIt()
584         {
585             // ************************************
586             //               BEFORE
587             // ************************************
588             string projectOriginalContents = @"
589                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
590                     <ItemGroup>
591                         <MyWildCard Include=`a.cs; *.weirdo; c.cs`>
592                             <Culture>fr</Culture>
593                         </MyWildCard>
594                     </ItemGroup>
595                 </Project>
596                 ";
597 
598 
599             // ************************************
600             //               AFTER
601             // ************************************
602             string projectNewExpectedContents = @"
603                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
604                     <ItemGroup>
605                         <MyWildCard Include=`a.cs; *.weirdo; c.cs`>
606                             <Culture>fr</Culture>
607                         </MyWildCard>
608                     </ItemGroup>
609                 </Project>
610                 ";
611 
612             BuildItem newItem = AddNewItemHelper(projectOriginalContents,
613                 projectNewExpectedContents, "MyWildCard", "foo.weirdo");
614 
615             Assertion.AssertEquals("Newly added item should have correct ItemName", "MyWildCard", newItem.Name);
616             Assertion.AssertEquals("Newly added item should have correct Include", "a.cs; *.weirdo; c.cs", newItem.Include);
617             Assertion.AssertEquals("Newly added item should have correct FinalItemSpec", "foo.weirdo", newItem.FinalItemSpecEscaped);
618 
619             newItem.SetMetadata("Culture", "en");
620 
621             // ************************************
622             //               AFTER MODIFICATION
623             // ************************************
624             string projectFinalExpectedContents = @"
625                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
626                     <ItemGroup>
627                         <MyWildCard Include=`a.cs`>
628                             <Culture>fr</Culture>
629                         </MyWildCard>
630                         <MyWildCard Include=`c.cs`>
631                             <Culture>fr</Culture>
632                         </MyWildCard>
633                         <MyWildCard Include=`foo.weirdo`>
634                             <Culture>en</Culture>
635                         </MyWildCard>
636                     </ItemGroup>
637                 </Project>
638                 ";
639 
640             ObjectModelHelpers.CompareProjectContents(newItem.ParentPersistedItem.ParentPersistedItemGroup.ParentProject,
641                 projectFinalExpectedContents);
642         }
643 
644         /// <summary>
645         /// There's a wildcard in the project already, and the user tries to add an item
646         /// that matches that wildcard.  In this case, we don't touch the project at all,
647         /// even though the existing item had metadata on it.
648         /// </summary>
649         /// <owner>RGoel</owner>
650         [Test]
AddNewItemThatMatchesWildcardWithMetadata()651         public void AddNewItemThatMatchesWildcardWithMetadata()
652         {
653             // ************************************
654             //               BEFORE
655             // ************************************
656             string projectOriginalContents = @"
657                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
658 
659                     <PropertyGroup>
660                         <MyCulture>fr</MyCulture>
661                     </PropertyGroup>
662 
663                     <ItemGroup>
664                         <MyWildCard Include=`*.weirdo`>
665                             <Culture>$(MyCulture)</Culture>
666                         </MyWildCard>
667                     </ItemGroup>
668 
669                 </Project>
670                 ";
671 
672 
673             // ************************************
674             //               AFTER
675             // ************************************
676             string projectNewExpectedContents = @"
677                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
678 
679                     <PropertyGroup>
680                         <MyCulture>fr</MyCulture>
681                     </PropertyGroup>
682 
683                     <ItemGroup>
684                         <MyWildCard Include=`*.weirdo`>
685                             <Culture>$(MyCulture)</Culture>
686                         </MyWildCard>
687                     </ItemGroup>
688 
689                 </Project>
690                 ";
691 
692             BuildItem newItem = AddNewItemHelper(projectOriginalContents,
693                 projectNewExpectedContents, "MyWildCard", "foo.weirdo");
694 
695             Assertion.AssertEquals("Newly added item should have correct ItemName", "MyWildCard", newItem.Name);
696             Assertion.AssertEquals("Newly added item should have correct Include", "*.weirdo", newItem.Include);
697             Assertion.AssertEquals("Newly added item should have correct FinalItemSpec", "foo.weirdo", newItem.FinalItemSpecEscaped);
698             Assertion.AssertEquals("Newly added item should have correct metadata Culture", "$(MyCulture)", newItem.GetMetadata("Culture"));
699             Assertion.AssertEquals("Newly added item should have correct evaluated metadata Culture", "fr", newItem.GetEvaluatedMetadata("Culture"));
700         }
701 
702         /// <summary>
703         /// There's a wildcard in the project already, but it's part of a semicolon-separated
704         /// list of items.  Now the user tries to add an item that matches that wildcard.
705         /// In this case, we don't touch the project at all.
706         /// </summary>
707         /// <owner>RGoel</owner>
708         [Test]
AddNewItemThatMatchesWildcardInSemicolonList()709         public void AddNewItemThatMatchesWildcardInSemicolonList()
710         {
711             // ************************************
712             //               BEFORE
713             // ************************************
714             string projectOriginalContents = @"
715                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
716                     <ItemGroup>
717                         <MyWildCard Include=`a.cs; *.weirdo; c.cs`/>
718                     </ItemGroup>
719                 </Project>
720                 ";
721 
722 
723             // ************************************
724             //               AFTER
725             // ************************************
726             string projectNewExpectedContents = @"
727                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
728                     <ItemGroup>
729                         <MyWildCard Include=`a.cs; *.weirdo; c.cs`/>
730                     </ItemGroup>
731                 </Project>
732                 ";
733 
734             BuildItem newItem = AddNewItemHelper(projectOriginalContents,
735                 projectNewExpectedContents, "MyWildCard", "foo.weirdo");
736 
737             Assertion.AssertEquals("Newly added item should have correct ItemName", "MyWildCard", newItem.Name);
738             Assertion.AssertEquals("Newly added item should have correct Include", "a.cs; *.weirdo; c.cs", newItem.Include);
739             Assertion.AssertEquals("Newly added item should have correct FinalItemSpec", "foo.weirdo", newItem.FinalItemSpecEscaped);
740         }
741 
742         /// <summary>
743         /// There's a wildcard in the project already, but it's part of a semicolon-separated
744         /// list of items, and it uses a property reference.  Now the user tries to add a new
745         /// item that matches that wildcard.  In this case, we don't touch the project at all.
746         /// We're so smart.
747         /// </summary>
748         /// <owner>RGoel</owner>
749         [Test]
AddNewItemThatMatchesWildcardWithPropertyReference()750         public void AddNewItemThatMatchesWildcardWithPropertyReference()
751         {
752             // ************************************
753             //               BEFORE
754             // ************************************
755             string projectOriginalContents = @"
756                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
757 
758                     <PropertyGroup>
759                         <MySpecialFileExtension>weirdo</MySpecialFileExtension>
760                     </PropertyGroup>
761 
762                     <ItemGroup>
763                         <MyWildCard Include=`a.cs; *.$(MySpecialFileExtension); c.cs`/>
764                     </ItemGroup>
765 
766                 </Project>
767                 ";
768 
769 
770             // ************************************
771             //               AFTER
772             // ************************************
773             string projectNewExpectedContents = @"
774                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
775 
776                     <PropertyGroup>
777                         <MySpecialFileExtension>weirdo</MySpecialFileExtension>
778                     </PropertyGroup>
779 
780                     <ItemGroup>
781                         <MyWildCard Include=`a.cs; *.$(MySpecialFileExtension); c.cs`/>
782                     </ItemGroup>
783 
784                 </Project>
785                 ";
786 
787             BuildItem newItem = AddNewItemHelper(projectOriginalContents,
788                 projectNewExpectedContents, "MyWildCard", "foo.weirdo");
789 
790             Assertion.AssertEquals("Newly added item should have correct ItemName", "MyWildCard", newItem.Name);
791             Assertion.AssertEquals("Newly added item should have correct Include", "a.cs; *.$(MySpecialFileExtension); c.cs", newItem.Include);
792             Assertion.AssertEquals("Newly added item should have correct FinalItemSpec", "foo.weirdo", newItem.FinalItemSpecEscaped);
793         }
794 
795         /// <summary>
796         /// This tests that the project is correctly marked as dirty when we
797         /// add a new ItemGroup to the project.
798         /// </summary>
799         /// <owner>RGoel</owner>
800         [Test]
AddNewItemGroup()801         public void AddNewItemGroup()
802         {
803             // ************************************
804             //               BEFORE
805             // ************************************
806             string projectOriginalContents = @"
807                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
808 
809                     <PropertyGroup>
810                         <WarningLevel>4</WarningLevel>
811                     </PropertyGroup>
812 
813                     <ItemGroup>
814                         <Compile Include=`a.cs`>
815                             <HintPath>hint</HintPath>
816                         </Compile>
817                         <Compile Include=`b.cs` />
818                     </ItemGroup>
819 
820                     <Target Name=`Build` />
821 
822                 </Project>
823                 ";
824 
825 
826             // ************************************
827             //               AFTER
828             // ************************************
829             string projectNewExpectedContents = @"
830                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
831 
832                     <PropertyGroup>
833                         <WarningLevel>4</WarningLevel>
834                     </PropertyGroup>
835 
836                     <ItemGroup>
837                         <Compile Include=`a.cs`>
838                             <HintPath>hint</HintPath>
839                         </Compile>
840                         <Compile Include=`b.cs` />
841                     </ItemGroup>
842 
843                     <ItemGroup />
844 
845                     <Target Name=`Build` />
846 
847                 </Project>
848                 ";
849 
850             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
851 
852             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
853 
854             project.AddNewItemGroup();
855 
856             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
857 
858             ObjectModelHelpers.CompareProjectContents(project, projectNewExpectedContents);
859         }
860     }
861 
862     [TestFixture]
863     public class RemoveItem
864     {
865         /// <summary>
866         /// This loads an existing project, and uses the MSBuild object model to
867         /// remove an item of a particular item spec (e.g., "b.cs").  It then
868         /// compares the final project XML to make sure the item was added in
869         /// the correct place.
870         /// </summary>
871         /// <param name="originalProjectContents"></param>
872         /// <param name="newExpectedProjectContents"></param>
873         /// <param name="itemSpecToRemove"></param>
874         /// <owner>RGoel</owner>
RemoveItemHelper( string originalProjectContents, string newExpectedProjectContents, string itemSpecToRemove )875         private void RemoveItemHelper
876             (
877             string originalProjectContents,
878             string newExpectedProjectContents,
879             string itemSpecToRemove
880             )
881         {
882             Project project = ObjectModelHelpers.CreateInMemoryProject(originalProjectContents);
883 
884             // The project shouldn't be marked dirty yet.
885             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
886 
887             // Get the set of evaluated items.
888             BuildItemGroup evaluatedItems = project.EvaluatedItemsIgnoringCondition;
889 
890             // The VS IDE does a few re-evaluations with different sets of global properties
891             // (i.e., Configuration=Debug, Configuration=Release, etc.).  This is to simulate
892             // that.  If there's a bug in the Project object, then re-evaluation can
893             // potentially mess up the number of items hanging around.
894             project.MarkProjectAsDirty ();
895             BuildItemGroup evaluatedItems2 = project.EvaluatedItemsIgnoringCondition;
896 
897             // The project should be marked dirty now.
898             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
899 
900             // Search all the evaluated items for the one with the item spec we want
901             // to remove.
902             foreach (BuildItem evaluatedItem in evaluatedItems)
903             {
904                 if (evaluatedItem.FinalItemSpecEscaped == itemSpecToRemove)
905                 {
906                     project.RemoveItem (evaluatedItem);
907                 }
908             }
909 
910             // The project should still be marked dirty now.
911             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
912 
913             ObjectModelHelpers.CompareProjectContents(project, newExpectedProjectContents);
914         }
915 
916         /// <summary>
917         /// This loads an existing project that contained a few items, and tries
918         /// to remove one of them through the MSBuild object model.
919         /// </summary>
920         /// <owner>RGoel</owner>
921         [Test]
RemoveItemBySpec()922         public void RemoveItemBySpec()
923         {
924             // ************************************
925             //               BEFORE
926             // ************************************
927             string projectOriginalContents = @"
928                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
929 
930                     <ItemGroup>
931                         <Compile Include=`a.cs` />
932                         <Compile Include=`b.cs` />
933                         <Compile Include=`c.cs` />
934                     </ItemGroup>
935 
936                     <ItemGroup>
937                         <Reference Include=`System` />
938                     </ItemGroup>
939 
940                     <Target Name=`Build` />
941 
942                 </Project>
943                 ";
944 
945 
946             // ************************************
947             //               AFTER
948             // ************************************
949             string projectNewExpectedContents = @"
950                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
951 
952                     <ItemGroup>
953                         <Compile Include=`a.cs` />
954                         <Compile Include=`c.cs` />
955                     </ItemGroup>
956 
957                     <ItemGroup>
958                         <Reference Include=`System` />
959                     </ItemGroup>
960 
961                     <Target Name=`Build` />
962 
963                 </Project>
964                 ";
965 
966             this.RemoveItemHelper (projectOriginalContents, projectNewExpectedContents, "b.cs");
967         }
968 
969         /// <summary>
970         /// This loads an existing project that contained an item tag that
971         /// declares several items with a single tag.  Then it tries
972         /// to remove one of those items through the MSBuild object model.
973         /// </summary>
974         /// <owner>RGoel</owner>
975         [Test]
RemoveItemBySpecFromMultiItemSpec()976         public void RemoveItemBySpecFromMultiItemSpec()
977         {
978             // ************************************
979             //               BEFORE
980             // ************************************
981             string projectOriginalContents = @"
982                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
983 
984                     <PropertyGroup>
985                         <File>a</File>
986                     </PropertyGroup>
987 
988                     <ItemGroup>
989                         <Compile Include=`$(File).cs; b.cs; c.cs` />
990                         <Compile Include=`d.cs` />
991                     </ItemGroup>
992 
993                     <ItemGroup>
994                         <Reference Include=`System` />
995                     </ItemGroup>
996 
997                     <Target Name=`Build` />
998 
999                 </Project>
1000                 ";
1001 
1002 
1003             // ************************************
1004             //               AFTER
1005             // ************************************
1006             string projectNewExpectedContents = @"
1007                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1008 
1009                     <PropertyGroup>
1010                         <File>a</File>
1011                     </PropertyGroup>
1012 
1013                     <ItemGroup>
1014                         <Compile Include=`a.cs` />
1015                         <Compile Include=`c.cs` />
1016                         <Compile Include=`d.cs` />
1017                     </ItemGroup>
1018 
1019                     <ItemGroup>
1020                         <Reference Include=`System` />
1021                     </ItemGroup>
1022 
1023                     <Target Name=`Build` />
1024 
1025                 </Project>
1026                 ";
1027 
1028             this.RemoveItemHelper (projectOriginalContents, projectNewExpectedContents, "b.cs");
1029         }
1030 
1031         /// <summary>
1032         /// This loads an existing project that contained an item tag that
1033         /// declares several items with a single tag.  Then it tries
1034         /// to remove one of those items through the MSBuild object model.
1035         /// The trick here is to test that we correctly preserve the metadata
1036         /// on the items when we do this.
1037         /// </summary>
1038         /// <owner>RGoel</owner>
1039         [Test]
RemoveItemBySpecFromMultiItemSpecWithMetadata()1040         public void RemoveItemBySpecFromMultiItemSpecWithMetadata()
1041         {
1042             // ************************************
1043             //               BEFORE
1044             // ************************************
1045             string projectOriginalContents = @"
1046                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1047 
1048                     <ItemGroup>
1049                         <Compile Include=`a.cs; b.cs; c.cs`>
1050                             <HintPath>$(mypath)</HintPath>
1051                         </Compile>
1052                     </ItemGroup>
1053 
1054                     <Target Name=`Build` />
1055 
1056                 </Project>
1057                 ";
1058 
1059 
1060             // ************************************
1061             //               AFTER
1062             // ************************************
1063             string projectNewExpectedContents = @"
1064                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1065 
1066                     <ItemGroup>
1067                         <Compile Include=`a.cs`>
1068                             <HintPath>$(mypath)</HintPath>
1069                         </Compile>
1070                         <Compile Include=`c.cs`>
1071                             <HintPath>$(mypath)</HintPath>
1072                         </Compile>
1073                     </ItemGroup>
1074 
1075                     <Target Name=`Build` />
1076 
1077                 </Project>
1078                 ";
1079 
1080             this.RemoveItemHelper (projectOriginalContents, projectNewExpectedContents, "b.cs");
1081         }
1082 
1083         /// <summary>
1084         /// Another simple test of removing a single item.
1085         /// </summary>
1086         /// <owner>RGoel</owner>
1087         [Test]
RemoveItemBySpecWhenMultiItemSpecExists()1088         public void RemoveItemBySpecWhenMultiItemSpecExists()
1089         {
1090             // ************************************
1091             //               BEFORE
1092             // ************************************
1093             string projectOriginalContents = @"
1094                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1095 
1096                     <ItemGroup>
1097                         <Compile Include=`a.cs; b.cs; c.cs`>
1098                             <HintPath>$(mypath)</HintPath>
1099                         </Compile>
1100                         <Compile Include=`d.cs` />
1101                     </ItemGroup>
1102 
1103                     <Target Name=`Build` />
1104 
1105                 </Project>
1106                 ";
1107 
1108 
1109             // ************************************
1110             //               AFTER
1111             // ************************************
1112             string projectNewExpectedContents = @"
1113                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1114 
1115                     <ItemGroup>
1116                         <Compile Include=`a.cs; b.cs; c.cs`>
1117                             <HintPath>$(mypath)</HintPath>
1118                         </Compile>
1119                     </ItemGroup>
1120 
1121                     <Target Name=`Build` />
1122 
1123                 </Project>
1124                 ";
1125 
1126             this.RemoveItemHelper (projectOriginalContents, projectNewExpectedContents, "d.cs");
1127         }
1128 
1129         /// <summary>
1130         /// This tests that the project is correctly marked as dirty when we
1131         /// remove an item.
1132         /// </summary>
1133         /// <owner>RGoel</owner>
1134         [Test]
RemoveSpecificItem()1135         public void RemoveSpecificItem()
1136         {
1137             // ************************************
1138             //               BEFORE
1139             // ************************************
1140             string projectOriginalContents = @"
1141                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1142 
1143                     <PropertyGroup>
1144                         <WarningLevel>4</WarningLevel>
1145                     </PropertyGroup>
1146 
1147                     <ItemGroup>
1148                         <Compile Include=`a.cs`>
1149                             <HintPath>hint</HintPath>
1150                         </Compile>
1151                         <Compile Include=`b.cs` />
1152                         <Resource Include=`strings.resx` />
1153                     </ItemGroup>
1154 
1155                     <Target Name=`Build` />
1156 
1157                 </Project>
1158                 ";
1159 
1160 
1161             // ************************************
1162             //               AFTER
1163             // ************************************
1164             string projectNewExpectedContents = @"
1165                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1166 
1167                     <PropertyGroup>
1168                         <WarningLevel>4</WarningLevel>
1169                     </PropertyGroup>
1170 
1171                     <ItemGroup>
1172                         <Compile Include=`b.cs` />
1173                         <Resource Include=`strings.resx` />
1174                     </ItemGroup>
1175 
1176                     <Target Name=`Build` />
1177 
1178                 </Project>
1179                 ";
1180 
1181             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
1182 
1183             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
1184 
1185             BuildItemGroup evaluatedItems = project.EvaluatedItemsIgnoringCondition;
1186             BuildItem itemToRemove = null;
1187             foreach (BuildItem item in evaluatedItems)
1188             {
1189                 if (item.Include == "a.cs")
1190                 {
1191                     itemToRemove = item;
1192                 }
1193             }
1194             Assertion.Assert(itemToRemove != null);
1195             project.RemoveItem(itemToRemove);
1196 
1197             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
1198 
1199             ObjectModelHelpers.CompareProjectContents(project, projectNewExpectedContents);
1200         }
1201 
1202         /// <summary>
1203         /// This tests that the project is correctly marked as dirty when we
1204         /// remove a whole class of items from the project.
1205         /// </summary>
1206         /// <owner>RGoel</owner>
1207         [Test]
RemoveItemsByName()1208         public void RemoveItemsByName()
1209         {
1210             // ************************************
1211             //               BEFORE
1212             // ************************************
1213             string projectOriginalContents = @"
1214                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1215 
1216                     <PropertyGroup>
1217                         <WarningLevel>4</WarningLevel>
1218                     </PropertyGroup>
1219 
1220                     <ItemGroup>
1221                         <Compile Include=`a.cs`>
1222                             <HintPath>hint</HintPath>
1223                         </Compile>
1224                         <Compile Include=`b.cs` />
1225                         <Resource Include=`strings.resx` />
1226                     </ItemGroup>
1227 
1228                     <Target Name=`Build` />
1229 
1230                 </Project>
1231                 ";
1232 
1233 
1234             // ************************************
1235             //               AFTER
1236             // ************************************
1237             string projectNewExpectedContents = @"
1238                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1239 
1240                     <PropertyGroup>
1241                         <WarningLevel>4</WarningLevel>
1242                     </PropertyGroup>
1243 
1244                     <ItemGroup>
1245                         <Resource Include=`strings.resx` />
1246                     </ItemGroup>
1247 
1248                     <Target Name=`Build` />
1249 
1250                 </Project>
1251                 ";
1252 
1253             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
1254 
1255             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
1256 
1257             project.RemoveItemsByName("Compile");
1258 
1259             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
1260 
1261             ObjectModelHelpers.CompareProjectContents(project, projectNewExpectedContents);
1262         }
1263 
1264         /// <summary>
1265         /// This tests that the project is correctly marked as dirty when we
1266         /// remove an ItemGroup from the project.
1267         /// </summary>
1268         /// <owner>RGoel</owner>
1269         [Test]
RemoveItemGroup()1270         public void RemoveItemGroup()
1271         {
1272             // ************************************
1273             //               BEFORE
1274             // ************************************
1275             string projectOriginalContents = @"
1276                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1277 
1278                     <PropertyGroup>
1279                         <WarningLevel>4</WarningLevel>
1280                     </PropertyGroup>
1281 
1282                     <ItemGroup>
1283                         <Compile Include=`a.cs`>
1284                             <HintPath>hint</HintPath>
1285                         </Compile>
1286                         <Compile Include=`b.cs` />
1287                     </ItemGroup>
1288 
1289                     <Target Name=`Build` />
1290 
1291                 </Project>
1292                 ";
1293 
1294 
1295             // ************************************
1296             //               AFTER
1297             // ************************************
1298             string projectNewExpectedContents = @"
1299                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1300 
1301                     <PropertyGroup>
1302                         <WarningLevel>4</WarningLevel>
1303                     </PropertyGroup>
1304 
1305                     <Target Name=`Build` />
1306 
1307                 </Project>
1308                 ";
1309 
1310             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
1311 
1312             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
1313 
1314             project.RemoveItemGroup(project.ItemGroups.LastLocalItemGroup);
1315 
1316             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
1317 
1318             ObjectModelHelpers.CompareProjectContents(project, projectNewExpectedContents);
1319         }
1320 
1321         /// <summary>
1322         /// Test the RemoveAllItemGroups method.
1323         /// </summary>
1324         /// <owner>RGoel</owner>
1325         [Test]
RemoveAllItemGroups()1326         public void RemoveAllItemGroups()
1327         {
1328             // ************************************
1329             //               BEFORE
1330             // ************************************
1331             string original = @"
1332                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1333 
1334                     <ItemGroup Condition=`'$(x)'=='y'`>
1335                         <ReferencePath Include='c:\foobar'/>
1336                     </ItemGroup>
1337 
1338                     <ItemGroup Condition=`'$(x)'=='z'`>
1339                         <ReferencePath Include='c:\foobar'/>
1340                     </ItemGroup>
1341 
1342                 </Project>
1343                 ";
1344 
1345 
1346             // ************************************
1347             //               AFTER
1348             // ************************************
1349             string expected = @"
1350                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1351                 </Project>
1352                 ";
1353 
1354             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
1355             Assertion.AssertEquals(2, project.ItemGroups.Count);
1356 
1357             project.RemoveAllItemGroups();
1358 
1359             Assertion.AssertEquals(0, project.ItemGroups.Count);
1360             ObjectModelHelpers.CompareProjectContents(project, expected);
1361         }
1362 
1363         /// <summary>
1364         /// Test the Unload method
1365         /// </summary>
1366         [Test]
TestUnload()1367         public void TestUnload()
1368         {
1369             string original = @"
1370                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1371                     <ItemGroup Condition=`'$(x)'=='y'`>
1372                         <ReferencePath Include=`c:\foobar` />
1373                     </ItemGroup>
1374                 </Project>
1375                 ";
1376 
1377             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
1378             bool exceptionThrown = false;
1379 
1380             // Unload to cover the method
1381             project.ParentEngine.UnloadProject(project);
1382             try
1383             {
1384                 Engine engine = project.ParentEngine;
1385             }
1386             catch (InvalidOperationException e)
1387             {
1388                 exceptionThrown = true;
1389                 Assertion.AssertEquals(AssemblyResources.GetString("ProjectInvalidUnloaded"), e.Message);
1390             }
1391 
1392             Assertion.AssertEquals(true, exceptionThrown);
1393         }
1394 
1395         /// <summary>
1396         /// Test the RemoveAllItemGroups method.
1397         /// </summary>
1398         /// <owner>RGoel</owner>
1399         [Test]
RemoveAllItemGroupsWithChoose()1400         public void RemoveAllItemGroupsWithChoose()
1401         {
1402             // ************************************
1403             //               BEFORE
1404             // ************************************
1405             string original = @"
1406                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1407 
1408                     <ItemGroup Condition=`'$(x)'=='y'`>
1409                         <ReferencePath Include='c:\foobar'/>
1410                     </ItemGroup>
1411 
1412                     <Choose>
1413                         <When Condition = `true`>
1414                             <ItemGroup Condition=`'$(x)'=='z'`>
1415                                 <ReferencePath Include='c:\foobar'/>
1416                             </ItemGroup>
1417                         </When>
1418                         <Otherwise>
1419                             <ItemGroup Condition=`'$(x)'=='v'`>
1420                                 <ReferencePath Include='c:\foobar'/>
1421                             </ItemGroup>
1422                         </Otherwise>
1423                     </Choose>
1424 
1425                 </Project>
1426                 ";
1427 
1428 
1429             // ************************************
1430             //               AFTER
1431             // ************************************
1432             string expected = @"
1433                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1434 
1435                     <Choose>
1436                         <When Condition=`true`>
1437                         </When>
1438                         <Otherwise>
1439                         </Otherwise>
1440                     </Choose>
1441 
1442                 </Project>
1443                 ";
1444 
1445             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
1446             Assertion.AssertEquals(3, project.ItemGroups.Count);
1447 
1448             project.RemoveAllItemGroups();
1449 
1450             Assertion.AssertEquals(0, project.ItemGroups.Count);
1451             ObjectModelHelpers.CompareProjectContents(project, expected);
1452         }
1453 
1454         /// <summary>
1455         /// Test the RemoveAllItemGroupsByCondition method.
1456         /// </summary>
1457         /// <owner>RGoel</owner>
1458         [Test]
RemoveAllItemGroupsByCondition()1459         public void RemoveAllItemGroupsByCondition()
1460         {
1461             // ************************************
1462             //               BEFORE
1463             // ************************************
1464             string original = @"
1465                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1466 
1467                     <ItemGroup Condition=`'$(x)'=='y'`>
1468                         <ReferencePath Include=`c:\foobar` />
1469                     </ItemGroup>
1470 
1471                     <ItemGroup Condition=`'$(x)'=='z'`>
1472                         <ReferencePath Include=`c:\foobar` />
1473                     </ItemGroup>
1474 
1475                 </Project>
1476                 ";
1477 
1478 
1479             // ************************************
1480             //               AFTER
1481             // ************************************
1482             string expected = @"
1483                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1484 
1485                     <ItemGroup Condition=`'$(x)'=='z'`>
1486                         <ReferencePath Include=`c:\foobar` />
1487                     </ItemGroup>
1488 
1489                 </Project>
1490                 ";
1491 
1492             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
1493             Assertion.AssertEquals(2, project.ItemGroups.Count);
1494 
1495             project.RemoveItemGroupsWithMatchingCondition("'$(x)'=='y'");
1496 
1497             Assertion.AssertEquals(1, project.ItemGroups.Count);
1498             ObjectModelHelpers.CompareProjectContents(project, expected);
1499         }
1500 
1501 
1502         /// <summary>
1503         /// Test the RemoveAllItemGroupsByCondition method.
1504         /// </summary>
1505         /// <owner>RGoel</owner>
1506         [Test]
RemoveAllItemGroupsByConditionWithChoose()1507         public void RemoveAllItemGroupsByConditionWithChoose()
1508         {
1509             // ************************************
1510             //               BEFORE
1511             // ************************************
1512             string original = @"
1513                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1514 
1515                     <ItemGroup Condition=`'$(x)'=='y'`>
1516                         <ReferencePath Include=`c:\foobar` />
1517                     </ItemGroup>
1518 
1519                     <ItemGroup Condition=`'$(x)'=='z'`>
1520                         <ReferencePath Include=`c:\foobar` />
1521                     </ItemGroup>
1522 
1523                     <Choose>
1524                         <When Condition = `true`>
1525                             <ItemGroup Condition=`'$(x)'=='y'`>
1526                                 <ReferencePath Include=`c:\foobar` />
1527                             </ItemGroup>
1528 
1529                             <ItemGroup Condition=`'$(x)'=='z'`>
1530                                 <ReferencePath Include=`c:\foobar` />
1531                             </ItemGroup>
1532                         </When>
1533                         <Otherwise>
1534                             <ItemGroup Condition=`'$(x)'=='y'`>
1535                                 <ReferencePath Include=`c:\foobar` />
1536                             </ItemGroup>
1537 
1538                             <ItemGroup Condition=`'$(x)'=='z'`>
1539                                 <ReferencePath Include=`c:\foobar` />
1540                             </ItemGroup>
1541                         </Otherwise>
1542                     </Choose>
1543 
1544                 </Project>
1545                 ";
1546 
1547 
1548             // ************************************
1549             //               AFTER
1550             // ************************************
1551             string expected = @"
1552                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1553 
1554                     <ItemGroup Condition=`'$(x)'=='z'`>
1555                         <ReferencePath Include=`c:\foobar` />
1556                     </ItemGroup>
1557 
1558                     <Choose>
1559                         <When Condition=`true`>
1560                             <ItemGroup Condition=`'$(x)'=='z'`>
1561                                 <ReferencePath Include=`c:\foobar` />
1562                             </ItemGroup>
1563                         </When>
1564                         <Otherwise>
1565                             <ItemGroup Condition=`'$(x)'=='z'`>
1566                                 <ReferencePath Include=`c:\foobar` />
1567                             </ItemGroup>
1568                         </Otherwise>
1569                     </Choose>
1570 
1571                 </Project>
1572                 ";
1573 
1574             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
1575             Assertion.AssertEquals(6, project.ItemGroups.Count);
1576 
1577             project.RemoveItemGroupsWithMatchingCondition("'$(x)'=='y'");
1578 
1579             Assertion.AssertEquals(3, project.ItemGroups.Count);
1580             ObjectModelHelpers.CompareProjectContents(project, expected);
1581         }
1582 
1583         /// <summary>
1584         /// Test the RemoveAllItemGroupsByCondition method.
1585         /// </summary>
1586         /// <owner>RGoel</owner>
1587         [Test]
RemoveItemsByNameWithChoose()1588         public void RemoveItemsByNameWithChoose()
1589         {
1590             // ************************************
1591             //               BEFORE
1592             // ************************************
1593             string original = @"
1594                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1595 
1596                     <ItemGroup Condition=`'$(x)'=='y'`>
1597                         <ReferencePath Include=`c:\foobar` />
1598                     </ItemGroup>
1599 
1600                     <ItemGroup Condition=`'$(x)'=='z'`>
1601                         <IncludePath Include=`c:\foobaz` />
1602                     </ItemGroup>
1603 
1604                     <Choose>
1605                         <When Condition = `true`>
1606                             <ItemGroup Condition=`'$(x)'=='y'`>
1607                                 <IncludePath Include=`c:\foobaz` />
1608                             </ItemGroup>
1609 
1610                             <ItemGroup Condition=`'$(x)'=='z'`>
1611                                 <ReferencePath Include=`c:\foobar` />
1612                             </ItemGroup>
1613                         </When>
1614                         <Otherwise>
1615                             <ItemGroup Condition=`'$(x)'=='y'`>
1616                                 <IncludePath Include=`c:\foobaz` />
1617                             </ItemGroup>
1618 
1619                             <ItemGroup Condition=`'$(x)'=='z'`>
1620                                 <ReferencePath Include=`c:\foobar` />
1621                             </ItemGroup>
1622                         </Otherwise>
1623                     </Choose>
1624 
1625                 </Project>
1626                 ";
1627 
1628 
1629             // ************************************
1630             //               AFTER
1631             // ************************************
1632             string expected = @"
1633                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1634 
1635                     <ItemGroup Condition=`'$(x)'=='y'`>
1636                         <ReferencePath Include=`c:\foobar` />
1637                     </ItemGroup>
1638 
1639                     <Choose>
1640                         <When Condition=`true`>
1641                             <ItemGroup Condition=`'$(x)'=='z'`>
1642                                 <ReferencePath Include=`c:\foobar` />
1643                             </ItemGroup>
1644                         </When>
1645                         <Otherwise>
1646                             <ItemGroup Condition=`'$(x)'=='z'`>
1647                                 <ReferencePath Include=`c:\foobar` />
1648                             </ItemGroup>
1649                         </Otherwise>
1650                     </Choose>
1651 
1652                 </Project>
1653                 ";
1654 
1655             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
1656             Assertion.AssertEquals(6, project.ItemGroups.Count);
1657 
1658             project.RemoveItemsByName("IncludePath");
1659 
1660             Assertion.AssertEquals(3, project.ItemGroups.Count);
1661             ObjectModelHelpers.CompareProjectContents(project, expected);
1662         }
1663     }
1664 
1665     [TestFixture]
1666     public class ModifyItem
1667     {
1668         /// <summary>
1669         /// This loads an existing project, and uses the MSBuild object model to
1670         /// modify the "Include" attribute of an item of a particular item spec (e.g.,
1671         /// "b.cs").  It then compares the final project XML to make sure the item was
1672         /// modified correctly.
1673         /// </summary>
1674         /// <param name="originalProjectContents"></param>
1675         /// <param name="newExpectedProjectContents"></param>
1676         /// <param name="oldItemSpec"></param>
1677         /// <param name="newIncludePath"></param>
1678         /// <owner>RGoel</owner>
ModifyItemIncludeHelper( string originalProjectContents, string newExpectedProjectContents, string oldItemSpec, string newIncludePath )1679         internal static void ModifyItemIncludeHelper
1680             (
1681             string originalProjectContents,
1682             string newExpectedProjectContents,
1683             string oldItemSpec,
1684             string newIncludePath
1685             )
1686         {
1687             Project project = ObjectModelHelpers.CreateInMemoryProject(originalProjectContents);
1688 
1689             // The project shouldn't be marked dirty yet.
1690             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
1691 
1692             // Get the set of evaluated items.
1693             BuildItemGroup evaluatedItems = project.EvaluatedItemsIgnoringCondition;
1694 
1695             // The VS IDE does a few re-evaluations with different sets of global properties
1696             // (i.e., Configuration=Debug, Configuration=Release, etc.).  This is to simulate
1697             // that.  If there's a bug in the Project object, then re-evaluation can
1698             // potentially mess up the number of items hanging around.
1699             project.MarkProjectAsDirty ();
1700             BuildItemGroup evaluatedItems2 = project.EvaluatedItemsIgnoringCondition;
1701 
1702             // The project should be marked dirty now.
1703             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
1704 
1705             // Search all the evaluated items for the one with the item spec we want
1706             // to remove.
1707             foreach (BuildItem evaluatedItem in evaluatedItems)
1708             {
1709                 if (evaluatedItem.FinalItemSpecEscaped == oldItemSpec)
1710                 {
1711                     evaluatedItem.Include = newIncludePath;
1712                 }
1713             }
1714 
1715             // The project should still be marked dirty now.
1716             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
1717 
1718             ObjectModelHelpers.CompareProjectContents(project, newExpectedProjectContents);
1719         }
1720 
1721         /// <summary>
1722         /// Tests the ability to change an item's "Include" path through the object
1723         /// model.
1724         /// </summary>
1725         /// <owner>RGoel</owner>
1726         [Test]
ModifyItemIncludeWithEmbeddedProperty()1727         public void ModifyItemIncludeWithEmbeddedProperty()
1728         {
1729             // ************************************
1730             //               BEFORE
1731             // ************************************
1732             string projectOriginalContents = @"
1733                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1734 
1735                     <PropertyGroup>
1736                         <fname>b</fname>
1737                     </PropertyGroup>
1738 
1739                     <ItemGroup>
1740                         <Compile Include=`a.cs`>
1741                             <HintPath>$(mypath1)</HintPath>
1742                         </Compile>
1743                         <Compile Include=`$(fname).cs` Condition=`'1'=='1'`>
1744                             <HintPath>$(mypath2)</HintPath>
1745                         </Compile>
1746                         <Compile Include=`c.cs`>
1747                             <HintPath>$(mypath3)</HintPath>
1748                         </Compile>
1749                     </ItemGroup>
1750 
1751                     <Target Name=`Build` />
1752 
1753                 </Project>
1754                 ";
1755 
1756 
1757             // ************************************
1758             //               AFTER
1759             // ************************************
1760             string projectNewExpectedContents = @"
1761                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1762 
1763                     <PropertyGroup>
1764                         <fname>b</fname>
1765                     </PropertyGroup>
1766 
1767                     <ItemGroup>
1768                         <Compile Include=`a.cs`>
1769                             <HintPath>$(mypath1)</HintPath>
1770                         </Compile>
1771                         <Compile Include=`d.cs` Condition=`'1'=='1'`>
1772                             <HintPath>$(mypath2)</HintPath>
1773                         </Compile>
1774                         <Compile Include=`c.cs`>
1775                             <HintPath>$(mypath3)</HintPath>
1776                         </Compile>
1777                     </ItemGroup>
1778 
1779                     <Target Name=`Build` />
1780 
1781                 </Project>
1782                 ";
1783 
1784             ModifyItemIncludeHelper (projectOriginalContents, projectNewExpectedContents,
1785                 "b.cs", "d.cs");
1786         }
1787 
1788         /// <summary>
1789         /// Tests the ability to change an item's "Include" path that was originally
1790         /// declared using a multi-item item tag.
1791         /// </summary>
1792         /// <owner>RGoel</owner>
1793         [Test]
ModifyItemIncludeWithinMultiItemSpec()1794         public void ModifyItemIncludeWithinMultiItemSpec()
1795         {
1796             // ************************************
1797             //               BEFORE
1798             // ************************************
1799             string projectOriginalContents = @"
1800                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1801 
1802                     <ItemGroup>
1803                         <Compile Include=`a.cs;b.cs;c.cs` Condition=`'0'=='1'`>
1804                             <HintPath>$(mypath1)</HintPath>
1805                         </Compile>
1806                         <Compile Include=`d.cs`>
1807                             <HintPath>$(mypath3)</HintPath>
1808                         </Compile>
1809                     </ItemGroup>
1810 
1811                     <Target Name=`Build` />
1812 
1813                 </Project>
1814                 ";
1815 
1816 
1817             // ************************************
1818             //               AFTER
1819             // ************************************
1820             string projectNewExpectedContents = @"
1821                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1822 
1823                     <ItemGroup>
1824                         <Compile Include=`a.cs` Condition=`'0'=='1'`>
1825                             <HintPath>$(mypath1)</HintPath>
1826                         </Compile>
1827                         <Compile Include=`foo.cs` Condition=`'0'=='1'`>
1828                             <HintPath>$(mypath1)</HintPath>
1829                         </Compile>
1830                         <Compile Include=`c.cs` Condition=`'0'=='1'`>
1831                             <HintPath>$(mypath1)</HintPath>
1832                         </Compile>
1833                         <Compile Include=`d.cs`>
1834                             <HintPath>$(mypath3)</HintPath>
1835                         </Compile>
1836                     </ItemGroup>
1837 
1838                     <Target Name=`Build` />
1839 
1840                 </Project>
1841                 ";
1842 
1843             ModifyItemIncludeHelper (projectOriginalContents, projectNewExpectedContents,
1844                 "b.cs", "foo.cs");
1845         }
1846 
1847         /// <summary>
1848         /// Deletes all *.weirdo files from the temp path, and dumps 3 files there --
1849         /// a.weirdo, b.weirdo, c.weirdo.  This is so that we can exercise our wildcard
1850         /// matching a little bit without having to plumb mock objects all the way through
1851         /// the engine.
1852         /// </summary>
1853         /// <owner>RGoel</owner>
CreateThreeWeirdoFilesHelper()1854         internal static void CreateThreeWeirdoFilesHelper()
1855         {
1856             CleanupWeirdoFilesHelper();
1857 
1858             string tempPath = Path.GetTempPath();
1859 
1860             // Create 3 files in the temp path -- a.weirdo, b.weirdo, and c.weirdo.
1861             File.WriteAllText(Path.Combine(tempPath, "a.weirdo"), String.Empty);
1862             File.WriteAllText(Path.Combine(tempPath, "b.weirdo"), String.Empty);
1863             File.WriteAllText(Path.Combine(tempPath, "c.weirdo"), String.Empty);
1864         }
1865 
1866         /// <summary>
1867         /// Delete all *.weirdo files from the temp directory.
1868         /// </summary>
1869         /// <owner>RGoel</owner>
CleanupWeirdoFilesHelper()1870         internal static void CleanupWeirdoFilesHelper()
1871         {
1872             // Delete all *.weirdo files from the temp path.
1873             string[] filesEndingWithWeirdo = Directory.GetFiles(Path.GetTempPath(), "*.weirdo");
1874             foreach (string fileEndingWithWeirdo in filesEndingWithWeirdo)
1875             {
1876                 File.Delete(fileEndingWithWeirdo);
1877             }
1878         }
1879 
1880         /// <summary>
1881         /// Tests the ability to change an item's "Include" path that was originally
1882         /// part of a wildcard.  The new item spec does not match the original wildcard,
1883         /// so the wildcard has to be exploded.
1884         /// </summary>
1885         /// <owner>RGoel</owner>
1886         [Test]
ModifyItemIncludeWithinNonMatchingWildcard()1887         public void ModifyItemIncludeWithinNonMatchingWildcard()
1888         {
1889             // Populate the project directory with three physical files on disk -- a.weirdo, b.weirdo, c.weirdo.
1890             CreateThreeWeirdoFilesHelper();
1891 
1892             // ************************************
1893             //               BEFORE
1894             // ************************************
1895             string projectOriginalContents = @"
1896                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1897 
1898                     <ItemGroup>
1899                         <MyWildcard Include=`*.weirdo` />
1900                     </ItemGroup>
1901 
1902                 </Project>
1903                 ";
1904 
1905 
1906             // ************************************
1907             //               AFTER
1908             // ************************************
1909             string projectNewExpectedContents = @"
1910                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1911 
1912                     <ItemGroup>
1913                         <MyWildcard Include=`a.weirdo` />
1914                         <MyWildcard Include=`banana.cs` />
1915                         <MyWildcard Include=`c.weirdo` />
1916                     </ItemGroup>
1917 
1918                 </Project>
1919                 ";
1920 
1921             // Change b.weirdo to banana.cs.
1922             ModifyItemIncludeHelper(projectOriginalContents, projectNewExpectedContents,
1923                 "b.weirdo", "banana.cs");
1924 
1925             CleanupWeirdoFilesHelper();
1926         }
1927 
1928         /// <summary>
1929         /// Tests the ability to change an item's "Include" path that was originally
1930         /// part of a wildcard.  The new item spec matches the original wildcard,
1931         /// so the project file doesn't have to be touched at all.
1932         /// </summary>
1933         /// <owner>RGoel</owner>
1934         [Test]
ModifyItemIncludeWithinMatchingWildcard()1935         public void ModifyItemIncludeWithinMatchingWildcard()
1936         {
1937             // Populate the project directory with three physical files on disk -- a.weirdo, b.weirdo, c.weirdo.
1938             CreateThreeWeirdoFilesHelper();
1939 
1940             // ************************************
1941             //               BEFORE
1942             // ************************************
1943             string projectOriginalContents = @"
1944                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1945 
1946                     <ItemGroup>
1947                         <MyWildcard Include=`*.weirdo` />
1948                     </ItemGroup>
1949 
1950                 </Project>
1951                 ";
1952 
1953 
1954             // ************************************
1955             //               AFTER
1956             // ************************************
1957             string projectNewExpectedContents = @"
1958                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1959 
1960                     <ItemGroup>
1961                         <MyWildcard Include=`*.weirdo` />
1962                     </ItemGroup>
1963 
1964                 </Project>
1965                 ";
1966 
1967             // Change b.weirdo to banana.weirdo.
1968             ModifyItemIncludeHelper(projectOriginalContents, projectNewExpectedContents,
1969                 "b.weirdo", "banana.weirdo");
1970 
1971             CleanupWeirdoFilesHelper();
1972         }
1973 
1974         /// <summary>
1975         /// Tests the ability to change an item's "Include" path that was originally
1976         /// part of a wildcard.  In this test, we grab a reference to the *raw* item
1977         /// instead of the evaluated item.  When changing the Include of the raw item,
1978         /// we never try to be smart with respect to wildcards.  We just literally replace
1979         /// the "Include" attribute with the new string given to us by the user.
1980         /// </summary>
1981         /// <owner>RGoel</owner>
1982         [Test]
ModifyRawItemIncludeWithinMatchingWildcard()1983         public void ModifyRawItemIncludeWithinMatchingWildcard()
1984         {
1985             // Populate the project directory with three physical files on disk -- a.weirdo, b.weirdo, c.weirdo.
1986             CreateThreeWeirdoFilesHelper();
1987 
1988             // ************************************
1989             //               BEFORE
1990             // ************************************
1991             string projectOriginalContents = @"
1992                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
1993 
1994                     <ItemGroup>
1995                         <MyWildcard Include=`*.weirdo` />
1996                     </ItemGroup>
1997 
1998                 </Project>
1999                 ";
2000 
2001 
2002             // ************************************
2003             //               AFTER
2004             // ************************************
2005             string projectNewExpectedContents = @"
2006                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2007 
2008                     <ItemGroup>
2009                         <MyWildcard Include=`banana.weirdo` />
2010                     </ItemGroup>
2011 
2012                 </Project>
2013                 ";
2014 
2015             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
2016 
2017             // Get a reference to the one and only raw item in the original project file.
2018             BuildItem rawItemForStarDotWeirdo = null;
2019             foreach (BuildItemGroup rawItemGroup in project.ItemGroups)
2020             {
2021                 foreach (BuildItem rawItem in rawItemGroup)
2022                 {
2023                     rawItemForStarDotWeirdo = rawItem;
2024                 }
2025             }
2026 
2027             Assertion.AssertNotNull("Original raw item not found?", rawItemForStarDotWeirdo);
2028             Assertion.AssertEquals("Original raw item should have Include *.weirdo", "*.weirdo", rawItemForStarDotWeirdo.Include);
2029 
2030             // Change the Include attribute to "banana.weirdo".
2031             rawItemForStarDotWeirdo.Include = "banana.weirdo";
2032 
2033             // The project should still be marked dirty now.
2034             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
2035 
2036             ObjectModelHelpers.CompareProjectContents(project, projectNewExpectedContents);
2037 
2038             CleanupWeirdoFilesHelper();
2039         }
2040 
2041         /// <summary>
2042         /// This helper method checks the project to determine whether a particular item of a particular
2043         /// name exists in the project, such that the build process (the tasks) would see it.
2044         /// </summary>
2045         /// <param name="project"></param>
2046         /// <param name="itemType"></param>
2047         /// <param name="itemSpec"></param>
2048         /// <returns></returns>
2049         /// <owner>RGoel</owner>
ItemExistsInBuildProcessHelper( Project project, string itemType, string itemSpec )2050         private bool ItemExistsInBuildProcessHelper
2051             (
2052             Project project,
2053             string itemType,
2054             string itemSpec
2055             )
2056         {
2057             BuildItemGroup evaluatedItemsOfParticularType = project.GetEvaluatedItemsByName(itemType);
2058 
2059             Assertion.AssertNotNull(evaluatedItemsOfParticularType);
2060 
2061             // Search all the evaluated items for the one with the item spec we want
2062             // to remove.
2063             foreach (BuildItem evaluatedItem in evaluatedItemsOfParticularType)
2064             {
2065                 if (evaluatedItem.FinalItemSpecEscaped == itemSpec)
2066                 {
2067                     return true;
2068                 }
2069             }
2070 
2071             return false;
2072         }
2073 
2074         /// <summary>
2075         /// This loads an existing project, and uses the MSBuild object model to
2076         /// modify the Name of an item of a particular item spec (e.g.,
2077         /// "b.cs").  It then compares the final project XML to make sure the item was
2078         /// modified correctly.
2079         /// </summary>
2080         /// <param name="originalProjectContents"></param>
2081         /// <param name="newExpectedProjectContents"></param>
2082         /// <param name="itemSpecToModify"></param>
2083         /// <param name="newItemType"></param>
2084         /// <owner>RGoel</owner>
ModifyItemNameHelper( string originalProjectContents, string newExpectedProjectContents, string itemSpecToModify, string newItemType )2085         private void ModifyItemNameHelper
2086             (
2087             string originalProjectContents,
2088             string newExpectedProjectContents,
2089             string itemSpecToModify,
2090             string newItemType
2091             )
2092         {
2093             Project project = ObjectModelHelpers.CreateInMemoryProject(originalProjectContents);
2094 
2095             // The project shouldn't be marked dirty yet.
2096             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
2097 
2098             // Get the set of evaluated items.
2099             BuildItemGroup evaluatedItems = project.EvaluatedItemsIgnoringCondition;
2100 
2101             // The VS IDE does a few re-evaluations with different sets of global properties
2102             // (i.e., Configuration=Debug, Configuration=Release, etc.).  This is to simulate
2103             // that.  If there's a bug in the Project object, then re-evaluation can
2104             // potentially mess up the number of items hanging around.
2105             project.MarkProjectAsDirty();
2106             BuildItemGroup evaluatedItems2 = project.EvaluatedItemsIgnoringCondition;
2107 
2108             // The project should be marked dirty now.
2109             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
2110 
2111             // If we were to do a build right now, we certainly hope the tasks would not
2112             // see the new item type.
2113             Assertion.Assert("New item already exists?", !ItemExistsInBuildProcessHelper(project, newItemType, itemSpecToModify));
2114 
2115             // The above assertion caused a re-evaluation, so we're no longer dirty.
2116             Assertion.Assert("Project should not be dirty", !project.IsDirtyNeedToReevaluate);
2117 
2118             // Search all the evaluated items for the one with the item spec we want
2119             // to remove.
2120             foreach (BuildItem evaluatedItem in evaluatedItems)
2121             {
2122                 if (evaluatedItem.FinalItemSpecEscaped == itemSpecToModify)
2123                 {
2124                     evaluatedItem.Name = newItemType;
2125                 }
2126             }
2127 
2128             // The project should be dirty again, because we modified an item.
2129             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
2130 
2131             // If we were to do a build right now, we hope the tasks would see the new item type.
2132             Assertion.Assert("New item type did not get set properly.", ItemExistsInBuildProcessHelper(project, newItemType, itemSpecToModify));
2133 
2134             ObjectModelHelpers.CompareProjectContents(project, newExpectedProjectContents);
2135         }
2136 
2137         /// <summary>
2138         /// Tests the ability to change an item's Name through the object model.
2139         /// </summary>
2140         /// <owner>RGoel</owner>
2141         [Test]
ModifyItemNameWithEmbeddedProperty()2142         public void ModifyItemNameWithEmbeddedProperty()
2143         {
2144             // ************************************
2145             //               BEFORE
2146             // ************************************
2147             string projectOriginalContents = @"
2148                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2149 
2150                     <PropertyGroup>
2151                         <fname>b</fname>
2152                     </PropertyGroup>
2153 
2154                     <ItemGroup>
2155                         <Compile Include=`a.cs`>
2156                             <HintPath>$(mypath1)</HintPath>
2157                         </Compile>
2158                         <Compile Include=`$(fname).cs` Condition=`'1'=='1'`>
2159                             <HintPath>$(mypath2)</HintPath>
2160                         </Compile>
2161                         <Compile Include=`c.cs`>
2162                             <HintPath>$(mypath3)</HintPath>
2163                         </Compile>
2164                     </ItemGroup>
2165 
2166                     <Target Name=`Build` />
2167 
2168                 </Project>
2169                 ";
2170 
2171 
2172             // ************************************
2173             //               AFTER
2174             // ************************************
2175             string projectNewExpectedContents = @"
2176                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2177 
2178                     <PropertyGroup>
2179                         <fname>b</fname>
2180                     </PropertyGroup>
2181 
2182                     <ItemGroup>
2183                         <Compile Include=`a.cs`>
2184                             <HintPath>$(mypath1)</HintPath>
2185                         </Compile>
2186                         <Resource Include=`$(fname).cs` Condition=`'1'=='1'`>
2187                             <HintPath>$(mypath2)</HintPath>
2188                         </Resource>
2189                         <Compile Include=`c.cs`>
2190                             <HintPath>$(mypath3)</HintPath>
2191                         </Compile>
2192                     </ItemGroup>
2193 
2194                     <Target Name=`Build` />
2195 
2196                 </Project>
2197                 ";
2198 
2199             this.ModifyItemNameHelper(projectOriginalContents, projectNewExpectedContents,
2200                 "b.cs", "Resource");
2201         }
2202 
2203         /// <summary>
2204         /// Tests the ability to change an item's "Type" that was originally
2205         /// declared using a multi-item item tag.
2206         /// </summary>
2207         /// <owner>RGoel</owner>
2208         [Test]
ModifyItemNameWithinMultiItemSpec()2209         public void ModifyItemNameWithinMultiItemSpec()
2210         {
2211             // ************************************
2212             //               BEFORE
2213             // ************************************
2214             string projectOriginalContents = @"
2215                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2216 
2217                     <ItemGroup>
2218                         <Compile Include=`a.cs;b.cs;c.cs` Condition=`'1'=='1'`>
2219                             <HintPath>$(mypath1)</HintPath>
2220                         </Compile>
2221                         <Compile Include=`d.cs`>
2222                             <HintPath>$(mypath3)</HintPath>
2223                         </Compile>
2224                     </ItemGroup>
2225 
2226                     <Target Name=`Build` />
2227 
2228                 </Project>
2229                 ";
2230 
2231 
2232             // ************************************
2233             //               AFTER
2234             // ************************************
2235             string projectNewExpectedContents = @"
2236                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2237 
2238                     <ItemGroup>
2239                         <Compile Include=`a.cs` Condition=`'1'=='1'`>
2240                             <HintPath>$(mypath1)</HintPath>
2241                         </Compile>
2242                         <Resource Include=`b.cs` Condition=`'1'=='1'`>
2243                             <HintPath>$(mypath1)</HintPath>
2244                         </Resource>
2245                         <Compile Include=`c.cs` Condition=`'1'=='1'`>
2246                             <HintPath>$(mypath1)</HintPath>
2247                         </Compile>
2248                         <Compile Include=`d.cs`>
2249                             <HintPath>$(mypath3)</HintPath>
2250                         </Compile>
2251                     </ItemGroup>
2252 
2253                     <Target Name=`Build` />
2254 
2255                 </Project>
2256                 ";
2257 
2258             this.ModifyItemNameHelper(projectOriginalContents, projectNewExpectedContents,
2259                 "b.cs", "Resource");
2260         }
2261 
2262         /// <summary>
2263         /// This tests that the project is correctly marked as dirty when we
2264         /// modify an item metadata.
2265         /// </summary>
2266         /// <owner>RGoel</owner>
2267         [Test]
ModifyItemMetadata()2268         public void ModifyItemMetadata()
2269         {
2270             // ************************************
2271             //               BEFORE
2272             // ************************************
2273             string projectOriginalContents = @"
2274                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2275 
2276                     <PropertyGroup>
2277                         <WarningLevel>4</WarningLevel>
2278                     </PropertyGroup>
2279 
2280                     <ItemGroup>
2281                         <Compile Include=`a.cs;b.cs`>
2282                             <HintPath>hint</HintPath>
2283                         </Compile>
2284                         <Resource Include=`strings.resx` />
2285                     </ItemGroup>
2286 
2287                     <Target Name=`Build` />
2288 
2289                 </Project>
2290                 ";
2291 
2292 
2293             // ************************************
2294             //               AFTER
2295             // ************************************
2296             string projectNewExpectedContents = @"
2297                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2298 
2299                     <PropertyGroup>
2300                         <WarningLevel>4</WarningLevel>
2301                     </PropertyGroup>
2302 
2303                     <ItemGroup>
2304                         <Compile Include=`a.cs`>
2305                             <HintPath>flint</HintPath>
2306                         </Compile>
2307                         <Compile Include=`b.cs`>
2308                             <HintPath>hint</HintPath>
2309                         </Compile>
2310                         <Resource Include=`strings.resx` />
2311                     </ItemGroup>
2312 
2313                     <Target Name=`Build` />
2314 
2315                 </Project>
2316                 ";
2317 
2318             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
2319 
2320             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
2321 
2322             BuildItemGroup evaluatedItems = project.EvaluatedItemsIgnoringCondition;
2323             foreach (BuildItem item in evaluatedItems)
2324             {
2325                 if (item.FinalItemSpecEscaped == "a.cs")
2326                 {
2327                     item.SetMetadata("HintPath", "flint");
2328                 }
2329             }
2330 
2331             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
2332 
2333             ObjectModelHelpers.CompareProjectContents(project, projectNewExpectedContents);
2334         }
2335     }
2336 
2337     [TestFixture]
2338     public class AddProperty
2339     {
2340         /// <summary>
2341         /// Tests that the object model correctly adds a new property to the correct
2342         /// existing PropertyGroup.
2343         /// </summary>
2344         /// <owner>RGoel</owner>
2345         [Test]
SetPropertyOnNewPropertyInExistingPropertyGroup()2346         public void SetPropertyOnNewPropertyInExistingPropertyGroup()
2347         {
2348             // ************************************
2349             //               BEFORE
2350             // ************************************
2351             string projectOriginalContents = @"
2352                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2353 
2354                     <PropertyGroup Condition=` '$(A)' == 'B' `>
2355                         <OutputPath>c:\blah</OutputPath>
2356                     </PropertyGroup>
2357 
2358                     <PropertyGroup>
2359                         <WarningLevel>1</WarningLevel>
2360                     </PropertyGroup>
2361 
2362                     <PropertyGroup>
2363                         <Optimize>true</Optimize>
2364                     </PropertyGroup>
2365 
2366                     <Target Name=`Build` />
2367 
2368                 </Project>
2369                 ";
2370 
2371 
2372             // ************************************
2373             //               AFTER
2374             // ************************************
2375             string projectNewExpectedContents = @"
2376                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2377 
2378                     <PropertyGroup Condition=` '$(A)' == 'B' `>
2379                         <OutputPath>c:\blah</OutputPath>
2380                     </PropertyGroup>
2381 
2382                     <PropertyGroup>
2383                         <WarningLevel>1</WarningLevel>
2384                         <MyNewProperty>woohoo</MyNewProperty>
2385                     </PropertyGroup>
2386 
2387                     <PropertyGroup>
2388                         <Optimize>true</Optimize>
2389                     </PropertyGroup>
2390 
2391                     <Target Name=`Build` />
2392 
2393                 </Project>
2394                 ";
2395 
2396             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
2397 
2398             // The project shouldn't be marked dirty yet.
2399             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
2400 
2401             // Set the given new property in the project file using
2402             // the object model.
2403             project.SetProperty("MyNewProperty", "woohoo", "");
2404 
2405             // The project should be marked dirty now.
2406             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
2407 
2408             ObjectModelHelpers.CompareProjectContents(project, projectNewExpectedContents);
2409         }
2410 
2411         /// <summary>
2412         /// This tests that the project is correctly marked as dirty when we
2413         /// add a new property to the project.
2414         /// </summary>
2415         /// <owner>RGoel</owner>
2416         [Test]
AddNewPropertyThroughPropertyGroup()2417         public void AddNewPropertyThroughPropertyGroup()
2418         {
2419             // ************************************
2420             //               BEFORE
2421             // ************************************
2422             string projectOriginalContents = @"
2423                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2424 
2425                     <PropertyGroup>
2426                         <WarningLevel>4</WarningLevel>
2427                     </PropertyGroup>
2428 
2429                     <ItemGroup>
2430                         <Compile Include=`a.cs`>
2431                             <HintPath>hint</HintPath>
2432                         </Compile>
2433                         <Compile Include=`b.cs` />
2434                     </ItemGroup>
2435 
2436                     <Target Name=`Build` />
2437 
2438                 </Project>
2439                 ";
2440 
2441 
2442             // ************************************
2443             //               AFTER
2444             // ************************************
2445             string projectNewExpectedContents = @"
2446                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2447 
2448                     <PropertyGroup>
2449                         <WarningLevel>4</WarningLevel>
2450                         <Optimize>true</Optimize>
2451                     </PropertyGroup>
2452 
2453                     <ItemGroup>
2454                         <Compile Include=`a.cs`>
2455                             <HintPath>hint</HintPath>
2456                         </Compile>
2457                         <Compile Include=`b.cs` />
2458                     </ItemGroup>
2459 
2460                     <Target Name=`Build` />
2461 
2462                 </Project>
2463                 ";
2464 
2465             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
2466 
2467             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
2468 
2469             project.PropertyGroups.LastLocalPropertyGroup.AddNewProperty("Optimize", "true");
2470 
2471             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
2472 
2473             ObjectModelHelpers.CompareProjectContents(project, projectNewExpectedContents);
2474         }
2475 
2476         /// <summary>
2477         /// This tests that the project is correctly marked as dirty when we
2478         /// add a new PropertyGroup to the project.
2479         /// </summary>
2480         /// <owner>RGoel</owner>
2481         [Test]
AddNewPropertyGroup()2482         public void AddNewPropertyGroup()
2483         {
2484             // ************************************
2485             //               BEFORE
2486             // ************************************
2487             string projectOriginalContents = @"
2488                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2489 
2490                     <PropertyGroup>
2491                         <WarningLevel>4</WarningLevel>
2492                     </PropertyGroup>
2493 
2494                     <ItemGroup>
2495                         <Compile Include=`a.cs`>
2496                             <HintPath>hint</HintPath>
2497                         </Compile>
2498                         <Compile Include=`b.cs` />
2499                     </ItemGroup>
2500 
2501                     <Target Name=`Build` />
2502 
2503                 </Project>
2504                 ";
2505 
2506 
2507             // ************************************
2508             //               AFTER
2509             // ************************************
2510             string projectNewExpectedContents = @"
2511                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2512 
2513                     <PropertyGroup>
2514                         <WarningLevel>4</WarningLevel>
2515                     </PropertyGroup>
2516 
2517                     <PropertyGroup />
2518 
2519                     <ItemGroup>
2520                         <Compile Include=`a.cs`>
2521                             <HintPath>hint</HintPath>
2522                         </Compile>
2523                         <Compile Include=`b.cs` />
2524                     </ItemGroup>
2525 
2526                     <Target Name=`Build` />
2527 
2528                 </Project>
2529                 ";
2530 
2531             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
2532 
2533             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
2534 
2535             project.AddNewPropertyGroup(false);
2536 
2537             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
2538 
2539             ObjectModelHelpers.CompareProjectContents(project, projectNewExpectedContents);
2540         }
2541 
2542         [Test]
AddPropertyToImportedProjectFile()2543         public void AddPropertyToImportedProjectFile()
2544         {
2545             // Create temp files on disk for the main project file and the imported project file.
2546             string importedProjFilename = ObjectModelHelpers.CreateTempFileOnDisk(@"
2547 
2548                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2549                     </Project>
2550 
2551                 ");
2552 
2553             string mainProjFilename = ObjectModelHelpers.CreateTempFileOnDisk(@"
2554 
2555                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2556 
2557                         <PropertyGroup>
2558                             <NonConsumingRefPath>$(ReferencePath)$(NewImportedProperty)</NonConsumingRefPath>
2559                         </PropertyGroup>
2560 
2561                         <Import Project=`{0}`/>
2562 
2563                         <PropertyGroup>
2564                             <ConsumingRefPath>$(ReferencePath)$(NewImportedProperty)</ConsumingRefPath>
2565                         </PropertyGroup>
2566 
2567                     </Project>
2568 
2569                 ", importedProjFilename);
2570 
2571             try
2572             {
2573                 // Load the two projects using the MSBuild object model.
2574                 Project mainProj = new Project(new Engine(@"c:\"));
2575                 Project importedProj = new Project(mainProj.ParentEngine);
2576 
2577                 mainProj.Load(mainProjFilename);
2578                 importedProj.Load(importedProjFilename);
2579 
2580                 // This adds a new property
2581                 mainProj.SetImportedProperty("ReferencePath", @"c:\foobar", "'true' == 'true'", importedProj);
2582 
2583                 // Initially, the main project gets the correct value of the property from
2584                 // the imported project.  So ConsumingRefPath == "$(ReferencePath)" == "c:\foobar".
2585                 Assertion.AssertEquals(@"c:\foobar", mainProj.EvaluatedProperties["ConsumingRefPath"].FinalValueEscaped);
2586                 Assertion.AssertEquals(string.Empty, mainProj.EvaluatedProperties["NonConsumingRefPath"].FinalValueEscaped);
2587 
2588                 mainProj.SetImportedProperty("ReferencePath", @"c:\boobah", "'true' == 'true'", importedProj);
2589 
2590                 // Now if we query for the property, we should get back the new value.
2591                 Assertion.AssertEquals(@"c:\boobah", mainProj.EvaluatedProperties["ConsumingRefPath"].FinalValueEscaped);
2592                 Assertion.AssertEquals(@"c:\boobah", importedProj.EvaluatedProperties["ReferencePath"].FinalValueEscaped);
2593                 Assertion.AssertEquals(string.Empty, mainProj.EvaluatedProperties["NonConsumingRefPath"].FinalValueEscaped);
2594 
2595                 // Now we add a new imported property to the main file, into an existing imported
2596                 // property group.
2597                 mainProj.SetImportedProperty("NewImportedProperty", @"newpropertyvalue", "'true' == 'true'", importedProj);
2598 
2599                 // Now if we query for the property, we should get back the new value.
2600                 Assertion.AssertEquals(@"newpropertyvalue", mainProj.EvaluatedProperties["NewImportedProperty"].FinalValueEscaped);
2601                 Assertion.AssertEquals(@"newpropertyvalue", importedProj.EvaluatedProperties["NewImportedProperty"].FinalValueEscaped);
2602                 Assertion.AssertEquals(@"c:\boobahnewpropertyvalue", mainProj.EvaluatedProperties["ConsumingRefPath"].FinalValueEscaped);
2603                 Assertion.AssertEquals(string.Empty, mainProj.EvaluatedProperties["NonConsumingRefPath"].FinalValueEscaped);
2604             }
2605             finally
2606             {
2607                 File.Delete(mainProjFilename);
2608                 File.Delete(importedProjFilename);
2609             }
2610         }
2611     }
2612 
2613     [TestFixture]
2614     public class RemoveProperty
2615     {
2616         /// <summary>
2617         /// This tests that the project is correctly marked as dirty when we
2618         /// remove a property.
2619         /// </summary>
2620         /// <owner>RGoel</owner>
2621         [Test]
RemovePropertyByName()2622         public void RemovePropertyByName()
2623         {
2624             // ************************************
2625             //               BEFORE
2626             // ************************************
2627             string projectOriginalContents = @"
2628                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2629 
2630                     <PropertyGroup>
2631                         <Optimize>false</Optimize>
2632                         <WarningLevel>4</WarningLevel>
2633                     </PropertyGroup>
2634 
2635                     <ItemGroup>
2636                         <Compile Include=`a.cs`>
2637                             <HintPath>hint</HintPath>
2638                         </Compile>
2639                         <Compile Include=`b.cs` />
2640                     </ItemGroup>
2641 
2642                     <Target Name=`Build` />
2643 
2644                 </Project>
2645                 ";
2646 
2647 
2648             // ************************************
2649             //               AFTER
2650             // ************************************
2651             string projectNewExpectedContents = @"
2652                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2653 
2654                     <PropertyGroup>
2655                         <Optimize>false</Optimize>
2656                     </PropertyGroup>
2657 
2658                     <ItemGroup>
2659                         <Compile Include=`a.cs`>
2660                             <HintPath>hint</HintPath>
2661                         </Compile>
2662                         <Compile Include=`b.cs` />
2663                     </ItemGroup>
2664 
2665                     <Target Name=`Build` />
2666 
2667                 </Project>
2668                 ";
2669 
2670             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
2671 
2672             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
2673 
2674             project.PropertyGroups.LastLocalPropertyGroup.RemoveProperty("WarningLevel");
2675 
2676             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
2677 
2678             ObjectModelHelpers.CompareProjectContents(project, projectNewExpectedContents);
2679         }
2680 
2681         /// <summary>
2682         /// This tests that the project is correctly marked as dirty when we
2683         /// remove a BuildPropertyGroup from the project.
2684         /// </summary>
2685         /// <owner>RGoel</owner>
2686         [Test]
RemovePropertyGroup()2687         public void RemovePropertyGroup()
2688         {
2689             // ************************************
2690             //               BEFORE
2691             // ************************************
2692             string projectOriginalContents = @"
2693                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2694 
2695                     <PropertyGroup>
2696                         <WarningLevel>4</WarningLevel>
2697                     </PropertyGroup>
2698 
2699                     <ItemGroup>
2700                         <Compile Include=`a.cs`>
2701                             <HintPath>hint</HintPath>
2702                         </Compile>
2703                         <Compile Include=`b.cs` />
2704                     </ItemGroup>
2705 
2706                     <Target Name=`Build` />
2707 
2708                 </Project>
2709                 ";
2710 
2711 
2712             // ************************************
2713             //               AFTER
2714             // ************************************
2715             string projectNewExpectedContents = @"
2716                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2717 
2718                     <ItemGroup>
2719                         <Compile Include=`a.cs`>
2720                             <HintPath>hint</HintPath>
2721                         </Compile>
2722                         <Compile Include=`b.cs` />
2723                     </ItemGroup>
2724 
2725                     <Target Name=`Build` />
2726 
2727                 </Project>
2728                 ";
2729 
2730             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
2731 
2732             Assertion.AssertEquals(1, project.PropertyGroups.Count);
2733             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
2734 
2735             project.RemovePropertyGroup(project.PropertyGroups.LastLocalPropertyGroup);
2736 
2737             Assertion.AssertEquals(0, project.PropertyGroups.Count);
2738             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
2739 
2740             ObjectModelHelpers.CompareProjectContents(project, projectNewExpectedContents);
2741         }
2742 
2743         /// <summary>
2744         /// Test the RemoveAllPropertyGroups method.
2745         /// </summary>
2746         /// <owner>RGoel</owner>
2747         [Test]
RemoveAllPropertyGroups()2748         public void RemoveAllPropertyGroups()
2749         {
2750             // ************************************
2751             //               BEFORE
2752             // ************************************
2753             string original = @"
2754                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2755 
2756                     <PropertyGroup Condition=`'$(x)'=='y'`>
2757                         <ReferencePath>c:\foobar</ReferencePath>
2758                     </PropertyGroup>
2759 
2760                     <PropertyGroup Condition=`'$(x)'=='z'`>
2761                         <ReferencePath>c:\foobar</ReferencePath>
2762                     </PropertyGroup>
2763 
2764                 </Project>
2765                 ";
2766 
2767 
2768             // ************************************
2769             //               AFTER
2770             // ************************************
2771             string expected = @"
2772                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2773                 </Project>
2774                 ";
2775 
2776             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
2777             Assertion.AssertEquals(2, project.PropertyGroups.Count);
2778 
2779             project.RemoveAllPropertyGroups();
2780 
2781             Assertion.AssertEquals(0, project.PropertyGroups.Count);
2782             ObjectModelHelpers.CompareProjectContents(project, expected);
2783         }
2784 
2785         /// <summary>
2786         /// Test the RemoveAllPropertyGroups method.
2787         /// </summary>
2788         /// <owner>RGoel</owner>
2789         [Test]
RemoveAllPropertyGroupsWithChoose()2790         public void RemoveAllPropertyGroupsWithChoose()
2791         {
2792             // ************************************
2793             //               BEFORE
2794             // ************************************
2795             string original = @"
2796                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2797 
2798                     <PropertyGroup Condition=`'$(x)'=='y'`>
2799                         <ReferencePath>c:\foobar</ReferencePath>
2800                     </PropertyGroup>
2801 
2802                     <Choose>
2803                         <When Condition = `true`>
2804                             <PropertyGroup Condition=`'$(x)'=='z'`>
2805                                 <ReferencePath>c:\foobar</ReferencePath>
2806                             </PropertyGroup>
2807                         </When>
2808                         <Otherwise>
2809                             <PropertyGroup Condition=`'$(x)'=='v'`>
2810                                 <ReferencePath>c:\foobar</ReferencePath>
2811                             </PropertyGroup>
2812                         </Otherwise>
2813                     </Choose>
2814 
2815                 </Project>
2816                 ";
2817 
2818 
2819             // ************************************
2820             //               AFTER
2821             // ************************************
2822             string expected = @"
2823                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2824 
2825                     <Choose>
2826                         <When Condition=`true`>
2827                         </When>
2828                         <Otherwise>
2829                         </Otherwise>
2830                     </Choose>
2831 
2832                 </Project>
2833                 ";
2834 
2835             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
2836             Assertion.AssertEquals(3, project.PropertyGroups.Count);
2837 
2838             project.RemoveAllPropertyGroups();
2839 
2840             Assertion.AssertEquals(0, project.PropertyGroups.Count);
2841             ObjectModelHelpers.CompareProjectContents(project, expected);
2842         }
2843 
2844         /// <summary>
2845         /// Test the RemoveAllPropertyGroupsByCondition method.
2846         /// </summary>
2847         /// <owner>RGoel</owner>
2848         [Test]
RemoveAllPropertyGroupsByCondition()2849         public void RemoveAllPropertyGroupsByCondition()
2850         {
2851             // ************************************
2852             //               BEFORE
2853             // ************************************
2854             string original = @"
2855                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2856 
2857                     <PropertyGroup Condition=`'$(x)'=='y'`>
2858                         <ReferencePath>c:\foobar</ReferencePath>
2859                     </PropertyGroup>
2860 
2861                     <PropertyGroup Condition=`'$(x)'=='z'`>
2862                         <ReferencePath>c:\foobar</ReferencePath>
2863                     </PropertyGroup>
2864 
2865                 </Project>
2866                 ";
2867 
2868 
2869             // ************************************
2870             //               AFTER
2871             // ************************************
2872             string expected = @"
2873                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2874 
2875                     <PropertyGroup Condition=`'$(x)'=='z'`>
2876                         <ReferencePath>c:\foobar</ReferencePath>
2877                     </PropertyGroup>
2878 
2879                 </Project>
2880                 ";
2881 
2882             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
2883             Assertion.AssertEquals(2, project.PropertyGroups.Count);
2884 
2885             project.RemovePropertyGroupsWithMatchingCondition("'$(x)'=='y'");
2886 
2887             Assertion.AssertEquals(1, project.PropertyGroups.Count);
2888             ObjectModelHelpers.CompareProjectContents(project, expected);
2889         }
2890 
2891         /// <summary>
2892         /// Test the RemoveAllPropertyGroupsByCondition method.
2893         /// </summary>
2894         /// <owner>RGoel</owner>
2895         [Test]
RemoveAllPropertyGroupsByConditionWithChoose()2896         public void RemoveAllPropertyGroupsByConditionWithChoose()
2897         {
2898             // ************************************
2899             //               BEFORE
2900             // ************************************
2901             string original = @"
2902                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2903 
2904                     <PropertyGroup Condition=`'$(x)'=='y'`>
2905                         <ReferencePath>c:\foobar</ReferencePath>
2906                     </PropertyGroup>
2907 
2908                     <PropertyGroup Condition=`'$(x)'=='z'`>
2909                         <ReferencePath>c:\foobar</ReferencePath>
2910                     </PropertyGroup>
2911 
2912                     <Choose>
2913                         <When Condition = `true`>
2914                             <PropertyGroup Condition=`'$(x)'=='y'`>
2915                                   <ReferencePath>c:\foobar</ReferencePath>
2916                             </PropertyGroup>
2917 
2918                             <PropertyGroup Condition=`'$(x)'=='z'`>
2919                                   <ReferencePath>c:\foobar</ReferencePath>
2920                             </PropertyGroup>
2921                         </When>
2922                         <Otherwise>
2923                             <PropertyGroup Condition=`'$(x)'=='y'`>
2924                                   <ReferencePath>c:\foobar</ReferencePath>
2925                             </PropertyGroup>
2926 
2927                             <PropertyGroup Condition=`'$(x)'=='z'`>
2928                                   <ReferencePath>c:\foobar</ReferencePath>
2929                             </PropertyGroup>
2930                         </Otherwise>
2931                     </Choose>
2932 
2933                 </Project>
2934                 ";
2935 
2936 
2937             // ************************************
2938             //               AFTER
2939             // ************************************
2940             string expected = @"
2941                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2942 
2943                     <PropertyGroup Condition=`'$(x)'=='z'`>
2944                         <ReferencePath>c:\foobar</ReferencePath>
2945                     </PropertyGroup>
2946 
2947                     <Choose>
2948                         <When Condition=`true`>
2949                             <PropertyGroup Condition=`'$(x)'=='z'`>
2950                                 <ReferencePath>c:\foobar</ReferencePath>
2951                             </PropertyGroup>
2952                         </When>
2953                         <Otherwise>
2954                             <PropertyGroup Condition=`'$(x)'=='z'`>
2955                                 <ReferencePath>c:\foobar</ReferencePath>
2956                             </PropertyGroup>
2957                         </Otherwise>
2958                     </Choose>
2959 
2960                 </Project>
2961                 ";
2962 
2963             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
2964             Assertion.AssertEquals(6, project.PropertyGroups.Count);
2965 
2966             project.RemovePropertyGroupsWithMatchingCondition("'$(x)'=='y'");
2967 
2968             Assertion.AssertEquals(3, project.PropertyGroups.Count);
2969             ObjectModelHelpers.CompareProjectContents(project, expected);
2970         }
2971 
2972         [Test]
RemoveImportedPropertyGroupMatchingCondition()2973         public void RemoveImportedPropertyGroupMatchingCondition()
2974         {
2975             // Create temp files on disk for the main project file and the imported project file.
2976             string importedProjFilename = ObjectModelHelpers.CreateTempFileOnDisk(@"
2977 
2978                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2979                     </Project>
2980 
2981                 ");
2982 
2983             string mainProjFilename = ObjectModelHelpers.CreateTempFileOnDisk(@"
2984 
2985                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2986                         <Import Project=`{0}`/>
2987                     </Project>
2988 
2989                 ", importedProjFilename);
2990 
2991             try
2992             {
2993                 // Load the two projects using the MSBuild object model.
2994                 Project mainProj = new Project(new Engine(@"c:\"));
2995                 Project importedProj = new Project(mainProj.ParentEngine);
2996 
2997                 mainProj.Load(mainProjFilename);
2998                 importedProj.Load(importedProjFilename);
2999 
3000                 string configCondition1 = "'$(Configuration)' == 'Config1'";
3001                 string configCondition2 = "'$(Configuration)' == 'Config2'";
3002                 string configTestCondition = "'$(Configuration)' == 'Test'";
3003 
3004                 // Add same property to imported user file
3005                 mainProj.SetImportedProperty("Prop", "FromUserFile",
3006                     configCondition1, importedProj, PropertyPosition.UseExistingOrCreateAfterLastImport);
3007 
3008                 mainProj.SetImportedProperty("Prop", "FromUserFile",
3009                     configCondition2, importedProj, PropertyPosition.UseExistingOrCreateAfterLastImport);
3010 
3011                 mainProj.SetImportedProperty("Prop", "FromUserFile",
3012                     configTestCondition, importedProj, PropertyPosition.UseExistingOrCreateAfterLastImport);
3013 
3014                 mainProj.RemovePropertyGroupsWithMatchingCondition(configTestCondition, true /* include imported */);
3015                 importedProj.RemovePropertyGroupsWithMatchingCondition(configTestCondition, true /* include imported */);
3016 
3017                 string[] configs = mainProj.GetConditionedPropertyValues("Configuration");
3018 
3019                 Assertion.AssertEquals(2, configs.Length);
3020                 Assertion.Assert(configs[0] == "Config1" || configs[1] == "Config1");
3021                 Assertion.Assert(configs[0] == "Config2" || configs[1] == "Config2");
3022             }
3023             finally
3024             {
3025                 File.Delete(mainProjFilename);
3026                 File.Delete(importedProjFilename);
3027             }
3028         }
3029 
3030         [Test]
RemovePersistedImportedPropertyGroupMatchingCondition()3031         public void RemovePersistedImportedPropertyGroupMatchingCondition()
3032         {
3033             // Create temp files on disk for the main project file and the imported project file.
3034             string importedProjFilename = ObjectModelHelpers.CreateTempFileOnDisk(@"
3035 
3036                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3037 
3038                         <PropertyGroup Condition=`'$(Configuration)' == 'Config1'`>
3039                             <Prop>FromUserFile1</Prop>
3040                         </PropertyGroup>
3041                         <PropertyGroup Condition=`'$(Configuration)' == 'Config2'`>
3042                             <Prop>FromUserFile2</Prop>
3043                         </PropertyGroup>
3044                         <PropertyGroup Condition=`'$(Configuration)' == 'Test'`>
3045                             <Prop>FromUserFileTest</Prop>
3046                         </PropertyGroup>
3047 
3048                     </Project>
3049 
3050                 ");
3051 
3052             string mainProjFilename = ObjectModelHelpers.CreateTempFileOnDisk(@"
3053 
3054                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3055                         <Import Project=`{0}`/>
3056                     </Project>
3057 
3058                 ", importedProjFilename);
3059 
3060             try
3061             {
3062                 // Load the two projects using the MSBuild object model.
3063                 Project mainProj = new Project(new Engine(@"c:\"));
3064                 Project importedProj = new Project(mainProj.ParentEngine);
3065 
3066                 mainProj.Load(mainProjFilename);
3067                 importedProj.Load(importedProjFilename);
3068 
3069                 mainProj.RemovePropertyGroupsWithMatchingCondition("'$(Configuration)' == 'Test'", true /* include imported */);
3070                 importedProj.RemovePropertyGroupsWithMatchingCondition("'$(Configuration)' == 'Test'", true /* include imported */);
3071 
3072                 string[] configs = mainProj.GetConditionedPropertyValues("Configuration");
3073 
3074                 Assertion.AssertEquals(2, configs.Length);
3075                 Assertion.Assert(configs[0] == "Config1" || configs[1] == "Config1");
3076                 Assertion.Assert(configs[0] == "Config2" || configs[1] == "Config2");
3077             }
3078             finally
3079             {
3080                 File.Delete(mainProjFilename);
3081                 File.Delete(importedProjFilename);
3082             }
3083         }
3084     }
3085 
3086     [TestFixture]
3087     public class ModifyProperty
3088     {
3089         /// <summary>
3090         /// This tests that the project is correctly marked as dirty when we
3091         /// set a property value for a property that already existed on the project.
3092         /// </summary>
3093         /// <owner>RGoel</owner>
3094         [Test]
SetPropertyOnExistingProperty()3095         public void SetPropertyOnExistingProperty()
3096         {
3097             // ************************************
3098             //               BEFORE
3099             // ************************************
3100             string projectOriginalContents = @"
3101                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3102 
3103                     <PropertyGroup>
3104                         <WarningLevel>4</WarningLevel>
3105                     </PropertyGroup>
3106 
3107                     <ItemGroup>
3108                         <Compile Include=`a.cs`>
3109                             <HintPath>hint</HintPath>
3110                         </Compile>
3111                         <Compile Include=`b.cs` />
3112                     </ItemGroup>
3113 
3114                     <Target Name=`Build` />
3115 
3116                 </Project>
3117                 ";
3118 
3119 
3120             // ************************************
3121             //               AFTER
3122             // ************************************
3123             string projectNewExpectedContents = @"
3124                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3125 
3126                     <PropertyGroup>
3127                         <WarningLevel>3</WarningLevel>
3128                     </PropertyGroup>
3129 
3130                     <ItemGroup>
3131                         <Compile Include=`a.cs`>
3132                             <HintPath>hint</HintPath>
3133                         </Compile>
3134                         <Compile Include=`b.cs` />
3135                     </ItemGroup>
3136 
3137                     <Target Name=`Build` />
3138 
3139                 </Project>
3140                 ";
3141 
3142             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
3143 
3144             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
3145 
3146             project.SetProperty("WarningLevel", "3", null);
3147 
3148             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
3149 
3150             ObjectModelHelpers.CompareProjectContents(project, projectNewExpectedContents);
3151         }
3152 
3153         /// <summary>
3154         /// This tests that the project is correctly marked as dirty when we
3155         /// directly change the value of a property.
3156         /// </summary>
3157         /// <owner>RGoel</owner>
3158         [Test]
ModifyPropertyValue()3159         public void ModifyPropertyValue()
3160         {
3161             // ************************************
3162             //               BEFORE
3163             // ************************************
3164             string projectOriginalContents = @"
3165                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3166 
3167                     <PropertyGroup>
3168                         <Optimize>false</Optimize>
3169                         <WarningLevel>4</WarningLevel>
3170                     </PropertyGroup>
3171 
3172                     <ItemGroup>
3173                         <Compile Include=`a.cs`>
3174                             <HintPath>hint</HintPath>
3175                         </Compile>
3176                         <Compile Include=`b.cs` />
3177                     </ItemGroup>
3178 
3179                     <Target Name=`Build` />
3180 
3181                 </Project>
3182                 ";
3183 
3184 
3185             // ************************************
3186             //               AFTER
3187             // ************************************
3188             string projectNewExpectedContents = @"
3189                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3190 
3191                     <PropertyGroup>
3192                         <Optimize>false</Optimize>
3193                         <WarningLevel>3</WarningLevel>
3194                     </PropertyGroup>
3195 
3196                     <ItemGroup>
3197                         <Compile Include=`a.cs`>
3198                             <HintPath>hint</HintPath>
3199                         </Compile>
3200                         <Compile Include=`b.cs` />
3201                     </ItemGroup>
3202 
3203                     <Target Name=`Build` />
3204 
3205                 </Project>
3206                 ";
3207 
3208             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
3209 
3210             // Force an evaluation of the project.  If there were a bug in the project code, this might
3211             // cause the back pointers from properties to the parent BuildPropertyGroup to get messed up.
3212             BuildPropertyGroup evaluatedProperties = project.EvaluatedProperties;
3213 
3214             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
3215 
3216             BuildPropertyGroup propertyGroup = project.PropertyGroups.LastLocalPropertyGroup;
3217             foreach (BuildProperty property in propertyGroup)
3218             {
3219                 if (property.Name == "WarningLevel")
3220                 {
3221                     property.Value = "3";
3222                 }
3223             }
3224 
3225             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
3226 
3227             ObjectModelHelpers.CompareProjectContents(project, projectNewExpectedContents);
3228         }
3229 
3230         /// <summary>
3231         /// Tests to see if the main project's evaluated properties will pick up changes made
3232         /// to the imported project file.  This is necessary for IDE scenarios, particularly for
3233         /// things like the "ReferencePath" property which is stored in the .USER file and is
3234         /// used during the build process for the main project.
3235         /// </summary>
3236         /// <owner>RGoel</owner>
3237         [Test]
ModifyPropertyInImportedProjectFile()3238         public void ModifyPropertyInImportedProjectFile()
3239         {
3240             // Create temp files on disk for the main project file and the imported project file.
3241             string importedProjFilename = ObjectModelHelpers.CreateTempFileOnDisk(@"
3242 
3243                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3244 
3245                         <PropertyGroup>
3246                             <ReferencePath>c:\foobar</ReferencePath>
3247                         </PropertyGroup>
3248 
3249                     </Project>
3250 
3251                 ");
3252 
3253             string mainProjFilename = ObjectModelHelpers.CreateTempFileOnDisk(@"
3254 
3255                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3256 
3257                         <Import Project=`{0}`/>
3258 
3259                         <PropertyGroup>
3260                             <ConsumingRefPath>$(ReferencePath)</ConsumingRefPath>
3261                         </PropertyGroup>
3262 
3263                     </Project>
3264 
3265                 ", importedProjFilename);
3266 
3267             try
3268             {
3269                 // Load the two projects using the MSBuild object model.
3270                 Project mainProj = new Project(new Engine(@"c:\"));
3271                 Project importedProj = new Project(mainProj.ParentEngine);
3272 
3273                 mainProj.Load(mainProjFilename);
3274                 importedProj.Load(importedProjFilename);
3275 
3276                 // Initially, the main project gets the correct value of the property from
3277                 // the imported project.  So ConsumingRefPath == "$(ReferencePath)" == "c:\foobar".
3278                 Assertion.AssertEquals(@"c:\foobar", mainProj.EvaluatedProperties["ConsumingRefPath"].FinalValueEscaped);
3279 
3280                 Assertion.Assert("Main project should not be dirty for Save before setting imported property", !mainProj.IsDirty);
3281                 Assertion.Assert("Main project should not be dirty for Evaluation before setting imported property", !mainProj.IsDirtyNeedToReevaluate);
3282                 Assertion.Assert("Imported project should not be dirty for Save before setting imported property", !importedProj.IsDirty);
3283                 Assertion.Assert("Imported project should not be dirty for Evaluation before setting imported property", !importedProj.IsDirtyNeedToReevaluate);
3284 
3285                 mainProj.SetImportedProperty("ReferencePath", @"c:\boobah", null, importedProj);
3286 
3287                 Assertion.Assert("Main project should not be dirty for Save after setting first imported property", !mainProj.IsDirty);
3288                 Assertion.Assert("Main project should be dirty for Evaluation after setting first imported property", mainProj.IsDirtyNeedToReevaluate);
3289                 Assertion.Assert("Imported project should be dirty for Save after setting first imported property", importedProj.IsDirty);
3290                 Assertion.Assert("Imported project should be dirty for Evaluation after setting first imported property", importedProj.IsDirtyNeedToReevaluate);
3291 
3292                 // Now if we query for the property, we should get back the new value.
3293                 Assertion.AssertEquals(@"c:\boobah", mainProj.EvaluatedProperties["ConsumingRefPath"].FinalValueEscaped);
3294                 Assertion.AssertEquals(@"c:\boobah", importedProj.EvaluatedProperties["ReferencePath"].FinalValueEscaped);
3295 
3296                 // Now we add a new imported property to the main file, into an existing imported
3297                 // property group.
3298                 mainProj.SetImportedProperty("NewImportedProperty", @"newpropertyvalue", null, importedProj);
3299 
3300                 // Now if we query for the property, we should get back the new value.
3301                 Assertion.AssertEquals(@"newpropertyvalue", mainProj.EvaluatedProperties["NewImportedProperty"].FinalValueEscaped);
3302                 Assertion.AssertEquals(@"newpropertyvalue", importedProj.EvaluatedProperties["NewImportedProperty"].FinalValueEscaped);
3303 
3304                 // Now we add a new imported property to the main file, into a new imported
3305                 // property group (by setting a unique condition).
3306                 mainProj.SetImportedProperty("NewImportedPropertyWithCondition", @"anotherpropertyvalue", "'$(foo)' == '$(bar)'", importedProj);
3307 
3308                 // Now if we query for the property, we should get back the new value.
3309                 Assertion.AssertEquals(@"anotherpropertyvalue", mainProj.EvaluatedProperties["NewImportedPropertyWithCondition"].FinalValueEscaped);
3310                 Assertion.AssertEquals(@"anotherpropertyvalue", importedProj.EvaluatedProperties["NewImportedPropertyWithCondition"].FinalValueEscaped);
3311 
3312                 Assertion.Assert("Main project should not be dirty for Save after setting last imported property", !mainProj.IsDirty);
3313                 Assertion.Assert("Imported project should be dirty for Save after setting last imported property", importedProj.IsDirty);
3314             }
3315             finally
3316             {
3317                 File.Delete(mainProjFilename);
3318                 File.Delete(importedProjFilename);
3319             }
3320         }
3321 
3322         [Test]
ModifyImportedPropertyGroupCondition()3323         public void ModifyImportedPropertyGroupCondition()
3324         {
3325             // Create temp files on disk for the main project file and the imported project file.
3326             string importedProjFilename = ObjectModelHelpers.CreateTempFileOnDisk(@"
3327 
3328                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3329                     </Project>
3330 
3331                 ");
3332 
3333             string mainProjFilename = ObjectModelHelpers.CreateTempFileOnDisk(@"
3334 
3335                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3336                         <Import Project=`{0}`/>
3337                     </Project>
3338 
3339                 ", importedProjFilename);
3340 
3341             try
3342             {
3343                 // Load the two projects using the MSBuild object model.
3344                 Project mainProj = new Project(new Engine(@"c:\"));
3345                 Project importedProj = new Project(mainProj.ParentEngine);
3346 
3347                 mainProj.Load(mainProjFilename);
3348                 importedProj.Load(importedProjFilename);
3349 
3350                 string configCondition1 = "'$(Configuration)' == 'Config1'";
3351                 string configCondition2 = "'$(Configuration)' == 'Config2'";
3352 
3353                 // Add same property to imported user file
3354                 mainProj.SetImportedProperty("Prop", "FromUserFile",
3355                     configCondition1, importedProj, PropertyPosition.UseExistingOrCreateAfterLastImport);
3356 
3357                 mainProj.SetImportedProperty("Prop", "FromUserFile",
3358                     configCondition2, importedProj, PropertyPosition.UseExistingOrCreateAfterLastImport);
3359 
3360                 foreach (BuildPropertyGroup bpg in mainProj.PropertyGroups)
3361                 {
3362                     if (bpg.Condition == configCondition2)
3363                     {
3364                         bpg.SetImportedPropertyGroupCondition("'$(Configuration)' == 'NewConfig'");
3365                     }
3366                 }
3367 
3368                 string[] configs = mainProj.GetConditionedPropertyValues("Configuration");
3369 
3370                 Assertion.AssertEquals(2, configs.Length);
3371                 Assertion.Assert(configs[0] == "Config1" || configs[1] == "Config1");
3372                 Assertion.Assert(configs[0] == "NewConfig" || configs[1] == "NewConfig");
3373             }
3374             finally
3375             {
3376                 File.Delete(mainProjFilename);
3377                 File.Delete(importedProjFilename);
3378             }
3379         }
3380 
3381         [Test]
ModifyPersistedImportedPropertyGroupCondition()3382         public void ModifyPersistedImportedPropertyGroupCondition()
3383         {
3384             // Create temp files on disk for the main project file and the imported project file.
3385             string importedProjFilename = ObjectModelHelpers.CreateTempFileOnDisk(@"
3386 
3387                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3388 
3389                         <PropertyGroup Condition=`'$(Configuration)' == 'Config1'`>
3390                             <Prop>FromUserFile1</Prop>
3391                         </PropertyGroup>
3392                         <PropertyGroup Condition=`'$(Configuration)' == 'Config2'`>
3393                             <Prop>FromUserFile2</Prop>
3394                         </PropertyGroup>
3395 
3396                     </Project>
3397 
3398                 ");
3399 
3400             string mainProjFilename = ObjectModelHelpers.CreateTempFileOnDisk(@"
3401 
3402                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3403                         <Import Project=`{0}`/>
3404                     </Project>
3405 
3406                 ", importedProjFilename);
3407 
3408             try
3409             {
3410                 // Load the two projects using the MSBuild object model.
3411                 Project mainProj = new Project(new Engine(@"c:\"));
3412                 Project importedProj = new Project(mainProj.ParentEngine);
3413 
3414                 mainProj.Load(mainProjFilename);
3415                 importedProj.Load(importedProjFilename);
3416 
3417                 foreach (BuildPropertyGroup bpg in mainProj.PropertyGroups)
3418                 {
3419                     if (bpg.Condition == "'$(Configuration)' == 'Config2'")
3420                     {
3421                         bpg.SetImportedPropertyGroupCondition("'$(Configuration)' == 'NewConfig'");
3422                     }
3423                 }
3424 
3425                 string[] configs = mainProj.GetConditionedPropertyValues("Configuration");
3426 
3427                 Assertion.AssertEquals(2, configs.Length);
3428                 Assertion.Assert(configs[0] == "Config1" || configs[1] == "Config1");
3429                 Assertion.Assert(configs[0] == "NewConfig" || configs[1] == "NewConfig");
3430             }
3431             finally
3432             {
3433                 File.Delete(mainProjFilename);
3434                 File.Delete(importedProjFilename);
3435             }
3436         }
3437 
3438         /// <summary>
3439         /// Verify that the programfiles32 property points to the correct location
3440         /// </summary>
3441         [Test]
VerifyMsbuildProgramFiles32ReservedProperty()3442         public void VerifyMsbuildProgramFiles32ReservedProperty()
3443         {
3444             MockLogger ml = ObjectModelHelpers.BuildProjectExpectSuccess(@"
3445                              <Project ToolsVersion=""msbuilddefaulttoolsversion"" xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
3446                                 <PropertyGroup>
3447                                     <abcdef>$(MsBuildProgramFiles32)</abcdef>
3448                                 </PropertyGroup>
3449 
3450                                 <Target Name='t'>
3451                                     <Message Text='[$(abcdef)]' />
3452                                 </Target>
3453                               </Project>");
3454 
3455             // Make sure the log contains the correct strings.
3456             ml .AssertLogContains(String.Format("[{0}]", FrameworkLocationHelper.programFiles32));
3457         }
3458 
3459         /// <summary>
3460         /// Tests to see if the main project's evaluated properties will pick up changes made
3461         /// to the imported project file.  This is necessary for IDE scenarios, particularly for
3462         /// things like the "ReferencePath" property which is stored in the .USER file and is
3463         /// used during the build process for the main project.
3464         /// </summary>
3465         /// <owner>RGoel</owner>
3466         [Test]
ModifyPropertyInImportedProjectFileAfterRename()3467         public void ModifyPropertyInImportedProjectFileAfterRename()
3468         {
3469             ObjectModelHelpers.DeleteTempProjectDirectory();
3470 
3471             // Create temp files on disk for the main project file and the imported project file.
3472             string importedProjFilename = ObjectModelHelpers.CreateFileInTempProjectDirectory("imported.proj", @"
3473 
3474                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3475 
3476                         <PropertyGroup>
3477                             <ReferencePath>c:\foobar</ReferencePath>
3478                         </PropertyGroup>
3479 
3480                     </Project>
3481 
3482                 ");
3483 
3484             string mainProjFilename = ObjectModelHelpers.CreateFileInTempProjectDirectory("main.proj", string.Format(@"
3485 
3486                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3487 
3488                         <Import Project=`{0}`/>
3489 
3490                         <PropertyGroup>
3491                             <ConsumingRefPath>$(ReferencePath)</ConsumingRefPath>
3492                         </PropertyGroup>
3493 
3494                     </Project>
3495 
3496                 ", importedProjFilename));
3497 
3498             // Load the two projects using the MSBuild object model.
3499             Project mainProj = new Project(new Engine(@"c:\"));
3500             Project importedProj = new Project(mainProj.ParentEngine);
3501 
3502             mainProj.Load(mainProjFilename);
3503             importedProj.Load(importedProjFilename);
3504 
3505             // Initially, the main project gets the correct value of the property from
3506             // the imported project.  So ConsumingRefPath == "$(ReferencePath)" == "c:\foobar".
3507             Assertion.AssertEquals(@"c:\foobar", mainProj.EvaluatedProperties["ConsumingRefPath"].FinalValueEscaped);
3508 
3509             mainProj.SetImportedProperty("ReferencePath", @"c:\boobah", null, importedProj);
3510 
3511             // Now if we query for the property, we should get back the new value.
3512             Assertion.AssertEquals(@"c:\boobah", mainProj.EvaluatedProperties["ConsumingRefPath"].FinalValueEscaped);
3513             Assertion.AssertEquals(@"c:\boobah", importedProj.EvaluatedProperties["ReferencePath"].FinalValueEscaped);
3514 
3515             importedProj.Save(Path.Combine(ObjectModelHelpers.TempProjectDir, "newimported.proj"));
3516 
3517             // Now we add a new imported property to the main file, into an existing imported
3518             // property group.
3519             mainProj.SetImportedProperty("ReferencePath", @"c:\hoohah", null, importedProj);
3520 
3521             // Now if we query for the property, we should get back the new value.
3522             Assertion.AssertEquals(@"c:\hoohah", importedProj.EvaluatedProperties["ReferencePath"].FinalValueEscaped);
3523             Assertion.AssertEquals(@"c:\hoohah", mainProj.EvaluatedProperties["ReferencePath"].FinalValueEscaped);
3524         }
3525     }
3526 
3527     /// <summary>
3528     /// A very simple task that logs a constant message.
3529     /// </summary>
3530     /// <owner>RGoel</owner>
3531     public class WashCar : Microsoft.Build.Utilities.Task
3532     {
Execute()3533         public override bool Execute()
3534         {
3535             this.Log.LogMessage("Done washing car.");
3536             return true;
3537         }
3538     }
3539 
3540     /// <summary>
3541     /// A very simple Message task.  We are intentionally giving it the same name as one of our shipping tasks.
3542     /// </summary>
3543     /// <owner>RGoel</owner>
3544     public class Message : Microsoft.Build.Utilities.Task
3545     {
3546         private string text;
3547         public string Text
3548         {
3549             set {text = value;}
3550         }
3551 
Execute()3552         public override bool Execute()
3553         {
3554             this.Log.LogMessage("Custom Message task: " + text);
3555             return true;
3556         }
3557     }
3558 
3559     [TestFixture]
3560     public class UsingTask
3561     {
3562         /// <summary>
3563         /// Register a custom task using the fully qualified name, and invoke it using the fully qualified name.
3564         /// </summary>
3565         /// <owner>RGoel</owner>
3566         [Test]
CustomTaskRegisteredFullInvokedFull()3567         public void CustomTaskRegisteredFullInvokedFull()
3568         {
3569             // Create a project file that calls our custom WashCar task (implemented just a few lines above).
3570             MockLogger ml = ObjectModelHelpers.BuildProjectExpectSuccess(String.Format(@"
3571 
3572                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3573 
3574                         <UsingTask TaskName=`Microsoft.Build.UnitTests.Project_Tests.WashCar` AssemblyFile=`{0}`/>
3575 
3576                         <Target Name=`Build`>
3577                             <Microsoft.Build.UnitTests.Project_Tests.WashCar/>
3578                         </Target>
3579 
3580                     </Project>
3581 
3582                 ", new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath));
3583 
3584             // Make sure the log contains the correct strings.
3585             Assertion.Assert("Custom WashCar task should have been called.", ml.FullLog.Contains("Done washing car."));
3586         }
3587 
3588         /// <summary>
3589         /// Register a custom task using the fully qualified name, and invoke it using the simple name.
3590         /// </summary>
3591         /// <owner>RGoel</owner>
3592         [Test]
CustomTaskRegisteredFullInvokedSimple()3593         public void CustomTaskRegisteredFullInvokedSimple()
3594         {
3595             // Create a project file that calls our custom WashCar task (implemented just a few lines above).
3596             MockLogger ml = ObjectModelHelpers.BuildProjectExpectSuccess(String.Format(@"
3597 
3598                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3599 
3600                         <UsingTask TaskName=`Microsoft.Build.UnitTests.Project_Tests.WashCar` AssemblyFile=`{0}`/>
3601 
3602                         <Target Name=`Build`>
3603                             <WashCar/>
3604                         </Target>
3605 
3606                     </Project>
3607 
3608                 ", new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath));
3609 
3610             // Make sure the log contains the correct strings.
3611             Assertion.Assert("Custom WashCar task should have been called.", ml.FullLog.Contains("Done washing car."));
3612         }
3613 
3614         /// <summary>
3615         /// Register a custom task using the simple name, and invoke it using the fully qualified name.
3616         /// </summary>
3617         /// <owner>RGoel</owner>
3618         [Test]
CustomTaskRegisteredSimpleInvokedFull()3619         public void CustomTaskRegisteredSimpleInvokedFull()
3620         {
3621             // Create a project file that calls our custom WashCar task (implemented just a few lines above).
3622             MockLogger ml = ObjectModelHelpers.BuildProjectExpectSuccess(String.Format(@"
3623 
3624                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3625 
3626                         <UsingTask TaskName=`WashCar` AssemblyFile=`{0}`/>
3627 
3628                         <Target Name=`Build`>
3629                             <Microsoft.Build.UnitTests.Project_Tests.WashCar/>
3630                         </Target>
3631 
3632                     </Project>
3633 
3634                 ", new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath));
3635 
3636             // Make sure the log contains the correct strings.
3637             Assertion.Assert("Custom WashCar task should have been called.", ml.FullLog.Contains("Done washing car."));
3638         }
3639 
3640         /// <summary>
3641         /// Register a custom task using the simple name, and invoke it using the simple name.
3642         /// </summary>
3643         /// <owner>RGoel</owner>
3644         [Test]
CustomTaskRegisteredSimpleInvokedSimple()3645         public void CustomTaskRegisteredSimpleInvokedSimple()
3646         {
3647             // Create a project file that calls our custom WashCar task (implemented just a few lines above).
3648             MockLogger ml = ObjectModelHelpers.BuildProjectExpectSuccess(String.Format(@"
3649 
3650                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3651 
3652                         <UsingTask TaskName=`WashCar` AssemblyFile=`{0}`/>
3653 
3654                         <Target Name=`Build`>
3655                             <WashCar/>
3656                         </Target>
3657 
3658                     </Project>
3659 
3660                 ", new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath));
3661 
3662             // Make sure the log contains the correct strings.
3663             Assertion.Assert("Custom WashCar task should have been called.", ml.FullLog.Contains("Done washing car."));
3664         }
3665 
3666         /// <summary>
3667         /// Register a custom task that has the same class name as one of our shipping tasks, but in a different
3668         /// namespace.  Register it using the fully qualified namespace.  Then invoke the task with:
3669         /// 1.) fully qualified namespace of the custom task.  This should invoke the custom task.
3670         /// 2.) fully qualified namespace of the shipping task.  This should invoke the shipping task.
3671         /// 3.) unqualified task name.  This should invoke the shipping task.
3672         /// </summary>
3673         /// <owner>RGoel</owner>
3674         [Test]
CustomMessageTaskRegisteredFullyQualifed()3675         public void CustomMessageTaskRegisteredFullyQualifed()
3676         {
3677             // Create a project file that calls our custom Message task and the shipping Message task.
3678             // (The custom Message task is implemented just a few lines above.)
3679             MockLogger ml = ObjectModelHelpers.BuildProjectExpectSuccess(String.Format(ObjectModelHelpers.CleanupFileContents(@"
3680 
3681                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3682 
3683                         <UsingTask TaskName=`Microsoft.Build.UnitTests.Project_Tests.Message` AssemblyFile=`{0}`/>
3684 
3685                         <!-- In our .tasks file Message is used fully qualified. In order to perform this test, we need to make sure it's defined partially qualified-->
3686                         <UsingTask TaskName='Message' AssemblyName='Microsoft.Build.Tasks.Core, Version=msbuildassemblyversion, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'/>
3687 
3688                         <Target Name=`Build`>
3689 
3690                             <!-- This should run the custom Message task. -->
3691                             <Microsoft.Build.UnitTests.Project_Tests.Message Text=`Being`/>
3692 
3693                             <!-- This should run the shipping Message task. -->
3694                             <Microsoft.Build.Tasks.Message Text=`John`/>
3695 
3696                             <!-- This should run the shipping Message task. -->
3697                             <Message Text=`Malkovich`/>
3698 
3699                         </Target>
3700 
3701                     </Project>
3702 
3703                 "), new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath));
3704 
3705             // Make sure the log contains the correct strings.
3706             Assertion.Assert("Custom Message task should have been called the first time.",     ml.FullLog.Contains("Custom Message task: Being"));
3707 
3708             Assertion.Assert("Some Message task should have been called the second time.",      ml.FullLog.Contains("John"));
3709             Assertion.Assert("Shipping Message task should have been called the second time.", !ml.FullLog.Contains("Custom Message task: John"));
3710 
3711             Assertion.Assert("Some Message task should have been called the third time.",       ml.FullLog.Contains("Malkovich"));
3712             Assertion.Assert("Shipping Message task should have been called the third time.",  !ml.FullLog.Contains("Custom Message task: Malkovich"));
3713         }
3714 
3715         /// <summary>
3716         /// Register a custom task that has the same class name as one of our shipping tasks, but in a different
3717         /// namespace.  Register it using only the simple name.  Then invoke the task with:
3718         /// 1.) fully qualified namespace of the custom task.  This should invoke the custom task.
3719         /// 2.) fully qualified namespace of the shipping task.  This should invoke the shipping task.
3720         /// 3.) unqualified task name.  This should invoke the custom task.
3721         /// </summary>
3722         /// <owner>RGoel</owner>
3723         [Test]
CustomMessageTaskRegisteredSimple()3724         public void CustomMessageTaskRegisteredSimple()
3725         {
3726             // Create a project file that calls our custom Message task and the shipping Message task.
3727             // (The custom Message task is implemented just a few lines above.)
3728             MockLogger ml = ObjectModelHelpers.BuildProjectExpectSuccess(String.Format(@"
3729 
3730                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3731 
3732                         <UsingTask TaskName=`Message` AssemblyFile=`{0}`/>
3733 
3734                         <Target Name=`Build`>
3735 
3736                             <!-- This should run the custom Message task. -->
3737                             <Microsoft.Build.UnitTests.Project_Tests.Message Text=`Being`/>
3738 
3739                             <!-- This should run the shipping Message task. -->
3740                             <Microsoft.Build.Tasks.Message Text=`John`/>
3741 
3742                             <!-- This should run the custom Message task. -->
3743                             <Message Text=`Malkovich`/>
3744 
3745                         </Target>
3746 
3747                     </Project>
3748 
3749                 ", new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath));
3750 
3751             // Make sure the log contains the correct strings.
3752             Assertion.Assert("Custom Message task should have been called the first time.",     ml.FullLog.Contains("Custom Message task: Being"));
3753 
3754             Assertion.Assert("Some Message task should have been called the second time.",      ml.FullLog.Contains("John"));
3755             Assertion.Assert("Shipping Message task should have been called the second time.", !ml.FullLog.Contains("Custom Message task: John"));
3756 
3757             Assertion.Assert("Custom Message task should have been called the third time.",     ml.FullLog.Contains("Custom Message task: Malkovich"));
3758         }
3759     }
3760 
3761     [TestFixture]
3762     public class Properties
3763     {
3764         [TearDown]
TearDown()3765         public void TearDown()
3766         {
3767             if (Registry.CurrentUser.OpenSubKey("msbuildUnitTests") != null)
3768             {
3769                 Registry.CurrentUser.DeleteSubKeyTree("msbuildUnitTests");
3770             }
3771         }
3772 
3773         private const string testRegistryPath = @"msbuildUnitTests";
3774 
3775         /// <summary>
3776         /// Basic test.
3777         /// </summary>
3778         [Test]
RegistryProperties()3779         public void RegistryProperties()
3780         {
3781             RegistryKey testRegistryKey = null;
3782 
3783             testRegistryKey = Registry.CurrentUser.CreateSubKey(testRegistryPath);
3784             testRegistryKey.SetValue("Foo", "FooValue");
3785 
3786             Project p = ObjectModelHelpers.CreateInMemoryProject(@"
3787 
3788                    <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3789                       <PropertyGroup>
3790                         <P>$(Registry:HKEY_CURRENT_USER\" + testRegistryPath + @"@Foo)</P>
3791                         <Q Condition=""'$(Registry:HKEY_CURRENT_USER\" + testRegistryPath + @"@Foo)' == 'FooValue'"">QValue</Q>
3792                       </PropertyGroup>
3793                     </Project>
3794                 ");
3795 
3796             Assertion.AssertEquals("FooValue", p.EvaluatedProperties["P"].FinalValue);
3797             Assertion.AssertEquals("QValue", p.EvaluatedProperties["Q"].FinalValue);
3798         }
3799 
3800         /// <summary>
3801         /// Basic test.
3802         /// </summary>
3803         [Test]
3804         public void RegistryPropertiesWithEscapedCharactersInValue()
3805         {
3806             RegistryKey testRegistryKey = null;
3807             testRegistryKey = Registry.CurrentUser.CreateSubKey(testRegistryPath);
3808             testRegistryKey.SetValue("Foo", "FooValue");
3809             testRegistryKey.SetValue("Bar", "%24(Foo)");
3810             testRegistryKey.SetValue("Property3", "%24(NUMBER_OF_PROCESSORS)");
3811             testRegistryKey.SetValue("@Property4", "Value4");
3812 
3813             Project p = ObjectModelHelpers.CreateInMemoryProject(@"
3814 
3815                    <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3816                       <PropertyGroup>
3817                         <P>$(Registry:HKEY_CURRENT_USER\" + testRegistryPath + @"@Foo)</P>
3818                         <Q Condition=""'$(Registry:HKEY_CURRENT_USER\" + testRegistryPath + @"@Bar)' == '%24(Foo)'"">QValue</Q>
3819                         <R>$(Registry:HKEY_CURRENT_USER\" + testRegistryPath + @"@Property3)</R>
3820                         <S>$(Registry:HKEY_CURRENT_USER\" + testRegistryPath + @"@%40Property4)</S>
3821                       </PropertyGroup>
3822                     </Project>
3823                 ");
3824 
3825             Assertion.AssertEquals("FooValue", p.EvaluatedProperties["P"].FinalValue);
3826             Assertion.AssertEquals("QValue", p.EvaluatedProperties["Q"].FinalValue);
3827             Assertion.AssertEquals("$(NUMBER_OF_PROCESSORS)", p.EvaluatedProperties["R"].FinalValue);
3828             Assertion.AssertEquals("Value4", p.EvaluatedProperties["S"].FinalValue);
3829         }
3830     }
3831 
3832     [TestFixture]
3833     public class QueryProjectState
3834     {
3835         /// <summary>
3836         /// This tests the Project.EvaluatedItemsIgnoringCondition property.  This
3837         /// property should return the list of evaluated items in the project,
3838         /// pretending that all "Condition"s evaluated to true.
3839         /// </summary>
3840         /// <owner>RGoel</owner>
3841         [Test]
3842         public void GetEvaluatedItemsIgnoringCondition()
3843         {
3844             string projectOriginalContents = @"
3845                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3846 
3847                     <PropertyGroup>
3848                         <Ext>.cs</Ext>
3849                     </PropertyGroup>
3850 
3851                     <ItemGroup>
3852                         <Compile Include=`a$(Ext); b.cs` Condition=`'1'=='0'`>
3853                             <HintPath>hint</HintPath>
3854                         </Compile>
3855                         <Compile Include=`c$(Ext)` />
3856                     </ItemGroup>
3857 
3858                     <Target Name=`Build` />
3859 
3860                 </Project>
3861                 ";
3862 
3863             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
3864 
3865             // Get the set of evaluated items.
3866             BuildItem[] evaluatedItemsIgnoringCondition1 = project.EvaluatedItemsIgnoringCondition.ToArray();
3867             BuildItem[] evaluatedItems1 = project.EvaluatedItems.ToArray();
3868 
3869             // The VS IDE does a few re-evaluations with different sets of global properties
3870             // (i.e., Configuration=Debug, Configuration=Release, etc.).  This is to simulate
3871             // that.  The point is that re-evaluating a project should NOT change the items
3872             // that are returned from Project.EvaluatedItemsIgnoringCondition.  Those items
3873             // need to be semi-permanent so that the IDE can hang on to them across multiple
3874             // builds.
3875             project.MarkProjectAsDirty ();
3876             BuildItem[] evaluatedItemsIgnoringCondition2 = project.EvaluatedItemsIgnoringCondition.ToArray();
3877             BuildItem[] evaluatedItems2 = project.EvaluatedItems.ToArray();
3878 
3879             // Confirm the "IgnoreCondition" lists:
3880             {
3881                 EngineHelpers.AssertItemsMatch(@"
3882                     a.cs : HintPath=hint
3883                     b.cs : HintPath=hint
3884                     c.cs : HintPath=
3885                     ", evaluatedItemsIgnoringCondition1);
3886 
3887                 EngineHelpers.AssertItemsMatch(@"
3888                     a.cs : HintPath=hint
3889                     b.cs : HintPath=hint
3890                     c.cs : HintPath=
3891                     ", evaluatedItemsIgnoringCondition2);
3892 
3893                 // Confirm that both lists contain the exact same 3 items.
3894                 Assertion.AssertEquals(evaluatedItemsIgnoringCondition1[0], evaluatedItemsIgnoringCondition2[0]);
3895                 Assertion.AssertEquals(evaluatedItemsIgnoringCondition1[1], evaluatedItemsIgnoringCondition2[1]);
3896                 Assertion.AssertEquals(evaluatedItemsIgnoringCondition1[2], evaluatedItemsIgnoringCondition2[2]);
3897             }
3898 
3899             // Confirm the other "normal" lists:
3900             {
3901                 EngineHelpers.AssertItemsMatch("c.cs", evaluatedItems1);
3902                 EngineHelpers.AssertItemsMatch("c.cs", evaluatedItems2);
3903 
3904                 // Confirm that both lists do *not* contain the exact same item.
3905                 Assertion.Assert(evaluatedItems1[0] != evaluatedItems2[0]);
3906             }
3907         }
3908 
3909         /// <summary>
3910         /// Ensures that if we define a new item list based on another previously defined
3911         /// item list that the new item list inherits the metadata from the previous item list.
3912         /// </summary>
3913         /// <owner>RGoel</owner>
3914         [Test]
3915         public void GetItemMetadataInheritedFromOtherItems()
3916         {
3917             string projectOriginalContents = @"
3918                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3919 
3920                     <ItemGroup>
3921                         <Compile Include=`a;b`>
3922                             <Culture>Klingon</Culture>
3923                             <Magazine>Time</Magazine>
3924                         </Compile>
3925                         <Compile Include=`c`>
3926                             <Culture>Smurf</Culture>
3927                         </Compile>
3928                         <Compile2 Include=`@(Compile)` />
3929                         <Compile3 Include=`@(Compile->'%(Identity)3')`>
3930                             <Magazine>Newsweek</Magazine>
3931                         </Compile3>
3932                     </ItemGroup>
3933 
3934                 </Project>
3935                 ";
3936 
3937             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
3938 
3939             // ==========================================
3940             // VALIDATE @(COMPILE2) IS DEFINED CORRECTLY.
3941             // ==========================================
3942             BuildItemGroup compile2 = project.GetEvaluatedItemsByName("Compile2");
3943 
3944             EngineHelpers.AssertItemsMatch(@"
3945                 a : Culture=Klingon ; Magazine=Time
3946                 b : Culture=Klingon ; Magazine=Time
3947                 c : Culture=Smurf   ; Magazine=
3948                 ", compile2);
3949 
3950             // ==========================================
3951             // VALIDATE @(COMPILE3) IS DEFINED CORRECTLY.
3952             // ==========================================
3953             BuildItemGroup compile3 = project.GetEvaluatedItemsByName("Compile3");
3954 
3955             EngineHelpers.AssertItemsMatch(@"
3956                 a3 : Culture=Klingon ; Magazine=Newsweek
3957                 b3 : Culture=Klingon ; Magazine=Newsweek
3958                 c3 : Culture=Smurf   ; Magazine=Newsweek
3959                 ", compile3);
3960         }
3961     }
3962 
3963     [TestFixture]
3964     public class Imports
3965     {
3966         /// <summary>
3967         /// Tests that the engine does care about an invalid import if the condition
3968         /// is false - #484931
3969         /// </summary>
3970         /// <owner>danmose</owner>
3971         [Test]
3972         public void InvalidImportFalseCondition()
3973         {
3974             // Very important to put the Project attribute before the Condition attribute,
3975             // to repro the crash in #484931
3976             Project p = ObjectModelHelpers.CreateInMemoryProject(@"
3977 
3978                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
3979                         <Import Project=`||oops*invalid*project||` Condition=`'$(nonexistent)'!=''`  />
3980                         <Target Name=`t`/>
3981                     </Project>
3982 
3983                 ");
3984 
3985             // Should build successfully
3986             Assertion.Assert(p.Build(new string[] { "t" }, null));
3987 
3988             // No exception
3989         }
3990 
3991         /// <summary>
3992         /// Tests that the engine handles invalid Import project path nicely - #347276
3993         /// </summary>
3994         /// <owner>danmose</owner>
3995         [Test]
3996         [ExpectedException(typeof(InvalidProjectFileException))]
3997         public void InvalidImportProject()
3998         {
3999             Project p = ObjectModelHelpers.CreateInMemoryProject(@"
4000 
4001                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4002                         <Import Project=`||||` Condition=`true` />
4003                         <Target Name=`t`/>
4004                     </Project>
4005 
4006                 ");
4007 
4008             p.Build(new string[] { "t" }, null); // Should throw
4009         }
4010 
4011         /// <summary>
4012         /// Tests that the engine handles invalid Import project path nicely - #347276
4013         /// </summary>
4014         /// <owner>danmose</owner>
4015         [Test]
4016         [ExpectedException(typeof(InvalidProjectFileException))]
4017         public void InvalidImportProject2()
4018         {
4019             Project p = ObjectModelHelpers.CreateInMemoryProject(@"
4020 
4021                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4022                         <Import Project=`     `  />
4023                         <Target Name=`t`/>
4024                     </Project>
4025 
4026                 ");
4027 
4028             p.Build(new string[] { "t" }, null); // Should throw
4029         }
4030 
4031         /// <summary>
4032         /// Tests that the engine handles invalid Import project path nicely - #347276
4033         /// </summary>
4034         /// <owner>danmose</owner>
4035         [Test]
4036         [ExpectedException(typeof(InvalidProjectFileException))]
4037         public void InvalidImportProject3()
4038         {
4039             Project p = ObjectModelHelpers.CreateInMemoryProject(@"
4040 
4041                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4042                         <Import Project=`***`  />
4043                         <Target Name=`t`/>
4044                     </Project>
4045 
4046                 ");
4047 
4048             p.Build(new string[] { "t" }, null); // Should throw
4049         }
4050 
4051         /// <summary>
4052         /// Test replacing the project import path with something else
4053         /// </summary>
4054         [Test]
4055         public void ReplaceImport()
4056         {
4057             XmlDocument xmldoc = new XmlDocument();
4058             xmldoc.LoadXml(ObjectModelHelpers.CleanupFileContents(@"
4059                 <Project ToolsVersion=`msbuilddefaulttoolsversion` DefaultTargets=`Build` InitialTargets=`Clean` xmlns=`msbuildnamespace`>
4060                     <Import Project=`Microsoft.Uncommon.targets` />
4061                 </Project>
4062                 "));
4063 
4064             Engine engine = new Engine(@"c:\");
4065             Project project = engine.CreateNewProject();
4066             project.LoadFromXmlDocument(xmldoc, null, ProjectLoadSettings.IgnoreMissingImports);
4067 
4068             IEnumerator enumerator = project.Imports.GetEnumerator();
4069             enumerator.MoveNext();
4070             Import import = (Import)enumerator.Current;
4071 
4072             import.ProjectPath = "Microsoft.Rare.targets";
4073             Assert.AreEqual(null, import.Condition);
4074             import.Condition = "'true' == 'true'";
4075             Assert.AreEqual("'true' == 'true'", import.Condition);
4076 
4077             StringWriter writer = new StringWriter();
4078             project.Save(writer);
4079 
4080             // Load the modified project into a new project object
4081             xmldoc = new XmlDocument();
4082             xmldoc.LoadXml(writer.ToString());
4083 
4084             Project projectWithChangedImport = engine.CreateNewProject();
4085             project.LoadFromXmlDocument(xmldoc, null, ProjectLoadSettings.IgnoreMissingImports);
4086             Assert.AreEqual(1, project.Imports.Count);
4087 
4088             enumerator = project.Imports.GetEnumerator();
4089             enumerator.MoveNext();
4090             import = (Import)enumerator.Current;
4091 
4092             Assert.AreEqual("Microsoft.Rare.targets", import.ProjectPath);
4093             Assert.AreEqual("'true' == 'true'", import.Condition);
4094         }
4095     }
4096 
4097     [TestFixture]
4098     public class Evaluation
4099     {
4100         /// <summary>
4101         /// Relative paths in 'exists' on conditions should be evalauted relative to the
4102         /// project directory.
4103         /// </summary>
4104         [Test]
4105         public void ImportConditionsEvaluatedUsingProjectsDirectory()
4106         {
4107             string importFile = ObjectModelHelpers.CreateTempFileOnDisk(@"
4108                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4109                         <Target Name=`t`>
4110                            <Message Text=`[in import]`/>
4111                         </Target>
4112                     </Project>
4113                 ");
4114 
4115             // Import the file (and express the import condition) using a *relative* path
4116             string importFileRelative = Path.GetFileName(importFile);
4117 
4118             string projectFile = ObjectModelHelpers.CreateTempFileOnDisk(@"
4119                     <Project xmlns=`msbuildnamespace`>
4120                         <Import Project=`{0}` Condition=`exists('{0}')` />
4121                     </Project>
4122                 ", importFileRelative);
4123 
4124             string currentDirectory = Environment.CurrentDirectory;
4125             try
4126             {
4127                 MockLogger logger = new MockLogger();
4128                 Project project = LoadAndBuildInDifferentCurrentDirectory(projectFile, logger);
4129 
4130                 logger.AssertLogContains("[in import]");
4131                 logger.ClearLog();
4132 
4133                 DirtyAndBuildInDifferentCurrentDirectory(project);
4134                 logger.AssertLogContains("[in import]");
4135             }
4136             finally
4137             {
4138                 File.Delete(projectFile);
4139                 File.Delete(importFile);
4140                 Environment.CurrentDirectory = currentDirectory;
4141             }
4142         }
4143 
4144         /// <summary>
4145         /// Relative paths in 'exists' on conditions should be evalauted relative to the
4146         /// project directory.
4147         /// </summary>
4148         [Test]
4149         public void PropertyConditionsEvaluatedUsingProjectsDirectory()
4150         {
4151             string tempFile = Path.GetTempFileName();
4152             string relativeTempFile = Path.GetFileName(tempFile);
4153 
4154             string projectFile = ObjectModelHelpers.CreateTempFileOnDisk(@"
4155                     <Project xmlns=`msbuildnamespace`>
4156                         <PropertyGroup>
4157                             <p Condition=`exists('{0}')`>v1</p>
4158                         </PropertyGroup>
4159                         <Target Name=`t`>
4160                            <Message Text=`[$(p)]`/>
4161                         </Target>
4162                     </Project>
4163                 ", relativeTempFile);
4164 
4165             try
4166             {
4167                 MockLogger logger = new MockLogger();
4168                 Project project = LoadAndBuildInDifferentCurrentDirectory(projectFile, logger);
4169 
4170                 logger.AssertLogContains("[v1]");
4171                 logger.ClearLog();
4172 
4173                 DirtyAndBuildInDifferentCurrentDirectory(project);
4174                 logger.AssertLogContains("[v1]");
4175             }
4176             finally
4177             {
4178                 File.Delete(projectFile);
4179                 File.Delete(tempFile);
4180             }
4181         }
4182 
4183         /// <summary>
4184         /// Relative paths in 'exists' on conditions should be evalauted relative to the
4185         /// project directory.
4186         /// </summary>
4187         [Test]
4188         public void ItemAndTargetConditionsEvaluatedUsingProjectsDirectory()
4189         {
4190             string tempFile = Path.GetTempFileName();
4191             string relativeTempFile = Path.GetFileName(tempFile);
4192 
4193             string projectFile = ObjectModelHelpers.CreateTempFileOnDisk(@"
4194                     <Project xmlns=`msbuildnamespace`>
4195                         <ItemGroup>
4196                             <i Include=`i1` Condition=`exists('{0}')`/>
4197                         </ItemGroup>
4198                         <Target Name=`t` Condition=`exists('{0}')`>
4199                            <Message Text=`[@(i)]`/>
4200                         </Target>
4201                     </Project>
4202                 ", relativeTempFile);
4203 
4204             try
4205             {
4206                 MockLogger logger = new MockLogger();
4207                 Project project = LoadAndBuildInDifferentCurrentDirectory(projectFile, logger);
4208 
4209                 logger.AssertLogContains("[i1]");
4210                 logger.ClearLog();
4211 
4212                 DirtyAndBuildInDifferentCurrentDirectory(project);
4213                 logger.AssertLogContains("[i1]");
4214             }
4215             finally
4216             {
4217                 File.Delete(projectFile);
4218                 File.Delete(tempFile);
4219             }
4220         }
4221 
4222         /// <summary>
4223         /// Changes the current directory, loads the project, changes the current directory again, and builds it.
4224         /// </summary>
4225         private static Project LoadAndBuildInDifferentCurrentDirectory(string projectFile, MockLogger logger)
4226         {
4227             string currentDirectory = Environment.CurrentDirectory;
4228             Project project;
4229 
4230             try
4231             {
4232                 project = new Project(new Engine());
4233                 project.ParentEngine.RegisterLogger(logger);
4234 
4235                 // Make sure that the current directory isn't the project directory somehow
4236                 Environment.CurrentDirectory = Environment.SystemDirectory;
4237                 project.Load(projectFile);
4238 
4239                 // Make sure that the current directory isn't the project directory somehow
4240                 Environment.CurrentDirectory = Environment.SystemDirectory;
4241                 project.Build();
4242             }
4243             finally
4244             {
4245                 Environment.CurrentDirectory = currentDirectory;
4246             }
4247 
4248             return project;
4249         }
4250 
4251         /// <summary>
4252         /// Dirties the project, changes the current directory, and builds it.
4253         /// </summary>
4254         private void DirtyAndBuildInDifferentCurrentDirectory(Project project)
4255         {
4256             string currentDirectory = Environment.CurrentDirectory;
4257 
4258             try
4259             {
4260                 project.MarkProjectAsDirty();
4261 
4262                 // Make sure that the current directory isn't the project directory somehow
4263                 Environment.CurrentDirectory = Environment.SystemDirectory;
4264                 project.Build();
4265             }
4266             finally
4267             {
4268                 Environment.CurrentDirectory = currentDirectory;
4269             }
4270         }
4271     }
4272 
4273     [TestFixture]
4274     public class EscapingAndRecursiveDir
4275     {
4276         /// <summary>
4277         /// Regress DDB# 114268. %28 in an item's include in the XML should
4278         /// match '(' in a file path. This was not happening in the code path
4279         /// that produces %(RecursiveDir).
4280         /// </summary>
4281         [Test]
4282         public void RecursiveDirPathWithParentheses()
4283         {
4284             string directory = null, subdirectory = null, file1 = null;
4285 
4286             try
4287             {
4288                 directory = Path.Combine(Path.GetTempPath(), "a(b)c");
4289                 subdirectory = Path.Combine(directory, "d");
4290                 file1 = Path.Combine(subdirectory, "e");
4291                 Directory.CreateDirectory(subdirectory);
4292 
4293                 // Create a file "%temp%\a(b)c\d\e"
4294                 File.WriteAllText(file1, String.Empty);
4295 
4296 
4297                 string xml = @"
4298                     <Project xmlns=`http://schemas.microsoft.com/developer/msbuild/2003`>
4299                         <ItemGroup>
4300                             <i Include=`" + directory + @"\**\*" + @"`/>
4301                         </ItemGroup>
4302                         <Target Name=`t`>
4303                            <Message Text=`[%(i.identity)-->c:\foo\%(i.recursivedir)%(i.filename)%(i.extension)]`/>
4304                         </Target>
4305                     </Project>
4306                 ";
4307 
4308                 Console.WriteLine(xml);
4309 
4310                 Project p = new Project();
4311                 p.FullFileName = Path.Combine(subdirectory, "x.proj");
4312                 p.LoadXml(xml.Replace('`', '"'));
4313 
4314                 MockLogger logger = new MockLogger();
4315                 p.ParentEngine.RegisterLogger(logger);
4316 
4317                 p.Build();
4318 
4319                 logger.AssertLogContains("[" + directory + @"\d\e-->c:\foo\d\e]");
4320             }
4321             finally
4322             {
4323                 File.Delete(file1);
4324                 Directory.Delete(subdirectory);
4325                 Directory.Delete(directory);
4326             }
4327         }
4328     }
4329 
4330     [TestFixture]
4331     public class ErrorCases
4332     {
4333         /// <summary>
4334         /// Tests that the engine correctly reports a failure when somebody tries to
4335         /// reference an item list inside the Condition for an Import.
4336         /// </summary>
4337         /// <owner>RGoel</owner>
4338         [Test]
4339         [ExpectedException(typeof(InvalidProjectFileException))]
ItemGroupInAnImportCondition()4340         public void ItemGroupInAnImportCondition()
4341         {
4342             Project p = ObjectModelHelpers.CreateInMemoryProject(@"
4343 
4344                     <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4345 
4346                         <Import Condition=`@(foo) == 'a.cs'` Project=`foo.proj` />
4347 
4348                         <Target Name=`t`>
4349                             <Message Text=`[$(a)]`/>
4350                         </Target>
4351 
4352                     </Project>
4353 
4354                 ");
4355 
4356             p.Build(new string[] { "t" }, null);
4357         }
4358 
4359         /// <summary>
4360         /// Tests to make sure we correctly fail on a target name that contains an embedded property.
4361         /// </summary>
4362         /// <owner>RGoel</owner>
4363         [Test]
4364         [ExpectedException(typeof(InvalidProjectFileException))]
InvalidTargetName()4365         public void InvalidTargetName()
4366         {
4367             string projectOriginalContents = @"
4368                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4369                     <Target Name=`Build$(Configuration)` />
4370                 </Project>
4371                 ";
4372 
4373             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
4374         }
4375 
4376         /// <summary>
4377         /// Tests to make sure we correctly fail on a meta-data name containing a period.
4378         /// </summary>
4379         /// <owner>danmose</owner>
4380         [Test]
4381         [ExpectedException(typeof(InvalidProjectFileException))]
InvalidMetadataName()4382         public void InvalidMetadataName()
4383         {
4384             string projectOriginalContents = @"
4385                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4386                     <ItemGroup>
4387                         <a Include=`x`>
4388                             <meta.data>foo</meta.data>
4389                         </a>
4390                     </ItemGroup>
4391                     <Target Name=`t` />
4392                 </Project>
4393                 ";
4394 
4395             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
4396         }
4397 
4398         /// <summary>
4399         /// Regression test for bug VSWhidbey 243657
4400         /// </summary>
4401         /// <owner>RGoel</owner>
4402         [Test]
4403         [ExpectedException(typeof(InvalidProjectFileException))]
IllegalCharactersInItemName()4404         public void IllegalCharactersInItemName()
4405         {
4406             string original = @"
4407                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4408 
4409                     <ItemGroup>
4410                         <A.B Include=`blah` />
4411                     </ItemGroup>
4412 
4413                 </Project>
4414                 ";
4415 
4416             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
4417         }
4418 
4419         /// <summary>
4420         /// Regression test for bug VSWhidbey 412627
4421         /// </summary>
4422         /// <owner>danmose</owner>
4423         [Test]
4424         [ExpectedException(typeof(InvalidProjectFileException))]
IllegalCharactersInUsingTaskAssemblyFile()4425         public void IllegalCharactersInUsingTaskAssemblyFile()
4426         {
4427             string original = @"
4428                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4429                     <UsingTask TaskName=`x` AssemblyFile=`||invalid||`/>
4430                 </Project>
4431                 ";
4432 
4433             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
4434         }
4435 
4436         /// <summary>
4437         /// Unknown attribute on UsingTask should throw
4438         /// </summary>
4439         /// <owner>danmose</owner>
4440         [Test]
4441         [ExpectedException(typeof(InvalidProjectFileException))]
UnknownAttributeInUsingTask()4442         public void UnknownAttributeInUsingTask()
4443         {
4444             string original = @"
4445                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4446                     <UsingTask TaskName=`x` AssemblyFile=`x` BogusAttribute=`v3.5`/>
4447                 </Project>
4448                 ";
4449 
4450             // Should throw
4451             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
4452         }
4453 
4454         /// <summary>
4455         /// RequiredRuntime attribute on UsingTask should be ignored
4456         /// (we'll make it actually do something in V3.5+1
4457         /// </summary>
4458         /// <owner>danmose</owner>
4459         [Test]
RequiredRuntimeAttributeInUsingTask()4460         public void RequiredRuntimeAttributeInUsingTask()
4461         {
4462             string original = @"
4463                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4464                     <UsingTask TaskName=`x` AssemblyFile=`x` RequiredRuntime=`v3.5`/>
4465                 </Project>
4466                 ";
4467 
4468             // Should not throw
4469             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
4470         }
4471 
4472         /// <summary>
4473         /// Tests that putting invalid characters in the <Import> path results in a
4474         /// InvalidProjectFileException.
4475         /// </summary>
4476         /// <owner>RGoel</owner>
4477         [ExpectedException(typeof(InvalidProjectFileException))]
4478         [Test]
InvalidCharactersInImportedPath()4479         public void InvalidCharactersInImportedPath()
4480         {
4481             string original = @"
4482                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4483                     <Import Project=`|||.proj` />
4484                 </Project>
4485                 ";
4486 
4487             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
4488         }
4489 
4490         /// <summary>
4491         /// Regress Whidbey 531457. Make sure that we restore the current directory after a child project
4492         /// throws an exception
4493         /// </summary>
4494         /// <owner>LukaszG</owner>
4495         [Test]
CurrentDirectoryRestoredAfterException()4496         public void CurrentDirectoryRestoredAfterException()
4497         {
4498             ObjectModelHelpers.DeleteTempProjectDirectory();
4499 
4500             // ---------------------
4501             // dirs.proj
4502             // ---------------------
4503             ObjectModelHelpers.CreateFileInTempProjectDirectory("dirs.proj", @"
4504                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4505                     <ItemGroup>
4506                         <Project Include=`a\a.proj` />
4507                         <Project Include=`b\b.proj` />
4508                     </ItemGroup>
4509                     <Target Name=`dirs`>
4510                         <MSBuild Projects=`@(Project)` />
4511                     </Target>
4512                 </Project>
4513             ");
4514 
4515             // ---------------------
4516             // a.proj
4517             // An invalid project file that will cause an InvalidProjectException
4518             // ---------------------
4519             ObjectModelHelpers.CreateFileInTempProjectDirectory(@"a\a.proj", @"
4520                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4521                     <Message Text=`a` />
4522                     <Target Name=`a`>
4523                         <Message Text=`Greetings from an invalid project!`/>
4524                     </Target>
4525                 </Project>
4526             ");
4527 
4528             // ---------------------
4529             // b.proj
4530             // A control project file that should build correctly after a.proj throws
4531             // ---------------------
4532             ObjectModelHelpers.CreateFileInTempProjectDirectory(@"b\b.proj", @"
4533                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4534                     <Target Name=`b`>
4535                         <Message Text=`Greetings from a valid project!`/>
4536                     </Target>
4537                 </Project>
4538             ");
4539 
4540             // Create a logger.
4541             MockLogger logger = new MockLogger();
4542             Project project = ObjectModelHelpers.LoadProjectFileInTempProjectDirectory("dirs.proj", logger);
4543 
4544             bool success = project.Build(null, null);
4545             Assertion.Assert("Build should have failed.  See Standard Out tab for details", !success);
4546 
4547             logger.AssertLogDoesntContain("Greetings from an invalid project!");
4548             logger.AssertLogContains("Greetings from a valid project!");
4549         }
4550     }
4551 
4552     [TestFixture]
4553     public class GlobalProperties
4554     {
4555         /// <summary>
4556         /// This tests that the project is correctly marked as dirty when we
4557         /// modify a global property.
4558         /// </summary>
4559         /// <owner>RGoel</owner>
4560         [Test]
SetNewGlobalProperty()4561         public void SetNewGlobalProperty()
4562         {
4563             string projectOriginalContents = @"
4564                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
4565 
4566                     <PropertyGroup>
4567                         <WarningLevel>4</WarningLevel>
4568                     </PropertyGroup>
4569 
4570                     <ItemGroup>
4571                         <Compile Include=`a.cs;b.cs`>
4572                             <HintPath>hint</HintPath>
4573                         </Compile>
4574                         <Resource Include=`strings.resx` />
4575                     </ItemGroup>
4576 
4577                     <Target Name=`Build` />
4578 
4579                 </Project>
4580                 ";
4581 
4582             Project project = ObjectModelHelpers.CreateInMemoryProject(projectOriginalContents);
4583 
4584             Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate);
4585 
4586             project.GlobalProperties.SetProperty("Configuration", "Debug");
4587 
4588             Assertion.Assert("Project should be dirty", project.IsDirtyNeedToReevaluate);
4589         }
4590 
4591         /// <summary>
4592         /// This tests that the project is NOT marked as dirty when we set a
4593         /// global property to the exact same value it had before.
4594         /// </summary>
4595         /// <owner>RGoel</owner>
4596         [Test]
SetGlobalPropertyToSameValue()4597         public void SetGlobalPropertyToSameValue()
4598         {
4599             Project project = ObjectModelHelpers.CreateInMemoryProject(@"<Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`/>");
4600 
4601             Assertion.Assert("Project shouldn't be dirty to begin with.", !project.IsDirtyNeedToReevaluate);
4602 
4603             project.GlobalProperties.SetProperty("Configuration", "Debug");
4604 
4605             Assertion.Assert("Project should be dirty after setting a global property for the first time.", project.IsDirtyNeedToReevaluate);
4606 
4607             // This forces a re-evaluation.
4608             BuildPropertyGroup evaluatedProps = project.EvaluatedProperties;
4609 
4610             Assertion.Assert("Project should not be dirty after re-evaluation.", !project.IsDirtyNeedToReevaluate);
4611 
4612             // Set the global property to the exact same value it had before.
4613             project.GlobalProperties.SetProperty("Configuration", "Debug");
4614 
4615             Assertion.Assert("Project should not be dirty after setting global property to same value.", !project.IsDirtyNeedToReevaluate);
4616         }
4617 
4618         /// <summary>
4619         /// Test that the default value for $(MSBuildExtensionsPath) points to "c:\program files\msbuild" on a 32 bit machine
4620         /// or points to c:\program files(x86)\msbuild on 64 bit machine.
4621         /// </summary>
4622         /// <owner>RGoel</owner>
4623         [Test]
MSBuildExtensionsPathDefault()4624         public void MSBuildExtensionsPathDefault()
4625         {
4626             string specialPropertyName = ReservedPropertyNames.extensionsPath;  // "MSBuildExtensionsPath"
4627 
4628             // Save the old copy of the MSBuildExtensionsPath, so we can restore it when the unit test is done.
4629             string backupMSBuildExtensionsPath = Environment.GetEnvironmentVariable(specialPropertyName);
4630 
4631             // Set an environment variable called MSBuildExtensionsPath to some value, for the purpose
4632             // of seeing whether our value wins.
4633             Environment.SetEnvironmentVariable(specialPropertyName, null);
4634 
4635             // Need to create a new engine object in order to pick up the new environment variables.
4636             Engine myEngine = new Engine();
4637             myEngine.BinPath = @"c:\";
4638 
4639             // Create a new project, and see what MSBuild gives us for the value of MSBuildExtensionsPath.
4640             // we should get the default value which is "c:\program files\msbuild".
4641             Project myProject = new Project(myEngine);
4642 
4643             string expectedValue = null;
4644             if(Environment.Is64BitOperatingSystem)
4645             {
4646                 expectedValue = Environment.GetEnvironmentVariable("ProgramFiles(x86)");
4647             }
4648             else
4649             {
4650                 expectedValue = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
4651             }
4652 
4653             Assertion.AssertEquals(expectedValue + @"\MSBuild",
4654                 (string)myProject.EvaluatedProperties[specialPropertyName]);
4655 
4656             // Restore the original value of the MSBuildExtensionsPath environment variable.
4657             Environment.SetEnvironmentVariable(specialPropertyName, backupMSBuildExtensionsPath);
4658         }
4659 
4660         /// <summary>
4661         /// When MSBUILDLEGACYEXTENSIONSPATH is set test tha $(MSBuildExtensionsPath) points to "c:\program files\msbuild". This should be valid for both 32 and 64 bit.
4662         /// </summary>
4663         [Test]
MSBuildExtensionsPathDefault_Legacy()4664         public void MSBuildExtensionsPathDefault_Legacy()
4665         {
4666             string specialPropertyName = "MSBuildExtensionsPath";
4667 
4668             // Save the old copy of the MSBuildExtensionsPath, so we can restore it when the unit test is done.
4669             string backupMSBuildExtensionsPath = Environment.GetEnvironmentVariable(specialPropertyName);
4670             string backupMagicSwitch = Environment.GetEnvironmentVariable("MSBUILDLEGACYEXTENSIONSPATH");
4671             string targetVar = Environment.GetEnvironmentVariable("Target");
4672             string numberVar = Environment.GetEnvironmentVariable("0env");
4673             string msbuildVar = Environment.GetEnvironmentVariable("msbuildtoolsversion");
4674 
4675             try
4676             {
4677                 // Set an environment variable called MSBuildExtensionsPath to some value, for the purpose
4678                 // of seeing whether our value wins.
4679                 Environment.SetEnvironmentVariable(specialPropertyName, null);
4680                 Environment.SetEnvironmentVariable("MSBUILDLEGACYEXTENSIONSPATH", "1");
4681 
4682                 // Need to create a new engine object in order to pick up the new environment variables.
4683                 Engine myEngine = new Engine();
4684                 myEngine.BinPath = @"c:\";
4685 
4686                 // Create a new project, and see what MSBuild gives us for the value of MSBuildExtensionsPath.
4687                 // we should get the default value which is "c:\program files\msbuild".
4688                 Project myProject = new Project(myEngine);
4689                 Assertion.AssertEquals(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + @"\MSBuild",
4690                                       (string)myProject.EvaluatedProperties[specialPropertyName]);
4691             }
4692             finally
4693             {
4694                 // Restore the original value of the MSBuildExtensionsPath environment variable.
4695                 Environment.SetEnvironmentVariable(specialPropertyName, backupMSBuildExtensionsPath);
4696                 Environment.SetEnvironmentVariable("MSBUILDLEGACYEXTENSIONSPATH", backupMagicSwitch);
4697                 Environment.SetEnvironmentVariable("Target", targetVar);
4698                 Environment.SetEnvironmentVariable("0env", numberVar);
4699                 Environment.SetEnvironmentVariable("msbuildtoolsversion", msbuildVar);
4700             }
4701         }
4702 
4703         /// <summary>
4704         /// Test that if I set an environment variable called "MSBuildExtensionPath", that my env var
4705         /// should win over whatever MSBuild thinks the default is.
4706         /// </summary>
4707         /// <owner>RGoel</owner>
4708         [Test]
MSBuildExtensionsPathWithEnvironmentOverride()4709         public void MSBuildExtensionsPathWithEnvironmentOverride()
4710         {
4711             string specialPropertyName = ReservedPropertyNames.extensionsPath;  // "MSBuildExtensionsPath"
4712 
4713             // Save the old copy of the MSBuildExtensionsPath, so we can restore it when the unit test is done.
4714             string backupMSBuildExtensionsPath = Environment.GetEnvironmentVariable(specialPropertyName);
4715 
4716             // Set an environment variable called MSBuildExtensionsPath to some value, for the purpose
4717             // of seeing whether our value wins.
4718             Environment.SetEnvironmentVariable(specialPropertyName, @"c:\devdiv\vscore\msbuild");
4719 
4720             // Need to create a new engine object in order to pick up the new environment variables.
4721             Engine myEngine = new Engine();
4722             myEngine.BinPath = @"c:\";
4723 
4724             // Create a new project, and see what MSBuild gives us for the value of MSBuildExtensionsPath.
4725             Project myProject = new Project(myEngine);
4726             Assertion.AssertEquals(@"c:\devdiv\vscore\msbuild",
4727                 (string)myProject.EvaluatedProperties[specialPropertyName]);
4728 
4729             // Restore the original value of the MSBuildExtensionsPath environment variable.
4730             Environment.SetEnvironmentVariable(specialPropertyName, backupMSBuildExtensionsPath);
4731         }
4732 
4733         /// <summary>
4734         /// Test that if I set a global property called "MSBuildExtensionPath", that my global property
4735         /// should win over whatever MSBuild thinks the default is.
4736         /// </summary>
4737         /// <owner>RGoel</owner>
4738         [Test]
MSBuildExtensionsPathWithGlobalOverride()4739         public void MSBuildExtensionsPathWithGlobalOverride()
4740         {
4741             string specialPropertyName = ReservedPropertyNames.extensionsPath;  // "MSBuildExtensionsPath"
4742 
4743             // Create a new project, and see what MSBuild gives us for the value of MSBuildExtensionsPath.
4744             Engine engine = new Engine(@"c:\");
4745             Project myProject = new Project(engine);
4746 
4747             // Set a global property called MSBuildExtensionsPath to some value, for the purpose
4748             // of seeing whether our value wins.
4749             myProject.GlobalProperties.SetProperty(specialPropertyName, @"c:\devdiv\vscore\msbuild");
4750 
4751             Assertion.AssertEquals(@"c:\devdiv\vscore\msbuild",
4752                 (string)myProject.EvaluatedProperties[specialPropertyName]);
4753         }
4754 
4755         /// <summary>
4756         /// The default value for $(MSBuildExtensionsPath32) should point to "c:\program files (x86)\msbuild" on a 64 bit machine.
4757         /// We can't test that directly since tests generally don't run on 64 bit boxes. However we can set the "ProgramFiles(x86)"
4758         /// environment variable and make sure that that's the value used.
4759         /// </summary>
4760         [Test]
MSBuildExtensionsPath32Default()4761         public void MSBuildExtensionsPath32Default()
4762         {
4763             string programFiles32EnvVar = "ProgramFiles(x86)";
4764             string originalProgramFiles32Value = Environment.GetEnvironmentVariable(programFiles32EnvVar);
4765             string extensionsPath32EnvValue = Environment.GetEnvironmentVariable("MSBuildExtensionsPath32");
4766 
4767             try
4768             {
4769                 Environment.SetEnvironmentVariable("MSBuildExtensionsPath32", null);
4770 
4771                 if (String.IsNullOrEmpty(originalProgramFiles32Value))
4772                 {
4773                     // 32 bit box
4774                     originalProgramFiles32Value = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
4775                 }
4776 
4777                 // First try it with whatever value it currently has (it will probably be blank since it's a 32 bit box)
4778                 Project p = new Project(new Engine());
4779                 string msbuildExtensionsPath32Value = (string)p.EvaluatedProperties[ReservedPropertyNames.extensionsPath32];
4780                 Assertion.AssertEquals(originalProgramFiles32Value + @"\MSBuild", msbuildExtensionsPath32Value);
4781 
4782                 // Now try setting it temporarily to some value -- as if we were on a 64 bit machine -- and getting MSBuildExtensionsPath32 again.
4783                 try
4784                 {
4785                     Environment.SetEnvironmentVariable(programFiles32EnvVar, @"c:\Program Files (x86)");
4786                     Project p2 = new Project(new Engine());
4787                     msbuildExtensionsPath32Value = (string)p2.EvaluatedProperties[ReservedPropertyNames.extensionsPath32];
4788                     Assertion.AssertEquals(@"c:\Program Files (x86)\MSBuild", msbuildExtensionsPath32Value);
4789                 }
4790                 finally
4791                 {
4792                     // And restore the old value
4793                     Environment.SetEnvironmentVariable(programFiles32EnvVar, originalProgramFiles32Value);
4794                 }
4795             }
4796             finally
4797             {
4798                 Environment.SetEnvironmentVariable("MSBuildExtensionsPath32", extensionsPath32EnvValue);
4799             }
4800         }
4801 
4802         [Test]
MSBuildExtensionsPath32WithEnvironmentOverride()4803         public void MSBuildExtensionsPath32WithEnvironmentOverride()
4804         {
4805             string originalMSBuildExtensionsPath32Value = Environment.GetEnvironmentVariable(ReservedPropertyNames.extensionsPath32);
4806 
4807             try
4808             {
4809                 // Set an env var called MSBuildExtensionsPath32 to some value, for the purpose
4810                 // of seeing whether our value wins.
4811                 Environment.SetEnvironmentVariable(ReservedPropertyNames.extensionsPath32, @"c:\devdiv\vscore\msbuild");
4812                 Project p = new Project(new Engine());
4813                 string msbuildExtensionsPath32Value = (string)p.EvaluatedProperties[ReservedPropertyNames.extensionsPath32];
4814                 Assertion.AssertEquals(@"c:\devdiv\vscore\msbuild", msbuildExtensionsPath32Value);
4815             }
4816             finally
4817             {
4818                 // And restore the old value
4819                 Environment.SetEnvironmentVariable(ReservedPropertyNames.extensionsPath32, originalMSBuildExtensionsPath32Value);
4820             }
4821         }
4822 
4823         [Test]
MSBuildExtensionsPath32WithGlobalOverride()4824         public void MSBuildExtensionsPath32WithGlobalOverride()
4825         {
4826             Project p = new Project(new Engine());
4827 
4828             // Set a global property called MSBuildExtensionsPath32 to some value, for the purpose
4829             // of seeing whether our value wins.
4830             p.GlobalProperties.SetProperty(ReservedPropertyNames.extensionsPath32, @"c:\devdiv\vscore\msbuild");
4831             string msbuildExtensionsPath32Value = (string)p.EvaluatedProperties[ReservedPropertyNames.extensionsPath32];
4832             Assertion.AssertEquals(@"c:\devdiv\vscore\msbuild", msbuildExtensionsPath32Value);
4833         }
4834 
4835 
4836         /// <summary>
4837         /// Test standard reserved properties
4838         /// </summary>
4839         [Test]
ReservedProjectProperties()4840         public void ReservedProjectProperties()
4841         {
4842             string file = ObjectModelHelpers.CreateTempFileOnDisk(
4843                   @"<Project InitialTargets=`CheckForErrors` DefaultTargets=`Build` xmlns=`msbuildnamespace`/>
4844                 ");
4845             Project project = new Project(new Engine());
4846             project.Load(file);
4847 
4848             Assertion.AssertEquals(Path.GetDirectoryName(file), (string)project.EvaluatedProperties["MSBuildProjectDirectory"]);
4849             Assertion.AssertEquals(Path.GetFileName(file), (string)project.EvaluatedProperties["MSBuildProjectFile"]);
4850             Assertion.AssertEquals(Path.GetExtension(file), (string)project.EvaluatedProperties["MSBuildProjectExtension"]);
4851             Assertion.AssertEquals(file, (string)project.EvaluatedProperties["MSBuildProjectFullPath"]);
4852             Assertion.AssertEquals(Path.GetFileNameWithoutExtension(file), (string)project.EvaluatedProperties["MSBuildProjectName"]);
4853             Assertion.AssertEquals("Build", (string)project.EvaluatedProperties["MSBuildProjectDefaultTargets"]);
4854 
4855             int rootLength = Path.GetPathRoot(file).Length;
4856             int fileLength = Path.GetFileName(file).Length;
4857             string projectDirectoryNoRoot = file.Substring(rootLength, file.Length - fileLength - rootLength - 1 /* no slash */);
4858 
4859             Console.WriteLine("project is: " + file);
4860             Console.WriteLine("expect MSBuildProjectDirectoryNoRoot:" + projectDirectoryNoRoot);
4861             Console.WriteLine("actual MSBuildProjectDirectoryNoRoot:" + (string)project.EvaluatedProperties["MSBuildProjectDirectoryNoRoot"]);
4862             Assertion.AssertEquals(projectDirectoryNoRoot, (string)project.EvaluatedProperties["MSBuildProjectDirectoryNoRoot"]);
4863         }
4864 
4865         /// <summary>
4866         /// Test standard reserved properties
4867         /// </summary>
4868         [Test]
ReservedProjectPropertiesAtRoot()4869         public void ReservedProjectPropertiesAtRoot()
4870         {
4871             string file = Path.Combine(@"c:\", "MSBuildUnitTests_ReservedProjectPropertiesAtRoot.proj");
4872             try
4873             {
4874                 File.WriteAllText(file, @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'/>");
4875                 Project project = new Project(new Engine());
4876                 project.Load(file);
4877 
4878                 Assertion.AssertEquals(Path.GetDirectoryName(file), (string)project.EvaluatedProperties["MSBuildProjectDirectory"]);
4879                 Assertion.AssertEquals(Path.GetFileName(file), (string)project.EvaluatedProperties["MSBuildProjectFile"]);
4880                 Assertion.AssertEquals(Path.GetExtension(file), (string)project.EvaluatedProperties["MSBuildProjectExtension"]);
4881                 Assertion.AssertEquals(file, (string)project.EvaluatedProperties["MSBuildProjectFullPath"]);
4882                 Assertion.AssertEquals(Path.GetFileNameWithoutExtension(file), (string)project.EvaluatedProperties["MSBuildProjectName"]);
4883 
4884                 // Should be empty as there's no directory
4885                 Console.WriteLine("project is: " + file);
4886                 Console.WriteLine("expect MSBuildProjectDirectoryNoRoot: (empty)");
4887                 Console.WriteLine("actual MSBuildProjectDirectoryNoRoot:" + (string)project.EvaluatedProperties["MSBuildProjectDirectoryNoRoot"]);
4888                 Assertion.AssertEquals(String.Empty, (string)project.EvaluatedProperties["MSBuildProjectDirectoryNoRoot"]);
4889             }
4890             finally
4891             {
4892                 if (file != null) File.Delete(file);
4893             }
4894         }
4895 
4896         /// <summary>
4897         /// Test standard reserved properties on UNC
4898         /// </summary>
4899         [Test]
ReservedProjectPropertiesOnUNC()4900         public void ReservedProjectPropertiesOnUNC()
4901         {
4902             string file = ObjectModelHelpers.CreateTempFileOnDisk(
4903                   @"<Project xmlns=`msbuildnamespace`/>
4904                 ");
4905             Project project = new Project(new Engine());
4906 
4907             // Hacky way to get UNC path to file
4908             string uncFile = @"\\" + Environment.MachineName + @"\" + file[0] + "$" + file.Substring(2);
4909 
4910             project.Load(uncFile);
4911 
4912             Assertion.AssertEquals(Path.GetDirectoryName(uncFile), (string)project.EvaluatedProperties["MSBuildProjectDirectory"]);
4913             Assertion.AssertEquals(Path.GetFileName(uncFile), (string)project.EvaluatedProperties["MSBuildProjectFile"]);
4914             Assertion.AssertEquals(Path.GetExtension(uncFile), (string)project.EvaluatedProperties["MSBuildProjectExtension"]);
4915             Assertion.AssertEquals(uncFile, (string)project.EvaluatedProperties["MSBuildProjectFullPath"]);
4916             Assertion.AssertEquals(Path.GetFileNameWithoutExtension(uncFile), (string)project.EvaluatedProperties["MSBuildProjectName"]);
4917 
4918             int fileLength = Path.GetFileName(uncFile).Length;
4919             int rootLength = Path.GetPathRoot(uncFile).Length;
4920             string projectDirectoryNoRoot = uncFile.Substring(rootLength, uncFile.Length - rootLength - fileLength);
4921             projectDirectoryNoRoot = EngineHelpers.EnsureNoLeadingSlash(projectDirectoryNoRoot);
4922             projectDirectoryNoRoot = EngineHelpers.EnsureNoTrailingSlash(projectDirectoryNoRoot);
4923 
4924             Console.WriteLine("project is: " + uncFile);
4925             Console.WriteLine("expect MSBuildProjectDirectoryNoRoot:" + projectDirectoryNoRoot);
4926             Console.WriteLine("actual MSBuildProjectDirectoryNoRoot:" + (string)project.EvaluatedProperties["MSBuildProjectDirectoryNoRoot"]);
4927             Assertion.AssertEquals(projectDirectoryNoRoot, (string)project.EvaluatedProperties["MSBuildProjectDirectoryNoRoot"]);
4928         }
4929 
4930         /// <summary>
4931         /// MSBuildStartupDirectory should point to the directory in which the process hosting the Engine
4932         /// was first started.
4933         /// </summary>
4934         [Test]
MSBuildStartupDirectory()4935         public void MSBuildStartupDirectory()
4936         {
4937             // This test crashes if Engine is not GAC'd, as it typically isn't during development
4938             if (Environment.GetEnvironmentVariable("DO_NOT_RUN_TESTS_REQUIRING_ENGINE_GACD") == "1")
4939             {
4940                 return;
4941             }
4942 
4943             string projectFile = null;
4944             string resultFile = null;
4945             string startDirectory = null;
4946 
4947             string oldCurrentDirectory = Environment.CurrentDirectory;
4948 
4949             try
4950             {
4951                 projectFile = Path.GetTempFileName();
4952                 resultFile = Path.GetTempFileName();
4953                 Random rand = new Random();
4954                 startDirectory = Path.Combine(Path.GetTempPath(), Convert.ToString(rand.NextDouble()));
4955                 Directory.CreateDirectory(startDirectory);
4956 
4957                 Environment.CurrentDirectory = startDirectory;
4958                 Engine e = new Engine();
4959 
4960                 Project p = ObjectModelHelpers.CreateInMemoryProject(e, @"
4961                     <Project ToolsVersion='msbuilddefaulttoolsversion' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
4962                         <Target Name='Build'>
4963                             <WriteLinesToFile File='" + resultFile + @"' Lines='[$(MSBuildStartupDirectory)]'/>
4964                         </Target>
4965                     </Project>", null);
4966 
4967                 bool result = p.Build();
4968 
4969                 Assertion.Assert("ERROR: Check Engine is in the GAC?", File.Exists(resultFile));
4970 
4971                 FileInfo fileInfo = new FileInfo(resultFile);
4972                 Assertion.Assert("ERROR: Check Engine is in the GAC?", fileInfo.Length > 0);
4973 
4974                 string resultContent = File.ReadAllLines(resultFile)[0];
4975                 Console.WriteLine("[$(MSBuildStartupDirectory)] was: " + resultContent);
4976                 Assertion.AssertEquals("[" + startDirectory + "]", resultContent);
4977             }
4978             catch (Exception e)
4979             {
4980                 string stack = e.StackTrace.Replace('\n', ' ').Replace('\t', ' ');
4981                 Assertion.Fail(stack);
4982                 throw;
4983             }
4984             finally
4985             {
4986                 // Sometimes this fails with "being used by another process" - heaven knows why;
4987                 // use a Sleep and a catch to make it more robust.
4988                 System.Threading.Thread.Sleep(3);
4989 
4990                 try
4991                 {
4992                     Environment.CurrentDirectory = oldCurrentDirectory;
4993                     File.Delete(projectFile);
4994                     File.Delete(resultFile);
4995                     Directory.Delete(startDirectory);
4996                 }
4997                 catch (Exception ex)
4998                 {
4999                     Console.WriteLine(ex.ToString());
5000                 }
5001             }
5002         }
5003     }
5004 
5005     [TestFixture]
5006     public class LoadAndSave
5007     {
5008         /// <summary>
5009         /// Just load an MSBuild project by passing in a TextReader, and get back the contents to
5010         /// make sure the project was read in correctly.
5011         /// </summary>
5012         /// <owner>RGoel</owner>
5013         [Test]
LoadFromTextReader()5014         public void LoadFromTextReader()
5015         {
5016             StringReader stringReader = new StringReader(ObjectModelHelpers.CleanupFileContents(@"
5017                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5018                     <PropertyGroup>
5019                         <blah>true</blah>
5020                     </PropertyGroup>
5021                 </Project>
5022                 "));
5023 
5024             Engine engine = new Engine(@"c:\");
5025             Project project = engine.CreateNewProject();
5026             project.Load(stringReader);
5027 
5028             ObjectModelHelpers.CompareProjectContents(project, @"
5029                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5030                     <PropertyGroup>
5031                         <blah>true</blah>
5032                     </PropertyGroup>
5033                 </Project>
5034                 ");
5035         }
5036 
5037         /// <summary>
5038         /// Just load an MSBuild project with invalid XML by passing in a TextReader, and make sure
5039         /// it throws an InvalidProjectFileException.
5040         /// </summary>
5041         /// <owner>RGoel</owner>
5042         [Test]
5043         [ExpectedException(typeof(InvalidProjectFileException))]
LoadFromTextReaderInvalidXml()5044         public void LoadFromTextReaderInvalidXml()
5045         {
5046             StringReader stringReader = new StringReader(ObjectModelHelpers.CleanupFileContents(@"
5047                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5048                 </PROJECT>
5049                 "));
5050 
5051             Engine engine = new Engine(@"c:\");
5052             Project project = engine.CreateNewProject();
5053             project.Load(stringReader);
5054         }
5055 
5056         /// <summary>
5057         /// Just load an MSBuild project with invalid XML by passing in a TextReader, and make sure
5058         /// it throws an InvalidProjectFileException.
5059         /// </summary>
5060         /// <owner>RGoel</owner>
5061         [Test]
5062         [ExpectedException(typeof(InvalidProjectFileException))]
LoadFromTextReaderInvalidProject()5063         public void LoadFromTextReaderInvalidProject()
5064         {
5065             StringReader stringReader = new StringReader(ObjectModelHelpers.CleanupFileContents(@"
5066                 <Project xmlns=`foobar`>
5067                 </Project>
5068                 "));
5069 
5070             Engine engine = new Engine(@"c:\");
5071             Project project = engine.CreateNewProject();
5072             project.Load(stringReader);
5073         }
5074 
5075         /// <summary>
5076         /// Exercises the internal-only feature of being able to load a project from a XML document.
5077         /// </summary>
5078         /// <owner>RGoel</owner>
5079         [Test]
LoadFromXmlDocument()5080         public void LoadFromXmlDocument()
5081         {
5082             XmlDocument xmldoc = new XmlDocument();
5083             xmldoc.LoadXml(ObjectModelHelpers.CleanupFileContents(@"
5084                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5085                     <PropertyGroup>
5086                         <blah>true</blah>
5087                     </PropertyGroup>
5088                 </Project>
5089                 "));
5090 
5091             Engine engine = new Engine(@"c:\");
5092             Project project = engine.CreateNewProject();
5093             project.LoadFromXmlDocument(xmldoc, null, ProjectLoadSettings.None);
5094 
5095             ObjectModelHelpers.CompareProjectContents(project, @"
5096                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5097                     <PropertyGroup>
5098                         <blah>true</blah>
5099                     </PropertyGroup>
5100                 </Project>
5101                 ");
5102         }
5103 
5104         /// <summary>
5105         /// Exercises the internal-only feature of being able to load a project from a XML document,
5106         /// when the project file is invalid.
5107         /// </summary>
5108         /// <owner>RGoel</owner>
5109         [Test]
5110         [ExpectedException(typeof(InvalidProjectFileException))]
LoadFromXmlDocumentInvalidProject()5111         public void LoadFromXmlDocumentInvalidProject()
5112         {
5113             XmlDocument xmldoc = new XmlDocument();
5114             xmldoc.LoadXml(ObjectModelHelpers.CleanupFileContents(@"
5115                 <Project xmlns=`foobar`>
5116                 </Project>
5117                 "));
5118 
5119             Engine engine = new Engine(@"c:\");
5120             Project project = engine.CreateNewProject();
5121             project.LoadFromXmlDocument(xmldoc, null, ProjectLoadSettings.None);
5122         }
5123 
5124         /// <summary>
5125         /// Exercises the internal-only feature of being able to load a project from a XML document,
5126         /// when the project file is invalid.
5127         /// </summary>
5128         /// <owner>RGoel</owner>
5129         [Test]
5130         [ExpectedException(typeof(InvalidProjectFileException))]
LoadFromStringInvalidXml()5131         public void LoadFromStringInvalidXml()
5132         {
5133             string projectContents = ObjectModelHelpers.CleanupFileContents(@"
5134                 <Project xmlns=`foobar`>
5135                 </PROJECT>
5136                 ");
5137 
5138             Engine engine = new Engine(@"c:\");
5139             Project project = engine.CreateNewProject();
5140             project.LoadXml(projectContents);
5141         }
5142 
5143         /// <summary>
5144         /// Tests the 'load project ignoring imports' flag
5145         /// </summary>
5146         [Test]
LoadFromXmlDocumentIgnoringImports()5147         public void LoadFromXmlDocumentIgnoringImports()
5148         {
5149             XmlDocument xmldoc = new XmlDocument();
5150             xmldoc.LoadXml(ObjectModelHelpers.CleanupFileContents(@"
5151                 <Project ToolsVersion=`msbuilddefaulttoolsversion` DefaultTargets=`Build` InitialTargets=`Clean` xmlns=`msbuildnamespace`>
5152                     <Import Project=`Microsoft.Uncommon.targets` />
5153                 </Project>
5154                 "));
5155 
5156             Engine engine = new Engine(@"c:\");
5157             Project project = engine.CreateNewProject();
5158             project.LoadFromXmlDocument(xmldoc, null, ProjectLoadSettings.IgnoreMissingImports);
5159 
5160             Assert.AreEqual(1, project.Imports.Count);
5161         }
5162 
5163         /// <summary>
5164         /// Make sure missing imports throw for the standard load
5165         /// </summary>
5166         [Test]
5167         [ExpectedException(typeof(InvalidProjectFileException))]
LoadFromXmlDocumentMissingImport()5168         public void LoadFromXmlDocumentMissingImport()
5169         {
5170             XmlDocument xmldoc = new XmlDocument();
5171             xmldoc.LoadXml(ObjectModelHelpers.CleanupFileContents(@"
5172                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5173                     <Import Project=`Microsoft.Uncommon.targets` />
5174                 </Project>
5175                 "));
5176 
5177             Engine engine = new Engine(@"c:\");
5178             Project project = engine.CreateNewProject();
5179             project.LoadFromXmlDocument(xmldoc, null, ProjectLoadSettings.None);
5180         }
5181 
5182         [Test]
RemoveMissingImportAndLoadNormally()5183         public void RemoveMissingImportAndLoadNormally()
5184         {
5185             XmlDocument xmldoc = new XmlDocument();
5186             xmldoc.LoadXml(ObjectModelHelpers.CleanupFileContents(@"
5187                 <Project ToolsVersion=`msbuilddefaulttoolsversion` DefaultTargets=`Build` InitialTargets=`Clean` xmlns=`msbuildnamespace`>
5188                     <Import Project=`Microsoft.Uncommon.targets` />
5189                 </Project>
5190                 "));
5191 
5192             // Load the project ignoring missing imports
5193             Engine engine = new Engine(@"c:\");
5194             Project project = engine.CreateNewProject();
5195             project.LoadFromXmlDocument(xmldoc, null, ProjectLoadSettings.IgnoreMissingImports);
5196             Assert.AreEqual(1, project.Imports.Count);
5197 
5198             IEnumerator enumerator = project.Imports.GetEnumerator();
5199             enumerator.MoveNext();
5200             Import import = (Import) enumerator.Current;
5201 
5202             // Remove the import
5203             project.Imports.RemoveImport(import);
5204             Assert.AreEqual(0, project.Imports.Count);
5205 
5206             // Save the modified project
5207             StringWriter writer = new StringWriter();
5208             project.Save(writer);
5209 
5210             // Load the modified project into a new project object
5211             xmldoc = new XmlDocument();
5212             xmldoc.LoadXml(writer.ToString());
5213 
5214             Project projectWithNoImport = engine.CreateNewProject();
5215             project.LoadFromXmlDocument(xmldoc, null, ProjectLoadSettings.None);
5216             Assert.AreEqual(0, project.Imports.Count);
5217         }
5218     }
5219 
5220     [TestFixture]
5221     public class Build
5222     {
5223         /// <summary>
5224         /// Targets that fail should not produce target outputs (the list that comes back from project.build())
5225         /// (Note that they may change the items and properties that are visible in the project after a build is done, though.)
5226         /// All this is Whidbey behavior.
5227         /// </summary>
5228         [Test]
FailingTargetsDoNotHaveOutputs()5229         public void FailingTargetsDoNotHaveOutputs()
5230         {
5231             Project p = ObjectModelHelpers.CreateInMemoryProject(@"
5232 
5233                     <Project xmlns=`msbuildnamespace`>
5234                         <Target Name=`Build` Outputs=`$(p);@(i);$(q)`>
5235                             <CreateProperty Value=`v`>
5236                                 <Output TaskParameter=`Value` PropertyName=`p` />
5237                             </CreateProperty>
5238                             <CreateItem Value=`a`>
5239                                 <Output TaskParameter=`Value` ItemName=`i` />
5240                             </CreateItem>
5241                             <ItemGroup>
5242                                 <i Include='b'/>
5243                             </ItemGroup>
5244                             <PropertyGroup>
5245                                 <q>u</q>
5246                             </PropertyGroup>
5247                             <Error Text='error occurred'/>
5248                         </Target>
5249                     </Project>
5250 
5251                 ");
5252 
5253             Hashtable outputs = new Hashtable();
5254             bool result = p.Build(new string[] { "Build" }, outputs);
5255 
5256             Assertion.AssertEquals(false, result);
5257             Assertion.AssertEquals(0, outputs.Count);
5258         }
5259 
5260         /// <summary>
5261         /// Checks to make sure that passing in the DoNotResetPreviouslyBuiltTargets flag
5262         /// works as expected.
5263         /// </summary>
5264         /// <owner>JomoF</owner>
5265         [Test]
CheckDoNotResetPreviouslyBuiltTargets()5266         public void CheckDoNotResetPreviouslyBuiltTargets()
5267         {
5268             // Create a temporary file.
5269             string tempFile = Path.GetTempFileName();
5270 
5271             // This project sets $(FileExists) to true if the file exists
5272             Project p = ObjectModelHelpers.CreateInMemoryProject(String.Format(@"
5273 
5274                     <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5275                         <Target Name=`Build` Condition=`Exists('{0}')`>
5276                             <CreateProperty Value=`true`>
5277                                 <Output TaskParameter=`Value` PropertyName=`FileExists` />
5278                             </CreateProperty>
5279                         </Target>
5280                     </Project>
5281 
5282                 ", tempFile));
5283 
5284             // Build first time with 'DoNotResetPreviouslyBuiltTargets' passed in.
5285             p.Build(new string[]{"Build"}, null, BuildSettings.DoNotResetPreviouslyBuiltTargets);
5286 
5287             // At this point, the property $(FileExists) should be 'true'
5288             Assertion.AssertEquals("true", p.GetEvaluatedProperty("FileExists"));
5289 
5290             // Delete the file
5291             File.Delete(tempFile);
5292 
5293             // Build again. The result should still be 'true' because the target won't be reevaluated.
5294             p.Build(new string[]{"Build"}, null, BuildSettings.DoNotResetPreviouslyBuiltTargets);
5295             Assertion.AssertEquals("true", p.GetEvaluatedProperty("FileExists"));
5296 
5297             // Build a third time, but now don't pass the DoNotResetPreviouslyBuiltTargets flag. The target should
5298             // be reevaluated and the result should be empty.
5299             p.Build(new string[]{"Build"}, null, BuildSettings.None);
5300             Assertion.AssertEquals(null, p.GetEvaluatedProperty("FileExists"));
5301         }
5302 
5303         /// <summary>
5304         /// Makes sure that after somebody requests a build of some targets in a project, the project
5305         /// should always be in the "not reset" state.
5306         /// </summary>
5307         /// <owner>RGoel</owner>
5308         [Test]
AllTargetsGetRebuiltAfterModificationToProject()5309         public void AllTargetsGetRebuiltAfterModificationToProject()
5310         {
5311             Project p = ObjectModelHelpers.CreateInMemoryProject(@"
5312 
5313                     <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5314                         <Target Name=`Build`/>
5315                     </Project>
5316 
5317                 ");
5318 
5319             // Set some random property in the project to force it to be dirty.
5320             p.SetProperty("Foo", "Bar", null);
5321 
5322             Assertion.Assert("Project should be dirty.", p.IsDirty);
5323 
5324             // Build the target.
5325             p.Build("Build");
5326 
5327             Assertion.Assert("Project should not be in the 'reset' state after a build", !p.IsReset);
5328         }
5329     }
5330 
5331     [TestFixture]
5332     public class InitialTargets
5333     {
5334         /// <summary>
5335         /// Simple case.  Just make sure that the target specified in InitialTargets gets run.
5336         /// </summary>
5337         [Test]
RunInitialTargetsInMainProject()5338         public void RunInitialTargetsInMainProject()
5339         {
5340             MockLogger myLogger = new MockLogger();
5341             Project p = ObjectModelHelpers.CreateInMemoryProject(@"
5342 
5343                     <Project InitialTargets=`CheckForErrors` DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5344                         <Target Name=`Build`>
5345                             <Message Text=`BuildTargetExecuted`/>
5346                         </Target>
5347                         <Target Name=`CheckForErrors`>
5348                             <Message Text=`CheckForErrorsTargetExecuted`/>
5349                         </Target>
5350                     </Project>
5351 
5352                 ", myLogger);
5353 
5354             // Build the target.
5355             p.Build(null, null);
5356 
5357             Assertion.Assert("Build target should have been run.", myLogger.FullLog.Contains("BuildTargetExecuted"));
5358             Assertion.Assert("CheckForErrors target should have been run.", myLogger.FullLog.Contains("CheckForErrorsTargetExecuted"));
5359         }
5360 
5361         /// <summary>
5362         /// Simple case.  Just make sure that the target specified in InitialTargets gets run.
5363         /// </summary>
5364         [Test]
RunInitialTargetsInMainProjectWithMissingTargets()5365         public void RunInitialTargetsInMainProjectWithMissingTargets()
5366         {
5367             MockLogger myLogger = new MockLogger();
5368             Project p = ObjectModelHelpers.CreateInMemoryProject(new Engine(),
5369                   @"<Project InitialTargets=`CheckForErrors` DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5370                         <Target Name=`Build`>
5371                             <Message Text=`BuildTargetExecuted`/>
5372                         </Target>
5373                         <Target Name=`CheckForErrors`>
5374                             <Message Text=`CheckForErrorsTargetExecuted`/>
5375                         </Target>
5376                         <Import Project=`baaaa`/>
5377                     </Project>
5378 
5379                 ", myLogger, null, ProjectLoadSettings.IgnoreMissingImports);
5380 
5381             // Build the target.
5382             p.Build(null, null);
5383 
5384             Assertion.Assert("Build target should have been run.", myLogger.FullLog.Contains("BuildTargetExecuted"));
5385             Assertion.Assert("CheckForErrors target should have been run.", myLogger.FullLog.Contains("CheckForErrorsTargetExecuted"));
5386         }
5387 
5388         /// <summary>
5389         /// We have an "InitialTargets" attribute in the main project as well as two imported projects.  Make sure we
5390         /// run those initial targets in the correct expected order.
5391         /// </summary>
5392         /// <owner>RGoel</owner>
5393         [Test]
RunInitialTargetsInMainAndImportedProjects()5394         public void RunInitialTargetsInMainAndImportedProjects()
5395         {
5396             Environment.SetEnvironmentVariable("MyNewChecks", "NewChecks");
5397 
5398             string importedProject1 = ObjectModelHelpers.CreateTempFileOnDisk(@"
5399 
5400                     <Project InitialTargets=`CheckForBadProperties` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5401 
5402                         <Target Name=`CheckForBadItems`>
5403                             <CreateItem Include=`CheckForBadItems_Executed`><Output ItemName=`TargetOrder` TaskParameter=`Include`/></CreateItem>
5404                         </Target>
5405 
5406                         <Target Name=`CheckForBadProperties` DependsOnTargets=`CheckForBadPlatforms; CheckForBadItems`>
5407                             <CreateItem Include=`CheckForBadProperties_Executed`><Output ItemName=`TargetOrder` TaskParameter=`Include`/></CreateItem>
5408                         </Target>
5409 
5410                     </Project>
5411 
5412                 ");
5413 
5414             string importedProject2 = ObjectModelHelpers.CreateTempFileOnDisk(@"
5415 
5416                     <Project InitialTargets=`CheckForBadConfigurations` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5417 
5418                         <Target Name=`CheckForBadPlatforms`>
5419                             <CreateItem Include=`CheckForBadPlatforms_Executed`><Output ItemName=`TargetOrder` TaskParameter=`Include`/></CreateItem>
5420                         </Target>
5421 
5422                         <Target Name=`CheckForBadConfigurations` DependsOnTargets=`CheckForBadPlatforms`>
5423                             <CreateItem Include=`CheckForBadConfigurations_Executed`><Output ItemName=`TargetOrder` TaskParameter=`Include`/></CreateItem>
5424                         </Target>
5425 
5426                     </Project>
5427 
5428                 ");
5429 
5430             try
5431             {
5432                 Project p = ObjectModelHelpers.CreateInMemoryProject(String.Format(@"
5433 
5434                         <Project InitialTargets=`CheckForBadUser` DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5435 
5436                             <Import Project=`{0}`/>
5437 
5438                             <Target Name=`Build`>
5439                                 <CreateItem Include=`Build_Executed`><Output ItemName=`TargetOrder` TaskParameter=`Include`/></CreateItem>
5440                             </Target>
5441 
5442                             <Import Project=`{1}`/>
5443 
5444                             <Target Name=`CheckForBadUser`>
5445                                 <CreateItem Include=`CheckForBadUser_Executed`><Output ItemName=`TargetOrder` TaskParameter=`Include`/></CreateItem>
5446                             </Target>
5447 
5448                             <Target Name=`CheckForBadItems`>
5449                                 <CreateItem Include=`CheckForBadItems_Overridden_Executed`><Output ItemName=`TargetOrder` TaskParameter=`Include`/></CreateItem>
5450                             </Target>
5451 
5452                             <Target Name=`NewChecks`>
5453                                 <CreateItem Include=`NewChecks_Executed`><Output ItemName=`TargetOrder` TaskParameter=`Include`/></CreateItem>
5454                             </Target>
5455 
5456                         </Project>
5457 
5458                     ", importedProject1, importedProject2));
5459 
5460                 Assertion.AssertEquals("Check all InitialTargets", "CheckForBadUser; CheckForBadProperties; CheckForBadConfigurations",
5461                     p.InitialTargets);
5462 
5463                 // Build the default target.
5464                 p.Build(null, null);
5465 
5466                 DumpBuildItemGroup(p.GetEvaluatedItemsByName("TargetOrder"));
5467 
5468                 // The following method will ensure that the targets were executed in the correct order.
5469                 EngineHelpers.AssertItemsMatch(@"
5470                     CheckForBadUser_Executed
5471                     CheckForBadPlatforms_Executed
5472                     CheckForBadItems_Overridden_Executed
5473                     CheckForBadProperties_Executed
5474                     CheckForBadConfigurations_Executed
5475                     Build_Executed
5476                     ",
5477                     p.GetEvaluatedItemsByName("TargetOrder"));
5478 
5479                 // Change the InitialTargets on the main project to be "NewChecks", but do it via an environment variable.
5480                 p.InitialTargets = "$(MyNewChecks)";
5481 
5482                 Assertion.AssertEquals("Check all InitialTargets", "NewChecks; CheckForBadProperties; CheckForBadConfigurations",
5483                     p.InitialTargets);
5484 
5485                 // Build the default target.
5486                 p.Build(null, null);
5487 
5488                 DumpBuildItemGroup(p.GetEvaluatedItemsByName("TargetOrder"));
5489 
5490                 // The following method will ensure that the targets were executed in the correct order.
5491                 EngineHelpers.AssertItemsMatch(@"
5492                     NewChecks_Executed
5493                     CheckForBadPlatforms_Executed
5494                     CheckForBadItems_Overridden_Executed
5495                     CheckForBadProperties_Executed
5496                     CheckForBadConfigurations_Executed
5497                     Build_Executed
5498                     ",
5499                     p.GetEvaluatedItemsByName("TargetOrder"));
5500             }
5501             finally
5502             {
5503                 File.Delete(importedProject1);
5504                 File.Delete(importedProject2);
5505             }
5506         }
5507 
DumpBuildItemGroup(BuildItemGroup itemGroup)5508         private static void DumpBuildItemGroup(BuildItemGroup itemGroup)
5509         {
5510             Console.WriteLine(itemGroup.Count);
5511             foreach (BuildItem item in itemGroup)
5512             {
5513                 Console.WriteLine(item.Name + " : " + item.FinalItemSpec);
5514             }
5515         }
5516 
5517         /// <summary>
5518         /// Makes sure that after somebody requests a build of some targets in a project, the project
5519         /// should always be in the "not reset" state.
5520         /// </summary>
5521         /// <owner>RGoel</owner>
5522         [Test]
ModifyInitialTargetsInMainProject()5523         public void ModifyInitialTargetsInMainProject()
5524         {
5525             MockLogger myLogger = new MockLogger();
5526             Project p = ObjectModelHelpers.CreateInMemoryProject(@"
5527 
5528                     <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5529                         <Target Name=`Build`>
5530                             <Message Text=`BuildTargetExecuted`/>
5531                         </Target>
5532                         <Target Name=`CheckForErrors`>
5533                             <Message Text=`CheckForErrorsTargetExecuted`/>
5534                         </Target>
5535                     </Project>
5536 
5537                 ", myLogger);
5538 
5539             Assertion.AssertEquals("InitialTargets should be empty to start with.", String.Empty, p.InitialTargets);
5540             p.InitialTargets = "CheckForErrors";
5541             Assertion.AssertEquals("InitialTargets should be set.", "CheckForErrors", p.InitialTargets);
5542 
5543             ObjectModelHelpers.CompareProjectContents(p, @"
5544 
5545                     <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace` InitialTargets=`CheckForErrors`>
5546                         <Target Name=`Build`>
5547                             <Message Text=`BuildTargetExecuted`/>
5548                         </Target>
5549                         <Target Name=`CheckForErrors`>
5550                             <Message Text=`CheckForErrorsTargetExecuted`/>
5551                         </Target>
5552                     </Project>
5553 
5554                 ");
5555 
5556             // Build the default target.
5557             p.Build(null, null);
5558 
5559             Assertion.Assert("Build target should have been run.", myLogger.FullLog.Contains("BuildTargetExecuted"));
5560             Assertion.Assert("CheckForErrors target should have been run.", myLogger.FullLog.Contains("CheckForErrorsTargetExecuted"));
5561         }
5562 
5563     }
5564 
5565     [TestFixture]
5566     public class Miscellaneous
5567     {
5568         /// <summary>
5569         /// Regression test for bug VSWhidbey 403429
5570         /// </summary>
5571         /// <owner>danmose</owner>
5572         [Test]
DocTypeInProject()5573         public void DocTypeInProject()
5574         {
5575             string original = @"
5576                 <!DOCTYPE content PUBLIC `foo` `` []>
5577                 <?xml-stylesheet type=`test/xsl` href=`file:||foo||`?>
5578                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5579                     <Target Name=`t`/>
5580                 </Project>
5581                 ";
5582 
5583             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
5584             // Does not throw
5585         }
5586 
5587         /// <summary>
5588         /// Test the various encoding and writing methods.
5589         /// </summary>
5590         /// <owner>RGoel</owner>
5591         [Test]
LoadAndSaveWithDifferentEncodings()5592         public void LoadAndSaveWithDifferentEncodings()
5593         {
5594             string file = Path.GetTempFileName();
5595 
5596             string original = @"
5597                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5598 
5599                     <ItemGroup Condition=`'$(x)'=='y'`>
5600                         <ReferencePath Include=`c:\foobar` />
5601                     </ItemGroup>
5602 
5603                     <ItemGroup Condition=`'$(x)'=='z'`>
5604                         <ReferencePath Include=`c:\foobar` />
5605                     </ItemGroup>
5606 
5607                 </Project>
5608                 ";
5609 
5610             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
5611 
5612             Engine engine = new Engine();
5613             engine.BinPath = @"c:\";
5614 
5615             // Save and load using each encoding scheme. The ultimate result should be identity.
5616             File.Delete(file);
5617             project.Save(file, Encoding.Default);
5618             project = new Project(engine);
5619             project.Load(file);
5620             string header = string.Format(@"<?xml version=`1.0` encoding=`{0}`?>", Encoding.Default.WebName);
5621             ObjectModelHelpers.CompareProjectContents(project, header + original);
5622 
5623             File.Delete(file);
5624             project.Save(file, Encoding.UTF8);
5625             project.ParentEngine.UnloadProject(project);
5626             project = new Project(engine);
5627             project.Load(file);
5628             ObjectModelHelpers.CompareProjectContents(project, @"<?xml version=`1.0` encoding=`utf-8`?>" + original);
5629 
5630             File.Delete(file);
5631             project.Save(file, Encoding.Unicode);
5632             project.ParentEngine.UnloadProject(project);
5633             project = new Project(engine);
5634             project.Load(file);
5635             ObjectModelHelpers.CompareProjectContents(project, @"<?xml version=`1.0` encoding=`utf-16`?>" + original);
5636 
5637             // Save with current encoding.
5638             File.Delete(file);
5639             project.Save(file);
5640             project.ParentEngine.UnloadProject(project);
5641             project = new Project(engine);
5642             project.Load(file);
5643             ObjectModelHelpers.CompareProjectContents(project, @"<?xml version=`1.0` encoding=`utf-16`?>" + original);
5644             File.Delete(file);
5645 
5646             // Save to writer.
5647             File.Delete(file);
5648             using (StreamWriter writer = new StreamWriter(file))
5649             {
5650                 project.Save(writer);
5651             }
5652             project.ParentEngine.UnloadProject(project);
5653             project = new Project(engine);
5654             project.Load(file);
5655             ObjectModelHelpers.CompareProjectContents(project, @"<?xml version=`1.0` encoding=`utf-8`?>" + original);
5656             File.Delete(file);
5657 
5658             // Verify the final encoding state is Utf-8.
5659             Assertion.AssertEquals(Encoding.UTF8, project.Encoding);
5660         }
5661 
5662         /// <summary>
5663         /// Set and Get ProjectExtensions
5664         /// </summary>
5665         /// <owner>danmose</owner>
5666         [Test]
SetGetProjectExtensions()5667         public void SetGetProjectExtensions()
5668         {
5669             string original = @"
5670                 <Project ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5671                     <Target Name=`t`/>
5672                 </Project>
5673                 ";
5674 
5675             Project project = ObjectModelHelpers.CreateInMemoryProject(original);
5676             project.SetProjectExtensions("myID", "<foo />");
5677             project.SetProjectExtensions("myOtherID", "<bar />");
5678             Assertion.AssertEquals("<foo />", project.GetProjectExtensions("myID"));
5679             Assertion.AssertEquals("", project.GetProjectExtensions("myNonexistent"));
5680         }
5681 
5682         /// <summary>
5683         /// There is a certain error that the MSBuild engine fires when you try to do a build on
5684         /// a project that has had its targets disabled because of security.  However, the project
5685         /// system doesn't want to show this error to the user because it's not actionable for
5686         /// the user.  So it looks for code MSB4112 to throw away this error.  Here we're just
5687         /// to catch the case where somebody accidentally changes the error code for this error,
5688         /// without realizing that somebody else has a dependency on it.
5689         /// </summary>
5690         /// <owner>RGoel</owner>
5691         [Test]
VerifySecurityErrorHasCodeMSB4112()5692         public void VerifySecurityErrorHasCodeMSB4112()
5693         {
5694             ResourceManager resourceManager = new ResourceManager("Microsoft.Build.Engine.Resources.Strings", typeof(Project).Assembly);
5695             string securityMessage = resourceManager.GetString("SecurityProjectBuildDisabled", CultureInfo.CurrentUICulture);
5696 
5697             Assertion.Assert(
5698                 "Security message about disabled targets need to have code MSB4112, because code in the VS Core project system depends on this.  See DesignTimeBuildFeedback.cpp.",
5699                 securityMessage.Contains("MSB4112")
5700             );
5701         }
5702 
5703         /// <summary>
5704         /// Verify that warning & error tags at target level work correctly
5705         /// </summary>
5706         /// <owner>LukaszG</owner>
5707         [Test]
WarningErrorTagsTargetLevel()5708         public void WarningErrorTagsTargetLevel()
5709         {
5710             MockLogger logger = new MockLogger();
5711             Project project = ObjectModelHelpers.CreateInMemoryProject(@"
5712                 <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5713                     <Target Name=`Build`>
5714                         <Warning Text=`This is a scary warning message.` Code=`MSB9999` HelpKeyword=`MSBuild.keyword`/>
5715                         <Error Text=`A horrible error has occurred. Be very afraid.` Code=`MSB1111` HelpKeyword=`MSBuild.otherkeyword`/>
5716                     </Target>
5717                 </Project>
5718                 ", logger);
5719 
5720             project.Build(null, null);
5721 
5722             Assertion.AssertEquals(1, logger.Warnings.Count);
5723             BuildWarningEventArgs warning = logger.Warnings[0];
5724 
5725             Assertion.AssertEquals("This is a scary warning message.", warning.Message);
5726             Assertion.AssertEquals("MSB9999", warning.Code);
5727             Assertion.AssertEquals("MSBuild.keyword", warning.HelpKeyword);
5728 
5729             Assertion.AssertEquals(1, logger.Errors.Count);
5730             BuildErrorEventArgs error = logger.Errors[0];
5731 
5732             Assertion.AssertEquals("A horrible error has occurred. Be very afraid.", error.Message);
5733             Assertion.AssertEquals("MSB1111", error.Code);
5734             Assertion.AssertEquals("MSBuild.otherkeyword", error.HelpKeyword);
5735         }
5736 
5737         [Test]
TestLoadProjectDifferentGP()5738         public void TestLoadProjectDifferentGP()
5739         {
5740             MockLogger logger = new MockLogger();
5741             string path = ObjectModelHelpers.CreateTempFileOnDisk(@"
5742                 <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5743                     <Target Name=`Build`>
5744                         <Warning Text=`This is a scary warning message.` Code=`MSB9999` HelpKeyword=`MSBuild.keyword`/>
5745                         <Error Text=`A horrible error has occurred. Be very afraid.` Code=`MSB1111` HelpKeyword=`MSBuild.otherkeyword`/>
5746                     </Target>
5747                 </Project>
5748                 ");
5749 
5750             Engine engine = new Engine();
5751 
5752             Project project = engine.CreateNewProject();
5753 
5754             project.Load(path);
5755 
5756             project.GlobalProperties.SetProperty("a", "b");
5757             engine.BuildProjectFile(path);
5758         }
5759 
5760         [Test]
ICollectionMethodsOnItemPropertyGroupCollection()5761         public void ICollectionMethodsOnItemPropertyGroupCollection()
5762         {
5763             Engine engine = new Engine(@"C:\");
5764             Project project = new Project(engine);
5765             BuildPropertyGroup pg1 = project.AddNewPropertyGroup(true);
5766             BuildPropertyGroup pg2 = project.AddNewPropertyGroup(true);
5767             BuildItemGroup ig1 = project.AddNewItemGroup();
5768             BuildItemGroup ig2 = project.AddNewItemGroup();
5769 
5770             BuildPropertyGroup[] pgarray = new BuildPropertyGroup[2];
5771             BuildItemGroup[] igarray = new BuildItemGroup[2];
5772 
5773             project.PropertyGroups.CopyTo(pgarray, 0);
5774             project.ItemGroups.CopyTo(igarray, 0);
5775 
5776             Assertion.Assert(pgarray[0] == pg1 || pgarray[1] == pg1);
5777             Assertion.Assert(pgarray[0] == pg2 || pgarray[1] == pg2);
5778 
5779             Assertion.Assert(igarray[0] == ig1 || igarray[1] == ig1);
5780             Assertion.Assert(igarray[0] == ig2 || igarray[1] == ig2);
5781         }
5782 
5783         [Test]
RegressVsWhidbey579075()5784         public void RegressVsWhidbey579075()
5785         {
5786             MockProjectStartedLogger logger = new MockProjectStartedLogger();
5787             Project project = ObjectModelHelpers.CreateInMemoryProject(@"
5788                 <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
5789                     <Target Name=`Build`>
5790                     </Target>
5791                 </Project>
5792                 ", logger);
5793 
5794             // Set a property and force project evaluation
5795             project.SetProperty("Configuration", "Release");
5796             BuildPropertyGroup evaluatedProperties = project.EvaluatedProperties;
5797 
5798             // Set a different value of the property and build without forced reevaluation,
5799             // check if the new value is passed to the logger
5800             project.SetProperty("Configuration", "Debug");
5801             project.Build();
5802             Assertion.AssertEquals("Debug", logger.ProjectStartedProperties["Configuration"]);
5803         }
5804     }
5805 
5806     [TestFixture]
5807     public class ToolsVersion
5808     {
5809         [Test]
VersionBasedMSBuildBinPathDefault()5810         public void VersionBasedMSBuildBinPathDefault()
5811         {
5812             Engine e = new Engine("www.msbuild.org");
5813             Project project = ObjectModelHelpers.CreateInMemoryProject(e, @"
5814                 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace`>
5815                     <Target Name=`Build`/>
5816                 </Project>", null);
5817 
5818             Assertion.AssertEquals("Nonexistent ToolsVersion should evaluate to the default version",
5819                 Constants.defaultToolsVersion, project.ToolsVersion);
5820 
5821             Assertion.AssertEquals("Nonexistent ToolsVersion should mean ToolsVersionAttribute is the default version",
5822                 Constants.defaultToolsVersion, project.DefaultToolsVersion);
5823 
5824             Assertion.AssertEquals("BinPath is the MSBuildBinPath for the default version",
5825                 "www.msbuild.org", project.EvaluatedProperties[ReservedPropertyNames.binPath].FinalValue);
5826 
5827             Assertion.AssertEquals("BinPath is the MSBuildToolsPath for the default version",
5828                 "www.msbuild.org", project.EvaluatedProperties[ReservedPropertyNames.toolsPath].FinalValue);
5829         }
5830 
5831         [Test]
VersionBasedMSBuildBinPathExplicit()5832         public void VersionBasedMSBuildBinPathExplicit()
5833         {
5834             Engine e = new Engine("www.msbuild.org");
5835             e.AddToolset(new Toolset("myValidToolsVersion", "myValidToolsVersion's path"));
5836 
5837             Project project = ObjectModelHelpers.CreateInMemoryProject(e, @"
5838                 <Project ToolsVersion=`myValidToolsVersion` DefaultTargets=`Build` xmlns=`msbuildnamespace`>
5839                     <Target Name=`Build`/>
5840                 </Project>", null);
5841 
5842             Assertion.AssertEquals("ToolsVersion should have been picked up from the project attribute",
5843                 "myValidToolsVersion", project.ToolsVersion);
5844 
5845             Assertion.AssertEquals("ToolsVersionAttribute should have been picked up from the project attribute",
5846                 "myValidToolsVersion", project.DefaultToolsVersion);
5847 
5848             Assertion.AssertEquals("BinPath is the MSBuildBinPath for the default version",
5849                 "myValidToolsVersion's path", project.EvaluatedProperties[ReservedPropertyNames.binPath].FinalValue);
5850 
5851             Assertion.AssertEquals("BinPath is the MSBuildToolsPath for the default version",
5852                 "myValidToolsVersion's path", project.EvaluatedProperties[ReservedPropertyNames.toolsPath].FinalValue);
5853         }
5854 
5855         [Test]
ChangingToolsVersion()5856         public void ChangingToolsVersion()
5857         {
5858             Engine e = new Engine("www.msbuild.org");
5859             e.AddToolset(new Toolset("myValidToolsVersion", "myValidToolsVersion's path"));
5860 
5861             Project project = ObjectModelHelpers.CreateInMemoryProject(e, @"
5862                 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace`>
5863                     <Target Name=`Build`/>
5864                 </Project>", null);
5865 
5866             Assertion.AssertEquals("Nonexistent ToolsVersion should evaluate to the default version",
5867                 Constants.defaultToolsVersion, project.ToolsVersion);
5868 
5869             Assertion.AssertEquals("BinPath is the MSBuildBinPath for the default version",
5870                 "www.msbuild.org", project.EvaluatedProperties[ReservedPropertyNames.binPath].FinalValue);
5871 
5872             Assertion.AssertEquals("BinPath is the MSBuildToolsPath for the default version",
5873                 "www.msbuild.org", project.EvaluatedProperties[ReservedPropertyNames.toolsPath].FinalValue);
5874 
5875             project.DefaultToolsVersion = "myValidToolsVersion";
5876 
5877             Assertion.AssertEquals("ToolsVersion should have been changed by the project attribute (because it wasn't overridden)",
5878                 "myValidToolsVersion", project.ToolsVersion);
5879 
5880             Assertion.AssertEquals("ToolsVersionAttribute should have been picked up from the project attribute",
5881                 "myValidToolsVersion", project.DefaultToolsVersion);
5882 
5883             Assertion.AssertEquals("OverridingToolsVersion should be false",
5884                 false, project.OverridingToolsVersion);
5885 
5886             Assertion.AssertEquals("BinPath is the MSBuildBinPath for the default version",
5887                 "myValidToolsVersion's path", project.EvaluatedProperties[ReservedPropertyNames.binPath].FinalValue);
5888 
5889             Assertion.AssertEquals("BinPath is the MSBuildToolsPath for the default version",
5890                 "myValidToolsVersion's path", project.EvaluatedProperties[ReservedPropertyNames.toolsPath].FinalValue);
5891         }
5892 
5893         /// <summary>
5894         /// It's okay to change DefaultToolsVersion to some apparently bogus value -- the project can be persisted
5895         /// that way, and maybe later it'll correspond to some known toolset. If the effective ToolsVersion was being
5896         /// gotten from the attribute, that'll be affected too; and thus might be bogus.
5897         /// </summary>
5898         [Test]
ChangingToolsVersionAttributeToUnrecognizedValue()5899         public void ChangingToolsVersionAttributeToUnrecognizedValue()
5900         {
5901             Engine e = new Engine("www.msbuild.org");
5902             e.AddToolset(new Toolset("myValidToolsVersion", "myValidToolsVersion's path"));
5903 
5904             Project project = ObjectModelHelpers.CreateInMemoryProject(e, @"
5905                 <Project ToolsVersion=`myValidToolsVersion` DefaultTargets=`Build` xmlns=`msbuildnamespace`>
5906                     <Target Name=`Build`/>
5907                 </Project>", null);
5908 
5909             Assertion.AssertEquals("We should have toolsVersion equal to myValidToolsVersion",
5910                 "myValidToolsVersion", project.DefaultToolsVersion);
5911 
5912             // Build should succeed at this point
5913             Assertion.Assert(project.Build());
5914 
5915             project.DefaultToolsVersion = "UnknownToolsVersion";
5916 
5917             // When an unknown toolsversion is used, MSBuild treats it as TV4.0.
5918             Assertion.AssertEquals("Because a bogus ToolsVersion was set, the ToolsVersion gets treated as v4.0", "4.0", project.ToolsVersion);
5919 
5920             Assertion.AssertEquals("ToolsVersionAttribute has the new value", "4.0", project.DefaultToolsVersion);
5921 
5922             // It's a valid ToolsVersion, so the build should succeed
5923             Assertion.Assert(project.Build());
5924         }
5925 
5926         /// <summary>
5927         /// If project has not loaded from XML, it should have the default tools version
5928         /// </summary>
5929         [Test]
EmptyProjectCreatedViaOMHasDefaultToolsVersion()5930         public void EmptyProjectCreatedViaOMHasDefaultToolsVersion()
5931         {
5932             Engine e = new Engine("www.msbuild.org");
5933             Project p = new Project(e);
5934             // Don't load any project here
5935             Assertion.AssertEquals(Constants.defaultToolsVersion, p.ToolsVersion);
5936         }
5937 
5938         [Test]
ToolsVersionAttributeConstructor()5939         public void ToolsVersionAttributeConstructor()
5940         {
5941             Engine e = new Engine("www.msbuild.org");
5942             e.AddToolset(new Toolset("myValidToolsVersion", "myValidToolsVersion's path"));
5943             e.AddToolset(new Toolset("myOtherToolsVersion", "myOtherToolsVersion's path"));
5944 
5945             Project project = ObjectModelHelpers.CreateInMemoryProject(e, @"
5946                 <Project ToolsVersion=`myOtherToolsVersion` DefaultTargets=`Build` xmlns=`msbuildnamespace`>
5947                     <Target Name=`Build`/>
5948                 </Project>", null, "myValidToolsVersion");
5949 
5950             Assertion.AssertEquals("We should have Override equal to true",
5951                 true, project.OverridingToolsVersion);
5952             Assertion.AssertEquals("We should have ToolsVersion equal to myValidToolsVersion",
5953                 "myValidToolsVersion", project.ToolsVersion);
5954             Assertion.AssertEquals("We should have ToolsVersionAttribute equal to myOtherToolsVersion",
5955                 "myOtherToolsVersion", project.DefaultToolsVersion);
5956         }
5957 
5958         // Regular case of setting DefaultToolsVersion
5959         [Test]
SettingToolsVersionAttribute()5960         public void SettingToolsVersionAttribute()
5961         {
5962             Engine e = new Engine("www.msbuild.org");
5963             e.AddToolset(new Toolset("myValidToolsVersion", "myValidToolsVersion's path"));
5964 
5965             Project project = ObjectModelHelpers.CreateInMemoryProject(e, @"
5966                 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace`>
5967                     <Target Name=`Build`/>
5968                 </Project>", null);
5969 
5970             project.DefaultToolsVersion = "myValidToolsVersion";
5971 
5972             Assertion.AssertEquals("We should have Override equal to false",
5973                 false, project.OverridingToolsVersion);
5974             Assertion.AssertEquals("We should have ToolsVersion equal to myValidToolsVersion",
5975                 "myValidToolsVersion", project.ToolsVersion);
5976             Assertion.AssertEquals("We should have ToolsVersionAttribute equal to myValidToolsVersion",
5977                 "myValidToolsVersion", project.DefaultToolsVersion);
5978         }
5979 
5980         // Setting DefaultToolsVersion should not modify ToolsVersion if it was an override value
5981         [Test]
SettingToolsVersionAttributeAfterToolsVersionSetInProjectConstructor()5982         public void SettingToolsVersionAttributeAfterToolsVersionSetInProjectConstructor()
5983         {
5984             Engine e = new Engine("www.msbuild.org");
5985             e.AddToolset(new Toolset("myValidToolsVersion", "myValidToolsVersion's path"));
5986             e.AddToolset(new Toolset("myOtherToolsVersion", "myOtherToolsVersion's path"));
5987 
5988             Project project = ObjectModelHelpers.CreateInMemoryProject(e, @"
5989                 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace`>
5990                     <Target Name=`Build`/>
5991                 </Project>", null, "myValidToolsVersion");
5992 
5993             project.DefaultToolsVersion = "myOtherToolsVersion";
5994 
5995             Assertion.AssertEquals("We should have Override equal to true",
5996                 true, project.OverridingToolsVersion);
5997             Assertion.AssertEquals("We should have ToolsVersion equal to myOtherToolsVersion",
5998                 "myValidToolsVersion", project.ToolsVersion);
5999             Assertion.AssertEquals("We should have ToolsVersionAttribute equal to myOtherToolsVersion",
6000                 "myOtherToolsVersion", project.DefaultToolsVersion);
6001         }
6002 
6003         [Test]
MSBuildToolsVersionProperty()6004         public void MSBuildToolsVersionProperty()
6005         {
6006             Engine e = new Engine("www.msbuild.org");
6007             e.AddToolset(new Toolset("myValidToolsVersion", "myValidToolsVersion's path"));
6008 
6009             MockLogger logger = new MockLogger();
6010 
6011             Project project = ObjectModelHelpers.CreateInMemoryProject(e, ObjectModelHelpers.CleanupFileContents(@"
6012                 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace`>
6013                     <UsingTask TaskName='Message' AssemblyName='Microsoft.Build.Tasks.Core, Version=msbuildassemblyversion, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'/>
6014                     <Target Name=`Build`>
6015                         <Message Text=`##$(MSBuildToolsVersion)##`/>
6016                     </Target>
6017                 </Project>"), logger);
6018 
6019             project.Build();
6020 
6021             logger.AssertLogContains("##2.0##");
6022         }
6023 
6024         [Test]
MSBuildToolsVersionProperty2()6025         public void MSBuildToolsVersionProperty2()
6026         {
6027             Engine e = new Engine("www.msbuild.org");
6028             e.AddToolset(new Toolset("myValidToolsVersion", "myValidToolsVersion's path"));
6029 
6030             MockLogger logger = new MockLogger();
6031 
6032             Project project = ObjectModelHelpers.CreateInMemoryProject(e, ObjectModelHelpers.CleanupFileContents(@"
6033                 <Project DefaultTargets=`Build` xmlns=`msbuildnamespace`>
6034                     <UsingTask TaskName='Message' AssemblyName='Microsoft.Build.Tasks.Core, Version=msbuildassemblyversion, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'/>
6035                     <Target Name=`Build`>
6036                         <Message Text=`##$(MSBuildToolsVersion)##`/>
6037                     </Target>
6038                 </Project>"), logger);
6039             project.DefaultToolsVersion = "myValidToolsVersion";
6040 
6041             project.Build();
6042 
6043             logger.AssertLogContains("##myValidToolsVersion##");
6044         }
6045 
6046         [Test]
SetEffectiveToolsVersionAttribute()6047         public void SetEffectiveToolsVersionAttribute()
6048         {
6049             Engine e = new Engine("www.msbuild.org");
6050             e.AddToolset(new Toolset("myValidToolsVersion", "myValidToolsVersion's path"));
6051             e.AddToolset(new Toolset("myOtherToolsVersion", "myOtherToolsVersion's path"));
6052 
6053             MockLogger logger = new MockLogger();
6054 
6055             Project project = ObjectModelHelpers.CreateInMemoryProject(e, ObjectModelHelpers.CleanupFileContents(@"
6056                 <Project ToolsVersion=`myValidToolsVersion` DefaultTargets=`Build` xmlns=`msbuildnamespace`>
6057                     <UsingTask TaskName='Message' AssemblyName='Microsoft.Build.Tasks.Core, Version=msbuildassemblyversion, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'/>
6058                     <PropertyGroup>
6059                        <TheToolsVersion>$(MSBuildToolsVersion)</TheToolsVersion>
6060                     </PropertyGroup>
6061                     <Target Name=`Build`>
6062                        <Message Text=`### $(TheToolsVersion) ###` />
6063                     </Target>
6064                 </Project>"), logger, "myValidToolsVersion");
6065 
6066             project.ToolsVersion = "myOtherToolsVersion";
6067 
6068             project.Build();
6069 
6070             Assertion.Assert("We should be using a ToolsVersion override", project.OverridingToolsVersion);
6071             Assertion.AssertEquals("We should have ToolsVersion equal to myOtherToolsVersion",
6072                 "myOtherToolsVersion", project.ToolsVersion);
6073             Assertion.AssertEquals("We should have ToolsVersionAttribute equal to myValidToolsVersion",
6074                 "myValidToolsVersion", project.DefaultToolsVersion);
6075             logger.AssertLogContains("### myOtherToolsVersion ###");
6076         }
6077 
6078 
6079         [Test]
PropertiesFromToolsetAppliedToProjectWhenToolsVersionSet()6080         public void PropertiesFromToolsetAppliedToProjectWhenToolsVersionSet()
6081         {
6082             Engine e = new Engine("www.msbuild.org");
6083             BuildPropertyGroup properties1 = new BuildPropertyGroup();
6084             properties1.SetProperty("foo1", "bar1");
6085             properties1.SetProperty("foo2", "bar2");
6086             BuildPropertyGroup properties2 = new BuildPropertyGroup();
6087             properties2.SetProperty("foo3", "bar3");
6088             properties2.SetProperty("foo1", "bar4");
6089             e.AddToolset(new Toolset("myValidToolsVersion", "myValidToolsVersion's path", properties1));
6090             e.AddToolset(new Toolset("myOtherToolsVersion", "myOtherToolsVersion's path", properties2));
6091 
6092             MockLogger logger = new MockLogger();
6093 
6094             Project project = ObjectModelHelpers.CreateInMemoryProject(e, @"
6095                 <Project ToolsVersion=`myValidToolsVersion` xmlns=`msbuildnamespace`/>", logger);
6096 
6097             Assertion.AssertEquals("bar1", project.EvaluatedProperties["foo1"].Value);
6098             Assertion.AssertEquals("bar2", project.EvaluatedProperties["foo2"].Value);
6099             Assertion.AssertEquals(null, project.EvaluatedProperties["foo3"]);
6100 
6101             // Now update tools version: should grab properties from the new toolset
6102             project.ToolsVersion = "myOtherToolsVersion";
6103 
6104             Assertion.AssertEquals("bar4", project.EvaluatedProperties["foo1"].Value);  // Updated
6105             Assertion.AssertEquals(null, project.EvaluatedProperties["foo2"]);      // Reset
6106             Assertion.AssertEquals("bar3", project.EvaluatedProperties["foo3"].Value);  // New
6107         }
6108 
6109         [Test]
PropertiesFromToolsetAppliedToProjectWhenToolsVersionOverridden()6110         public void PropertiesFromToolsetAppliedToProjectWhenToolsVersionOverridden()
6111         {
6112             Engine e = new Engine("www.msbuild.org");
6113             BuildPropertyGroup properties1 = new BuildPropertyGroup();
6114             properties1.SetProperty("foo1", "bar1");
6115             properties1.SetProperty("foo2", "bar2");
6116             BuildPropertyGroup properties2 = new BuildPropertyGroup();
6117             properties2.SetProperty("foo3", "bar3");
6118             properties2.SetProperty("foo1", "bar4");
6119             e.AddToolset(new Toolset("myValidToolsVersion", "myValidToolsVersion's path", properties1));
6120             e.AddToolset(new Toolset("myOtherToolsVersion", "myOtherToolsVersion's path", properties2));
6121 
6122             MockLogger logger = new MockLogger();
6123 
6124             Project project = ObjectModelHelpers.CreateInMemoryProject(e, @"
6125                 <Project ToolsVersion=`myValidToolsVersion` xmlns=`msbuildnamespace`/>", logger, "myOtherToolsVersion");
6126 
6127             Assertion.AssertEquals("bar4", project.EvaluatedProperties["foo1"].Value);  // Updated
6128             Assertion.AssertEquals(null, project.EvaluatedProperties["foo2"]);      // Reset
6129             Assertion.AssertEquals("bar3", project.EvaluatedProperties["foo3"].Value);  // New
6130         }
6131     }
6132 }
6133 
6134