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