diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs index 8d6c9c3f33e4aa..51ed8ec44e2f54 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs @@ -17,24 +17,38 @@ public class InMemoryDirectoryInfo : DirectoryInfoBase { private static readonly char[] DirectorySeparators = new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; private readonly IEnumerable _files; + private readonly StringComparison _comparisonType; /// - /// Creates a new InMemoryDirectoryInfo with the root directory and files given. + /// Creates a new case-sensitive InMemoryDirectoryInfo with the root directory and files given. /// /// The root directory that this FileSystem will use. /// Collection of file names. If relative paths will be prepended to the paths. public InMemoryDirectoryInfo(string rootDir, IEnumerable? files) - : this(rootDir, files, false) + : this(rootDir, files, false, StringComparison.Ordinal) + { + } + + /// + /// Creates a new InMemoryDirectoryInfo with the root directory and files given. + /// + /// The root directory that this FileSystem will use. + /// Collection of file names. If relative paths will be prepended to the paths. + /// The comparison type for the root directory. When files are enumerated they will be compared with the root directory using this comparison type. + internal InMemoryDirectoryInfo(string rootDir, IEnumerable? files, StringComparison comparisonType) + : this(rootDir, files, false, comparisonType) { } - private InMemoryDirectoryInfo(string rootDir, IEnumerable? files, bool normalized) + private InMemoryDirectoryInfo(string rootDir, IEnumerable? files, bool normalized, StringComparison comparisonType) { if (string.IsNullOrEmpty(rootDir)) { throw new ArgumentNullException(nameof(rootDir)); } + _comparisonType = comparisonType; + files ??= new List(); Name = Path.GetFileName(rootDir); @@ -76,7 +90,7 @@ private InMemoryDirectoryInfo(string rootDir, IEnumerable? files, bool n /// public override DirectoryInfoBase? ParentDirectory => - new InMemoryDirectoryInfo(Path.GetDirectoryName(FullName)!, _files, true); + new InMemoryDirectoryInfo(Path.GetDirectoryName(FullName)!, _files, true, _comparisonType); /// public override IEnumerable EnumerateFileSystemInfos() @@ -113,15 +127,15 @@ public override IEnumerable EnumerateFileSystemInfos() foreach (KeyValuePair> item in dict) { - yield return new InMemoryDirectoryInfo(item.Key, item.Value, true); + yield return new InMemoryDirectoryInfo(item.Key, item.Value, true, _comparisonType); } } - private static bool IsRootDirectory(string rootDir, string filePath) + private bool IsRootDirectory(string rootDir, string filePath) { int rootDirLength = rootDir.Length; - return filePath.StartsWith(rootDir, StringComparison.Ordinal) && + return filePath.StartsWith(rootDir, _comparisonType) && (rootDir[rootDirLength - 1] == Path.DirectorySeparatorChar || filePath.IndexOf(Path.DirectorySeparatorChar, rootDirLength) == rootDirLength); } @@ -131,12 +145,12 @@ public override DirectoryInfoBase GetDirectory(string path) { if (string.Equals(path, "..", StringComparison.Ordinal)) { - return new InMemoryDirectoryInfo(Path.Combine(FullName, path), _files, true); + return new InMemoryDirectoryInfo(Path.Combine(FullName, path), _files, true, _comparisonType); } else { string normPath = Path.GetFullPath(path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)); - return new InMemoryDirectoryInfo(normPath, _files, true); + return new InMemoryDirectoryInfo(normPath, _files, true, _comparisonType); } } diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Matcher.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Matcher.cs index 61503f7e0e4761..b3bf45b5af1736 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Matcher.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Matcher.cs @@ -100,9 +100,10 @@ public class Matcher private readonly List? _excludePatterns; private readonly List>? _includeOrExcludePatterns; private readonly PatternBuilder _builder; - private readonly StringComparison _comparison; private readonly bool _preserveFilterOrder; + internal StringComparison ComparisonType { get; } + /// /// Initializes an instance of using case-insensitive matching /// @@ -130,7 +131,7 @@ public Matcher(StringComparison comparisonType) /// public Matcher(StringComparison comparisonType = StringComparison.OrdinalIgnoreCase, bool preserveFilterOrder = false) { - _comparison = comparisonType; + ComparisonType = comparisonType; _builder = new PatternBuilder(comparisonType); _preserveFilterOrder = preserveFilterOrder; @@ -199,8 +200,8 @@ public virtual PatternMatchingResult Execute(DirectoryInfoBase directoryInfo) ArgumentNullException.ThrowIfNull(directoryInfo); return _preserveFilterOrder ? - new MatcherContext(_includeOrExcludePatterns!, directoryInfo, _comparison).Execute() : - new MatcherContext(_includePatterns!, _excludePatterns!, directoryInfo, _comparison).Execute(); + new MatcherContext(_includeOrExcludePatterns!, directoryInfo, ComparisonType).Execute() : + new MatcherContext(_includePatterns!, _excludePatterns!, directoryInfo, ComparisonType).Execute(); } } } diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/MatcherExtensions.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/MatcherExtensions.cs index b18877202c2c37..8c26745aa28efd 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/MatcherExtensions.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/MatcherExtensions.cs @@ -111,7 +111,7 @@ public static PatternMatchingResult Match(this Matcher matcher, string rootDir, { ArgumentNullException.ThrowIfNull(matcher); - return matcher.Execute(new InMemoryDirectoryInfo(rootDir, files)); + return matcher.Execute(new InMemoryDirectoryInfo(rootDir, files, matcher.ComparisonType)); } } } diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs index e8bcdacdb9558b..63e06e841184af 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs @@ -901,5 +901,50 @@ public void VerifyFiles_ParentRedundantSegment_CurrentDirectory_HasMatches() Assert.True(matcher.Match([$"../{cwdFolderName}/{file}"]).HasMatches); } } + + [Theory] + [InlineData(@"/this/example/root", @"/this/EXAMPLE/root", "**/*", new[] { "some/test/file.txt" })] + [InlineData(@"/this/example/root", @"/this/example/root", "**/*", new[] { "some/test/file.txt" })] + [InlineData(@"/this/EXAMPLE/root", @"/this/example/root", "**/*", new[] { "some/test/file.txt" })] + public void VerifyFiles_InMemory_HasCaseInsensitiveRootMatches(string matchRoot, string filesRoot, string pattern, string[] expectedSubPaths) + { + Matcher matcher = new(StringComparison.OrdinalIgnoreCase); + matcher.AddInclude(pattern); + + PatternMatchingResult patternMatchingResult = matcher.Match(matchRoot, + expectedSubPaths.Select(expectedSubPath => Path.Combine(filesRoot, expectedSubPath))); + + Assert.True(patternMatchingResult.HasMatches); + Assert.Equal(expectedSubPaths.Length, patternMatchingResult.Files.Count()); + } + + [Theory] + [InlineData(@"/this/example/root", @"/this/example/root", "**/*", new[] { "some/test/file.txt" })] + public void VerifyFiles_InMemory_HasCaseSensitiveRootMatches(string matchRoot, string filesRoot, string pattern, string[] expectedSubPaths) + { + Matcher matcher = new(StringComparison.Ordinal); + matcher.AddInclude(pattern); + + PatternMatchingResult patternMatchingResult = matcher.Match(matchRoot, + expectedSubPaths.Select(expectedSubPath => Path.Combine(filesRoot, expectedSubPath))); + + Assert.True(patternMatchingResult.HasMatches); + Assert.Equal(expectedSubPaths.Length, patternMatchingResult.Files.Count()); + } + + [Theory] + [InlineData(@"/this/example/root", @"/this/EXAMPLE/root", "**/*", new[] { "some/test/file.txt" })] + [InlineData(@"/this/EXAMPLE/root", @"/this/example/root", "**/*", new[] { "some/test/file.txt" })] + public void VerifyFiles_InMemory_HasCaseSensitiveRootMisses(string matchRoot, string filesRoot, string pattern, string[] expectedSubPaths) + { + Matcher matcher = new(StringComparison.Ordinal); + matcher.AddInclude(pattern); + + PatternMatchingResult patternMatchingResult = matcher.Match(matchRoot, + expectedSubPaths.Select(expectedSubPath => Path.Combine(filesRoot, expectedSubPath))); + + Assert.False(patternMatchingResult.HasMatches); + Assert.Equal(0, patternMatchingResult.Files.Count()); + } } }