Skip to content

Commit 52ddde6

Browse files
authored
Adaptions to exclude by attribute feature (#1603)
1 parent 16eda38 commit 52ddde6

File tree

7 files changed

+32
-21
lines changed

7 files changed

+32
-21
lines changed

Documentation/Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919
- Fix unable to instrument module for Microsoft.AspNetCore.Mvc.Razor [#1459](https://github.com/coverlet-coverage/coverlet/issues/1459) by https://github.com/lg2de
2020

2121
### Improvements
22+
- Extended exclude by attribute feature to work with fully qualified name [#1589](https://github.com/coverlet-coverage/coverlet/issues/1589)
2223
- Use System.CommandLine instead of McMaster.Extensions.CommandLineUtils [#1474](https://github.com/coverlet-coverage/coverlet/issues/1474) by https://github.com/Bertk
2324
- Fix deadlog in Coverlet.Integration.Tests.BaseTest [#1541](https://github.com/coverlet-coverage/coverlet/pull/1541) by https://github.com/Bertk
2425
- Add coverlet.msbuild.tasks unit tests [#1534](https://github.com/coverlet-coverage/coverlet/pull/1534) by https://github.com/Bertk

Documentation/GlobalTool.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,10 @@ coverlet <ASSEMBLY> --target <TARGET> --targetargs <TARGETARGS> --threshold 80 -
182182

183183
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.
184184

185-
You can also ignore additional attributes by using the `ExcludeByAttribute` property (short name or full name supported):
185+
You can also ignore additional attributes by using the `ExcludeByAttribute` property
186+
187+
* Can be specified multiple times
188+
* Use attribute name, attribute full name or fully qualified name of the attribute type (`Obsolete`, `ObsoleteAttribute`, `System.ObsoleteAttribute`)
186189

187190
```bash
188191
coverlet <ASSEMBLY> --target <TARGET> --targetargs <TARGETARGS> --exclude-by-attribute 'Obsolete' --exclude-by-attribute 'GeneratedCode' --exclude-by-attribute 'CompilerGenerated'

Documentation/MSBuildIntegration.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,10 @@ dotnet test /p:CollectCoverage=true /p:Threshold=80 /p:ThresholdType=line /p:Thr
138138

139139
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.
140140

141-
You can also ignore additional attributes by using the `ExcludeByAttribute` property (short name, i.e. the type name without the namespace, supported only):
141+
You can also ignore additional attributes by using the `ExcludeByAttribute` property
142+
143+
* Use single or multiple attributes (separate by comma)
144+
* Use attribute name, attribute full name or fully qualified name of the attribute type (`Obsolete`, `ObsoleteAttribute`, `System.ObsoleteAttribute`)
142145

143146
```bash
144147
dotnet test /p:CollectCoverage=true /p:ExcludeByAttribute="Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute"

Documentation/VSTestIntegration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ These are a list of options that are supported by coverlet. These can be specifi
100100
|:-------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------|
101101
| Format | Coverage output format. These are either cobertura, json, lcov, opencover or teamcity as well as combinations of these formats. |
102102
| Exclude | Exclude from code coverage analysing using filter expressions. |
103+
| ExcludeByAttribute | Exclude a method, an entire class or assembly from code coverage decorated by an attribute. |
103104
| ExcludeByFile | Ignore specific source files from code coverage. |
104105
| Include | Explicitly set what to include in code coverage analysis using filter expressions. |
105106
| IncludeDirectory | Explicitly set which directories to include in code coverage analysis. |

src/coverlet.core/Instrumentation/Instrumenter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,8 @@ private static void ReplaceExceptionHandlerBoundary(ExceptionHandler handler, ID
774774

775775
private bool IsExcludeAttribute(CustomAttribute customAttribute)
776776
{
777-
return Array.IndexOf(_excludedAttributes, customAttribute.AttributeType.Name) != -1;
777+
return Array.IndexOf(_excludedAttributes, customAttribute.AttributeType.Name) != -1 ||
778+
Array.IndexOf(_excludedAttributes, customAttribute.AttributeType.FullName) != -1;
778779
}
779780

780781
private static MethodBody GetMethodBody(MethodDefinition method)

test/coverlet.collector.tests/CoverletSettingsParserTests.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,18 @@ public void ParseShouldSelectFirstTestModuleFromTestModulesList()
4646
}
4747

4848
[Theory]
49-
[InlineData("[*]*,[coverlet]*", "[coverlet.*.tests?]*,[coverlet.*.tests.*]*", @"E:\temp,/var/tmp", "module1.cs,module2.cs", "Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute", "DoesNotReturnAttribute,ThrowsAttribute")]
50-
[InlineData("[*]*,,[coverlet]*", "[coverlet.*.tests?]*,,[coverlet.*.tests.*]*", @"E:\temp,,/var/tmp", "module1.cs,,module2.cs", "Obsolete,,GeneratedCodeAttribute,,CompilerGeneratedAttribute", "DoesNotReturnAttribute,,ThrowsAttribute")]
51-
[InlineData("[*]*, ,[coverlet]*", "[coverlet.*.tests?]*, ,[coverlet.*.tests.*]*", @"E:\temp, ,/var/tmp", "module1.cs, ,module2.cs", "Obsolete, ,GeneratedCodeAttribute, ,CompilerGeneratedAttribute", "DoesNotReturnAttribute, ,ThrowsAttribute")]
52-
[InlineData("[*]*,\t,[coverlet]*", "[coverlet.*.tests?]*,\t,[coverlet.*.tests.*]*", "E:\\temp,\t,/var/tmp", "module1.cs,\t,module2.cs", "Obsolete,\t,GeneratedCodeAttribute,\t,CompilerGeneratedAttribute", "DoesNotReturnAttribute,\t,ThrowsAttribute")]
53-
[InlineData("[*]*, [coverlet]*", "[coverlet.*.tests?]*, [coverlet.*.tests.*]*", @"E:\temp, /var/tmp", "module1.cs, module2.cs", "Obsolete, GeneratedCodeAttribute, CompilerGeneratedAttribute", "DoesNotReturnAttribute, ThrowsAttribute")]
54-
[InlineData("[*]*,\t[coverlet]*", "[coverlet.*.tests?]*,\t[coverlet.*.tests.*]*", "E:\\temp,\t/var/tmp", "module1.cs,\tmodule2.cs", "Obsolete,\tGeneratedCodeAttribute,\tCompilerGeneratedAttribute", "DoesNotReturnAttribute,\tThrowsAttribute")]
55-
[InlineData("[*]*, \t[coverlet]*", "[coverlet.*.tests?]*, \t[coverlet.*.tests.*]*", "E:\\temp, \t/var/tmp", "module1.cs, \tmodule2.cs", "Obsolete, \tGeneratedCodeAttribute, \tCompilerGeneratedAttribute", "DoesNotReturnAttribute, \tThrowsAttribute")]
56-
[InlineData("[*]*,\r\n[coverlet]*", "[coverlet.*.tests?]*,\r\n[coverlet.*.tests.*]*", "E:\\temp,\r\n/var/tmp", "module1.cs,\r\nmodule2.cs", "Obsolete,\r\nGeneratedCodeAttribute,\r\nCompilerGeneratedAttribute", "DoesNotReturnAttribute,\r\nThrowsAttribute")]
57-
[InlineData("[*]*, \r\n [coverlet]*", "[coverlet.*.tests?]*, \r\n [coverlet.*.tests.*]*", "E:\\temp, \r\n /var/tmp", "module1.cs, \r\n module2.cs", "Obsolete, \r\n GeneratedCodeAttribute, \r\n CompilerGeneratedAttribute", "DoesNotReturnAttribute, \r\n ThrowsAttribute")]
58-
[InlineData("[*]*,\t\r\n\t[coverlet]*", "[coverlet.*.tests?]*,\t\r\n\t[coverlet.*.tests.*]*", "E:\\temp,\t\r\n\t/var/tmp", "module1.cs,\t\r\n\tmodule2.cs", "Obsolete,\t\r\n\tGeneratedCodeAttribute,\t\r\n\tCompilerGeneratedAttribute", "DoesNotReturnAttribute,\t\r\n\tThrowsAttribute")]
59-
[InlineData("[*]*, \t \r\n \t [coverlet]*", "[coverlet.*.tests?]*, \t \r\n \t [coverlet.*.tests.*]*", "E:\\temp, \t \r\n \t /var/tmp", "module1.cs, \t \r\n \t module2.cs", "Obsolete, \t \r\n \t GeneratedCodeAttribute, \t \r\n \t CompilerGeneratedAttribute", "DoesNotReturnAttribute, \t \r\n \t ThrowsAttribute")]
60-
[InlineData(" [*]* , [coverlet]* ", " [coverlet.*.tests?]* , [coverlet.*.tests.*]* ", " E:\\temp , /var/tmp ", " module1.cs , module2.cs ", " Obsolete , GeneratedCodeAttribute , CompilerGeneratedAttribute ", "DoesNotReturnAttribute , ThrowsAttribute")]
49+
[InlineData("[*]*,[coverlet]*", "[coverlet.*.tests?]*,[coverlet.*.tests.*]*", @"E:\temp,/var/tmp", "module1.cs,module2.cs", "Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute,System.ObsoleteAttribute", "DoesNotReturnAttribute,ThrowsAttribute")]
50+
[InlineData("[*]*,,[coverlet]*", "[coverlet.*.tests?]*,,[coverlet.*.tests.*]*", @"E:\temp,,/var/tmp", "module1.cs,,module2.cs", "Obsolete,,GeneratedCodeAttribute,,CompilerGeneratedAttribute,,System.ObsoleteAttribute", "DoesNotReturnAttribute,,ThrowsAttribute")]
51+
[InlineData("[*]*, ,[coverlet]*", "[coverlet.*.tests?]*, ,[coverlet.*.tests.*]*", @"E:\temp, ,/var/tmp", "module1.cs, ,module2.cs", "Obsolete, ,GeneratedCodeAttribute, ,CompilerGeneratedAttribute, ,System.ObsoleteAttribute", "DoesNotReturnAttribute, ,ThrowsAttribute")]
52+
[InlineData("[*]*,\t,[coverlet]*", "[coverlet.*.tests?]*,\t,[coverlet.*.tests.*]*", "E:\\temp,\t,/var/tmp", "module1.cs,\t,module2.cs", "Obsolete,\t,GeneratedCodeAttribute,\t,CompilerGeneratedAttribute,\t,System.ObsoleteAttribute", "DoesNotReturnAttribute,\t,ThrowsAttribute")]
53+
[InlineData("[*]*, [coverlet]*", "[coverlet.*.tests?]*, [coverlet.*.tests.*]*", @"E:\temp, /var/tmp", "module1.cs, module2.cs", "Obsolete, GeneratedCodeAttribute, CompilerGeneratedAttribute, System.ObsoleteAttribute", "DoesNotReturnAttribute, ThrowsAttribute")]
54+
[InlineData("[*]*,\t[coverlet]*", "[coverlet.*.tests?]*,\t[coverlet.*.tests.*]*", "E:\\temp,\t/var/tmp", "module1.cs,\tmodule2.cs", "Obsolete,\tGeneratedCodeAttribute,\tCompilerGeneratedAttribute,\tSystem.ObsoleteAttribute", "DoesNotReturnAttribute,\tThrowsAttribute")]
55+
[InlineData("[*]*, \t[coverlet]*", "[coverlet.*.tests?]*, \t[coverlet.*.tests.*]*", "E:\\temp, \t/var/tmp", "module1.cs, \tmodule2.cs", "Obsolete, \tGeneratedCodeAttribute, \tCompilerGeneratedAttribute, \tSystem.ObsoleteAttribute", "DoesNotReturnAttribute, \tThrowsAttribute")]
56+
[InlineData("[*]*,\r\n[coverlet]*", "[coverlet.*.tests?]*,\r\n[coverlet.*.tests.*]*", "E:\\temp,\r\n/var/tmp", "module1.cs,\r\nmodule2.cs", "Obsolete,\r\nGeneratedCodeAttribute,\r\nCompilerGeneratedAttribute,\r\nSystem.ObsoleteAttribute", "DoesNotReturnAttribute,\r\nThrowsAttribute")]
57+
[InlineData("[*]*, \r\n [coverlet]*", "[coverlet.*.tests?]*, \r\n [coverlet.*.tests.*]*", "E:\\temp, \r\n /var/tmp", "module1.cs, \r\n module2.cs", "Obsolete, \r\n GeneratedCodeAttribute, \r\n CompilerGeneratedAttribute, \r\n System.ObsoleteAttribute", "DoesNotReturnAttribute, \r\n ThrowsAttribute")]
58+
[InlineData("[*]*,\t\r\n\t[coverlet]*", "[coverlet.*.tests?]*,\t\r\n\t[coverlet.*.tests.*]*", "E:\\temp,\t\r\n\t/var/tmp", "module1.cs,\t\r\n\tmodule2.cs", "Obsolete,\t\r\n\tGeneratedCodeAttribute,\t\r\n\tCompilerGeneratedAttribute,\t\r\n\tSystem.ObsoleteAttribute", "DoesNotReturnAttribute,\t\r\n\tThrowsAttribute")]
59+
[InlineData("[*]*, \t \r\n \t [coverlet]*", "[coverlet.*.tests?]*, \t \r\n \t [coverlet.*.tests.*]*", "E:\\temp, \t \r\n \t /var/tmp", "module1.cs, \t \r\n \t module2.cs", "Obsolete, \t \r\n \t GeneratedCodeAttribute, \t \r\n \t CompilerGeneratedAttribute, \t \r\n \t System.ObsoleteAttribute", "DoesNotReturnAttribute, \t \r\n \t ThrowsAttribute")]
60+
[InlineData(" [*]* , [coverlet]* ", " [coverlet.*.tests?]* , [coverlet.*.tests.*]* ", " E:\\temp , /var/tmp ", " module1.cs , module2.cs ", " Obsolete , GeneratedCodeAttribute , CompilerGeneratedAttribute , System.ObsoleteAttribute ", "DoesNotReturnAttribute , ThrowsAttribute")]
6161
public void ParseShouldCorrectlyParseConfigurationElement(string includeFilters,
6262
string excludeFilters,
6363
string includeDirectories,

test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ public void TestInstrument_ClassesWithExcludeAttributeAreExcluded(Type excludedT
145145

146146
[Theory]
147147
[InlineData(typeof(ClassExcludedByAttrWithoutAttributeNameSuffix), nameof(TestSDKAutoGeneratedCode))]
148+
[InlineData(typeof(ClassExcludedByAttrWithoutAttributeNameSuffix), "Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode")]
148149
public void TestInstrument_ClassesWithExcludeAttributeWithoutAttributeNameSuffixAreExcluded(Type excludedType, string excludedAttribute)
149150
{
150151
InstrumenterTest instrumenterTest = CreateInstrumentor(attributesToIgnore: new string[] { excludedAttribute });
@@ -162,7 +163,7 @@ public void TestInstrument_ClassesWithExcludeAttributeWithoutAttributeNameSuffix
162163
[Theory]
163164
[InlineData(nameof(ObsoleteAttribute))]
164165
[InlineData("Obsolete")]
165-
[InlineData(nameof(TestSDKAutoGeneratedCode))]
166+
[InlineData("System.ObsoleteAttribute")]
166167
public void TestInstrument_ClassesWithCustomExcludeAttributeAreExcluded(string excludedAttribute)
167168
{
168169
InstrumenterTest instrumenterTest = CreateInstrumentor(attributesToIgnore: new string[] { excludedAttribute });
@@ -171,7 +172,7 @@ public void TestInstrument_ClassesWithCustomExcludeAttributeAreExcluded(string e
171172
Document doc = result.Documents.Values.FirstOrDefault(d => Path.GetFileName(d.Path) == "Samples.cs");
172173
Assert.NotNull(doc);
173174
#pragma warning disable CS0612 // Type or member is obsolete
174-
bool found = doc.Lines.Values.Any(l => l.Class.Equals(nameof(ClassExcludedByObsoleteAttr)));
175+
bool found = doc.Lines.Values.Any(l => l.Class.Equals(typeof(ClassExcludedByObsoleteAttr).FullName));
175176
#pragma warning restore CS0612 // Type or member is obsolete
176177
Assert.False(found, "Class decorated with with exclude attribute should be excluded");
177178

@@ -181,7 +182,8 @@ public void TestInstrument_ClassesWithCustomExcludeAttributeAreExcluded(string e
181182
[Theory]
182183
[InlineData(nameof(ObsoleteAttribute), "ClassWithMethodExcludedByObsoleteAttr")]
183184
[InlineData("Obsolete", "ClassWithMethodExcludedByObsoleteAttr")]
184-
[InlineData(nameof(TestSDKAutoGeneratedCode), "ClassExcludedByAttrWithoutAttributeNameSuffix")]
185+
[InlineData("System.ObsoleteAttribute", "ClassWithMethodExcludedByObsoleteAttr")]
186+
[InlineData(nameof(TestSDKAutoGeneratedCode), "ClassExcludedByAttrWithoutAttributeNameSuffix")] //extend this with full name
185187
public void TestInstrument_ClassesWithMethodWithCustomExcludeAttributeAreExcluded(string excludedAttribute, string testClassName)
186188
{
187189
InstrumenterTest instrumenterTest = CreateInstrumentor(attributesToIgnore: new string[] { excludedAttribute });
@@ -196,9 +198,9 @@ public void TestInstrument_ClassesWithMethodWithCustomExcludeAttributeAreExclude
196198
}
197199

198200
[Theory]
199-
[InlineData(nameof(ObsoleteAttribute), "ClassWithMethodExcludedByObsoleteAttr")]
200-
[InlineData("Obsolete", "ClassWithMethodExcludedByObsoleteAttr")]
201-
[InlineData(nameof(TestSDKAutoGeneratedCode), "ClassExcludedByAttrWithoutAttributeNameSuffix")]
201+
[InlineData(nameof(ObsoleteAttribute), "ClassWithPropertyExcludedByObsoleteAttr")]
202+
[InlineData("Obsolete", "ClassWithPropertyExcludedByObsoleteAttr")]
203+
[InlineData("System.ObsoleteAttribute", "ClassWithPropertyExcludedByObsoleteAttr")]
202204
public void TestInstrument_ClassesWithPropertyWithCustomExcludeAttributeAreExcluded(string excludedAttribute, string testClassName)
203205
{
204206
InstrumenterTest instrumenterTest = CreateInstrumentor(attributesToIgnore: new string[] { excludedAttribute });

0 commit comments

Comments
 (0)