diff --git a/TextFilter.Test/TextFilterTests.cs b/TextFilter.Test/TextFilterTests.cs index 9c20e60..9d08b60 100644 --- a/TextFilter.Test/TextFilterTests.cs +++ b/TextFilter.Test/TextFilterTests.cs @@ -792,4 +792,30 @@ public void GlobWithAHalfTypedRangeDoesNotThrow(string pattern) TextFilter.Filter(["file1.txt"], pattern, TextFilterType.Glob, TextFilterMatchOptions.ByWordAny, caseSensitivity).ToList()); } } + + [TestMethod] + [DataRow("", TextFilterType.Regex)] + [DataRow(" ", TextFilterType.Regex)] + [DataRow("", TextFilterType.Glob)] + [DataRow(" ", TextFilterType.Glob)] + public void ByWordAllDoesNotMatchTextWithNoWords(string text, TextFilterType filterType) + { + // Blank text splits into no words, and All over nothing is vacuously true, so the regex path + // used to match it against every pattern while the glob path did not. Both now agree. + Assert.IsFalse(TextFilter.IsMatch(text, "zzz", filterType, TextFilterMatchOptions.ByWordAll)); + CollectionAssert.AreEqual( + new List(), + TextFilter.Filter([text], "zzz", filterType, TextFilterMatchOptions.ByWordAll).ToList()); + } + + [TestMethod] + public void RegexByWordAllFilterDropsBlankEntries() + { + CollectionAssert.AreEqual( + new List(), + TextFilter.Filter(["", "hello"], "zzz", TextFilterType.Regex, TextFilterMatchOptions.ByWordAll).ToList()); + CollectionAssert.AreEqual( + new List { "hello" }, + TextFilter.Filter(["", "hello"], "hel", TextFilterType.Regex, TextFilterMatchOptions.ByWordAll).ToList()); + } } diff --git a/TextFilter/TextFilter.cs b/TextFilter/TextFilter.cs index 7b7f1fd..ce99917 100644 --- a/TextFilter/TextFilter.cs +++ b/TextFilter/TextFilter.cs @@ -475,6 +475,14 @@ public static bool DoesMatchRegex(string text, string filter, TextFilterMatchOpt AddBounded(RegexCache, cacheKey, regex); } + // Text with no words -- "" or " " split by word -- has nothing for the pattern to match. + // Without this, Enumerable.All over the empty set is vacuously true, so under ByWordAll blank + // text matched every pattern while the glob path reported no match for the same input. + if (textTokens.Count == 0) + { + return false; + } + Func, Func, bool> matchFunc = textFilterMatchOptions is TextFilterMatchOptions.ByWordAny ? Enumerable.Any : Enumerable.All;