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