-
Notifications
You must be signed in to change notification settings - Fork 392
Add ability to exclude assemblies by expression #94
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
Conversation
Codecov Report
@@ Coverage Diff @@
## master #94 +/- ##
==========================================
- Coverage 98.83% 96.86% -1.98%
==========================================
Files 15 15
Lines 1291 1372 +81
==========================================
+ Hits 1276 1329 +53
- Misses 15 43 +28
Continue to review full report at Codecov.
|
| foreach (var filter in filters) | ||
| { | ||
| if (!IsValidFilterExpression(filter)) | ||
| continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do not signal invalid espression with something like console log/error?
|
Nice!! Good work I see that you already merged this but here my two cents: In Coverage.PrepareModules method the 'InstrumentationHelper.IsModuleExcluded' is called for every module. The _filter field is passed as a parameter. Inside the IsModuleExcluded, each item in _filter variable is checked for validity (IsValidFilterExpression(filter)). I would take this up to reduce the number of calls to IsValidFilterExpression. So something like this (did not try to compile ): public void PrepareModules()
{
string[] modules = InstrumentationHelper.GetCoverableModules(_module);
string[] excludedFiles = InstrumentationHelper.GetExcludedFiles(_rules);
_filters = _filters.Where(filterItem => IsValidFilterExpression(filterItem)).ToArray();
// at this point, _filters contains only valid expressions.
}You can even take if further by not storing the filters as string[] but by creating new SomeSortOfModuleTypeFilterObject containing a module and type regex. class SomeSortOfModuleTypeFilterObject
{
public SomeSortOfModuleTypeFilterObject ( Regex typeRegex, Regex moduleRegex)
{
TypeRegex = typeRegex;
ModuleRegex = moduleRegex;
}
public Regex TypeRegex { get; }
public Regex ModuleRegex { get; }
}
public void PrepareModules()
{
string[] modules = InstrumentationHelper.GetCoverableModules(_module);
string[] excludedFiles = InstrumentationHelper.GetExcludedFiles(_rules);
var filters = _filters
.Where(filterItem => IsValidFilterExpression(filterItem))
.Select(filter =>
{
// Code taken from InstrumentationHelper.IsModuleExcluded.
// Probably such code will end up in its own method.
string typePattern = filter.Substring(filter.IndexOf(']') + 1);
string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1);
typePattern = WildcardToRegex(typePattern);
modulePattern = WildcardToRegex(modulePattern);
return new SomeSortOfModuleTypeFilterObject (
new Regex(typePattern, RegexOptions.Compiled),
new Regex(modulePattern, RegexOptions.Compiled));
// or using some sort of lazy construction..
// return new SomeSortOfModuleTypeFilterObject (
// new Lazy<Regex>(() => new Regex(typePattern, RegexOptions.Compiled)),
// new Lazy<Regex>(() => new Regex((modulePattern, , RegexOptions.Compiled)),
})
.ToArray();
// at this point, filters is an array of 'SomeSortOfModuleTypeFilterObject' containing regex for object and module matching.
}Such data can be usefull for the foreach loops in IsModuleExcluded and in IsTypeExcluded methods. Now, each filter in filters is parsed and Regex object created each single time. Constructing compiled regex objects can be faster when executing the same regex multiple times. Also storing and reusing them puts less presure on the GC. |
coenm
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inline comments related to my previous post
| foreach (var filter in filters) | ||
| { | ||
| if (!IsValidFilterExpression(filter)) | ||
| continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of unnecessary calls to IsValidFilterExpression. An other approach is to make sure that the input parameter filters contains only valid filter expressions.
|
|
||
| foreach (var filter in filters) | ||
| { | ||
| if (!IsValidFilterExpression(filter)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of unnecessary calls to IsValidFilterExpression. Make sure that the input parameter filters contains valid data.
| typePattern = WildcardToRegex(typePattern); | ||
| modulePattern = WildcardToRegex(modulePattern); | ||
|
|
||
| isMatch = new Regex(typePattern).IsMatch(type) && new Regex(modulePattern).IsMatch(module); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For each module, this method is called. For each filter these two regexes are created. This can be cached in some sort of object and the regex can be created using compile mode to speed up the process and to reduce GC presure
| modulePattern = WildcardToRegex(modulePattern); | ||
|
|
||
| var regex = new Regex(modulePattern); | ||
| isMatch = regex.IsMatch(module) && typePattern == "*"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For each module, this method is called. For each filter this regex is created. This objecty be cached in some sort of object and the regex can be created using compile mode to speed up the process and to reduce GC presure
| if (filter.EndsWith("]")) | ||
| return false; | ||
|
|
||
| if (new Regex(@"[^\w*]").IsMatch(filter.Replace(".", "").Replace("[", "").Replace("]", ""))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regex object can be static field and created in compile mode
|
Improvement: I have a question why not to have the same syntax as for other tools?
for /p:ExcludeByFile?, for example:
My build script: |
|
@hack2root I don't see a reason why |
Add ability to exclude assemblies by expression
This PR introduces the ability to filter assemblies with an expression format closely resembling the one used by OpenCover. However, unlike OpenCover Coverlet doesn't support inclusion/exclusion syntax (
+/-).All instrument-able assemblies are inclusive by default and the
Excludeproperty has been repurposed to accept a comma separated list of filter expressions. The ability to exclude source files (based on the work by @ido-namely) has been moved to the newExcludeByFileproperty.The filter rules can be used to exclude entire assemblies, all types in an assembly or all types in a specific namespace.
Syntax:
[Assembly-Filter]Type-FilterExamples:
/p:Exclude=[*]*=> Excludes all types in all assemblies (nothing is instrumented)/p:Exclude=[coverlet.*]Coverlet.Core.Coverage=> Excludes theCoverageclass in theCoverlet.Corenamespace belonging to any assembly that matchescoverlet.*(e.gcoverlet.core)/p:Exclude=[*]Coverlet.Core.Instrumentation.*=> Excludes all types belonging toCoverlet.Core.Instrumentationnamespace in any assemblyThe PR introduces backwards incompatible changes and will be part of the
2.0release this weekcc @MarcoRossignoli @ido-namely @JasonBock @SteveGilham @coenm @ryanmvt
Addresses #77