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 //----------------------------------------------------------------------- 5 // <copyright file="TargetCollection_Tests.cs" company="Microsoft"> 6 // Copyright (c) Microsoft Corporation. All rights reserved. 7 // </copyright> 8 // <summary>Tests for all Public TargetCollection objects</summary> 9 //----------------------------------------------------------------------- 10 11 using System; 12 using System.IO; 13 using System.Xml; 14 using System.Reflection; 15 using System.Collections.Generic; 16 using NUnit.Framework; 17 18 using Microsoft.Build.BuildEngine; 19 using Microsoft.Build.Framework; 20 using Microsoft.Build.UnitTests; 21 22 namespace Microsoft.Build.UnitTests.OM.OrcasCompatibility 23 { 24 /// <summary> 25 /// Tests for TargetCollection 26 /// </summary> 27 [TestFixture] 28 public class TargetCollection_Tests 29 { 30 #region Common Helpers 31 /// <summary> 32 /// Basic project content with several targets, where depends on targets is used 33 /// </summary> 34 private const string ProjectContentSeveralTargets = @" 35 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 36 <Target Name='t1' DependsOnTargets='t2' Inputs='in' Outputs='out'/> 37 <Target Name='t2' DependsOnTargets='t3' Condition=""'true' == 'true'""> 38 <Message Text='t2.task' /> 39 </Target> 40 <Target Name='t3' DependsOnTargets='t4'> 41 <Message Text='t3.task' /> 42 </Target> 43 <Target Name='t4'> 44 <Message Text='t4.task' /> 45 </Target> 46 <Target Name='t5'> 47 <Message Text='t5.task' /> 48 </Target> 49 </Project> 50 "; 51 52 /// <summary> 53 /// Basic project content with no targets - an emtpy project 54 /// </summary> 55 private const string ProjectContentNoTargets = @" 56 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 57 </Project> 58 "; 59 60 /// <summary> 61 /// Engine that is used through out test class 62 /// </summary> 63 private Engine engine; 64 65 /// <summary> 66 /// Project that is used through out test class 67 /// </summary> 68 private Project project; 69 70 /// <summary> 71 /// Creates the engine and parent object. 72 /// </summary> 73 [SetUp()] Initialize()74 public void Initialize() 75 { 76 ObjectModelHelpers.DeleteTempProjectDirectory(); 77 78 engine = new Engine(); 79 project = new Project(engine); 80 } 81 82 /// <summary> 83 /// Unloads projects 84 /// </summary> 85 [TearDown()] Cleanup()86 public void Cleanup() 87 { 88 engine.UnloadProject(project); 89 engine.UnloadAllProjects(); 90 91 ObjectModelHelpers.DeleteTempProjectDirectory(); 92 } 93 #endregion 94 95 #region Count Tests 96 /// <summary> 97 /// Tests TargetCollection.Count with many targets 98 /// </summary> 99 [Test] CountMany()100 public void CountMany() 101 { 102 project.LoadXml(ProjectContentSeveralTargets); 103 TargetCollection targets = project.Targets; 104 105 Assertion.AssertEquals(5, targets.Count); 106 } 107 108 /// <summary> 109 /// Tests TargetCollection.Count with some targets that are imported 110 /// </summary> 111 [Test] CountWithImportedTargets()112 public void CountWithImportedTargets() 113 { 114 string importProjectContents = @" 115 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 116 <Target Name='t2'> 117 <Message Text='imported.t2.task' /> 118 </Target> 119 <Target Name='t3' /> 120 </Project> 121 "; 122 123 string projectContents = @" 124 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 125 <Target Name='t1'> 126 <Message Text='parent.t1.task' /> 127 </Target> 128 <Import Project='import.proj' /> 129 </Project> 130 "; 131 132 Project p = GetProjectThatImportsAnotherProject(importProjectContents, projectContents); 133 TargetCollection targets = p.Targets; 134 135 Assertion.AssertEquals(3, targets.Count); 136 } 137 138 /// <summary> 139 /// Tests TargetCollection.Count when the imported project and parent project both contain 140 /// a target of the same name. 141 /// </summary> 142 [Test] CountWhenImportedAndParentBothContainSameTarget()143 public void CountWhenImportedAndParentBothContainSameTarget() 144 { 145 string importProjectContents = @" 146 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 147 <Target Name='t1'> 148 <Message Text='imported.t2.task' /> 149 </Target> 150 </Project> 151 "; 152 153 string parentProjectContents = @" 154 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 155 <Target Name='t1'> 156 <Message Text='parent.t1.task' /> 157 </Target> 158 <Import Project='import.proj' /> 159 </Project> 160 "; 161 162 Project p = GetProjectThatImportsAnotherProject(importProjectContents, parentProjectContents); 163 TargetCollection targets = p.Targets; 164 165 Assertion.AssertEquals(1, targets.Count); 166 } 167 168 /// <summary> 169 /// Tests TargetCollection.Count when no targets exist 170 /// </summary> 171 [Test] CountWithNoTargets()172 public void CountWithNoTargets() 173 { 174 project.LoadXml(ProjectContentNoTargets); 175 TargetCollection targets = project.Targets; 176 177 Assertion.AssertEquals(0, targets.Count); 178 } 179 180 /// <summary> 181 /// Tests TargetCollection.Count after adding a new target 182 /// </summary> 183 [Test] CountAfterAddingNewTarget()184 public void CountAfterAddingNewTarget() 185 { 186 project.LoadXml(ProjectContentSeveralTargets); 187 project.Targets.AddNewTarget("t6"); 188 189 TargetCollection targets = project.Targets; 190 Assertion.AssertEquals(6, targets.Count); 191 } 192 193 /// <summary> 194 /// Tests TargetCollection.Count after removing a target 195 /// </summary> 196 [Test] CountAfterRemovingTarget()197 public void CountAfterRemovingTarget() 198 { 199 project.LoadXml(ProjectContentSeveralTargets); 200 Target t = GetSpecificTargetFromProject(project, "t5"); 201 project.Targets.RemoveTarget(t); 202 203 TargetCollection targets = project.Targets; 204 Assertion.AssertEquals(4, targets.Count); 205 } 206 #endregion 207 208 #region Exists Tests 209 /// <summary> 210 /// Tests TargetCollection.Exists when target exists 211 /// </summary> 212 [Test] ExistsWhenTargetExists()213 public void ExistsWhenTargetExists() 214 { 215 project.LoadXml(ProjectContentSeveralTargets); 216 TargetCollection targets = project.Targets; 217 218 Assertion.AssertEquals(true, targets.Exists("t2")); 219 } 220 221 /// <summary> 222 /// Tests TargetCollection.Exists when target doesn't exist 223 /// </summary> 224 [Test] ExistsWhenTargetDoesNotExist()225 public void ExistsWhenTargetDoesNotExist() 226 { 227 project.LoadXml(ProjectContentSeveralTargets); 228 TargetCollection targets = project.Targets; 229 230 Assertion.AssertEquals(false, targets.Exists("tNot")); 231 } 232 233 /// <summary> 234 /// Tests TargetCollection.Exists of an imported target 235 /// </summary> 236 [Test] ExistsOfImportedTarget()237 public void ExistsOfImportedTarget() 238 { 239 Project p = GetProjectThatImportsAnotherProject(null, null); 240 TargetCollection targets = p.Targets; 241 242 Assertion.AssertEquals(true, targets.Exists("t4")); 243 } 244 245 /// <summary> 246 /// Tests TargetCollection.Exists of a target that comes from an import as well as parent project 247 /// </summary> 248 [Test] ExistsWhenImportedTargetAndParentTargetHaveSameName()249 public void ExistsWhenImportedTargetAndParentTargetHaveSameName() 250 { 251 string importProjectContents = @" 252 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 253 <Target Name='t1'> 254 <Message Text='imported.t2.task' /> 255 </Target> 256 </Project> 257 "; 258 259 string parentProjectContents = @" 260 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 261 <Target Name='t1'> 262 <Message Text='parent.t1.task' /> 263 </Target> 264 <Target Name='t2' /> 265 <Import Project='import.proj' /> 266 </Project> 267 "; 268 269 Project p = GetProjectThatImportsAnotherProject(importProjectContents, parentProjectContents); 270 TargetCollection targets = p.Targets; 271 272 Assertion.AssertEquals(true, targets.Exists("t1")); 273 } 274 #endregion 275 276 #region AddNewTarget Tests 277 /// <summary> 278 /// Tests TargetCollection.AddNewTarget by adding a new target 279 /// </summary> 280 [Test] AddNewTargetSimple()281 public void AddNewTargetSimple() 282 { 283 project.LoadXml(ProjectContentSeveralTargets); 284 TargetCollection targets = project.Targets; 285 targets.AddNewTarget("tNew"); 286 287 Assertion.AssertEquals(true, targets.Exists("tNew")); 288 Assertion.AssertEquals(6, targets.Count); 289 } 290 291 /// <summary> 292 /// Tests TargetCollection.AddNewTarget by adding a new target of the same name 293 /// </summary> 294 [Test] AddNewTargetWhenTargetOfSameNameAlreadyExists()295 public void AddNewTargetWhenTargetOfSameNameAlreadyExists() 296 { 297 project.LoadXml(ProjectContentSeveralTargets); 298 TargetCollection targets = project.Targets; 299 targets.AddNewTarget("t1"); 300 301 Assertion.AssertEquals(true, targets.Exists("t1")); 302 Assertion.AssertEquals(5, targets.Count); 303 } 304 305 /// <summary> 306 /// Tests TargetCollection.AddNewTarget by adding a new target with an String.Empty name 307 /// </summary> 308 [Test] 309 [ExpectedException(typeof(InvalidProjectFileException))] AddNewTargetEmptyStringName()310 public void AddNewTargetEmptyStringName() 311 { 312 project.LoadXml(ProjectContentSeveralTargets); 313 TargetCollection targets = project.Targets; 314 targets.AddNewTarget(String.Empty); 315 } 316 317 /// <summary> 318 /// Tests TargetCollection.AddNewTarget by adding a new target with a null name 319 /// </summary> 320 [Test] 321 [ExpectedException(typeof(InvalidProjectFileException))] AddNewTargetNullName()322 public void AddNewTargetNullName() 323 { 324 project.LoadXml(ProjectContentSeveralTargets); 325 TargetCollection targets = project.Targets; 326 targets.AddNewTarget(null); 327 } 328 329 /// <summary> 330 /// Tests TargetCollection.AddNewTarget by adding a new target with special characters in the name 331 /// </summary> 332 [Test] 333 [ExpectedException(typeof(InvalidProjectFileException))] AddNewTargetSpecialCharacterName()334 public void AddNewTargetSpecialCharacterName() 335 { 336 project.LoadXml(ProjectContentSeveralTargets); 337 TargetCollection targets = project.Targets; 338 targets.AddNewTarget("%24%40%3b%5c%25"); 339 } 340 341 /// <summary> 342 /// Tests TargetCollection.AddNewTarget by adding a new target to a project with no other targets 343 /// </summary> 344 [Test] AddNewTargetWhenNoOtherTargetsExist()345 public void AddNewTargetWhenNoOtherTargetsExist() 346 { 347 project.LoadXml(ProjectContentNoTargets); 348 TargetCollection targets = project.Targets; 349 targets.AddNewTarget("t"); 350 351 Assertion.AssertEquals(true, targets.Exists("t")); 352 Assertion.AssertEquals(1, targets.Count); 353 } 354 #endregion 355 356 #region RemoveTarget Tests 357 /// <summary> 358 /// Tests TargetCollection.RemoveTarget by removing an existing target 359 /// </summary> 360 [Test] RemoveTargetOfExistingTarget()361 public void RemoveTargetOfExistingTarget() 362 { 363 project.LoadXml(ProjectContentSeveralTargets); 364 TargetCollection targets = project.Targets; 365 targets.RemoveTarget(GetSpecificTargetFromProject(project, "t1")); 366 367 Assertion.AssertEquals(false, targets.Exists("t1")); 368 } 369 370 /// <summary> 371 /// Tests TargetCollection.RemoveTarget passing in null 372 /// </summary> 373 [Test] 374 [ExpectedException(typeof(ArgumentNullException))] RemoveTargetNull()375 public void RemoveTargetNull() 376 { 377 project.LoadXml(ProjectContentSeveralTargets); 378 TargetCollection targets = project.Targets; 379 targets.RemoveTarget(null); 380 } 381 382 /// <summary> 383 /// Tests TargetCollection.RemoveTarget of an imported target 384 /// </summary> 385 [Test] 386 [ExpectedException(typeof(InvalidOperationException))] RemoveTargetFromImport()387 public void RemoveTargetFromImport() 388 { 389 Project p = GetProjectThatImportsAnotherProject(null, null); 390 TargetCollection targets = p.Targets; 391 targets.RemoveTarget(GetSpecificTargetFromProject(p, "t2")); 392 } 393 #endregion 394 395 #region CopyTo Tests 396 /// <summary> 397 /// Tests TargetCollection.CopyTo basic case 398 /// </summary> 399 [Test] CopyToSimple()400 public void CopyToSimple() 401 { 402 project.LoadXml(ProjectContentSeveralTargets); 403 TargetCollection targets = project.Targets; 404 405 object[] array = new object[targets.Count]; 406 targets.CopyTo(array, 0); 407 408 List<string> listOfTargets = new List<string>(); 409 foreach (Target t in array) 410 { 411 listOfTargets.Add(t.Name); 412 } 413 414 // This originates in a hashtable, whose ordering is undefined 415 // and indeed changes in CLR4 416 listOfTargets.Sort(); 417 418 Assertion.AssertEquals(targets["t1"].Name, listOfTargets[0]); 419 Assertion.AssertEquals(targets["t2"].Name, listOfTargets[1]); 420 Assertion.AssertEquals(targets["t3"].Name, listOfTargets[2]); 421 Assertion.AssertEquals(targets["t4"].Name, listOfTargets[3]); 422 Assertion.AssertEquals(targets["t5"].Name, listOfTargets[4]); 423 } 424 425 /// <summary> 426 /// Tests TargetCollection.CopyTo passing in a null 427 /// </summary> 428 [Test] 429 [ExpectedException(typeof(ArgumentNullException))] CopyToNull()430 public void CopyToNull() 431 { 432 project.LoadXml(ProjectContentSeveralTargets); 433 TargetCollection targets = project.Targets; 434 435 targets.CopyTo(null, 0); 436 } 437 438 /// <summary> 439 /// Tests TargetCollection.CopyTo when you attempt CopyTo into an Array that's not long enough 440 /// </summary> 441 [Test] 442 [ExpectedException(typeof(ArgumentException))] CopyToArrayThatsNotLargeEnough()443 public void CopyToArrayThatsNotLargeEnough() 444 { 445 project.LoadXml(ProjectContentSeveralTargets); 446 TargetCollection targets = project.Targets; 447 448 object[] array = new object[2]; 449 targets.CopyTo(array, 0); 450 } 451 #endregion 452 453 #region IsSynchronized Tests 454 /// <summary> 455 /// Tests TargetCollection.IsSynchronized for the default case 456 /// </summary> 457 [Test] IsSynchronizedDefault()458 public void IsSynchronizedDefault() 459 { 460 project.LoadXml(ProjectContentSeveralTargets); 461 TargetCollection targets = project.Targets; 462 463 Assertion.AssertEquals(false, targets.IsSynchronized); 464 } 465 #endregion 466 467 #region TargetThis Tests 468 /// <summary> 469 /// Tests TargetCollection["targetname"].property where no imports exist 470 /// </summary> 471 [Test] TargetThisNoImports()472 public void TargetThisNoImports() 473 { 474 project.LoadXml(ProjectContentSeveralTargets); 475 TargetCollection targets = project.Targets; 476 477 Assertion.AssertEquals("in", targets["t1"].Inputs); 478 Assertion.AssertEquals("out", targets["t1"].Outputs); 479 Assertion.AssertEquals(false, targets["t2"].IsImported); 480 Assertion.AssertEquals("'true' == 'true'", targets["t2"].Condition); 481 Assertion.AssertEquals("t3", targets["t2"].DependsOnTargets); 482 Assertion.AssertEquals("t2", targets["t2"].Name); 483 } 484 485 /// <summary> 486 /// Tests TargetCollection["targetname"].property where imports exist 487 /// </summary> 488 [Test] TargetThisWithImports()489 public void TargetThisWithImports() 490 { 491 Project p = GetProjectThatImportsAnotherProject(null, null); 492 TargetCollection targets = p.Targets; 493 494 Assertion.AssertEquals("in", targets["t3"].Inputs); 495 Assertion.AssertEquals("out", targets["t3"].Outputs); 496 Assertion.AssertEquals(true, targets["t3"].IsImported); 497 Assertion.AssertEquals("'true' == 'true'", targets["t3"].Condition); 498 Assertion.AssertEquals("t2", targets["t3"].DependsOnTargets); 499 Assertion.AssertEquals("t3", targets["t3"].Name); 500 } 501 #endregion 502 503 #region Helpers 504 /// <summary> 505 /// Gets a specified Target from a Project 506 /// </summary> 507 /// <param name="p">Project</param> 508 /// <param name="nameOfTarget">Target name of the Target you want</param> 509 /// <returns>Target requested. null if specific target isn't found</returns> GetSpecificTargetFromProject(Project p, string nameOfTarget)510 private Target GetSpecificTargetFromProject(Project p, string nameOfTarget) 511 { 512 foreach (Target t in p.Targets) 513 { 514 if (String.Equals(t.Name, nameOfTarget, StringComparison.OrdinalIgnoreCase)) 515 { 516 return t; 517 } 518 } 519 520 return null; 521 } 522 523 /// <summary> 524 /// Gets a Project that imports another Project 525 /// </summary> 526 /// <param name="importProjectContents">Project Contents of the imported Project, to get default content, pass in an empty string</param> 527 /// <param name="parentProjectContents">Project Contents of the Parent Project, to get default content, pass in an empty string</param> 528 /// <returns>Project</returns> GetProjectThatImportsAnotherProject(string importProjectContents, string parentProjectContents)529 private Project GetProjectThatImportsAnotherProject(string importProjectContents, string parentProjectContents) 530 { 531 if (String.IsNullOrEmpty(importProjectContents)) 532 { 533 importProjectContents = @" 534 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 535 <Target Name='t2' /> 536 <Target Name='t3' DependsOnTargets='t2' Inputs='in' Outputs='out' Condition=""'true' == 'true'""/> 537 <Target Name='t4' /> 538 </Project> 539 "; 540 } 541 542 if (String.IsNullOrEmpty(parentProjectContents)) 543 { 544 parentProjectContents = @" 545 <Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> 546 <Target Name='t1'> 547 <Message Text='parent.t1.task' /> 548 </Target> 549 <Import Project='import.proj' /> 550 </Project> 551 "; 552 } 553 554 ObjectModelHelpers.CreateFileInTempProjectDirectory("import.proj", importProjectContents); 555 ObjectModelHelpers.CreateFileInTempProjectDirectory("main.proj", parentProjectContents); 556 return ObjectModelHelpers.LoadProjectFileInTempProjectDirectory("main.proj", null); 557 } 558 #endregion 559 } 560 } 561