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

Add include option #164

Merged
merged 1 commit into from Aug 6, 2018
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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