Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exclude by assembly level System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage #589

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Documentation/MSBuildIntegration.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ dotnet test /p:CollectCoverage=true /p:Threshold=80 /p:ThresholdType=line /p:Thr

### Attributes

You can ignore a method or an entire class from code coverage by creating and applying the `ExcludeFromCodeCoverage` attribute present in the `System.Diagnostics.CodeAnalysis` namespace.
You can ignore a method an entire class or assembly from code coverage by creating and applying the `ExcludeFromCodeCoverage` attribute present in the `System.Diagnostics.CodeAnalysis` namespace.

You can also ignore additional attributes by using the `ExcludeByAttribute` property (short name or full name supported):

Expand Down
7 changes: 7 additions & 0 deletions coverlet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
nuget.config = nuget.config
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.tests.projectsample.excludedbyattribute", "test\coverlet.tests.projectsample.excludedbyattribute\coverlet.tests.projectsample.excludedbyattribute.csproj", "{D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -87,6 +89,10 @@ Global
{085A3AFB-C086-4E98-86F1-1B481446EC5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{085A3AFB-C086-4E98-86F1-1B481446EC5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{085A3AFB-C086-4E98-86F1-1B481446EC5E}.Release|Any CPU.Build.0 = Release|Any CPU
{D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -102,6 +108,7 @@ Global
{5ED4FA81-8F8C-4211-BA88-7573BD63262E} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{3E0F9E47-A1D7-4DF5-841D-A633486E2475} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{085A3AFB-C086-4E98-86F1-1B481446EC5E} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
{D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10}
Expand Down
9 changes: 6 additions & 3 deletions src/coverlet.core/Coverage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,12 @@ public CoveragePrepareResult PrepareModules()
// Guard code path and restore if instrumentation fails.
try
{
var result = instrumenter.Instrument();
_results.Add(result);
_logger.LogVerbose($"Instrumented module: '{module}'");
InstrumenterResult result = instrumenter.Instrument();
if (!instrumenter.SkipModule)
{
_results.Add(result);
_logger.LogVerbose($"Instrumented module: '{module}'");
}
}
catch (Exception ex)
{
Expand Down
3 changes: 2 additions & 1 deletion src/coverlet.core/Helpers/FileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public virtual Stream NewFileStream(string path, FileMode mode)
return new FileStream(path, mode);
}

public Stream NewFileStream(string path, FileMode mode, FileAccess access)
// We need to partial mock this method on tests
public virtual Stream NewFileStream(string path, FileMode mode, FileAccess access)
{
return new FileStream(path, mode, access);
}
Expand Down
12 changes: 12 additions & 0 deletions src/coverlet.core/Instrumentation/Instrumenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ internal class Instrumenter
private List<string> _excludedSourceFiles;
private List<string> _branchesInCompiledGeneratedClass;

public bool SkipModule { get; set; } = false;

public Instrumenter(
string module,
string identifier,
Expand Down Expand Up @@ -151,6 +153,16 @@ private void InstrumentModule()

using (var module = ModuleDefinition.ReadModule(stream, parameters))
{
foreach (CustomAttribute customAttribute in module.Assembly.CustomAttributes)
{
if (customAttribute.AttributeType.FullName == "System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute")
{
_logger.LogVerbose($"Excluded module: '{module}' for assembly level attribute 'System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute'");
SkipModule = true;
return;
}
}

var containsAppContext = module.GetType(nameof(System), nameof(AppContext)) != null;
var types = module.GetTypes();
AddCustomModuleTrackerToModule(module);
Expand Down
20 changes: 20 additions & 0 deletions test/coverlet.core.tests/CoverageTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Coverlet.Core.Abstracts;
using Coverlet.Core.Helpers;
Expand Down Expand Up @@ -40,6 +41,25 @@ public void TestCoverage()
directory.Delete(true);
}

[Fact]
public void TestCoverageSkipModule__AssemblyMarkedAsExcludeFromCodeCoverage()
{
Mock<FileSystem> partialMockFileSystem = new Mock<FileSystem>();
partialMockFileSystem.CallBase = true;
partialMockFileSystem.Setup(fs => fs.NewFileStream(It.IsAny<string>(), It.IsAny<FileMode>(), It.IsAny<FileAccess>())).Returns((string path, FileMode mode, FileAccess access) =>
{
return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
});
var loggerMock = new Mock<ILogger>();

string excludedbyattributeDll = Directory.GetFiles(Path.Combine(Directory.GetCurrentDirectory(), "TestAssets"), "coverlet.tests.projectsample.excludedbyattribute.dll").First();
// test skip module includint test assembly feature
var coverage = new Coverage(excludedbyattributeDll, new string[] { "[coverlet.tests.projectsample.excludedbyattribute*]*" }, Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), true, false, string.Empty, false, loggerMock.Object, _instrumentationHelper, partialMockFileSystem.Object);
CoveragePrepareResult result = coverage.PrepareModules();
Assert.Empty(result.Results);
loggerMock.Verify(l => l.LogVerbose(It.IsAny<string>()));
}

[Fact]
public void TestCoverageWithTestAssembly()
{
Expand Down
17 changes: 17 additions & 0 deletions test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -416,5 +416,22 @@ public void TestInstrument_MissingModule()
loggerMock.Verify(l => l.LogWarning(It.IsAny<string>()));
}

[Fact]
public void TestInstrument_AssemblyMarkedAsExcludeFromCodeCoverage()
{
Mock<FileSystem> partialMockFileSystem = new Mock<FileSystem>();
partialMockFileSystem.CallBase = true;
partialMockFileSystem.Setup(fs => fs.NewFileStream(It.IsAny<string>(), It.IsAny<FileMode>(), It.IsAny<FileAccess>())).Returns((string path, FileMode mode, FileAccess access) =>
{
return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
});
var loggerMock = new Mock<ILogger>();

string excludedbyattributeDll = Directory.GetFiles(Path.Combine(Directory.GetCurrentDirectory(), "TestAssets"), "coverlet.tests.projectsample.excludedbyattribute.dll").First();
Instrumenter instrumenter = new Instrumenter(excludedbyattributeDll, "_xunit_excludedbyattribute", Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), false, loggerMock.Object, _instrumentationHelper, partialMockFileSystem.Object);
InstrumenterResult result = instrumenter.Instrument();
Assert.Empty(result.Documents);
loggerMock.Verify(l => l.LogVerbose(It.IsAny<string>()));
}
}
}
4 changes: 3 additions & 1 deletion test/coverlet.core.tests/coverlet.core.tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Import Project="$(RepoRoot)src\coverlet.msbuild.tasks\coverlet.msbuild.props" />

<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<IsPackable>false</IsPackable>
<LangVersion>preview</LangVersion>
<NoWarn>$(NoWarn);CS8002</NoWarn>
Expand Down Expand Up @@ -39,6 +39,8 @@
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Condition="$([MSBuild]::IsOSPlatform('Windows'))" Command="copy $(ProjectDir)..\coverlet.tests.remoteexecutor\$(OutDir) $(TargetDir) /Y" />
<Exec Condition="$([MSBuild]::IsOSPlatform('OSX')) OR $([MSBuild]::IsOSPlatform('Linux'))" Command="cp -r $(ProjectDir)..\coverlet.tests.remoteexecutor\$(OutDir). $(TargetDir)" />
<Exec Condition="$([MSBuild]::IsOSPlatform('Windows'))" Command="copy $(ProjectDir)..\coverlet.tests.projectsample.excludedbyattribute\$(OutDir) $(TargetDir)TestAssets /Y" />
<Exec Condition="$([MSBuild]::IsOSPlatform('OSX')) OR $([MSBuild]::IsOSPlatform('Linux'))" Command="cp -r $(ProjectDir)..\coverlet.tests.projectsample.excludedbyattribute\$(OutDir). $(TargetDir)TestAssets" />
</Target>

<Import Project="$(RepoRoot)src\coverlet.msbuild.tasks\coverlet.msbuild.targets" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Diagnostics.CodeAnalysis;

[assembly: ExcludeFromCodeCoverage]

namespace coverlet.tests.projectsample.excludedbyattribute
{
public class SampleClass
{
public int SampleMethod()
{
return new Random().Next();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<IsPackable>false</IsPackable>
<IsTestProject>false</IsTestProject>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RootNamespace>Coverlet.Tests.RemoteExecutor</RootNamespace>
<IsPackable>false</IsPackable>
<IsTestProject>false</IsTestProject>
Expand Down