diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index 82b66056b..7ae12972e 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -360,19 +360,27 @@ private string GetModuleKeysForIncludeFilters(IEnumerable filters, char { string[] validFilters = GetValidFilters(filters); - return !validFilters.Any() ? moduleKeys : GetModuleKeysForValidFilters(escapeSymbol, moduleKeys, validFilters); + return !validFilters.Any() ? moduleKeys : GetIncludeModuleKeysForValidFilters(escapeSymbol, moduleKeys, validFilters); } private string GetModuleKeysForExcludeFilters(IEnumerable filters, char escapeSymbol, string moduleKeys) { string[] validFilters = GetValidFilters(filters); - return !validFilters.Any() ? string.Empty : GetModuleKeysForValidFilters(escapeSymbol, moduleKeys, validFilters); + return !validFilters.Any() ? string.Empty : GetExcludeModuleKeysForValidFilters(escapeSymbol, moduleKeys, validFilters); } - private static string GetModuleKeysForValidFilters(char escapeSymbol, string moduleKeys, string[] validFilters) + private string[] GetValidFilters(IEnumerable filters) + { + return (filters ?? Array.Empty()) + .Where(IsValidFilterExpression) + .Where(x => x.EndsWith("*")) + .ToArray(); + } + + private static string GetExcludeModuleKeysForValidFilters(char escapeSymbol, string moduleKeys, string[] validFilters) { - string pattern = CreateRegexPattern(validFilters, escapeSymbol); + string pattern = CreateRegexExcludePattern(validFilters, escapeSymbol); IEnumerable matches = Regex.Matches(moduleKeys, pattern, RegexOptions.IgnoreCase).Cast(); return string.Join( @@ -380,18 +388,28 @@ private static string GetModuleKeysForValidFilters(char escapeSymbol, string mod matches.Where(x => x.Success).Select(x => x.Groups[0].Value)); } - private string[] GetValidFilters(IEnumerable filters) + private static string GetIncludeModuleKeysForValidFilters(char escapeSymbol, string moduleKeys, string[] validFilters) { - return (filters ?? Array.Empty()) - .Where(IsValidFilterExpression) - .Where(x => x.EndsWith("*")) - .ToArray(); + string pattern = CreateRegexIncludePattern(validFilters, escapeSymbol); + IEnumerable matches = Regex.Matches(moduleKeys, pattern, RegexOptions.IgnoreCase).Cast(); + + return string.Join( + Environment.NewLine, + matches.Where(x => x.Success).Select(x => x.Groups[0].Value)); } - private static string CreateRegexPattern(IEnumerable filters, char escapeSymbol) + private static string CreateRegexExcludePattern(IEnumerable filters, char escapeSymbol) + //only look for module filters here, types will be filtered out when instrumenting + => CreateRegexPattern(filters, escapeSymbol, filter => filter.Substring(filter.IndexOf(']') + 1) == "*"); + + private static string CreateRegexIncludePattern(IEnumerable filters, char escapeSymbol) => + CreateRegexPattern(filters, escapeSymbol); + + private static string CreateRegexPattern(IEnumerable filters, char escapeSymbol, Func filterPredicate = null) { - IEnumerable regexPatterns = filters.Select(x => - $"{escapeSymbol}{WildcardToRegex(x.Substring(1, x.IndexOf(']') - 1)).Trim('^', '$')}{escapeSymbol}"); + IEnumerable filteredFilters = filterPredicate != null ? filters.Where(filterPredicate) : filters; + IEnumerable regexPatterns = filteredFilters.Select(x => + $"{escapeSymbol}{WildcardToRegex(x.Substring(1, x.IndexOf(']') - 1)).Trim('^', '$')}{escapeSymbol}"); return string.Join("|", regexPatterns); } diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index 557835806..fe2d9c7c7 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -198,6 +198,30 @@ public void TestIsModuleExcludedAndIncludedWithMatchingAndMismatchingFilter(stri Assert.Empty(result); } + [Fact] + public void TestSelectModulesWithTypeFiltersDoesNotExcludeAssemblyWithType() + { + string[] modules = new[] { "Module.dll", "Module.Tests.dll" }; + string[] includeFilters = new[] { "[*]Module*" }; + string[] excludeFilters = new[] { "[*]Module.Tests.*" }; + + IEnumerable result = _instrumentationHelper.SelectModules(modules, includeFilters, excludeFilters); + + Assert.Equal(modules, result); + } + + [Fact] + public void TestSelectModulesWithModuleFilterExcludesExpectedModules() + { + string[] modules = new[] { "ModuleA.dll", "ModuleA.Tests.dll", "ModuleB.dll", "Module.B.Tests.dll" }; + string[] includeFilters = new[] { "" }; + string[] excludeFilters = new[] { "[ModuleA*]*" }; + + IEnumerable result = _instrumentationHelper.SelectModules(modules, includeFilters, excludeFilters); + + Assert.Equal(["ModuleB.dll", "Module.B.Tests.dll"], result); + } + [Fact] public void TestIsTypeExcludedWithoutFilter() { @@ -326,6 +350,7 @@ public void InstrumentationHelper_IsLocalMethod_ReturnsExpectedResult(string met new object[] { "[Mod*le*]*" }, new object[] { "[Module?]*" }, new object[] { "[ModuleX?]*" }, + new object[] { "[*]*" } }; public static IEnumerable ValidModuleAndNamespaceFilterData =>