diff --git a/TextFilter.Test/TextFilterTests.cs b/TextFilter.Test/TextFilterTests.cs index 66011f5..e61d8aa 100644 --- a/TextFilter.Test/TextFilterTests.cs +++ b/TextFilter.Test/TextFilterTests.cs @@ -682,6 +682,35 @@ public void ARegexCompiledUnderOneCultureAnswersTheSameUnderAnother() } } + [TestMethod] + public void AnInlineIgnoreCaseFoldsTheSameWhateverCultureCompiledIt() + { + // Under CaseSensitive the pattern can still ask for a fold itself with (?i), and that fold is + // culture-dependent unless CultureInvariant is set. Compiled under tr-TR first, "(?i)img" would + // then miss "IMG_1234.JPG" for every later caller, because the cache keeps the first compile. + CultureInfo original = CultureInfo.CurrentCulture; + try + { + CultureInfo.CurrentCulture = new CultureInfo("tr-TR"); + if (CultureInfo.CurrentCulture.TextInfo.ToUpper("i") == "I") + { + Assert.Inconclusive("This runtime does not apply Turkish case mapping, so the regression cannot be provoked here."); + } + + bool underTurkish = TextFilter.IsMatch("IMG_1234.JPG", "(?i)img", TextFilterType.Regex, TextFilterMatchOptions.ByWholeString); + + CultureInfo.CurrentCulture = new CultureInfo("en-US"); + bool underEnglish = TextFilter.IsMatch("IMG_1234.JPG", "(?i)img", TextFilterType.Regex, TextFilterMatchOptions.ByWholeString); + + Assert.IsTrue(underTurkish, "An inline (?i) should fold i and I whatever locale compiled the pattern."); + Assert.IsTrue(underEnglish, "The first caller's culture should not decide the answer for later callers."); + } + finally + { + CultureInfo.CurrentCulture = original; + } + } + [TestMethod] public void TheTwoSensitivitiesDoNotCollideInTheRegexCache() { diff --git a/TextFilter/TextFilter.cs b/TextFilter/TextFilter.cs index 65c8a36..60af356 100644 --- a/TextFilter/TextFilter.cs +++ b/TextFilter/TextFilter.cs @@ -458,10 +458,12 @@ public static bool DoesMatchRegex(string text, string filter, TextFilterMatchOpt // matches "IMG_1234.JPG" -- and the cache below is keyed by pattern and sensitivity only, // so whichever culture happened to compile the pattern first decides the answer for every // later caller on any thread. A filter pattern is machine text, not prose, and the glob - // path already folds invariantly, so the two paths now agree. + // path already folds invariantly, so the two paths now agree. It applies under CaseSensitive + // too, because the pattern itself can ask for a fold with an inline (?i), and that fold is + // just as culture-dependent and just as cached as the one the option asks for. RegexOptions regexOptions = caseSensitivity is TextFilterCaseSensitivity.CaseInsensitive ? RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant - : RegexOptions.Compiled; + : RegexOptions.Compiled | RegexOptions.CultureInvariant; try {