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.Reflection;
7 using System.Collections;
8 using Microsoft.Build.Framework;
9 using Microsoft.Build.Tasks;
10 using Microsoft.Build.Utilities;
11 using Microsoft.Build.Shared;
12 
13 using System.Text;
14 using Shouldly;
15 using Xunit;
16 using Xunit.Abstractions;
17 using System.Collections.Generic;
18 
19 namespace Microsoft.Build.UnitTests.GenerateResource_Tests.InProc
20 {
21     [Trait("Category", "mono-osx-failing")]
22     [Trait("Category", "mono-windows-failing")]
23     public sealed class RequiredTransformations
24     {
25         private readonly TestEnvironment _env;
26         private readonly ITestOutputHelper _output;
27 
RequiredTransformations(ITestOutputHelper output)28         public RequiredTransformations(ITestOutputHelper output)
29         {
30             _env = TestEnvironment.Create(output);
31             _output = output;
32         }
33 
34         /// <summary>
35         ///  ResX to Resources, no references
36         /// </summary>
37         [Theory]
38         [InlineData(true)]
39         [InlineData(false)]
BasicResX2Resources(bool resourceReadOnly)40         public void BasicResX2Resources(bool resourceReadOnly)
41         {
42             // This WriteLine is a hack.  On a slow machine, the Tasks unittest fails because remoting
43             // times out the object used for remoting console writes.  Adding a write in the middle of
44             // keeps remoting from timing out the object.
45             Console.WriteLine("Performing BasicResX2Resources() test");
46 
47             string resxFile = null;
48 
49             GenerateResource t = Utilities.CreateTask(_output);
50             t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
51 
52             try
53             {
54                 resxFile = Utilities.WriteTestResX(false, null, null);
55 
56                 if (resourceReadOnly)
57                 {
58                     File.SetAttributes(resxFile, FileAttributes.ReadOnly);
59                 }
60 
61                 t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
62                 t.Sources[0].SetMetadata("Attribute", "InputValue");
63 
64                 Utilities.ExecuteTask(t);
65 
66                 Assert.Equal("InputValue", t.OutputResources[0].GetMetadata("Attribute"));
67                 string resourcesFile = t.OutputResources[0].ItemSpec;
68                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
69                 resourcesFile = t.FilesWritten[0].ItemSpec;
70                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
71 
72 #if FEATURE_RESGENCACHE
73                 Utilities.AssertStateFileWasWritten(t);
74 #endif
75 
76                 Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", resxFile, resourcesFile);
77                 Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 1, resxFile);
78             }
79             finally
80             {
81                 // Done, so clean up.
82                 if (resourceReadOnly && !string.IsNullOrEmpty(resxFile))
83                 {
84                     File.SetAttributes(resxFile, FileAttributes.Normal);
85                 }
86 
87                 File.Delete(t.Sources[0].ItemSpec);
88                 foreach (ITaskItem item in t.FilesWritten)
89                 {
90                     if (File.Exists(item.ItemSpec))
91                     {
92                         File.Delete(item.ItemSpec);
93                     }
94                 }
95             }
96         }
97 
98         /// <summary>
99         /// Ensure that OutputResource Metadata is populated on the Sources item
100         /// </summary>
101         [Fact]
OutputResourceMetadataPopulatedOnInputItems()102         public void OutputResourceMetadataPopulatedOnInputItems()
103         {
104             string resxFile0 = Utilities.WriteTestResX(false, null, null);
105             string resxFile1 = Utilities.WriteTestResX(false, null, null);
106             string resxFile2 = Utilities.WriteTestResX(false, null, null);
107             string resxFile3 = Utilities.WriteTestResX(false, null, null);
108 
109             string expectedOutFile0 = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(resxFile0, ".resources"));
110             string expectedOutFile1 = Path.Combine(Path.GetTempPath(), "resx1.foo.resources");
111             string expectedOutFile2 = Path.Combine(Path.GetTempPath(), Utilities.GetTempFileName(".resources"));
112             string expectedOutFile3 = Path.Combine(Path.GetTempPath(), Utilities.GetTempFileName(".resources"));
113 
114             GenerateResource t = Utilities.CreateTask(_output);
115             t.Sources = new ITaskItem[] {
116                 new TaskItem(resxFile0), new TaskItem(resxFile1), new TaskItem(resxFile2), new TaskItem(resxFile3) };
117 
118             t.OutputResources = new ITaskItem[] {
119                 new TaskItem(expectedOutFile0), new TaskItem(expectedOutFile1), new TaskItem(expectedOutFile2), new TaskItem(expectedOutFile3) };
120 
121             Utilities.ExecuteTask(t);
122 
123             Assert.Equal(expectedOutFile0, t.Sources[0].GetMetadata("OutputResource"));
124             Assert.Equal(expectedOutFile1, t.Sources[1].GetMetadata("OutputResource"));
125             Assert.Equal(expectedOutFile2, t.Sources[2].GetMetadata("OutputResource"));
126             Assert.Equal(expectedOutFile3, t.Sources[3].GetMetadata("OutputResource"));
127 
128             // Done, so clean up.
129             File.Delete(resxFile0);
130             File.Delete(resxFile1);
131             File.Delete(resxFile2);
132             File.Delete(resxFile3);
133             foreach (ITaskItem item in t.FilesWritten)
134                 File.Delete(item.ItemSpec);
135         }
136 
137         /// <summary>
138         ///  Text to Resources
139         /// </summary>
140         [Fact]
BasicText2Resources()141         public void BasicText2Resources()
142         {
143             GenerateResource t = Utilities.CreateTask(_output);
144             t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
145 
146             try
147             {
148                 string textFile = Utilities.WriteTestText(null, null);
149                 t.Sources = new ITaskItem[] { new TaskItem(textFile) };
150                 t.Sources[0].SetMetadata("Attribute", "InputValue");
151 
152                 Utilities.ExecuteTask(t);
153 
154                 Assert.Equal("InputValue", t.OutputResources[0].GetMetadata("Attribute"));
155                 string resourcesFile = t.OutputResources[0].ItemSpec;
156                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
157                 resourcesFile = t.FilesWritten[0].ItemSpec;
158                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
159 
160 #if FEATURE_RESGENCACHE
161                 Utilities.AssertStateFileWasWritten(t);
162 #endif
163 
164                 Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", textFile, resourcesFile);
165                 Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 4, textFile);
166             }
167             finally
168             {
169                 // Done, so clean up.
170                 File.Delete(t.Sources[0].ItemSpec);
171                 foreach (ITaskItem item in t.FilesWritten)
172                 {
173                     if (File.Exists(item.ItemSpec))
174                     {
175                         File.Delete(item.ItemSpec);
176                     }
177                 }
178             }
179         }
180 
181         /// <summary>
182         ///  ResX to Resources with references that are used in the resx
183         /// </summary>
184         /// <remarks>System dll is not locked because it forces a new app domain</remarks>
185 #if RUNTIME_TYPE_NETCORE
186         [Fact(Skip = "Depends on referencing System.dll")]
187 #else
188         [Fact]
189 #endif
ResX2ResourcesWithReferences()190         public void ResX2ResourcesWithReferences()
191         {
192             string systemDll = Utilities.GetPathToCopiedSystemDLL();
193             string resxFile = null;
194             string resourcesFile = null;
195 
196             try
197             {
198                 GenerateResource t = Utilities.CreateTask(_output);
199 
200                 resxFile = Utilities.WriteTestResX(true /*system type*/, null, null);
201                 t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
202                 t.References = new ITaskItem[] { new TaskItem(systemDll) };
203 
204                 Utilities.ExecuteTask(t);
205 
206                 resourcesFile = t.OutputResources[0].ItemSpec;
207                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
208                 Assert.Equal(t.FilesWritten[0].ItemSpec, resourcesFile);
209 
210                 Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", resxFile, resourcesFile);
211                 Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 2, resxFile);
212             }
213             finally
214             {
215                 File.Delete(systemDll);
216                 if (resxFile != null) File.Delete(resxFile);
217                 if (resourcesFile != null) File.Delete(resourcesFile);
218             }
219         }
220 
221         /// <summary>
222         ///  Resources to ResX
223         /// </summary>
224 #if FEATURE_RESX_RESOURCE_READER
225         [Fact]
226 #else
227         [Fact (Skip = "ResGen.exe not supported on .NET Core MSBuild")]
228 #endif
BasicResources2ResX()229         public void BasicResources2ResX()
230         {
231             string resourcesFile = Utilities.CreateBasicResourcesFile(false, _output);
232 
233             // Fork 1: create a resx file directly from the resources
234             GenerateResource t = Utilities.CreateTask(_output);
235             t.Sources = new ITaskItem[] { new TaskItem(resourcesFile) };
236             t.OutputResources = new ITaskItem[] { new TaskItem(Path.ChangeExtension(resourcesFile, ".resx")) };
237             Utilities.ExecuteTask(t);
238             Assert.Equal(Path.GetExtension(t.FilesWritten[0].ItemSpec), ".resx");
239 
240             // Fork 2a: create a text file from the resources
241             GenerateResource t2a = Utilities.CreateTask(_output);
242             t2a.Sources = new ITaskItem[] { new TaskItem(resourcesFile) };
243             t2a.OutputResources = new ITaskItem[] { new TaskItem(Path.ChangeExtension(resourcesFile, ".txt")) };
244             Utilities.ExecuteTask(t2a);
245             Assert.Equal(Path.GetExtension(t2a.FilesWritten[0].ItemSpec), ".txt");
246 
247             // Fork 2b: create a resx file from the text file
248             GenerateResource t2b = Utilities.CreateTask(_output);
249             t2b.Sources = new ITaskItem[] { new TaskItem(t2a.FilesWritten[0].ItemSpec) };
250             t2b.OutputResources = new ITaskItem[] { new TaskItem(Utilities.GetTempFileName(".resx")) };
251             Utilities.ExecuteTask(t2b);
252             Assert.Equal(Path.GetExtension(t2b.FilesWritten[0].ItemSpec), ".resx");
253 
254             // make sure the output resx files from each fork are the same
255             Assert.Equal(Utilities.ReadFileContent(t.OutputResources[0].ItemSpec),
256                                    Utilities.ReadFileContent(t2b.OutputResources[0].ItemSpec));
257             Utilities.AssertLogContainsResource(t2b, "GenerateResource.ProcessingFile", t2b.Sources[0].ItemSpec, t2b.OutputResources[0].ItemSpec);
258             Utilities.AssertLogContainsResource(t2b, "GenerateResource.ReadResourceMessage", 4, t2b.Sources[0].ItemSpec);
259 
260             // Done, so clean up.
261             File.Delete(resourcesFile);
262             File.Delete(t.OutputResources[0].ItemSpec);
263             File.Delete(t2a.OutputResources[0].ItemSpec);
264             foreach (ITaskItem item in t2b.FilesWritten)
265                 File.Delete(item.ItemSpec);
266         }
267 
268         /// <summary>
269         ///  Resources to Text
270         /// </summary>
271 #if FEATURE_RESX_RESOURCE_READER
272         [Fact]
273 #else
274         [Fact(Skip = "ResGen.exe not supported on .NET Core MSBuild")]
275 #endif
BasicResources2Text()276         public void BasicResources2Text()
277         {
278             string resourcesFile = Utilities.CreateBasicResourcesFile(false, _output);
279 
280             GenerateResource t = Utilities.CreateTask(_output);
281 
282             t.Sources = new ITaskItem[] { new TaskItem(resourcesFile) };
283 
284             string outputFile = Path.ChangeExtension(resourcesFile, ".txt");
285             t.OutputResources = new ITaskItem[] { new TaskItem(outputFile) };
286             Utilities.ExecuteTask(t);
287 
288             resourcesFile = t.FilesWritten[0].ItemSpec;
289             Assert.Equal(Path.GetExtension(resourcesFile), ".txt");
290             Assert.Equal(Utilities.GetTestTextContent(null, null, true /*cleaned up */), Utilities.ReadFileContent(resourcesFile));
291             Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", t.Sources[0].ItemSpec, outputFile);
292             Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 4, t.Sources[0].ItemSpec);
293 
294             // Done, so clean up.
295             File.Delete(t.Sources[0].ItemSpec);
296             foreach (ITaskItem item in t.FilesWritten)
297                 File.Delete(item.ItemSpec);
298         }
299 
300         /// <summary>
301         ///  Force out-of-date with ShouldRebuildResgenOutputFile on the source only
302         /// </summary>
303         [Fact]
304         [Trait("Category", "netcore-osx-failing")]
305         [Trait("Category", "netcore-linux-failing")]
ForceOutOfDate()306         public void ForceOutOfDate()
307         {
308             var folder = _env.CreateFolder();
309             string resxFile = Utilities.WriteTestResX(false, null, null, _env.CreateFile(folder, ".resx").Path);
310 
311             GenerateResource t = Utilities.CreateTask(_output);
312             t.StateFile = new TaskItem(_env.GetTempFile(".cache").Path);
313             t.Sources = new ITaskItem[] {new TaskItem(resxFile)};
314 
315             Utilities.ExecuteTask(t);
316 
317             string resourcesFile = t.OutputResources[0].ItemSpec;
318             Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
319             resourcesFile = t.FilesWritten[0].ItemSpec;
320             Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
321 
322 #if FEATURE_RESGENCACHE
323                 Utilities.AssertStateFileWasWritten(t);
324 #endif
325             GenerateResource t2 = Utilities.CreateTask(_output);
326             t2.StateFile = new TaskItem(t.StateFile);
327             t2.Sources = new ITaskItem[] {new TaskItem(resxFile)};
328 
329             DateTime time = File.GetLastWriteTime(t.OutputResources[0].ItemSpec);
330 
331             System.Threading.Thread.Sleep(200);
332             if (NativeMethodsShared.IsOSX)
333             {
334                 // Must be > 1 sec for HFS+ timestamp granularity
335                 System.Threading.Thread.Sleep(1100);
336             }
337 
338             File.SetLastWriteTime(resxFile, DateTime.Now);
339 
340             Utilities.ExecuteTask(t2);
341 
342             Assert.True(DateTime.Compare(File.GetLastWriteTime(t2.OutputResources[0].ItemSpec), time) > 0);
343         }
344 
345         /// <summary>
346         ///  Force out-of-date with ShouldRebuildResgenOutputFile on the linked file
347         /// </summary>
348 #if FEATURE_LINKED_RESOURCES
349         [Fact]
350 #else
351         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1247")]
352 #endif
353         [Trait("Category", "mono-osx-failing")] // libgdiplus not found
ForceOutOfDateLinked()354         public void ForceOutOfDateLinked()
355         {
356             string bitmap = Utilities.CreateWorldsSmallestBitmap();
357             string resxFile = Utilities.WriteTestResX(false, bitmap, null, false);
358 
359             GenerateResource t = Utilities.CreateTask(_output);
360             t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
361 
362             try
363             {
364                 t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
365 
366                 Utilities.ExecuteTask(t);
367 
368                 string resourcesFile = t.OutputResources[0].ItemSpec;
369                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
370                 resourcesFile = t.FilesWritten[0].ItemSpec;
371                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
372 
373 #if FEATURE_RESGENCACHE
374                 Utilities.AssertStateFileWasWritten(t);
375 #endif
376 
377                 GenerateResource t2 = Utilities.CreateTask(_output);
378                 t2.StateFile = new TaskItem(t.StateFile);
379                 t2.Sources = new ITaskItem[] { new TaskItem(resxFile) };
380 
381                 DateTime time = File.GetLastWriteTime(t.OutputResources[0].ItemSpec);
382                 System.Threading.Thread.Sleep(200);
383                 File.SetLastWriteTime(bitmap, DateTime.Now);
384 
385                 Utilities.ExecuteTask(t2);
386 
387                 Assert.True(DateTime.Compare(File.GetLastWriteTime(t2.OutputResources[0].ItemSpec), time) > 0);
388             }
389             finally
390             {
391                 // Done, so clean up.
392                 File.Delete(t.Sources[0].ItemSpec);
393                 File.Delete(bitmap);
394                 foreach (ITaskItem item in t.FilesWritten)
395                 {
396                     if (File.Exists(item.ItemSpec))
397                     {
398                         File.Delete(item.ItemSpec);
399                     }
400                 }
401             }
402         }
403 
404         /// <summary>
405         ///  Force partially out-of-date: should build only the out of date inputs
406         /// </summary>
407         [Fact]
ForceSomeOutOfDate()408         public void ForceSomeOutOfDate()
409         {
410             var folder = _env.CreateFolder();
411 
412             var firstResx = Utilities.WriteTestResX(false, null, null, _env.CreateFile(folder, ".resx").Path);
413             var secondResx = Utilities.WriteTestResX(false, null, null, _env.CreateFile(folder, ".resx").Path);
414             var cache = _env.GetTempFile(folder, ".cache").Path;
415 
416             GenerateResource createResources = Utilities.CreateTask(_output);
417             createResources.StateFile = new TaskItem(cache);
418             createResources.Sources = new ITaskItem[] {new TaskItem(firstResx), new TaskItem(secondResx)};
419 
420             _output.WriteLine("Transform both");
421             Utilities.ExecuteTask(createResources);
422 
423             _output.WriteLine("Get current write times of outputs");
424             DateTime firstOutputCreationTime = File.GetLastWriteTime(createResources.OutputResources[0].ItemSpec);
425             DateTime secondOutputCreationTime = File.GetLastWriteTime(createResources.OutputResources[1].ItemSpec);
426 
427             _output.WriteLine("Create a new task to transform them again");
428             GenerateResource t2 = Utilities.CreateTask(_output);
429             t2.StateFile = new TaskItem(createResources.StateFile.ItemSpec);
430             t2.Sources = new ITaskItem[] {new TaskItem(firstResx), new TaskItem(secondResx)};
431 
432             System.Threading.Thread.Sleep(200);
433             if (!NativeMethodsShared.IsWindows)
434             {
435                 // Must be > 1 sec on some file systems for proper timestamp granularity
436                 // TODO: Implement an interface for fetching deterministic timestamps rather than relying on the file
437                 System.Threading.Thread.Sleep(1000);
438             }
439 
440             _output.WriteLine("Touch one input");
441             File.SetLastWriteTime(firstResx, DateTime.Now);
442 
443             Utilities.ExecuteTask(t2);
444 
445             _output.WriteLine("Check only one output was updated");
446             File.GetLastWriteTime(t2.OutputResources[0].ItemSpec).ShouldBeGreaterThan(firstOutputCreationTime);
447             File.GetLastWriteTime(t2.OutputResources[1].ItemSpec).ShouldBe(secondOutputCreationTime);
448 
449             // Although only one file was updated, both should be in OutputResources and FilesWritten
450             t2.OutputResources[0].ItemSpec.ShouldBe(createResources.OutputResources[0].ItemSpec);
451             t2.OutputResources[1].ItemSpec.ShouldBe(createResources.OutputResources[1].ItemSpec);
452             t2.FilesWritten[0].ItemSpec.ShouldBe(createResources.FilesWritten[0].ItemSpec);
453             t2.FilesWritten[1].ItemSpec.ShouldBe(createResources.FilesWritten[1].ItemSpec);
454         }
455 
456         /// <summary>
457         ///  Allow ShouldRebuildResgenOutputFile to return "false" since nothing's out of date, including linked file
458         /// </summary>
459 #if FEATURE_RESX_RESOURCE_READER
460         [Fact]
461 #else
462         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1247")]
463 #endif
464         [Trait("Category", "mono-osx-failing")] // libgdiplus not found
AllowLinkedNoGenerate()465         public void AllowLinkedNoGenerate()
466         {
467             string bitmap = Utilities.CreateWorldsSmallestBitmap();
468             string resxFile = Utilities.WriteTestResX(false, bitmap, null, false);
469 
470             GenerateResource t = Utilities.CreateTask(_output);
471             t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
472 
473             try
474             {
475                 t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
476 
477                 Utilities.ExecuteTask(t);
478 
479                 string resourcesFile = t.OutputResources[0].ItemSpec;
480                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
481                 resourcesFile = t.FilesWritten[0].ItemSpec;
482                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
483 
484                 Utilities.AssertStateFileWasWritten(t);
485 
486                 DateTime time = File.GetLastWriteTime(t.OutputResources[0].ItemSpec);
487 
488                 GenerateResource t2 = Utilities.CreateTask(_output);
489                 t2.StateFile = new TaskItem(t.StateFile);
490                 t2.Sources = new ITaskItem[] { new TaskItem(resxFile) };
491 
492                 System.Threading.Thread.Sleep(500);
493 
494                 Utilities.ExecuteTask(t2);
495 
496                 Assert.True(time.Equals(File.GetLastWriteTime(t2.OutputResources[0].ItemSpec)));
497             }
498             finally
499             {
500                 // Done, so clean up.
501                 File.Delete(t.Sources[0].ItemSpec);
502                 File.Delete(bitmap);
503                 foreach (ITaskItem item in t.FilesWritten)
504                 {
505                     if (File.Exists(item.ItemSpec))
506                     {
507                         File.Delete(item.ItemSpec);
508                     }
509                 }
510             }
511         }
512 
513         /// <summary>
514         ///  Allow the task to skip processing based on having nothing out of date
515         /// </summary>
516         [Fact]
NothingOutOfDate()517         public void NothingOutOfDate()
518         {
519             string resxFile = null;
520             string txtFile = null;
521             string resourcesFile1 = null;
522             string resourcesFile2 = null;
523 
524             try
525             {
526                 resxFile = Utilities.WriteTestResX(false, null, null);
527                 txtFile = Utilities.WriteTestText(null, null);
528 
529                 GenerateResource t = Utilities.CreateTask(_output);
530                 t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
531                 t.Sources = new ITaskItem[] { new TaskItem(resxFile), new TaskItem(txtFile) };
532                 resourcesFile1 = Path.ChangeExtension(resxFile, ".resources");
533                 resourcesFile2 = Path.ChangeExtension(txtFile, ".resources");
534 
535                 Utilities.ExecuteTask(t);
536 
537                 Assert.Equal(t.OutputResources[0].ItemSpec, resourcesFile1);
538                 Assert.Equal(t.FilesWritten[0].ItemSpec, resourcesFile1);
539                 Assert.Equal(t.OutputResources[1].ItemSpec, resourcesFile2);
540                 Assert.Equal(t.FilesWritten[1].ItemSpec, resourcesFile2);
541 
542 #if FEATURE_RESGENCACHE
543                 Utilities.AssertStateFileWasWritten(t);
544 #endif
545 
546                 // Repeat, and it should do nothing as they are up to date
547                 GenerateResource t2 = Utilities.CreateTask(_output);
548                 t2.StateFile = new TaskItem(t.StateFile);
549                 t2.Sources = new ITaskItem[] { new TaskItem(resxFile), new TaskItem(txtFile) };
550 
551                 DateTime time = File.GetLastWriteTime(t.OutputResources[0].ItemSpec);
552                 DateTime time2 = File.GetLastWriteTime(t.OutputResources[1].ItemSpec);
553                 System.Threading.Thread.Sleep(200);
554 
555                 Utilities.ExecuteTask(t2);
556                 // Although everything was up to date, OutputResources and FilesWritten
557                 // must contain the files that would have been created if they weren't up to date.
558                 Assert.Equal(t2.OutputResources[0].ItemSpec, resourcesFile1);
559                 Assert.Equal(t2.FilesWritten[0].ItemSpec, resourcesFile1);
560                 Assert.Equal(t2.OutputResources[1].ItemSpec, resourcesFile2);
561                 Assert.Equal(t2.FilesWritten[1].ItemSpec, resourcesFile2);
562 
563 #if FEATURE_RESGENCACHE
564                 Utilities.AssertStateFileWasWritten(t2);
565 #endif
566 
567                 Assert.True(time.Equals(File.GetLastWriteTime(t2.OutputResources[0].ItemSpec)));
568                 Assert.True(time2.Equals(File.GetLastWriteTime(t2.OutputResources[1].ItemSpec)));
569             }
570             finally
571             {
572                 if (resxFile != null) File.Delete(resxFile);
573                 if (txtFile != null) File.Delete(txtFile);
574                 if (resourcesFile1 != null) File.Delete(resourcesFile1);
575                 if (resourcesFile2 != null) File.Delete(resourcesFile2);
576             }
577         }
578 
579         /// <summary>
580         /// If the reference has been touched, it should rebuild even if the inputs are
581         /// otherwise up to date
582         /// </summary>
583         /// <remarks>System dll is not locked because it forces a new app domain</remarks>
584 #if RUNTIME_TYPE_NETCORE
585         [Fact(Skip = "Depends on referencing System.dll")]
586 #else
587         [Fact]
588 #endif
NothingOutOfDateExceptReference()589         public void NothingOutOfDateExceptReference()
590         {
591             string resxFile = null;
592             string resourcesFile = null;
593             string stateFile = Utilities.GetTempFileName(".cache");
594             string localSystemDll = Utilities.GetPathToCopiedSystemDLL();
595 
596             try
597             {
598                 resxFile = Utilities.WriteTestResX(true /* uses system type */, null, null);
599 
600                 _output.WriteLine("** Running task to create resources.");
601 
602                 GenerateResource initialCreator = Utilities.CreateTask(_output);
603                 initialCreator.Sources = new ITaskItem[] { new TaskItem(resxFile) };
604                 initialCreator.References = new ITaskItem[] { new TaskItem(localSystemDll) };
605                 initialCreator.StateFile = new TaskItem(stateFile);
606                 Utilities.ExecuteTask(initialCreator);
607 
608                 DateTime firstWriteTime = File.GetLastWriteTime(initialCreator.OutputResources[0].ItemSpec);
609 
610                 _output.WriteLine("** Repeat, and it should do nothing as they are up to date");
611 
612                 GenerateResource incrementalUpToDate = Utilities.CreateTask(_output);
613                 incrementalUpToDate.Sources = new ITaskItem[] { new TaskItem(resxFile) };
614                 incrementalUpToDate.References = new ITaskItem[] { new TaskItem(localSystemDll) };
615                 incrementalUpToDate.StateFile = new TaskItem(stateFile);
616                 Utilities.ExecuteTask(incrementalUpToDate);
617 
618                 File.GetLastWriteTime(incrementalUpToDate.OutputResources[0].ItemSpec).ShouldBe(firstWriteTime);
619 
620 
621                 _output.WriteLine("** Touch the reference, and repeat, it should now rebuild");
622                 DateTime newTime = DateTime.Now + new TimeSpan(0, 1, 0);
623                 File.SetLastWriteTime(localSystemDll, newTime);
624 
625                 GenerateResource incrementalOutOfDate = Utilities.CreateTask(_output);
626                 incrementalOutOfDate.Sources = new ITaskItem[] { new TaskItem(resxFile) };
627                 incrementalOutOfDate.References = new ITaskItem[] { new TaskItem(localSystemDll) };
628                 incrementalOutOfDate.StateFile = new TaskItem(stateFile);
629 
630                 if (!NativeMethodsShared.IsWindows)
631                 {
632                     // Must be > 1 sec on some file systems for proper timestamp granularity
633                     // TODO: Implement an interface for fetching deterministic timestamps rather than relying on the file
634                     System.Threading.Thread.Sleep(1100);
635                 }
636 
637                 Utilities.ExecuteTask(incrementalOutOfDate);
638 
639                 File.GetLastWriteTime(incrementalOutOfDate.OutputResources[0].ItemSpec).ShouldBeGreaterThan(firstWriteTime);
640 
641                 resourcesFile = incrementalOutOfDate.OutputResources[0].ItemSpec;
642             }
643             finally
644             {
645                 if (resxFile != null) File.Delete(resxFile);
646                 if (resourcesFile != null) File.Delete(resourcesFile);
647                 if (stateFile != null) File.Delete(stateFile);
648                 if (localSystemDll != null) File.Delete(localSystemDll);
649             }
650         }
651 
652         /// <summary>
653         /// If an additional input is out of date, resources should be regenerated.
654         /// </summary>
655         [Fact]
NothingOutOfDateExceptAdditionalInput()656         public void NothingOutOfDateExceptAdditionalInput()
657         {
658             string resxFile = null;
659             string resourcesFile = null;
660             ITaskItem[] additionalInputs = null;
661 
662             try
663             {
664                 resxFile = Utilities.WriteTestResX(false, null, null);
665                 additionalInputs = new ITaskItem[] { new TaskItem(FileUtilities.GetTemporaryFile()), new TaskItem(FileUtilities.GetTemporaryFile()) };
666 
667                 foreach (ITaskItem file in additionalInputs)
668                 {
669                     if (!File.Exists(file.ItemSpec))
670                     {
671                         File.WriteAllText(file.ItemSpec, "");
672                     }
673                 }
674 
675                 GenerateResource t = Utilities.CreateTask(_output);
676                 t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
677                 t.AdditionalInputs = additionalInputs;
678                 t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
679                 Utilities.ExecuteTask(t);
680 
681                 // Repeat, and it should do nothing as they are up to date
682                 GenerateResource t2 = Utilities.CreateTask(_output);
683                 t2.Sources = new ITaskItem[] { new TaskItem(resxFile) };
684                 t2.AdditionalInputs = additionalInputs;
685                 t2.StateFile = new TaskItem(t.StateFile);
686                 Utilities.ExecuteTask(t2);
687                 Utilities.AssertLogContainsResource(t2, "GenerateResource.NothingOutOfDate", "");
688 
689                 // Touch one of the additional inputs and repeat, it should now rebuild
690                 DateTime newTime = DateTime.Now + new TimeSpan(0, 1, 0);
691                 File.SetLastWriteTime(additionalInputs[1].ItemSpec, newTime);
692                 GenerateResource t3 = Utilities.CreateTask(_output);
693                 t3.Sources = new ITaskItem[] { new TaskItem(resxFile) };
694                 t3.AdditionalInputs = additionalInputs;
695                 t3.StateFile = new TaskItem(t.StateFile);
696                 Utilities.ExecuteTask(t3);
697                 Utilities.AssertLogNotContainsResource(t3, "GenerateResource.NothingOutOfDate", "");
698                 resourcesFile = t3.OutputResources[0].ItemSpec;
699             }
700             finally
701             {
702                 if (resxFile != null) File.Delete(resxFile);
703                 if (resourcesFile != null) File.Delete(resourcesFile);
704                 if (additionalInputs != null && additionalInputs[0] != null && File.Exists(additionalInputs[0].ItemSpec)) File.Delete(additionalInputs[0].ItemSpec);
705                 if (additionalInputs != null && additionalInputs[1] != null && File.Exists(additionalInputs[1].ItemSpec)) File.Delete(additionalInputs[1].ItemSpec);
706             }
707         }
708 
709         /// <summary>
710         ///  Text to ResX
711         /// </summary>
712 #if FEATURE_RESX_RESOURCE_READER
713         [Fact]
714 #else
715         [Fact(Skip = "Writing to XML not supported on .net core")]
716 #endif
BasicText2ResX()717         public void BasicText2ResX()
718         {
719             GenerateResource t = Utilities.CreateTask(_output);
720 
721             t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
722 
723             string textFile = Utilities.WriteTestText(null, null);
724             t.Sources = new ITaskItem[] { new TaskItem(textFile) };
725             t.OutputResources = new ITaskItem[] { new TaskItem(Path.ChangeExtension(textFile, ".resx")) };
726 
727             Utilities.ExecuteTask(t);
728 
729             string resourcesFile = t.OutputResources[0].ItemSpec;
730             Assert.Equal(Path.GetExtension(resourcesFile), ".resx");
731 
732             Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", textFile, resourcesFile);
733             Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 4, textFile);
734 
735             // Done, so clean up.
736             File.Delete(t.Sources[0].ItemSpec);
737             foreach (ITaskItem item in t.FilesWritten)
738                 File.Delete(item.ItemSpec);
739         }
740 
741         /// <summary>
742         ///  Round trip from resx to resources to resx with the same blobs
743         /// </summary>
744 #if FEATURE_RESX_RESOURCE_READER
745         [Fact]
746 #else
747         [Fact(Skip = "ResGen.exe not supported on.NET Core MSBuild")]
748 #endif
ResX2ResX()749         public void ResX2ResX()
750         {
751             string resourcesFile = Utilities.CreateBasicResourcesFile(true, _output);
752 
753             // Step 1: create a resx file directly from the resources, to get a framework generated resx
754             GenerateResource t = Utilities.CreateTask(_output);
755             t.Sources = new ITaskItem[] { new TaskItem(resourcesFile) };
756             t.OutputResources = new ITaskItem[] { new TaskItem(Path.ChangeExtension(resourcesFile, ".resx")) };
757             Utilities.ExecuteTask(t);
758             Assert.Equal(Path.GetExtension(t.FilesWritten[0].ItemSpec), ".resx");
759 
760             // Step 2a: create a resources file from the resx
761             GenerateResource t2a = Utilities.CreateTask(_output);
762             t2a.Sources = new ITaskItem[] { new TaskItem(t.FilesWritten[0].ItemSpec) };
763             t2a.OutputResources = new ITaskItem[] { new TaskItem(Path.ChangeExtension(t.FilesWritten[0].ItemSpec, ".resources")) };
764             Utilities.ExecuteTask(t2a);
765             Assert.Equal(Path.GetExtension(t2a.FilesWritten[0].ItemSpec), ".resources");
766 
767             // Step 2b: create a resx from the resources
768             GenerateResource t2b = Utilities.CreateTask(_output);
769             t2b.Sources = new ITaskItem[] { new TaskItem(t2a.FilesWritten[0].ItemSpec) };
770             t2b.OutputResources = new ITaskItem[] { new TaskItem(Utilities.GetTempFileName(".resx")) };
771             File.Delete(t2b.OutputResources[0].ItemSpec);
772             Utilities.ExecuteTask(t2b);
773             Assert.Equal(Path.GetExtension(t2b.FilesWritten[0].ItemSpec), ".resx");
774 
775             // make sure the output resx files from each fork are the same
776             Assert.Equal(Utilities.ReadFileContent(t.OutputResources[0].ItemSpec),
777                          Utilities.ReadFileContent(t2b.OutputResources[0].ItemSpec));
778 
779             // Done, so clean up.
780             File.Delete(resourcesFile);
781             File.Delete(t.OutputResources[0].ItemSpec);
782             File.Delete(t2a.OutputResources[0].ItemSpec);
783             foreach (ITaskItem item in t2b.FilesWritten)
784                 File.Delete(item.ItemSpec);
785         }
786 
787         /// <summary>
788         ///  Round trip from text to resources to text with the same blobs
789         /// </summary>
790 #if FEATURE_RESX_RESOURCE_READER
791         [Fact]
792 #else
793         [Fact(Skip = "ResGen.exe not supported on.NET Core MSBuild")]
794 #endif
Text2Text()795         public void Text2Text()
796         {
797             string textFile = Utilities.WriteTestText(null, null);
798 
799             // Round 1, do the Text2Resource
800             GenerateResource t = Utilities.CreateTask(_output);
801             t.Sources = new ITaskItem[] { new TaskItem(textFile) };
802 
803             Utilities.ExecuteTask(t);
804 
805             // make sure round 1 is successful
806             string resourcesFile = t.OutputResources[0].ItemSpec;
807             Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
808 
809             // round 2, do the resources2Text from the same file
810             GenerateResource t2 = Utilities.CreateTask(_output);
811 
812             t2.Sources = new ITaskItem[] { new TaskItem(resourcesFile) };
813             string outputFile = Utilities.GetTempFileName(".txt");
814             t2.OutputResources = new ITaskItem[] { new TaskItem(outputFile) };
815             Utilities.ExecuteTask(t2);
816 
817             resourcesFile = t2.FilesWritten[0].ItemSpec;
818             Assert.Equal(Path.GetExtension(resourcesFile), ".txt");
819 
820             Assert.Equal(Utilities.GetTestTextContent(null, null, true /*cleaned up */), Utilities.ReadFileContent(resourcesFile));
821 
822             // Done, so clean up.
823             File.Delete(t.Sources[0].ItemSpec);
824             foreach (ITaskItem item in t.FilesWritten)
825                 File.Delete(item.ItemSpec);
826             File.Delete(t2.Sources[0].ItemSpec);
827             foreach (ITaskItem item in t2.FilesWritten)
828                 File.Delete(item.ItemSpec);
829         }
830 
831         /// <summary>
832         ///  STR without references yields proper output, message
833         /// </summary>
834 #if FEATURE_CODEDOM
835         [Fact]
836 #else
837         [Fact (Skip = "Does not support strongly typed resources on netcore")]
838 #endif
StronglyTypedResources()839         public void StronglyTypedResources()
840         {
841             GenerateResource t = Utilities.CreateTask(_output);
842             try
843             {
844                 string textFile = Utilities.WriteTestText(null, null);
845                 t.Sources = new ITaskItem[] { new TaskItem(textFile) };
846                 t.StronglyTypedLanguage = "CSharp";
847                 t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
848 
849                 Utilities.ExecuteTask(t);
850 
851                 string resourcesFile = t.OutputResources[0].ItemSpec;
852                 // STR class name should have been generated from the output
853                 string stronglyTypedClassName = Path.GetFileNameWithoutExtension(t.OutputResources[0].ItemSpec);
854                 Assert.Equal(t.StronglyTypedClassName, stronglyTypedClassName);
855                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
856                 resourcesFile = t.FilesWritten[0].ItemSpec;
857                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
858                 Utilities.AssertStateFileWasWritten(t);
859                 // Files written should contain STR class file
860                 string stronglyTypedFileName = Path.ChangeExtension(t.Sources[0].ItemSpec, ".cs");
861                 Assert.Equal(t.FilesWritten[2].ItemSpec, stronglyTypedFileName);
862                 Assert.True(File.Exists(stronglyTypedFileName));
863 
864                 Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", textFile, resourcesFile);
865                 Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 4, textFile);
866 
867                 string typeName = null;
868                 if (t.StronglyTypedNamespace != null)
869                     typeName = t.StronglyTypedNamespace + ".";
870                 else
871                     typeName = "";
872 
873                 typeName += t.StronglyTypedClassName;
874 
875                 Utilities.AssertLogContainsResource(t, "GenerateResource.CreatingSTR", stronglyTypedFileName);
876             }
877             finally
878             {
879                 // Done, so clean up.
880                 File.Delete(t.Sources[0].ItemSpec);
881                 File.Delete(t.StronglyTypedFileName);
882                 foreach (ITaskItem item in t.FilesWritten)
883                 {
884                     if (File.Exists(item.ItemSpec))
885                     {
886                         File.Delete(item.ItemSpec);
887                     }
888                 }
889             }
890         }
891 
892         /// <summary>
893         ///  STR without references yields proper output, message
894         /// </summary>
895 #if FEATURE_CODEDOM
896         [Fact]
897 #else
898         [Fact(Skip = "Does not support strongly typed resources on netcore")]
899 #endif
StronglyTypedResourcesUpToDate()900         public void StronglyTypedResourcesUpToDate()
901         {
902             GenerateResource t = Utilities.CreateTask(_output);
903             GenerateResource t2 = Utilities.CreateTask(_output);
904             try
905             {
906                 string textFile = Utilities.WriteTestText(null, null);
907 
908                 t.Sources = new ITaskItem[] { new TaskItem(textFile) };
909                 t.StronglyTypedLanguage = "CSharp";
910                 t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
911 
912                 Utilities.ExecuteTask(t);
913 
914                 string resourcesFile = t.OutputResources[0].ItemSpec;
915                 // STR class name should have been generated from the output
916                 string stronglyTypedClassName = Path.GetFileNameWithoutExtension(t.OutputResources[0].ItemSpec);
917                 Assert.Equal(t.StronglyTypedClassName, stronglyTypedClassName);
918                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
919                 resourcesFile = t.FilesWritten[0].ItemSpec;
920                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
921 
922                 Utilities.AssertStateFileWasWritten(t);
923                 // Files written should contain STR class file
924                 string stronglyTypedFileName = Path.ChangeExtension(t.Sources[0].ItemSpec, ".cs");
925                 Assert.Equal(t.FilesWritten[2].ItemSpec, stronglyTypedFileName);
926                 Assert.True(File.Exists(stronglyTypedFileName));
927 
928                 Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", textFile, resourcesFile);
929                 Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 4, textFile);
930 
931                 string typeName = null;
932                 if (t.StronglyTypedNamespace != null)
933                     typeName = t.StronglyTypedNamespace + ".";
934                 else
935                     typeName = "";
936 
937                 typeName += t.StronglyTypedClassName;
938 
939                 Utilities.AssertLogContainsResource(t, "GenerateResource.CreatingSTR", stronglyTypedFileName);
940 
941                 // Now that we have done it, do it again to make sure that we don't do
942                 t2.StateFile = new TaskItem(t.StateFile);
943 
944                 t2.Sources = new ITaskItem[] { new TaskItem(textFile) };
945                 t2.StronglyTypedLanguage = "CSharp";
946 
947                 Utilities.ExecuteTask(t2);
948 
949                 Assert.Equal(t2.OutputResources[0].ItemSpec, resourcesFile);
950                 Assert.Equal(t2.FilesWritten[0].ItemSpec, resourcesFile);
951 
952                 Utilities.AssertStateFileWasWritten(t2);
953                 Assert.Equal(t2.FilesWritten[2].ItemSpec, Path.ChangeExtension(t2.Sources[0].ItemSpec, ".cs"));
954             }
955             finally
956             {
957                 // Done, so clean up.
958                 File.Delete(t.Sources[0].ItemSpec);
959                 File.Delete(t.StronglyTypedFileName);
960                 foreach (ITaskItem item in t.FilesWritten)
961                 {
962                     if (File.Exists(item.ItemSpec))
963                     {
964                         File.Delete(item.ItemSpec);
965                     }
966                 }
967                 foreach (ITaskItem item in t2.FilesWritten)
968                 {
969                     if (File.Exists(item.ItemSpec))
970                     {
971                         File.Delete(item.ItemSpec);
972                     }
973                 }
974             }
975         }
976 
977         /// <summary>
978         /// STR class file is out of date, but resources are up to date. Should still generate it.
979         /// </summary>
980 #if FEATURE_CODEDOM
981         [Fact]
982 #else
983         [Fact(Skip = "Does not support strongly typed resources on netcore")]
984 #endif
StronglyTypedResourcesOutOfDate()985         public void StronglyTypedResourcesOutOfDate()
986         {
987             string resxFile = null;
988             string resourcesFile = null;
989             string strFile = null;
990             string stateFile = null;
991 
992             try
993             {
994                 GenerateResource t = Utilities.CreateTask(_output);
995                 resxFile = Utilities.WriteTestResX(false, null, null);
996                 resourcesFile = Utilities.GetTempFileName(".resources");
997                 strFile = Path.ChangeExtension(resourcesFile, ".cs"); // STR filename should be generated from output not input filename
998                 stateFile = Utilities.GetTempFileName(".cache");
999 
1000                 // Make sure the .cs file isn't already there.
1001                 File.Delete(strFile);
1002                 t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
1003                 t.OutputResources = new ITaskItem[] { new TaskItem(resourcesFile) };
1004                 t.StronglyTypedLanguage = "C#";
1005                 t.StateFile = new TaskItem(stateFile);
1006                 Utilities.ExecuteTask(t);
1007 
1008                 // STR class name generated from output resource file name
1009                 string stronglyTypedClassName = Path.GetFileNameWithoutExtension(resourcesFile);
1010                 Assert.Equal(t.StronglyTypedClassName, stronglyTypedClassName);
1011                 resourcesFile = t.OutputResources[0].ItemSpec;
1012                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
1013                 Assert.True(File.Exists(resourcesFile));
1014                 Assert.Equal(t.FilesWritten[2].ItemSpec, strFile);
1015                 Assert.True(File.Exists(strFile));
1016 
1017                 // Repeat. It should not update either file.
1018                 // First move both the timestamps back so they're still up to date,
1019                 // but we'd know if they were updated (this is quicker than sleeping and okay as there's no cache being used)
1020                 Utilities.MoveBackTimestamp(resxFile, 1);
1021                 DateTime strTime = Utilities.MoveBackTimestamp(strFile, 1);
1022                 t = Utilities.CreateTask(_output);
1023                 t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
1024                 t.OutputResources = new ITaskItem[] { new TaskItem(resourcesFile) };
1025                 t.StronglyTypedLanguage = "C#";
1026                 t.StateFile = new TaskItem(stateFile);
1027                 Utilities.ExecuteTask(t);
1028                 Utilities.AssertLogContainsResource(t, "GenerateResource.NothingOutOfDate", "");
1029                 Assert.False(Utilities.FileUpdated(strFile, strTime)); // Was not updated
1030 
1031                 // OK, now delete the STR class file
1032                 File.Delete(strFile);
1033 
1034                 // Repeat. It should recreate the STR class file
1035                 t = Utilities.CreateTask(_output);
1036                 t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
1037                 t.OutputResources = new ITaskItem[] { new TaskItem(resourcesFile) };
1038                 t.StronglyTypedLanguage = "C#";
1039                 t.StateFile = new TaskItem(stateFile);
1040                 Utilities.ExecuteTask(t);
1041                 Assert.True(Utilities.FileUpdated(strFile, strTime)); // Was updated
1042                 Assert.Equal(t.OutputResources[0].ItemSpec, resourcesFile);
1043                 Assert.True(File.Exists(resourcesFile));
1044                 Assert.Equal(t.FilesWritten[2].ItemSpec, strFile);
1045                 Assert.True(File.Exists(strFile));
1046 
1047                 // OK, now delete the STR class file again
1048                 File.Delete(strFile);
1049 
1050                 // Repeat, but specify the filename this time, instead of having it generated from the output resources
1051                 // It should recreate the STR class file again
1052                 t = Utilities.CreateTask(_output);
1053                 t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
1054                 t.OutputResources = new ITaskItem[] { new TaskItem(resourcesFile) };
1055                 t.StronglyTypedLanguage = "C#";
1056                 t.StronglyTypedFileName = strFile;
1057                 Utilities.ExecuteTask(t);
1058                 Assert.True(File.Exists(strFile));
1059             }
1060             finally
1061             {
1062                 if (resxFile != null) File.Delete(resxFile);
1063                 if (resourcesFile != null) File.Delete(resourcesFile);
1064                 if (strFile != null) File.Delete(strFile);
1065             }
1066         }
1067 
1068         /// <summary>
1069         /// Verify STR generation with a specified specific filename
1070         /// </summary>
1071 #if FEATURE_CODEDOM
1072         [Fact]
1073 #else
1074         [Fact(Skip = "Does not support strongly typed resources on netcore")]
1075 #endif
StronglyTypedResourcesWithFilename()1076         public void StronglyTypedResourcesWithFilename()
1077         {
1078             string txtFile = null;
1079             string strFile = null;
1080             string resourcesFile = null;
1081 
1082             try
1083             {
1084                 GenerateResource t = Utilities.CreateTask(_output);
1085 
1086                 txtFile = Utilities.WriteTestText(null, null);
1087                 t.Sources = new ITaskItem[] { new TaskItem(txtFile) };
1088                 t.StronglyTypedLanguage = "CSharp";
1089                 strFile = FileUtilities.GetTemporaryFile();
1090                 t.StronglyTypedFileName = strFile;
1091 
1092                 Utilities.ExecuteTask(t);
1093 
1094                 // Check resources is output
1095                 resourcesFile = t.OutputResources[0].ItemSpec;
1096                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
1097                 Assert.Equal(1, t.OutputResources.Length);
1098                 Assert.Equal(Path.GetExtension(t.FilesWritten[0].ItemSpec), ".resources");
1099                 Assert.True(File.Exists(resourcesFile));
1100 
1101                 // Check STR file is output
1102                 Assert.Equal(t.FilesWritten[1].ItemSpec, strFile);
1103                 Assert.Equal(t.StronglyTypedFileName, strFile);
1104                 Assert.True(File.Exists(strFile));
1105 
1106                 // Check log
1107                 Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", txtFile, t.OutputResources[0].ItemSpec);
1108                 Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 4, txtFile);
1109 
1110                 string typeName = "";
1111                 if (t.StronglyTypedNamespace != null)
1112                 {
1113                     typeName = t.StronglyTypedNamespace + ".";
1114                 }
1115 
1116                 typeName += t.StronglyTypedClassName;
1117                 Utilities.AssertLogContainsResource(t, "GenerateResource.CreatingSTR", strFile);
1118             }
1119             finally
1120             {
1121                 if (txtFile != null) File.Delete(txtFile);
1122                 if (resourcesFile != null) File.Delete(resourcesFile);
1123                 if (strFile != null) File.Delete(strFile);
1124             }
1125         }
1126 
1127         /// <summary>
1128         ///  STR with VB
1129         /// </summary>
1130 #if FEATURE_CODEDOM
1131         [Fact]
1132 #else
1133         [Fact(Skip = "Does not support strongly typed resources on netcore")]
1134 #endif
StronglyTypedResourcesVB()1135         public void StronglyTypedResourcesVB()
1136         {
1137             GenerateResource t = Utilities.CreateTask(_output);
1138             try
1139             {
1140                 string textFile = Utilities.WriteTestText(null, null);
1141                 t.Sources = new ITaskItem[] { new TaskItem(textFile) };
1142                 t.StronglyTypedLanguage = "VB";
1143                 t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
1144 
1145                 Utilities.ExecuteTask(t);
1146 
1147                 // FilesWritten should contain STR class file
1148                 string stronglyTypedFileName = Path.ChangeExtension(t.Sources[0].ItemSpec, ".vb");
1149                 Assert.Equal(t.FilesWritten[2].ItemSpec, stronglyTypedFileName);
1150 
1151                 string resourcesFile = t.OutputResources[0].ItemSpec;
1152                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
1153                 resourcesFile = t.FilesWritten[0].ItemSpec;
1154                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
1155 
1156                 Utilities.AssertStateFileWasWritten(t);
1157                 Assert.True(File.Exists(stronglyTypedFileName));
1158 
1159                 Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", textFile, resourcesFile);
1160                 Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 4, textFile);
1161 
1162                 string typeName = null;
1163                 if (t.StronglyTypedNamespace != null)
1164                     typeName = t.StronglyTypedNamespace + ".";
1165                 else
1166                     typeName = "";
1167 
1168                 typeName += t.StronglyTypedClassName;
1169 
1170                 Utilities.AssertLogContainsResource(t, "GenerateResource.CreatingSTR", stronglyTypedFileName);
1171             }
1172             finally
1173             {
1174                 // Done, so clean up.
1175                 File.Delete(t.Sources[0].ItemSpec);
1176                 File.Delete(t.StronglyTypedFileName);
1177                 foreach (ITaskItem item in t.FilesWritten)
1178                 {
1179                     if (File.Exists(item.ItemSpec))
1180                     {
1181                         File.Delete(item.ItemSpec);
1182                     }
1183                 }
1184             }
1185         }
1186 
1187         /// <summary>
1188         ///  STR namespace can be empty
1189         /// </summary>
1190 #if FEATURE_CODEDOM
1191         [Fact]
1192 #else
1193         [Fact(Skip = "Does not support strongly typed resources on netcore")]
1194 #endif
StronglyTypedResourcesWithoutNamespaceOrClassOrFilename()1195         public void StronglyTypedResourcesWithoutNamespaceOrClassOrFilename()
1196         {
1197             GenerateResource t = Utilities.CreateTask(_output);
1198             try
1199             {
1200                 string textFile = Utilities.WriteTestText(null, null);
1201                 t.Sources = new ITaskItem[] { new TaskItem(textFile) };
1202                 t.StronglyTypedLanguage = "CSharp";
1203                 t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
1204 
1205                 Utilities.ExecuteTask(t);
1206 
1207                 string resourcesFile = t.OutputResources[0].ItemSpec;
1208                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
1209                 resourcesFile = t.FilesWritten[0].ItemSpec;
1210                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
1211 
1212                 Utilities.AssertStateFileWasWritten(t);
1213 
1214                 // Should have defaulted the STR filename to the bare output resource name + ".cs"
1215                 string STRfile = Path.ChangeExtension(t.Sources[0].ItemSpec, ".cs");
1216                 Assert.Equal(t.StronglyTypedFileName, STRfile);
1217                 Assert.True(File.Exists(STRfile));
1218 
1219                 // Should have defaulted the class name to the bare output resource name
1220                 Assert.Equal(t.StronglyTypedClassName, Path.GetFileNameWithoutExtension(t.OutputResources[0].ItemSpec));
1221 
1222                 Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", textFile, resourcesFile);
1223                 Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 4, textFile);
1224                 Utilities.AssertLogContainsResource(t, "GenerateResource.CreatingSTR", t.StronglyTypedFileName);
1225 
1226                 // Should not have used a namespace
1227                 Assert.False(File.ReadAllText(t.StronglyTypedFileName).Contains("namespace"));
1228             }
1229             finally
1230             {
1231                 // Done, so clean up.
1232                 File.Delete(t.Sources[0].ItemSpec);
1233                 File.Delete(t.StronglyTypedFileName);
1234                 foreach (ITaskItem item in t.FilesWritten)
1235                 {
1236                     if (File.Exists(item.ItemSpec))
1237                     {
1238                         File.Delete(item.ItemSpec);
1239                     }
1240                 }
1241             }
1242         }
1243 
1244         /// <summary>
1245         ///  STR with resource namespace yields proper output, message (CS)
1246         /// </summary>
1247 #if FEATURE_CODEDOM
1248         [Fact]
1249 #else
1250         [Fact(Skip = "Does not support strongly typed resources on netcore")]
1251 #endif
STRWithResourcesNamespaceCS()1252         public void STRWithResourcesNamespaceCS()
1253         {
1254             Utilities.STRNamespaceTestHelper("CSharp", "MyResourcesNamespace", null, _output);
1255         }
1256 
1257         /// <summary>
1258         ///  STR with resource namespace yields proper output, message (VB)
1259         /// </summary>
1260 #if FEATURE_CODEDOM
1261         [Fact]
1262 #else
1263         [Fact(Skip = "Does not support strongly typed resources on netcore")]
1264 #endif
STRWithResourcesNamespaceVB()1265         public void STRWithResourcesNamespaceVB()
1266         {
1267             Utilities.STRNamespaceTestHelper("VB", "MyResourcesNamespace", null, _output);
1268         }
1269 
1270         /// <summary>
1271         ///  STR with resource namespace and STR namespace yields proper output, message (CS)
1272         /// </summary>
1273 #if FEATURE_CODEDOM
1274         [Fact]
1275 #else
1276         [Fact(Skip = "Does not support strongly typed resources on netcore")]
1277 #endif
STRWithResourcesNamespaceAndSTRNamespaceCS()1278         public void STRWithResourcesNamespaceAndSTRNamespaceCS()
1279         {
1280             Utilities.STRNamespaceTestHelper("CSharp", "MyResourcesNamespace", "MySTClassNamespace", _output);
1281         }
1282 
1283         /// <summary>
1284         ///  STR with resource namespace and STR namespace yields proper output, message (CS)
1285         /// </summary>
1286 #if FEATURE_CODEDOM
1287         [Fact]
1288 #else
1289         [Fact(Skip = "Does not support strongly typed resources on netcore")]
1290 #endif
STRWithResourcesNamespaceAndSTRNamespaceVB()1291         public void STRWithResourcesNamespaceAndSTRNamespaceVB()
1292         {
1293             Utilities.STRNamespaceTestHelper("VB", "MyResourcesNamespace", "MySTClassNamespace", _output);
1294         }
1295     }
1296 
1297     sealed public class TransformationErrors
1298     {
1299         private readonly ITestOutputHelper _output;
1300 
TransformationErrors(ITestOutputHelper output)1301         public TransformationErrors(ITestOutputHelper output)
1302         {
1303             _output = output;
1304         }
1305 
1306         /// <summary>
1307         ///  Text input failures, no name, no '=', 'strings' token, invalid token, invalid escape
1308         /// </summary>
1309         [Fact]
TextToResourcesBadFormat()1310         public void TextToResourcesBadFormat()
1311         {
1312             // This WriteLine is a hack.  On a slow machine, the Tasks unittest fails because remoting
1313             // times out the object used for remoting console writes.  Adding a write in the middle of
1314             // keeps remoting from timing out the object.
1315             Console.WriteLine("Performing TextToResourcesBadFormat() test");
1316 
1317             // The first string in each row is passed into the text block that's created in the file
1318             // The second string is a fragment of the expected error message
1319             string[][] tests = new string[][] {
1320                 // invalid token in file, "unsupported square bracket keyword"
1321                 new string[] {   "[goober]", "MSB3563" },
1322                 // no '=', "resource line without an equals sign"
1323                 new string[] {   "abcdefaghha", "MSB3564" },
1324                 // no name, "resource line without a name"
1325                 new string[] {   "=abced", "MSB3565" },
1326                 // invalid escape, "unsupported or invalid escape character"
1327                 new string[] {   "abc=de\\efght", "MSB3566" },
1328                 // another invalid escape, this one more serious, "unsupported or invalid escape character"
1329                 new string[] {   @"foo=\ujjjjbar", "MSB3569"},
1330             };
1331 
1332             GenerateResource t = null;
1333             string textFile = null;
1334 
1335             foreach (string[] test in tests)
1336             {
1337                 t = Utilities.CreateTask(_output);
1338 
1339                 textFile = Utilities.WriteTestText(null, test[0]);
1340                 t.Sources = new ITaskItem[] { new TaskItem(textFile) };
1341                 t.Execute();
1342                 Utilities.AssertLogContains(t, test[1]);
1343 
1344                 // Done, so clean up.
1345                 File.Delete(t.Sources[0].ItemSpec);
1346                 foreach (ITaskItem item in t.FilesWritten)
1347                     File.Delete(item.ItemSpec);
1348             }
1349 
1350             // text file uses the strings token; since it's only a warning we have to have special asserts
1351             t = Utilities.CreateTask(_output);
1352 
1353             textFile = Utilities.WriteTestText(null, "[strings]");
1354             t.Sources = new ITaskItem[] { new TaskItem(textFile) };
1355             bool success = t.Execute();
1356             // Task should have succeeded (it was just a warning)
1357             Assert.True(success);
1358             // warning that 'strings' is an obsolete tag
1359             Utilities.AssertLogContains(t, "MSB3562");
1360 
1361             // Done, so clean up.
1362             File.Delete(t.Sources[0].ItemSpec);
1363             foreach (ITaskItem item in t.FilesWritten)
1364                 File.Delete(item.ItemSpec);
1365         }
1366 
1367         /// <summary>
1368         ///  Cause failures in ResXResourceReader
1369         /// </summary>
1370         [Fact]
FailedResXReader()1371         public void FailedResXReader()
1372         {
1373             string resxFile1 = null;
1374             string resxFile2 = null;
1375             string resourcesFile1 = null;
1376             string resourcesFile2 = null;
1377 
1378             try
1379             {
1380                 GenerateResource t = Utilities.CreateTask(_output);
1381                 t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
1382 
1383                 // Invalid one
1384                 resxFile1 = Utilities.WriteTestResX(false, null, "  <data name='ack!'>>>>>>\xd\xa    <valueAB>Assembly</value>\xd\xa  </data>\xd\xa", false);
1385                 // Also include a valid one. It should still get processed
1386                 resxFile2 = Utilities.WriteTestResX(false, null, null);
1387                 t.Sources = new ITaskItem[] { new TaskItem(resxFile1), new TaskItem(resxFile2) };
1388                 resourcesFile1 = Path.ChangeExtension(resxFile1, ".resources");
1389                 resourcesFile2 = Path.ChangeExtension(resxFile2, ".resources");
1390                 File.Delete(resourcesFile1);
1391                 File.Delete(resourcesFile2);
1392                 bool success = t.Execute();
1393                 // Task should have failed
1394                 Assert.False(success);
1395 
1396 #if FEATURE_RESGENCACHE
1397                 Utilities.AssertStateFileWasWritten(t);
1398 #endif
1399                 // Should not have created an output for the invalid resx
1400                 // Should have created the other file
1401                 Assert.False(File.Exists(resourcesFile1));
1402                 Assert.Equal(t.OutputResources[0].ItemSpec, resourcesFile2);
1403                 Assert.Equal(1, t.OutputResources.Length);
1404                 Assert.Equal(t.FilesWritten[0].ItemSpec, resourcesFile2);
1405                 Assert.True(File.Exists(resourcesFile2));
1406 
1407                 // "error in resource file" with exception from the framework
1408                 Utilities.AssertLogContains(t, "MSB3103");
1409             }
1410             finally
1411             {
1412                 if (null != resxFile1) File.Delete(resxFile1);
1413                 if (null != resxFile2) File.Delete(resxFile2);
1414                 if (null != resourcesFile1) File.Delete(resourcesFile1);
1415                 if (null != resourcesFile2) File.Delete(resourcesFile2);
1416             }
1417         }
1418 
1419         /// <summary>
1420         ///  Cause failures in ResXResourceReader, different codepath
1421         /// </summary>
1422         [Fact]
FailedResXReaderWithAllOutputResourcesSpecified()1423         public void FailedResXReaderWithAllOutputResourcesSpecified()
1424         {
1425             string resxFile1 = null;
1426             string resxFile2 = null;
1427             string resourcesFile1 = null;
1428             string resourcesFile2 = null;
1429 
1430             try
1431             {
1432                 GenerateResource t = Utilities.CreateTask(_output);
1433                 t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
1434 
1435                 // Invalid one
1436                 resxFile1 = Utilities.WriteTestResX(false, null, "  <data name='ack!'>>>>>>\xd\xa    <valueAB>Assembly</value>\xd\xa  </data>\xd\xa", false);
1437                 // Also include a valid one. It should still get processed
1438                 resxFile2 = Utilities.WriteTestResX(false, null, null);
1439                 t.Sources = new ITaskItem[] { new TaskItem(resxFile1), new TaskItem(resxFile2) };
1440                 resourcesFile1 = Path.ChangeExtension(resxFile1, ".resources");
1441                 resourcesFile2 = Path.ChangeExtension(resxFile2, ".resources");
1442                 File.Delete(resourcesFile1);
1443                 File.Delete(resourcesFile2);
1444                 t.OutputResources = new ITaskItem[] { new TaskItem(resourcesFile1), new TaskItem(resourcesFile2) };
1445 
1446                 bool success = t.Execute();
1447                 // Task should have failed
1448                 Assert.False(success);
1449 
1450 #if FEATURE_RESGENCACHE
1451                 Utilities.AssertStateFileWasWritten(t);
1452 #endif
1453 
1454                 // Should not have created an output for the invalid resx
1455                 // Should have created the other file
1456                 Assert.False(File.Exists(resourcesFile1));
1457                 Assert.Equal(t.OutputResources[0].ItemSpec, resourcesFile2);
1458                 Assert.Equal(1, t.OutputResources.Length);
1459                 Assert.Equal(t.FilesWritten[0].ItemSpec, resourcesFile2);
1460                 Assert.True(File.Exists(resourcesFile2));
1461 
1462                 // "error in resource file" with exception from the framework
1463                 Utilities.AssertLogContains(t, "MSB3103");
1464 
1465 #if false       // we can't do this because FX strings ARE localized -- VSW#455956
1466                 // This is a literal because it comes from the ResX parser in the framework
1467                 Utilities.AssertLogContains(t, "'valueAB' start tag on line 18 does not match the end tag of 'value'");
1468 #endif
1469                 // so just look for the unlocalizable portions
1470                 Utilities.AssertLogContains(t, "valueAB");
1471                 Utilities.AssertLogContains(t, "value");
1472             }
1473             finally
1474             {
1475                 if (null != resxFile1) File.Delete(resxFile1);
1476                 if (null != resxFile2) File.Delete(resxFile2);
1477                 if (null != resourcesFile1) File.Delete(resourcesFile1);
1478                 if (null != resourcesFile2) File.Delete(resourcesFile2);
1479             }
1480         }
1481 
1482         /// <summary>
1483         ///  Duplicate resource names
1484         /// </summary>
1485         [Fact]
DuplicateResourceNames()1486         public void DuplicateResourceNames()
1487         {
1488             GenerateResource t = Utilities.CreateTask(_output);
1489 
1490             string textFile = Utilities.WriteTestText(null, "Marley=some guy from Jamaica");
1491             t.Sources = new ITaskItem[] { new TaskItem(textFile) };
1492             bool success = t.Execute();
1493             // Task should have succeeded (it was just a warning)
1494             Assert.True(success);
1495 
1496             // "duplicate resource name"
1497             Utilities.AssertLogContains(t, "MSB3568");
1498 
1499             // Done, so clean up.
1500             File.Delete(t.Sources[0].ItemSpec);
1501             foreach (ITaskItem item in t.FilesWritten)
1502                 File.Delete(item.ItemSpec);
1503         }
1504 
1505         /// <summary>
1506         ///  Non-string resource with text output
1507         /// </summary>
1508 #if RUNTIME_TYPE_NETCORE
1509         [Fact (Skip = "https://github.com/Microsoft/msbuild/issues/308")]
1510 #else
1511         [Fact]
1512 #endif
1513         [Trait("Category", "mono-osx-failing")] // libgdiplus not found
UnsupportedTextType()1514         public void UnsupportedTextType()
1515         {
1516             string bitmap = Utilities.CreateWorldsSmallestBitmap();
1517             string resxFile = Utilities.WriteTestResX(false, bitmap, null, false);
1518 
1519             GenerateResource t = Utilities.CreateTask(_output);
1520             t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
1521             t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
1522             t.OutputResources = new ITaskItem[] { new TaskItem(Path.ChangeExtension(resxFile, ".txt")) };
1523             bool success = t.Execute();
1524             // Task should have failed
1525             Assert.False(success);
1526             // "only strings can be written to a .txt file"
1527             Utilities.AssertLogContains(t, "MSB3556");
1528 
1529             // Done, so clean up.
1530             File.Delete(t.Sources[0].ItemSpec);
1531             File.Delete(bitmap);
1532             foreach (ITaskItem item in t.FilesWritten)
1533                 File.Delete(item.ItemSpec);
1534         }
1535 
1536         /// <summary>
1537         /// Can't write the statefile
1538         /// </summary>
1539         [Fact]
InvalidStateFile()1540         public void InvalidStateFile()
1541         {
1542             string resxFile = null;
1543             string resourcesFile = null;
1544 
1545             try
1546             {
1547                 GenerateResource t = Utilities.CreateTask(_output);
1548                 resxFile = Utilities.WriteTestResX(false, null, null);
1549                 t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
1550                 t.StateFile = new TaskItem("||//invalid filename||");
1551 
1552                 // Should still succeed
1553                 Assert.True(t.Execute());
1554 
1555                 resourcesFile = t.OutputResources[0].ItemSpec;
1556                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
1557                 Assert.Equal(t.FilesWritten[0].ItemSpec, t.OutputResources[0].ItemSpec);
1558 
1559                 Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", resxFile, resourcesFile);
1560                 Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 1, resxFile);
1561             }
1562             finally
1563             {
1564                 if (null != resxFile) File.Delete(resxFile);
1565                 if (null != resourcesFile) File.Delete(resourcesFile);
1566             }
1567         }
1568 
1569         /// <summary>
1570         ///  Cause failures in ResourceReader
1571         /// </summary>
1572 #if RUNTIME_TYPE_NETCORE
1573         [Fact (Skip = "https://github.com/Microsoft/msbuild/issues/308")]
1574 #else
1575         [Fact]
1576 #endif
FailedResourceReader()1577         public void FailedResourceReader()
1578         {
1579             GenerateResource t = Utilities.CreateTask(_output);
1580             t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
1581 
1582             // to cause a failure, we're going to transform a bad .resources file to a .resx
1583             // the simplest thing is to create a .resx, but call it .resources
1584             string resxFile = Utilities.WriteTestResX(false, null, null);
1585             string resourcesFile = Path.ChangeExtension(resxFile, ".resources");
1586             File.Move(resxFile, resourcesFile);
1587             t.Sources = new ITaskItem[] { new TaskItem(resourcesFile) };
1588             t.OutputResources = new ITaskItem[] { new TaskItem(resxFile) };
1589 
1590             bool success = t.Execute();
1591             // Task should have failed
1592             Assert.False(success);
1593 
1594             // "error in resource file" with exception from the framework
1595             Utilities.AssertLogContains(t, "MSB3103");
1596 
1597             // Done, so clean up.
1598             File.Delete(t.Sources[0].ItemSpec);
1599             foreach (ITaskItem item in t.FilesWritten)
1600                 File.Delete(item.ItemSpec);
1601         }
1602 
1603         /// <summary>
1604         ///  Invalid STR Class name
1605         /// </summary>
1606 #if FEATURE_CODEDOM
1607         [Fact]
1608 #else
1609         [Fact(Skip = "Does not support strongly typed resources on netcore")]
1610 #endif
FailedSTRProperty()1611         public void FailedSTRProperty()
1612         {
1613             GenerateResource t = Utilities.CreateTask(_output);
1614             t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
1615 
1616             string textFile = Utilities.WriteTestText(null, "object=some string");
1617             string resourcesFile = Path.ChangeExtension(textFile, ".resources");
1618 
1619             t.Sources = new ITaskItem[] { new TaskItem(textFile) };
1620             t.StronglyTypedLanguage = "CSharp";
1621             // Invalid class name
1622             t.StronglyTypedClassName = "~!@#$%^&amp;*(";
1623 
1624             bool success = t.Execute();
1625             // Task should have failed
1626             Assert.False(success);
1627 
1628             Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", textFile, resourcesFile);
1629             Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 5, textFile);
1630             Utilities.AssertLogContainsResource(t, "GenerateResource.CreatingSTR", t.StronglyTypedFileName);
1631             Utilities.AssertLogContains(t, "MSB3570");
1632             Utilities.AssertLogContains(t, t.StronglyTypedFileName);
1633 
1634             // Done, so clean up.
1635             File.Delete(t.Sources[0].ItemSpec);
1636             File.Delete(t.StronglyTypedFileName);
1637             foreach (ITaskItem item in t.FilesWritten)
1638                 File.Delete(item.ItemSpec);
1639         }
1640 
1641         /// <summary>
1642         /// Reference passed in that can't be loaded should error
1643         /// </summary>
1644 #if RUNTIME_TYPE_NETCORE
1645         [Fact (Skip = "https://github.com/Microsoft/msbuild/issues/308")]
1646 #else
1647         [Fact]
1648 #endif
InvalidReference()1649         public void InvalidReference()
1650         {
1651             string txtFile = null;
1652 
1653             try
1654             {
1655                 GenerateResource t = Utilities.CreateTask(_output);
1656 
1657                 // Create resx with invalid ref "INVALID"
1658                 txtFile = Utilities.WriteTestResX(false, null, null, true /*data with invalid type*/);
1659                 string resourcesFile = Path.ChangeExtension(txtFile, ".resources");
1660                 File.Delete(resourcesFile);
1661                 t.Sources = new ITaskItem[] { new TaskItem(txtFile) };
1662                 t.References = new TaskItem[] { new TaskItem("INVALID") };
1663 
1664                 bool result = t.Execute();
1665                 // Task should have failed
1666                 Assert.False(result);
1667 
1668                 // Should have not written any files
1669                 Assert.True(t.FilesWritten != null && t.FilesWritten.Length == 0);
1670                 Assert.False(File.Exists(resourcesFile));
1671             }
1672             finally
1673             {
1674                 if (txtFile != null) File.Delete(txtFile);
1675             }
1676         }
1677     }
1678 
1679     sealed public class PropertyHandling
1680     {
1681         private readonly ITestOutputHelper _output;
1682 
PropertyHandling(ITestOutputHelper output)1683         public PropertyHandling(ITestOutputHelper output)
1684         {
1685             _output = output;
1686         }
1687 
1688         /// <summary>
1689         ///  Sources attributes are copied to given OutputResources
1690         /// </summary>
1691         [Fact]
AttributeForwarding()1692         public void AttributeForwarding()
1693         {
1694             // This WriteLine is a hack.  On a slow machine, the Tasks unittest fails because remoting
1695             // times out the object used for remoting console writes.  Adding a write in the middle of
1696             // keeps remoting from timing out the object.
1697             Console.WriteLine("Performing AttributeForwarding() test");
1698 
1699             GenerateResource t = Utilities.CreateTask(_output);
1700 
1701             string resxFile = Utilities.WriteTestResX(false, null, null);
1702             ITaskItem i = new TaskItem(resxFile);
1703             i.SetMetadata("Locale", "en-GB");
1704             t.Sources = new ITaskItem[] { i };
1705 
1706             ITaskItem o = new TaskItem("MyAlternateResource.resources");
1707             o.SetMetadata("Locale", "fr");
1708             o.SetMetadata("Flavor", "Pumpkin");
1709             t.OutputResources = new ITaskItem[] { o };
1710 
1711             Utilities.ExecuteTask(t);
1712 
1713             // Locale was forward from source item and should overwrite the 'fr'
1714             // locale that the output item originally had.
1715             Assert.Equal("fr", t.OutputResources[0].GetMetadata("Locale"));
1716 
1717             // Output ItemSpec should not be overwritten.
1718             Assert.Equal("MyAlternateResource.resources", t.OutputResources[0].ItemSpec);
1719 
1720             // Attributes not on Sources should be left untouched.
1721             Assert.Equal("Pumpkin", t.OutputResources[0].GetMetadata("Flavor"));
1722 
1723             // Done, so clean up.
1724             File.Delete(t.Sources[0].ItemSpec);
1725             foreach (ITaskItem item in t.FilesWritten)
1726                 File.Delete(item.ItemSpec);
1727         }
1728 
1729         /// <summary>
1730         ///  Sources attributes copied to computed OutputResources
1731         /// </summary>
1732         [Fact]
AttributeForwardingOnEmptyOutputs()1733         public void AttributeForwardingOnEmptyOutputs()
1734         {
1735             GenerateResource t = Utilities.CreateTask(_output);
1736 
1737             string resxFile = Utilities.WriteTestResX(false, null, null);
1738             ITaskItem i = new TaskItem(resxFile);
1739             i.SetMetadata("Locale", "en-GB");
1740             t.Sources = new ITaskItem[] { i };
1741 
1742             Utilities.ExecuteTask(t);
1743 
1744             // Output ItemSpec should be computed from input
1745             string resourcesFile = Path.ChangeExtension(resxFile, ".resources");
1746             Assert.Equal(resourcesFile, t.OutputResources[0].ItemSpec);
1747 
1748             // Attribute from source should be copied to output
1749             Assert.Equal("en-GB", t.OutputResources[0].GetMetadata("Locale"));
1750 
1751             // Done, so clean up.
1752             File.Delete(t.Sources[0].ItemSpec);
1753             foreach (ITaskItem item in t.FilesWritten)
1754                 File.Delete(item.ItemSpec);
1755         }
1756 
1757         /// <summary>
1758         ///  OutputFiles used for output, and also are synthesized if not set on input
1759         /// </summary>
1760         [Fact]
OutputFilesNotSpecified()1761         public void OutputFilesNotSpecified()
1762         {
1763             GenerateResource t = Utilities.CreateTask(_output);
1764 
1765             t.Sources = new ITaskItem[] {
1766                 new TaskItem( Utilities.WriteTestResX(false, null, null) ),
1767                 new TaskItem( Utilities.WriteTestResX(false, null, null) ),
1768                 new TaskItem( Utilities.WriteTestResX(false, null, null) ),
1769                 new TaskItem( Utilities.WriteTestResX(false, null, null)),
1770             };
1771 
1772             Utilities.ExecuteTask(t);
1773 
1774             // Output ItemSpec should be computed from input
1775             for (int i = 0; i < t.Sources.Length; i++)
1776             {
1777                 string outputFile = Path.ChangeExtension(t.Sources[i].ItemSpec, ".resources");
1778                 Assert.Equal(outputFile, t.OutputResources[i].ItemSpec);
1779             }
1780 
1781             // Done, so clean up.
1782             foreach (ITaskItem item in t.Sources)
1783                 File.Delete(item.ItemSpec);
1784             foreach (ITaskItem item in t.FilesWritten)
1785                 File.Delete(item.ItemSpec);
1786         }
1787 
1788         /// <summary>
1789         ///  FilesWritten contains OutputResources + StateFile
1790         /// </summary>
1791         [Fact]
FilesWrittenSet()1792         public void FilesWrittenSet()
1793         {
1794             GenerateResource t = Utilities.CreateTask(_output);
1795 
1796             t.Sources = new ITaskItem[] {
1797                 new TaskItem( Utilities.WriteTestResX(false, null, null) ),
1798                 new TaskItem( Utilities.WriteTestResX(false, null, null) ),
1799                 new TaskItem( Utilities.WriteTestResX(false, null, null) ),
1800                 new TaskItem( Utilities.WriteTestResX(false, null, null)),
1801             };
1802 
1803             t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
1804 
1805             Utilities.ExecuteTask(t);
1806 
1807             int i = 0;
1808 
1809             // should be four files written, not including the tlogs
1810             for (i = 0; i < 4; i++)
1811             {
1812                 Assert.Equal(t.FilesWritten[i].ItemSpec, t.OutputResources[i].ItemSpec);
1813                 Assert.True(File.Exists(t.FilesWritten[i].ItemSpec));
1814             }
1815 
1816 #if FEATURE_RESGENCACHE
1817             Utilities.AssertStateFileWasWritten(t);
1818 #endif
1819 
1820             // Done, so clean up.
1821             File.Delete(t.StateFile.ItemSpec);
1822             foreach (ITaskItem item in t.Sources)
1823             {
1824                 File.Delete(item.ItemSpec);
1825             }
1826             foreach (ITaskItem item in t.FilesWritten)
1827             {
1828                 File.Delete(item.ItemSpec);
1829             }
1830         }
1831 
1832         /// <summary>
1833         ///  Resource transformation fails on 3rd of 4 inputs, inputs 1 & 2 & 4 are in outputs and fileswritten.
1834         /// </summary>
1835         [Fact]
OutputFilesPartialInputs()1836         public void OutputFilesPartialInputs()
1837         {
1838             GenerateResource t = Utilities.CreateTask(_output);
1839 
1840             try
1841             {
1842                 t.Sources = new ITaskItem[] {
1843                     new TaskItem( Utilities.WriteTestText(null, null) ),
1844                     new TaskItem( Utilities.WriteTestText(null, null) ),
1845                     new TaskItem( Utilities.WriteTestText("goober", null) ),
1846                     new TaskItem( Utilities.WriteTestText(null, null)),
1847                 };
1848 
1849                 foreach (ITaskItem taskItem in t.Sources)
1850                 {
1851                     File.Delete(Path.ChangeExtension(taskItem.ItemSpec, ".resources"));
1852                 }
1853 
1854                 t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
1855 
1856                 bool success = t.Execute();
1857                 // Task should have failed
1858                 Assert.False(success);
1859 
1860                 string outputFile = Path.ChangeExtension(t.Sources[0].ItemSpec, ".resources");
1861                 Assert.Equal(outputFile, t.OutputResources[0].ItemSpec);
1862                 Assert.True(File.Exists(t.OutputResources[0].ItemSpec));
1863                 outputFile = Path.ChangeExtension(t.Sources[1].ItemSpec, ".resources");
1864                 Assert.Equal(outputFile, t.OutputResources[1].ItemSpec);
1865                 Assert.True(File.Exists(t.OutputResources[1].ItemSpec));
1866                 // Sources[2] should NOT have been converted and should not be in OutputResources
1867                 outputFile = Path.ChangeExtension(t.Sources[2].ItemSpec, ".resources");
1868                 Assert.False(File.Exists(outputFile));
1869                 // Sources[3] should have been converted
1870                 outputFile = Path.ChangeExtension(t.Sources[3].ItemSpec, ".resources");
1871                 Assert.Equal(outputFile, t.OutputResources[2].ItemSpec);
1872                 Assert.True(File.Exists(t.OutputResources[2].ItemSpec));
1873 
1874                 // FilesWritten should contain only the 3 successfully output .resources and the cache
1875                 Assert.Equal(t.FilesWritten[0].ItemSpec, Path.ChangeExtension(t.Sources[0].ItemSpec, ".resources"));
1876                 Assert.Equal(t.FilesWritten[1].ItemSpec, Path.ChangeExtension(t.Sources[1].ItemSpec, ".resources"));
1877                 Assert.Equal(t.FilesWritten[2].ItemSpec, Path.ChangeExtension(t.Sources[3].ItemSpec, ".resources"));
1878 
1879 #if FEATURE_RESGENCACHE
1880                 Utilities.AssertStateFileWasWritten(t);
1881 #endif
1882 
1883                 // Make sure there was an error on the second resource
1884                 // "unsupported square bracket keyword"
1885                 Utilities.AssertLogContains(t, "MSB3563");
1886                 Utilities.AssertLogContains(t, "[goober]");
1887             }
1888             finally
1889             {
1890                 // Done, so clean up.
1891                 foreach (ITaskItem item in t.Sources)
1892                 {
1893                     File.Delete(item.ItemSpec);
1894                 }
1895                 foreach (ITaskItem item in t.FilesWritten)
1896                 {
1897                     if (File.Exists(item.ItemSpec))
1898                     {
1899                         File.Delete(item.ItemSpec);
1900                     }
1901                 }
1902             }
1903         }
1904 
1905         /// <summary>
1906         ///  STR class name derived from output file transformation
1907         /// </summary>
1908 #if FEATURE_CODEDOM
1909         [Fact]
1910 #else
1911         [Fact(Skip = "Does not support strongly typed resources on netcore")]
1912 #endif
1913         [Trait("Category", "mono-osx-failing")]
1914         [Trait("Category", "mono-windows-failing")]
StronglyTypedClassName()1915         public void StronglyTypedClassName()
1916         {
1917             GenerateResource t = Utilities.CreateTask(_output);
1918 
1919             try
1920             {
1921                 t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
1922 
1923                 string textFile = Utilities.WriteTestText(null, null);
1924                 t.Sources = new ITaskItem[] { new TaskItem(textFile) };
1925                 t.StronglyTypedLanguage = "CSharp";
1926                 t.StronglyTypedFileName = "somefile.cs";
1927                 t.PublicClass = true;
1928                 t.OutputResources = new ITaskItem[] { new TaskItem("somefile.resources") };
1929 
1930                 Utilities.ExecuteTask(t);
1931 
1932                 Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", textFile, t.OutputResources[0].ItemSpec);
1933                 Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 4, textFile);
1934                 Utilities.AssertLogContainsResource(t, "GenerateResource.CreatingSTR", t.StronglyTypedFileName);
1935                 Assert.Equal(t.StronglyTypedClassName, Path.GetFileNameWithoutExtension(t.StronglyTypedFileName));
1936                 // Verify class was public, as we specified
1937                 Assert.True(File.ReadAllText(t.StronglyTypedFileName).Contains("public class " + t.StronglyTypedClassName));
1938 
1939                 Utilities.AssertStateFileWasWritten(t);
1940             }
1941             finally
1942             {
1943                 // Done, so clean up.
1944                 File.Delete(t.Sources[0].ItemSpec);
1945                 File.Delete(t.StronglyTypedFileName);
1946                 foreach (ITaskItem item in t.FilesWritten)
1947                 {
1948                     if (File.Exists(item.ItemSpec))
1949                     {
1950                         File.Delete(item.ItemSpec);
1951                     }
1952                 }
1953             }
1954         }
1955 
1956         /// <summary>
1957         ///  STR class file name derived from class name transformation
1958         /// </summary>
1959 #if FEATURE_CODEDOM
1960         [Fact]
1961 #else
1962         [Fact(Skip = "Does not support strongly typed resources on netcore")]
1963 #endif
1964         [Trait("Category", "mono-osx-failing")]
1965         [Trait("Category", "mono-windows-failing")]
StronglyTypedFileName()1966         public void StronglyTypedFileName()
1967         {
1968             GenerateResource t = Utilities.CreateTask(_output);
1969 
1970             try
1971             {
1972                 string textFile = Utilities.WriteTestText(null, null);
1973                 t.Sources = new ITaskItem[] { new TaskItem(textFile) };
1974                 t.StronglyTypedLanguage = "CSharp";
1975                 File.Delete(Path.ChangeExtension(t.Sources[0].ItemSpec, ".cs"));
1976                 t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
1977 
1978                 Utilities.ExecuteTask(t);
1979 
1980                 Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", textFile, t.OutputResources[0].ItemSpec);
1981                 Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 4, textFile);
1982                 Utilities.AssertLogContainsResource(t, "GenerateResource.CreatingSTR", t.StronglyTypedFileName);
1983 
1984                 Assert.Equal(t.StronglyTypedFileName, Path.ChangeExtension(t.Sources[0].ItemSpec, ".cs"));
1985                 Assert.True(File.Exists(t.StronglyTypedFileName));
1986 
1987                 Utilities.AssertStateFileWasWritten(t);
1988 
1989                 // Verify class was internal, since we didn't specify a preference
1990                 Assert.True(File.ReadAllText(t.StronglyTypedFileName).Contains("internal class " + t.StronglyTypedClassName));
1991             }
1992             finally
1993             {
1994                 // Done, so clean up.
1995                 File.Delete(t.Sources[0].ItemSpec);
1996                 File.Delete(t.StronglyTypedFileName);
1997 
1998                 foreach (ITaskItem item in t.FilesWritten)
1999                 {
2000                     if (File.Exists(item.ItemSpec))
2001                     {
2002                         File.Delete(item.ItemSpec);
2003                     }
2004                 }
2005             }
2006         }
2007     }
2008 
2009     sealed public class PropertyErrors
2010     {
2011         private readonly ITestOutputHelper _output;
2012 
PropertyErrors(ITestOutputHelper output)2013         public PropertyErrors(ITestOutputHelper output)
2014         {
2015             _output = output;
2016         }
2017 
2018         /// <summary>
2019         ///  Empty Sources yields message, success
2020         /// </summary>
2021         [Fact]
EmptySources()2022         public void EmptySources()
2023         {
2024             // This WriteLine is a hack.  On a slow machine, the Tasks unittest fails because remoting
2025             // times out the object used for remoting console writes.  Adding a write in the middle of
2026             // keeps remoting from timing out the object.
2027             Console.WriteLine("Performing EmptySources() test");
2028 
2029             GenerateResource t = Utilities.CreateTask(_output);
2030             Utilities.ExecuteTask(t);
2031             Utilities.AssertLogContainsResource(t, "GenerateResource.NoSources", "");
2032             if (t.FilesWritten != null)
2033             {
2034                 foreach (ITaskItem item in t.FilesWritten)
2035                 {
2036                     if (item.ItemSpec != null)
2037                         File.Delete(item.ItemSpec);
2038                 }
2039             }
2040         }
2041 
2042         /// <summary>
2043         ///  References with invalid assemblies yields warning
2044         /// </summary>
2045         [Fact]
ReferencesToBadAssemblies()2046         public void ReferencesToBadAssemblies()
2047         {
2048             GenerateResource t = Utilities.CreateTask(_output);
2049             string textFile = null;
2050 
2051             try
2052             {
2053                 textFile = Utilities.WriteTestText(null, null);
2054                 t.Sources = new ITaskItem[] { new TaskItem(textFile) };
2055                 t.References = new ITaskItem[] { new TaskItem("some non-existent DLL name goes here.dll") };
2056                 bool success = t.Execute();
2057                 // Task should have succeeded, because the bad reference was never consumed.
2058                 Assert.True(success);
2059             }
2060             finally
2061             {
2062                 // Done, so clean up.
2063                 if (textFile != null)
2064                 {
2065                     File.Delete(textFile);
2066                     File.Delete(Path.ChangeExtension(textFile, ".resources"));
2067                 }
2068             }
2069         }
2070 
2071         /// <summary>
2072         ///  Source item not found
2073         /// </summary>
2074         [Fact]
SourceItemMissing()2075         public void SourceItemMissing()
2076         {
2077             string txtFile = null;
2078             string resourcesFile = null;
2079 
2080             try
2081             {
2082                 GenerateResource t = Utilities.CreateTask(_output);
2083                 txtFile = Utilities.WriteTestText(null, null);
2084                 resourcesFile = Path.ChangeExtension(txtFile, ".resources");
2085                 File.Delete(resourcesFile);
2086                 t.Sources = new ITaskItem[] { new TaskItem("non-existent.resx"), new TaskItem(txtFile) };
2087                 bool success = t.Execute();
2088                 // Task should have failed
2089                 Assert.False(success);
2090 
2091                 // "Resource file cannot be found"
2092                 Utilities.AssertLogContains(t, "MSB3552");
2093 
2094                 // Should have processed remaining file
2095                 Assert.Equal(1, t.OutputResources.Length);
2096                 Assert.Equal(t.OutputResources[0].ItemSpec, resourcesFile);
2097                 Assert.True(File.Exists(resourcesFile));
2098             }
2099             finally
2100             {
2101                 if (txtFile != null) File.Delete(txtFile);
2102                 if (resourcesFile != null) File.Delete(resourcesFile);
2103             }
2104         }
2105 
2106         /// <summary>
2107         ///  Read-only StateFile yields message
2108         /// </summary>
2109 #if FEATURE_RESGENCACHE
2110         [Fact]
2111 #else
2112         [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/297")]
2113 #endif
2114         [PlatformSpecific(TestPlatforms.Windows)]
StateFileUnwritable()2115         public void StateFileUnwritable()
2116         {
2117             GenerateResource t = Utilities.CreateTask(_output);
2118             try
2119             {
2120                 string textFile = Utilities.WriteTestText(null, null);
2121                 t.Sources = new ITaskItem[] { new TaskItem(textFile) };
2122                 t.StateFile = new TaskItem(FileUtilities.GetTemporaryFile());
2123                 File.SetAttributes(t.StateFile.ItemSpec, FileAttributes.ReadOnly);
2124                 t.Execute();
2125 
2126                 // "cannot read state file (opening for read/write)"
2127                 Utilities.AssertLogContains(t, "MSB3088");
2128                 // "cannot write state file (opening for read/write)"
2129                 Utilities.AssertLogContains(t, "MSB3101");
2130             }
2131             finally
2132             {
2133                 // Done, so clean up.
2134                 File.Delete(t.Sources[0].ItemSpec);
2135                 File.SetAttributes(t.StateFile.ItemSpec, FileAttributes.Normal);
2136                 if (t.FilesWritten != null)
2137                 {
2138                     foreach (ITaskItem item in t.FilesWritten)
2139                     {
2140                         if (item.ItemSpec != null)
2141                             File.Delete(item.ItemSpec);
2142                     }
2143                 }
2144             }
2145         }
2146 
2147         /// <summary>
2148         ///  Bad file extension on input
2149         /// </summary>
2150         [Fact]
InputFileExtension()2151         public void InputFileExtension()
2152         {
2153             GenerateResource t = Utilities.CreateTask(_output);
2154 
2155             string textFile = Utilities.WriteTestText(null, null);
2156             string newTextFile = Path.ChangeExtension(textFile, ".foo");
2157             File.Move(textFile, newTextFile);
2158             t.Sources = new ITaskItem[] { new TaskItem(newTextFile) };
2159 
2160             t.Execute();
2161 
2162             // "unsupported file extension"
2163             Utilities.AssertLogContains(t, "MSB3558");
2164             Utilities.AssertLogNotContainsResource(t, "GenerateResource.ReadResourceMessage", 4, newTextFile);
2165 
2166             // Done, so clean up.
2167             File.Delete(t.Sources[0].ItemSpec);
2168             if (t.FilesWritten != null)
2169             {
2170                 foreach (ITaskItem item in t.FilesWritten)
2171                 {
2172                     if (item.ItemSpec != null)
2173                         File.Delete(item.ItemSpec);
2174                 }
2175             }
2176         }
2177 
2178         /// <summary>
2179         ///  Bad file extension on output
2180         /// </summary>
2181         [Fact]
OutputFileExtension()2182         public void OutputFileExtension()
2183         {
2184             GenerateResource t = Utilities.CreateTask(_output);
2185 
2186             string textFile = Utilities.WriteTestText(null, null);
2187             string resxFile = Path.ChangeExtension(textFile, ".foo");
2188             t.Sources = new ITaskItem[] { new TaskItem(textFile) };
2189             t.OutputResources = new ITaskItem[] { new TaskItem(resxFile) };
2190 
2191             t.Execute();
2192 
2193             // "unsupported file extension"
2194             Utilities.AssertLogContains(t, "MSB3558");
2195 
2196             // Done, so clean up.
2197             File.Delete(t.Sources[0].ItemSpec);
2198             if (t.FilesWritten != null)
2199             {
2200                 foreach (ITaskItem item in t.FilesWritten)
2201                 {
2202                     if (item.ItemSpec != null)
2203                         File.Delete(item.ItemSpec);
2204                 }
2205             }
2206         }
2207 
2208         /// <summary>
2209         ///  Sources and OutputResources different # of elements
2210         /// </summary>
2211         [Fact]
SourcesMatchesOutputResources()2212         public void SourcesMatchesOutputResources()
2213         {
2214             GenerateResource t = Utilities.CreateTask(_output);
2215 
2216             string textFile = Utilities.WriteTestText(null, null);
2217             string resxFile = Path.ChangeExtension(textFile, ".resources");
2218             t.Sources = new ITaskItem[] { new TaskItem(textFile) };
2219             t.OutputResources = new ITaskItem[] { new TaskItem(resxFile), new TaskItem("someother.resources") };
2220 
2221             t.Execute();
2222 
2223             // "two vectors must have the same length"
2224             Utilities.AssertLogContains(t, "MSB3094");
2225 
2226             // Done, so clean up.
2227             File.Delete(t.Sources[0].ItemSpec);
2228             if (t.FilesWritten != null)
2229             {
2230                 foreach (ITaskItem item in t.FilesWritten)
2231                 {
2232                     if (item.ItemSpec != null)
2233                         File.Delete(item.ItemSpec);
2234                 }
2235             }
2236         }
2237 
2238         /// <summary>
2239         ///  Invalid StronglyTypedLanguage yields CodeDOM exception
2240         /// </summary>
2241 #if FEATURE_CODEDOM
2242         [Fact]
2243 #else
2244         [Fact(Skip = "Does not support strongly typed resources on netcore")]
2245 #endif
2246         [Trait("Category", "mono-osx-failing")]
UnknownStronglyTypedLanguage()2247         public void UnknownStronglyTypedLanguage()
2248         {
2249             GenerateResource t = Utilities.CreateTask(_output);
2250             t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
2251 
2252             string textFile = Utilities.WriteTestText(null, null);
2253             t.Sources = new ITaskItem[] { new TaskItem(textFile) };
2254             t.StronglyTypedLanguage = "AkbarAndJeff";
2255 
2256             t.Execute();
2257 
2258             // "the codedom provider failed"
2259             Utilities.AssertLogContains(t, "MSB3559");
2260 
2261             // Done, so clean up.
2262             File.Delete(t.Sources[0].ItemSpec);
2263             if (t.FilesWritten != null)
2264             {
2265                 foreach (ITaskItem item in t.FilesWritten)
2266                 {
2267                     if (item.ItemSpec != null)
2268                         File.Delete(item.ItemSpec);
2269                 }
2270             }
2271         }
2272 
2273         /// <summary>
2274         /// StronglyTypedLanguage, but more than one resources file
2275         /// </summary>
2276         [Fact]
StronglyTypedResourceWithMoreThanOneInputResourceFile()2277         public void StronglyTypedResourceWithMoreThanOneInputResourceFile()
2278         {
2279             string resxFile = null;
2280             string resxFile2 = null;
2281 
2282             try
2283             {
2284                 resxFile = Utilities.WriteTestResX(false, null, null);
2285                 resxFile2 = Utilities.WriteTestResX(false, null, null);
2286 
2287                 GenerateResource t = Utilities.CreateTask(_output);
2288                 t.Sources = new ITaskItem[] { new TaskItem(resxFile), new TaskItem(resxFile2) };
2289                 t.StronglyTypedLanguage = "VisualBasic";
2290 
2291                 Assert.False(t.Execute());
2292 
2293                 // "str language but more than one source file"
2294                 Utilities.AssertLogContains(t, "MSB3573");
2295 
2296                 Assert.Equal(0, t.FilesWritten.Length);
2297                 Assert.True(t.OutputResources == null || t.OutputResources.Length == 0);
2298             }
2299             finally
2300             {
2301                 if (null != resxFile) File.Delete(resxFile);
2302                 if (null != resxFile2) File.Delete(resxFile2);
2303                 if (null != resxFile) File.Delete(Path.ChangeExtension(resxFile, ".resources"));
2304                 if (null != resxFile2) File.Delete(Path.ChangeExtension(resxFile2, ".resources"));
2305             }
2306         }
2307 
2308         /// <summary>
2309         ///  STR class name derived from output file transformation
2310         /// </summary>
2311 #if FEATURE_CODEDOM
2312         [Fact]
2313 #else
2314         [Fact(Skip = "Does not support strongly typed resources on netcore")]
2315 #endif
2316         [Trait("Category", "mono-osx-failing")]
BadStronglyTypedFilename()2317         public void BadStronglyTypedFilename()
2318         {
2319             string txtFile = null;
2320 
2321             try
2322             {
2323                 GenerateResource t = Utilities.CreateTask(_output);
2324                 t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
2325 
2326                 txtFile = Utilities.WriteTestText(null, null);
2327                 t.Sources = new ITaskItem[] { new TaskItem(txtFile) };
2328                 t.StronglyTypedLanguage = "CSharp";
2329                 t.StronglyTypedClassName = "cc";
2330                 t.StronglyTypedFileName = NativeMethodsShared.IsWindows ? "||" : "\0";
2331                 t.OutputResources = new ITaskItem[] { new TaskItem("somefile.resources") };
2332 
2333                 bool success = t.Execute();
2334                 // Task should have failed
2335                 Assert.False(success);
2336 
2337                 // Cannot create strongly typed resource file
2338                 Utilities.AssertLogContains(t, "MSB3570");
2339 
2340                 // it didn't write the STR class successfully, but it did still do some processing, so the
2341                 // state file is still around.
2342                 Assert.Equal(1, t.FilesWritten.Length);
2343             }
2344             finally
2345             {
2346                 if (txtFile != null) File.Delete(txtFile);
2347             }
2348         }
2349 
2350         /// <summary>
2351         /// Verify that passing a STR class without a language, errors
2352         /// </summary>
2353         [Fact]
StronglyTypedResourceClassWithoutLanguage()2354         public void StronglyTypedResourceClassWithoutLanguage()
2355         {
2356             string txtFile = null;
2357 
2358             try
2359             {
2360                 GenerateResource t = Utilities.CreateTask(_output);
2361                 txtFile = Utilities.WriteTestText(null, null);
2362                 string resourcesFile = Path.ChangeExtension(txtFile, ".resources");
2363                 File.Delete(resourcesFile);
2364                 t.Sources = new ITaskItem[] { new TaskItem(txtFile) };
2365                 t.StronglyTypedClassName = "myclassname";
2366                 // no language
2367 
2368                 bool success = t.Execute();
2369                 // Task should have failed
2370                 Assert.False(success);
2371 
2372                 Utilities.AssertLogContainsResource(t, "GenerateResource.STRClassNamespaceOrFilenameWithoutLanguage");
2373 
2374                 // Even the .resources wasn't created
2375                 Assert.False(File.Exists(resourcesFile));
2376                 Assert.Equal(0, t.FilesWritten.Length);
2377             }
2378             finally
2379             {
2380                 if (txtFile != null) File.Delete(txtFile);
2381             }
2382         }
2383 
2384         /// <summary>
2385         /// Verify that passing a STR namespace without a language, errors
2386         /// </summary>
2387         [Fact]
StronglyTypedResourceNamespaceWithoutLanguage()2388         public void StronglyTypedResourceNamespaceWithoutLanguage()
2389         {
2390             string txtFile = null;
2391 
2392             try
2393             {
2394                 GenerateResource t = Utilities.CreateTask(_output);
2395                 txtFile = Utilities.WriteTestText(null, null);
2396                 string resourcesFile = Path.ChangeExtension(txtFile, ".resources");
2397                 File.Delete(resourcesFile);
2398                 t.Sources = new ITaskItem[] { new TaskItem(txtFile) };
2399                 t.StronglyTypedNamespace = "mynamespace";
2400                 // no language
2401 
2402                 bool success = t.Execute();
2403                 // Task should have failed
2404                 Assert.False(success);
2405 
2406                 Utilities.AssertLogContainsResource(t, "GenerateResource.STRClassNamespaceOrFilenameWithoutLanguage");
2407 
2408                 // Even the .resources wasn't created
2409                 Assert.False(File.Exists(resourcesFile));
2410                 Assert.Equal(0, t.FilesWritten.Length);
2411             }
2412             finally
2413             {
2414                 if (txtFile != null) File.Delete(txtFile);
2415             }
2416         }
2417 
2418         /// <summary>
2419         /// Verify that passing a STR filename without a language, errors
2420         /// </summary>
2421         [Fact]
StronglyTypedResourceFilenameWithoutLanguage()2422         public void StronglyTypedResourceFilenameWithoutLanguage()
2423         {
2424             string txtFile = null;
2425 
2426             try
2427             {
2428                 GenerateResource t = Utilities.CreateTask(_output);
2429                 txtFile = Utilities.WriteTestText(null, null);
2430                 string resourcesFile = Path.ChangeExtension(txtFile, ".resources");
2431                 File.Delete(resourcesFile);
2432                 t.Sources = new ITaskItem[] { new TaskItem(txtFile) };
2433                 t.StronglyTypedFileName = "myfile";
2434                 // no language
2435 
2436                 bool success = t.Execute();
2437                 // Task should have failed
2438                 Assert.False(success);
2439 
2440                 Utilities.AssertLogContainsResource(t, "GenerateResource.STRClassNamespaceOrFilenameWithoutLanguage");
2441 
2442                 // Even the .resources wasn't created
2443                 Assert.False(File.Exists(resourcesFile));
2444                 Assert.Equal(0, t.FilesWritten.Length);
2445             }
2446             finally
2447             {
2448                 if (null != txtFile) File.Delete(txtFile);
2449             }
2450         }
2451 
2452         /// <summary>
2453         /// Verify that passing a STR language with more than 1 sources errors
2454         /// </summary>
2455 #if FEATURE_CODEDOM
2456         [Fact]
2457 #else
2458         [Fact(Skip = "Does not support strongly typed resources on netcore")]
2459 #endif
StronglyTypedResourceFileIsExistingDirectory()2460         public void StronglyTypedResourceFileIsExistingDirectory()
2461         {
2462             string dir = null;
2463             string txtFile = null;
2464             string resourcesFile = null;
2465 
2466             try
2467             {
2468                 GenerateResource t = Utilities.CreateTask(_output);
2469                 txtFile = Utilities.WriteTestText(null, null);
2470                 resourcesFile = Path.ChangeExtension(txtFile, ".resources");
2471                 File.Delete(resourcesFile);
2472                 string csFile = Path.ChangeExtension(txtFile, ".cs");
2473                 File.Delete(csFile);
2474                 t.Sources = new ITaskItem[] { new TaskItem(txtFile) };
2475                 t.StronglyTypedLanguage = "C#";
2476                 dir = Path.Combine(Path.GetTempPath(), "directory");
2477                 Directory.CreateDirectory(dir);
2478                 t.StronglyTypedFileName = dir;
2479 
2480                 bool success = t.Execute();
2481                 // Task should have failed
2482                 Assert.False(success);
2483 
2484                 Utilities.AssertLogContains(t, "MSB3570");
2485                 Utilities.AssertLogContains(t, t.StronglyTypedClassName);
2486 
2487                 // Since STR creation fails, doesn't create the .resources file either
2488                 Assert.False(File.Exists(resourcesFile));
2489                 Assert.False(File.Exists(csFile));
2490                 Assert.Equal(0, t.FilesWritten.Length);
2491             }
2492             finally
2493             {
2494                 if (txtFile != null) File.Delete(txtFile);
2495                 if (resourcesFile != null) File.Delete(resourcesFile);
2496                 if (dir != null) FileUtilities.DeleteWithoutTrailingBackslash(dir);
2497             }
2498         }
2499 
2500         [Fact]
2501         [PlatformSpecific(TestPlatforms.Windows)]
2502         [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp, ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486")]
Regress25163_OutputResourcesContainsInvalidPathCharacters()2503         public void Regress25163_OutputResourcesContainsInvalidPathCharacters()
2504         {
2505             string resourcesFile = null;
2506 
2507             try
2508             {
2509                 GenerateResource t = Utilities.CreateTask(_output);
2510                 resourcesFile = Utilities.WriteTestResX(false, null, null);
2511 
2512                 t.Sources = new ITaskItem[] { new TaskItem(resourcesFile) };
2513                 t.OutputResources = new ITaskItem[] { new TaskItem( "||" ) };
2514 
2515                 bool success = t.Execute();
2516 
2517                 Assert.False(success); // "Task should have failed."
2518 
2519                 Utilities.AssertLogContains(t, "MSB3553");
2520             }
2521             finally
2522             {
2523                 if (resourcesFile != null) File.Delete(resourcesFile);
2524             }
2525         }
2526     }
2527 
2528     public class References
2529     {
2530         private readonly ITestOutputHelper _output;
2531 
References(ITestOutputHelper output)2532         public References(ITestOutputHelper output)
2533         {
2534             _output = output;
2535         }
2536 
2537         [Fact]
2538         [Trait("Category", "netcore-osx-failing")]
2539         [Trait("Category", "netcore-linux-failing")] // https://github.com/Microsoft/msbuild/issues/309
2540         [Trait("Category", "mono-osx-failing")] // https://github.com/Microsoft/msbuild/issues/677
DontLockP2PReferenceWhenResolvingSystemTypes()2541         public void DontLockP2PReferenceWhenResolvingSystemTypes()
2542         {
2543             // This WriteLine is a hack.  On a slow machine, the Tasks unittest fails because remoting
2544             // times out the object used for remoting console writes.  Adding a write in the middle of
2545             // keeps remoting from timing out the object.
2546             Console.WriteLine("Performing DontLockP2PReferenceWhenResolvingSystemTypes() test");
2547 
2548             // -------------------------------------------------------------------------------
2549             // Need to produce a .DLL assembly on disk, so we can pass it in as a reference to
2550             // GenerateResource.
2551             // -------------------------------------------------------------------------------
2552             ObjectModelHelpers.DeleteTempProjectDirectory();
2553 
2554             ObjectModelHelpers.CreateFileInTempProjectDirectory("lib1.csproj", @"
2555 
2556                     <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2557                         <PropertyGroup>
2558                             <ProjectType>Local</ProjectType>
2559                             <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
2560                             <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
2561                             <AssemblyName>lib1</AssemblyName>
2562                             <OutputType>Library</OutputType>
2563                             <RootNamespace>lib1</RootNamespace>
2564                         </PropertyGroup>
2565                         <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' `>
2566                             <OutputPath>bin\Debug\</OutputPath>
2567                             <DebugSymbols>true</DebugSymbols>
2568                             <Optimize>false</Optimize>
2569                         </PropertyGroup>
2570                         <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Release|AnyCPU' `>
2571                             <OutputPath>bin\Release\</OutputPath>
2572                             <DebugSymbols>false</DebugSymbols>
2573                             <Optimize>true</Optimize>
2574                         </PropertyGroup>
2575                         <ItemGroup>
2576                             <Reference Include=`System`/>
2577                             <Compile Include=`Class1.cs`/>
2578                         </ItemGroup>
2579                         <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
2580                     </Project>
2581 
2582                 ");
2583 
2584             ObjectModelHelpers.CreateFileInTempProjectDirectory("Class1.cs", @"
2585                     public class Class1
2586                     {
2587                     }
2588                 ");
2589 
2590             ObjectModelHelpers.BuildTempProjectFileExpectSuccess("lib1.csproj");
2591 
2592             string p2pReference = Path.Combine(ObjectModelHelpers.TempProjectDir, "bin", "debug", "lib1.dll");
2593             Assert.True(File.Exists(p2pReference)); // "lib1.dll doesn't exist."
2594 
2595             // -------------------------------------------------------------------------------
2596             // Done producing an assembly on disk.
2597             // -------------------------------------------------------------------------------
2598 
2599             // Create a .RESX that references unqualified (without an assembly name) System types.
2600             ObjectModelHelpers.CreateFileInTempProjectDirectory(@"MyStrings.resx", @"
2601 
2602                     <root>
2603                         <xsd:schema id=`root` xmlns=`` xmlns:xsd=`http://www.w3.org/2001/XMLSchema` xmlns:msdata=`urn:schemas-microsoft-com:xml-msdata`>
2604                             <xsd:element name=`root` msdata:IsDataSet=`true`>
2605                                 <xsd:complexType>
2606                                     <xsd:choice maxOccurs=`unbounded`>
2607                                         <xsd:element name=`data`>
2608                                             <xsd:complexType>
2609                                                 <xsd:sequence>
2610                                                     <xsd:element name=`value` type=`xsd:string` minOccurs=`0` msdata:Ordinal=`1` />
2611                                                     <xsd:element name=`comment` type=`xsd:string` minOccurs=`0` msdata:Ordinal=`2` />
2612                                                 </xsd:sequence>
2613                                                 <xsd:attribute name=`name` type=`xsd:string` />
2614                                                 <xsd:attribute name=`type` type=`xsd:string` />
2615                                                 <xsd:attribute name=`mimetype` type=`xsd:string` />
2616                                             </xsd:complexType>
2617                                         </xsd:element>
2618                                         <xsd:element name=`resheader`>
2619                                             <xsd:complexType>
2620                                                 <xsd:sequence>
2621                                                     <xsd:element name=`value` type=`xsd:string` minOccurs=`0` msdata:Ordinal=`1` />
2622                                                 </xsd:sequence>
2623                                                 <xsd:attribute name=`name` type=`xsd:string` use=`required` />
2624                                             </xsd:complexType>
2625                                         </xsd:element>
2626                                     </xsd:choice>
2627                                 </xsd:complexType>
2628                             </xsd:element>
2629                         </xsd:schema>
2630                         <resheader name=`ResMimeType`>
2631                             <value>text/microsoft-resx</value>
2632                         </resheader>
2633                         <resheader name=`Version`>
2634                             <value>1.0.0.0</value>
2635                         </resheader>
2636                         <resheader name=`Reader`>
2637                             <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
2638                         </resheader>
2639                         <resheader name=`Writer`>
2640                             <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
2641                         </resheader>
2642                         <data name=`GraphLegend` type=`System.String`>
2643                             <value>Graph Legend</value>
2644                             <comment>Used in reports to label the graph legend that pops up</comment>
2645                         </data>
2646                         <data name=`ccResponses` type=`System.String`>
2647                             <value>{0}'s Responses</value>
2648                             <comment>Used in challenge checklist tables</comment>
2649                         </data>
2650                         <data name=`ccStrength` type=`System.String`>
2651                             <value>Strength Area</value>
2652                             <comment>Used in challenge checklist tables</comment>
2653                         </data>
2654                         <data name=`ccNeutral` type=`System.String`>
2655                             <value>Neutral Area</value>
2656                             <comment>Used in challenge checklist tables</comment>
2657                         </data>
2658                         <data name=`ccChallenge` type=`System.String`>
2659                             <value>Challenge Area</value>
2660                             <comment>Used in challenge checklist tables</comment>
2661                         </data>
2662                         <data name=`calculation` type=`System.String`>
2663                             <value>Click here for scale calculation</value>
2664                             <comment>Used in Profile Scale area of main report to point to resource section scale tables.</comment>
2665                         </data>
2666                         <data name=`PageNumber` type=`System.String`>
2667                             <value>Page </value>
2668                             <comment>In footer of PDF report, and used in PDF links</comment>
2669                         </data>
2670                         <data name=`TOC` type=`System.String`>
2671                             <value>Table of Contents</value>
2672                             <comment>On second page of PDF report</comment>
2673                         </data>
2674                         <data name=`ParticipantListingAnd`>
2675                             <value>and</value>
2676                             <comment>On title page of PDF, joining two participants in a list</comment>
2677                         </data>
2678                     </root>
2679 
2680                 ");
2681 
2682             // Run the GenerateResource task on the above .RESX file, passing in an unused reference
2683             // to lib1.dll.
2684             GenerateResource t = Utilities.CreateTask(_output);
2685             t.Sources = new ITaskItem[] { new TaskItem(Path.Combine(ObjectModelHelpers.TempProjectDir, "MyStrings.resx")) };
2686             t.UseSourcePath = false;
2687             t.NeverLockTypeAssemblies = false;
2688             t.References = new ITaskItem[]
2689                 {
2690                     new TaskItem(p2pReference),
2691 #if !RUNTIME_TYPE_NETCORE
2692                     // Path to System.dll
2693                     new TaskItem(new Uri((typeof(string)).Assembly.EscapedCodeBase).LocalPath)
2694 #else
2695 #endif
2696                 };
2697 
2698             bool success = t.Execute();
2699 
2700             // Make sure the resource was built.
2701             Assert.True(success); // "GenerateResource failed"
2702             ObjectModelHelpers.AssertFileExistsInTempProjectDirectory("MyStrings.resources");
2703 
2704             // Make sure the P2P reference is not locked after calling GenerateResource.
2705             File.Delete(p2pReference);
2706         }
2707 
2708         /// <summary>
2709         /// A reference is being passed into the GenerateResource task, but it's specified
2710         /// using a relative path.  GenerateResource was failing on this, because in the
2711         /// ResolveAssembly handler, it was calling Assembly.LoadFile on that relative path,
2712         /// which fails (LoadFile requires an absolute path).  The fix was to use
2713         /// Assembly.LoadFrom instead.
2714         /// </summary>
2715         [Fact]
2716         [Trait("Category", "netcore-osx-failing")]
2717         [Trait("Category", "netcore-linux-failing")] // https://github.com/Microsoft/msbuild/issues/309
2718         [Trait("Category", "mono-osx-failing")] // https://github.com/Microsoft/msbuild/issues/677
ReferencedAssemblySpecifiedUsingRelativePath()2719         public void ReferencedAssemblySpecifiedUsingRelativePath()
2720         {
2721             // This WriteLine is a hack.  On a slow machine, the Tasks unittest fails because remoting
2722             // times out the object used for remoting console writes.  Adding a write in the middle of
2723             // keeps remoting from timing out the object.
2724             Console.WriteLine("Performing ReferencedAssemblySpecifiedUsingRelativePath() test");
2725 
2726             // -------------------------------------------------------------------------------
2727             // Need to produce a .DLL assembly on disk, so we can pass it in as a reference to
2728             // GenerateResource.
2729             // -------------------------------------------------------------------------------
2730             ObjectModelHelpers.DeleteTempProjectDirectory();
2731 
2732             ObjectModelHelpers.CreateFileInTempProjectDirectory("ClassLibrary20.csproj", @"
2733 
2734                     <Project DefaultTargets=`Build` ToolsVersion=`msbuilddefaulttoolsversion` xmlns=`msbuildnamespace`>
2735                         <PropertyGroup>
2736                             <ProjectType>Local</ProjectType>
2737                             <Configuration Condition=` '$(Configuration)' == '' `>Debug</Configuration>
2738                             <Platform Condition=` '$(Platform)' == '' `>AnyCPU</Platform>
2739                             <AssemblyName>ClassLibrary20</AssemblyName>
2740                             <OutputType>Library</OutputType>
2741                             <RootNamespace>lib1</RootNamespace>
2742                         </PropertyGroup>
2743                         <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' `>
2744                             <OutputPath>bin\Debug\</OutputPath>
2745                             <DebugSymbols>true</DebugSymbols>
2746                             <Optimize>false</Optimize>
2747                         </PropertyGroup>
2748                         <PropertyGroup Condition=` '$(Configuration)|$(Platform)' == 'Release|AnyCPU' `>
2749                             <OutputPath>bin\Release\</OutputPath>
2750                             <DebugSymbols>false</DebugSymbols>
2751                             <Optimize>true</Optimize>
2752                         </PropertyGroup>
2753                         <ItemGroup>
2754                             <Reference Include=`System`/>
2755                             <Compile Include=`Class1.cs`/>
2756                         </ItemGroup>
2757                         <Import Project=`$(MSBuildBinPath)\Microsoft.CSharp.targets` />
2758                     </Project>
2759 
2760                 ");
2761 
2762             ObjectModelHelpers.CreateFileInTempProjectDirectory("Class1.cs", @"
2763                     using System;
2764                     using System.Collections.Generic;
2765                     using System.Text;
2766 
2767                     namespace ClassLibrary20
2768                     {
2769                         [Serializable]
2770                         public class Class1
2771                         {
2772                             public string foo;
2773                         }
2774                     }
2775                 ");
2776 
2777             ObjectModelHelpers.BuildTempProjectFileExpectSuccess("ClassLibrary20.csproj");
2778 
2779             // -------------------------------------------------------------------------------
2780             // Done producing an assembly on disk.
2781             // -------------------------------------------------------------------------------
2782 
2783             // Create a .RESX that references a type from ClassLibrary20.dll
2784             ObjectModelHelpers.CreateFileInTempProjectDirectory(@"MyStrings.resx", @"
2785 
2786                     <root>
2787                         <xsd:schema id=`root` xmlns=`` xmlns:xsd=`http://www.w3.org/2001/XMLSchema` xmlns:msdata=`urn:schemas-microsoft-com:xml-msdata`>
2788                             <xsd:element name=`root` msdata:IsDataSet=`true`>
2789                                 <xsd:complexType>
2790                                     <xsd:choice maxOccurs=`unbounded`>
2791                                         <xsd:element name=`data`>
2792                                             <xsd:complexType>
2793                                                 <xsd:sequence>
2794                                                     <xsd:element name=`value` type=`xsd:string` minOccurs=`0` msdata:Ordinal=`1` />
2795                                                     <xsd:element name=`comment` type=`xsd:string` minOccurs=`0` msdata:Ordinal=`2` />
2796                                                 </xsd:sequence>
2797                                                 <xsd:attribute name=`name` type=`xsd:string` />
2798                                                 <xsd:attribute name=`type` type=`xsd:string` />
2799                                                 <xsd:attribute name=`mimetype` type=`xsd:string` />
2800                                             </xsd:complexType>
2801                                         </xsd:element>
2802                                         <xsd:element name=`resheader`>
2803                                             <xsd:complexType>
2804                                                 <xsd:sequence>
2805                                                     <xsd:element name=`value` type=`xsd:string` minOccurs=`0` msdata:Ordinal=`1` />
2806                                                 </xsd:sequence>
2807                                                 <xsd:attribute name=`name` type=`xsd:string` use=`required` />
2808                                             </xsd:complexType>
2809                                         </xsd:element>
2810                                     </xsd:choice>
2811                                 </xsd:complexType>
2812                             </xsd:element>
2813                         </xsd:schema>
2814                         <resheader name=`ResMimeType`>
2815                             <value>text/microsoft-resx</value>
2816                         </resheader>
2817                         <resheader name=`Version`>
2818                             <value>1.0.0.0</value>
2819                         </resheader>
2820                         <resheader name=`Reader`>
2821                             <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
2822                         </resheader>
2823                         <resheader name=`Writer`>
2824                             <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
2825                         </resheader>
2826                         <data name=`Image1` type=`ClassLibrary20.Class1, ClassLibrary20, version=1.0.0.0, Culture=neutral, PublicKeyToken=null`>
2827                             <value>blah</value>
2828                         </data>
2829                     </root>
2830 
2831                 ");
2832 
2833             // Run the GenerateResource task on the above .RESX file, passing in an unused reference
2834             // to lib1.dll.
2835             GenerateResource t = Utilities.CreateTask(_output);
2836             t.Sources = new ITaskItem[] { new TaskItem(Path.Combine(ObjectModelHelpers.TempProjectDir, "MyStrings.resx")) };
2837             t.UseSourcePath = false;
2838             t.NeverLockTypeAssemblies = false;
2839 
2840             TaskItem reference = new TaskItem(@"bin\debug\ClassLibrary20.dll");
2841             reference.SetMetadata("FusionName", "ClassLibrary20, version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
2842 
2843             t.References = new ITaskItem[] { reference };
2844 
2845             // Set the current working directory to the location of ClassLibrary20.csproj.
2846             // This is what allows us to pass in a relative path to the referenced assembly.
2847             string originalCurrentDirectory = Directory.GetCurrentDirectory();
2848             Directory.SetCurrentDirectory(ObjectModelHelpers.TempProjectDir);
2849 
2850             try
2851             {
2852                 bool success = t.Execute();
2853                 // Make sure the resource was built.
2854                 Assert.True(success); // "GenerateResource failed"
2855                 ObjectModelHelpers.AssertFileExistsInTempProjectDirectory("MyStrings.resources");
2856             }
2857             finally
2858             {
2859                 Directory.SetCurrentDirectory(originalCurrentDirectory);
2860             }
2861         }
2862     }
2863 
2864     public class MiscTests
2865     {
2866         private readonly ITestOutputHelper _output;
2867 
MiscTests(ITestOutputHelper output)2868         public MiscTests(ITestOutputHelper output)
2869         {
2870             _output = output;
2871         }
2872 
2873         [Fact]
ResgenCommandLineLogging()2874         public void ResgenCommandLineLogging()
2875         {
2876             // This WriteLine is a hack.  On a slow machine, the Tasks unittest fails because remoting
2877             // times out the object used for remoting console writes.  Adding a write in the middle of
2878             // keeps remoting from timing out the object.
2879             Console.WriteLine("Performing ResgenCommandLineLogging() test");
2880 
2881             // we use this to check if paths need quoting
2882             CommandLineBuilderHelper commandLineBuilderHelper = new CommandLineBuilderHelper();
2883 
2884             string resxFile = Utilities.WriteTestResX(false, null, null);
2885             string resourcesFile = Path.ChangeExtension(resxFile, ".resources");
2886             File.Delete(resourcesFile);
2887 
2888             try
2889             {
2890                 GenerateResource t = Utilities.CreateTask(_output);
2891                 t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
2892                 t.UseSourcePath = false;
2893                 t.NeverLockTypeAssemblies = false;
2894                 t.Execute();
2895 
2896                 string possiblyQuotedResxFile = resxFile;
2897                 string possiblyQuotedResourcesFile = resourcesFile;
2898 
2899                 if (commandLineBuilderHelper.DoesPathNeedQuotes(resxFile))
2900                 {
2901                     possiblyQuotedResxFile = "\"" + resxFile + "\"";
2902                 }
2903 
2904                 if (commandLineBuilderHelper.DoesPathNeedQuotes(resourcesFile))
2905                 {
2906                     possiblyQuotedResourcesFile = "\"" + resourcesFile + "\"";
2907                 }
2908 
2909                 Utilities.AssertLogContains(
2910                     t,
2911                     "/compile " + possiblyQuotedResxFile + ","
2912                     + possiblyQuotedResourcesFile);
2913             }
2914             finally
2915             {
2916                 File.Delete(resxFile);
2917                 File.Delete(resourcesFile);
2918             }
2919 
2920             resxFile = Utilities.WriteTestResX(false, null, null);
2921             resourcesFile = Path.ChangeExtension(resxFile, ".resources");
2922             File.Delete(resourcesFile);
2923 
2924             try
2925             {
2926                 GenerateResource t = Utilities.CreateTask(_output);
2927                 t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
2928                 t.References = new ITaskItem[] { new TaskItem("baz"), new TaskItem("jazz") };
2929                 t.UseSourcePath = true;
2930                 t.PublicClass = true;
2931                 t.StronglyTypedLanguage = "C#";
2932                 t.NeverLockTypeAssemblies = false;
2933                 t.Execute();
2934 
2935                 string possiblyQuotedResxFile = resxFile;
2936                 string possiblyQuotedResourcesFile = resourcesFile;
2937 
2938                 if (commandLineBuilderHelper.DoesPathNeedQuotes(resxFile))
2939                 {
2940                     possiblyQuotedResxFile = "\"" + resxFile + "\"";
2941                 }
2942 
2943                 if (commandLineBuilderHelper.DoesPathNeedQuotes(resourcesFile))
2944                 {
2945                     possiblyQuotedResourcesFile = "\"" + resourcesFile + "\"";
2946                 }
2947 
2948                 Utilities.AssertLogContains(
2949                     t,
2950                     "/useSourcePath "
2951                     + "/publicClass "
2952                     + "/r:baz "
2953                     + "/r:jazz " + possiblyQuotedResxFile + " "
2954                     + possiblyQuotedResourcesFile + " " + "/str:\"C#\",,,");
2955             }
2956             finally
2957             {
2958                 File.Delete(resxFile);
2959                 File.Delete(resourcesFile);
2960                 File.Delete(Path.ChangeExtension(resxFile, ".cs"));
2961             }
2962 
2963             resxFile = Utilities.WriteTestResX(false, null, null);
2964             resourcesFile = Path.ChangeExtension(resxFile, ".resources");
2965             File.Delete(resourcesFile);
2966 
2967             try
2968             {
2969                 GenerateResource t = Utilities.CreateTask(_output);
2970                 t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
2971                 t.References = new ITaskItem[] { new TaskItem("baz"), new TaskItem("jazz") };
2972                 t.UseSourcePath = true;
2973                 t.StronglyTypedLanguage = "C#";
2974                 t.StronglyTypedClassName = "wagwag";
2975                 t.StronglyTypedFileName = "boo";
2976                 t.NeverLockTypeAssemblies = false;
2977                 t.Execute();
2978 
2979                 string possiblyQuotedResxFile = resxFile;
2980                 string possiblyQuotedResourcesFile = resourcesFile;
2981 
2982                 if (commandLineBuilderHelper.DoesPathNeedQuotes(resxFile))
2983                 {
2984                     possiblyQuotedResxFile = "\"" + resxFile + "\"";
2985                 }
2986 
2987                 if (commandLineBuilderHelper.DoesPathNeedQuotes(resourcesFile))
2988                 {
2989                     possiblyQuotedResourcesFile = "\"" + resourcesFile + "\"";
2990                 }
2991 
2992                 Utilities.AssertLogContains(
2993                     t,
2994                     "/useSourcePath "
2995                     + "/r:baz "
2996                     + "/r:jazz " + possiblyQuotedResxFile + " "
2997                     + possiblyQuotedResourcesFile + " "
2998                     + "/str:\"C#\",,wagwag,boo");
2999             }
3000             finally
3001             {
3002                 File.Delete(resxFile);
3003                 File.Delete(resourcesFile);
3004             }
3005 
3006             resxFile = Utilities.WriteTestResX(false, null, null);
3007             resourcesFile = Path.ChangeExtension(resxFile, ".myresources");
3008             File.Delete(resourcesFile);
3009             string resxFile1 = Utilities.WriteTestResX(false, null, null);
3010             string resourcesFile1 = Path.ChangeExtension(resxFile1, ".myresources");
3011             File.Delete(resourcesFile1);
3012 
3013             try
3014             {
3015                 GenerateResource t = Utilities.CreateTask(_output);
3016                 t.Sources = new ITaskItem[] { new TaskItem(resxFile), new TaskItem(resxFile1) };
3017                 t.OutputResources = new ITaskItem[]
3018                                     {
3019                                         new TaskItem(resourcesFile),
3020                                         new TaskItem(resourcesFile1)
3021                                     };
3022                 t.NeverLockTypeAssemblies = false;
3023                 t.Execute();
3024 
3025                 string possiblyQuotedResxFile = resxFile;
3026                 string possiblyQuotedResourcesFile = resourcesFile;
3027                 string possiblyQuotedResxFile1 = resxFile1;
3028                 string possiblyQuotedResourcesFile1 = resourcesFile1;
3029 
3030                 if (commandLineBuilderHelper.DoesPathNeedQuotes(resxFile))
3031                 {
3032                     possiblyQuotedResxFile = "\"" + resxFile + "\"";
3033                 }
3034 
3035                 if (commandLineBuilderHelper.DoesPathNeedQuotes(resourcesFile))
3036                 {
3037                     possiblyQuotedResourcesFile = "\"" + resourcesFile + "\"";
3038                 }
3039 
3040                 if (commandLineBuilderHelper.DoesPathNeedQuotes(resxFile1))
3041                 {
3042                     possiblyQuotedResxFile1 = "\"" + resxFile1 + "\"";
3043                 }
3044 
3045                 if (commandLineBuilderHelper.DoesPathNeedQuotes(resourcesFile1))
3046                 {
3047                     possiblyQuotedResourcesFile1 = "\"" + resourcesFile1 + "\"";
3048                 }
3049 
3050                 Utilities.AssertLogContains(t,
3051                     "/compile " +
3052                     possiblyQuotedResxFile +
3053                     "," +
3054                     possiblyQuotedResourcesFile +
3055                     " " +
3056                     possiblyQuotedResxFile1 +
3057                     "," +
3058                     possiblyQuotedResourcesFile1);
3059             }
3060             finally
3061             {
3062                 File.Delete(resxFile);
3063                 File.Delete(resourcesFile);
3064                 File.Delete(resxFile1);
3065                 File.Delete(resourcesFile1);
3066             }
3067         }
3068 
3069         /// <summary>
3070         /// In order to make GenerateResource multitargetable, a property, ExecuteAsTool, was added.
3071         /// In order to have correct behavior when using pre-4.0
3072         /// toolsversions, ExecuteAsTool must default to true, and the paths to the tools will be the
3073         /// v3.5 path.  It is difficult to verify the tool paths in a unit test, however, so
3074         /// this was done by ad hoc testing and will be maintained by the dev suites.
3075         /// </summary>
3076         [Fact]
MultiTargetingDefaultsSetCorrectly()3077         public void MultiTargetingDefaultsSetCorrectly()
3078         {
3079             GenerateResource t = new GenerateResource();
3080 
3081             Assert.True(t.ExecuteAsTool); // "ExecuteAsTool should default to true"
3082         }
3083 
3084         [Fact]
3085         [PlatformSpecific(TestPlatforms.AnyUnix)]
RebuildsInPresenceOfFileRefWithWindowsPath()3086         public void RebuildsInPresenceOfFileRefWithWindowsPath()
3087         {
3088             // This WriteLine is a hack.  On a slow machine, the Tasks unittest fails because remoting
3089             // times out the object used for remoting console writes.  Adding a write in the middle of
3090             // keeps remoting from timing out the object.
3091             Console.WriteLine("Performing RebuildsInPresenceOfFileRefWithWindowsPath() test");
3092 
3093             string originalCurrentDirectory = Directory.GetCurrentDirectory();
3094             GenerateResource t = null;
3095             string relPathToTextFile = null;
3096 
3097             try
3098             {
3099                 Directory.SetCurrentDirectory(ObjectModelHelpers.TempProjectDir);
3100 
3101                 relPathToTextFile = Path.Combine("tmp_dir", "test_file.txt");
3102                 string fileRef = "<data name=\"TextFile1\" type=\"System.Resources.ResXFileRef, System.Windows.Forms\">" +
3103                                 $"<value>.\\tmp_dir\\test_file.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value></data>";
3104 
3105                 string textFile = ObjectModelHelpers.CreateFileInTempProjectDirectory(relPathToTextFile, "xyz");
3106 
3107                 string resxFile = Utilities.WriteTestResX(false, null, fileRef, false);
3108 
3109                 Func<GenerateResource> executeTask = () => {
3110                     GenerateResource task = Utilities.CreateTask(_output);
3111                     task.Sources = new ITaskItem[] { new TaskItem(resxFile) };
3112 
3113                     Utilities.ExecuteTask(task);
3114 
3115                     string outputResourceFile = task.OutputResources[0].ItemSpec;
3116                     Assert.Equal(Path.GetExtension(outputResourceFile), ".resources");
3117                     outputResourceFile = task.FilesWritten[0].ItemSpec;
3118                     Assert.Equal(Path.GetExtension(outputResourceFile), ".resources");
3119 
3120                     return task;
3121                 };
3122 
3123                 t = executeTask();
3124                 string resourcesFile = t.OutputResources[0].ItemSpec;
3125                 DateTime initialWriteTime = File.GetLastWriteTime(resourcesFile);
3126 
3127                 // fs granularity on HFS is 1 sec!
3128                 System.Threading.Thread.Sleep(1000);
3129 
3130                 // Rebuild, it shouldn't regen .resources file since the sources
3131                 // haven't changed
3132                 t = executeTask();
3133                 resourcesFile = t.OutputResources[0].ItemSpec;
3134 
3135                 Assert.False(Utilities.FileUpdated(resourcesFile, initialWriteTime));
3136             }
3137             finally
3138             {
3139                 // Done, so clean up.
3140                 File.Delete(t.Sources[0].ItemSpec);
3141                 foreach (ITaskItem item in t.FilesWritten)
3142                 {
3143                     if (File.Exists(item.ItemSpec))
3144                     {
3145                         File.Delete(item.ItemSpec);
3146                     }
3147                 }
3148                 if (relPathToTextFile != null)
3149                     File.Delete(relPathToTextFile);
3150                 Directory.Delete(Path.GetDirectoryName(relPathToTextFile));
3151 
3152                 Directory.SetCurrentDirectory(originalCurrentDirectory);
3153             }
3154         }
3155 
3156         //  Regression test for https://github.com/Microsoft/msbuild/issues/2206
3157         [Theory]
3158         [InlineData("\n")]
3159         [InlineData("\r\n")]
3160         [InlineData("\r")]
ResxValueNewlines(string newline)3161         public void ResxValueNewlines(string newline)
3162         {
3163             string resxValue = "First line" + newline + "second line" + newline;
3164             string resxDataName = "DataWithNewline";
3165             string data = "<data name=\"" + resxDataName + "\">" + newline +
3166                 "<value>" + resxValue + "</value>" + newline + "</data>";
3167 
3168             string resxFile = null;
3169 
3170             GenerateResource t = Utilities.CreateTask(_output);
3171             t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
3172 
3173             try
3174             {
3175                 resxFile = Utilities.WriteTestResX(false, null, data);
3176 
3177                 t.Sources = new ITaskItem[] { new TaskItem(resxFile) };
3178 
3179                 Utilities.ExecuteTask(t);
3180 
3181                 string resourcesFile = t.OutputResources[0].ItemSpec;
3182                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
3183                 resourcesFile = t.FilesWritten[0].ItemSpec;
3184                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
3185 
3186                 Dictionary<string, object> valuesFromResource = new Dictionary<string, object>();
3187                 using (var resourceReader = new System.Resources.ResourceReader(resourcesFile))
3188                 {
3189                     IDictionaryEnumerator resEnum = resourceReader.GetEnumerator();
3190                     while (resEnum.MoveNext())
3191                     {
3192                         string name = (string)resEnum.Key;
3193                         object value = resEnum.Value;
3194                         valuesFromResource[name] = value;
3195                     }
3196                 }
3197 
3198                 Assert.True(valuesFromResource.ContainsKey(resxDataName));
3199                 Assert.Equal(resxValue, valuesFromResource[resxDataName]);
3200             }
3201             finally
3202             {
3203 
3204                 File.Delete(t.Sources[0].ItemSpec);
3205                 foreach (ITaskItem item in t.FilesWritten)
3206                 {
3207                     if (File.Exists(item.ItemSpec))
3208                     {
3209                         File.Delete(item.ItemSpec);
3210                     }
3211                 }
3212             }
3213         }
3214     }
3215 }
3216 
3217 namespace Microsoft.Build.UnitTests.GenerateResource_Tests
3218 {
3219     /// <summary>
3220     /// This Utilities class provides some static helper methods for resource tests
3221     /// </summary>
3222     internal sealed partial class Utilities
3223     {
3224         /// <summary>
3225         /// Set the last write time to be n minutes back in time.
3226         /// </summary>
MoveBackTimestamp(string fileName, int minutes)3227         public static DateTime MoveBackTimestamp(string fileName, int minutes)
3228         {
3229             DateTime newTime = File.GetLastWriteTime(fileName) - new TimeSpan(0, minutes, 0);
3230             File.SetLastWriteTime(fileName, newTime);
3231             return newTime;
3232         }
3233 
3234         /// <summary>
3235         /// Return whether the file was written to since the specified time.
3236         /// </summary>
FileUpdated(string fileName, DateTime previousWriteTime)3237         public static bool FileUpdated(string fileName, DateTime previousWriteTime)
3238         {
3239             return (File.GetLastWriteTime(fileName) > previousWriteTime);
3240         }
3241 
3242         /// <summary>
3243         /// Looks for a message in the output log for the task execution, including formatted parameters.
3244         /// </summary>
AssertLogContainsResource(GenerateResource t, string messageID, params object[] replacements)3245         public static void AssertLogContainsResource(GenerateResource t, string messageID, params object[] replacements)
3246         {
3247             Assert.True(
3248                 ((MockEngine)t.BuildEngine).Log.Contains(String.Format(AssemblyResources.GetString(messageID), replacements))
3249                 );
3250         }
3251 
3252         /// <summary>
3253         /// Looks for a message in the output log for the task execution., including formatted parameters.
3254         /// </summary>
AssertLogContains(GenerateResource t, string message)3255         public static void AssertLogContains(GenerateResource t, string message)
3256         {
3257             Assert.True(((MockEngine)t.BuildEngine).Log.Contains(message));
3258         }
3259 
3260         /// <summary>
3261         /// Looks for a message in the output log for the task execution, including formatted parameters.
3262         /// </summary>
AssertLogNotContainsResource(GenerateResource t, string messageID, params object[] replacements)3263         public static void AssertLogNotContainsResource(GenerateResource t, string messageID, params object[] replacements)
3264         {
3265             Assert.False(((MockEngine)t.BuildEngine).Log.Contains(String.Format(AssemblyResources.GetString(messageID), replacements)));
3266         }
3267 
3268         /// <summary>
3269         /// Looks for a message in the output log for the task execution., including formatted parameters.
3270         /// </summary>
AssertLogNotContains(GenerateResource t, string message)3271         public static void AssertLogNotContains(GenerateResource t, string message)
3272         {
3273             Assert.False(((MockEngine)t.BuildEngine).Log.Contains(message));
3274         }
3275 
3276         /// <summary>
3277         /// Given an array of ITaskItems, checks to make sure that at least one read tlog and at least one
3278         /// write tlog exist, and that they were written to disk.  If that is not true, asserts.
3279         /// </summary>
AssertStateFileWasWritten(GenerateResource t)3280         public static void AssertStateFileWasWritten(GenerateResource t)
3281         {
3282             Assert.NotNull(t.FilesWritten); // "The state file should have been written, but there aren't any."
3283             Assert.NotNull(t.StateFile); // "State file should be defined"
3284             Assert.True(File.Exists(t.StateFile.ItemSpec)); // "State file should exist"
3285 
3286             bool foundStateFile = false;
3287 
3288             // start from the end because the statefile is usually marked as a written file fairly late in the process
3289             for (int i = t.FilesWritten.Length - 1; i >= 0; i--)
3290             {
3291                 if (t.StateFile.ItemSpec.Equals(t.FilesWritten[i].ItemSpec))
3292                 {
3293                     foundStateFile = true;
3294                     break;
3295                 }
3296             }
3297 
3298             Assert.True(foundStateFile); // "Expected there to be a state file, but there wasn't"
3299         }
3300 
3301         /// <summary>
3302         /// </summary>
CreateBasicResourcesFile(bool useResX, ITestOutputHelper output)3303         public static string CreateBasicResourcesFile(bool useResX, ITestOutputHelper output)
3304         {
3305             GenerateResource t = CreateTask(output);
3306 
3307             string sourceFile = null;
3308             if (useResX)
3309                 sourceFile = WriteTestResX(false, null, null);
3310             else
3311                 sourceFile = WriteTestText(null, null);
3312 
3313             t.Sources = new ITaskItem[] { new TaskItem(sourceFile) };
3314 
3315             // phase 1, generate the .resources file (we don't care about outcomes)
3316             Utilities.ExecuteTask(t);
3317 
3318             File.Delete(sourceFile);
3319             return t.OutputResources[0].ItemSpec;
3320         }
3321 
3322         /// <summary>
3323         /// </summary>
ReadFileContent(string fileName)3324         public static string ReadFileContent(string fileName)
3325         {
3326             return File.ReadAllText(fileName);
3327         }
3328 
3329         /// <summary>
3330         /// ExecuteTask performs the task Execute method and asserts basic success criteria
3331         /// </summary>
ExecuteTask(GenerateResource t)3332         public static void ExecuteTask(GenerateResource t)
3333         {
3334             bool success = t.Execute();
3335             Assert.True(success);
3336 
3337             if (t.OutputResources != null && t.OutputResources[0] != null)
3338             {
3339                 DateTime resourcesLastModified = File.GetLastWriteTime(t.OutputResources[0].ItemSpec);
3340                 if (t.Sources[0] != null)
3341                     Assert.True(resourcesLastModified >= File.GetLastWriteTime(t.Sources[0].ItemSpec));
3342             }
3343         }
3344 
3345         /// <summary>
3346         /// This method creates a GenerateResource task and performs basic setup on it, e.g. BuildEngine
3347         /// </summary>
3348         /// <param name="output"></param>
CreateTask(ITestOutputHelper output)3349         public static GenerateResource CreateTask(ITestOutputHelper output)
3350         {
3351             // always use the internal ctor that says don't perform separate app domain check
3352             GenerateResource t = new GenerateResource();
3353             t.BuildEngine = new MockEngine(output);
3354 
3355             // Make the task execute in-proc
3356             t.ExecuteAsTool = false;
3357 
3358             return t;
3359         }
3360 
3361         /// <summary>
3362         /// This method creates and returns a string that is the contents of a canonical .txt resource file.
3363         /// <param name="tagName">Gives the opportunity to create a warning/error in the text by specifying a [tag] value, null for nothing.</param>
3364         /// <param name="oneLine">Gives the opportunity to add one name-value pair to the text.  Null for nothing.</param>
3365         /// </summary>
3366         /// <returns>The content of the text blob as a string</returns>
GetTestTextContent(string tagName, string oneLine)3367         public static string GetTestTextContent(string tagName, string oneLine)
3368         {
3369             return GetTestTextContent(tagName, oneLine, false);
3370         }
3371 
3372         /// <summary>
3373         /// Allows test to get the cleaned up resources, as they would be expected after being transformed
3374         /// back and forth.
3375         /// </summary>
3376         /// <param name="tagName"></param>
3377         /// <param name="oneLine"></param>
3378         /// <param name="cleanedUp"></param>
3379         /// <returns></returns>
GetTestTextContent(string tagName, string oneLine, bool cleanedUp)3380         public static string GetTestTextContent(string tagName, string oneLine, bool cleanedUp)
3381         {
3382             // Make sure these are in alpha order by name, as the round trip will sort them
3383             string textFileContents;
3384 
3385             if (!cleanedUp)
3386             {
3387                 textFileContents =
3388                     "\nMalade=There is trouble in the hen\\n house\xd\xa"
3389                    + "# this is a comment\xd\xa"
3390                    + "Marley=The man, the myth, \\rthe legend\xd\xa"
3391                    + "Name2 = Put the li\u1111me in the \\tcoconut and drink 'em both up\xd\xa"
3392                    + "Name1=Some S\\\\tring Comes \\\"Here\xd\xa";
3393             }
3394             else
3395             {
3396                 // Content as it would be expected after being transformed and transformed back
3397                 textFileContents =
3398                     "Malade=There is trouble in the hen\\n house\xd\xa"
3399                    + "Marley=The man, the myth, \\rthe legend\xd\xa"
3400                    + "Name2=Put the li\u1111me in the \\tcoconut and drink 'em both up\xd\xa"
3401                    + "Name1=Some S\\\\tring Comes \"Here\xd\xa";
3402             }
3403 
3404             StringBuilder txt = new StringBuilder();
3405 
3406             if (tagName != null)
3407             {
3408                 txt.Append("[");
3409                 txt.Append(tagName);
3410                 txt.Append("]\xd\xa");
3411             }
3412 
3413             txt.Append(textFileContents);
3414 
3415             if (oneLine != null)
3416             {
3417                 txt.Append(oneLine);
3418                 txt.Append("\xd\xa");
3419             }
3420 
3421             return txt.ToString();
3422         }
3423 
3424         /// <summary>
3425         /// This method creates a temporary file based on the canonical .txt resource file.
3426         /// <param name="tagName">Gives the opportunity to create a warning/error in the text by specifying a [tag] value, null for nothing.</param>
3427         /// <param name="oneLine">Gives the opportunity to add one name-value pair to the text.  Null for nothing.</param>
3428         /// </summary>
WriteTestText(string tagName, string oneLine)3429         public static string WriteTestText(string tagName, string oneLine)
3430         {
3431             string textFile = Utilities.GetTempFileName(".txt");
3432             File.Delete(textFile);
3433             File.WriteAllText(textFile, GetTestTextContent(tagName, oneLine));
3434             return textFile;
3435         }
3436 
3437         /// <summary>
3438         /// Write a test .resx file to a temporary location.
3439         /// </summary>
3440         /// <param name="useType">Indicates whether to include an enum to test type-specific resource encoding with assembly references</param>
3441         /// <param name="linkedBitmap">The name of a linked-in bitmap.  use 'null' for no bitmap.</param>
3442         /// <returns>The content of the resx blob as a string</returns>
3443         /// <returns>The name of the text file</returns>
GetTestResXContent(bool useType, string linkedBitmap, string extraToken, bool useInvalidType)3444         public static string GetTestResXContent(bool useType, string linkedBitmap, string extraToken, bool useInvalidType)
3445         {
3446             StringBuilder resgenFileContents = new StringBuilder();
3447 
3448             resgenFileContents.Append(
3449                  "<root>\xd\xa"
3450                 + "  <resheader name='resmimetype'>\xd\xa"
3451                 + "    <value>text/microsoft-resx</value>\xd\xa"
3452                 + "  </resheader>\xd\xa"
3453                 + "  <resheader name='version'>\xd\xa"
3454                 + "    <value>2.0</value>\xd\xa"
3455                 + "  </resheader>\xd\xa"
3456                 + "  <resheader name='reader'>\xd\xa"
3457                 + "    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\xd\xa"
3458                 + "  </resheader>\xd\xa"
3459                 + "  <resheader name='writer'>\xd\xa"
3460                 + "    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\xd\xa"
3461                 + "  </resheader>\xd\xa"
3462                 );
3463 
3464             resgenFileContents.Append(
3465                  // A plain old string value.
3466                  "  <data name=\"MyString\">\xd\xa"
3467                 + "    <value>MyValue</value>\xd\xa"
3468                 + "  </data>\xd\xa"
3469                 );
3470 
3471             if (extraToken != null)
3472                 resgenFileContents.Append(extraToken);
3473 
3474             if (useType)
3475             {
3476                 // A non-standard type. In this case, an enum.
3477                 resgenFileContents.Append(
3478                      "  <data name='Label.Modifiers' type='System.CodeDom.MemberAttributes, System'>\xd\xa"
3479                     + "    <value>Assembly</value>\xd\xa"
3480                     + "  </data>\xd\xa"
3481                     );
3482             }
3483 
3484             if (useInvalidType)
3485             {
3486                 // A type that won't be resolved.. oops!
3487                 resgenFileContents.Append(
3488                      "  <data name='xx' type='X, INVALID'>\xd\xa"
3489                     + "    <value>1</value>\xd\xa"
3490                     + "  </data>\xd\xa"
3491                     );
3492             }
3493 
3494             if (linkedBitmap != null)
3495             {
3496                 // A linked-in bitmap.
3497                 resgenFileContents.Append(
3498                      "  <data name='Image1' type='System.Resources.ResXFileRef, System.Windows.Forms'>\xd\xa"
3499                     + "    <value>"
3500                     );
3501 
3502                 // The linked file may have a different case than reported by the filesystem
3503                 // simulate this by lower-casing our file before writing it into the resx.
3504                 resgenFileContents.Append(
3505                     NativeMethodsShared.IsWindows
3506                         ? linkedBitmap.ToUpperInvariant()
3507                         : linkedBitmap);
3508 
3509                 resgenFileContents.Append(
3510                      ";System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\xd\xa"
3511                     + "  </data>\xd\xa"
3512                     );
3513             }
3514 
3515             resgenFileContents.Append("</root>\xd\xa");
3516 
3517             return resgenFileContents.ToString();
3518         }
3519 
3520         /// <summary>
3521         /// Write a test .resx file to a temporary location.
3522         /// </summary>
3523         /// <param name="useType">Indicates whether to include an enum to test type-specific resource encoding with assembly references</param>
3524         /// <param name="linkedBitmap">The name of a linked-in bitmap.  use 'null' for no bitmap.</param>
3525         /// <returns>The name of the resx file</returns>
WriteTestResX(bool useType, string linkedBitmap, string extraToken, string resxFileToWrite = null)3526         public static string WriteTestResX(bool useType, string linkedBitmap, string extraToken, string resxFileToWrite = null)
3527         {
3528             return WriteTestResX(useType, linkedBitmap, extraToken, useInvalidType: false, resxFileToWrite:resxFileToWrite);
3529         }
3530 
3531         /// <summary>
3532         /// Write a test .resx file to a temporary location.
3533         /// </summary>
3534         /// <param name="useType">Indicates whether to include an enum to test type-specific resource encoding with assembly references</param>
3535         /// <param name="linkedBitmap">The name of a linked-in bitmap.  use 'null' for no bitmap.</param>
3536         /// <returns>The name of the resx file</returns>
WriteTestResX(bool useType, string linkedBitmap, string extraToken, bool useInvalidType, string resxFileToWrite = null)3537         public static string WriteTestResX(bool useType, string linkedBitmap, string extraToken, bool useInvalidType, string resxFileToWrite = null)
3538         {
3539             string resgenFile = resxFileToWrite;
3540             if (string.IsNullOrEmpty(resgenFile))
3541             {
3542                 resgenFile = GetTempFileName(".resx");
3543                 File.Delete(resgenFile);
3544             }
3545 
3546             File.WriteAllText(resgenFile, GetTestResXContent(useType, linkedBitmap, extraToken, useInvalidType));
3547             return resgenFile;
3548         }
3549 
3550         /// <summary>
3551         /// Copy system.dll (so we can later touch it) to a temporary location.
3552         /// </summary>
3553         /// <returns>The name of the copied file.</returns>
GetPathToCopiedSystemDLL()3554         public static string GetPathToCopiedSystemDLL()
3555         {
3556             string tempSystemDLL = Utilities.GetTempFileName(".dll");
3557 
3558             string pathToSystemDLL =
3559 #if FEATURE_INSTALLED_MSBUILD
3560                 ToolLocationHelper.GetPathToDotNetFrameworkFile("System.dll", TargetDotNetFrameworkVersion.Version45);
3561 #else
3562                 Path.Combine(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, "System.dll");
3563 #endif
3564 
3565             File.Copy(pathToSystemDLL, tempSystemDLL);
3566             return tempSystemDLL;
3567         }
3568 
3569         /// <summary>
3570         /// Create a tiny bitmap at a temporary location.
3571         /// </summary>
3572         /// <returns>The name of the bitmap.</returns>
CreateWorldsSmallestBitmap()3573         public static string CreateWorldsSmallestBitmap()
3574         {
3575             string smallestBitmapFile = Utilities.GetTempFileName(".bmp");
3576 
3577             byte[] bmp = new byte[66];
3578             bmp[0x00] = 0x42; bmp[0x01] = 0x4D; bmp[0x02] = 0x42;
3579             bmp[0x0a] = 0x3E; bmp[0x0e] = 0x28; bmp[0x12] = 0x01; bmp[0x16] = 0x01;
3580             bmp[0x1a] = 0x01; bmp[0x1c] = 0x01; bmp[0x22] = 0x04;
3581             bmp[0x3a] = 0xFF; bmp[0x3b] = 0xFF; bmp[0x3c] = 0xFF;
3582             bmp[0x3e] = 0x80;
3583 
3584             File.Delete(smallestBitmapFile);
3585             File.WriteAllBytes(
3586                 NativeMethodsShared.IsWindows ? smallestBitmapFile.ToUpperInvariant() : smallestBitmapFile,
3587                 bmp);
3588             return smallestBitmapFile;
3589         }
3590 
3591         /// <summary>
3592         /// </summary>
GetPrivateMethod(object o, string methodName)3593         public static MethodInfo GetPrivateMethod(object o, string methodName)
3594         {
3595             return o.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic);
3596         }
3597 
3598         /// <summary>
3599         /// Since GetTempFileName creates an empty file, it's bad mojo to just append a new extension
3600         /// because when you clean up your modified filename, you'll leave behind the original .tmp
3601         /// file.  This method gives you a unique filename with your desired extension, but also
3602         /// deletes the original root file.  It's not perfect, but...
3603         /// </summary>
GetTempFileName(string extension)3604         public static string GetTempFileName(string extension)
3605         {
3606             string f = FileUtilities.GetTemporaryFile();
3607             string filename = Path.ChangeExtension(f, extension);
3608             File.Delete(f);
3609             // Make sure that the new file doesn't already exist, since the test is probably
3610             // expecting it not to
3611             File.Delete(filename);
3612             return filename;
3613         }
3614 
3615         /// <summary>
3616         /// Helper method to test STRNamespace parameter of Generate Resource task
3617         /// </summary>
3618         /// <param name="strLanguage"></param>
3619         /// <param name="resourcesNamespace"></param>
3620         /// <param name="classNamespace"></param>
3621         /// <param name="output"></param>
STRNamespaceTestHelper(string strLanguage, string resourcesNamespace, string classNamespace, ITestOutputHelper output)3622         public static void STRNamespaceTestHelper(string strLanguage, string resourcesNamespace, string classNamespace, ITestOutputHelper output)
3623         {
3624             // these two parameters should not be null
3625             Assert.NotNull(strLanguage);
3626             Assert.NotNull(resourcesNamespace);
3627             // Generate Task
3628             GenerateResource t = Utilities.CreateTask(output);
3629             try
3630             {
3631                 t.StateFile = new TaskItem(Utilities.GetTempFileName(".cache"));
3632                 // Create an input text file
3633                 string textFile = Utilities.WriteTestText(null, null);
3634                 // set the Sources parameter
3635                 t.Sources = new ITaskItem[] { new TaskItem(textFile) };
3636                 // Set the StronglyTypedLanguage parameter
3637                 t.StronglyTypedLanguage = strLanguage;
3638                 // Set the StronglyTypedManifestPrefix parameter
3639                 t.StronglyTypedManifestPrefix = resourcesNamespace;
3640 
3641                 // Set the StronglyTypedNamespace parameter
3642                 t.StronglyTypedNamespace = classNamespace;
3643 
3644                 string codeFileExtension = null;
3645                 if (strLanguage == "CSharp")
3646                     codeFileExtension = ".cs";
3647                 else if (strLanguage == "VB")
3648                     codeFileExtension = ".vb";
3649 
3650                 // Execute task
3651                 Utilities.ExecuteTask(t);
3652 
3653                 // Get the OutputResources
3654                 string resourcesFile = t.OutputResources[0].ItemSpec;
3655 
3656                 // Verify that the OutputResources has the same name as Sources (=textFile)
3657                 Assert.True(Path.GetFileNameWithoutExtension(textFile).Equals(Path.GetFileNameWithoutExtension(t.OutputResources[0].ItemSpec)));
3658 
3659                 // Verify that STR class name should have been generated from the output
3660                 string stronglyTypedClassName = Path.GetFileNameWithoutExtension(t.OutputResources[0].ItemSpec);
3661                 Assert.Equal(t.StronglyTypedClassName, stronglyTypedClassName);
3662 
3663                 // Verify that the extension of the resource file is .resources
3664                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
3665 
3666                 // Verify that the 1st item in FilesWritten property is the .resource file generated
3667                 resourcesFile = t.FilesWritten[0].ItemSpec;
3668                 Assert.Equal(Path.GetExtension(resourcesFile), ".resources");
3669 
3670                 Utilities.AssertStateFileWasWritten(t);
3671 
3672                 // Files written should contain STR class file
3673                 Assert.Equal(Path.ChangeExtension(t.Sources[0].ItemSpec, codeFileExtension), t.StronglyTypedFileName);
3674                 Assert.Equal(t.FilesWritten[2].ItemSpec, t.StronglyTypedFileName);
3675 
3676                 // Verify that the STR File is generated
3677                 Assert.True(File.Exists(t.StronglyTypedFileName));
3678 
3679                 // Verify that the STR File was generated correctly
3680                 string STRFile = Path.ChangeExtension(textFile, codeFileExtension);
3681                 // Verify that the ResourceManager in the STR class is instantiated correctly
3682                 Assert.True(Utilities.ReadFileContent(STRFile).Contains("ResourceManager(\"" + resourcesNamespace + "." + t.StronglyTypedClassName));
3683                 // Verify that the class name of the STR class is as expected
3684                 Assert.True(Utilities.ReadFileContent(STRFile).ToLower().Contains("class " + Path.GetFileNameWithoutExtension(textFile).ToLower()));
3685                 // Verify that the namespace of the STR class is as expected
3686 
3687                 Assert.False(Utilities.ReadFileContent(STRFile).ToLower().Contains("namespace " + resourcesNamespace.ToLower()));
3688                 if (classNamespace != null)
3689                     Assert.True(Utilities.ReadFileContent(STRFile).ToLower().Contains("namespace " + classNamespace.ToLower()));
3690 
3691 
3692                 // Verify log is as expected
3693                 Utilities.AssertLogContainsResource(t, "GenerateResource.ProcessingFile", textFile, resourcesFile);
3694                 Utilities.AssertLogContainsResource(t, "GenerateResource.ReadResourceMessage", 4, textFile);
3695 
3696                 string typeName = null;
3697                 if (t.StronglyTypedNamespace != null)
3698                     typeName = t.StronglyTypedNamespace + ".";
3699                 else
3700                     typeName = "";
3701 
3702                 typeName += t.StronglyTypedClassName;
3703                 // Verify that the type is generated correctly
3704                 Utilities.AssertLogContainsResource(t, "GenerateResource.CreatingSTR", t.StronglyTypedFileName);
3705             }
3706             finally
3707             {
3708                 // Done, so clean up.
3709                 File.Delete(t.Sources[0].ItemSpec);
3710                 File.Delete(t.StronglyTypedFileName);
3711                 foreach (ITaskItem item in t.FilesWritten)
3712                 {
3713                     if (File.Exists(item.ItemSpec))
3714                     {
3715                         File.Delete(item.ItemSpec);
3716                     }
3717                 }
3718             }
3719         }
3720     }
3721 
3722     /// <summary>
3723     /// Extends the CommandLineBuilderClass to get at its protected methods.
3724     /// </summary>
3725     internal sealed class CommandLineBuilderHelper : CommandLineBuilder
3726     {
3727         /// <summary>
3728         /// Redirects to the protected method IsQuotingRequired().
3729         /// </summary>
3730         /// <returns>true, if given path needs to be quoted.</returns>
DoesPathNeedQuotes(string path)3731         internal bool DoesPathNeedQuotes(string path)
3732         {
3733             return base.IsQuotingRequired(path);
3734         }
3735     }
3736 }
3737