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
path: root/src
diff options
context:
space:
mode:
authorJeremy Kuhne <jeremy.kuhne@microsoft.com>2017-05-19 23:20:11 +0300
committerGitHub <noreply@github.com>2017-05-19 23:20:11 +0300
commit801dde95a5eac06140d0ac633ac3f9bfdd25aca5 (patch)
treef5bedd525be96f65ab99472a5546155c3399f92c /src
parent5350d4e1321d84df1e377060710fae8f58ca0149 (diff)
Fix Unix missing file state (#19959)
* Fix Unix missing file state In the original .NET implementation a missing file never throws directly with FileInfo. Attributes get the error result from Windows (-1) and times aren't initialized. * Feedback * Change FileSystemTest derivation FileSystemTest should derive from RemoteFileSystemTest so we can have one base class. * Add RemoteExecutorBase to performance test project * Address test feedback * Fix File.GetAttributes on Unix NetFX doesn't rely on FileInfo and throws when getting attributes on missing files through File.
Diffstat (limited to 'src')
-rw-r--r--src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs73
-rw-r--r--src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Windows.cs4
-rw-r--r--src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs7
-rw-r--r--src/System.IO.FileSystem/src/System/IO/Win32FileSystem.cs13
-rw-r--r--src/System.IO.FileSystem/tests/Directory/Delete.cs14
-rw-r--r--src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str.cs14
-rw-r--r--src/System.IO.FileSystem/tests/Directory/GetSetTimes.cs30
-rw-r--r--src/System.IO.FileSystem/tests/DirectoryInfo/Exists.cs8
-rw-r--r--src/System.IO.FileSystem/tests/DirectoryInfo/GetSetAttributes.cs29
-rw-r--r--src/System.IO.FileSystem/tests/File/GetSetAttributes.cs24
-rw-r--r--src/System.IO.FileSystem/tests/File/GetSetTimes.cs30
-rw-r--r--src/System.IO.FileSystem/tests/FileInfo/Exists.cs8
-rw-r--r--src/System.IO.FileSystem/tests/FileInfo/GetSetAttributes.cs27
-rw-r--r--src/System.IO.FileSystem/tests/FileInfo/IsReadOnly.cs70
-rw-r--r--src/System.IO.FileSystem/tests/FileInfo/Length.cs23
-rw-r--r--src/System.IO.FileSystem/tests/FileStream/ctor_str_fm.cs18
-rw-r--r--src/System.IO.FileSystem/tests/FileSystemTest.cs9
-rw-r--r--src/System.IO.FileSystem/tests/Performance/System.IO.FileSystem.Performance.Tests.csproj3
-rw-r--r--src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj5
-rw-r--r--src/System.IO.FileSystem/tests/TestData.cs22
20 files changed, 326 insertions, 105 deletions
diff --git a/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs b/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs
index 28db0fbe09..647bcd3c82 100644
--- a/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs
+++ b/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs
@@ -12,6 +12,11 @@ namespace System.IO
private bool _targetOfSymlinkIsDirectory;
/// <summary>
+ /// Exists as a path as of last refresh.
+ /// </summary>
+ private bool _exists;
+
+ /// <summary>
/// Whether we've successfully cached a stat structure.
/// -1 if we need to refresh _fileStatus, 0 if we've successfully cached one,
/// or any other value that serves as an errno error code from the
@@ -35,6 +40,9 @@ namespace System.IO
{
EnsureStatInitialized();
+ if (!_exists)
+ return (FileAttributes)(-1);
+
FileAttributes attrs = default(FileAttributes);
if (IsDirectoryAssumesInitialized) // this is the one attribute where we follow symlinks
@@ -85,11 +93,31 @@ namespace System.IO
// The only thing we can reasonably change is whether the file object is readonly,
// just changing its permissions accordingly.
EnsureStatInitialized();
+
+ if (!_exists)
+ {
+ ThrowNotFound(FullPath);
+ }
+
IsReadOnlyAssumesInitialized = (value & FileAttributes.ReadOnly) != 0;
_fileStatusInitialized = -1;
}
}
+ internal static void ThrowNotFound(string path)
+ {
+ // Windows distinguishes between whether the directory or the file isn't found,
+ // and throws a different exception in these cases. We attempt to approximate that
+ // here; there is a race condition here, where something could change between
+ // when the error occurs and our checks, but it's the best we can do, and the
+ // worst case in such a race condition (which could occur if the file system is
+ // being manipulated concurrently with these checks) is that we throw a
+ // FileNotFoundException instead of DirectoryNotFoundException.
+
+ bool directoryError = !Directory.Exists(Path.GetDirectoryName(PathHelpers.TrimEndingDirectorySeparator(path)));
+ throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(Interop.Error.ENOENT), path, directoryError);
+ }
+
/// <summary>Gets whether stat reported this system object as a directory.</summary>
private bool IsDirectoryAssumesInitialized =>
(_fileStatus.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR ||
@@ -161,7 +189,7 @@ namespace System.IO
}
return
- _fileStatusInitialized == 0 && // avoid throwing if Refresh failed; instead just return false
+ _exists &&
(this is DirectoryInfo) == IsDirectoryAssumesInitialized;
}
}
@@ -171,6 +199,9 @@ namespace System.IO
get
{
EnsureStatInitialized();
+ if (!_exists)
+ return DateTimeOffset.FromFileTime(0);
+
long rawTime = (_fileStatus.Flags & Interop.Sys.FileStatusFlags.HasBirthTime) != 0 ?
_fileStatus.BirthTime :
Math.Min(_fileStatus.CTime, _fileStatus.MTime); // fall back to the oldest time we have in between change and modify time
@@ -190,6 +221,8 @@ namespace System.IO
get
{
EnsureStatInitialized();
+ if (!_exists)
+ return DateTimeOffset.FromFileTime(0);
return DateTimeOffset.FromUnixTimeSeconds(_fileStatus.ATime).ToLocalTime();
}
set { SetAccessWriteTimes(value.ToUnixTimeSeconds(), null); }
@@ -200,6 +233,8 @@ namespace System.IO
get
{
EnsureStatInitialized();
+ if (!_exists)
+ return DateTimeOffset.FromFileTime(0);
return DateTimeOffset.FromUnixTimeSeconds(_fileStatus.MTime).ToLocalTime();
}
set { SetAccessWriteTimes(null, value.ToUnixTimeSeconds()); }
@@ -236,17 +271,32 @@ namespace System.IO
// storing those results separately. We only report failure if the initial
// lstat fails, as a broken symlink should still report info on exists, attributes, etc.
_targetOfSymlinkIsDirectory = false;
- int result = Interop.Sys.LStat(FullPath, out _fileStatus);
+ string path = PathHelpers.TrimEndingDirectorySeparator(FullPath);
+ int result = Interop.Sys.LStat(path, out _fileStatus);
if (result < 0)
{
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
- _fileStatusInitialized = errorInfo.RawErrno;
+
+ // This should never set the error if the file can't be found.
+ // (see the Windows refresh passing returnErrorOnNotFound: false).
+ if (errorInfo.Error == Interop.Error.ENOENT
+ || errorInfo.Error == Interop.Error.ENOTDIR)
+ {
+ _fileStatusInitialized = 0;
+ _exists = false;
+ }
+ else
+ {
+ _fileStatusInitialized = errorInfo.RawErrno;
+ }
return;
}
+ _exists = true;
+
Interop.Sys.FileStatus targetStatus;
if ((_fileStatus.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFLNK &&
- Interop.Sys.Stat(FullPath, out targetStatus) >= 0)
+ Interop.Sys.Stat(path, out targetStatus) >= 0)
{
_targetOfSymlinkIsDirectory = (targetStatus.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR;
}
@@ -265,20 +315,7 @@ namespace System.IO
{
int errno = _fileStatusInitialized;
_fileStatusInitialized = -1;
- var errorInfo = new Interop.ErrorInfo(errno);
-
- // Windows distinguishes between whether the directory or the file isn't found,
- // and throws a different exception in these cases. We attempt to approximate that
- // here; there is a race condition here, where something could change between
- // when the error occurs and our checks, but it's the best we can do, and the
- // worst case in such a race condition (which could occur if the file system is
- // being manipulated concurrently with these checks) is that we throw a
- // FileNotFoundException instead of DirectoryNotFoundexception.
-
- // directoryError is true only if a FileNotExists error was provided and the parent
- // directory of the file represented by _fullPath is nonexistent
- bool directoryError = (errorInfo.Error == Interop.Error.ENOENT && !Directory.Exists(Path.GetDirectoryName(PathHelpers.TrimEndingDirectorySeparator(FullPath)))); // The destFile's path is invalid
- throw Interop.GetExceptionForIoErrno(errorInfo, FullPath, directoryError);
+ throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(errno), FullPath);
}
}
}
diff --git a/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Windows.cs b/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Windows.cs
index 46aacb6420..3c6aa499c5 100644
--- a/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Windows.cs
+++ b/src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Windows.cs
@@ -106,7 +106,7 @@ namespace System.IO
get
{
EnsureDataInitialized();
- return ((long)_data.fileSizeHigh) << 32 | ((long)_data.fileSizeLow & 0xFFFFFFFFL);
+ return ((long)_data.fileSizeHigh) << 32 | _data.fileSizeLow & 0xFFFFFFFFL;
}
}
@@ -126,7 +126,7 @@ namespace System.IO
{
// This should not throw, instead we store the result so that we can throw it
// when someone actually accesses a property
- _dataInitialized = Win32FileSystem.FillAttributeInfo(FullPath, ref _data, false, false);
+ _dataInitialized = Win32FileSystem.FillAttributeInfo(FullPath, ref _data, tryagain: false, returnErrorOnNotFound: false);
}
}
}
diff --git a/src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs b/src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs
index 9d2ef672bc..1d25e9c3b0 100644
--- a/src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs
+++ b/src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs
@@ -672,7 +672,12 @@ namespace System.IO
public override FileAttributes GetAttributes(string fullPath)
{
- return new FileInfo(fullPath, null).Attributes;
+ FileAttributes attributes = new FileInfo(fullPath, null).Attributes;
+
+ if (attributes == (FileAttributes)(-1))
+ FileSystemInfo.ThrowNotFound(fullPath);
+
+ return attributes;
}
public override void SetAttributes(string fullPath, FileAttributes attributes)
diff --git a/src/System.IO.FileSystem/src/System/IO/Win32FileSystem.cs b/src/System.IO.FileSystem/src/System/IO/Win32FileSystem.cs
index 11c27349d8..a2a6733554 100644
--- a/src/System.IO.FileSystem/src/System/IO/Win32FileSystem.cs
+++ b/src/System.IO.FileSystem/src/System/IO/Win32FileSystem.cs
@@ -234,10 +234,13 @@ namespace System.IO
// Remove trailing slash since this can cause grief to FindFirstFile. You will get an invalid argument error
String tempPath = path.TrimEnd(PathHelpers.DirectorySeparatorChars);
- // For floppy drives, normally the OS will pop up a dialog saying
- // there is no disk in drive A:, please insert one. We don't want that.
- // SetErrorMode will let us disable this, but we should set the error
- // mode back, since this may have wide-ranging effects.
+ // For removable media drives, normally the OS will pop up a dialog requesting insertion
+ // of the relevant media (CD, floppy, memory card, etc.). We don't want this prompt so we
+ // set SEM_FAILCRITICALERRORS to suppress it.
+ //
+ // Note that said dialog only shows once the relevant filesystem has been loaded, which
+ // does not happen until actual media is accessed at least once since booting.
+
uint oldMode;
bool success = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode);
try
@@ -253,7 +256,7 @@ namespace System.IO
if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND ||
errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND ||
- errorCode == Interop.Errors.ERROR_NOT_READY) // floppy device not ready
+ errorCode == Interop.Errors.ERROR_NOT_READY) // Removable media not inserted
{
if (!returnErrorOnNotFound)
{
diff --git a/src/System.IO.FileSystem/tests/Directory/Delete.cs b/src/System.IO.FileSystem/tests/Directory/Delete.cs
index b88478abbb..23721d88e9 100644
--- a/src/System.IO.FileSystem/tests/Directory/Delete.cs
+++ b/src/System.IO.FileSystem/tests/Directory/Delete.cs
@@ -75,10 +75,18 @@ namespace System.IO.Tests
Assert.False(testDir.Exists);
}
- [Fact]
- public void ShouldThrowDirectoryNotFoundExceptionForNonexistentDirectory()
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void MissingFile_ThrowsDirectoryNotFound(char trailingChar)
+ {
+ string path = GetTestFilePath() + trailingChar;
+ Assert.Throws<DirectoryNotFoundException>(() => Delete(path));
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void MissingDirectory_ThrowsDirectoryNotFound(char trailingChar)
{
- Assert.Throws<DirectoryNotFoundException>(() => Delete(GetTestFilePath()));
+ string path = Path.Combine(GetTestFilePath(), "file" + trailingChar);
+ Assert.Throws<DirectoryNotFoundException>(() => Delete(path));
}
[Fact]
diff --git a/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str.cs b/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str.cs
index 1e5da1a05c..e6c9b74ef1 100644
--- a/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str.cs
+++ b/src/System.IO.FileSystem/tests/Directory/GetFileSystemEntries_str.cs
@@ -137,10 +137,18 @@ namespace System.IO.Tests
}
}
- [Fact]
- public void NonexistentPath()
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void MissingFile_ThrowsDirectoryNotFound(char trailingChar)
+ {
+ string path = GetTestFilePath() + trailingChar;
+ Assert.Throws<DirectoryNotFoundException>(() => GetEntries(path));
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void MissingDirectory_ThrowsDirectoryNotFound(char trailingChar)
{
- Assert.Throws<DirectoryNotFoundException>(() => GetEntries(GetTestFilePath()));
+ string path = Path.Combine(GetTestFilePath(), "file" + trailingChar);
+ Assert.Throws<DirectoryNotFoundException>(() => GetEntries(path));
}
[Fact]
diff --git a/src/System.IO.FileSystem/tests/Directory/GetSetTimes.cs b/src/System.IO.FileSystem/tests/Directory/GetSetTimes.cs
index 3e94d573fa..f5978b4aab 100644
--- a/src/System.IO.FileSystem/tests/Directory/GetSetTimes.cs
+++ b/src/System.IO.FileSystem/tests/Directory/GetSetTimes.cs
@@ -120,7 +120,6 @@ namespace System.IO.Tests
}
[Fact]
- [PlatformSpecific(TestPlatforms.Windows)] // Non-existing directory returns default values
public void Windows_DirectoryDoesntExist_ReturnDefaultValues()
{
string path = GetTestFilePath();
@@ -147,34 +146,5 @@ namespace System.IO.Tests
Assert.Equal(DateTime.FromFileTimeUtc(0).Ticks, new DirectoryInfo(path).CreationTimeUtc.Ticks);
}
}
-
- [Fact]
- [PlatformSpecific(TestPlatforms.AnyUnix)] // Non-existing directory causes FileNotFoundException
- public void Unix_DirectoryDoesntExist_Throws()
- {
- string path = GetTestFilePath();
-
- //non-utc
- Assert.Throws<FileNotFoundException>(() => Directory.GetLastAccessTime(path));
- Assert.Throws<FileNotFoundException>(() => new DirectoryInfo(path).LastAccessTime);
- Assert.Throws<FileNotFoundException>(() => Directory.GetLastWriteTime(path));
- Assert.Throws<FileNotFoundException>(() => new DirectoryInfo(path).LastWriteTime);
- if (IOInputs.SupportsGettingCreationTime)
- {
- Assert.Throws<FileNotFoundException>(() => Directory.GetCreationTime(path));
- Assert.Throws<FileNotFoundException>(() => new DirectoryInfo(path).CreationTime);
- }
-
- //utc
- Assert.Throws<FileNotFoundException>(() => Directory.GetLastAccessTimeUtc(path));
- Assert.Throws<FileNotFoundException>(() => new DirectoryInfo(path).LastAccessTimeUtc);
- Assert.Throws<FileNotFoundException>(() => Directory.GetLastWriteTimeUtc(path));
- Assert.Throws<FileNotFoundException>(() => new DirectoryInfo(path).LastWriteTimeUtc);
- if (IOInputs.SupportsGettingCreationTime)
- {
- Assert.Throws<FileNotFoundException>(() => Directory.GetCreationTimeUtc(path));
- Assert.Throws<FileNotFoundException>(() => new DirectoryInfo(path).CreationTimeUtc);
- }
- }
}
}
diff --git a/src/System.IO.FileSystem/tests/DirectoryInfo/Exists.cs b/src/System.IO.FileSystem/tests/DirectoryInfo/Exists.cs
index e94775c156..d54f799cf0 100644
--- a/src/System.IO.FileSystem/tests/DirectoryInfo/Exists.cs
+++ b/src/System.IO.FileSystem/tests/DirectoryInfo/Exists.cs
@@ -55,6 +55,14 @@ namespace System.IO.Tests
Assert.False(new DirectoryInfo("Da drar vi til fjells").Exists);
}
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void MissingDirectory(char trailingChar)
+ {
+ string path = GetTestFilePath();
+ FileInfo info = new FileInfo(Path.Combine(path, "file" + trailingChar));
+ Assert.False(info.Exists);
+ }
+
[Fact]
[PlatformSpecific(CaseInsensitivePlatforms)]
public void CaseInsensitivity()
diff --git a/src/System.IO.FileSystem/tests/DirectoryInfo/GetSetAttributes.cs b/src/System.IO.FileSystem/tests/DirectoryInfo/GetSetAttributes.cs
index 9910c3f1b3..02b816d679 100644
--- a/src/System.IO.FileSystem/tests/DirectoryInfo/GetSetAttributes.cs
+++ b/src/System.IO.FileSystem/tests/DirectoryInfo/GetSetAttributes.cs
@@ -38,12 +38,31 @@ namespace System.IO.Tests
Assert.Throws<ArgumentException>(() => Set(string.Empty, FileAttributes.Normal));
}
- [Fact]
- public void NonExistentFile()
+ // In NetFX we ignore "not found" errors, which leaves the attributes
+ // state as invalid (0xFFFFFFFF), which makes all flags true.
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void GetAttributes_MissingFile(char trailingChar)
+ {
+ Assert.Equal((FileAttributes)(-1), Get(GetTestFilePath() + trailingChar));
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void GetAttributes_MissingDirectory(char trailingChar)
+ {
+ Assert.Equal((FileAttributes)(-1), Get(Path.Combine(GetTestFilePath(), "file" + trailingChar)));
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void SetAttributes_MissingFile(char trailingChar)
+ {
+ Assert.Throws<FileNotFoundException>(() => Set(GetTestFilePath() + trailingChar, FileAttributes.ReadOnly));
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void SetAttributes_MissingDirectory(char trailingChar)
{
- Assert.Throws<FileNotFoundException>(() => Set(GetTestFilePath(), FileAttributes.Normal));
- Assert.Throws<FileNotFoundException>(() => Set(IOServices.AddTrailingSlashIfNeeded(GetTestFilePath()), FileAttributes.Normal));
- Assert.Throws<FileNotFoundException>(() => Set(IOServices.RemoveTrailingSlash(GetTestFilePath()), FileAttributes.Normal));
+ Assert.Throws<DirectoryNotFoundException>(() => Set(Path.Combine(GetTestFilePath(), "file" + trailingChar), FileAttributes.ReadOnly));
}
[Theory]
diff --git a/src/System.IO.FileSystem/tests/File/GetSetAttributes.cs b/src/System.IO.FileSystem/tests/File/GetSetAttributes.cs
index ca5f06a2d0..3e3ab2563d 100644
--- a/src/System.IO.FileSystem/tests/File/GetSetAttributes.cs
+++ b/src/System.IO.FileSystem/tests/File/GetSetAttributes.cs
@@ -137,5 +137,29 @@ namespace System.IO.Tests
Set(path, Get(path) & ~FileAttributes.ReadOnly);
}
}
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void GetAttributes_MissingFile(char trailingChar)
+ {
+ Assert.Throws<FileNotFoundException>(() => Get(GetTestFilePath() + trailingChar));
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void GetAttributes_MissingDirectory(char trailingChar)
+ {
+ Assert.Throws<DirectoryNotFoundException>(() => Get(Path.Combine(GetTestFilePath(), "dir" + trailingChar)));
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void SetAttributes_MissingFile(char trailingChar)
+ {
+ Assert.Throws<FileNotFoundException>(() => Set(GetTestFilePath() + trailingChar, FileAttributes.ReadOnly));
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void SetAttributes_MissingDirectory(char trailingChar)
+ {
+ Assert.Throws<DirectoryNotFoundException>(() => Set(Path.Combine(GetTestFilePath(), "dir" + trailingChar), FileAttributes.ReadOnly));
+ }
}
}
diff --git a/src/System.IO.FileSystem/tests/File/GetSetTimes.cs b/src/System.IO.FileSystem/tests/File/GetSetTimes.cs
index 89a83ccf53..a89906b145 100644
--- a/src/System.IO.FileSystem/tests/File/GetSetTimes.cs
+++ b/src/System.IO.FileSystem/tests/File/GetSetTimes.cs
@@ -121,7 +121,6 @@ namespace System.IO.Tests
}
[Fact]
- [PlatformSpecific(TestPlatforms.Windows)] // Non-existing file returns default values
public void Windows_FileDoesntExist_ReturnDefaultValues()
{
string path = GetTestFilePath();
@@ -148,34 +147,5 @@ namespace System.IO.Tests
Assert.Equal(DateTime.FromFileTimeUtc(0).Ticks, new FileInfo(path).CreationTimeUtc.Ticks);
}
}
-
- [Fact]
- [PlatformSpecific(TestPlatforms.AnyUnix)] // Non-existing file throws FileNotFoundException
- public void Unix_FileDoesntExist_Throws_FileNotFoundException()
- {
- string path = GetTestFilePath();
-
- //non-utc
- Assert.Throws<FileNotFoundException>(() => File.GetLastAccessTime(path));
- Assert.Throws<FileNotFoundException>(() => new FileInfo(path).LastAccessTime);
- Assert.Throws<FileNotFoundException>(() => File.GetLastWriteTime(path));
- Assert.Throws<FileNotFoundException>(() => new FileInfo(path).LastWriteTime);
- if (IOInputs.SupportsGettingCreationTime)
- {
- Assert.Throws<FileNotFoundException>(() => File.GetCreationTime(path));
- Assert.Throws<FileNotFoundException>(() => new FileInfo(path).CreationTime);
- }
-
- //utc
- Assert.Throws<FileNotFoundException>(() => File.GetLastAccessTimeUtc(path));
- Assert.Throws<FileNotFoundException>(() => new FileInfo(path).LastAccessTimeUtc);
- Assert.Throws<FileNotFoundException>(() => File.GetLastWriteTimeUtc(path));
- Assert.Throws<FileNotFoundException>(() => new FileInfo(path).LastWriteTimeUtc);
- if (IOInputs.SupportsGettingCreationTime)
- {
- Assert.Throws<FileNotFoundException>(() => File.GetCreationTimeUtc(path));
- Assert.Throws<FileNotFoundException>(() => new FileInfo(path).CreationTimeUtc);
- }
- }
}
}
diff --git a/src/System.IO.FileSystem/tests/FileInfo/Exists.cs b/src/System.IO.FileSystem/tests/FileInfo/Exists.cs
index ab075a6e43..a7deb781b1 100644
--- a/src/System.IO.FileSystem/tests/FileInfo/Exists.cs
+++ b/src/System.IO.FileSystem/tests/FileInfo/Exists.cs
@@ -43,6 +43,14 @@ namespace System.IO.Tests
Assert.False(new FileInfo("Da drar vi til fjells").Exists);
}
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void MissingDirectory(char trailingChar)
+ {
+ string path = GetTestFilePath();
+ FileInfo info = new FileInfo(Path.Combine(path, "file" + trailingChar));
+ Assert.False(info.Exists);
+ }
+
[Fact]
[PlatformSpecific(CaseInsensitivePlatforms)]
public void CaseInsensitivity()
diff --git a/src/System.IO.FileSystem/tests/FileInfo/GetSetAttributes.cs b/src/System.IO.FileSystem/tests/FileInfo/GetSetAttributes.cs
index 81eb880cd1..c32a0da379 100644
--- a/src/System.IO.FileSystem/tests/FileInfo/GetSetAttributes.cs
+++ b/src/System.IO.FileSystem/tests/FileInfo/GetSetAttributes.cs
@@ -122,5 +122,32 @@ namespace System.IO.Tests
Set(path, attr);
Assert.Equal(FileAttributes.Normal, Get(path));
}
+
+ // In NetFX we ignore "not found" errors, which leaves the attributes
+ // state as invalid (0xFFFFFFFF), which makes all flags true.
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void GetAttributes_MissingFile(char trailingChar)
+ {
+ Assert.Equal((FileAttributes)(-1), Get(GetTestFilePath() + trailingChar));
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void GetAttributes_MissingDirectory(char trailingChar)
+ {
+ Assert.Equal((FileAttributes)(-1), Get(Path.Combine(GetTestFilePath(), "file" + trailingChar)));
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void SetAttributes_MissingFile(char trailingChar)
+ {
+ Assert.Throws<FileNotFoundException>(() => Set(GetTestFilePath() + trailingChar, FileAttributes.ReadOnly));
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void SetAttributes_MissingDirectory(char trailingChar)
+ {
+ Assert.Throws<DirectoryNotFoundException>(() => Set(Path.Combine(GetTestFilePath(), "file" + trailingChar), FileAttributes.ReadOnly));
+ }
}
}
diff --git a/src/System.IO.FileSystem/tests/FileInfo/IsReadOnly.cs b/src/System.IO.FileSystem/tests/FileInfo/IsReadOnly.cs
new file mode 100644
index 0000000000..2833dc6a09
--- /dev/null
+++ b/src/System.IO.FileSystem/tests/FileInfo/IsReadOnly.cs
@@ -0,0 +1,70 @@
+// 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 Xunit;
+
+namespace System.IO.Tests
+{
+ public class IsReadOnly : FileSystemTest
+ {
+ protected virtual FileSystemInfo Create(string path)
+ {
+ return new FileInfo(path);
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void NotReadOnly(char trailingChar)
+ {
+ string path = GetTestFilePath() + trailingChar;
+ File.Create(path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)).Dispose();
+ FileInfo info = new FileInfo(path);
+ Assert.False(info.IsReadOnly);
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void ReadOnly(char trailingChar)
+ {
+ string path = GetTestFilePath() + trailingChar;
+ string trimmedPath = path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
+ File.Create(trimmedPath).Dispose();
+ var attributes = File.GetAttributes(path);
+ File.SetAttributes(trimmedPath, attributes | FileAttributes.ReadOnly);
+ FileInfo info = null;
+
+ try
+ {
+ info = new FileInfo(path);
+ Assert.True(info.IsReadOnly);
+ }
+ finally
+ {
+ File.SetAttributes(trimmedPath, attributes);
+ }
+
+ Assert.True(info.IsReadOnly);
+ info.Refresh();
+ Assert.Equal(attributes, File.GetAttributes(path));
+ Assert.False(info.IsReadOnly);
+ }
+
+ // In NetFX we ignore "not found" errors, which leaves the attributes
+ // state as invalid (0xFFFFFFFF), which makes all flags true.
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void MissingFile(char trailingChar)
+ {
+ string path = GetTestFilePath();
+ FileInfo info = new FileInfo(path + trailingChar);
+ Assert.True(info.IsReadOnly);
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void MissingDirectory(char trailingChar)
+ {
+ string path = GetTestFilePath();
+ FileInfo info = new FileInfo(Path.Combine(path, "file" + trailingChar));
+ Assert.True(info.IsReadOnly);
+ }
+ }
+}
diff --git a/src/System.IO.FileSystem/tests/FileInfo/Length.cs b/src/System.IO.FileSystem/tests/FileInfo/Length.cs
index 3ae14731c5..2b09ad576d 100644
--- a/src/System.IO.FileSystem/tests/FileInfo/Length.cs
+++ b/src/System.IO.FileSystem/tests/FileInfo/Length.cs
@@ -42,6 +42,29 @@ namespace System.IO.Tests
Assert.Throws<FileNotFoundException>(() => info.Length);
}
+ // Windows returns FILE_NOT_FOUND if the path exists up to the last directory separator,
+ // or PATH_NOT_FOUND otherwise. Normally we convert those to FileNotFound and
+ // DirectoryNotFound exceptions, but in this particular case we ignored the actual
+ // result and always gave FileNotFound.
+ //
+ // https://github.com/dotnet/corefx/issues/19850
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void Length_MissingFile_ThrowsFileNotFound(char trailingChar)
+ {
+ string path = GetTestFilePath();
+ FileInfo info = new FileInfo(path + trailingChar);
+ Assert.Throws<FileNotFoundException>(() => info.Length);
+ }
+
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void Length_MissingDirectory_ThrowsFileNotFound(char trailingChar)
+ {
+ string path = GetTestFilePath();
+ FileInfo info = new FileInfo(Path.Combine(path, "file" + trailingChar));
+ Assert.Throws<FileNotFoundException>(() => info.Length);
+ }
+
[ConditionalFact(nameof(CanCreateSymbolicLinks))]
public void SymLinkLength()
{
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 6470b7a99f..cbb3af9594 100644
--- a/src/System.IO.FileSystem/tests/FileStream/ctor_str_fm.cs
+++ b/src/System.IO.FileSystem/tests/FileStream/ctor_str_fm.cs
@@ -2,12 +2,11 @@
// 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;
namespace System.IO.Tests
{
- public class FileStream_ctor_str_fm : RemoteExecutorTestBase
+ public class FileStream_ctor_str_fm : FileSystemTest
{
protected virtual FileStream CreateFileStream(string path, FileMode mode)
{
@@ -38,6 +37,21 @@ namespace System.IO.Tests
AssertExtensions.Throws<ArgumentOutOfRangeException>("mode", () => CreateFileStream(GetTestFilePath(), ~FileMode.Open));
}
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void MissingFile_ThrowsFileNotFound(char trailingChar)
+ {
+ string path = GetTestFilePath() + trailingChar;
+ Assert.Throws<FileNotFoundException>(() => CreateFileStream(path, FileMode.Open));
+ }
+
+ [ActiveIssue(19965, TestPlatforms.AnyUnix)]
+ [Theory, MemberData(nameof(TrailingCharacters))]
+ public void MissingDirectory_ThrowsDirectoryNotFound(char trailingChar)
+ {
+ string path = Path.Combine(GetTestFilePath(), "file" + trailingChar);
+ Assert.Throws<DirectoryNotFoundException>(() => CreateFileStream(path, FileMode.Open));
+ }
+
[Fact]
public void FileModeCreate()
{
diff --git a/src/System.IO.FileSystem/tests/FileSystemTest.cs b/src/System.IO.FileSystem/tests/FileSystemTest.cs
index 1386fcbf23..07aaef81f6 100644
--- a/src/System.IO.FileSystem/tests/FileSystemTest.cs
+++ b/src/System.IO.FileSystem/tests/FileSystemTest.cs
@@ -2,11 +2,12 @@
// 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;
namespace System.IO.Tests
{
- public abstract partial class FileSystemTest : FileCleanupTestBase
+ public abstract partial class FileSystemTest : RemoteExecutorTestBase
{
public static readonly byte[] TestBuffer = { 0xBA, 0x5E, 0xBA, 0x11, 0xF0, 0x07, 0xBA, 0x11 };
@@ -19,9 +20,9 @@ namespace System.IO.Tests
public static bool UsingNewNormalization => !PathFeatures.IsUsingLegacyPathNormalization();
- public static TheoryData<string> PathsWithInvalidColons => TestData.PathsWithInvalidColons;
-
- public static TheoryData<string> PathsWithInvalidCharacters => TestData.PathsWithInvalidCharacters;
+ public static TheoryData<string> PathsWithInvalidColons = TestData.PathsWithInvalidColons;
+ public static TheoryData<string> PathsWithInvalidCharacters = TestData.PathsWithInvalidCharacters;
+ public static TheoryData<char> TrailingCharacters = TestData.TrailingCharacters;
/// <summary>
/// In some cases (such as when running without elevated privileges),
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 2d9fd50a0f..85258bbfe6 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
@@ -29,6 +29,9 @@
<Compile Include="$(CommonTestPath)\System\IO\PathFeatures.cs">
<Link>Common\System\IO\PathFeatures.cs</Link>
</Compile>
+ <Compile Include="$(CommonTestPath)\System\Diagnostics\RemoteExecutorTestBase.cs">
+ <Link>Common\System\Diagnostics\RemoteExecutorTestBase.cs</Link>
+ </Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(CommonPath)\..\perf\PerfRunner\PerfRunner.csproj">
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 6b1bf119ec..02cbadaf0d 100644
--- a/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
+++ b/src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
@@ -14,12 +14,14 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Windows_NT-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Windows_NT-Release|AnyCPU'" />
<ItemGroup>
+ <Compile Include="FileInfo\IsReadOnly.cs" />
<Compile Include="FileInfo\Replace.cs" />
<Compile Include="FileStream\Handle.cs" />
<Compile Include="Directory\GetLogicalDrives.cs" />
<Compile Include="FileStream\EndRead.cs" />
<Compile Include="FileStream\EndWrite.cs" />
<Compile Include="FileStream\LockUnlock.cs" />
+ <Compile Include="FileSystemTest.cs" />
<Compile Include="File\EncryptDecrypt.cs" />
<Compile Include="File\Replace.cs" />
<Compile Include="$(CommonTestPath)\System\Runtime\Serialization\Formatters\BinaryFormatterHelpers.cs">
@@ -98,7 +100,6 @@
<Compile Include="TestData.cs" />
<Compile Include="UnseekableFileStream.cs" />
<Compile Include="FSAssert.cs" />
- <Compile Include="FileSystemTest.cs" />
<!-- Ported -->
<Compile Include="PortedCommon\CommonUtilities.cs" />
<Compile Include="PortedCommon\DllImports.cs" />
@@ -175,4 +176,4 @@
</ProjectReference>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project>
+</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 148c163509..3ca4af5160 100644
--- a/src/System.IO.FileSystem/tests/TestData.cs
+++ b/src/System.IO.FileSystem/tests/TestData.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.IO;
using System.Runtime.InteropServices;
using Xunit;
@@ -84,4 +85,25 @@ internal static class TestData
return data;
}
}
+
+ /// <summary>
+ /// Normal path char and any valid directory separators
+ /// </summary>
+ public static TheoryData<char> TrailingCharacters
+ {
+ get
+ {
+ TheoryData<char> data = new TheoryData<char>
+ {
+ // A valid, non separator
+ 'a',
+ Path.DirectorySeparatorChar
+ };
+
+ if (Path.DirectorySeparatorChar != Path.AltDirectorySeparatorChar)
+ data.Add(Path.AltDirectorySeparatorChar);
+
+ return data;
+ }
+ }
}