Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corefx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarek Safar <marek.safar@gmail.com>2018-04-02 00:54:57 +0300
committerMarek Safar <marek.safar@gmail.com>2018-04-02 00:54:57 +0300
commitf4951179bb053a9264f063c824720d6237ae55f1 (patch)
treef7beda10c1e1c9f8a8f4c21a000993598ea0502c /src/System.IO.FileSystem/tests
parente89c5c9646d29d74a069f8f9630e35858b6bb076 (diff)
parent79b3c40e4322fd1778ad075214c90af93e5d2adf (diff)
Merge remote-tracking branch 'upstream/release/2.1' into 2.1-merge
Diffstat (limited to 'src/System.IO.FileSystem/tests')
-rw-r--r--src/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs70
-rw-r--r--src/System.IO.FileSystem/tests/Base/FileGetSetAttributes.cs53
-rw-r--r--src/System.IO.FileSystem/tests/Base/StaticGetSetTimes.cs2
-rw-r--r--src/System.IO.FileSystem/tests/Directory/CreateDirectory.cs109
-rw-r--r--src/System.IO.FileSystem/tests/Directory/Delete.cs2
-rw-r--r--src/System.IO.FileSystem/tests/Directory/EnumerableTests.cs11
-rw-r--r--src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str.cs122
-rw-r--r--src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str_str.cs14
-rw-r--r--src/System.IO.FileSystem/tests/Directory/Move.cs24
-rw-r--r--src/System.IO.FileSystem/tests/DirectoryInfo/CreateSubdirectory.cs42
-rw-r--r--src/System.IO.FileSystem/tests/DirectoryInfo/Exists.cs6
-rw-r--r--src/System.IO.FileSystem/tests/DirectoryInfo/GetSetTimes.cs2
-rw-r--r--src/System.IO.FileSystem/tests/DirectoryInfo/ToString.cs18
-rw-r--r--src/System.IO.FileSystem/tests/Enumeration/AttributeTests.netcoreapp.cs141
-rw-r--r--src/System.IO.FileSystem/tests/Enumeration/ConstructionTests.netcoreapp.cs46
-rw-r--r--src/System.IO.FileSystem/tests/Enumeration/ErrorHandlingTests.netcoreapp.cs71
-rw-r--r--src/System.IO.FileSystem/tests/Enumeration/ExampleTests.netcoreapp.cs141
-rw-r--r--src/System.IO.FileSystem/tests/Enumeration/FileSystemNameTests.netcoreapp.cs154
-rw-r--r--src/System.IO.FileSystem/tests/Enumeration/IncludePredicateTests.netcoreapp.cs55
-rw-r--r--src/System.IO.FileSystem/tests/Enumeration/MatchCasingTests.netcoreapp.cs60
-rw-r--r--src/System.IO.FileSystem/tests/Enumeration/MatchTypesTests.netcoreapp.cs80
-rw-r--r--src/System.IO.FileSystem/tests/Enumeration/PatternTransformTests.netcoreapp.cs126
-rw-r--r--src/System.IO.FileSystem/tests/Enumeration/RootTests.netcoreapp.cs59
-rw-r--r--src/System.IO.FileSystem/tests/Enumeration/SkipAttributeTests.netcoreapp.cs107
-rw-r--r--src/System.IO.FileSystem/tests/Enumeration/SpecialDirectoryTests.netcoreapp.cs76
-rw-r--r--src/System.IO.FileSystem/tests/Enumeration/TrimmedPaths.netcoreapp.cs284
-rw-r--r--src/System.IO.FileSystem/tests/FSAssert.cs7
-rw-r--r--src/System.IO.FileSystem/tests/File/Copy.cs131
-rw-r--r--src/System.IO.FileSystem/tests/File/Create.cs93
-rw-r--r--src/System.IO.FileSystem/tests/File/Delete.cs25
-rw-r--r--src/System.IO.FileSystem/tests/File/Exists.cs2
-rw-r--r--src/System.IO.FileSystem/tests/File/GetSetAttributes.cs14
-rw-r--r--src/System.IO.FileSystem/tests/File/Move.cs111
-rw-r--r--src/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs53
-rw-r--r--src/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs52
-rw-r--r--src/System.IO.FileSystem/tests/FileInfo/GetSetTimes.cs2
-rw-r--r--src/System.IO.FileSystem/tests/FileInfo/Open.cs12
-rw-r--r--src/System.IO.FileSystem/tests/FileStream/Dispose.cs92
-rw-r--r--src/System.IO.FileSystem/tests/FileStream/Name.cs14
-rw-r--r--src/System.IO.FileSystem/tests/FileStream/ReadWriteSpan.netcoreapp.cs48
-rw-r--r--src/System.IO.FileSystem/tests/FileStream/WriteAsync.cs35
-rw-r--r--src/System.IO.FileSystem/tests/FileStream/ctor_str_fm.cs106
-rw-r--r--src/System.IO.FileSystem/tests/FileSystemTest.cs2
-rw-r--r--src/System.IO.FileSystem/tests/Performance/Perf.Directory.cs86
-rw-r--r--src/System.IO.FileSystem/tests/Performance/System.IO.FileSystem.Performance.Tests.csproj3
-rw-r--r--src/System.IO.FileSystem/tests/PortedCommon/IOInputs.cs2
-rw-r--r--src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj19
-rw-r--r--src/System.IO.FileSystem/tests/TestData.cs23
48 files changed, 2587 insertions, 220 deletions
diff --git a/src/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs b/src/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs
index f650ae07f3..865cde2ad8 100644
--- a/src/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs
+++ b/src/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
+using System.Threading;
using Xunit;
namespace System.IO.Tests
@@ -15,6 +16,8 @@ namespace System.IO.Tests
public abstract T GetExistingItem();
public abstract T GetMissingItem();
+ public abstract string GetItemPath(T item);
+
public abstract IEnumerable<TimeFunction> TimeFunctions(bool requiresRoundtripping = false);
public class TimeFunction : Tuple<SetTime, GetTime, DateTimeKind>
@@ -68,19 +71,35 @@ namespace System.IO.Tests
}
[Fact]
- [ActiveIssue(26349, TestPlatforms.AnyUnix)]
- public void TimesIncludeMillisecondPart()
+ [PlatformSpecific(TestPlatforms.Linux)] // Windows tested below, and OSX does not currently support millisec granularity
+ public void TimesIncludeMillisecondPart_Linux()
{
T item = GetExistingItem();
+
+ string driveFormat = new DriveInfo(GetItemPath(item)).DriveFormat;
+
Assert.All(TimeFunctions(), (function) =>
{
var msec = 0;
- for (int i = 0; i < 3; i++)
+ for (int i = 0; i < 5; i++)
{
- msec = function.Getter(item).Millisecond;
+ DateTime time = function.Getter(item);
+ msec = time.Millisecond;
+
if (msec != 0)
break;
+ // This case should only happen 1/1000 times, unless the OS/Filesystem does
+ // not support millisecond granularity.
+
+ // If it's 1/1000, or low granularity, this may help:
+ Thread.Sleep(1234);
+
+ // If it's the OS/Filesystem often returns 0 for the millisecond part, this may
+ // help prove it. This should only be written 1/1000 runs, unless the test is going to
+ // fail.
+ Console.WriteLine($"## TimesIncludeMillisecondPart got a file time of {time.ToString("o")} on {driveFormat}");
+
item = GetExistingItem(); // try a new file/directory
}
@@ -88,6 +107,49 @@ namespace System.IO.Tests
});
}
+
+ [Fact]
+ [PlatformSpecific(TestPlatforms.Windows)] // Breaking out Windows as it passes no problem there
+ public void TimesIncludeMillisecondPart_Windows()
+ {
+ T item = GetExistingItem();
+ Assert.All(TimeFunctions(), (function) =>
+ {
+ var msec = 0;
+ for (int i = 0; i < 5; i++)
+ {
+ DateTime time = function.Getter(item);
+ msec = time.Millisecond;
+ if (msec != 0)
+ break;
+
+ // This case should only happen 1/1000 times, unless the OS/Filesystem does
+ // not support millisecond granularity.
+
+ // If it's 1/1000, or low granularity, this may help:
+ Thread.Sleep(1234);
+
+ item = GetExistingItem(); // try a new file/directory
+ }
+
+ Assert.NotEqual(0, msec);
+ });
+ }
+
+ [Fact]
+ // OSX does not currently support millisec granularity: use this test as a canary to flag
+ // if this ever changes so we can enable the actual test
+ [PlatformSpecific(TestPlatforms.OSX)]
+ public void TimesIncludeMillisecondPart_OSX()
+ {
+ T item = GetExistingItem();
+ Assert.All(TimeFunctions(), (function) =>
+ {
+ DateTime time = function.Getter(item);
+ Assert.Equal(0, time.Millisecond);
+ });
+ }
+
protected void ValidateSetTimes(T item, DateTime beforeTime, DateTime afterTime)
{
Assert.All(TimeFunctions(), (function) =>
diff --git a/src/System.IO.FileSystem/tests/Base/FileGetSetAttributes.cs b/src/System.IO.FileSystem/tests/Base/FileGetSetAttributes.cs
index bdb4cee984..59d784fbe9 100644
--- a/src/System.IO.FileSystem/tests/Base/FileGetSetAttributes.cs
+++ b/src/System.IO.FileSystem/tests/Base/FileGetSetAttributes.cs
@@ -12,12 +12,12 @@ namespace System.IO.Tests
[Theory]
[InlineData(FileAttributes.ReadOnly)]
[InlineData(FileAttributes.Normal)]
- [PlatformSpecific(TestPlatforms.AnyUnix)] // Unix valid file attributes
- public void UnixAttributeSetting(FileAttributes attr)
+ [PlatformSpecific(TestPlatforms.AnyUnix)]
+ public void SettingAttributes_Unix(FileAttributes attributes)
{
string path = CreateItem();
- SetAttributes(path, attr);
- Assert.Equal(attr, GetAttributes(path));
+ SetAttributes(path, attributes);
+ Assert.Equal(attributes, GetAttributes(path));
SetAttributes(path, 0);
}
@@ -29,12 +29,12 @@ namespace System.IO.Tests
[InlineData(FileAttributes.Normal)]
[InlineData(FileAttributes.Temporary)]
[InlineData(FileAttributes.ReadOnly | FileAttributes.Hidden)]
- [PlatformSpecific(TestPlatforms.Windows)] // Valid Windows file attribute
- public void WindowsAttributeSetting(FileAttributes attr)
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void SettingAttributes_Windows(FileAttributes attributes)
{
string path = CreateItem();
- SetAttributes(path, attr);
- Assert.Equal(attr, GetAttributes(path));
+ SetAttributes(path, attributes);
+ Assert.Equal(attributes, GetAttributes(path));
SetAttributes(path, 0);
}
@@ -44,11 +44,11 @@ namespace System.IO.Tests
[InlineData(FileAttributes.SparseFile)]
[InlineData(FileAttributes.ReparsePoint)]
[InlineData(FileAttributes.Compressed)]
- [PlatformSpecific(TestPlatforms.AnyUnix)] // Unix invalid file attributes
- public void UnixInvalidAttributes(FileAttributes attr)
+ [PlatformSpecific(TestPlatforms.AnyUnix)]
+ public void SettingInvalidAttributes_Unix(FileAttributes attributes)
{
string path = CreateItem();
- SetAttributes(path, attr);
+ SetAttributes(path, attributes);
Assert.Equal(FileAttributes.Normal, GetAttributes(path));
}
@@ -58,11 +58,36 @@ namespace System.IO.Tests
[InlineData(FileAttributes.SparseFile)]
[InlineData(FileAttributes.ReparsePoint)]
[InlineData(FileAttributes.Compressed)]
- [PlatformSpecific(TestPlatforms.Windows)] // Invalid Windows file attributes
- public void WindowsInvalidAttributes(FileAttributes attr)
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void SettingInvalidAttributes_Windows(FileAttributes attributes)
{
string path = CreateItem();
- SetAttributes(path, attr);
+ SetAttributes(path, attributes);
+ Assert.Equal(FileAttributes.Normal, GetAttributes(path));
+ }
+
+ [Theory,
+ InlineData(":bar"),
+ InlineData(":bar:$DATA")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void GettingAndSettingAttributes_AlternateDataStream_Windows(string streamName)
+ {
+ string path = CreateItem();
+ streamName = path + streamName;
+ File.Create(streamName);
+
+ FileAttributes attributes = GetAttributes(streamName);
+ Assert.NotEqual((FileAttributes)0, attributes);
+ Assert.NotEqual((FileAttributes)(-1), attributes);
+
+ // Attributes are shared for the file and all streams
+ SetAttributes(streamName, FileAttributes.Hidden);
+ Assert.Equal(FileAttributes.Hidden, GetAttributes(streamName));
+ Assert.Equal(FileAttributes.Hidden, GetAttributes(path));
+
+ SetAttributes(path, FileAttributes.Normal);
+ Assert.Equal(FileAttributes.Normal, GetAttributes(streamName));
Assert.Equal(FileAttributes.Normal, GetAttributes(path));
}
}
diff --git a/src/System.IO.FileSystem/tests/Base/StaticGetSetTimes.cs b/src/System.IO.FileSystem/tests/Base/StaticGetSetTimes.cs
index 6444ff203f..12ee8997fd 100644
--- a/src/System.IO.FileSystem/tests/Base/StaticGetSetTimes.cs
+++ b/src/System.IO.FileSystem/tests/Base/StaticGetSetTimes.cs
@@ -10,6 +10,8 @@ namespace System.IO.Tests
{
public override string GetMissingItem() => GetTestFilePath();
+ public override string GetItemPath(string item) => item;
+
[Fact]
public void NullPath_ThrowsArgumentNullException()
{
diff --git a/src/System.IO.FileSystem/tests/Directory/CreateDirectory.cs b/src/System.IO.FileSystem/tests/Directory/CreateDirectory.cs
index bfd60bb977..021a05b949 100644
--- a/src/System.IO.FileSystem/tests/Directory/CreateDirectory.cs
+++ b/src/System.IO.FileSystem/tests/Directory/CreateDirectory.cs
@@ -34,14 +34,20 @@ namespace System.IO.Tests
}
[Theory, MemberData(nameof(PathsWithInvalidCharacters))]
- public void PathWithInvalidCharactersAsPath_ThrowsArgumentException(string invalidPath)
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void PathWithInvalidCharactersAsPath_Desktop(string invalidPath)
{
- if (invalidPath.Equals(@"\\?\") && !PathFeatures.IsUsingLegacyPathNormalization())
- AssertExtensions.ThrowsAny<IOException, UnauthorizedAccessException>(() => Create(invalidPath));
- else if (invalidPath.Contains(@"\\?\") && !PathFeatures.IsUsingLegacyPathNormalization())
- Assert.Throws<DirectoryNotFoundException>(() => Create(invalidPath));
+ Assert.Throws<ArgumentException>(() => Create(invalidPath));
+ }
+
+ [Theory, MemberData(nameof(PathsWithInvalidCharacters))]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void PathWithInvalidCharactersAsPath_Core(string invalidPath)
+ {
+ if (invalidPath.Contains('\0'))
+ Assert.Throws<ArgumentException>("path", () => Create(invalidPath));
else
- Assert.Throws<ArgumentException>(() => Create(invalidPath));
+ Assert.Throws<IOException>(() => Create(invalidPath));
}
[Fact]
@@ -203,11 +209,28 @@ namespace System.IO.Tests
#region PlatformSpecific
[Theory, MemberData(nameof(PathsWithInvalidColons))]
- [PlatformSpecific(TestPlatforms.Windows)] // invalid colons throws ArgumentException
- [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Versions of netfx older than 4.6.2 throw an ArgumentException instead of NotSupportedException. Until all of our machines run netfx against the actual latest version, these will fail.")]
- public void PathWithInvalidColons_ThrowsNotSupportedException(string invalidPath)
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void PathWithInvalidColons_ThrowsNotSupportedException_Desktop(string invalidPath)
{
- Assert.Throws<NotSupportedException>(() => Create(invalidPath));
+ if (PathFeatures.IsUsingLegacyPathNormalization())
+ {
+ Assert.Throws<ArgumentException>(() => Create(invalidPath));
+ }
+ else
+ {
+ Assert.Throws<NotSupportedException>(() => Create(invalidPath));
+ }
+ }
+
+ [Theory, MemberData(nameof(PathsWithInvalidColons))]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void PathsWithInvalidColons_ThrowIOException_Core(string invalidPath)
+ {
+ // You can't actually create a directory with a colon in it. It was a preemptive
+ // check, now we let the OS give us failures on usage.
+ Assert.ThrowsAny<IOException>(() => Create(invalidPath));
}
[ConditionalFact(nameof(AreAllLongPathsAvailable))]
@@ -308,14 +331,33 @@ namespace System.IO.Tests
}
[Theory,
- MemberData(nameof(WhiteSpace))]
- [PlatformSpecific(TestPlatforms.Windows)] // whitespace as path throws ArgumentException on Windows
- public void WindowsWhiteSpaceAsPath_ThrowsArgumentException(string path)
+ MemberData(nameof(SimpleWhiteSpace))]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void WindowsSimpleWhiteSpaceAsPath_ThrowsArgumentException(string path)
+ {
+ Assert.Throws<ArgumentException>(() => Create(path));
+ }
+
+ [Theory,
+ MemberData(nameof(ControlWhiteSpace))]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void WindowsControlWhiteSpaceAsPath_ThrowsArgumentException_Desktop(string path)
{
Assert.Throws<ArgumentException>(() => Create(path));
}
[Theory,
+ MemberData(nameof(ControlWhiteSpace))]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsWhiteSpaceAsPath_ThrowsIOException_Core(string path)
+ {
+ Assert.Throws<IOException>(() => Create(path));
+ }
+
+
+ [Theory,
MemberData(nameof(WhiteSpace))]
[PlatformSpecific(TestPlatforms.AnyUnix)] // whitespace as path allowed
public void UnixWhiteSpaceAsPath_Allowed(string path)
@@ -397,14 +439,24 @@ namespace System.IO.Tests
}
[Theory,
- MemberData(nameof(PathsWithAlternativeDataStreams))]
+ MemberData(nameof(PathsWithColons))]
[PlatformSpecific(TestPlatforms.Windows)] // alternate data streams
- public void PathWithAlternateDataStreams_ThrowsNotSupportedException(string path)
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void PathWithColons_ThrowsNotSupportedException_Desktop(string path)
{
Assert.Throws<NotSupportedException>(() => Create(path));
}
[Theory,
+ MemberData(nameof(PathsWithColons))]
+ [PlatformSpecific(TestPlatforms.Windows)] // alternate data streams
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void PathWithColons_ThrowsIOException_Core(string path)
+ {
+ Assert.ThrowsAny<IOException>(() => Create(Path.Combine(TestDirectory, path)));
+ }
+
+ [Theory,
MemberData(nameof(PathsWithReservedDeviceNames))]
[PlatformSpecific(TestPlatforms.Windows)] // device name prefixes
public void PathWithReservedDeviceNameAsPath_ThrowsDirectoryNotFoundException(string path)
@@ -424,20 +476,39 @@ namespace System.IO.Tests
[Theory,
MemberData(nameof(UncPathsWithoutShareName))]
- [PlatformSpecific(TestPlatforms.Windows)] // UNC shares
- public void UncPathWithoutShareNameAsPath_ThrowsArgumentException(string path)
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void UncPathWithoutShareNameAsPath_ThrowsArgumentException_Desktop(string path)
{
Assert.Throws<ArgumentException>(() => Create(path));
}
+ [Theory,
+ MemberData(nameof(UncPathsWithoutShareName))]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void UncPathWithoutShareNameAsPath_ThrowsIOException_Core(string path)
+ {
+ Assert.ThrowsAny<IOException>(() => Create(path));
+ }
+
[Fact]
- [PlatformSpecific(TestPlatforms.Windows)] // UNC shares
- public void UNCPathWithOnlySlashes()
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void UNCPathWithOnlySlashes_Desktop()
{
Assert.Throws<ArgumentException>(() => Create("//"));
}
[Fact]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void UNCPathWithOnlySlashes_Core()
+ {
+ Assert.ThrowsAny<IOException>(() => Create("//"));
+ }
+
+ [Fact]
[PlatformSpecific(TestPlatforms.Windows)] // drive labels
[ActiveIssue(20117, TargetFrameworkMonikers.Uap)]
public void CDriveCase()
diff --git a/src/System.IO.FileSystem/tests/Directory/Delete.cs b/src/System.IO.FileSystem/tests/Directory/Delete.cs
index 98ab80c3ef..52f5b9dfad 100644
--- a/src/System.IO.FileSystem/tests/Directory/Delete.cs
+++ b/src/System.IO.FileSystem/tests/Directory/Delete.cs
@@ -208,7 +208,7 @@ namespace System.IO.Tests
[Trait(XunitConstants.Category, XunitConstants.RequiresElevation)]
public void Unix_NotFoundDirectory_ReadOnlyVolume()
{
- if (PlatformDetection.IsRedHatFamily6)
+ if (PlatformDetection.IsRedHatFamily6 || PlatformDetection.IsAlpine)
return; // [ActiveIssue(https://github.com/dotnet/corefx/issues/21920)]
ReadOnly_FileSystemHelper(readOnlyDirectory =>
diff --git a/src/System.IO.FileSystem/tests/Directory/EnumerableTests.cs b/src/System.IO.FileSystem/tests/Directory/EnumerableTests.cs
index e9c6028c56..7d604b6f1f 100644
--- a/src/System.IO.FileSystem/tests/Directory/EnumerableTests.cs
+++ b/src/System.IO.FileSystem/tests/Directory/EnumerableTests.cs
@@ -32,6 +32,17 @@ namespace System.IO.Tests
}
}
+ [Fact]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void EnumerateDirectories_NonBreakingSpace()
+ {
+ DirectoryInfo rootDirectory = Directory.CreateDirectory(GetTestFilePath());
+ DirectoryInfo subDirectory1 = rootDirectory.CreateSubdirectory("\u00A0");
+ DirectoryInfo subDirectory2 = subDirectory1.CreateSubdirectory(GetTestFileName());
+
+ FSAssert.EqualWhenOrdered(new string[] { subDirectory1.FullName, subDirectory2.FullName }, Directory.EnumerateDirectories(rootDirectory.FullName, string.Empty, SearchOption.AllDirectories));
+ }
+
class ThreadSafeRepro
{
volatile IEnumerator<string> _enumerator;
diff --git a/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str.cs b/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str.cs
index 8a4175705a..01402a222b 100644
--- a/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str.cs
+++ b/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str.cs
@@ -11,7 +11,7 @@ namespace System.IO.Tests
{
#region Utilities
- public static TheoryData WindowsInvalidUnixValid = new TheoryData<string> { " ", " ", "\n", ">", "<", "\t" };
+
protected virtual bool TestFiles { get { return true; } } // True if the virtual GetEntries mmethod returns files
protected virtual bool TestDirectories { get { return true; } } // True if the virtual GetEntries mmethod returns Directories
@@ -175,42 +175,125 @@ namespace System.IO.Tests
}
}
+ [Fact]
+ public void HiddenFilesAreReturned()
+ {
+ // Note that APIs that take EnumerationOptions do NOT find hidden files by default
+
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ 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()));
+ fileOne.Create().Dispose();
+ fileTwo.Create().Dispose();
+ if (PlatformDetection.IsWindows)
+ fileTwo.Attributes = fileTwo.Attributes | FileAttributes.Hidden;
+
+ if (TestFiles)
+ {
+ FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName }, GetEntries(testDirectory.FullName));
+ }
+ else
+ {
+ Assert.Empty(GetEntries(testDirectory.FullName));
+ }
+ }
+
#endregion
#region PlatformSpecific
[Fact]
- public void InvalidPath()
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void InvalidPath_Desktop()
{
foreach (char invalid in Path.GetInvalidFileNameChars())
{
- if (invalid == '/' || invalid == '\\')
- {
- Assert.Throws<DirectoryNotFoundException>(() => GetEntries(Path.Combine(TestDirectory, string.Format("te{0}st", invalid.ToString()))));
- }
- else if (invalid == ':')
+ string badPath = string.Format($"{TestDirectory}{Path.DirectorySeparatorChar}te{invalid}st");
+ switch (invalid)
{
- if (FileSystemDebugInfo.IsCurrentDriveNTFS())
- Assert.Throws<NotSupportedException>(() => GetEntries(Path.Combine(TestDirectory, string.Format("te{0}st", invalid.ToString()))));
+ case '/':
+ case '\\':
+ Assert.Throws<DirectoryNotFoundException>(() => GetEntries(badPath));
+ break;
+ case ':':
+ Assert.Throws<NotSupportedException>(() => GetEntries(badPath));
+ break;
+ case '\0':
+ Assert.Throws<ArgumentException>(() => GetEntries(badPath));
+ break;
+ default:
+ Assert.Throws<ArgumentException>(() => GetEntries(badPath));
+ break;
}
- else
+ }
+ }
+
+ [Fact]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void InvalidPath_Core()
+ {
+ foreach (char invalid in Path.GetInvalidFileNameChars())
+ {
+ string badPath = string.Format($"{TestDirectory}{Path.DirectorySeparatorChar}te{invalid}st");
+ switch (invalid)
{
- Assert.Throws<ArgumentException>(() => GetEntries(Path.Combine(TestDirectory, string.Format("te{0}st", invalid.ToString()))));
+ case '/':
+ case '\\':
+ case ':':
+ Assert.Throws<DirectoryNotFoundException>(() => GetEntries(badPath));
+ break;
+ case '\0':
+ Assert.Throws<ArgumentException>(() => GetEntries(badPath));
+ break;
+ default:
+ Assert.Throws<IOException>(() => GetEntries(badPath));
+ break;
}
}
}
[Theory,
- MemberData(nameof(WindowsInvalidUnixValid))]
- [PlatformSpecific(TestPlatforms.Windows)] // Windows-only Invalid chars in path
- public void WindowsInvalidCharsPath(string invalid)
+ InlineData(" "),
+ InlineData(" ")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void WindowsWhitespaceOnlyPath(string invalid)
+ {
+ Assert.Throws<ArgumentException>(() => GetEntries(invalid));
+ }
+
+ [Theory,
+ InlineData("\n"),
+ InlineData(">"),
+ InlineData("<"),
+ InlineData("\t")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void WindowsInvalidCharsPath_Desktop(string invalid)
{
-
Assert.Throws<ArgumentException>(() => GetEntries(invalid));
}
[Theory,
- MemberData(nameof(WindowsInvalidUnixValid))]
+ InlineData("\n"),
+ InlineData(">"),
+ InlineData("<"),
+ InlineData("\t")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsInvalidCharsPath_Core(string invalid)
+ {
+ Assert.Throws<IOException>(() => GetEntries(invalid));
+ }
+
+ [Theory,
+ InlineData(" "),
+ InlineData(" "),
+ InlineData("\n"),
+ InlineData(">"),
+ InlineData("<"),
+ InlineData("\t")]
[PlatformSpecific(TestPlatforms.AnyUnix)] // Unix-only valid chars in file path
public void UnixValidCharsFilePath(string valid)
{
@@ -226,7 +309,12 @@ namespace System.IO.Tests
}
[Theory,
- MemberData(nameof(WindowsInvalidUnixValid))]
+ InlineData(" "),
+ InlineData(" "),
+ InlineData("\n"),
+ InlineData(">"),
+ InlineData("<"),
+ InlineData("\t")]
[PlatformSpecific(TestPlatforms.AnyUnix)] // Windows-only invalid chars in directory path
public void UnixValidCharsDirectoryPath(string valid)
{
diff --git a/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str_str.cs b/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str_str.cs
index a352bf6529..e96881bc46 100644
--- a/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str_str.cs
+++ b/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str_str.cs
@@ -985,7 +985,12 @@ namespace System.IO.Tests
}
[Theory,
- MemberData(nameof(WindowsInvalidUnixValid))]
+ InlineData(" "),
+ InlineData(" "),
+ InlineData("\n"),
+ InlineData(">"),
+ InlineData("<"),
+ InlineData("\t")]
[PlatformSpecific(TestPlatforms.AnyUnix)] // Unix-valid chars in file search patterns
public void UnixSearchPatternFileValidChar(string valid)
{
@@ -999,7 +1004,12 @@ namespace System.IO.Tests
}
[Theory,
- MemberData(nameof(WindowsInvalidUnixValid))]
+ InlineData(" "),
+ InlineData(" "),
+ InlineData("\n"),
+ InlineData(">"),
+ InlineData("<"),
+ InlineData("\t")]
[PlatformSpecific(TestPlatforms.AnyUnix)] // Unix-valid chars in directory search patterns
public void UnixSearchPatternDirectoryValidChar(string valid)
{
diff --git a/src/System.IO.FileSystem/tests/Directory/Move.cs b/src/System.IO.FileSystem/tests/Directory/Move.cs
index 8c21954c03..1da296c1c3 100644
--- a/src/System.IO.FileSystem/tests/Directory/Move.cs
+++ b/src/System.IO.FileSystem/tests/Directory/Move.cs
@@ -242,8 +242,9 @@ namespace System.IO.Tests
}
[Fact]
- [PlatformSpecific(TestPlatforms.Windows)] // Wild characters in path, wild chars are normal chars on Unix
- public void WindowsWildCharacterPath()
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void WindowsWildCharacterPath_Desktop()
{
Assert.Throws<ArgumentException>(() => Move("*", GetTestFilePath()));
Assert.Throws<ArgumentException>(() => Move(TestDirectory, "*"));
@@ -252,6 +253,17 @@ namespace System.IO.Tests
}
[Fact]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsWildCharacterPath_Core()
+ {
+ Assert.ThrowsAny<IOException>(() => Move(Path.Combine(TestDirectory, "*"), GetTestFilePath()));
+ Assert.ThrowsAny<IOException>(() => Move(TestDirectory, Path.Combine(TestDirectory, "*")));
+ Assert.ThrowsAny<IOException>(() => Move(TestDirectory, Path.Combine(TestDirectory, "Test*t")));
+ Assert.ThrowsAny<IOException>(() => Move(TestDirectory, Path.Combine(TestDirectory, "*Test")));
+ }
+
+ [Fact]
[PlatformSpecific(TestPlatforms.AnyUnix)] // Wild characters in path are allowed
public void UnixWildCharacterPath()
{
@@ -278,17 +290,13 @@ namespace System.IO.Tests
}
[Fact]
- [PlatformSpecific(TestPlatforms.Windows)] // Whitespace path causes ArgumentException
- public void WindowsWhitespacePath()
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void WindowsEmptyPath()
{
DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
Assert.Throws<ArgumentException>(() => Move(testDir.FullName, " "));
- Assert.Throws<ArgumentException>(() => Move(testDir.FullName, "\n"));
Assert.Throws<ArgumentException>(() => Move(testDir.FullName, ""));
- Assert.Throws<ArgumentException>(() => Move(testDir.FullName, ">"));
- Assert.Throws<ArgumentException>(() => Move(testDir.FullName, "<"));
Assert.Throws<ArgumentException>(() => Move(testDir.FullName, "\0"));
- Assert.Throws<ArgumentException>(() => Move(testDir.FullName, "\t"));
}
[Fact]
diff --git a/src/System.IO.FileSystem/tests/DirectoryInfo/CreateSubdirectory.cs b/src/System.IO.FileSystem/tests/DirectoryInfo/CreateSubdirectory.cs
index 6122cb1f5c..75af106c41 100644
--- a/src/System.IO.FileSystem/tests/DirectoryInfo/CreateSubdirectory.cs
+++ b/src/System.IO.FileSystem/tests/DirectoryInfo/CreateSubdirectory.cs
@@ -140,27 +140,40 @@ namespace System.IO.Tests
[Theory,
MemberData(nameof(ControlWhiteSpace))]
- [PlatformSpecific(TestPlatforms.Windows)] // Control whitespace in path throws ArgumentException
- public void WindowsControlWhiteSpace(string component)
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void WindowsControlWhiteSpace_Desktop(string component)
+ {
+ Assert.Throws<ArgumentException>(() => new DirectoryInfo(TestDirectory).CreateSubdirectory(component));
+ }
+
+ [Theory,
+ MemberData(nameof(ControlWhiteSpace))]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsControlWhiteSpace_Core(string component)
+ {
+ Assert.Throws<IOException>(() => new DirectoryInfo(TestDirectory).CreateSubdirectory(component));
+ }
+
+ [Theory,
+ MemberData(nameof(SimpleWhiteSpace))]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void WindowsSimpleWhiteSpaceThrowsException(string component)
{
- // CreateSubdirectory will throw when passed a path with control whitespace e.g. "\t"
- string path = IOServices.RemoveTrailingSlash(GetTestFileName());
Assert.Throws<ArgumentException>(() => new DirectoryInfo(TestDirectory).CreateSubdirectory(component));
}
[Theory,
MemberData(nameof(SimpleWhiteSpace))]
- [PlatformSpecific(TestPlatforms.Windows)] // Simple whitespace is trimmed in path
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] // Simple whitespace is trimmed in path
public void WindowsSimpleWhiteSpace(string component)
{
- // CreateSubdirectory trims all simple whitespace, returning us the parent directory
- // that called CreateSubdirectory
- string path = IOServices.RemoveTrailingSlash(GetTestFileName());
DirectoryInfo result = new DirectoryInfo(TestDirectory).CreateSubdirectory(component);
Assert.True(Directory.Exists(result.FullName));
Assert.Equal(TestDirectory, IOServices.RemoveTrailingSlash(result.FullName));
-
}
[Theory,
@@ -170,7 +183,6 @@ namespace System.IO.Tests
{
new DirectoryInfo(TestDirectory).CreateSubdirectory(path);
Assert.True(Directory.Exists(Path.Combine(TestDirectory, path)));
-
}
[Theory,
@@ -207,6 +219,16 @@ namespace System.IO.Tests
Assert.Throws<ArgumentException>(() => testDir.CreateSubdirectory("//"));
}
+ [Fact]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void ParentDirectoryNameAsPrefixShouldThrow()
+ {
+ string randomName = GetTestFileName();
+ DirectoryInfo di = Directory.CreateDirectory(Path.Combine(TestDirectory, randomName));
+
+ Assert.Throws<ArgumentException>(() => di.CreateSubdirectory(Path.Combine("..", randomName + "abc", GetTestFileName())));
+ }
+
#endregion
}
}
diff --git a/src/System.IO.FileSystem/tests/DirectoryInfo/Exists.cs b/src/System.IO.FileSystem/tests/DirectoryInfo/Exists.cs
index d54f799cf0..be9e7e2641 100644
--- a/src/System.IO.FileSystem/tests/DirectoryInfo/Exists.cs
+++ b/src/System.IO.FileSystem/tests/DirectoryInfo/Exists.cs
@@ -38,6 +38,12 @@ namespace System.IO.Tests
}
[Fact]
+ public void Root()
+ {
+ Assert.True(new DirectoryInfo(Path.GetPathRoot(Directory.GetCurrentDirectory())).Exists);
+ }
+
+ [Fact]
public void DotPath()
{
Assert.True(new DirectoryInfo(Path.Combine(TestDirectory, ".")).Exists);
diff --git a/src/System.IO.FileSystem/tests/DirectoryInfo/GetSetTimes.cs b/src/System.IO.FileSystem/tests/DirectoryInfo/GetSetTimes.cs
index 927c770d61..779be70df6 100644
--- a/src/System.IO.FileSystem/tests/DirectoryInfo/GetSetTimes.cs
+++ b/src/System.IO.FileSystem/tests/DirectoryInfo/GetSetTimes.cs
@@ -12,6 +12,8 @@ namespace System.IO.Tests
public override DirectoryInfo GetMissingItem() => new DirectoryInfo(GetTestFilePath());
+ public override string GetItemPath(DirectoryInfo item) => item.FullName;
+
public override void InvokeCreate(DirectoryInfo item) => item.Create();
public override IEnumerable<TimeFunction> TimeFunctions(bool requiresRoundtripping = false)
diff --git a/src/System.IO.FileSystem/tests/DirectoryInfo/ToString.cs b/src/System.IO.FileSystem/tests/DirectoryInfo/ToString.cs
index 732c71ee62..de2ea39621 100644
--- a/src/System.IO.FileSystem/tests/DirectoryInfo/ToString.cs
+++ b/src/System.IO.FileSystem/tests/DirectoryInfo/ToString.cs
@@ -38,12 +38,28 @@ namespace System.IO.Tests
}
[Fact]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp)]
[PlatformSpecific(TestPlatforms.Windows)] // Drive letter only
- public void DriveOnlyReturnsPeriod_Windows()
+ public void DriveOnlyReturnsPeriod_Windows_Desktop()
{
string path = @"C:";
var info = new DirectoryInfo(path);
Assert.Equal(".", info.ToString());
}
+
+ [Fact]
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.Netcoreapp)]
+ [PlatformSpecific(TestPlatforms.Windows)] // Drive letter only
+ public void DriveOnlyReturnsPeriod_Windows_Core()
+ {
+ // This was likely a limited trust hack that was strangely implemented.
+ // Getting the current directory for a specified drive relative path
+ // doesn't make a lot of sense. There is no reason to hide original paths
+ // when in full trust.
+ string path = @"C:";
+ var info = new DirectoryInfo(path);
+ Assert.Equal("C:", info.ToString());
+ }
+
}
}
diff --git a/src/System.IO.FileSystem/tests/Enumeration/AttributeTests.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/AttributeTests.netcoreapp.cs
new file mode 100644
index 0000000000..2ba62dfb42
--- /dev/null
+++ b/src/System.IO.FileSystem/tests/Enumeration/AttributeTests.netcoreapp.cs
@@ -0,0 +1,141 @@
+// 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.Collections.Generic;
+using System.IO.Enumeration;
+using Xunit;
+
+namespace System.IO.Tests.Enumeration
+{
+ public class AttributeTests : FileSystemTest
+ {
+ private class DefaultFileAttributes : FileSystemEnumerator<string>
+ {
+ public DefaultFileAttributes(string directory, EnumerationOptions options)
+ : base(directory, options)
+ {
+ }
+
+ protected override bool ContinueOnError(int error)
+ {
+ Assert.False(true, $"Should not have errored {error}");
+ return false;
+ }
+
+ protected override bool ShouldIncludeEntry(ref FileSystemEntry entry)
+ => !entry.IsDirectory;
+
+ protected override string TransformEntry(ref FileSystemEntry entry)
+ {
+ string path = entry.ToFullPath();
+ File.Delete(path);
+
+ // Attributes require a stat call on Unix- ensure that we have the right attributes
+ // even if the returned file is deleted.
+ Assert.Equal(FileAttributes.Normal, entry.Attributes);
+ Assert.Equal(path, entry.ToFullPath());
+ return new string(entry.FileName);
+ }
+ }
+
+ [Fact]
+ public void FileAttributesAreExpected()
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, GetTestFileName()));
+
+ fileOne.Create().Dispose();
+
+ if (PlatformDetection.IsWindows)
+ {
+ // Archive should always be set on a new file. Clear it and other expected flags to
+ // see that we get "Normal" as the default when enumerating.
+
+ Assert.True((fileOne.Attributes & FileAttributes.Archive) != 0);
+ fileOne.Attributes &= ~(FileAttributes.Archive | FileAttributes.NotContentIndexed);
+ }
+
+ using (var enumerator = new DefaultFileAttributes(testDirectory.FullName, new EnumerationOptions()))
+ {
+ Assert.True(enumerator.MoveNext());
+ Assert.Equal(fileOne.Name, enumerator.Current);
+ Assert.False(enumerator.MoveNext());
+ }
+ }
+
+ private class DefaultDirectoryAttributes : FileSystemEnumerator<string>
+ {
+ public DefaultDirectoryAttributes(string directory, EnumerationOptions options)
+ : base(directory, options)
+ {
+ }
+
+ protected override bool ShouldIncludeEntry(ref FileSystemEntry entry)
+ => entry.IsDirectory;
+
+ protected override bool ContinueOnError(int error)
+ {
+ Assert.False(true, $"Should not have errored {error}");
+ return false;
+ }
+
+ protected override string TransformEntry(ref FileSystemEntry entry)
+ {
+ string path = entry.ToFullPath();
+ Directory.Delete(path);
+
+ // Attributes require a stat call on Unix- ensure that we have the right attributes
+ // even if the returned directory is deleted.
+ Assert.Equal(FileAttributes.Directory, entry.Attributes);
+ Assert.Equal(path, entry.ToFullPath());
+ return new string(entry.FileName);
+ }
+ }
+
+ [Fact]
+ public void DirectoryAttributesAreExpected()
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ DirectoryInfo subDirectory = Directory.CreateDirectory(Path.Combine(testDirectory.FullName, GetTestFileName()));
+
+ if (PlatformDetection.IsWindows)
+ {
+ // Clear possible extra flags to see that we get Directory
+ subDirectory.Attributes &= ~FileAttributes.NotContentIndexed;
+ }
+
+ using (var enumerator = new DefaultDirectoryAttributes(testDirectory.FullName, new EnumerationOptions()))
+ {
+ Assert.True(enumerator.MoveNext());
+ Assert.Equal(subDirectory.Name, enumerator.Current);
+ Assert.False(enumerator.MoveNext());
+ }
+ }
+
+ [Fact]
+ public void IsHiddenAttribute()
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ 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()));
+
+ fileOne.Create().Dispose();
+ fileTwo.Create().Dispose();
+ if (PlatformDetection.IsWindows)
+ fileTwo.Attributes = fileTwo.Attributes | FileAttributes.Hidden;
+
+ IEnumerable<string> enumerable = new FileSystemEnumerable<string>(
+ testDirectory.FullName,
+ (ref FileSystemEntry entry) => entry.ToFullPath(),
+ new EnumerationOptions() { AttributesToSkip = 0 })
+ {
+ ShouldIncludePredicate = (ref FileSystemEntry entry) => entry.IsHidden
+ };
+
+ Assert.Equal(new string[] { fileTwo.FullName }, enumerable);
+ }
+ }
+}
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/ErrorHandlingTests.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/ErrorHandlingTests.netcoreapp.cs
new file mode 100644
index 0000000000..1414156cf3
--- /dev/null
+++ b/src/System.IO.FileSystem/tests/Enumeration/ErrorHandlingTests.netcoreapp.cs
@@ -0,0 +1,71 @@
+// 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 ErrorHandlingTests : FileSystemTest
+ {
+ private class IgnoreErrors : FileSystemEnumerator<string>
+ {
+ public IgnoreErrors(string directory)
+ : base(directory)
+ { }
+
+ public int ErrorCount { get; private set; }
+ public string DirectoryFinished { get; private set; }
+
+ protected override string TransformEntry(ref FileSystemEntry entry)
+ => entry.FileName.ToString();
+
+ protected override bool ContinueOnError(int error)
+ {
+ ErrorCount++;
+ return true;
+ }
+
+ protected override void OnDirectoryFinished(ReadOnlySpan<char> directory)
+ => DirectoryFinished = directory.ToString();
+ }
+
+ [Fact]
+ public void OpenErrorDoesNotHappenAgainOnMoveNext()
+ {
+ // What we're checking for here is that we don't try to enumerate when we
+ // couldn't even open the root directory (e.g. open the handle again, try
+ // to get data, etc.)
+ using (IgnoreErrors ie = new IgnoreErrors(Path.GetRandomFileName()))
+ {
+ Assert.Equal(1, ie.ErrorCount);
+ Assert.False(ie.MoveNext());
+ Assert.Equal(1, ie.ErrorCount);
+
+ // Since we didn't start, the directory shouldn't finish.
+ Assert.Null(ie.DirectoryFinished);
+ }
+ }
+
+ [Fact]
+ public void DeleteDirectoryAfterOpening()
+ {
+ // We shouldn't prevent the directory from being deleted, even though we've
+ // opened (and are holding) the handle. On Windows this means we've opened
+ // the handle with file share of delete.
+ DirectoryInfo info = Directory.CreateDirectory(GetTestFilePath());
+ using (IgnoreErrors ie = new IgnoreErrors(info.FullName))
+ {
+ Assert.Equal(0, ie.ErrorCount);
+ Directory.Delete(info.FullName);
+ Assert.False(ie.MoveNext());
+
+ // This doesn't cause an error as the directory is still valid until the
+ // the enumerator is closed (as we have an open handle)
+ Assert.Equal(0, ie.ErrorCount);
+ Assert.Equal(info.FullName, ie.DirectoryFinished);
+ }
+ }
+ }
+}
diff --git a/src/System.IO.FileSystem/tests/Enumeration/ExampleTests.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/ExampleTests.netcoreapp.cs
new file mode 100644
index 0000000000..9db4f452d1
--- /dev/null
+++ b/src/System.IO.FileSystem/tests/Enumeration/ExampleTests.netcoreapp.cs
@@ -0,0 +1,141 @@
+// 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.Collections.Generic;
+using System.IO.Enumeration;
+using System.Linq;
+using Xunit;
+
+namespace System.IO.Tests.Enumeration
+{
+ // For tests that cover examples from documentation, blog posts, etc. While these overlap with
+ // existing tests, having explicit coverage here is extra insurance we are covering the
+ // examples we've given out publicly.
+ public class ExampleTests : FileSystemTest
+ {
+ [Fact]
+ public void GetFileNamesEnumerable()
+ {
+ // https://blogs.msdn.microsoft.com/jeremykuhne/2018/03/09/custom-directory-enumeration-in-net-core-2-1/
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ File.Create(Path.Join(testDirectory.FullName, "one")).Dispose();
+ File.Create(Path.Join(testDirectory.FullName, "two")).Dispose();
+ Directory.CreateDirectory(Path.Join(testDirectory.FullName, "three"));
+
+ IEnumerable<string> fileNames =
+ new FileSystemEnumerable<string>(
+ testDirectory.FullName,
+ (ref FileSystemEntry entry) => entry.FileName.ToString())
+ {
+ ShouldIncludePredicate = (ref FileSystemEntry entry) => !entry.IsDirectory
+ };
+
+ FSAssert.EqualWhenOrdered(new string[] { "one", "two" }, fileNames);
+ }
+
+ private static IEnumerable<FileInfo> GetFilesWithExtensions(string directory,
+ bool recursive, params string[] extensions)
+ {
+ return new FileSystemEnumerable<FileInfo>(
+ directory,
+ (ref FileSystemEntry entry) => (FileInfo)entry.ToFileSystemInfo(),
+ new EnumerationOptions() { RecurseSubdirectories = recursive })
+ {
+ ShouldIncludePredicate = (ref FileSystemEntry entry) =>
+ {
+ if (entry.IsDirectory)
+ return false;
+ foreach (string extension in extensions)
+ {
+ if (Path.GetExtension(entry.FileName).SequenceEqual(extension))
+ return true;
+ }
+ return false;
+ }
+ };
+ }
+
+ [Fact]
+ public void TestGetFilesWithExtensions()
+ {
+ // https://blogs.msdn.microsoft.com/jeremykuhne/2018/03/09/custom-directory-enumeration-in-net-core-2-1/
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ File.Create(Path.Join(testDirectory.FullName, "file.one")).Dispose();
+ File.Create(Path.Join(testDirectory.FullName, "file.two")).Dispose();
+ File.Create(Path.Join(testDirectory.FullName, "file.three")).Dispose();
+ DirectoryInfo subDirectory = testDirectory.CreateSubdirectory("three.one");
+ File.Create(Path.Join(subDirectory.FullName, "subfile.one")).Dispose();
+
+ FSAssert.EqualWhenOrdered(
+ new string[] { "file.one", "file.three" },
+ GetFilesWithExtensions(testDirectory.FullName, false, ".one", ".three").Select(f => f.Name));
+
+ FSAssert.EqualWhenOrdered(
+ new string[] { "file.one", "file.three", "subfile.one" },
+ GetFilesWithExtensions(testDirectory.FullName, true, ".one", ".three").Select(f => f.Name));
+ }
+
+ private static int CountFiles(string directory, bool recursive)
+ {
+ return (new FileSystemEnumerable<int>(
+ directory,
+ (ref FileSystemEntry entry) => 1,
+ new EnumerationOptions() { RecurseSubdirectories = recursive })
+ {
+ ShouldIncludePredicate = (ref FileSystemEntry entry) => !entry.IsDirectory
+ }).Count();
+ }
+
+ [Fact]
+ public void TestCountFiles()
+ {
+ // https://blogs.msdn.microsoft.com/jeremykuhne/2018/03/09/custom-directory-enumeration-in-net-core-2-1/
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ File.Create(Path.Join(testDirectory.FullName, "file.one")).Dispose();
+ File.Create(Path.Join(testDirectory.FullName, "file.two")).Dispose();
+ File.Create(Path.Join(testDirectory.FullName, "file.three")).Dispose();
+ DirectoryInfo subDirectory = testDirectory.CreateSubdirectory("three.one");
+ File.Create(Path.Join(subDirectory.FullName, "subfile.one")).Dispose();
+
+ Assert.Equal(3, CountFiles(testDirectory.FullName, false));
+
+ Assert.Equal(4, CountFiles(testDirectory.FullName, true));
+ }
+
+ private static long CountFileBytes(string directory, bool recursive)
+ {
+ return (new FileSystemEnumerable<long>(
+ directory,
+ (ref FileSystemEntry entry) => entry.Length,
+ new EnumerationOptions() { RecurseSubdirectories = recursive })
+ {
+ ShouldIncludePredicate = (ref FileSystemEntry entry) => !entry.IsDirectory
+ }).Sum();
+ }
+
+ [Fact]
+ public void TestCountFileBytes()
+ {
+ // https://blogs.msdn.microsoft.com/jeremykuhne/2018/03/09/custom-directory-enumeration-in-net-core-2-1/
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ FileInfo firstFile = new FileInfo(Path.Join(testDirectory.FullName, "file.one"));
+ using (var writer = firstFile.CreateText())
+ {
+ for (int i = 0; i < 100; i++)
+ writer.WriteLine("The quick brown fox jumped over the lazy dog.");
+ }
+
+ firstFile.CopyTo(Path.Join(testDirectory.FullName, "file.two"));
+ firstFile.CopyTo(Path.Join(testDirectory.FullName, "file.three"));
+ DirectoryInfo subDirectory = testDirectory.CreateSubdirectory("three.one");
+ firstFile.CopyTo(Path.Join(subDirectory.FullName, "subfile.one"));
+
+ firstFile.Refresh();
+ Assert.True(firstFile.Length > 0, "The file we wrote should have a length.");
+ Assert.Equal(firstFile.Length * 3, CountFileBytes(testDirectory.FullName, false));
+
+ Assert.Equal(firstFile.Length * 4, CountFileBytes(testDirectory.FullName, true));
+ }
+ }
+}
diff --git a/src/System.IO.FileSystem/tests/Enumeration/FileSystemNameTests.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/FileSystemNameTests.netcoreapp.cs
new file mode 100644
index 0000000000..a199d2c298
--- /dev/null
+++ b/src/System.IO.FileSystem/tests/Enumeration/FileSystemNameTests.netcoreapp.cs
@@ -0,0 +1,154 @@
+// 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 FileSystemNameTests
+ {
+ [Theory,
+ MemberData(nameof(SimpleMatchData)),
+ MemberData(nameof(EscapedSimpleMatchData)),
+ MemberData(nameof(Win32MatchData)),
+ MemberData(nameof(EscapedWin32MatchData))]
+ public static void Win32Match(string expression, string name, bool ignoreCase, bool expected)
+ {
+ Assert.Equal(expected, FileSystemName.MatchesWin32Expression(expression, name.AsSpan(), ignoreCase));
+ }
+
+ [Theory,
+ MemberData(nameof(SimpleMatchData)),
+ MemberData(nameof(EscapedSimpleMatchData))]
+ public static void SimpleMatch(string expression, string name, bool ignoreCase, bool expected)
+ {
+ Assert.Equal(expected, FileSystemName.MatchesSimpleExpression(expression, name.AsSpan(), ignoreCase));
+ }
+
+ public static TheoryData<string, string, bool, bool> EscapedSimpleMatchData => 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 },
+ };
+
+ public static TheoryData<string, string, bool, bool> EscapedWin32MatchData => new TheoryData<string, string, bool, bool>
+ {
+ { "\\\"", "a", false, false },
+ { "\\\"", "a", true, false },
+ { "\\\"", "\"", false, true },
+ { "\\\"", "\"", true, true },
+ };
+
+ public static TheoryData<string, string, bool, bool> SimpleMatchData => 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 },
+ };
+
+ public static TheoryData<string, string, bool, bool> Win32MatchData => new TheoryData<string, string, bool, bool>
+ {
+ { "<\"*", @"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 },
+ };
+
+ [Theory,
+ InlineData("", "*"),
+ InlineData("*.*", "*"),
+ InlineData("*", "*"),
+ InlineData(".", "."),
+ InlineData("?", ">"),
+ InlineData("*.", "<"),
+ InlineData("?.?", ">\">"),
+ InlineData("foo*.", "foo<")]
+ public void TranslateExpression(string expression, string expected)
+ {
+ Assert.Equal(expected, FileSystemName.TranslateWin32Expression(expression));
+ }
+
+ [Fact]
+ public void TranslateVeryLongExpression()
+ {
+ string longString = new string('a', 10_000_000);
+ Assert.Equal(longString, FileSystemName.TranslateWin32Expression(longString));
+ }
+ }
+}
diff --git a/src/System.IO.FileSystem/tests/Enumeration/IncludePredicateTests.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/IncludePredicateTests.netcoreapp.cs
new file mode 100644
index 0000000000..0a6737e064
--- /dev/null
+++ b/src/System.IO.FileSystem/tests/Enumeration/IncludePredicateTests.netcoreapp.cs
@@ -0,0 +1,55 @@
+// 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.Collections.Generic;
+using System.IO.Enumeration;
+using System.Linq;
+using Xunit;
+
+namespace System.IO.Tests.Enumeration
+{
+ public abstract class IncludePredicateTests : FileSystemTest
+ {
+ public static IEnumerable<string> GetFileFullPathsWithExtension(string directory,
+ bool recursive, params string[] extensions)
+ {
+ return new FileSystemEnumerable<string>(
+ directory,
+ (ref FileSystemEntry entry) => entry.ToFullPath(),
+ new EnumerationOptions() { RecurseSubdirectories = recursive })
+ {
+ ShouldIncludePredicate = (ref FileSystemEntry entry) =>
+ {
+ if (entry.IsDirectory) return false;
+ foreach (string extension in extensions)
+ {
+ if (Path.GetExtension(entry.FileName).EndsWith(extension))
+ return true;
+ }
+ return false;
+ }
+ };
+ }
+
+ [Fact]
+ public void CustomExtensionMatch()
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ DirectoryInfo testSubdirectory = Directory.CreateDirectory(Path.Combine(testDirectory.FullName, "Subdirectory"));
+ FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, "fileone.htm"));
+ FileInfo fileTwo = new FileInfo(Path.Combine(testDirectory.FullName, "filetwo.html"));
+ FileInfo fileThree = new FileInfo(Path.Combine(testSubdirectory.FullName, "filethree.doc"));
+ FileInfo fileFour = new FileInfo(Path.Combine(testSubdirectory.FullName, "filefour.docx"));
+
+ fileOne.Create().Dispose();
+ fileTwo.Create().Dispose();
+ fileThree.Create().Dispose();
+ fileFour.Create().Dispose();
+
+ string[] paths = GetFileFullPathsWithExtension(testDirectory.FullName, true, ".htm", ".doc").ToArray();
+
+ FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileThree.FullName }, paths);
+ }
+ }
+}
diff --git a/src/System.IO.FileSystem/tests/Enumeration/MatchCasingTests.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/MatchCasingTests.netcoreapp.cs
new file mode 100644
index 0000000000..00a74055ab
--- /dev/null
+++ b/src/System.IO.FileSystem/tests/Enumeration/MatchCasingTests.netcoreapp.cs
@@ -0,0 +1,60 @@
+// 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 abstract class MatchCasingTests : FileSystemTest
+ {
+ protected abstract string[] GetPaths(string directory, string pattern, EnumerationOptions options);
+
+ [Fact]
+ public void MatchCase()
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ DirectoryInfo testSubdirectory = Directory.CreateDirectory(Path.Combine(testDirectory.FullName, "Subdirectory"));
+ FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, "FileOne"));
+ FileInfo fileTwo = new FileInfo(Path.Combine(testDirectory.FullName, "FileTwo"));
+ FileInfo fileThree = new FileInfo(Path.Combine(testSubdirectory.FullName, "FileThree"));
+ FileInfo fileFour = new FileInfo(Path.Combine(testSubdirectory.FullName, "FileFour"));
+
+ fileOne.Create().Dispose();
+ fileTwo.Create().Dispose();
+ fileThree.Create().Dispose();
+ fileFour.Create().Dispose();
+
+ string[] paths = GetPaths(testDirectory.FullName, "file*", new EnumerationOptions { MatchCasing = MatchCasing.CaseSensitive, RecurseSubdirectories = true });
+
+ Assert.Empty(paths);
+
+ paths = GetPaths(testDirectory.FullName, "file*", new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive, RecurseSubdirectories = true });
+ FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName, fileThree.FullName, fileFour.FullName }, paths);
+
+ paths = GetPaths(testDirectory.FullName, "FileT*", new EnumerationOptions { MatchCasing = MatchCasing.CaseSensitive, RecurseSubdirectories = true });
+ FSAssert.EqualWhenOrdered(new string[] { fileTwo.FullName, fileThree.FullName }, paths);
+
+ paths = GetPaths(testDirectory.FullName, "File???", new EnumerationOptions { MatchCasing = MatchCasing.CaseSensitive, RecurseSubdirectories = true });
+ FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName }, paths);
+ }
+ }
+
+ public class MatchCasingTests_Directory_GetFiles : MatchCasingTests
+ {
+ protected override string[] GetPaths(string directory, string pattern, EnumerationOptions options)
+ {
+ return Directory.GetFiles(directory, pattern, options);
+ }
+ }
+
+ public class MatchCasingTests_DirectoryInfo_GetFiles : MatchCasingTests
+ {
+ protected override string[] GetPaths(string directory, string pattern, EnumerationOptions options)
+ {
+ return new DirectoryInfo(directory).GetFiles(pattern, options).Select(i => i.FullName).ToArray();
+ }
+ }
+}
diff --git a/src/System.IO.FileSystem/tests/Enumeration/MatchTypesTests.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/MatchTypesTests.netcoreapp.cs
new file mode 100644
index 0000000000..49b8e1b9e0
--- /dev/null
+++ b/src/System.IO.FileSystem/tests/Enumeration/MatchTypesTests.netcoreapp.cs
@@ -0,0 +1,80 @@
+// 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.Linq;
+using Xunit;
+
+namespace System.IO.Tests.Enumeration
+{
+ public abstract class MatchTypesTests : FileSystemTest
+ {
+ protected abstract string[] GetPaths(string directory, string pattern, EnumerationOptions options);
+
+ [Fact]
+ public void QuestionMarkBehavior()
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, "a.one"));
+ FileInfo fileTwo = new FileInfo(Path.Combine(testDirectory.FullName, "ab.two"));
+ FileInfo fileThree = new FileInfo(Path.Combine(testDirectory.FullName, "abc.three"));
+
+ fileOne.Create().Dispose();
+ fileTwo.Create().Dispose();
+ fileThree.Create().Dispose();
+
+ // Question marks collapse to periods in Win32
+ string[] paths = GetPaths(testDirectory.FullName, "a??.*", new EnumerationOptions { MatchType = MatchType.Win32 });
+ FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName, fileThree.FullName }, paths);
+
+ paths = GetPaths(testDirectory.FullName, "*.?????", new EnumerationOptions { MatchType = MatchType.Win32 });
+ FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName, fileThree.FullName }, paths);
+
+ // Simple, one question mark is one character
+ paths = GetPaths(testDirectory.FullName, "a??.*", new EnumerationOptions { MatchType = MatchType.Simple });
+ FSAssert.EqualWhenOrdered(new string[] { fileThree.FullName }, paths);
+
+ paths = GetPaths(testDirectory.FullName, "*.?????", new EnumerationOptions { MatchType = MatchType.Simple });
+ FSAssert.EqualWhenOrdered(new string[] { fileThree.FullName }, paths);
+ }
+
+ [Fact]
+ public void StarDotBehavior()
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, "one"));
+ FileInfo fileTwo = new FileInfo(Path.Combine(testDirectory.FullName, "one.two"));
+ string fileThree = Path.Combine(testDirectory.FullName, "three.");
+
+ fileOne.Create().Dispose();
+ fileTwo.Create().Dispose();
+
+ // Need extended device syntax to create a name with a trailing dot.
+ File.Create(PlatformDetection.IsWindows ? @"\\?\" + fileThree : fileThree).Dispose();
+
+ // *. means any file without an extension
+ string[] paths = GetPaths(testDirectory.FullName, "*.", new EnumerationOptions { MatchType = MatchType.Win32 });
+ FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileThree }, paths);
+
+ // Simple, anything with a trailing period
+ paths = GetPaths(testDirectory.FullName, "*.", new EnumerationOptions { MatchType = MatchType.Simple });
+ FSAssert.EqualWhenOrdered(new string[] { fileThree }, paths);
+ }
+ }
+
+ public class MatchTypesTests_Directory_GetFiles : MatchTypesTests
+ {
+ protected override string[] GetPaths(string directory, string pattern, EnumerationOptions options)
+ {
+ return Directory.GetFiles(directory, pattern, options);
+ }
+ }
+
+ public class MatchTypesTests_DirectoryInfo_GetFiles : MatchTypesTests
+ {
+ protected override string[] GetPaths(string directory, string pattern, EnumerationOptions options)
+ {
+ return new DirectoryInfo(directory).GetFiles(pattern, options).Select(i => i.FullName).ToArray();
+ }
+ }
+}
diff --git a/src/System.IO.FileSystem/tests/Enumeration/PatternTransformTests.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/PatternTransformTests.netcoreapp.cs
new file mode 100644
index 0000000000..9cb33e5853
--- /dev/null
+++ b/src/System.IO.FileSystem/tests/Enumeration/PatternTransformTests.netcoreapp.cs
@@ -0,0 +1,126 @@
+// 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.Linq;
+using Xunit;
+
+namespace System.IO.Tests.Enumeration
+{
+ public class PatternTransformTests_Directory : FileSystemTest
+ {
+ protected virtual string[] GetFiles(string directory, string pattern)
+ {
+ return Directory.GetFiles(directory, pattern);
+ }
+
+ protected virtual string[] GetFiles(string directory, string pattern, EnumerationOptions options)
+ {
+ return Directory.GetFiles(directory, pattern, options);
+ }
+
+ [Theory,
+ InlineData("."),
+ InlineData("*.*")]
+ public void GetFiles_WildcardPatternIsTranslated(string pattern)
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, "File.One"));
+ FileInfo fileTwo = new FileInfo(Path.Combine(testDirectory.FullName, "FileTwo"));
+ fileOne.Create().Dispose();
+ fileTwo.Create().Dispose();
+ string[] results = GetFiles(testDirectory.FullName, pattern);
+ FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName }, results);
+
+ results = GetFiles(testDirectory.FullName, pattern, new EnumerationOptions { MatchType = MatchType.Win32 });
+ FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName }, results);
+ }
+
+ [Fact]
+ public void GetFiles_WildcardPatternIsNotTranslated()
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, "File.One"));
+ FileInfo fileTwo = new FileInfo(Path.Combine(testDirectory.FullName, "FileTwo"));
+ fileOne.Create().Dispose();
+ fileTwo.Create().Dispose();
+ string[] results = GetFiles(testDirectory.FullName, ".", new EnumerationOptions());
+ Assert.Empty(results);
+
+ results = GetFiles(testDirectory.FullName, "*.*", new EnumerationOptions());
+ Assert.Equal(new string[] { fileOne.FullName }, results);
+ }
+
+ [Fact]
+ public void GetFiles_EmptyPattern()
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, "File.One"));
+ FileInfo fileTwo = new FileInfo(Path.Combine(testDirectory.FullName, "FileTwo"));
+ fileOne.Create().Dispose();
+ fileTwo.Create().Dispose();
+
+ // We allow for expression to be "foo\" which would translate to "foo\*".
+ string[] results = GetFiles(testDirectory.Parent.FullName, testDirectory.Name + Path.DirectorySeparatorChar);
+ FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName }, results);
+
+ results = GetFiles(testDirectory.Parent.FullName, testDirectory.Name + Path.AltDirectorySeparatorChar);
+ FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName }, results);
+
+ results = GetFiles(testDirectory.FullName, string.Empty);
+ FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName }, results);
+ }
+
+ [Fact]
+ [PlatformSpecific(TestPlatforms.AnyUnix)]
+ public void GetFiles_EmptyPattern_Unix()
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, "File\\One"));
+ FileInfo fileTwo = new FileInfo(Path.Combine(testDirectory.FullName, "FileTwo"));
+ fileOne.Create().Dispose();
+ fileTwo.Create().Dispose();
+
+ // We allow for expression to be "foo\" which would translate to "foo\*". On Unix we should not be
+ // considering the backslash as a directory separator.
+ string[] results = GetFiles(testDirectory.FullName, "File\\One");
+ Assert.Equal(new string[] { fileOne.FullName }, results);
+ }
+
+ [Fact]
+ [PlatformSpecific(TestPlatforms.AnyUnix)]
+ public void GetFiles_ExtendedDosWildcards_Unix()
+ {
+ // The extended wildcards ('"', '<', and '>') should not be considered on Unix, even when doing DOS style matching.
+ // Getting these behaviors requires using the FileSystemEnumerable/Enumerator directly.
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, "File\"One"));
+ FileInfo fileTwo = new FileInfo(Path.Combine(testDirectory.FullName, "File<Two"));
+ FileInfo fileThree = new FileInfo(Path.Combine(testDirectory.FullName, "File>Three"));
+ fileOne.Create().Dispose();
+ fileTwo.Create().Dispose();
+ fileThree.Create().Dispose();
+
+ string[] results = GetFiles(testDirectory.FullName, "*\"*");
+ Assert.Equal(new string[] { fileOne.FullName }, results);
+ results = GetFiles(testDirectory.FullName, "*<*");
+ Assert.Equal(new string[] { fileTwo.FullName }, results);
+ results = GetFiles(testDirectory.FullName, "*>*");
+ Assert.Equal(new string[] { fileThree.FullName }, results);
+ }
+ }
+
+ public class PatternTransformTests_DirectoryInfo : PatternTransformTests_Directory
+ {
+
+ protected override string[] GetFiles(string directory, string pattern)
+ {
+ return new DirectoryInfo(directory).GetFiles(pattern).Select(i => i.FullName).ToArray();
+ }
+
+ protected override string[] GetFiles(string directory, string pattern, EnumerationOptions options)
+ {
+ return new DirectoryInfo(directory).GetFiles(pattern, options).Select(i => i.FullName).ToArray();
+ }
+ }
+}
diff --git a/src/System.IO.FileSystem/tests/Enumeration/RootTests.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/RootTests.netcoreapp.cs
new file mode 100644
index 0000000000..91b1376ede
--- /dev/null
+++ b/src/System.IO.FileSystem/tests/Enumeration/RootTests.netcoreapp.cs
@@ -0,0 +1,59 @@
+// 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 RootTests
+ {
+ private class DirectoryRecursed : FileSystemEnumerator<string>
+ {
+ public string LastDirectory { get; private set; }
+
+ public DirectoryRecursed(string directory, EnumerationOptions options)
+ : base(directory, options)
+ {
+ }
+
+ protected override bool ShouldIncludeEntry(ref FileSystemEntry entry)
+ => !entry.IsDirectory;
+
+ protected override string TransformEntry(ref FileSystemEntry entry)
+ => entry.ToFullPath();
+
+ protected override bool ShouldRecurseIntoEntry(ref FileSystemEntry entry)
+ {
+ LastDirectory = new string(entry.Directory);
+ return false;
+ }
+ }
+
+ [Fact]
+ public void CanRecurseFromRoot()
+ {
+ string root = Path.GetPathRoot(Path.GetTempPath());
+
+ using (var recursed = new DirectoryRecursed(root, new EnumerationOptions { AttributesToSkip = FileAttributes.System, RecurseSubdirectories = true }))
+ {
+ while (recursed.MoveNext())
+ {
+ if (recursed.LastDirectory != null)
+ {
+ Assert.Equal(root, recursed.LastDirectory);
+ return;
+ }
+
+ // Should start with the root and shouldn't have a separator after the root
+ Assert.StartsWith(root, recursed.Current);
+ Assert.True(recursed.Current.LastIndexOf(Path.DirectorySeparatorChar) < root.Length,
+ $"should have no separators pasth the root '{root}' in in '{recursed.Current}'");
+ }
+
+ Assert.NotNull(recursed.LastDirectory);
+ }
+ }
+ }
+}
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..39e4d84324
--- /dev/null
+++ b/src/System.IO.FileSystem/tests/Enumeration/SkipAttributeTests.netcoreapp.cs
@@ -0,0 +1,107 @@
+// 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;
+
+ // Default EnumerationOptions is to skip hidden
+ string[] paths = GetPaths(testDirectory.FullName, new EnumerationOptions());
+ Assert.Equal(new string[] { fileOne.FullName }, paths);
+
+ paths = GetPaths(testDirectory.FullName, new EnumerationOptions { AttributesToSkip = 0 });
+ FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName }, paths);
+
+ paths = GetPaths(testDirectory.FullName, new EnumerationOptions { 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 { RecurseSubdirectories = true });
+ Assert.Equal(new string[] { fileOne.FullName }, paths);
+ }
+
+ [Fact]
+ public void SkipComesFirst()
+ {
+ // If skip comes first we shouldn't find ourselves recursing.
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ DirectoryInfo testSubdirectory = Directory.CreateDirectory(Path.Combine(testDirectory.FullName, GetTestFileName()));
+
+ FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, GetTestFileName()));
+ 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();
+ fileThree.Create().Dispose();
+ fileFour.Create().Dispose();
+
+ string[] paths = GetPaths(testDirectory.FullName, new EnumerationOptions { AttributesToSkip = FileAttributes.Directory, RecurseSubdirectories = true });
+ FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName }, paths);
+ }
+ }
+
+ public class SkipAttributeTests_Directory_GetFiles : SkipAttributeTests
+ {
+ protected override string[] GetPaths(string directory, EnumerationOptions options)
+ {
+ return Directory.GetFiles(directory, "*", options);
+ }
+ }
+
+ 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..5043190cff
--- /dev/null
+++ b/src/System.IO.FileSystem/tests/Enumeration/SpecialDirectoryTests.netcoreapp.cs
@@ -0,0 +1,76 @@
+// 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.Collections.Generic;
+using System.IO.Enumeration;
+using System.Linq;
+using Xunit;
+
+namespace System.IO.Tests.Enumeration
+{
+
+ public class SpecialDirectoryTests : FileSystemTest
+ {
+ private class DirectoryRecursed : FileSystemEnumerator<string>
+ {
+ public int ShouldRecurseCalls { get; private set; }
+
+ public DirectoryRecursed(string directory, EnumerationOptions options)
+ : base(directory, options)
+ {
+ }
+
+ protected override string TransformEntry(ref FileSystemEntry entry)
+ =>new string(entry.FileName);
+
+ protected override bool ShouldRecurseIntoEntry(ref FileSystemEntry entry)
+ {
+ ShouldRecurseCalls++;
+ return base.ShouldRecurseIntoEntry(ref entry);
+ }
+ }
+
+ [Fact]
+ public void SpecialDirectoriesAreNotUpForRecursion()
+ {
+ using (var recursed = new DirectoryRecursed(TestDirectory, new EnumerationOptions { ReturnSpecialDirectories = true, RecurseSubdirectories = true, AttributesToSkip = 0 }))
+ {
+ List<string> results = new List<string>();
+ while (recursed.MoveNext())
+ results.Add(recursed.Current);
+
+ Assert.Equal(0, recursed.ShouldRecurseCalls);
+ Assert.Contains("..", results);
+ }
+ }
+ }
+
+ public class SpecialDirectoryTests_Enumerable : 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()
+ {
+ // Files that begin with periods are considered hidden on Unix
+ string[] paths = GetNames(TestDirectory, new EnumerationOptions { ReturnSpecialDirectories = true, AttributesToSkip = 0 });
+ Assert.Contains(".", paths);
+ Assert.Contains("..", paths);
+ }
+ }
+
+ public class SpecialDirectoryTests_DirectoryInfo_GetDirectories : SpecialDirectoryTests_Enumerable
+ {
+ 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/Enumeration/TrimmedPaths.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/TrimmedPaths.netcoreapp.cs
new file mode 100644
index 0000000000..ed73117634
--- /dev/null
+++ b/src/System.IO.FileSystem/tests/Enumeration/TrimmedPaths.netcoreapp.cs
@@ -0,0 +1,284 @@
+// 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.Linq;
+using Xunit;
+
+namespace System.IO.Tests.Enumeration
+{
+ public class TrimmedPaths : FileSystemTest
+ {
+ [Fact]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void TrimmedPathsAreFound_Windows()
+ {
+ // Trailing spaces and periods are eaten when normalizing in Windows, making them impossible
+ // to access without using the \\?\ device syntax. We should, however, be able to find them
+ // and retain the filename in the info classes and string results.
+
+ DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath());
+ File.Create(@"\\?\" + Path.Combine(directory.FullName, "Trailing space ")).Dispose();
+ File.Create(@"\\?\" + Path.Combine(directory.FullName, "Trailing period.")).Dispose();
+
+ FileInfo[] files = directory.GetFiles();
+ Assert.Equal(2, files.Count());
+ FSAssert.EqualWhenOrdered(new string[] { "Trailing space ", "Trailing period." }, files.Select(f => f.Name));
+
+ var paths = Directory.GetFiles(directory.FullName);
+ Assert.Equal(2, paths.Count());
+ FSAssert.EqualWhenOrdered(new string[] { "Trailing space ", "Trailing period." }, paths.Select(p => Path.GetFileName(p)));
+ }
+
+ [Fact]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void TrimmedPathsDeletion_Windows()
+ {
+ // Trailing spaces and periods are eaten when normalizing in Windows, making them impossible
+ // to access without using the \\?\ device syntax. We should, however, be able to delete them
+ // from the info class.
+
+ DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath());
+ File.Create(@"\\?\" + Path.Combine(directory.FullName, "Trailing space ")).Dispose();
+ File.Create(@"\\?\" + Path.Combine(directory.FullName, "Trailing period.")).Dispose();
+
+ // With just a path name, the trailing space/period will get eaten, so we
+ // can't delete without prepending- they won't "exist".
+ var paths = Directory.GetFiles(directory.FullName);
+ Assert.All(paths, p => Assert.False(File.Exists(p)));
+
+ FileInfo[] files = directory.GetFiles();
+ Assert.Equal(2, files.Count());
+ Assert.All(files, f => Assert.True(f.Exists));
+ foreach (FileInfo f in files)
+ f.Refresh();
+ Assert.All(files, f => Assert.True(f.Exists));
+ foreach (FileInfo f in files)
+ {
+ f.Delete();
+ f.Refresh();
+ }
+ Assert.All(files, f => Assert.False(f.Exists));
+
+ foreach (FileInfo f in files)
+ {
+ f.Create().Dispose();
+ f.Refresh();
+ }
+ Assert.All(files, f => Assert.True(f.Exists));
+ }
+
+ [Fact]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void TrimmedPathsOpen_Windows()
+ {
+ // Trailing spaces and periods are eaten when normalizing in Windows, making them impossible
+ // to access without using the \\?\ device syntax. We should, however, be able to open them
+ // from the info class when enumerating.
+
+ DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath());
+ string fileOne = Path.Join(directory.FullName, "Trailing space ");
+ string fileTwo = Path.Join(directory.FullName, "Trailing period.");
+ File.Create(@"\\?\" + fileOne).Dispose();
+ File.Create(@"\\?\" + fileTwo).Dispose();
+
+ FileInfo[] files = directory.GetFiles();
+ Assert.Equal(2, files.Length);
+ foreach (FileInfo fi in directory.GetFiles())
+ {
+ // Shouldn't throw hitting any of the Open overloads
+ using (FileStream stream = fi.Open(FileMode.Open))
+ { }
+ using (FileStream stream = fi.Open(FileMode.Open, FileAccess.Read))
+ { }
+ using (FileStream stream = fi.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+ { }
+ using (FileStream stream = fi.OpenRead())
+ { }
+ using (FileStream stream = fi.OpenWrite())
+ { }
+ }
+ }
+
+ [Fact]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void TrimmedPathsText_Windows()
+ {
+ // Trailing spaces and periods are eaten when normalizing in Windows, making them impossible
+ // to access without using the \\?\ device syntax. We should, however, be able to open readers
+ // and writers from the the info class when enumerating.
+
+ DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath());
+ string fileOne = Path.Join(directory.FullName, "Trailing space ");
+ string fileTwo = Path.Join(directory.FullName, "Trailing period.");
+ File.WriteAllText(@"\\?\" + fileOne, "space");
+ File.WriteAllText(@"\\?\" + fileTwo, "period");
+
+ FileInfo[] files = directory.GetFiles();
+ Assert.Equal(2, files.Length);
+ foreach (FileInfo fi in directory.GetFiles())
+ {
+ using (StreamReader reader = fi.OpenText())
+ {
+ string content = reader.ReadToEnd();
+ if (fi.FullName.EndsWith(fileOne))
+ {
+ Assert.Equal("space", content);
+ }
+ else if (fi.FullName.EndsWith(fileTwo))
+ {
+ Assert.Equal("period", content);
+ }
+ else
+ {
+ Assert.False(true, $"Unexpected name '{fi.FullName}'");
+ }
+ }
+
+ using (StreamWriter writer = fi.CreateText())
+ {
+ writer.Write("foo");
+ }
+
+ using (StreamReader reader = fi.OpenText())
+ {
+ Assert.Equal("foo", reader.ReadToEnd());
+ }
+
+ using (StreamWriter writer = fi.AppendText())
+ {
+ writer.Write("bar");
+ }
+
+ using (StreamReader reader = fi.OpenText())
+ {
+ Assert.Equal("foobar", reader.ReadToEnd());
+ }
+ }
+ }
+
+ [Fact]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void TrimmedPathsCopyTo_Windows()
+ {
+ // Trailing spaces and periods are eaten when normalizing in Windows, making them impossible
+ // to access without using the \\?\ device syntax. We should, however, be able to copy them
+ // without the special syntax from the info class when enumerating.
+
+ DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath());
+ string fileOne = Path.Join(directory.FullName, "Trailing space ");
+ string fileTwo = Path.Join(directory.FullName, "Trailing period.");
+ File.Create(@"\\?\" + fileOne).Dispose();
+ File.Create(@"\\?\" + fileTwo).Dispose();
+
+ FileInfo[] files = directory.GetFiles();
+ Assert.Equal(2, files.Length);
+ foreach (FileInfo fi in directory.GetFiles())
+ {
+ FileInfo newInfo = fi.CopyTo(Path.Join(directory.FullName, GetTestFileName()));
+ Assert.True(newInfo.Exists);
+ FileInfo newerInfo = fi.CopyTo(Path.Join(directory.FullName, GetTestFileName()), overwrite: true);
+ Assert.True(newerInfo.Exists);
+ }
+
+ Assert.Equal(6, directory.GetFiles().Length);
+ }
+
+ [Fact]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void TrimmedPathsReplace_Windows()
+ {
+ // Trailing spaces and periods are eaten when normalizing in Windows, making them impossible
+ // to access without using the \\?\ device syntax. We should, however, be able to replace them
+ // from the info class when enumerating.
+
+ DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath());
+ string fileOne = Path.Join(directory.FullName, "Trailing space ");
+ string fileTwo = Path.Join(directory.FullName, "Trailing period.");
+ File.WriteAllText(@"\\?\" + fileOne, "space");
+ File.WriteAllText(@"\\?\" + fileTwo, "period");
+
+ FileInfo[] files = directory.GetFiles();
+ Assert.Equal(2, files.Length);
+
+ FileInfo destination = new FileInfo(Path.Join(directory.FullName, GetTestFileName()));
+ destination.Create().Dispose();
+
+ foreach (FileInfo fi in files)
+ {
+ fi.Replace(destination.FullName, null);
+ using (StreamReader reader = destination.OpenText())
+ {
+ string content = reader.ReadToEnd();
+ if (fi.FullName.EndsWith(fileOne))
+ {
+ Assert.Equal("space", content);
+ }
+ else if (fi.FullName.EndsWith(fileTwo))
+ {
+ Assert.Equal("period", content);
+ }
+ else
+ {
+ Assert.False(true, $"Unexpected name '{fi.FullName}'");
+ }
+ }
+ }
+ }
+
+ [Fact]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void TrimmedPathsMoveTo_Windows()
+ {
+ // Trailing spaces and periods are eaten when normalizing in Windows, making them impossible
+ // to access without using the \\?\ device syntax. We should, however, be able to move them
+ // without the special syntax from the info class when enumerating.
+
+ DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath());
+ DirectoryInfo spaceDirectory = Directory.CreateDirectory(Path.Join(@"\\?\", directory.FullName, "Trailing space "));
+ DirectoryInfo periodDirectory = Directory.CreateDirectory(Path.Join(@"\\?\", directory.FullName, "Trailing period."));
+ string spaceFile = Path.Join(spaceDirectory.FullName, "space");
+ string periodFile = Path.Join(periodDirectory.FullName, "period");
+ File.Create(spaceFile).Dispose();
+ File.Create(periodFile).Dispose();
+
+ DirectoryInfo[] directories = directory.GetDirectories();
+ Assert.Equal(2, directories.Length);
+ foreach (DirectoryInfo di in directories)
+ {
+ if (di.Name == "Trailing space ")
+ {
+ di.MoveTo(Path.Join(directory.FullName, "WasSpace"));
+ }
+ else if (di.Name == "Trailing period.")
+ {
+ di.MoveTo(Path.Join(directory.FullName, "WasPeriod"));
+ }
+ else
+ {
+ Assert.False(true, $"Found unexpected name '{di.Name}'");
+ }
+ }
+
+ directories = directory.GetDirectories();
+ Assert.Equal(2, directories.Length);
+ foreach (DirectoryInfo di in directories)
+ {
+ if (di.Name == "WasSpace")
+ {
+ FileInfo fi = di.GetFiles().Single();
+ Assert.Equal("space", fi.Name);
+ }
+ else if (di.Name == "WasPeriod")
+ {
+ FileInfo fi = di.GetFiles().Single();
+ Assert.Equal("period", fi.Name);
+ }
+ else
+ {
+ Assert.False(true, $"Found unexpected name '{di.Name}'");
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.IO.FileSystem/tests/FSAssert.cs b/src/System.IO.FileSystem/tests/FSAssert.cs
index 9fbefaa874..10125d8537 100644
--- a/src/System.IO.FileSystem/tests/FSAssert.cs
+++ b/src/System.IO.FileSystem/tests/FSAssert.cs
@@ -2,6 +2,8 @@
// 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.Collections.Generic;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
@@ -62,5 +64,10 @@ namespace System.IO.Tests
Assert.NotNull(tce);
Assert.Equal(ct, tce.CancellationToken);
}
+
+ public static void EqualWhenOrdered<T>(IEnumerable<T> expected, IEnumerable<T> actual)
+ {
+ Assert.Equal(expected.OrderBy(e => e).Select(o => o), actual.OrderBy(a => a).Select(o => o));
+ }
}
}
diff --git a/src/System.IO.FileSystem/tests/File/Copy.cs b/src/System.IO.FileSystem/tests/File/Copy.cs
index 036dedae6a..1caca2aae1 100644
--- a/src/System.IO.FileSystem/tests/File/Copy.cs
+++ b/src/System.IO.FileSystem/tests/File/Copy.cs
@@ -4,23 +4,17 @@
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.InteropServices;
using Xunit;
namespace System.IO.Tests
{
public partial class File_Copy_str_str : FileSystemTest
{
- #region Utilities
-
- public static TheoryData WindowsInvalidUnixValid = new TheoryData<string> { " ", " ", "\n", ">", "<", "\t" };
public virtual void Copy(string source, string dest)
{
File.Copy(source, dest);
}
- #endregion
-
#region UniversalTests
[Fact]
@@ -160,10 +154,11 @@ namespace System.IO.Tests
#region PlatformSpecific
- [Theory,
- MemberData(nameof(WindowsInvalidUnixValid))]
- [PlatformSpecific(TestPlatforms.Windows)] // Whitespace path throws ArgumentException
- public void WindowsWhitespacePath(string invalid)
+ [Theory,
+ InlineData(" "),
+ InlineData(" ")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void WindowsAllSpacePath(string invalid)
{
string testFile = GetTestFilePath();
File.Create(testFile).Dispose();
@@ -173,24 +168,92 @@ namespace System.IO.Tests
}
[Theory,
- MemberData(nameof(WindowsInvalidUnixValid))]
- [PlatformSpecific(TestPlatforms.AnyUnix)] // Whitespace path allowed
- public void UnixWhitespacePath(string valid)
+ InlineData("\n"),
+ InlineData(">"),
+ InlineData("<"),
+ InlineData("\t")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void WindowsInvalidCharsPath_Desktop(string invalid)
{
string testFile = GetTestFilePath();
File.Create(testFile).Dispose();
+ Assert.Throws<ArgumentException>(() => Copy(testFile, invalid));
+ Assert.Throws<ArgumentException>(() => Copy(invalid, testFile));
+ }
+
+ [Theory,
+ InlineData("\n"),
+ InlineData(">"),
+ InlineData("<"),
+ InlineData("\t")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsInvalidCharsPath_Core(string invalid)
+ {
+ string testFile = GetTestFilePath();
+ File.Create(testFile).Dispose();
+
+ Assert.Throws<IOException>(() => Copy(testFile, invalid));
+ Assert.Throws<IOException>(() => Copy(invalid, testFile));
+ }
+
+ [Theory,
+ InlineData(" "),
+ InlineData(" "),
+ InlineData("\n"),
+ InlineData(">"),
+ InlineData("<"),
+ InlineData("\t")]
+ [PlatformSpecific(TestPlatforms.AnyUnix)]
+ public void UnixInvalidWindowsPaths(string valid)
+ {
+ // Unix allows whitespaces paths that aren't valid on Windows
+ string testFile = GetTestFilePath();
+ File.Create(testFile).Dispose();
+
Copy(testFile, Path.Combine(TestDirectory, valid));
Assert.True(File.Exists(testFile));
Assert.True(File.Exists(Path.Combine(TestDirectory, valid)));
}
+
+ [Theory,
+ InlineData("", ":bar"),
+ InlineData("", ":bar:$DATA"),
+ InlineData("::$DATA", ":bar"),
+ InlineData("::$DATA", ":bar:$DATA")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsAlternateDataStream(string defaultStream, string alternateStream)
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ string testFile = Path.Combine(testDirectory.FullName, GetTestFileName());
+ string testFileDefaultStream = testFile + defaultStream;
+ string testFileAlternateStream = testFile + alternateStream;
+
+ // Copy the default stream into an alternate stream
+ File.WriteAllText(testFileDefaultStream, "Foo");
+ Copy(testFileDefaultStream, testFileAlternateStream);
+ Assert.Equal(testFile, testDirectory.GetFiles().Single().FullName);
+ Assert.Equal("Foo", File.ReadAllText(testFileDefaultStream));
+ Assert.Equal("Foo", File.ReadAllText(testFileAlternateStream));
+
+ // Copy another file over the alternate stream
+ string testFile2 = Path.Combine(testDirectory.FullName, GetTestFileName());
+ string testFile2DefaultStream = testFile2 + defaultStream;
+ File.WriteAllText(testFile2DefaultStream, "Bar");
+ Assert.Throws<IOException>(() => Copy(testFile2DefaultStream, testFileAlternateStream));
+
+ // This always throws as you can't copy an alternate stream out (oddly)
+ Assert.Throws<IOException>(() => Copy(testFileAlternateStream, testFile2));
+ Assert.Throws<IOException>(() => Copy(testFileAlternateStream, testFile2 + alternateStream));
+ }
#endregion
}
public class File_Copy_str_str_b : File_Copy_str_str
{
- #region Utilities
-
public override void Copy(string source, string dest)
{
File.Copy(source, dest, false);
@@ -201,10 +264,6 @@ namespace System.IO.Tests
File.Copy(source, dest, overwrite);
}
- #endregion
-
- #region UniversalTests
-
[Fact]
public void OverwriteTrue()
{
@@ -257,6 +316,38 @@ namespace System.IO.Tests
}
}
- #endregion
+ [Theory,
+ InlineData("", ":bar"),
+ InlineData("", ":bar:$DATA"),
+ InlineData("::$DATA", ":bar"),
+ InlineData("::$DATA", ":bar:$DATA")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsAlternateDataStreamOverwrite(string defaultStream, string alternateStream)
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ string testFile = Path.Combine(testDirectory.FullName, GetTestFileName());
+ string testFileDefaultStream = testFile + defaultStream;
+ string testFileAlternateStream = testFile + alternateStream;
+
+ // Copy the default stream into an alternate stream
+ File.WriteAllText(testFileDefaultStream, "Foo");
+ Copy(testFileDefaultStream, testFileAlternateStream);
+ Assert.Equal(testFile, testDirectory.GetFiles().Single().FullName);
+ Assert.Equal("Foo", File.ReadAllText(testFileDefaultStream));
+ Assert.Equal("Foo", File.ReadAllText(testFileAlternateStream));
+
+ // Copy another file over the alternate stream
+ string testFile2 = Path.Combine(testDirectory.FullName, GetTestFileName());
+ string testFile2DefaultStream = testFile2 + defaultStream;
+ File.WriteAllText(testFile2DefaultStream, "Bar");
+ Copy(testFile2DefaultStream, testFileAlternateStream, overwrite: true);
+ Assert.Equal("Foo", File.ReadAllText(testFileDefaultStream));
+ Assert.Equal("Bar", File.ReadAllText(testFileAlternateStream));
+
+ // This always throws as you can't copy an alternate stream out (oddly)
+ Assert.Throws<IOException>(() => Copy(testFileAlternateStream, testFile2, overwrite: true));
+ Assert.Throws<IOException>(() => Copy(testFileAlternateStream, testFile2 + alternateStream, overwrite: true));
+ }
}
}
diff --git a/src/System.IO.FileSystem/tests/File/Create.cs b/src/System.IO.FileSystem/tests/File/Create.cs
index d53ac85512..c910e735b3 100644
--- a/src/System.IO.FileSystem/tests/File/Create.cs
+++ b/src/System.IO.FileSystem/tests/File/Create.cs
@@ -8,15 +8,11 @@ namespace System.IO.Tests
{
public class File_Create_str : FileSystemTest
{
- #region Utilities
-
public virtual FileStream Create(string path)
{
return File.Create(path);
}
- #endregion
-
#region UniversalTests
[Fact]
@@ -201,8 +197,9 @@ namespace System.IO.Tests
}
[Fact]
- [PlatformSpecific(TestPlatforms.Windows)] // Invalid file name with wildcard characters on Windows
- public void WindowsWildCharacterPath()
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void WindowsWildCharacterPath_Desktop()
{
DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
Assert.Throws<ArgumentException>(() => Create(Path.Combine(testDir.FullName, "dls;d", "442349-0", "v443094(*)(+*$#$*", new string(Path.DirectorySeparatorChar, 3))));
@@ -211,20 +208,53 @@ namespace System.IO.Tests
Assert.Throws<ArgumentException>(() => Create(Path.Combine(testDir.FullName, "*Tes*t")));
}
+ [Fact]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsWildCharacterPath_Core()
+ {
+ DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
+ Assert.ThrowsAny<IOException>(() => Create(Path.Combine(testDir.FullName, "dls;d", "442349-0", "v443094(*)(+*$#$*", new string(Path.DirectorySeparatorChar, 3))));
+ Assert.ThrowsAny<IOException>(() => Create(Path.Combine(testDir.FullName, "*")));
+ Assert.ThrowsAny<IOException>(() => Create(Path.Combine(testDir.FullName, "Test*t")));
+ Assert.ThrowsAny<IOException>(() => Create(Path.Combine(testDir.FullName, "*Tes*t")));
+ }
+
[Theory,
InlineData(" "),
- InlineData(" "),
+ InlineData(""),
+ InlineData("\0"),
+ InlineData(" ")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void WindowsEmptyPath(string path)
+ {
+ Assert.Throws<ArgumentException>(() => Create(path));
+ }
+
+ [Theory,
InlineData("\n"),
InlineData(">"),
InlineData("<"),
- InlineData("\0"),
InlineData("\t")]
- [PlatformSpecific(TestPlatforms.Windows)] // Invalid file name with whitespace on Windows
- public void WindowsWhitespacePath(string path)
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void WindowsInvalidPath_Desktop(string path)
{
Assert.Throws<ArgumentException>(() => Create(path));
}
+ [Theory,
+ InlineData("\n"),
+ InlineData(">"),
+ InlineData("<"),
+ InlineData("\t")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsInvalidPath_Core(string path)
+ {
+ Assert.ThrowsAny<IOException>(() => Create(Path.Combine(TestDirectory, path)));
+ }
+
[Fact]
[PlatformSpecific(TestPlatforms.AnyUnix)]
public void CreateNullThrows_Unix()
@@ -249,6 +279,49 @@ namespace System.IO.Tests
}
}
+ [Theory,
+ InlineData(":bar"),
+ InlineData(":bar:$DATA"),
+ InlineData("::$DATA")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsAlternateDataStream(string streamName)
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ streamName = Path.Combine(testDirectory.FullName, GetTestFileName()) + streamName;
+ using (Create(streamName))
+ {
+ Assert.True(File.Exists(streamName));
+ }
+ }
+
+ [Theory,
+ InlineData(":bar"),
+ InlineData(":bar:$DATA")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsAlternateDataStream_OnExisting(string streamName)
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+
+ // On closed file
+ string fileName = Path.Combine(testDirectory.FullName, GetTestFileName());
+ Create(fileName).Dispose();
+ streamName = fileName + streamName;
+ using (Create(streamName))
+ {
+ Assert.True(File.Exists(streamName));
+ }
+
+ // On open file
+ fileName = Path.Combine(testDirectory.FullName, GetTestFileName());
+ using (Create(fileName))
+ using (Create(streamName))
+ {
+ Assert.True(File.Exists(streamName));
+ }
+ }
+
#endregion
}
diff --git a/src/System.IO.FileSystem/tests/File/Delete.cs b/src/System.IO.FileSystem/tests/File/Delete.cs
index 398833f102..7cd005f944 100644
--- a/src/System.IO.FileSystem/tests/File/Delete.cs
+++ b/src/System.IO.FileSystem/tests/File/Delete.cs
@@ -2,7 +2,6 @@
// 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.Diagnostics;
using Xunit;
using Xunit.NetCore.Extensions;
@@ -10,8 +9,6 @@ namespace System.IO.Tests
{
public class File_Delete : FileSystemTest
{
- #region Utilities
-
public virtual void Delete(string path)
{
File.Delete(path);
@@ -24,8 +21,6 @@ namespace System.IO.Tests
return ret;
}
- #endregion
-
#region UniversalTests
[Fact]
@@ -129,7 +124,7 @@ namespace System.IO.Tests
[Trait(XunitConstants.Category, XunitConstants.RequiresElevation)]
public void Unix_NonExistentPath_ReadOnlyVolume()
{
- if (PlatformDetection.IsRedHatFamily6)
+ if (PlatformDetection.IsRedHatFamily6 || PlatformDetection.IsAlpine)
return; // [ActiveIssue(https://github.com/dotnet/corefx/issues/21920)]
ReadOnly_FileSystemHelper(readOnlyDirectory =>
@@ -199,6 +194,24 @@ namespace System.IO.Tests
Assert.False(testFile.Exists);
}
+ [Theory,
+ InlineData(":bar"),
+ InlineData(":bar:$DATA")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsDeleteAlternateDataStream(string streamName)
+ {
+ FileInfo testFile = Create(GetTestFilePath());
+ testFile.Create().Dispose();
+ streamName = testFile.FullName + streamName;
+ File.Create(streamName).Dispose();
+ Assert.True(File.Exists(streamName));
+ Delete(streamName);
+ Assert.False(File.Exists(streamName));
+ testFile.Refresh();
+ Assert.True(testFile.Exists);
+ }
+
#endregion
}
}
diff --git a/src/System.IO.FileSystem/tests/File/Exists.cs b/src/System.IO.FileSystem/tests/File/Exists.cs
index e1be14a175..24eec6c5cb 100644
--- a/src/System.IO.FileSystem/tests/File/Exists.cs
+++ b/src/System.IO.FileSystem/tests/File/Exists.cs
@@ -231,7 +231,7 @@ namespace System.IO.Tests
[Theory,
- MemberData(nameof(PathsWithAlternativeDataStreams))]
+ MemberData(nameof(PathsWithColons))]
[PlatformSpecific(TestPlatforms.Windows)] // alternate data stream
public void PathWithAlternateDataStreams_ReturnsFalse(string component)
{
diff --git a/src/System.IO.FileSystem/tests/File/GetSetAttributes.cs b/src/System.IO.FileSystem/tests/File/GetSetAttributes.cs
index d290ce7971..7a115ad80b 100644
--- a/src/System.IO.FileSystem/tests/File/GetSetAttributes.cs
+++ b/src/System.IO.FileSystem/tests/File/GetSetAttributes.cs
@@ -18,6 +18,20 @@ namespace System.IO.Tests
Assert.Throws<FileNotFoundException>(() => GetAttributes(GetTestFilePath() + trailingChar));
}
+ // Getting only throws for File, not FileInfo
+ [Theory,
+ InlineData(":bar"),
+ InlineData(":bar:$DATA")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void GetAttributes_MissingAlternateDataStream_Windows(string streamName)
+ {
+ string path = CreateItem();
+ streamName = path + streamName;
+
+ Assert.Throws<FileNotFoundException>(() => GetAttributes(streamName));
+ }
+
[Theory, MemberData(nameof(TrailingCharacters))]
public void GetAttributes_MissingDirectory(char trailingChar)
{
diff --git a/src/System.IO.FileSystem/tests/File/Move.cs b/src/System.IO.FileSystem/tests/File/Move.cs
index bc1199e122..304094a999 100644
--- a/src/System.IO.FileSystem/tests/File/Move.cs
+++ b/src/System.IO.FileSystem/tests/File/Move.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using Xunit;
+using System.Linq;
namespace System.IO.Tests
{
@@ -44,17 +45,26 @@ namespace System.IO.Tests
}
[Theory, MemberData(nameof(PathsWithInvalidCharacters))]
- public void PathWithIllegalCharacters(string invalidPath)
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void PathWithIllegalCharacters_Desktop(string invalidPath)
{
FileInfo testFile = new FileInfo(GetTestFilePath());
testFile.Create().Dispose();
- // Under legacy normalization we kick \\?\ paths back as invalid with ArgumentException
- // New style we don't prevalidate \\?\ at all
- if (invalidPath.Contains(@"\\?\") && !PathFeatures.IsUsingLegacyPathNormalization())
- Assert.Throws<IOException>(() => Move(testFile.FullName, invalidPath));
- else
+ Assert.Throws<ArgumentException>(() => Move(testFile.FullName, invalidPath));
+ }
+
+ [Theory, MemberData(nameof(PathsWithInvalidCharacters))]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void PathWithIllegalCharacters_Core(string invalidPath)
+ {
+ FileInfo testFile = new FileInfo(GetTestFilePath());
+ testFile.Create().Dispose();
+
+ if (invalidPath.Contains('\0'.ToString()))
Assert.Throws<ArgumentException>(() => Move(testFile.FullName, invalidPath));
+ else
+ Assert.ThrowsAny<IOException>(() => Move(testFile.FullName, invalidPath));
}
[Fact]
@@ -225,17 +235,35 @@ namespace System.IO.Tests
[Theory, MemberData(nameof(PathsWithInvalidColons))]
[PlatformSpecific(TestPlatforms.Windows)]
- [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Versions of netfx older than 4.6.2 throw an ArgumentException instead of NotSupportedException. Until all of our machines run netfx against the actual latest version, these will fail.")]
- public void WindowsPathWithIllegalColons(string invalidPath)
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void WindowsPathWithIllegalColons_Desktop(string invalidPath)
+ {
+ FileInfo testFile = new FileInfo(GetTestFilePath());
+ testFile.Create().Dispose();
+ if (PathFeatures.IsUsingLegacyPathNormalization())
+ {
+ Assert.Throws<ArgumentException>(() => Move(testFile.FullName, invalidPath));
+ }
+ else
+ {
+ Assert.Throws<NotSupportedException>(() => Move(testFile.FullName, invalidPath));
+ }
+ }
+
+ [Theory, MemberData(nameof(PathsWithInvalidColons))]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsPathWithIllegalColons_Core(string invalidPath)
{
FileInfo testFile = new FileInfo(GetTestFilePath());
testFile.Create().Dispose();
- Assert.Throws<NotSupportedException>(() => Move(testFile.FullName, invalidPath));
+ Assert.ThrowsAny<IOException>(() => Move(testFile.FullName, testFile.DirectoryName + Path.DirectorySeparatorChar + invalidPath));
}
[Fact]
- [PlatformSpecific(TestPlatforms.Windows)] // Wild characters in path throw ArgumentException
- public void WindowsWildCharacterPath()
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void WindowsWildCharacterPath_Desktop()
{
Assert.Throws<ArgumentException>(() => Move("*", GetTestFilePath()));
Assert.Throws<ArgumentException>(() => Move(GetTestFilePath(), "*"));
@@ -244,6 +272,18 @@ namespace System.IO.Tests
}
[Fact]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsWildCharacterPath_Core()
+ {
+ Assert.Throws<FileNotFoundException>(() => Move(Path.Combine(TestDirectory, "*"), GetTestFilePath()));
+ Assert.Throws<FileNotFoundException>(() => Move(GetTestFilePath(), Path.Combine(TestDirectory, "*")));
+ Assert.Throws<FileNotFoundException>(() => Move(GetTestFilePath(), Path.Combine(TestDirectory, "Test*t")));
+ Assert.Throws<FileNotFoundException>(() => Move(GetTestFilePath(), Path.Combine(TestDirectory, "*Test")));
+ }
+
+
+ [Fact]
[PlatformSpecific(TestPlatforms.AnyUnix)] // Wild characters in path are allowed
public void UnixWildCharacterPath()
{
@@ -268,9 +308,29 @@ namespace System.IO.Tests
}
[Theory,
- MemberData(nameof(WhiteSpace))]
- [PlatformSpecific(TestPlatforms.Windows)] // Whitespace in path throws ArgumentException
- public void WindowsWhitespacePath(string whitespace)
+ MemberData(nameof(ControlWhiteSpace))]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
+ public void WindowsControlPath_Desktop(string whitespace)
+ {
+ FileInfo testFile = new FileInfo(GetTestFilePath());
+ Assert.Throws<ArgumentException>(() => Move(testFile.FullName, whitespace));
+ }
+
+ [Theory,
+ MemberData(nameof(ControlWhiteSpace))]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsControlPath_Core(string whitespace)
+ {
+ FileInfo testFile = new FileInfo(GetTestFilePath());
+ Assert.ThrowsAny<IOException>(() => Move(testFile.FullName, Path.Combine(TestDirectory, whitespace)));
+ }
+
+ [Theory,
+ MemberData(nameof(SimpleWhiteSpace))]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void WindowsSimpleWhitespacePath(string whitespace)
{
FileInfo testFile = new FileInfo(GetTestFilePath());
Assert.Throws<ArgumentException>(() => Move(testFile.FullName, whitespace));
@@ -289,6 +349,29 @@ namespace System.IO.Tests
}
+ [Theory,
+ InlineData("", ":bar"),
+ InlineData("", ":bar:$DATA"),
+ InlineData("::$DATA", ":bar"),
+ InlineData("::$DATA", ":bar:$DATA")]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public void WindowsAlternateDataStreamMove(string defaultStream, string alternateStream)
+ {
+ DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+ string testFile = Path.Combine(testDirectory.FullName, GetTestFileName());
+ string testFileDefaultStream = testFile + defaultStream;
+ string testFileAlternateStream = testFile + alternateStream;
+
+ // Cannot move into an alternate stream
+ File.WriteAllText(testFileDefaultStream, "Foo");
+ Assert.Throws<IOException>(() => Move(testFileDefaultStream, testFileAlternateStream));
+
+ // Cannot move out of an alternate stream
+ File.WriteAllText(testFileAlternateStream, "Bar");
+ string testFile2 = Path.Combine(testDirectory.FullName, GetTestFileName());
+ Assert.Throws<IOException>(() => Move(testFileAlternateStream, testFile2));
+ }
#endregion
}
}
diff --git a/src/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs b/src/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs
index 7764cdfe5b..b92e5201bd 100644
--- a/src/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs
+++ b/src/System.IO.FileSystem/tests/File/ReadWriteAllBytes.cs
@@ -4,6 +4,7 @@
using System.Runtime.InteropServices;
using System.Text;
+using System.Threading.Tasks;
using Xunit;
namespace System.IO.Tests
@@ -120,5 +121,57 @@ namespace System.IO.Tests
File.SetAttributes(path, FileAttributes.Normal);
}
}
+
+ [Fact]
+ public void EmptyFile_ReturnsEmptyArray()
+ {
+ string path = GetTestFilePath();
+ File.Create(path).Dispose();
+ Assert.Equal(0, File.ReadAllBytes(path).Length);
+ }
+
+ [Theory]
+ [PlatformSpecific(TestPlatforms.Linux)]
+ [InlineData("/proc/cmdline")]
+ [InlineData("/proc/version")]
+ [InlineData("/proc/filesystems")]
+ public void ProcFs_EqualsReadAllText(string path)
+ {
+ byte[] bytes = null;
+ string text = null;
+
+ const int NumTries = 3; // some of these could theoretically change between reads, so allow retries just in case
+ for (int i = 1; i <= NumTries; i++)
+ {
+ try
+ {
+ bytes = File.ReadAllBytes(path);
+ text = File.ReadAllText(path);
+ Assert.Equal(text, Encoding.UTF8.GetString(bytes));
+ }
+ catch when (i < NumTries) { }
+ }
+ }
+
+ [Theory]
+ [PlatformSpecific(TestPlatforms.Linux)]
+ public void ReadAllBytes_ProcFs_Uptime_ContainsTwoNumbers()
+ {
+ string text = Encoding.UTF8.GetString(File.ReadAllBytes("/proc/uptime"));
+ string[] parts = text.Split(new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ Assert.Equal(2, parts.Length);
+ Assert.True(double.TryParse(parts[0].Trim(), out _));
+ Assert.True(double.TryParse(parts[1].Trim(), out _));
+ }
+
+ [Theory]
+ [PlatformSpecific(TestPlatforms.Linux)]
+ [InlineData("/proc/meminfo")]
+ [InlineData("/proc/stat")]
+ [InlineData("/proc/cpuinfo")]
+ public void ProcFs_NotEmpty(string path)
+ {
+ Assert.InRange(File.ReadAllBytes(path).Length, 1, int.MaxValue);
+ }
}
}
diff --git a/src/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs b/src/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs
index 47b3d71ccc..fa2a8faa7f 100644
--- a/src/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs
+++ b/src/System.IO.FileSystem/tests/File/ReadWriteAllBytesAsync.cs
@@ -134,5 +134,57 @@ namespace System.IO.Tests
File.SetAttributes(path, FileAttributes.Normal);
}
}
+
+ [Fact]
+ public async Task EmptyFile_ReturnsEmptyArray()
+ {
+ string path = GetTestFilePath();
+ File.Create(path).Dispose();
+ Assert.Equal(0, (await File.ReadAllBytesAsync(path)).Length);
+ }
+
+ [Theory]
+ [PlatformSpecific(TestPlatforms.Linux)]
+ [InlineData("/proc/cmdline")]
+ [InlineData("/proc/version")]
+ [InlineData("/proc/filesystems")]
+ public async Task ProcFs_EqualsReadAllText(string path)
+ {
+ byte[] bytes = null;
+ string text = null;
+
+ const int NumTries = 3; // some of these could theoretically change between reads, so allow retries just in case
+ for (int i = 1; i <= NumTries; i++)
+ {
+ try
+ {
+ bytes = await File.ReadAllBytesAsync(path);
+ text = await File.ReadAllTextAsync(path);
+ Assert.Equal(text, Encoding.UTF8.GetString(bytes));
+ }
+ catch when (i < NumTries) { }
+ }
+ }
+
+ [Theory]
+ [PlatformSpecific(TestPlatforms.Linux)]
+ public async Task ReadAllBytes_ProcFs_Uptime_ContainsTwoNumbers()
+ {
+ string text = Encoding.UTF8.GetString(await File.ReadAllBytesAsync("/proc/uptime"));
+ string[] parts = text.Split(' ', StringSplitOptions.RemoveEmptyEntries);
+ Assert.Equal(2, parts.Length);
+ Assert.True(double.TryParse(parts[0].Trim(), out _));
+ Assert.True(double.TryParse(parts[1].Trim(), out _));
+ }
+
+ [Theory]
+ [PlatformSpecific(TestPlatforms.Linux)]
+ [InlineData("/proc/meminfo")]
+ [InlineData("/proc/stat")]
+ [InlineData("/proc/cpuinfo")]
+ public async Task ProcFs_NotEmpty(string path)
+ {
+ Assert.InRange((await File.ReadAllBytesAsync(path)).Length, 1, int.MaxValue);
+ }
}
}
diff --git a/src/System.IO.FileSystem/tests/FileInfo/GetSetTimes.cs b/src/System.IO.FileSystem/tests/FileInfo/GetSetTimes.cs
index ed38b89719..d88fa6ea00 100644
--- a/src/System.IO.FileSystem/tests/FileInfo/GetSetTimes.cs
+++ b/src/System.IO.FileSystem/tests/FileInfo/GetSetTimes.cs
@@ -19,6 +19,8 @@ namespace System.IO.Tests
public override FileInfo GetMissingItem() => new FileInfo(GetTestFilePath());
+ public override string GetItemPath(FileInfo item) => item.FullName;
+
public override void InvokeCreate(FileInfo item) => item.Create();
public override IEnumerable<TimeFunction> TimeFunctions(bool requiresRoundtripping = false)
diff --git a/src/System.IO.FileSystem/tests/FileInfo/Open.cs b/src/System.IO.FileSystem/tests/FileInfo/Open.cs
index c5df42fd5e..b9b35f51c2 100644
--- a/src/System.IO.FileSystem/tests/FileInfo/Open.cs
+++ b/src/System.IO.FileSystem/tests/FileInfo/Open.cs
@@ -13,22 +13,22 @@ namespace System.IO.Tests
return new FileInfo(path).Open(mode);
}
- [Fact]
+ [Theory, MemberData(nameof(StreamSpecifiers))]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "FileInfo.Open(string, filemode) on netfx always uses FileAccess.ReadWrite instead of choosing a FileAccess based on the FileMode. This bug was fixed in netcoreapp.")]
- public override void FileModeAppend()
+ public override void FileModeAppend(string streamSpecifier)
{
- using (FileStream fs = CreateFileStream(GetTestFilePath(), FileMode.Append))
+ using (FileStream fs = CreateFileStream(GetTestFilePath() + streamSpecifier, FileMode.Append))
{
Assert.Equal(false, fs.CanRead);
Assert.Equal(true, fs.CanWrite);
}
}
- [Fact]
+ [Theory, MemberData(nameof(StreamSpecifiers))]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "FileInfo.Open(string, filemode) on netfx always uses FileAccess.ReadWrite instead of choosing a FileAccess based on the FileMode. This bug was fixed in netcoreapp.")]
- public override void FileModeAppendExisting()
+ public override void FileModeAppendExisting(string streamSpecifier)
{
- string fileName = GetTestFilePath();
+ string fileName = GetTestFilePath() + streamSpecifier;
using (FileStream fs = CreateFileStream(fileName, FileMode.Create))
{
fs.WriteByte(0);
diff --git a/src/System.IO.FileSystem/tests/FileStream/Dispose.cs b/src/System.IO.FileSystem/tests/FileStream/Dispose.cs
index 026a82b910..edee857a62 100644
--- a/src/System.IO.FileSystem/tests/FileStream/Dispose.cs
+++ b/src/System.IO.FileSystem/tests/FileStream/Dispose.cs
@@ -4,6 +4,7 @@
using Microsoft.Win32.SafeHandles;
using System;
+using System.Diagnostics;
using System.IO;
using Xunit;
@@ -45,6 +46,11 @@ namespace System.IO.Tests
: base(path, mode)
{ }
+ public MyFileStream(SafeFileHandle handle, FileAccess access, Action<bool> disposeMethod) : base(handle, access)
+ {
+ DisposeMethod = disposeMethod;
+ }
+
public Action<bool> DisposeMethod { get; set; }
protected override void Dispose(bool disposing)
@@ -58,6 +64,92 @@ namespace System.IO.Tests
}
}
+
+ [Fact]
+ public void Dispose_CallsVirtualDisposeTrueArg_ThrowsDuringFlushWriteBuffer_DisposeThrows()
+ {
+ RemoteInvoke(() =>
+ {
+ string fileName = GetTestFilePath();
+ using (FileStream fscreate = new FileStream(fileName, FileMode.Create))
+ {
+ fscreate.WriteByte(0);
+ }
+ bool writeDisposeInvoked = false;
+ Action<bool> writeDisposeMethod = _ => writeDisposeInvoked = true;
+ using (var fsread = new FileStream(fileName, FileMode.Open, FileAccess.Read))
+ {
+ Action act = () => // separate method to avoid JIT lifetime-extension issues
+ {
+ using (var fswrite = new MyFileStream(fsread.SafeFileHandle, FileAccess.Write, writeDisposeMethod))
+ {
+ fswrite.WriteByte(0);
+
+ // Normal dispose should call Dispose(true). Throws due to FS trying to flush write buffer
+ Assert.Throws<UnauthorizedAccessException>(() => fswrite.Dispose());
+ Assert.True(writeDisposeInvoked, "Expected Dispose(true) to be called from Dispose()");
+ writeDisposeInvoked = false;
+
+ // Only throws on first Dispose call
+ fswrite.Dispose();
+ Assert.True(writeDisposeInvoked, "Expected Dispose(true) to be called from Dispose()");
+ writeDisposeInvoked = false;
+ }
+ Assert.True(writeDisposeInvoked, "Expected Dispose(true) to be called from Dispose() again");
+ writeDisposeInvoked = false;
+ };
+ act();
+
+ for (int i = 0; i < 2; i++)
+ {
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ }
+ Assert.False(writeDisposeInvoked, "Expected finalizer to have been suppressed");
+ }
+ return SuccessExitCode;
+ }).Dispose();
+ }
+
+ [Fact]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Missing fix for https://github.com/dotnet/coreclr/pull/16250")]
+ public void NoDispose_CallsVirtualDisposeFalseArg_ThrowsDuringFlushWriteBuffer_FinalizerWontThrow()
+ {
+ RemoteInvoke(() =>
+ {
+ string fileName = GetTestFilePath();
+ using (FileStream fscreate = new FileStream(fileName, FileMode.Create))
+ {
+ fscreate.WriteByte(0);
+ }
+ bool writeDisposeInvoked = false;
+ Action<bool> writeDisposeMethod = (disposing) =>
+ {
+ writeDisposeInvoked = true;
+ Assert.False(disposing, "Expected false arg to Dispose(bool)");
+ };
+ using (var fsread = new FileStream(fileName, FileMode.Open, FileAccess.Read))
+ {
+ Action act = () => // separate method to avoid JIT lifetime-extension issues
+ {
+ var fswrite = new MyFileStream(fsread.SafeFileHandle, FileAccess.Write, writeDisposeMethod);
+ fswrite.WriteByte(0);
+ };
+ act();
+
+ // Dispose is not getting called here.
+ // instead, make sure finalizer gets called and doesnt throw exception
+ for (int i = 0; i < 2; i++)
+ {
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ }
+ Assert.True(writeDisposeInvoked, "Expected finalizer to be invoked but not throw exception");
+ }
+ return SuccessExitCode;
+ }).Dispose();
+ }
+
[Fact]
public void Dispose_CallsVirtualDispose_TrueArg()
{
diff --git a/src/System.IO.FileSystem/tests/FileStream/Name.cs b/src/System.IO.FileSystem/tests/FileStream/Name.cs
index 88fe39c402..d4dde6ed22 100644
--- a/src/System.IO.FileSystem/tests/FileStream/Name.cs
+++ b/src/System.IO.FileSystem/tests/FileStream/Name.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Globalization;
using System.IO;
using Xunit;
@@ -39,11 +40,16 @@ namespace System.IO.Tests
[Fact]
public void NameReturnsUnknownForHandle()
{
- using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite))
- using (FileStream fsh = new FileStream(fs.SafeFileHandle, FileAccess.ReadWrite))
+ RemoteInvoke(() =>
{
- Assert.Equal("[Unknown]", fsh.Name);
- }
+ CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
+
+ using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite))
+ using (FileStream fsh = new FileStream(fs.SafeFileHandle, FileAccess.ReadWrite))
+ {
+ Assert.Equal("[Unknown]", fsh.Name);
+ }
+ }).Dispose();
}
}
}
diff --git a/src/System.IO.FileSystem/tests/FileStream/ReadWriteSpan.netcoreapp.cs b/src/System.IO.FileSystem/tests/FileStream/ReadWriteSpan.netcoreapp.cs
index 272b19769e..0b1898d1f5 100644
--- a/src/System.IO.FileSystem/tests/FileStream/ReadWriteSpan.netcoreapp.cs
+++ b/src/System.IO.FileSystem/tests/FileStream/ReadWriteSpan.netcoreapp.cs
@@ -2,6 +2,7 @@
// 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.Buffers;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -188,6 +189,20 @@ namespace System.IO.Tests
}
[Fact]
+ public async Task NonEmptyFile_CustomOwnedMemory_ReadAsync_GetsExpectedData()
+ {
+ string fileName = GetTestFilePath();
+ File.WriteAllBytes(fileName, TestBuffer);
+
+ using (var fs = CreateFileStream(fileName, FileMode.Open))
+ using (var buffer = new NativeOwnedMemory(TestBuffer.Length))
+ {
+ Assert.Equal(TestBuffer.Length, await fs.ReadAsync(buffer.Memory));
+ Assert.Equal<byte>(TestBuffer, buffer.Memory.ToArray());
+ }
+ }
+
+ [Fact]
public void ReadOnly_WriteAsync_Throws()
{
string fileName = GetTestFilePath();
@@ -238,24 +253,49 @@ namespace System.IO.Tests
Assert.Equal(TestBuffer, buffer);
}
}
+
+ [Fact]
+ public async Task NonEmptyWriteAsync_CustomOwnedMemory_WritesExpectedData()
+ {
+ using (var mem = new NativeOwnedMemory(TestBuffer.Length))
+ using (var fs = CreateFileStream(GetTestFilePath(), FileMode.Create))
+ {
+ new Memory<byte>(TestBuffer).CopyTo(mem.Memory);
+
+ await fs.WriteAsync(mem.Memory);
+ Assert.Equal(TestBuffer.Length, fs.Length);
+ Assert.Equal(TestBuffer.Length, fs.Position);
+
+ fs.Position = 0;
+ var buffer = new byte[TestBuffer.Length];
+ Assert.Equal(TestBuffer.Length, await fs.ReadAsync(new Memory<byte>(buffer)));
+ Assert.Equal(TestBuffer, buffer);
+ }
+ }
}
public class Sync_FileStream_ReadWrite_Span : FileStream_ReadWrite_Span
{
protected override FileStream CreateFileStream(string path, FileMode mode, FileAccess access) =>
- new FileStream(path, mode, access, FileShare.None, 0x1000, FileOptions.None);
+ new FileStream(path, mode, access, FileShare.None, bufferSize: 0x1000, FileOptions.None);
}
public class Async_FileStream_ReadWrite_Span : FileStream_ReadWrite_Span
{
protected override FileStream CreateFileStream(string path, FileMode mode, FileAccess access) =>
- new FileStream(path, mode, access, FileShare.None, 0x1000, FileOptions.Asynchronous);
+ new FileStream(path, mode, access, FileShare.None, bufferSize: 0x1000, FileOptions.Asynchronous);
+ }
+
+ public class Async_NoBuffer_FileStream_ReadWrite_Span : FileStream_ReadWrite_Span
+ {
+ protected override FileStream CreateFileStream(string path, FileMode mode, FileAccess access) =>
+ new FileStream(path, mode, access, FileShare.None, bufferSize: 1, FileOptions.Asynchronous);
}
public sealed class Sync_DerivedFileStream_ReadWrite_Span : Sync_FileStream_ReadWrite_Span
{
protected override FileStream CreateFileStream(string path, FileMode mode, FileAccess access) =>
- new DerivedFileStream(path, mode, access, FileShare.None, 0x1000, FileOptions.None);
+ new DerivedFileStream(path, mode, access, FileShare.None, bufferSize: 0x1000, FileOptions.None);
[Fact]
public void CallSpanReadWriteOnDerivedFileStream_ArrayMethodsUsed()
@@ -299,7 +339,7 @@ namespace System.IO.Tests
public sealed class Async_DerivedFileStream_ReadWrite_Span : Async_FileStream_ReadWrite_Span
{
protected override FileStream CreateFileStream(string path, FileMode mode, FileAccess access) =>
- new DerivedFileStream(path, mode, access, FileShare.None, 0x1000, FileOptions.Asynchronous);
+ new DerivedFileStream(path, mode, access, FileShare.None, bufferSize: 0x1000, FileOptions.Asynchronous);
}
internal sealed class DerivedFileStream : FileStream
diff --git a/src/System.IO.FileSystem/tests/FileStream/WriteAsync.cs b/src/System.IO.FileSystem/tests/FileStream/WriteAsync.cs
index fa1f45095d..a80eb79fbb 100644
--- a/src/System.IO.FileSystem/tests/FileStream/WriteAsync.cs
+++ b/src/System.IO.FileSystem/tests/FileStream/WriteAsync.cs
@@ -388,35 +388,30 @@ namespace System.IO.Tests
string writeFileName = GetTestFilePath();
do
{
- // Create a new token that expires between 100-1000ms
- CancellationTokenSource tokenSource = new CancellationTokenSource();
- tokenSource.CancelAfter(rand.Next(100, 1000));
+
+ int totalBytesWritten = 0;
using (var stream = new FileStream(writeFileName, FileMode.Create, FileAccess.Write))
{
do
{
- try
+ // 20%: random write size
+ int bytesToWrite = (rand.NextDouble() < 0.2 ? rand.Next(16, MaximumWriteSize) : NormalWriteSize);
+
+ if (rand.NextDouble() < 0.1)
{
- // 20%: random write size
- int bytesToWrite = (rand.NextDouble() < 0.2 ? rand.Next(16, MaximumWriteSize) : NormalWriteSize);
-
- if (rand.NextDouble() < 0.1)
- {
- // 10%: Sync write
- stream.Write(dataToWrite, 0, bytesToWrite);
- }
- else
- {
- // 90%: Async write
- await WriteAsync(stream, dataToWrite, 0, bytesToWrite, tokenSource.Token);
- }
+ // 10%: Sync write
+ stream.Write(dataToWrite, 0, bytesToWrite);
}
- catch (TaskCanceledException)
+ else
{
- Assert.True(tokenSource.Token.IsCancellationRequested, "Received cancellation exception before token expired");
+ // 90%: Async write
+ await WriteAsync(stream, dataToWrite, 0, bytesToWrite);
}
- } while (!tokenSource.Token.IsCancellationRequested);
+
+ totalBytesWritten += bytesToWrite;
+ // Cap written bytes at 10 million to avoid writing too much to disk
+ } while (totalBytesWritten < 10_000_000);
}
} while (DateTime.UtcNow - testStartTime <= testRunTime);
}
diff --git a/src/System.IO.FileSystem/tests/FileStream/ctor_str_fm.cs b/src/System.IO.FileSystem/tests/FileStream/ctor_str_fm.cs
index 2f9655ac5e..45791fe767 100644
--- a/src/System.IO.FileSystem/tests/FileStream/ctor_str_fm.cs
+++ b/src/System.IO.FileSystem/tests/FileStream/ctor_str_fm.cs
@@ -51,17 +51,39 @@ namespace System.IO.Tests
Assert.Throws<DirectoryNotFoundException>(() => CreateFileStream(path, FileMode.Open));
}
- [Fact]
- public void FileModeCreate()
+
+ public static TheoryData<string> StreamSpecifiers
{
- using (CreateFileStream(GetTestFilePath(), FileMode.Create))
- { }
+ get
+ {
+ TheoryData<string> data = new TheoryData<string>();
+ data.Add("");
+
+ if (PlatformDetection.IsWindows && PlatformDetection.IsNetCore)
+ {
+ data.Add("::$DATA"); // Same as default stream (e.g. main file)
+ data.Add(":bar"); // $DATA isn't necessary
+ data.Add(":bar:$DATA"); // $DATA can be explicitly specified
+ }
+
+ return data;
+ }
}
- [Fact]
- public void FileModeCreateExisting()
+ [Theory, MemberData(nameof(StreamSpecifiers))]
+ public void FileModeCreate(string streamSpecifier)
{
- string fileName = GetTestFilePath();
+ string fileName = GetTestFilePath() + streamSpecifier;
+ using (CreateFileStream(fileName, FileMode.Create))
+ {
+ Assert.True(File.Exists(fileName));
+ }
+ }
+
+ [Theory, MemberData(nameof(StreamSpecifiers))]
+ public void FileModeCreateExisting(string streamSpecifier)
+ {
+ string fileName = GetTestFilePath() + streamSpecifier;
using (FileStream fs = CreateFileStream(fileName, FileMode.Create))
{
fs.WriteByte(0);
@@ -77,17 +99,20 @@ namespace System.IO.Tests
}
}
- [Fact]
- public void FileModeCreateNew()
+ [Theory, MemberData(nameof(StreamSpecifiers))]
+ public void FileModeCreateNew(string streamSpecifier)
{
- using (CreateFileStream(GetTestFilePath(), FileMode.CreateNew))
- { }
+ string fileName = GetTestFilePath() + streamSpecifier;
+ using (CreateFileStream(fileName, FileMode.CreateNew))
+ {
+ Assert.True(File.Exists(fileName));
+ }
}
- [Fact]
- public void FileModeCreateNewExistingThrows()
+ [Theory, MemberData(nameof(StreamSpecifiers))]
+ public void FileModeCreateNewExistingThrows(string streamSpecifier)
{
- string fileName = GetTestFilePath();
+ string fileName = GetTestFilePath() + streamSpecifier;
using (FileStream fs = CreateFileStream(fileName, FileMode.CreateNew))
{
fs.WriteByte(0);
@@ -98,18 +123,18 @@ namespace System.IO.Tests
Assert.Throws<IOException>(() => CreateFileStream(fileName, FileMode.CreateNew));
}
- [Fact]
- public void FileModeOpenThrows()
+ [Theory, MemberData(nameof(StreamSpecifiers))]
+ public void FileModeOpenThrows(string streamSpecifier)
{
- string fileName = GetTestFilePath();
+ string fileName = GetTestFilePath() + streamSpecifier;
FileNotFoundException fnfe = Assert.Throws<FileNotFoundException>(() => CreateFileStream(fileName, FileMode.Open));
Assert.Equal(fileName, fnfe.FileName);
}
- [Fact]
- public void FileModeOpenExisting()
+ [Theory, MemberData(nameof(StreamSpecifiers))]
+ public void FileModeOpenExisting(string streamSpecifier)
{
- string fileName = GetTestFilePath();
+ string fileName = GetTestFilePath() + streamSpecifier;
using (FileStream fs = CreateFileStream(fileName, FileMode.Create))
{
fs.WriteByte(0);
@@ -125,17 +150,20 @@ namespace System.IO.Tests
}
}
- [Fact]
- public void FileModeOpenOrCreate()
+ [Theory, MemberData(nameof(StreamSpecifiers))]
+ public void FileModeOpenOrCreate(string streamSpecifier)
{
- using (CreateFileStream(GetTestFilePath(), FileMode.OpenOrCreate))
- {}
+ string fileName = GetTestFilePath() + streamSpecifier;
+ using (CreateFileStream(fileName, FileMode.OpenOrCreate))
+ {
+ Assert.True(File.Exists(fileName));
+ }
}
- [Fact]
- public void FileModeOpenOrCreateExisting()
+ [Theory, MemberData(nameof(StreamSpecifiers))]
+ public void FileModeOpenOrCreateExisting(string streamSpecifier)
{
- string fileName = GetTestFilePath();
+ string fileName = GetTestFilePath() + streamSpecifier;
using (FileStream fs = CreateFileStream(fileName, FileMode.Create))
{
fs.WriteByte(0);
@@ -151,18 +179,18 @@ namespace System.IO.Tests
}
}
- [Fact]
- public void FileModeTruncateThrows()
+ [Theory, MemberData(nameof(StreamSpecifiers))]
+ public void FileModeTruncateThrows(string streamSpecifier)
{
- string fileName = GetTestFilePath();
+ string fileName = GetTestFilePath() + streamSpecifier;
FileNotFoundException fnfe = Assert.Throws<FileNotFoundException>(() => CreateFileStream(fileName, FileMode.Truncate));
Assert.Equal(fileName, fnfe.FileName);
}
- [Fact]
- public void FileModeTruncateExisting()
+ [Theory, MemberData(nameof(StreamSpecifiers))]
+ public void FileModeTruncateExisting(string streamSpecifier)
{
- string fileName = GetTestFilePath();
+ string fileName = GetTestFilePath() + streamSpecifier;
using (FileStream fs = CreateFileStream(fileName, FileMode.Create))
{
fs.WriteByte(0);
@@ -178,20 +206,20 @@ namespace System.IO.Tests
}
}
- [Fact]
- public virtual void FileModeAppend()
+ [Theory, MemberData(nameof(StreamSpecifiers))]
+ public virtual void FileModeAppend(string streamSpecifier)
{
- using (FileStream fs = CreateFileStream(GetTestFilePath(), FileMode.Append))
+ using (FileStream fs = CreateFileStream(GetTestFilePath() + streamSpecifier, FileMode.Append))
{
Assert.Equal(false, fs.CanRead);
Assert.Equal(true, fs.CanWrite);
}
}
- [Fact]
- public virtual void FileModeAppendExisting()
+ [Theory, MemberData(nameof(StreamSpecifiers))]
+ public virtual void FileModeAppendExisting(string streamSpecifier)
{
- string fileName = GetTestFilePath();
+ string fileName = GetTestFilePath() + streamSpecifier;
using (FileStream fs = CreateFileStream(fileName, FileMode.Create))
{
fs.WriteByte(0);
diff --git a/src/System.IO.FileSystem/tests/FileSystemTest.cs b/src/System.IO.FileSystem/tests/FileSystemTest.cs
index 4e593f27b8..6f020a00f6 100644
--- a/src/System.IO.FileSystem/tests/FileSystemTest.cs
+++ b/src/System.IO.FileSystem/tests/FileSystemTest.cs
@@ -28,7 +28,7 @@ namespace System.IO.Tests
public static TheoryData WhiteSpace = IOInputs.GetWhiteSpace().ToTheoryData();
public static TheoryData UncPathsWithoutShareName = IOInputs.GetUncPathsWithoutShareName().ToTheoryData();
public static TheoryData PathsWithReservedDeviceNames = IOInputs.GetPathsWithReservedDeviceNames().ToTheoryData();
- public static TheoryData PathsWithAlternativeDataStreams = IOInputs.GetPathsWithAlternativeDataStreams().ToTheoryData();
+ public static TheoryData PathsWithColons = IOInputs.GetPathsWithColons().ToTheoryData();
public static TheoryData PathsWithComponentLongerThanMaxComponent = IOInputs.GetPathsWithComponentLongerThanMaxComponent().ToTheoryData();
public static TheoryData ControlWhiteSpace = IOInputs.GetControlWhiteSpace().ToTheoryData();
public static TheoryData NonControlWhiteSpace = IOInputs.GetNonControlWhiteSpace().ToTheoryData();
diff --git a/src/System.IO.FileSystem/tests/Performance/Perf.Directory.cs b/src/System.IO.FileSystem/tests/Performance/Perf.Directory.cs
index 508275dc12..7a70751425 100644
--- a/src/System.IO.FileSystem/tests/Performance/Perf.Directory.cs
+++ b/src/System.IO.FileSystem/tests/Performance/Perf.Directory.cs
@@ -2,7 +2,9 @@
// 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.Text;
using Microsoft.Xunit.Performance;
+using Xunit;
namespace System.IO.Tests
{
@@ -55,5 +57,89 @@ namespace System.IO.Tests
// Teardown
Directory.Delete(testFile);
}
+
+ public string GetTestDeepFilePath(int depth)
+ {
+ string directory = Path.DirectorySeparatorChar + "a";
+ StringBuilder sb = new StringBuilder(depth * 2);
+ for (int i = 0; i < depth; i++)
+ {
+ sb.Append(directory);
+ }
+
+ return sb.ToString();
+ }
+
+ public static TheoryData<int, int> RecursiveDepthData
+ {
+ get
+ {
+ var data = new TheoryData<int, int>();
+ data.Add(10, 100);
+
+ // Length of the path can be 260 characters on netfx.
+ if (AreAllLongPathsAvailable)
+ {
+ data.Add(100, 10);
+ // Most Unix distributions have a maximum path length of 1024 characters (1024 UTF-8 bytes).
+ if (PlatformDetection.IsWindows)
+ data.Add(1000, 1);
+ }
+
+ return data;
+ }
+ }
+
+ [Benchmark]
+ [MemberData(nameof(RecursiveDepthData))]
+ [OuterLoop("Takes a lot of time to finish")]
+ public void RecursiveCreateDirectoryTest(int depth, int times)
+ {
+ // Setup
+ string rootDirectory = GetTestFilePath();
+ string path = GetTestDeepFilePath(depth);
+
+ foreach (var iteration in Benchmark.Iterations)
+ {
+ using (iteration.StartMeasurement())
+ {
+ for (int i = 0; i < times; i++)
+ {
+ Directory.CreateDirectory(rootDirectory + Path.DirectorySeparatorChar + i + path);
+ }
+ }
+ // TearDown For each iteration
+ Directory.Delete(rootDirectory, recursive: true);
+ }
+ }
+
+ [Benchmark]
+ [MemberData(nameof(RecursiveDepthData))]
+ [OuterLoop("Takes a lot of time to finish")]
+ public void RecursiveDeleteDirectoryTest(int depth, int times)
+ {
+ // Setup
+ string rootDirectory = GetTestFilePath();
+ string path = GetTestDeepFilePath(depth);
+
+ foreach (var iteration in Benchmark.Iterations)
+ {
+ // Setup For each Iteration
+ for (int i = 0; i < times; i++)
+ {
+ Directory.CreateDirectory(rootDirectory + Path.DirectorySeparatorChar + i + path);
+ }
+
+ using (iteration.StartMeasurement())
+ {
+ for (int i = 0; i < times; i++)
+ {
+ Directory.Delete(rootDirectory + Path.DirectorySeparatorChar + i, recursive: true);
+ }
+ }
+ }
+ // TearDown
+ Directory.Delete(rootDirectory, recursive: true);
+ }
}
}
diff --git a/src/System.IO.FileSystem/tests/Performance/System.IO.FileSystem.Performance.Tests.csproj b/src/System.IO.FileSystem/tests/Performance/System.IO.FileSystem.Performance.Tests.csproj
index afcd6a3da8..4d758dda6f 100644
--- a/src/System.IO.FileSystem/tests/Performance/System.IO.FileSystem.Performance.Tests.csproj
+++ b/src/System.IO.FileSystem/tests/Performance/System.IO.FileSystem.Performance.Tests.csproj
@@ -37,5 +37,8 @@
<Name>PerfRunner</Name>
</ProjectReference>
</ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project> \ No newline at end of file
diff --git a/src/System.IO.FileSystem/tests/PortedCommon/IOInputs.cs b/src/System.IO.FileSystem/tests/PortedCommon/IOInputs.cs
index 10fbbd8138..68dfcd7fa1 100644
--- a/src/System.IO.FileSystem/tests/PortedCommon/IOInputs.cs
+++ b/src/System.IO.FileSystem/tests/PortedCommon/IOInputs.cs
@@ -152,7 +152,7 @@ internal static class IOInputs
}
}
- public static IEnumerable<string> GetPathsWithAlternativeDataStreams()
+ public static IEnumerable<string> GetPathsWithColons()
{
yield return @"AA:";
yield return @"AAA:";
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 fb99199ad1..7597996101 100644
--- a/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
+++ b/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
@@ -54,6 +54,19 @@
<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\FileSystemNameTests.netcoreapp.cs" />
+ <Compile Include="Enumeration\MatchCasingTests.netcoreapp.cs" />
+ <Compile Include="Enumeration\TrimmedPaths.netcoreapp.cs" />
+ <Compile Include="Enumeration\ErrorHandlingTests.netcoreapp.cs" />
+ <Compile Include="Enumeration\IncludePredicateTests.netcoreapp.cs" />
+ <Compile Include="Enumeration\PatternTransformTests.netcoreapp.cs" />
+ <Compile Include="Enumeration\RootTests.netcoreapp.cs" />
+ <Compile Include="Enumeration\AttributeTests.netcoreapp.cs" />
+ <Compile Include="Enumeration\MatchTypesTests.netcoreapp.cs" />
+ <Compile Include="Enumeration\ExampleTests.netcoreapp.cs" />
</ItemGroup>
<ItemGroup>
<!-- Rewritten -->
@@ -164,6 +177,9 @@
<Compile Include="FileInfo\AppendText.cs" />
<Compile Include="FileInfo\CopyTo.cs" />
<!-- Helpers -->
+ <Compile Include="$(CommonTestPath)\System\Buffers\NativeOwnedMemory.cs">
+ <Link>Common\System\Buffers\NativeOwnedMemory.cs</Link>
+ </Compile>
<Compile Include="$(CommonTestPath)\System\IO\TempFile.cs">
<Link>Common\System\IO\TempFile.cs</Link>
</Compile>
@@ -182,5 +198,8 @@
<ItemGroup>
<EmbeddedResource Include="Resources\$(AssemblyName).rd.xml" />
</ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project> \ No newline at end of file
diff --git a/src/System.IO.FileSystem/tests/TestData.cs b/src/System.IO.FileSystem/tests/TestData.cs
index 3ca4af5160..69b4373bd9 100644
--- a/src/System.IO.FileSystem/tests/TestData.cs
+++ b/src/System.IO.FileSystem/tests/TestData.cs
@@ -57,25 +57,12 @@ internal static class TestData
{
get
{
- TheoryData<string> data = new TheoryData<string>();
-
- // NOTE: That I/O treats "file"/http" specially and throws ArgumentException.
- // Otherwise, it treats all other urls as alternative data streams
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // alternate data streams, drive labels, etc.
- {
- data.Add("\0");
- data.Add("middle\0path");
- data.Add("trailing\0");
- data.Add(@"\\?\");
- data.Add(@"\\?\UNC\");
- data.Add(@"\\?\UNC\LOCALHOST");
- }
- else
+ TheoryData<string> data = new TheoryData<string>
{
- data.Add("\0");
- data.Add("middle\0path");
- data.Add("trailing\0");
- }
+ "\0",
+ "middle\0path",
+ "trailing\0"
+ };
foreach (char c in s_invalidFileNameChars)
{