diff --git a/README.md b/README.md index c82cf4ee7..0f8a796c6 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,8 @@ You can ignore a method or an entire class from code coverage by creating and ap * ExcludeFromCoverage * ExcludeFromCoverageAttribute +* ExcludeFromCodeCoverage +* ExcludeFromCodeCoverageAttribute Coverlet just uses the type name, so the attributes can be created under any namespace of your choosing. diff --git a/coverlet.sln b/coverlet.sln index 3571bc819..f616006d5 100644 --- a/coverlet.sln +++ b/coverlet.sln @@ -1,19 +1,18 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26124.0 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.core", "src\coverlet.core\coverlet.core.csproj", "{31084026-D563-4B91-BE71-174C4270CCF4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core", "src\coverlet.core\coverlet.core.csproj", "{31084026-D563-4B91-BE71-174C4270CCF4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.msbuild.tasks", "src\coverlet.msbuild.tasks\coverlet.msbuild.tasks.csproj", "{FA73E423-9790-4F35-B018-3C4E3CA338BA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.msbuild.tasks", "src\coverlet.msbuild.tasks\coverlet.msbuild.tasks.csproj", "{FA73E423-9790-4F35-B018-3C4E3CA338BA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.core.tests", "test\coverlet.core.tests\coverlet.core.tests.csproj", "{E7637CC6-43F7-461A-A0BF-3C14562419BD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.tests", "test\coverlet.core.tests\coverlet.core.tests.csproj", "{E7637CC6-43F7-461A-A0BF-3C14562419BD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.console", "src\coverlet.console\coverlet.console.csproj", "{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.console", "src\coverlet.console\coverlet.console.csproj", "{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -24,58 +23,58 @@ Global Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.ActiveCfg = Debug|x64 - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.Build.0 = Debug|x64 - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.ActiveCfg = Debug|x86 - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.Build.0 = Debug|x86 + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.ActiveCfg = Debug|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.Build.0 = Debug|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.ActiveCfg = Debug|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.Build.0 = Debug|Any CPU {31084026-D563-4B91-BE71-174C4270CCF4}.Release|Any CPU.ActiveCfg = Release|Any CPU {31084026-D563-4B91-BE71-174C4270CCF4}.Release|Any CPU.Build.0 = Release|Any CPU - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.ActiveCfg = Release|x64 - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.Build.0 = Release|x64 - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.ActiveCfg = Release|x86 - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.Build.0 = Release|x86 + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.ActiveCfg = Release|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.Build.0 = Release|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.ActiveCfg = Release|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.Build.0 = Release|Any CPU {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.ActiveCfg = Debug|x64 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.Build.0 = Debug|x64 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.ActiveCfg = Debug|x86 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.Build.0 = Debug|x86 + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.ActiveCfg = Debug|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.Build.0 = Debug|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.ActiveCfg = Debug|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.Build.0 = Debug|Any CPU {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|Any CPU.Build.0 = Release|Any CPU - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.ActiveCfg = Release|x64 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.Build.0 = Release|x64 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.ActiveCfg = Release|x86 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.Build.0 = Release|x86 + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.ActiveCfg = Release|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.Build.0 = Release|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.ActiveCfg = Release|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.Build.0 = Release|Any CPU {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.ActiveCfg = Debug|x64 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.Build.0 = Debug|x64 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.ActiveCfg = Debug|x86 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.Build.0 = Debug|x86 + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.ActiveCfg = Debug|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.Build.0 = Debug|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.ActiveCfg = Debug|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.Build.0 = Debug|Any CPU {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|Any CPU.ActiveCfg = Release|Any CPU {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|Any CPU.Build.0 = Release|Any CPU - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.ActiveCfg = Release|x64 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.Build.0 = Release|x64 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.ActiveCfg = Release|x86 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.Build.0 = Release|x86 + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.ActiveCfg = Release|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.Build.0 = Release|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.ActiveCfg = Release|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.Build.0 = Release|Any CPU {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.ActiveCfg = Debug|x64 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.Build.0 = Debug|x64 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.ActiveCfg = Debug|x86 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.Build.0 = Debug|x86 + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.ActiveCfg = Debug|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.Build.0 = Debug|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.ActiveCfg = Debug|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.Build.0 = Debug|Any CPU {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|Any CPU.ActiveCfg = Release|Any CPU {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|Any CPU.Build.0 = Release|Any CPU - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.ActiveCfg = Release|x64 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.Build.0 = Release|x64 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.ActiveCfg = Release|x86 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.Build.0 = Release|x86 + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.ActiveCfg = Release|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.Build.0 = Release|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.ActiveCfg = Release|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {31084026-D563-4B91-BE71-174C4270CCF4} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} @@ -83,4 +82,7 @@ Global {E7637CC6-43F7-461A-A0BF-3C14562419BD} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10} + EndGlobalSection EndGlobal diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index fdbd56c97..c94a92d32 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; @@ -269,7 +270,16 @@ private static void ReplaceExceptionHandlerBoundary(ExceptionHandler handler, In private static bool IsExcludeAttribute(CustomAttribute customAttribute) { - return customAttribute.AttributeType.Name == nameof(ExcludeFromCoverageAttribute) || customAttribute.AttributeType.Name == "ExcludeFromCoverage"; + var excludeAttributeNames = new[] + { + nameof(ExcludeFromCoverageAttribute), + "ExcludeFromCoverage", + nameof(ExcludeFromCodeCoverageAttribute), + "ExcludeFromCodeCoverage" + }; + + var attributeName = customAttribute.AttributeType.Name; + return excludeAttributeNames.Any(a => a.Equals(attributeName)); } private static Mono.Cecil.Cil.MethodBody GetMethodBody(MethodDefinition method) diff --git a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs index 622cc1f7b..8f3c11b12 100644 --- a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs +++ b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs @@ -1,8 +1,9 @@ using System; using System.IO; - +using System.Linq; using Xunit; using Coverlet.Core.Instrumentation; +using Coverlet.Core.Samples.Tests; namespace Coverlet.Core.Instrumentation.Tests { @@ -10,24 +11,65 @@ public class InstrumenterTests { [Fact] public void TestInstrument() + { + var instrumenterTest = CreateInstrumentor(); + + var result = instrumenterTest.Instrumenter.Instrument(); + + Assert.Equal(Path.GetFileNameWithoutExtension(instrumenterTest.Module), result.Module); + Assert.Equal(instrumenterTest.Module, result.ModulePath); + + instrumenterTest.Directory.Delete(true); + } + + [Theory] + [InlineData(typeof(ClassExcludedByCodeAnalysisCodeCoverageAttr))] + [InlineData(typeof(ClassExcludedByCoverletCodeCoverageAttr))] + public void TestInstrument_ClassesWithExcludeAttributeAreExcluded(Type excludedType) + { + var instrumenterTest = CreateInstrumentor(); + var result = instrumenterTest.Instrumenter.Instrument(); + + var doc = result.Documents.FirstOrDefault(d => Path.GetFileName(d.Path) == "Samples.cs"); + Assert.NotNull(doc); + + var found = doc.Lines.Any(l => l.Class == excludedType.FullName); + Assert.False(found, "Class decorated with with exclude attribute should be excluded"); + + instrumenterTest.Directory.Delete(true); + } + + private InstrumenterTest CreateInstrumentor() { string module = GetType().Assembly.Location; string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb"); string identifier = Guid.NewGuid().ToString(); - var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), identifier)); + DirectoryInfo directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), identifier)); File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true); File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true); module = Path.Combine(directory.FullName, Path.GetFileName(module)); Instrumenter instrumenter = new Instrumenter(module, identifier, Array.Empty(), Array.Empty()); - var result = instrumenter.Instrument(); + return new InstrumenterTest + { + Instrumenter = instrumenter, + Module = module, + Identifier = identifier, + Directory = directory + }; + } + + class InstrumenterTest + { + public Instrumenter Instrumenter { get; set; } + + public string Module { get; set; } - Assert.Equal(Path.GetFileNameWithoutExtension(module), result.Module); - Assert.Equal(module, result.ModulePath); + public string Identifier { get; set; } - directory.Delete(true); + public DirectoryInfo Directory { get; set; } } } } \ No newline at end of file diff --git a/test/coverlet.core.tests/Samples/Samples.cs b/test/coverlet.core.tests/Samples/Samples.cs index 518a848fb..31917b11c 100644 --- a/test/coverlet.core.tests/Samples/Samples.cs +++ b/test/coverlet.core.tests/Samples/Samples.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Threading.Tasks; +using Coverlet.Core.Attributes; namespace Coverlet.Core.Samples.Tests { @@ -161,4 +163,32 @@ public IEnumerable Fetch() yield return "two"; } } + + [ExcludeFromCoverage] + public class ClassExcludedByCoverletCodeCoverageAttr + { + + public string Method(string input) + { + if(string.IsNullOrEmpty(input)) + throw new ArgumentException("Cannot be empty", nameof(input)); + + return input; + } + } + + [ExcludeFromCodeCoverage] + public class ClassExcludedByCodeAnalysisCodeCoverageAttr + { + + public string Method(string input) + { + if (string.IsNullOrEmpty(input)) + throw new ArgumentException("Cannot be empty", nameof(input)); + + return input; + } + } + + } \ No newline at end of file diff --git a/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs b/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs index 0cd079683..c37a1d2af 100644 --- a/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs +++ b/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs @@ -39,8 +39,8 @@ public void GetBranchPoints_OneBranch() Assert.Equal(points[0].Offset, points[1].Offset); Assert.Equal(0, points[0].Path); Assert.Equal(1, points[1].Path); - Assert.Equal(19, points[0].StartLine); - Assert.Equal(19, points[1].StartLine); + Assert.Equal(21, points[0].StartLine); + Assert.Equal(21, points[1].StartLine); Assert.NotNull(points[1].Document); Assert.Equal(points[0].Document, points[1].Document); } @@ -86,8 +86,8 @@ public void GetBranchPoints_TwoBranch() Assert.Equal(4, points.Count()); Assert.Equal(points[0].Offset, points[1].Offset); Assert.Equal(points[2].Offset, points[3].Offset); - Assert.Equal(25, points[0].StartLine); - Assert.Equal(26, points[2].StartLine); + Assert.Equal(27, points[0].StartLine); + Assert.Equal(28, points[2].StartLine); } [Fact] @@ -104,8 +104,8 @@ public void GetBranchPoints_CompleteIf() Assert.NotNull(points); Assert.Equal(2, points.Count()); Assert.Equal(points[0].Offset, points[1].Offset); - Assert.Equal(32, points[0].StartLine); - Assert.Equal(32, points[1].StartLine); + Assert.Equal(34, points[0].StartLine); + Assert.Equal(34, points[1].StartLine); } [Fact] @@ -125,10 +125,10 @@ public void GetBranchPoints_Switch() Assert.Equal(points[0].Offset, points[2].Offset); Assert.Equal(3, points[3].Path); - Assert.Equal(44, points[0].StartLine); - Assert.Equal(44, points[1].StartLine); - Assert.Equal(44, points[2].StartLine); - Assert.Equal(44, points[3].StartLine); + Assert.Equal(46, points[0].StartLine); + Assert.Equal(46, points[1].StartLine); + Assert.Equal(46, points[2].StartLine); + Assert.Equal(46, points[3].StartLine); } [Fact] @@ -148,10 +148,10 @@ public void GetBranchPoints_SwitchWithDefault() Assert.Equal(points[0].Offset, points[2].Offset); Assert.Equal(3, points[3].Path); - Assert.Equal(58, points[0].StartLine); - Assert.Equal(58, points[1].StartLine); - Assert.Equal(58, points[2].StartLine); - Assert.Equal(58, points[3].StartLine); + Assert.Equal(60, points[0].StartLine); + Assert.Equal(60, points[1].StartLine); + Assert.Equal(60, points[2].StartLine); + Assert.Equal(60, points[3].StartLine); } [Fact] @@ -171,10 +171,10 @@ public void GetBranchPoints_SwitchWithBreaks() Assert.Equal(points[0].Offset, points[2].Offset); Assert.Equal(3, points[3].Path); - Assert.Equal(74, points[0].StartLine); - Assert.Equal(74, points[1].StartLine); - Assert.Equal(74, points[2].StartLine); - Assert.Equal(74, points[3].StartLine); + Assert.Equal(76, points[0].StartLine); + Assert.Equal(76, points[1].StartLine); + Assert.Equal(76, points[2].StartLine); + Assert.Equal(76, points[3].StartLine); } [Fact] @@ -195,10 +195,10 @@ public void GetBranchPoints_SwitchWithMultipleCases() Assert.Equal(points[0].Offset, points[3].Offset); Assert.Equal(3, points[3].Path); - Assert.Equal(92, points[0].StartLine); - Assert.Equal(92, points[1].StartLine); - Assert.Equal(92, points[2].StartLine); - Assert.Equal(92, points[3].StartLine); + Assert.Equal(94, points[0].StartLine); + Assert.Equal(94, points[1].StartLine); + Assert.Equal(94, points[2].StartLine); + Assert.Equal(94, points[3].StartLine); } [Fact]