Merge pull request #3591 from directhex/mono_libdir_fallback
[mono.git] / mcs / class / Microsoft.Build.Tasks / Test / Microsoft.Build.Tasks / CopyTest.cs
1 //
2 // CopyTest.cs
3 //  
4 // Author:
5 //   Ankit Jain (jankit@novell.com)
6 //
7 // Copyright 2009 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28 using System;
29 using System.IO;
30 using Microsoft.Build.BuildEngine;
31 using NUnit.Framework;
32 using System.Text;
33 using System.Threading;
34 using Microsoft.Build.Tasks;
35 using Microsoft.Build.Framework;
36 using Microsoft.Build.Utilities;
37
38 namespace MonoTests.Microsoft.Build.Tasks {
39
40         [TestFixture]
41         public class CopyTest {
42                 string source_path, target_path;
43
44                 [SetUp]
45                 public void CreateDir ()
46                 {
47                         source_path = Path.Combine (Path.Combine ("Test", "resources"), "Copy");
48                         Directory.CreateDirectory (source_path);
49                         target_path = Path.Combine (Path.Combine ("Test", "resources"), "Target");
50                         Directory.CreateDirectory (target_path);
51                 }
52
53                 [TearDown]
54                 public void RemoveDirectories ()
55                 {
56                         Directory.Delete (source_path, true);
57                         Directory.Delete (target_path, true);
58                 }
59
60                 [Test]
61                 public void TestCopy_MissingSourceFile ()
62                 {
63                         Copy copy = new Copy ();
64                         copy.BuildEngine = new TestEngine ();
65                         copy.SourceFiles = new ITaskItem [1];
66                         copy.SourceFiles [0] = new TaskItem ("SourceDoesNotExist");
67                         copy.DestinationFiles = new ITaskItem [1];
68                         copy.DestinationFiles [0] = new TaskItem ("DestDoesNotExist");
69                         Assert.IsFalse (copy.Execute ());
70                 }
71
72                 [Test]
73                 public void TestCopy1 ()
74                 {
75                         Engine engine;
76                         Project project;
77                         string file_path = Path.Combine (source_path, "copy.txt");
78                         string target_file = Path.Combine (target_path, "copy.txt");
79
80                         using (File.CreateText (file_path)) { }
81
82                         string documentString = @"
83                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
84                                         <PropertyGroup><DestFile>" + target_file + @"</DestFile></PropertyGroup>
85                                         <ItemGroup>
86                                                 <SFiles Include='" + file_path + @"'><Md>1</Md></SFiles>
87                                                 <DFiles Include='$(DestFile)'><Mde>2</Mde></DFiles>
88                                         </ItemGroup>
89                                         <Target Name='1'>
90                                                 <Copy SourceFiles='@(SFiles)' DestinationFiles='@(DFiles)' SkipUnchangedFiles='true' >
91                                                         <Output TaskParameter='CopiedFiles' ItemName='I0'/>
92                                                         <Output TaskParameter='DestinationFiles' ItemName='I1'/>
93                                                 </Copy>
94                                                 <Message Text=""I0 : @(I0), I1: @(I1)""/>
95                                         </Target>
96                                 </Project>
97                         ";
98
99                         engine = new Engine (Consts.BinPath);
100                         project = engine.CreateNewProject ();
101
102                         TestMessageLogger testLogger = new TestMessageLogger ();
103                         engine.RegisterLogger (testLogger);
104
105                         project.LoadXml (documentString);
106
107                         if (!project.Build ("1")) {
108                                 var sb = new StringBuilder ();
109                                 testLogger.DumpMessages (sb);
110                                 Assert.Fail ("Build failed " + sb.ToString ());
111                         }
112                         Assert.IsTrue (File.Exists (target_file), "A2");
113
114                         BuildItemGroup big = project.GetEvaluatedItemsByName ("I0");
115                         Assert.AreEqual (1, big.Count, "A3");
116                         BuildItem bi = big [0];
117                         Assert.AreEqual (target_file, bi.FinalItemSpec, "A4");
118                         Assert.AreEqual ("1", bi.GetMetadata ("Md"), "A4");
119                         Assert.AreEqual ("2", bi.GetMetadata ("Mde"), "A5");
120
121                         big = project.GetEvaluatedItemsByName ("I1");
122                         Assert.AreEqual (1, big.Count, "A10");
123                         bi = big [0];
124                         Assert.AreEqual (target_file, bi.FinalItemSpec, "A11");
125                         Assert.AreEqual ("1", bi.GetMetadata ("Md"), "A12");
126                         Assert.AreEqual ("2", bi.GetMetadata ("Mde"), "A13");
127
128                         // build again, this time files won't get copied because
129                         // of SkipUnchangedFiles=true
130                         if (!project.Build ("1")) {
131                                 testLogger.DumpMessages ();
132                                 Assert.Fail ("Build failed #2");
133                         }
134                         Assert.IsTrue (File.Exists (target_file), "A20");
135
136                         big = project.GetEvaluatedItemsByName ("I0");
137                         Assert.AreEqual (1, big.Count, "A21");
138                         bi = big [0];
139                         Assert.AreEqual (target_file, bi.FinalItemSpec, "A22");
140                         Assert.AreEqual ("1", bi.GetMetadata ("Md"), "A23");
141                         Assert.AreEqual ("2", bi.GetMetadata ("Mde"), "A24");
142
143                         big = project.GetEvaluatedItemsByName ("I1");
144                         Assert.AreEqual (1, big.Count, "A25");
145                         bi = big [0];
146                         Assert.AreEqual (target_file, bi.FinalItemSpec, "A26");
147                         Assert.AreEqual ("1", bi.GetMetadata ("Md"), "A27");
148                         Assert.AreEqual ("2", bi.GetMetadata ("Mde"), "A28");
149                 }
150
151                 [Test]
152                 public void TestCopy2 ()
153                 {
154                         Engine engine;
155                         Project project;
156                         string [] file_paths = new string [] {
157                                 Path.Combine (source_path, "copy1.txt"),
158                                 Path.Combine (source_path, "copy2.txt")
159                         };
160
161                         using (File.CreateText (file_paths[0])) { }
162                         using (File.CreateText (file_paths[1])) { }
163
164                         string documentString = @"
165                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
166                                         <PropertyGroup><TargetPath>" + target_path + @"</TargetPath></PropertyGroup>
167                                         <ItemGroup>
168                                                 <SFiles Include='" + file_paths [0] + @"'><Md>1</Md></SFiles>
169                                                 <SFiles Include='" + file_paths [1] + @"'><Md>2</Md></SFiles>
170                                         </ItemGroup>
171                                         <Target Name='1'>
172                                                 <Copy SourceFiles='@(SFiles)' DestinationFolder='$(TargetPath)' SkipUnchangedFiles='true' >
173                                                         <Output TaskParameter='CopiedFiles' ItemName='I0'/>
174                                                         <Output TaskParameter='DestinationFiles' ItemName='I1'/>
175                                                 </Copy>
176                                         </Target>
177                                 </Project>
178                         ";
179                         engine = new Engine (Consts.BinPath);
180                         project = engine.CreateNewProject ();
181
182                         TestMessageLogger testLogger = new TestMessageLogger ();
183                         engine.RegisterLogger (testLogger);
184
185                         project.LoadXml (documentString);
186
187                         if (!project.Build ("1")) {
188                                 testLogger.DumpMessages ();
189                                 Assert.Fail ("Build failed");
190                         }
191
192                         CheckCopyBuildItems (project, file_paths, target_path, "A1");
193
194                         // build again, this time files won't get copied because
195                         // of SkipUnchangedFiles=true
196                         if (!project.Build ("1")) {
197                                 testLogger.DumpMessages ();
198                                 Assert.Fail ("Build failed #2");
199                         }
200                         CheckCopyBuildItems (project, file_paths, target_path, "A2");
201                 }
202
203                 [Test]
204                 public void TestCopy_EmptySources () {
205                         Engine engine;
206                         Project project;
207
208                         string documentString = @"
209                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
210                                         <Target Name='1'>
211                                                 <Copy SourceFiles='@(NonExistantSourceFiles)' DestinationFolder='$(TargetPath)' SkipUnchangedFiles='true' >
212                                                         <Output TaskParameter='CopiedFiles' ItemName='I0'/>
213                                                         <Output TaskParameter='DestinationFiles' ItemName='I1'/>
214                                                 </Copy>
215                                         </Target>
216                                 </Project>
217                         ";
218                         engine = new Engine (Consts.BinPath);
219                         project = engine.CreateNewProject ();
220
221                         TestMessageLogger testLogger = new TestMessageLogger ();
222                         engine.RegisterLogger (testLogger);
223
224                         project.LoadXml (documentString);
225
226                         
227                         if (!project.Build ("1")) {
228                                 testLogger.DumpMessages ();
229                                 Assert.Fail ("Build failed");
230                         }
231                 }
232
233                 [Test]
234                 public void TestCopy_EmptyDestFolder () {
235                         Engine engine;
236                         Project project;
237
238                         string documentString = @"
239                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
240                                         <ItemGroup>
241                                                 <SFiles Include='foo.txt'><Md>1</Md></SFiles>
242                                         </ItemGroup>
243                                         <Target Name='1'>
244                                                 <Copy SourceFiles='@(SFiles)' DestinationFolder='@(NonExistant)' DestinationFiles='@(NonExistant)' SkipUnchangedFiles='true' >
245                                                         <Output TaskParameter='CopiedFiles' ItemName='I0'/>
246                                                         <Output TaskParameter='DestinationFiles' ItemName='I1'/>
247                                                 </Copy>
248                                         </Target>
249                                 </Project>
250                         ";
251                         engine = new Engine (Consts.BinPath);
252                         project = engine.CreateNewProject ();
253
254                         TestMessageLogger testLogger = new TestMessageLogger ();
255                         engine.RegisterLogger (testLogger);
256
257                         project.LoadXml (documentString);
258                         if (project.Build ("1")) {
259                                 testLogger.DumpMessages ();
260                                 Assert.Fail ("Build should have failed");
261                         }
262                 }
263
264                 [Test]
265                 public void TestCopy_ReadOnlyUpdate ()
266                 {
267                         Engine engine;
268                         Project project;
269                         string file_path = Path.Combine (source_path, "copyro.txt");
270                         string target_file = Path.Combine (target_path, "copyro.txt");                  
271
272                         using (File.CreateText (file_path)) { }
273                         
274                         string documentString = @"
275                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
276                                         <PropertyGroup><DestFile>" + target_file + @"</DestFile></PropertyGroup>
277                                         <ItemGroup>
278                                                 <SFiles Include='" + file_path + @"'><Md>1</Md></SFiles>
279                                                 <DFiles Include='$(DestFile)'><Mde>2</Mde></DFiles>
280                                         </ItemGroup>
281                                         <Target Name='1'>
282                                                 <Copy SourceFiles='@(SFiles)' DestinationFiles='@(DFiles)' >
283                                                         <Output TaskParameter='CopiedFiles' ItemName='I0'/>
284                                                         <Output TaskParameter='DestinationFiles' ItemName='I1'/>
285                                                 </Copy>
286                                                 <Message Text=""I0 : @(I0), I1: @(I1)""/>
287                                         </Target>
288                                 </Project>
289                         ";
290
291                         engine = new Engine (Consts.BinPath);
292                         project = engine.CreateNewProject ();
293
294                         TestMessageLogger testLogger = new TestMessageLogger ();
295                         engine.RegisterLogger (testLogger);
296
297                         project.LoadXml (documentString);
298
299                         if (!project.Build ("1")) {
300                                 testLogger.DumpMessages ();
301                                 Assert.Fail ("Build failed");
302                         }
303                         Assert.IsTrue (File.Exists (target_file), "A2");
304                         if (Environment.OSVersion.Platform == PlatformID.Unix)
305                                 Assert.AreEqual (FileAttributes.Normal, File.GetAttributes (target_file), "A3");
306                         else
307                                 // On Windows the Archive attribute will be set, not the Normal attribute.
308                                 Assert.AreEqual (FileAttributes.Archive, File.GetAttributes (target_file), "A3");
309                 }
310
311                 [Test]
312                 public void TestCopy_OverwriteReadOnlyTrue ()
313                 {
314                         Engine engine;
315                         Project project;
316                         string file_path = Path.Combine (source_path, "copyro1.txt");
317                         string target_file = Path.Combine (target_path, "copyro1.txt");                 
318
319                         using (File.CreateText (file_path)) { }
320                         using (File.CreateText (target_file)) { }
321
322                         File.SetAttributes (target_file, FileAttributes.ReadOnly);
323                         Assert.AreEqual (FileAttributes.ReadOnly, File.GetAttributes (target_file), "A1");
324                         
325                         string documentString = @"
326                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" ToolsVersion=""4.0"">
327                                         <PropertyGroup><DestFile>" + target_file + @"</DestFile></PropertyGroup>
328                                         <ItemGroup>
329                                                 <SFiles Include='" + file_path + @"'><Md>1</Md></SFiles>
330                                                 <DFiles Include='$(DestFile)'><Mde>2</Mde></DFiles>
331                                         </ItemGroup>
332                                         <Target Name='1'>
333                                                 <Copy SourceFiles='@(SFiles)' DestinationFiles='@(DFiles)' OverwriteReadOnlyFiles='true'>
334                                                         <Output TaskParameter='CopiedFiles' ItemName='I0'/>
335                                                         <Output TaskParameter='DestinationFiles' ItemName='I1'/>
336                                                 </Copy>
337                                                 <Message Text=""I0 : @(I0), I1: @(I1)""/>
338                                         </Target>
339                                 </Project>
340                         ";
341
342                         engine = new Engine (Consts.BinPath);
343                         project = engine.CreateNewProject ();
344
345                         TestMessageLogger testLogger = new TestMessageLogger ();
346                         engine.RegisterLogger (testLogger);
347
348                         project.LoadXml (documentString);
349
350                         if (!project.Build ("1")) {
351                                 var sb = new StringBuilder ();
352                                 testLogger.DumpMessages (sb);
353                                 Assert.Fail ("Build failed " + sb.ToString ());
354                         }
355                         Assert.IsTrue (File.Exists (target_file), "A2");
356                         var target_file_attrs = File.GetAttributes (target_file);
357                         if (Environment.OSVersion.Platform == PlatformID.Unix)
358                                 Assert.AreEqual (FileAttributes.Normal, File.GetAttributes (target_file), "A3");
359                         else
360                                 // On Windows the Archive attribute will be set, not the Normal attribute.
361                                 Assert.AreEqual (FileAttributes.Archive, File.GetAttributes (target_file), "A3");
362                 }
363
364                 [Test]
365                 public void TestCopy_OverwriteReadOnlyFalse ()
366                 {
367                         Engine engine;
368                         Project project;
369                         string file_path = Path.Combine (source_path, "copyro2.txt");
370                         string target_file = Path.Combine (target_path, "copyro2.txt");                 
371
372                         using (File.CreateText (file_path)) { }
373                         using (File.CreateText (target_file)) { }
374
375                         File.SetAttributes (target_file, FileAttributes.ReadOnly);
376                         Assert.AreEqual (FileAttributes.ReadOnly, File.GetAttributes (target_file), "A1");
377                         
378                         string documentString = @"
379                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
380                                         <PropertyGroup><DestFile>" + target_file + @"</DestFile></PropertyGroup>
381                                         <ItemGroup>
382                                                 <SFiles Include='" + file_path + @"'><Md>1</Md></SFiles>
383                                                 <DFiles Include='$(DestFile)'><Mde>2</Mde></DFiles>
384                                         </ItemGroup>
385                                         <Target Name='1'>
386                                                 <Copy SourceFiles='@(SFiles)' DestinationFiles='@(DFiles)'>
387                                                         <Output TaskParameter='CopiedFiles' ItemName='I0'/>
388                                                         <Output TaskParameter='DestinationFiles' ItemName='I1'/>
389                                                 </Copy>
390                                                 <Message Text=""I0 : @(I0), I1: @(I1)""/>
391                                         </Target>
392                                 </Project>
393                         ";
394
395                         engine = new Engine (Consts.BinPath);
396                         project = engine.CreateNewProject ();
397
398                         TestMessageLogger testLogger = new TestMessageLogger ();
399                         engine.RegisterLogger (testLogger);
400
401                         project.LoadXml (documentString);
402
403                         // build should fail because of the readonly target file
404                         Assert.IsFalse (project.Build ("1"));
405                         
406                         File.SetAttributes (target_file, FileAttributes.Normal);
407                 }
408
409                 [Test]
410                 public void TestCopy_Retries ()
411                 {
412                         Engine engine;
413                         Project project;
414                         string file_path = Path.Combine (source_path, "copyretries.txt");
415                         string target_file = Path.Combine (target_path, "copyretries.txt");                     
416
417                         using (File.CreateText (file_path)) { }
418                         using (File.CreateText (target_file)) { }
419
420                         File.SetAttributes (target_file, FileAttributes.ReadOnly);
421                         Assert.AreEqual (FileAttributes.ReadOnly, File.GetAttributes (target_file), "A1");
422
423                         string documentString = @"
424                                 <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
425                                         <PropertyGroup><DestFile>" + target_file + @"</DestFile></PropertyGroup>
426                                         <ItemGroup>
427                                                 <SFiles Include='" + file_path + @"'><Md>1</Md></SFiles>
428                                                 <DFiles Include='$(DestFile)'><Mde>2</Mde></DFiles>
429                                         </ItemGroup>
430                                         <Target Name='1'>
431                                                 <Copy SourceFiles='@(SFiles)' DestinationFiles='@(DFiles)' Retries='3' RetryDelayMilliseconds='2000'>
432                                                         <Output TaskParameter='CopiedFiles' ItemName='I0'/>
433                                                         <Output TaskParameter='DestinationFiles' ItemName='I1'/>
434                                                 </Copy>
435                                                 <Message Text=""I0 : @(I0), I1: @(I1)""/>
436                                         </Target>
437                                 </Project>
438                         ";
439
440                         engine = new Engine (Consts.BinPath);
441                         project = engine.CreateNewProject ();
442
443                         TestMessageLogger testLogger = new TestMessageLogger ();
444                         engine.RegisterLogger (testLogger);
445
446                         project.LoadXml (documentString);
447
448                         // remove the read-only flag from the file after a few secs,
449                         // so copying works after retries
450                         new Thread ( () => {
451                                 Thread.Sleep (3000);
452                                 File.SetAttributes (target_file, FileAttributes.Normal);
453                         }).Start ();
454
455                         if (!project.Build ("1")) {
456                                 var sb = new StringBuilder ();
457                                 testLogger.DumpMessages (sb);
458                                 Assert.Fail ("Build failed " + sb.ToString ());
459                         }
460
461                         testLogger.CheckLoggedAny ("Copying failed. Retries left: 3.", MessageImportance.Normal, "A2");
462                 }
463
464                 void CheckCopyBuildItems (Project project, string [] source_files, string destination_folder, string prefix)
465                 {
466                         int num = source_files.Length;
467                         for (int i = 0; i < num; i ++)
468                                 Assert.IsTrue (File.Exists (source_files [i]), prefix + " C1");
469
470                         BuildItemGroup big = project.GetEvaluatedItemsByName ("I0");
471
472                         Assert.AreEqual (num, big.Count, prefix + " C2");
473                         for (int i = 0; i < num; i++) {
474                                 string suffix = (i + 1).ToString ();
475                                 BuildItem bi = big [i];
476                                 Assert.AreEqual (Path.Combine (destination_folder, Path.GetFileName (source_files [i])),
477                                         bi.FinalItemSpec, prefix + " C3 #" + suffix);
478
479                                 Assert.AreEqual (suffix, bi.GetMetadata ("Md"), prefix + " C4 #" + suffix);
480                         }
481
482                         big = project.GetEvaluatedItemsByName ("I1");
483                         Assert.AreEqual (num, big.Count, prefix + " C6");
484                         for (int i = 0; i < num; i++) {
485                                 string suffix = (i + 1).ToString ();
486                                 BuildItem bi = big [i];
487                                 Assert.AreEqual (Path.Combine (destination_folder, Path.GetFileName (source_files [i])),
488                                         bi.FinalItemSpec, prefix + " C7 #" + suffix);
489                                 Assert.AreEqual (suffix, bi.GetMetadata ("Md"), prefix + " C8 #" + suffix);
490                         }
491                  }
492         }
493 }