From 5835a6e8d6e56748fd04f090f7a0d587c86be299 Mon Sep 17 00:00:00 2001 From: Coen Munckhof Date: Tue, 15 May 2018 21:49:55 +0200 Subject: [PATCH 1/4] First add a number of test (including some failing) to describe expected behavior. --- .../Helpers/InstrumentationHelperTests.cs | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index 4b6740944..a41c4646c 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -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 ValidModuleFilterData => + new List + { + 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 ValidModuleAndNamespaceFilterData => + new List + { + 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); } } From 45eba432ba1f6e9f646d879746a6634095576264 Mon Sep 17 00:00:00 2001 From: Coen Munckhof Date: Tue, 15 May 2018 21:54:29 +0200 Subject: [PATCH 2/4] Fix IsModuleExcluded and IsTypeExcluded methods to immediately return true if a filter matches the input. --- .../Helpers/InstrumentationHelper.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index 7e8171657..0c4d7efdd 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -118,21 +118,27 @@ 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) @@ -140,8 +146,9 @@ 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) { @@ -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) From 0f5e184d4afdd5bf472b149585b6fed22219fd8e Mon Sep 17 00:00:00 2001 From: Coen Munckhof Date: Tue, 15 May 2018 22:07:48 +0200 Subject: [PATCH 3/4] Fix usage of question mark. Not sure if this is what was intended but seemed logical. --- README.md | 6 ++++++ src/coverlet.core/Helpers/InstrumentationHelper.cs | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 845499398..7ba379271 100644 --- a/README.md +++ b/README.md @@ -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" diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index 0c4d7efdd..5319da9a2 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -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; @@ -229,7 +229,7 @@ private static string WildcardToRegex(string pattern) { return "^" + Regex.Escape(pattern). Replace("\\*", ".*"). - Replace("\\?", ".") + "$"; + Replace("\\?", "?") + "$"; } private static bool IsAssembly(string filePath) From 9346a42f6fc99aadab14e38ac853613f058a4d1b Mon Sep 17 00:00:00 2001 From: Coen Munckhof Date: Tue, 15 May 2018 22:09:58 +0200 Subject: [PATCH 4/4] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ba379271..f8298c0f9 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ Examples - `/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 + - `/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"