Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 2678fc7

Browse files
author
Ian Hays
committed
Merge pull request #2512 from ianhays/master
Final major update to the tests for System.IO.FileSystem
2 parents f3fa757 + 481d5c6 commit 2678fc7

File tree

108 files changed

+2355
-9150
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+2355
-9150
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
7+
internal static partial class Interop
8+
{
9+
internal static partial class libc
10+
{
11+
[DllImport(Libraries.Libc, SetLastError = true)]
12+
internal static extern int link(string oldpath, string newpath);
13+
}
14+
}

src/System.IO.FileSystem/src/System.IO.FileSystem.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,9 @@
331331
<Compile Include="$(CommonPath)\Interop\Unix\libc\Interop.utime.cs">
332332
<Link>Common\Interop\Unix\Interop.utime.cs</Link>
333333
</Compile>
334+
<Compile Include="$(CommonPath)\Interop\Unix\libc\Interop.link.cs">
335+
<Link>Common\Interop\Unix\Interop.link.cs</Link>
336+
</Compile>
334337
<Compile Include="$(CommonPath)\Interop\Unix\libc\Interop.unlink.cs">
335338
<Link>Common\Interop\Unix\Interop.unlink.cs</Link>
336339
</Compile>

src/System.IO.FileSystem/src/System/IO/DirectoryInfo.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,13 @@ public void MoveTo(String destDirName)
392392
else
393393
fullSourcePath = FullPath + PathHelpers.DirectorySeparatorCharAsString;
394394

395+
int maxDirectoryPath = FileSystem.Current.MaxDirectoryPath;
396+
if (fullSourcePath.Length >= maxDirectoryPath)
397+
throw new PathTooLongException(SR.IO_PathTooLong);
398+
399+
if (fullDestDirName.Length >= maxDirectoryPath)
400+
throw new PathTooLongException(SR.IO_PathTooLong);
401+
395402
StringComparison pathComparison = PathInternal.GetComparison();
396403
if (String.Equals(fullSourcePath, fullDestDirName, pathComparison))
397404
throw new IOException(SR.IO_SourceDestMustBeDifferent);

src/System.IO.FileSystem/src/System/IO/FileInfo.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,13 +218,10 @@ public override bool Exists
218218
}
219219
}
220220

221-
222-
223-
224221
// User must explicitly specify opening a new file or appending to one.
225222
public FileStream Open(FileMode mode)
226223
{
227-
return Open(mode, FileAccess.ReadWrite, FileShare.None);
224+
return Open(mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.None);
228225
}
229226

230227
public FileStream Open(FileMode mode, FileAccess access)
@@ -271,6 +268,18 @@ public void MoveTo(String destFileName)
271268

272269
String fullDestFileName = PathHelpers.GetFullPathInternal(destFileName);
273270

271+
// These checks are in place to ensure Unix error throwing happens the same way
272+
// as it does on Windows.These checks can be removed if a solution to #2460 is
273+
// found that doesn't require validity checks before making an API call.
274+
if (!new DirectoryInfo(Path.GetDirectoryName(FullName)).Exists)
275+
{
276+
throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, FullName));
277+
}
278+
if (!Exists)
279+
{
280+
throw new FileNotFoundException(SR.Format(SR.IO_FileNotFound_FileName, FullName), FullName);
281+
}
282+
274283
FileSystem.Current.MoveFile(FullPath, fullDestFileName);
275284

276285
FullPath = fullDestFileName;

src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,38 @@ public override void CopyFile(string sourceFullPath, string destFullPath, bool o
5252

5353
public override void MoveFile(string sourceFullPath, string destFullPath)
5454
{
55+
// The desired behavior for Move(source, dest) is to not overwrite the destination file
56+
// if it exists. Since rename(source, dest) will replace the file at 'dest' if it exists,
57+
// a check is added before rename is called. Note that this is a race condition. Possible
58+
// better solutions would be to use link/unlink or platform specific functions instead of
59+
// rename(source, dest). Regardless of the implementation, an IOException should be thrown if
60+
// 'dest' refers to a file that already exists.
61+
if (File.Exists(destFullPath))
62+
throw new IOException(SR.GetResourceString(SR.IO_AlreadyExists_Name, destFullPath));
5563
while (Interop.libc.rename(sourceFullPath, destFullPath) < 0)
5664
{
5765
int errno = Marshal.GetLastWin32Error();
5866
if (errno == Interop.Errors.EINTR) // interrupted; try again
5967
{
6068
continue;
6169
}
62-
else if (errno == Interop.Errors.EXDEV) // rename fails across devices / mount points
70+
else if (errno == Interop.Errors.EXDEV) // link fails across devices / mount points
6371
{
6472
CopyFile(sourceFullPath, destFullPath, overwrite: false);
65-
DeleteFile(sourceFullPath);
6673
break;
6774
}
75+
//else if (errno == Interop.Errors.ENOENT && (!Directory.Exists(Path.GetDirectoryName(sourceFullPath)) || !Directory.Exists(Path.GetDirectoryName(destFullPath)))) // The parent directory for either source or dest can't be found
76+
else if (errno == Interop.Errors.ENOENT && !Directory.Exists(Path.GetDirectoryName(destFullPath))) // The parent directory of destFile can't be found
77+
{
78+
// Windows distinguishes between whether the directory or the file isn't found,
79+
// and throws a different exception in these cases. We attempt to approximate that
80+
// here; there is a race condition here, where something could change between
81+
// when the error occurs and our checks, but it's the best we can do, and the
82+
// worst case in such a race condition (which could occur if the file system is
83+
// being manipulated concurrently with these checks) is that we throw a
84+
// FileNotFoundException instead of DirectoryNotFoundexception.
85+
throw Interop.GetExceptionForIoErrno(errno, destFullPath, isDirectory: true);
86+
}
6887
else
6988
{
7089
throw Interop.GetExceptionForIoErrno(errno);

src/System.IO.FileSystem/src/System/IO/UnixFileSystemObject.cs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -265,20 +265,18 @@ private void EnsureStatInitialized()
265265
int errno = _fileinfoInitialized;
266266
_fileinfoInitialized = -1;
267267

268-
bool failedBecauseOfDirectory = _isDirectory;
269-
if (!failedBecauseOfDirectory && errno == Interop.Errors.ENOENT)
270-
{
271-
// Windows distinguishes between whether the directory or the file isn't found,
272-
// and throws a different exception in these cases. We attempt to approximate that
273-
// here; there is a race condition here, where something could change between
274-
// when the error occurs and our checks, but it's the best we can do, and the
275-
// worst case in such a race condition (which could occur if the file system is
276-
// being manipulated concurrently with these checks) is that we throw a
277-
// FileNotFoundException instead of DirectoryNotFoundexception.
278-
failedBecauseOfDirectory = !Directory.Exists(Path.GetDirectoryName(_fullPath));
279-
}
268+
// Windows distinguishes between whether the directory or the file isn't found,
269+
// and throws a different exception in these cases. We attempt to approximate that
270+
// here; there is a race condition here, where something could change between
271+
// when the error occurs and our checks, but it's the best we can do, and the
272+
// worst case in such a race condition (which could occur if the file system is
273+
// being manipulated concurrently with these checks) is that we throw a
274+
// FileNotFoundException instead of DirectoryNotFoundexception.
280275

281-
throw Interop.GetExceptionForIoErrno(errno, _fullPath, failedBecauseOfDirectory);
276+
// directoryError is true only if a FileNotExists error was provided and the parent
277+
// directory of the file represented by _fullPath is nonexistent
278+
bool directoryError = (errno == Interop.Errors.ENOENT && !Directory.Exists(Path.GetDirectoryName(PathHelpers.TrimEndingDirectorySeparator(_fullPath)))); // The destFile's path is invalid
279+
throw Interop.GetExceptionForIoErrno(errno, _fullPath, directoryError);
282280
}
283281
}
284282
}

src/System.IO.FileSystem/src/System/IO/Win32FileSystem.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,6 @@ public override void CreateDirectory(string fullPath)
5555

5656
int lengthRoot = PathInternal.GetRootLength(fullPath);
5757

58-
// For UNC paths that are only // or ///
59-
if (length == 2 && PathInternal.IsDirectorySeparator(fullPath[1]))
60-
throw new IOException(SR.Format(SR.IO_CannotCreateDirectory, fullPath));
61-
6258
// We can save a bunch of work if the directory we want to create already exists. This also
6359
// saves us in the case where sub paths are inaccessible (due to ERROR_ACCESS_DENIED) but the
6460
// final path is accessable and the directory already exists. For example, consider trying

src/System.IO.FileSystem/tests/Directory/CreateDirectory.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ public void DirectoryWithComponentLongerThanMaxComponentAsPath_ThrowsPathTooLong
204204
});
205205
}
206206

207+
#endregion
208+
209+
#region PlatformSpecific
210+
207211
[Fact]
208212
[PlatformSpecific(PlatformID.Windows)]
209213
public void DirectoryLongerThanMaxPathAsPath_ThrowsPathTooLongException()
@@ -215,10 +219,6 @@ public void DirectoryLongerThanMaxPathAsPath_ThrowsPathTooLongException()
215219
});
216220
}
217221

218-
#endregion
219-
220-
#region PlatformSpecific
221-
222222
[Fact]
223223
[PlatformSpecific(PlatformID.Windows)]
224224
public void DirectoryLongerThanMaxDirectoryAsPath_ThrowsPathTooLongException()
@@ -227,6 +227,7 @@ public void DirectoryLongerThanMaxDirectoryAsPath_ThrowsPathTooLongException()
227227
Assert.All(paths, (path) =>
228228
{
229229
Assert.Throws<PathTooLongException>(() => Create(path));
230+
Directory.Delete(Path.Combine(Path.GetPathRoot(Directory.GetCurrentDirectory()), path.Split(Path.DirectorySeparatorChar)[1]), true);
230231
});
231232
}
232233

@@ -373,6 +374,13 @@ public void UncPathWithoutShareNameAsPath_ThrowsArgumentException()
373374
}
374375
}
375376

377+
[Fact]
378+
[PlatformSpecific(PlatformID.Windows)] // UNC shares
379+
public void UNCPathWithOnlySlashes()
380+
{
381+
Assert.Throws<ArgumentException>(() => Create("//"));
382+
}
383+
376384
[Fact]
377385
[PlatformSpecific(PlatformID.Windows)] // drive labels
378386
public void CDriveCase()

src/System.IO.FileSystem/tests/Directory/EnumerableAPIs.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,51 @@ public override string[] GetEntries(string dirName, string searchPattern)
6969
{
7070
return Directory.EnumerateFileSystemEntries(dirName, searchPattern).ToArray();
7171
}
72+
73+
[Fact]
74+
public void Clone_Enumerator_Empty()
75+
{
76+
DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
77+
var enumerator1 = Directory.EnumerateFileSystemEntries(testDir.FullName);
78+
var enumerator2 = enumerator1;
79+
Assert.Equal(enumerator1.ToArray(), enumerator2.ToArray());
80+
}
81+
82+
[Fact]
83+
public void Clone_Enumerator_Trimmed_SearchPattern()
84+
{
85+
DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
86+
var enumerator1 = Directory.EnumerateFileSystemEntries(testDir.FullName, ((char)0xA).ToString());
87+
var enumerator2 = enumerator1;
88+
Assert.Empty(enumerator1.ToArray());
89+
Assert.Empty(enumerator2.ToArray());
90+
}
91+
92+
[Fact]
93+
public void Delete_Directory_After_Creating_Enumerable()
94+
{
95+
DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
96+
DirectoryInfo subDir1 = Directory.CreateDirectory(Path.Combine(testDir.FullName, "a"));
97+
DirectoryInfo subDir2 = Directory.CreateDirectory(Path.Combine(testDir.FullName, "b"));
98+
var enumerator = Directory.EnumerateDirectories(testDir.FullName);
99+
foreach (var dir in enumerator)
100+
{
101+
Directory.Delete(dir);
102+
}
103+
Assert.Equal(0, enumerator.ToArray().Length);
104+
}
105+
106+
107+
[Fact]
108+
public void Trailing_Slash_Adds_Trailing_Star()
109+
{
110+
// A searchpattern of c:\temp\ will become c:\temp\* internally
111+
DirectoryInfo testDir = Directory.CreateDirectory(GetTestFilePath());
112+
DirectoryInfo subDir1 = Directory.CreateDirectory(Path.Combine(testDir.FullName, "a"));
113+
DirectoryInfo subDir2 = Directory.CreateDirectory(Path.Combine(testDir.FullName, "b"));
114+
var enumerator = Directory.EnumerateDirectories(testDir.FullName, "a" + Path.DirectorySeparatorChar);
115+
Assert.Equal(0, enumerator.ToArray().Length);
116+
}
72117
}
73118

74119
public class Directory_EnumFSE_str_str_so : Directory_GetFileSystemEntries_str_str_so

src/System.IO.FileSystem/tests/Directory/Exists.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using System.Linq;
45
using Xunit;
56

67
namespace System.IO.FileSystem.Tests
@@ -54,9 +55,12 @@ public void ValidPathExists_ReturnsTrue()
5455
public void PathWithInvalidCharactersAsPath_ReturnsFalse()
5556
{
5657
// Checks that errors aren't thrown when calling Exists() on paths with impossible to create characters
58+
char[] trimmed = { (char)0x9, (char)0xA, (char)0xB, (char)0xC, (char)0xD, (char)0x20, (char)0x85, (char)0xA0 };
5759
Assert.All((IOInputs.GetPathsWithInvalidCharacters()), (component) =>
5860
{
5961
Assert.False(Exists(component));
62+
if (!trimmed.Contains(component.ToCharArray()[0]))
63+
Assert.False(Exists(TestDirectory + Path.DirectorySeparatorChar + component));
6064
});
6165
}
6266

0 commit comments

Comments
 (0)