Skip to content

Commit

Permalink
Merge pull request coverlet-coverage#164 from pjanotti/include.option
Browse files Browse the repository at this point in the history
Add include option
  • Loading branch information
tonerdo committed Aug 6, 2018
2 parents 580f2c2 + 9197760 commit cdb802b
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 44 deletions.
7 changes: 4 additions & 3 deletions src/coverlet.console/Program.cs
Expand Up @@ -31,8 +31,9 @@ static int Main(string[] args)
CommandOption formats = app.Option("-f|--format", "Format of the generated coverage report.", CommandOptionType.MultipleValue);
CommandOption threshold = app.Option("--threshold", "Exits with error if the coverage % is below value.", CommandOptionType.SingleValue);
CommandOption thresholdTypes = app.Option("--threshold-type", "Coverage type to apply the threshold to.", CommandOptionType.MultipleValue);
CommandOption filters = app.Option("--exclude", "Filter expressions to exclude specific modules and types.", CommandOptionType.MultipleValue);
CommandOption excludes = app.Option("--exclude-by-file", "Glob patterns specifying source files to exclude.", CommandOptionType.MultipleValue);
CommandOption excludeFilters = app.Option("--exclude", "Filter expressions to exclude specific modules and types.", CommandOptionType.MultipleValue);
CommandOption includeFilters = app.Option("--include", "Filter expressions to include only specific modules and types.", CommandOptionType.MultipleValue);
CommandOption excludedSourceFiles = app.Option("--exclude-by-file", "Glob patterns specifying source files to exclude.", CommandOptionType.MultipleValue);

app.OnExecute(() =>
{
Expand All @@ -42,7 +43,7 @@ static int Main(string[] args)
if (!target.HasValue())
throw new CommandParsingException(app, "Target must be specified.");
Coverage coverage = new Coverage(module.Value, filters.Values.ToArray(), excludes.Values.ToArray());
Coverage coverage = new Coverage(module.Value, excludeFilters.Values.ToArray(), includeFilters.Values.ToArray(), excludedSourceFiles.Values.ToArray());
coverage.PrepareModules();
Process process = new Process();
Expand Down
23 changes: 13 additions & 10 deletions src/coverlet.core/Coverage.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;

using Coverlet.Core.Helpers;
Expand All @@ -13,36 +12,40 @@ public class Coverage
{
private string _module;
private string _identifier;
private string[] _filters;
private string[] _excludes;
private string[] _excludeFilters;
private string[] _includeFilters;
private string[] _excludedSourceFiles;
private List<InstrumenterResult> _results;

public string Identifier
{
get { return _identifier; }
}

public Coverage(string module, string[] filters, string[] excludes)
public Coverage(string module, string[] excludeFilters, string[] includeFilters, string[] excludedSourceFiles)
{
_module = module;
_filters = filters;
_excludes = excludes;
_excludeFilters = excludeFilters;
_includeFilters = includeFilters;
_excludedSourceFiles = excludedSourceFiles;
_identifier = Guid.NewGuid().ToString();
_results = new List<InstrumenterResult>();
}

public void PrepareModules()
{
string[] modules = InstrumentationHelper.GetCoverableModules(_module);
string[] excludes = InstrumentationHelper.GetExcludedFiles(_excludes);
_filters = _filters?.Where(f => InstrumentationHelper.IsValidFilterExpression(f)).ToArray();
string[] excludes = InstrumentationHelper.GetExcludedFiles(_excludedSourceFiles);
_excludeFilters = _excludeFilters?.Where(f => InstrumentationHelper.IsValidFilterExpression(f)).ToArray();
_includeFilters = _includeFilters?.Where(f => InstrumentationHelper.IsValidFilterExpression(f)).ToArray();

foreach (var module in modules)
{
if (InstrumentationHelper.IsModuleExcluded(module, _filters))
if (InstrumentationHelper.IsModuleExcluded(module, _excludeFilters)
|| !InstrumentationHelper.IsModuleIncluded(module, _includeFilters))
continue;

var instrumenter = new Instrumenter(module, _identifier, _filters, excludes);
var instrumenter = new Instrumenter(module, _identifier, _excludeFilters, _includeFilters, excludes);
if (instrumenter.CanInstrument())
{
InstrumentationHelper.BackupOriginalModule(module, _identifier);
Expand Down
70 changes: 59 additions & 11 deletions src/coverlet.core/Helpers/InstrumentationHelper.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
Expand Down Expand Up @@ -111,23 +112,23 @@ public static bool IsValidFilterExpression(string filter)
return true;
}

public static bool IsModuleExcluded(string module, string[] filters)
public static bool IsModuleExcluded(string module, string[] excludeFilters)
{
if (filters == null)
if (excludeFilters == null || excludeFilters.Length == 0)
return false;

module = Path.GetFileNameWithoutExtension(module);
if (module == null)
return false;

foreach (var filter in filters)
foreach (var filter in excludeFilters)
{
string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1);
string typePattern = filter.Substring(filter.IndexOf(']') + 1);

if (typePattern != "*")
continue;

string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1);
modulePattern = WildcardToRegex(modulePattern);

var regex = new Regex(modulePattern);
Expand All @@ -139,30 +140,57 @@ public static bool IsModuleExcluded(string module, string[] filters)
return false;
}

public static bool IsTypeExcluded(string module, string type, string[] filters)
public static bool IsModuleIncluded(string module, string[] includeFilters)
{
if (filters == null)
return false;
if (includeFilters == null || includeFilters.Length == 0)
return true;

module = Path.GetFileNameWithoutExtension(module);
if (module == null)
return false;

foreach (var filter in filters)
foreach (var filter in includeFilters)
{
string typePattern = filter.Substring(filter.IndexOf(']') + 1);
string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1);

typePattern = WildcardToRegex(typePattern);
if (modulePattern == "*")
return true;

modulePattern = WildcardToRegex(modulePattern);

if (new Regex(typePattern).IsMatch(type) && new Regex(modulePattern).IsMatch(module))
var regex = new Regex(modulePattern);

if (regex.IsMatch(module))
return true;
}

return false;
}

public static bool IsTypeExcluded(string module, string type, string[] excludeFilters)
{
if (excludeFilters == null || excludeFilters.Length == 0)
return false;

module = Path.GetFileNameWithoutExtension(module);
if (module == null)
return false;

return IsTypeFilterMatch(module, type, excludeFilters);
}

public static bool IsTypeIncluded(string module, string type, string[] includeFilters)
{
if (includeFilters == null || includeFilters.Length == 0)
return true;

module = Path.GetFileNameWithoutExtension(module);
if (module == null)
return true;

return IsTypeFilterMatch(module, type, includeFilters);
}

public static bool IsLocalMethod(string method)
=> new Regex(WildcardToRegex("<*>*__*|*")).IsMatch(method);

Expand Down Expand Up @@ -206,6 +234,26 @@ public static string[] GetExcludedFiles(string[] excludes)
return files.Distinct().ToArray();
}

private static bool IsTypeFilterMatch(string module, string type, string[] filters)
{
Debug.Assert(module != null);
Debug.Assert(filters != null);

foreach (var filter in filters)
{
string typePattern = filter.Substring(filter.IndexOf(']') + 1);
string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1);

typePattern = WildcardToRegex(typePattern);
modulePattern = WildcardToRegex(modulePattern);

if (new Regex(typePattern).IsMatch(type) && new Regex(modulePattern).IsMatch(module))
return true;
}

return false;
}

private static string GetBackupPath(string module, string identifier)
{
return Path.Combine(
Expand Down
21 changes: 12 additions & 9 deletions src/coverlet.core/Instrumentation/Instrumenter.cs
Expand Up @@ -19,16 +19,18 @@ internal class Instrumenter
{
private readonly string _module;
private readonly string _identifier;
private readonly string[] _filters;
private readonly string[] _excludeFilters;
private readonly string[] _includeFilters;
private readonly string[] _excludedFiles;
private readonly static Lazy<MethodInfo> _markExecutedMethodLoader = new Lazy<MethodInfo>(GetMarkExecutedMethod);
private InstrumenterResult _result;

public Instrumenter(string module, string identifier, string[] filters, string[] excludedFiles)
public Instrumenter(string module, string identifier, string[] excludeFilters, string[] includeFilters, string[] excludedFiles)
{
_module = module;
_identifier = identifier;
_filters = filters;
_excludeFilters = excludeFilters;
_includeFilters = includeFilters;
_excludedFiles = excludedFiles ?? Array.Empty<string>();
}

Expand Down Expand Up @@ -69,7 +71,8 @@ private void InstrumentModule()
{
var actualType = type.DeclaringType ?? type;
if (!actualType.CustomAttributes.Any(IsExcludeAttribute)
&& !InstrumentationHelper.IsTypeExcluded(_module, actualType.FullName, _filters))
&& !InstrumentationHelper.IsTypeExcluded(_module, actualType.FullName, _excludeFilters)
&& InstrumentationHelper.IsTypeIncluded(_module, actualType.FullName, _includeFilters))
InstrumentType(type);
}

Expand All @@ -92,10 +95,10 @@ private void InstrumentType(TypeDefinition type)
}

var ctors = type.GetConstructors();
foreach (var ctor in ctors)
{
foreach (var ctor in ctors)
{
if (!ctor.CustomAttributes.Any(IsExcludeAttribute))
InstrumentMethod(ctor);
InstrumentMethod(ctor);
}
}

Expand Down Expand Up @@ -173,7 +176,7 @@ private void InstrumentIL(MethodDefinition method)
private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor processor, Instruction instruction, SequencePoint sequencePoint)
{
if (!_result.Documents.TryGetValue(sequencePoint.Document.Url, out var document))
{
{
document = new Document { Path = sequencePoint.Document.Url };
_result.Documents.Add(document.Path, document);
}
Expand All @@ -200,7 +203,7 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor
private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor processor, Instruction instruction, BranchPoint branchPoint)
{
if (!_result.Documents.TryGetValue(branchPoint.Document, out var document))
{
{
document = new Document { Path = branchPoint.Document };
_result.Documents.Add(document.Path, document);
}
Expand Down
14 changes: 11 additions & 3 deletions src/coverlet.msbuild.tasks/InstrumentationTask.cs
Expand Up @@ -10,6 +10,7 @@ public class InstrumentationTask : Task
private static Coverage _coverage;
private string _path;
private string _exclude;
private string _include;
private string _excludeByFile;

internal static Coverage Coverage
Expand All @@ -30,6 +31,12 @@ public string Exclude
set { _exclude = value; }
}

public string Include
{
get { return _include; }
set { _include = value; }
}

public string ExcludeByFile
{
get { return _excludeByFile; }
Expand All @@ -40,10 +47,11 @@ public override bool Execute()
{
try
{
var excludes = _excludeByFile?.Split(',');
var filters = _exclude?.Split(',');
var excludedSourceFiles = _excludeByFile?.Split(',');
var excludeFilters = _exclude?.Split(',');
var includeFilters = _include?.Split(',');

_coverage = new Coverage(_path, filters, excludes);
_coverage = new Coverage(_path, excludeFilters, includeFilters, excludedSourceFiles);
_coverage.PrepareModules();
}
catch (Exception ex)
Expand Down
2 changes: 1 addition & 1 deletion test/coverlet.core.performancetest/PerformanceTest.cs
Expand Up @@ -9,7 +9,7 @@ namespace coverlet.core.performancetest
/// Test the performance of coverlet by running a unit test that calls a reasonably big and complex test class.
/// Enable the test, compile, then run the test in the command line:
/// <code>
/// dotnet test -p:CollectCoverage=true -p:CoverletOutputFormat=opencover test/coverlet.core.performa ncetest/
/// dotnet test -p:CollectCoverage=true -p:CoverletOutputFormat=opencover test/coverlet.core.performancetest/
/// </code>
/// </summary>
public class PerformanceTest
Expand Down
2 changes: 1 addition & 1 deletion test/coverlet.core.tests/CoverageTests.cs
Expand Up @@ -27,7 +27,7 @@ public void TestCoverage()
// Since Coverage only instruments dependancies, we need a fake module here
var testModule = Path.Combine(directory.FullName, "test.module.dll");

var coverage = new Coverage(testModule, Array.Empty<string>(), Array.Empty<string>());
var coverage = new Coverage(testModule, Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>());
coverage.PrepareModules();

var result = coverage.GetCoverageResult();
Expand Down

0 comments on commit cdb802b

Please sign in to comment.