From 538b8d98c4317129a6da1d0e640b63a430f89fc8 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Mon, 23 Nov 2020 19:20:11 -0800 Subject: [PATCH 1/3] Allow rootDir paths ending with separator to match files correctly --- .../src/InMemoryDirectoryInfo.cs | 10 +- .../tests/FunctionalTests.cs | 122 +++++++++++++++++- 2 files changed, 124 insertions(+), 8 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs index d94f1982e1ea1c..55688a8baf4d52 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs @@ -119,13 +119,11 @@ public override IEnumerable EnumerateFileSystemInfos() private bool IsRootDirectory(string rootDir, string filePath) { - if (!filePath.StartsWith(rootDir, StringComparison.Ordinal) || - filePath.IndexOf(Path.DirectorySeparatorChar, rootDir.Length) != rootDir.Length) - { - return false; - } + int rootDirLength = rootDir.Length; - return true; + return filePath.StartsWith(rootDir, StringComparison.Ordinal) && + (rootDir[rootDirLength - 1] == Path.DirectorySeparatorChar || + filePath.IndexOf(Path.DirectorySeparatorChar, rootDirLength) == rootDirLength); } /// diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs index ce56c09bc1a728..d1df5fcf57bb81 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs @@ -635,9 +635,94 @@ public void StemIncludesAllSegmentsFromPatternStartingAtWildcard_TwoDirectoriesD Assert.Equal(expectedStem, actualStem); } - private List GetFileList() + [Theory] + [InlineData("C:\\", '\\')] + [InlineData("C:/", '/')] + [InlineData("/", '/')] + public void RootDir_IsPathRoot_WithInMemory(string rootDir, char separator) + { + var matcher = new Matcher(); + matcher.AddInclude($"**{separator}*.cs"); + + IEnumerable files = GetFileList(rootDir, separator); + PatternMatchingResult results = matcher.Match(rootDir, files); + + IEnumerable actual = results.Files.Select(match => match.Path); + IEnumerable expected = new string[] + { + "src/project/source1.cs", + "src/project/sub/source2.cs", + "src/project/sub/source3.cs", + "src/project/sub2/source4.cs", + "src/project/sub2/source5.cs", + "src/project/compiler/preprocess/preprocess-source1.cs", + "src/project/compiler/preprocess/sub/preprocess-source2.cs", + "src/project/compiler/preprocess/sub/sub/preprocess-source3.cs", + "src/project/compiler/shared/shared1.cs", + "src/project/compiler/shared/sub/shared2.cs", + "src/project/compiler/shared/sub/sub/sharedsub.cs", + "src/project2/source1.cs", + "src/project2/sub/source2.cs", + "src/project2/sub/source3.cs", + "src/project2/sub2/source4.cs", + "src/project2/sub2/source5.cs", + "src/project2/compiler/preprocess/preprocess-source1.cs", + "src/project2/compiler/preprocess/sub/preprocess-source2.cs", + "src/project2/compiler/preprocess/sub/sub/preprocess-source3.cs", + "src/project2/compiler/shared/shared1.cs", + "src/project2/compiler/shared/sub/shared2.cs", + "src/project2/compiler/shared/sub/sub/sharedsub.cs", + "lib/source6.cs", + "lib/sub3/source7.cs", + "lib/sub4/source8.cs", + }; + + Assert.Equal( + expected.OrderBy(e => e), + actual.OrderBy(e => e), + StringComparer.OrdinalIgnoreCase); + } + + [Theory] + [InlineData("C:\\src\\project", '\\')] + [InlineData("C:\\src\\project\\", '\\')] + [InlineData("C:/src/project", '/')] + [InlineData("C:/src/project/", '/')] + [InlineData("/src/project", '/')] + [InlineData("/src/project/", '/')] + public void RootDir_IsAbsolutePath_WithInMemory(string rootDir, char separator) + { + var matcher = new Matcher(); + matcher.AddInclude($"**{separator}*.cs"); + + IEnumerable files = GetFileList(Path.GetPathRoot(rootDir), separator); + PatternMatchingResult results = matcher.Match(rootDir, files); + + IEnumerable actual = results.Files.Select(match => match.Path); + IEnumerable expected = new string[] + { + "source1.cs", + "sub/source2.cs", + "sub/source3.cs", + "sub2/source4.cs", + "sub2/source5.cs", + "compiler/preprocess/preprocess-source1.cs", + "compiler/preprocess/sub/preprocess-source2.cs", + "compiler/preprocess/sub/sub/preprocess-source3.cs", + "compiler/shared/shared1.cs", + "compiler/shared/sub/shared2.cs", + "compiler/shared/sub/sub/sharedsub.cs" + }; + + Assert.Equal( + expected.OrderBy(e => e), + actual.OrderBy(e => e), + StringComparer.OrdinalIgnoreCase); + } + + private IEnumerable GetFileList(string rootDir = "", char directorySeparator = '/') { - return new List + var files = new List { "root/test.0", "root/dir1/test.1", @@ -693,6 +778,8 @@ private List GetFileList() ".hidden/file1.hid", ".hidden/sub/file2.hid" }; + + return files.Select(x => (rootDir + x).Replace('/', directorySeparator)); } private DisposableFileSystem CreateContext() @@ -713,5 +800,36 @@ private void ExecuteAndVerify(Matcher matcher, string directoryPath, params stri AssertExtensions.CollectionEqual(expected, actual, StringComparer.OrdinalIgnoreCase); } + + // https://github.com/dotnet/runtime/issues/44767 + [Fact] + public void VerifyAbsolutePaths_HasMatches() + { + var fileMatcher = new Matcher(); + fileMatcher.AddInclude("**/*"); + + string fakeWindowsPath = "C:\\This\\is\\a\\nested\\windows-like\\path\\somefile.cs"; + Assert.True(fileMatcher.Match(Path.GetPathRoot(fakeWindowsPath), fakeWindowsPath).HasMatches); + + string fakeUnixPath = "/This/is/a/nested/unix-like/path/somefile.cs"; + Assert.True(fileMatcher.Match(Path.GetPathRoot(fakeUnixPath), fakeUnixPath).HasMatches); + } + + // https://github.com/dotnet/runtime/issues/36415 + [Fact] + public void VerifyInMemoryDirectoryInfo_IsNotEmpty() + { + IEnumerable files = new[] { @"pagefile.sys" }; + + var directoryInfo = new InMemoryDirectoryInfo(@"C:\", files); + IEnumerable fileSystemInfos = directoryInfo.EnumerateFileSystemInfos(); + + Assert.NotEmpty(fileSystemInfos); + + directoryInfo = new InMemoryDirectoryInfo("/", files); + fileSystemInfos = directoryInfo.EnumerateFileSystemInfos(); + + Assert.NotEmpty(fileSystemInfos); + } } } From 21a809321add27f661ec04a991250973aa953c9d Mon Sep 17 00:00:00 2001 From: David Cantu Date: Mon, 1 Mar 2021 12:50:01 -0800 Subject: [PATCH 2/3] Avoid running tests with windows-like absolute paths on non-windows platforms --- .../tests/FunctionalTests.cs | 64 ++++++++++++++----- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs index d1df5fcf57bb81..6b1d2fa7903d7a 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs @@ -636,10 +636,22 @@ public void StemIncludesAllSegmentsFromPatternStartingAtWildcard_TwoDirectoriesD } [Theory] + [InlineData("/", '/')] + public void RootDir_IsPathRoot_WithInMemory_AllOS(string rootDir, char separator) + { + RootDir_IsPathRoot_WithInMemory(rootDir, separator); + } + + [Theory] + [PlatformSpecific(TestPlatforms.Windows)] [InlineData("C:\\", '\\')] [InlineData("C:/", '/')] - [InlineData("/", '/')] - public void RootDir_IsPathRoot_WithInMemory(string rootDir, char separator) + public void RootDir_IsPathRoot_WithInMemory_WindowsOnly(string rootDir, char separator) + { + RootDir_IsPathRoot_WithInMemory(rootDir, separator); + } + + private static void RootDir_IsPathRoot_WithInMemory(string rootDir, char separator) { var matcher = new Matcher(); matcher.AddInclude($"**{separator}*.cs"); @@ -684,13 +696,25 @@ public void RootDir_IsPathRoot_WithInMemory(string rootDir, char separator) } [Theory] + [InlineData("/src/project", '/')] + [InlineData("/src/project/", '/')] + public void RootDir_IsAbsolutePath_WithInMemory_AllOS(string rootDir, char separator) + { + RootDir_IsAbsolutePath_WithInMemory(rootDir, separator); + } + + [Theory] + [PlatformSpecific(TestPlatforms.Windows)] [InlineData("C:\\src\\project", '\\')] [InlineData("C:\\src\\project\\", '\\')] [InlineData("C:/src/project", '/')] [InlineData("C:/src/project/", '/')] - [InlineData("/src/project", '/')] - [InlineData("/src/project/", '/')] - public void RootDir_IsAbsolutePath_WithInMemory(string rootDir, char separator) + public void RootDir_IsAbsolutePath_WithInMemory_WindowsOnly(string rootDir, char separator) + { + RootDir_IsAbsolutePath_WithInMemory(rootDir, separator); + } + + private static void RootDir_IsAbsolutePath_WithInMemory(string rootDir, char separator) { var matcher = new Matcher(); matcher.AddInclude($"**{separator}*.cs"); @@ -720,7 +744,7 @@ public void RootDir_IsAbsolutePath_WithInMemory(string rootDir, char separator) StringComparer.OrdinalIgnoreCase); } - private IEnumerable GetFileList(string rootDir = "", char directorySeparator = '/') + private static IEnumerable GetFileList(string rootDir = "", char directorySeparator = '/') { var files = new List { @@ -801,30 +825,38 @@ private void ExecuteAndVerify(Matcher matcher, string directoryPath, params stri AssertExtensions.CollectionEqual(expected, actual, StringComparer.OrdinalIgnoreCase); } - // https://github.com/dotnet/runtime/issues/44767 - [Fact] + [Fact] // https://github.com/dotnet/runtime/issues/44767 public void VerifyAbsolutePaths_HasMatches() { var fileMatcher = new Matcher(); fileMatcher.AddInclude("**/*"); - string fakeWindowsPath = "C:\\This\\is\\a\\nested\\windows-like\\path\\somefile.cs"; - Assert.True(fileMatcher.Match(Path.GetPathRoot(fakeWindowsPath), fakeWindowsPath).HasMatches); - + if (PlatformDetection.IsWindows) + { + // Windows-like absolute paths are not supported on Unix. + string fakeWindowsPath = "C:\\This\\is\\a\\nested\\windows-like\\path\\somefile.cs"; + Assert.True(fileMatcher.Match(Path.GetPathRoot(fakeWindowsPath), fakeWindowsPath).HasMatches); + } + + // Unix-like absolute paths are treated as relative paths on Windows. string fakeUnixPath = "/This/is/a/nested/unix-like/path/somefile.cs"; Assert.True(fileMatcher.Match(Path.GetPathRoot(fakeUnixPath), fakeUnixPath).HasMatches); } - // https://github.com/dotnet/runtime/issues/36415 - [Fact] + [Fact] // https://github.com/dotnet/runtime/issues/36415 public void VerifyInMemoryDirectoryInfo_IsNotEmpty() { IEnumerable files = new[] { @"pagefile.sys" }; + InMemoryDirectoryInfo directoryInfo; + IEnumerable fileSystemInfos; - var directoryInfo = new InMemoryDirectoryInfo(@"C:\", files); - IEnumerable fileSystemInfos = directoryInfo.EnumerateFileSystemInfos(); + if (PlatformDetection.IsWindows) + { + directoryInfo = new InMemoryDirectoryInfo(@"C:\", files); + fileSystemInfos = directoryInfo.EnumerateFileSystemInfos(); - Assert.NotEmpty(fileSystemInfos); + Assert.NotEmpty(fileSystemInfos); + } directoryInfo = new InMemoryDirectoryInfo("/", files); fileSystemInfos = directoryInfo.EnumerateFileSystemInfos(); From 346c61c5201452f1c63eddb1739a3acf597fbc81 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Mon, 1 Mar 2021 13:12:12 -0800 Subject: [PATCH 3/3] Address test suggestion --- .../tests/FunctionalTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs index 6b1d2fa7903d7a..602047b5e4787c 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs @@ -855,13 +855,13 @@ public void VerifyInMemoryDirectoryInfo_IsNotEmpty() directoryInfo = new InMemoryDirectoryInfo(@"C:\", files); fileSystemInfos = directoryInfo.EnumerateFileSystemInfos(); - Assert.NotEmpty(fileSystemInfos); + Assert.Equal(1, fileSystemInfos.Count()); } directoryInfo = new InMemoryDirectoryInfo("/", files); fileSystemInfos = directoryInfo.EnumerateFileSystemInfos(); - Assert.NotEmpty(fileSystemInfos); + Assert.Equal(1, fileSystemInfos.Count()); } } }