Skip to content

Commit

Permalink
Merge pull request #99 from coenm/bugfix/MultipleExcludeFiltersOnlyRe…
Browse files Browse the repository at this point in the history
…spectLastFilterExpression

Bug: Only last filter expression is respected
  • Loading branch information
tonerdo committed May 16, 2018
2 parents 0b42b03 + 9346a42 commit a6046c4
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 8 deletions.
6 changes: 6 additions & 0 deletions README.md
Expand Up @@ -101,10 +101,16 @@ Coverlet gives the ability to have fine grained control over what gets excluded

Syntax: `/p:Exclude=[Assembly-Filter]Type-Filter`

Wildcards
- `*` => matches zero or more characters
- `?` => the prefixed character is optional

Examples
- `/p:Exclude="[*]*"` => Excludes all types in all assemblies (nothing is instrumented)
- `/p:Exclude="[coverlet.*]Coverlet.Core.Coverage"` => Excludes the Coverage class in the `Coverlet.Core` namespace belonging to any assembly that matches `coverlet.*` (e.g `coverlet.core`)
- `/p:Exclude="[*]Coverlet.Core.Instrumentation.*"` => Excludes all types belonging to `Coverlet.Core.Instrumentation` namespace in any assembly
- `/p:Exclude="[coverlet.*.tests?]*"` => Excludes all types in any assembly starting with `coverlet.` and ending with `.test` or `.tests` (the `?` makes the `s` optional)
- `/p:Exclude="[coverlet.*]*,[*]Coverlet.Core*"` => Excludes assemblies matching `coverlet.*` and excludes all types belonging to the `Coverlet.Core` namespace in any assembly

```bash
dotnet test /p:CollectCoverage=true /p:Exclude="[coverlet.*]Coverlet.Core.Coverage"
Expand Down
24 changes: 16 additions & 8 deletions src/coverlet.core/Helpers/InstrumentationHelper.cs
Expand Up @@ -107,7 +107,7 @@ public static bool IsValidFilterExpression(string filter)
if (filter.EndsWith("]"))
return false;

if (new Regex(@"[^\w*]").IsMatch(filter.Replace(".", "").Replace("[", "").Replace("]", "")))
if (new Regex(@"[^\w*]").IsMatch(filter.Replace(".", "").Replace("?", "").Replace("[", "").Replace("]", "")))
return false;

return true;
Expand All @@ -118,30 +118,37 @@ public static bool IsModuleExcluded(string module, string[] filters)
if (filters == null)
return false;

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

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

if (typePattern != "*")
continue;

modulePattern = WildcardToRegex(modulePattern);

var regex = new Regex(modulePattern);
isMatch = regex.IsMatch(module) && typePattern == "*";

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

return isMatch;
return false;
}

public static bool IsTypeExcluded(string module, string type, string[] filters)
{
if (filters == null)
return false;

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

foreach (var filter in filters)
{
Expand All @@ -151,10 +158,11 @@ public static bool IsTypeExcluded(string module, string type, string[] filters)
typePattern = WildcardToRegex(typePattern);
modulePattern = WildcardToRegex(modulePattern);

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

return isMatch;
return false;
}

public static string[] GetExcludedFiles(string[] rules)
Expand Down Expand Up @@ -221,7 +229,7 @@ private static string WildcardToRegex(string pattern)
{
return "^" + Regex.Escape(pattern).
Replace("\\*", ".*").
Replace("\\?", ".") + "$";
Replace("\\?", "?") + "$";
}

private static bool IsAssembly(string filePath)
Expand Down
100 changes: 100 additions & 0 deletions test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs
Expand Up @@ -141,6 +141,106 @@ public void TestGetExcludedFilesUsingGlobbing()

Assert.Equal(paths.Length, excludedFiles.Count());
}

[Fact]
public void TestIsModuleExcludedWithoutFilter()
{
var result = InstrumentationHelper.IsModuleExcluded("Module.dll", new string[0]);

Assert.False(result);
}

[Theory]
[InlineData("[Module]mismatch")]
[InlineData("[Mismatch]*")]
public void TestIsModuleExcludedWithSingleMismatchFilter(string filter)
{
var result = InstrumentationHelper.IsModuleExcluded("Module.dll", new[] { filter });

Assert.False(result);
}

[Theory]
[MemberData(nameof(ValidModuleFilterData))]
public void TestIsModuleExcludedWithFilter(string filter)
{
var result = InstrumentationHelper.IsModuleExcluded("Module.dll", new[] { filter });

Assert.True(result);
}

[Theory]
[MemberData(nameof(ValidModuleFilterData))]
public void TestIsModuleExcludedWithMatchingAndMismatchingFilter(string filter)
{
var filters = new[] { "[Mismatch]*", filter, "[Mismatch]*" };

var result = InstrumentationHelper.IsModuleExcluded("Module.dll", filters);

Assert.True(result);
}

[Fact]
public void TestIsTypeExcludedWithoutFilter()
{
var result = InstrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", new string[0]);

Assert.False(result);
}

[Theory]
[InlineData("[Module]mismatch")]
[InlineData("[Mismatch]*")]
[InlineData("[Mismatch]a.b.Dto")]
public void TestIsTypeExcludedWithSingleMismatchFilter(string filter)
{
var result = InstrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", new[] { filter });

Assert.False(result);
}

[Theory]
[MemberData(nameof(ValidModuleAndNamespaceFilterData))]
public void TestIsTypeExcludedWithFilter(string filter)
{
var result = InstrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", new[] { filter });

Assert.True(result);
}

[Theory]
[MemberData(nameof(ValidModuleAndNamespaceFilterData))]
public void TestIsTypeExcludedWithMatchingAndMismatchingFilter(string filter)
{
var filters = new[] { "[Mismatch]*", filter, "[Mismatch]*" };

var result = InstrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", filters);

Assert.True(result);
}

public static IEnumerable<object[]> ValidModuleFilterData =>
new List<object[]>
{
new object[] { "[Module]*" },
new object[] { "[Module*]*" },
new object[] { "[Mod*ule]*" },
new object[] { "[M*e]*" },
new object[] { "[Mod*le*]*" },
new object[] { "[Module?]*" },
new object[] { "[ModuleX?]*" },
};

public static IEnumerable<object[]> ValidModuleAndNamespaceFilterData =>
new List<object[]>
{
new object[] { "[Module]a.b.Dto" },
new object[] { "[Module]a.b.Dtos?" },
new object[] { "[Module]a.*" },
new object[] { "[Module]a*" },
new object[] { "[Module]*b.*" },
}
.Concat(ValidModuleFilterData);
}
}

Expand Down

0 comments on commit a6046c4

Please sign in to comment.