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 = "~!@#$%^&*("; 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