diff options
author | Jeremy Kuhne <jeremy.kuhne@microsoft.com> | 2018-02-06 02:28:14 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-02-06 02:28:14 +0300 |
commit | 66ada723c1b0ae9062a0e7b0b28862014b985019 (patch) | |
tree | 213ff5181f3399b1204f95394120da9d7d3e7034 /src/System.IO.FileSystem/tests | |
parent | 4adbec49e0bed722d17b1c1f1ee4cbc2124f0937 (diff) |
File enumeration extensibility (#26806)
* Windows file enumeration extensibility
This is the Windows implementation of System.IO.Enumeration. It adds
new find options to existing APIs and a public extensibilty model
for richer, low allocation file system enumeration.
The Unix implementation is in progress.
* Initial naive Unix implementation of FileSystemEnumerator
* Move a netcoreapp test to the right group
* Fix WinRT build.
* Address feedback from @danmosemsft
* Remove inline attribute
* Remove IsNameDotOrDotDot property
* Address futher feedback.
* Remove unreferenced common code
Diffstat (limited to 'src/System.IO.FileSystem/tests')
5 files changed, 291 insertions, 0 deletions
diff --git a/src/System.IO.FileSystem/tests/Enumeration/ConstructionTests.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/ConstructionTests.netcoreapp.cs new file mode 100644 index 0000000000..4f453200ee --- /dev/null +++ b/src/System.IO.FileSystem/tests/Enumeration/ConstructionTests.netcoreapp.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO.Enumeration; +using Xunit; + +namespace System.IO.Tests.Enumeration +{ + public class ConstructionTests : FileSystemTest + { + [Fact] + public void Enumerable_NullTransformThrows() + { + AssertExtensions.Throws<ArgumentNullException>("transform", + () => new FileSystemEnumerable<string>(TestDirectory, transform: null)); + } + + [Fact] + public void Enumerable_NullDirectoryThrows() + { + AssertExtensions.Throws<ArgumentNullException>("directory", + () => new FileSystemEnumerable<string>(null, null)); + } + + private class TestEnumerator : FileSystemEnumerator<string> + { + public TestEnumerator(string directory, EnumerationOptions options) + : base(directory, options) + { + } + + protected override string TransformEntry(ref FileSystemEntry entry) + { + throw new NotImplementedException(); + } + } + + [Fact] + public void Enumerator_NullDirectoryThrows() + { + AssertExtensions.Throws<ArgumentNullException>("directory", + () => new TestEnumerator(null, null)); + } + } +} diff --git a/src/System.IO.FileSystem/tests/Enumeration/DosMatcherTests.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/DosMatcherTests.netcoreapp.cs new file mode 100644 index 0000000000..cdbfbcc27a --- /dev/null +++ b/src/System.IO.FileSystem/tests/Enumeration/DosMatcherTests.netcoreapp.cs @@ -0,0 +1,116 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO.Enumeration; +using Xunit; + +namespace System.IO.Tests +{ + public class DosMatcherTests + { + [Theory, MemberData(nameof(DosMatchData)), MemberData(nameof(EscapedDosMatchData))] + public static void DosMatch(string expression, string name, bool ignoreCase, bool expected) + { + Assert.Equal(expected, FileSystemName.MatchesDosExpression(expression, name.AsReadOnlySpan(), ignoreCase)); + } + + public static TheoryData<string, string, bool, bool> EscapedDosMatchData => new TheoryData<string, string, bool, bool> + { + // Trailing escape matches as it is considered "invisible" + { "\\", "\\", false, true }, + { "\\", "\\", true, true }, + { "\\\\", "\\", false, true }, + { "\\\\", "\\", true, true }, + + + { "\\*", "a", false, false }, + { "\\*", "a", true, false }, + { "\\*", "*", false, true }, + { "\\*", "*", true, true }, + { "*\\*", "***", false, true }, + { "*\\*", "***", true, true }, + { "*\\*", "ABC*", false, true }, + { "*\\*", "ABC*", true, true }, + { "*\\*", "***A", false, false }, + { "*\\*", "***A", true, false }, + { "*\\*", "ABC*A", false, false }, + { "*\\*", "ABC*A", true, false }, + + { "\\\"", "a", false, false }, + { "\\\"", "a", true, false }, + { "\\\"", "\"", false, true }, + { "\\\"", "\"", true, true }, + }; + + public static TheoryData<string, string, bool, bool> DosMatchData => new TheoryData<string, string, bool, bool> + { + { null, "", false, false }, + { null, "", true, false }, + { "*", "", false, false }, + { "*", "", true, false }, + { "*", "ab", false, true }, + { "*", "AB", true, true }, + { "*foo", "foo", false, true }, + { "*foo", "foo", true, true }, + { "*foo", "FOO", false, false }, + { "*foo", "FOO", true, true }, + { "*foo", "nofoo", true, true }, + { "*foo", "NoFOO", true, true }, + { "*foo", "noFOO", false, false }, + + { @"*", @"foo.txt", true, true }, + { @".", @"foo.txt", true, false }, + { @".", @"footxt", true, false }, + { @"*.*", @"foo.txt", true, true }, + { @"*.*", @"foo.", true, true }, + { @"*.*", @".foo", true, true }, + { @"*.*", @"footxt", true, false }, + { "<\"*", @"footxt", true, true }, // DOS equivalent of *.* + { "<\"*", @"foo.txt", true, true }, // DOS equivalent of *.* + { "<\"*", @".foo", true, true }, // DOS equivalent of *.* + { "<\"*", @"foo.", true, true }, // DOS equivalent of *.* + { ">\">", @"a.b", true, true }, // DOS equivalent of ?.? + { ">\">", @"a.", true, true }, // DOS equivalent of ?.? + { ">\">", @"a", true, true }, // DOS equivalent of ?.? + { ">\">", @"ab", true, false }, // DOS equivalent of ?.? + { ">\">", @"a.bc", true, false }, // DOS equivalent of ?.? + { ">\">", @"ab.c", true, false }, // DOS equivalent of ?.? + { ">>\">>", @"a.b", true, true }, // DOS equivalent of ??.?? + { ">>\"\">>", @"a.b", true, false }, // Not possible to do from DOS ??""?? + { ">>\">>", @"a.bc", true, true }, // DOS equivalent of ??.?? + { ">>\">>", @"ab.ba", true, true }, // DOS equivalent of ??.?? + { ">>\">>", @"ab.", true, true }, // DOS equivalent of ??.?? + { ">>\"\"\">>", @"ab.", true, true }, // Not possible to do from DOS ??"""?? + { ">>b\">>", @"ab.ba", true, false }, // DOS equivalent of ??b.?? + { "a>>\">>", @"ab.ba", true, true }, // DOS equivalent of a??.?? + { ">>\">>a", @"ab.ba", true, false }, // DOS equivalent of ??.??a + { ">>\"b>>", @"ab.ba", true, true }, // DOS equivalent of ??.b?? + { ">>\"b>>", @"ab.b", true, true }, // DOS equivalent of ??.b?? + { ">>b.>>", @"ab.ba", true, false }, + { "a>>.>>", @"ab.ba", true, true }, + { ">>.>>a", @"ab.ba", true, false }, + { ">>.b>>", @"ab.ba", true, true }, + { ">>.b>>", @"ab.b", true, true }, + { ">>\">>\">>", @"ab.ba", true, true }, // DOS equivalent of ??.??.?? (The last " is an optional period) + { ">>\">>\">>", @"abba", true, false }, // DOS equivalent of ??.??.?? (The first " isn't, so this doesn't match) + { ">>\"ab\"ba", @"ab.ba", true, false }, // DOS equivalent of ??.ab.ba + { "ab\"ba\">>", @"ab.ba", true, true }, // DOS equivalent of ab.ba.?? + { "ab\">>\"ba", @"ab.ba", true, false }, // DOS equivalent of ab.??.ba + { ">>\">>\">>>", @"ab.ba.cab", true, true }, // DOS equivalent of ??.??.??? + { "a>>\"b>>\"c>>>", @"ab.ba.cab", true, true }, // DOS equivalent of a??.b??.c??? + { @"<", @"a", true, true }, // DOS equivalent of *. + { @"<", @"a.", true, true }, // DOS equivalent of *. + { @"<", @"a. ", true, false }, // DOS equivalent of *. + { @"<", @"a.b", true, false }, // DOS equivalent of *. + { @"foo<", @"foo.", true, true }, // DOS equivalent of foo*. + { @"foo<", @"foo. ", true, false }, // DOS equivalent of foo*. + { @"<<", @"a.b", true, true }, + { @"<<", @"a.b.c", true, true }, + { "<\"", @"a.b.c", true, false }, + { @"<.", @"a", true, false }, + { @"<.", @"a.", true, true }, + { @"<.", @"a.b", true, false }, + }; + } +} diff --git a/src/System.IO.FileSystem/tests/Enumeration/SkipAttributeTests.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/SkipAttributeTests.netcoreapp.cs new file mode 100644 index 0000000000..7aa609b452 --- /dev/null +++ b/src/System.IO.FileSystem/tests/Enumeration/SkipAttributeTests.netcoreapp.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO.Enumeration; +using System.Linq; +using Xunit; + +namespace System.IO.Tests.Enumeration +{ + public class SkipAttributeTests : FileSystemTest + { + protected virtual string[] GetPaths(string directory, EnumerationOptions options) + { + return new FileSystemEnumerable<string>( + directory, + (ref FileSystemEntry entry) => entry.ToFullPath(), + options) + { + ShouldIncludePredicate = (ref FileSystemEntry entry) => { return !entry.IsDirectory; } + }.ToArray(); + } + + [Fact] + public void SkippingHiddenFiles() + { + DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath()); + DirectoryInfo testSubdirectory = Directory.CreateDirectory(Path.Combine(testDirectory.FullName, GetTestFileName())); + FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, GetTestFileName())); + + // Put a period in front to make it hidden on Unix + FileInfo fileTwo = new FileInfo(Path.Combine(testDirectory.FullName, "." + GetTestFileName())); + FileInfo fileThree = new FileInfo(Path.Combine(testSubdirectory.FullName, GetTestFileName())); + FileInfo fileFour = new FileInfo(Path.Combine(testSubdirectory.FullName, "." + GetTestFileName())); + + fileOne.Create().Dispose(); + fileTwo.Create().Dispose(); + if (PlatformDetection.IsWindows) + fileTwo.Attributes = fileTwo.Attributes | FileAttributes.Hidden; + fileThree.Create().Dispose(); + fileFour.Create().Dispose(); + if (PlatformDetection.IsWindows) + fileFour.Attributes = fileTwo.Attributes | FileAttributes.Hidden; + + string[] paths = GetPaths(testDirectory.FullName, new EnumerationOptions { AttributesToSkip = FileAttributes.Hidden }); + Assert.Equal(new string[] { fileOne.FullName }, paths); + + paths = GetPaths(testDirectory.FullName, new EnumerationOptions { AttributesToSkip = FileAttributes.Hidden, RecurseSubdirectories = true }); + Assert.Equal(new string[] { fileOne.FullName, fileThree.FullName }, paths); + + if (PlatformDetection.IsWindows) + { + // Shouldn't recurse into the subdirectory now that it is hidden + testSubdirectory.Attributes = testSubdirectory.Attributes | FileAttributes.Hidden; + } + else + { + Directory.Move(testSubdirectory.FullName, Path.Combine(testDirectory.FullName, "." + testSubdirectory.Name)); + } + + paths = GetPaths(testDirectory.FullName, new EnumerationOptions { AttributesToSkip = FileAttributes.Hidden, RecurseSubdirectories = true }); + Assert.Equal(new string[] { fileOne.FullName }, paths); + } + } + + // Unix implementation not finished + [ActiveIssue(26715, TestPlatforms.AnyUnix)] + public class SkipAttributeTests_Directory_GetFiles : SkipAttributeTests + { + protected override string[] GetPaths(string directory, EnumerationOptions options) + { + return Directory.GetFiles(directory, "*", options); + } + } + + // Unix implementation not finished + [ActiveIssue(26715, TestPlatforms.AnyUnix)] + public class SkipAttributeTests_DirectoryInfo_GetFiles : SkipAttributeTests + { + protected override string[] GetPaths(string directory, EnumerationOptions options) + { + return new DirectoryInfo(directory).GetFiles("*", options).Select(i => i.FullName).ToArray(); + } + } +} diff --git a/src/System.IO.FileSystem/tests/Enumeration/SpecialDirectoryTests.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/SpecialDirectoryTests.netcoreapp.cs new file mode 100644 index 0000000000..98f3a2098a --- /dev/null +++ b/src/System.IO.FileSystem/tests/Enumeration/SpecialDirectoryTests.netcoreapp.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO.Enumeration; +using System.Linq; +using Xunit; + +namespace System.IO.Tests.Enumeration +{ + public class SpecialDirectoryTests : FileSystemTest + { + protected virtual string[] GetNames(string directory, EnumerationOptions options) + { + return new FileSystemEnumerable<string>( + directory, + (ref FileSystemEntry entry) => new string(entry.FileName), + options).ToArray(); + } + + [Fact] + public void SkippingHiddenFiles() + { + string[] paths = GetNames(TestDirectory, new EnumerationOptions { ReturnSpecialDirectories = true }); + Assert.Contains(".", paths); + Assert.Contains("..", paths); + } + } + + public class SpecialDirectoryTests_DirectoryInfo_GetDirectories : SpecialDirectoryTests + { + protected override string[] GetNames(string directory, EnumerationOptions options) + { + return new DirectoryInfo(directory).GetDirectories("*", options).Select(i => i.Name).ToArray(); + } + } +} diff --git a/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj index b6637446b0..c4ed74a5e8 100644 --- a/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj +++ b/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj @@ -53,6 +53,10 @@ <Compile Include="File\ReadWriteAllBytesAsync.cs" /> <Compile Include="File\ReadWriteAllTextAsync.cs" /> <Compile Include="FileStream\ReadWriteSpan.netcoreapp.cs" /> + <Compile Include="Enumeration\ConstructionTests.netcoreapp.cs" /> + <Compile Include="Enumeration\SpecialDirectoryTests.netcoreapp.cs" /> + <Compile Include="Enumeration\SkipAttributeTests.netcoreapp.cs" /> + <Compile Include="Enumeration\DosMatcherTests.netcoreapp.cs" /> </ItemGroup> <ItemGroup> <!-- Rewritten --> @@ -181,5 +185,8 @@ <ItemGroup> <EmbeddedResource Include="Resources\$(AssemblyName).rd.xml" /> </ItemGroup> + <ItemGroup> + <Folder Include="Matchers\" /> + </ItemGroup> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> </Project>
\ No newline at end of file |