|
22 | 22 |
|
23 | 23 | namespace Coverlet.Core.Instrumentation.Tests |
24 | 24 | { |
25 | | - public class InstrumenterTests |
| 25 | + public class InstrumenterTests : IDisposable |
26 | 26 | { |
27 | 27 | private readonly Mock<ILogger> _mockLogger = new Mock<ILogger>(); |
| 28 | + private Action disposeAction; |
| 29 | + |
| 30 | + public void Dispose() |
| 31 | + { |
| 32 | + if (disposeAction != null) |
| 33 | + { |
| 34 | + disposeAction(); |
| 35 | + } |
| 36 | + } |
28 | 37 |
|
29 | 38 | [ConditionalFact] |
30 | 39 | [SkipOnOS(OS.Linux)] |
@@ -504,29 +513,92 @@ public void TestInstrument_MissingModule() |
504 | 513 | loggerMock.Verify(l => l.LogWarning(It.IsAny<string>())); |
505 | 514 | } |
506 | 515 |
|
507 | | - [Fact] |
508 | | - public void TestInstrument_AssemblyMarkedAsExcludeFromCodeCoverage() |
| 516 | + [Theory] |
| 517 | + [InlineData("NotAMatch", new string[] { }, false)] |
| 518 | + [InlineData("ExcludeFromCoverageAttribute", new string[] { }, true)] |
| 519 | + [InlineData("ExcludeFromCodeCoverageAttribute", new string[] { }, true)] |
| 520 | + [InlineData("CustomExclude", new string[] { "CustomExclude" }, true)] |
| 521 | + [InlineData("CustomExcludeAttribute", new string[] { "CustomExclude" }, true)] |
| 522 | + [InlineData("CustomExcludeAttribute", new string[] { "CustomExcludeAttribute" }, true)] |
| 523 | + public void TestInstrument_AssemblyMarkedAsExcludeFromCodeCoverage(string attributeName, string[] excludedAttributes, bool expectedExcludes) |
509 | 524 | { |
| 525 | + string EmitAssemblyToInstrument(string outputFolder) |
| 526 | + { |
| 527 | + var attributeClassSyntaxTree = CSharpSyntaxTree.ParseText("[System.AttributeUsage(System.AttributeTargets.Assembly)]public class " + attributeName + ":System.Attribute{}"); |
| 528 | + var instrumentableClassSyntaxTree = CSharpSyntaxTree.ParseText($@" |
| 529 | +[assembly:{attributeName}] |
| 530 | +namespace coverlet.tests.projectsample.excludedbyattribute{{ |
| 531 | +public class SampleClass |
| 532 | +{{ |
| 533 | + public int SampleMethod() |
| 534 | + {{ |
| 535 | + return new System.Random().Next(); |
| 536 | + }} |
| 537 | +}} |
| 538 | +
|
| 539 | +}} |
| 540 | +"); |
| 541 | + var compilation = CSharpCompilation.Create(attributeName, new List<SyntaxTree> |
| 542 | + { |
| 543 | + attributeClassSyntaxTree,instrumentableClassSyntaxTree |
| 544 | + }).AddReferences( |
| 545 | + MetadataReference.CreateFromFile(typeof(Attribute).Assembly.Location)). |
| 546 | + WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, false)); |
| 547 | + |
| 548 | + var dllPath = Path.Combine(outputFolder, $"{attributeName}.dll"); |
| 549 | + var pdbPath = Path.Combine(outputFolder, $"{attributeName}.pdb"); |
| 550 | + |
| 551 | + using (var outputStream = File.Create(dllPath)) |
| 552 | + using (var pdbStream = File.Create(pdbPath)) |
| 553 | + { |
| 554 | + var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); |
| 555 | + var emitOptions = new EmitOptions(pdbFilePath: pdbPath); |
| 556 | + var emitResult = compilation.Emit(outputStream, pdbStream, options: isWindows ? emitOptions : emitOptions.WithDebugInformationFormat(DebugInformationFormat.PortablePdb)); |
| 557 | + if (!emitResult.Success) |
| 558 | + { |
| 559 | + var message = "Failure to dynamically create dll"; |
| 560 | + foreach (var diagnostic in emitResult.Diagnostics) |
| 561 | + { |
| 562 | + message += Environment.NewLine; |
| 563 | + message += diagnostic.GetMessage(); |
| 564 | + } |
| 565 | + throw new Xunit.Sdk.XunitException(message); |
| 566 | + } |
| 567 | + } |
| 568 | + return dllPath; |
| 569 | + } |
| 570 | + |
| 571 | + string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); |
| 572 | + Directory.CreateDirectory(tempDirectory); |
| 573 | + disposeAction = () => Directory.Delete(tempDirectory, true); |
| 574 | + |
510 | 575 | Mock<FileSystem> partialMockFileSystem = new Mock<FileSystem>(); |
511 | 576 | partialMockFileSystem.CallBase = true; |
512 | 577 | partialMockFileSystem.Setup(fs => fs.NewFileStream(It.IsAny<string>(), It.IsAny<FileMode>(), It.IsAny<FileAccess>())).Returns((string path, FileMode mode, FileAccess access) => |
513 | 578 | { |
514 | | - return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); |
| 579 | + return new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); |
515 | 580 | }); |
516 | 581 | var loggerMock = new Mock<ILogger>(); |
517 | 582 |
|
518 | | - string excludedbyattributeDll = Directory.GetFiles(Path.Combine(Directory.GetCurrentDirectory(), "TestAssets"), "coverlet.tests.projectsample.excludedbyattribute.dll").First(); |
| 583 | + string excludedbyattributeDll = EmitAssemblyToInstrument(tempDirectory); |
519 | 584 |
|
520 | 585 | InstrumentationHelper instrumentationHelper = |
521 | 586 | new InstrumentationHelper(new ProcessExitHandler(), new RetryHelper(), new FileSystem(), new Mock<ILogger>().Object, |
522 | 587 | new SourceRootTranslator(new Mock<ILogger>().Object, new FileSystem())); |
523 | 588 |
|
524 | 589 | Instrumenter instrumenter = new Instrumenter(excludedbyattributeDll, "_xunit_excludedbyattribute", Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), |
525 | | - Array.Empty<string>(), Array.Empty<string>(), false, false, loggerMock.Object, instrumentationHelper, partialMockFileSystem.Object, new SourceRootTranslator(loggerMock.Object, new FileSystem()), new CecilSymbolHelper()); |
| 590 | + excludedAttributes, Array.Empty<string>(), false, false, loggerMock.Object, instrumentationHelper, partialMockFileSystem.Object, new SourceRootTranslator(loggerMock.Object, new FileSystem()), new CecilSymbolHelper()); |
526 | 591 |
|
527 | 592 | InstrumenterResult result = instrumenter.Instrument(); |
528 | | - Assert.Empty(result.Documents); |
529 | | - loggerMock.Verify(l => l.LogVerbose(It.IsAny<string>())); |
| 593 | + if(expectedExcludes) |
| 594 | + { |
| 595 | + Assert.Empty(result.Documents); |
| 596 | + loggerMock.Verify(l => l.LogVerbose(It.IsAny<string>())); |
| 597 | + } |
| 598 | + else |
| 599 | + { |
| 600 | + Assert.NotEmpty(result.Documents); |
| 601 | + } |
530 | 602 | } |
531 | 603 |
|
532 | 604 | [Fact] |
@@ -710,5 +782,7 @@ public void TestReachabilityHelper() |
710 | 782 |
|
711 | 783 | instrumenterTest.Directory.Delete(true); |
712 | 784 | } |
| 785 | + |
| 786 | + |
713 | 787 | } |
714 | 788 | } |
0 commit comments