From f1fd023b9e55df50ceea6792a1252f8ff58d2c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Tue, 13 Dec 2022 17:45:14 +0100 Subject: [PATCH] feat: enable .NET 7.0 support (#923) Update interfaces to support new functionality from .NET 7. The implementation in the testing helper is not part of this PR! --- .github/workflows/ci.yml | 1 + Directory.Build.props | 7 +- System.IO.Abstractions.sln | 12 +- ...stem.IO.Abstractions.TestingHelpers.csproj | 2 +- .../System.IO.Abstractions.csproj | 2 +- .../CommonExceptions.cs | 3 + .../MockDirectory.cs | 18 +- .../MockDirectoryInfo.cs | 4 +- .../MockFile.Async.cs | 16 + .../MockFile.cs | 147 ++++- .../MockFileInfo.cs | 4 +- .../MockPath.cs | 8 + ...stem.IO.Abstractions.TestingHelpers.csproj | 2 +- .../DirectoryBase.cs | 12 +- .../DirectoryWrapper.cs | 20 +- .../FileBase.Async.cs | 11 + .../FileBase.cs | 541 ++++-------------- .../FileSystemInfoBase.cs | 15 +- .../FileSystemWatcherBase.cs | 5 + .../FileSystemWatcherWrapper.cs | 8 + .../FileWrapper.Async.cs | 12 + .../FileWrapper.cs | 152 ++++- .../PathBase.cs | 9 +- .../PathWrapper.cs | 12 +- ...eIO.System.IO.Abstractions.Wrappers.csproj | 2 +- .../IDirectory.cs | 12 +- .../IFile.Async.cs | 13 + .../IFile.cs | 96 +++- .../IFileSystemInfo.cs | 14 +- .../IFileSystemWatcher.cs | 5 + .../IPath.cs | 5 + .../TestableIO.System.IO.Abstractions.csproj | 2 +- .../MockFileArgumentPathTests.cs | 29 +- .../MockFileSystemTests.cs | 9 + .../MockPathTests.cs | 58 ++ ...O.Abstractions.TestingHelpers.Tests.csproj | 2 +- .../ApiParityTests.cs | 2 + ...stem.IO.Abstractions.Wrappers.Tests.csproj | 2 +- ...ApiParityTests.DirectoryInfo_.NET 7.0.snap | 9 + .../ApiParityTests.Directory_.NET 7.0.snap | 4 + .../ApiParityTests.DriveInfo_.NET 7.0.snap | 7 + .../ApiParityTests.FileInfo_.NET 7.0.snap | 9 + ...arityTests.FileSystemWatcher_.NET 7.0.snap | 15 + .../ApiParityTests.File_.NET 7.0.snap | 6 + .../ApiParityTests.Path_.NET 7.0.snap | 10 + version.json | 2 +- 46 files changed, 863 insertions(+), 473 deletions(-) create mode 100644 tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.DirectoryInfo_.NET 7.0.snap create mode 100644 tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.Directory_.NET 7.0.snap create mode 100644 tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.DriveInfo_.NET 7.0.snap create mode 100644 tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileInfo_.NET 7.0.snap create mode 100644 tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileSystemWatcher_.NET 7.0.snap create mode 100644 tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.File_.NET 7.0.snap create mode 100644 tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.Path_.NET 7.0.snap diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be26f2749..f6c388a4a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ jobs: 3.1.x 5.0.x 6.0.x + 7.0.x - name: Run tests run: dotnet test --collect:"XPlat Code Coverage" --logger "GitHubActions" - name: Upload coverage diff --git a/Directory.Build.props b/Directory.Build.props index 6e52d00bc..c6bd692f6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -11,9 +11,10 @@ MIT README.md $(DefineConstants);FEATURE_FILE_SYSTEM_ACL_EXTENSIONS - $(DefineConstants);FEATURE_ASYNC_FILE;FEATURE_ENUMERATION_OPTIONS;FEATURE_ADVANCED_PATH_OPERATIONS;FEATURE_PATH_JOIN_WITH_SPAN;FEATURE_SPAN - $(DefineConstants);FEATURE_FILE_MOVE_WITH_OVERWRITE;FEATURE_SUPPORTED_OS_ATTRIBUTE;FEATURE_FILE_SYSTEM_WATCHER_FILTERS;FEATURE_ENDS_IN_DIRECTORY_SEPARATOR;FEATURE_PATH_JOIN_WITH_PARAMS;FEATURE_PATH_JOIN_WITH_FOUR_PATHS - $(DefineConstants);FEATURE_FILE_SYSTEM_INFO_LINK_TARGET;FEATURE_CREATE_SYMBOLIC_LINK;FEATURE_FILESTREAM_OPTIONS + $(DefineConstants);FEATURE_ASYNC_FILE;FEATURE_ENUMERATION_OPTIONS;FEATURE_ADVANCED_PATH_OPERATIONS;FEATURE_PATH_JOIN_WITH_SPAN;FEATURE_SPAN + $(DefineConstants);FEATURE_FILE_MOVE_WITH_OVERWRITE;FEATURE_SUPPORTED_OS_ATTRIBUTE;FEATURE_FILE_SYSTEM_WATCHER_FILTERS;FEATURE_ENDS_IN_DIRECTORY_SEPARATOR;FEATURE_PATH_JOIN_WITH_PARAMS;FEATURE_PATH_JOIN_WITH_FOUR_PATHS + $(DefineConstants);FEATURE_FILE_SYSTEM_INFO_LINK_TARGET;FEATURE_CREATE_SYMBOLIC_LINK;FEATURE_FILESTREAM_OPTIONS + $(DefineConstants);FEATURE_PATH_EXISTS;FEATURE_FILE_SYSTEM_WATCHER_WAIT_WITH_TIMESPAN;FEATURE_FILE_ATTRIBUTES_VIA_HANDLE;FEATURE_CREATE_TEMP_SUBDIRECTORY;FEATURE_READ_LINES_ASYNC;FEATURE_UNIX_FILE_MODE diff --git a/System.IO.Abstractions.sln b/System.IO.Abstractions.sln index 9ae923aab..efa65ba7e 100644 --- a/System.IO.Abstractions.sln +++ b/System.IO.Abstractions.sln @@ -1,4 +1,3 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.3.32825.248 @@ -19,9 +18,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestableIO.System.IO.Abstra EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{C078E0B6-9747-475F-A999-B9E775DF6643}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestableIO.System.IO.Abstractions.TestingHelpers", "src\TestableIO.System.IO.Abstractions.TestingHelpers\TestableIO.System.IO.Abstractions.TestingHelpers.csproj", "{DE22AA55-408F-4041-B85D-26D6D6A158A4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestableIO.System.IO.Abstractions.TestingHelpers", "src\TestableIO.System.IO.Abstractions.TestingHelpers\TestableIO.System.IO.Abstractions.TestingHelpers.csproj", "{DE22AA55-408F-4041-B85D-26D6D6A158A4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestableIO.System.IO.Abstractions.TestingHelpers.Tests", "tests\TestableIO.System.IO.Abstractions.TestingHelpers.Tests\TestableIO.System.IO.Abstractions.TestingHelpers.Tests.csproj", "{919888D2-E37D-40E7-8AD0-600F9429316D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestableIO.System.IO.Abstractions.TestingHelpers.Tests", "tests\TestableIO.System.IO.Abstractions.TestingHelpers.Tests\TestableIO.System.IO.Abstractions.TestingHelpers.Tests.csproj", "{919888D2-E37D-40E7-8AD0-600F9429316D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_", "_", "{BBF7AD8D-5522-48C0-A906-00CBB72308A0}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -66,8 +70,8 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {0103D32A-6124-4A1A-AD6C-30EB957F09B0} = {C078E0B6-9747-475F-A999-B9E775DF6643} {B7DA254D-496F-4C50-969C-CF925758E2ED} = {C078E0B6-9747-475F-A999-B9E775DF6643} + {0103D32A-6124-4A1A-AD6C-30EB957F09B0} = {C078E0B6-9747-475F-A999-B9E775DF6643} {015B3812-E01D-479C-895D-BDDF16E798CA} = {BCEC61BD-4941-41EC-975A-ACEFC7AC1780} {7105D748-1253-409F-A624-4879412EF3C2} = {BCEC61BD-4941-41EC-975A-ACEFC7AC1780} {919888D2-E37D-40E7-8AD0-600F9429316D} = {BCEC61BD-4941-41EC-975A-ACEFC7AC1780} diff --git a/src/System.IO.Abstractions.TestingHelpers/System.IO.Abstractions.TestingHelpers.csproj b/src/System.IO.Abstractions.TestingHelpers/System.IO.Abstractions.TestingHelpers.csproj index e1c39b70d..b4d2012cf 100644 --- a/src/System.IO.Abstractions.TestingHelpers/System.IO.Abstractions.TestingHelpers.csproj +++ b/src/System.IO.Abstractions.TestingHelpers/System.IO.Abstractions.TestingHelpers.csproj @@ -3,7 +3,7 @@ System.IO.Abstractions.TestingHelpers System.IO.Abstractions.TestingHelpers A set of pre-built mocks to help when testing file system interactions. - net6.0;net5.0;netstandard2.1;netstandard2.0;net461 + net7.0;net6.0;net5.0;netstandard2.1;netstandard2.0;net461 icon_256x256.png diff --git a/src/System.IO.Abstractions/System.IO.Abstractions.csproj b/src/System.IO.Abstractions/System.IO.Abstractions.csproj index 0ff4dd0ce..259ef49a8 100644 --- a/src/System.IO.Abstractions/System.IO.Abstractions.csproj +++ b/src/System.IO.Abstractions/System.IO.Abstractions.csproj @@ -3,7 +3,7 @@ System.IO.Abstractions System.IO.Abstractions A set of abstractions to help make file system interactions testable. - net6.0;net5.0;netstandard2.1;netstandard2.0;net461 + net7.0;net6.0;net5.0;netstandard2.1;netstandard2.0;net461 icon_256x256.png diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/CommonExceptions.cs b/src/TestableIO.System.IO.Abstractions.TestingHelpers/CommonExceptions.cs index 1e5262d3f..e0d2d551c 100644 --- a/src/TestableIO.System.IO.Abstractions.TestingHelpers/CommonExceptions.cs +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/CommonExceptions.cs @@ -64,5 +64,8 @@ public static IOException ProcessCannotAccessFileInUse(string paramName = null) public static IOException FileAlreadyExists(string paramName) => new IOException(string.Format(StringResources.Manager.GetString("FILE_ALREADY_EXISTS"), paramName)); + + public static NotImplementedException NotImplemented() => + new NotImplementedException(StringResources.Manager.GetString("NOT_IMPLEMENTED_EXCEPTION")); } } \ No newline at end of file diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDirectory.cs b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDirectory.cs index 6a81caa88..c09cf6f09 100644 --- a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDirectory.cs +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDirectory.cs @@ -39,6 +39,14 @@ public override IDirectoryInfo CreateDirectory(string path) return CreateDirectoryInternal(path); } +#if FEATURE_UNIX_FILE_MODE + /// + public override IDirectoryInfo CreateDirectory(string path, UnixFileMode unixCreateMode) + { + throw CommonExceptions.NotImplemented(); + } +#endif + private IDirectoryInfo CreateDirectoryInternal(string path) { if (path == null) @@ -98,6 +106,14 @@ public override IFileSystemInfo CreateSymbolicLink(string path, string pathToTar } #endif +#if FEATURE_CREATE_TEMP_SUBDIRECTORY + /// + public override IDirectoryInfo CreateTempSubdirectory(string prefix = null) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// public override void Delete(string path) { @@ -514,7 +530,7 @@ public override void Move(string sourceDirName, string destDirName) /// public override IFileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget) { - throw new NotImplementedException(); + throw CommonExceptions.NotImplemented(); } #endif diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs index e9d9cf140..651dd33bf 100644 --- a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs @@ -52,7 +52,7 @@ public MockDirectoryInfo(IMockFileDataAccessor mockFileDataAccessor, string dire /// public override void CreateAsSymbolicLink(string pathToTarget) { - throw new NotImplementedException(); + throw CommonExceptions.NotImplemented(); } #endif @@ -74,7 +74,7 @@ public override void Refresh() /// public override IFileSystemInfo ResolveLinkTarget(bool returnFinalTarget) { - throw new NotImplementedException(); + throw CommonExceptions.NotImplemented(); } #endif diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.Async.cs b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.Async.cs index 73877f143..d7fe76f6e 100644 --- a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.Async.cs +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.Async.cs @@ -65,6 +65,22 @@ public override Task ReadAllTextAsync(string path, Encoding encoding, Ca return Task.FromResult(ReadAllText(path, encoding)); } +#if FEATURE_READ_LINES_ASYNC + /// + public override IAsyncEnumerable ReadLinesAsync(string path, + CancellationToken cancellationToken = default) + { + throw CommonExceptions.NotImplemented(); + } + + /// + public override IAsyncEnumerable ReadLinesAsync(string path, Encoding encoding, + CancellationToken cancellationToken = default) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// public override Task WriteAllBytesAsync(string path, byte[] bytes, CancellationToken cancellationToken) { diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.cs b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.cs index 17efc120a..fc34a9afc 100644 --- a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.cs +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.Linq; using System.Text; +using Microsoft.Win32.SafeHandles; namespace System.IO.Abstractions.TestingHelpers { @@ -311,6 +312,14 @@ public override FileAttributes GetAttributes(string path) return result; } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override FileAttributes GetAttributes(SafeFileHandle fileHandle) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// public override DateTime GetCreationTime(string path) { @@ -319,6 +328,14 @@ public override DateTime GetCreationTime(string path) return GetTimeFromFile(path, data => data.CreationTime.LocalDateTime, () => MockFileData.DefaultDateTimeOffset.LocalDateTime); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override DateTime GetCreationTime(SafeFileHandle fileHandle) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// public override DateTime GetCreationTimeUtc(string path) { @@ -327,6 +344,14 @@ public override DateTime GetCreationTimeUtc(string path) return GetTimeFromFile(path, data => data.CreationTime.UtcDateTime, () => MockFileData.DefaultDateTimeOffset.UtcDateTime); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override DateTime GetCreationTimeUtc(SafeFileHandle fileHandle) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// public override DateTime GetLastAccessTime(string path) { @@ -335,6 +360,14 @@ public override DateTime GetLastAccessTime(string path) return GetTimeFromFile(path, data => data.LastAccessTime.LocalDateTime, () => MockFileData.DefaultDateTimeOffset.LocalDateTime); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override DateTime GetLastAccessTime(SafeFileHandle fileHandle) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// public override DateTime GetLastAccessTimeUtc(string path) { @@ -343,6 +376,14 @@ public override DateTime GetLastAccessTimeUtc(string path) return GetTimeFromFile(path, data => data.LastAccessTime.UtcDateTime, () => MockFileData.DefaultDateTimeOffset.UtcDateTime); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override DateTime GetLastAccessTimeUtc(SafeFileHandle fileHandle) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// public override DateTime GetLastWriteTime(string path) { @@ -351,6 +392,14 @@ public override DateTime GetLastWriteTime(string path) return GetTimeFromFile(path, data => data.LastWriteTime.LocalDateTime, () => MockFileData.DefaultDateTimeOffset.LocalDateTime); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override DateTime GetLastWriteTime(SafeFileHandle fileHandle) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// public override DateTime GetLastWriteTimeUtc(string path) { @@ -359,6 +408,30 @@ public override DateTime GetLastWriteTimeUtc(string path) return GetTimeFromFile(path, data => data.LastWriteTime.UtcDateTime, () => MockFileData.DefaultDateTimeOffset.UtcDateTime); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override DateTime GetLastWriteTimeUtc(SafeFileHandle fileHandle) + { + throw CommonExceptions.NotImplemented(); + } +#endif + +#if FEATURE_UNIX_FILE_MODE + /// + public override UnixFileMode GetUnixFileMode(string path) + { + throw CommonExceptions.NotImplemented(); + } +#endif + +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override UnixFileMode GetUnixFileMode(SafeFileHandle fileHandle) + { + throw CommonExceptions.NotImplemented(); + } +#endif + private DateTime GetTimeFromFile(string path, Func existingFileFunction, Func nonExistingFileFunction) { DateTime result; @@ -704,7 +777,7 @@ public override void Replace(string sourceFileName, string destinationFileName, /// public override IFileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget) { - throw new NotImplementedException(); + throw CommonExceptions.NotImplemented(); } #endif @@ -733,6 +806,14 @@ public override void SetAttributes(string path, FileAttributes fileAttributes) } } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetAttributes(SafeFileHandle fileHandle, FileAttributes fileAttributes) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// public override void SetCreationTime(string path, DateTime creationTime) { @@ -741,6 +822,14 @@ public override void SetCreationTime(string path, DateTime creationTime) mockFileDataAccessor.GetFile(path).CreationTime = new DateTimeOffset(creationTime); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetCreationTime(SafeFileHandle fileHandle, DateTime creationTime) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// public override void SetCreationTimeUtc(string path, DateTime creationTimeUtc) { @@ -749,6 +838,14 @@ public override void SetCreationTimeUtc(string path, DateTime creationTimeUtc) mockFileDataAccessor.GetFile(path).CreationTime = new DateTimeOffset(creationTimeUtc, TimeSpan.Zero); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetCreationTimeUtc(SafeFileHandle fileHandle, DateTime creationTimeUtc) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// public override void SetLastAccessTime(string path, DateTime lastAccessTime) { @@ -757,6 +854,14 @@ public override void SetLastAccessTime(string path, DateTime lastAccessTime) mockFileDataAccessor.GetFile(path).LastAccessTime = new DateTimeOffset(lastAccessTime); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetLastAccessTime(SafeFileHandle fileHandle, DateTime lastAccessTime) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// public override void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) { @@ -765,6 +870,14 @@ public override void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUt mockFileDataAccessor.GetFile(path).LastAccessTime = new DateTimeOffset(lastAccessTimeUtc, TimeSpan.Zero); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetLastAccessTimeUtc(SafeFileHandle fileHandle, DateTime lastAccessTimeUtc) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// public override void SetLastWriteTime(string path, DateTime lastWriteTime) { @@ -773,6 +886,14 @@ public override void SetLastWriteTime(string path, DateTime lastWriteTime) mockFileDataAccessor.GetFile(path).LastWriteTime = new DateTimeOffset(lastWriteTime); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetLastWriteTime(SafeFileHandle fileHandle, DateTime lastWriteTime) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// public override void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) { @@ -781,6 +902,30 @@ public override void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) mockFileDataAccessor.GetFile(path).LastWriteTime = new DateTimeOffset(lastWriteTimeUtc, TimeSpan.Zero); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetLastWriteTimeUtc(SafeFileHandle fileHandle, DateTime lastWriteTimeUtc) + { + throw CommonExceptions.NotImplemented(); + } +#endif + +#if FEATURE_UNIX_FILE_MODE + /// + public override void SetUnixFileMode(string path, UnixFileMode mode) + { + throw CommonExceptions.NotImplemented(); + } +#endif + +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetUnixFileMode(SafeFileHandle fileHandle, UnixFileMode mode) + { + throw CommonExceptions.NotImplemented(); + } +#endif + /// /// Creates a new file, writes the specified byte array to the file, and then closes the file. /// If the target file already exists, it is overwritten. diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileInfo.cs b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileInfo.cs index bdf0580ea..1e6aa2220 100644 --- a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileInfo.cs +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileInfo.cs @@ -29,7 +29,7 @@ public MockFileInfo(IMockFileDataAccessor mockFileSystem, string path) : base(mo /// public override void CreateAsSymbolicLink(string pathToTarget) { - throw new NotImplementedException(); + throw CommonExceptions.NotImplemented(); } #endif @@ -51,7 +51,7 @@ public override void Refresh() /// public override IFileSystemInfo ResolveLinkTarget(bool returnFinalTarget) { - throw new NotImplementedException(); + throw CommonExceptions.NotImplemented(); } #endif diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockPath.cs b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockPath.cs index 9767571e2..e30fe9147 100644 --- a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockPath.cs +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockPath.cs @@ -23,6 +23,14 @@ public MockPath(IMockFileDataAccessor mockFileDataAccessor, string defaultTempDi this.defaultTempDirectory = !string.IsNullOrEmpty(defaultTempDirectory) ? defaultTempDirectory : base.GetTempPath(); } +#if FEATURE_PATH_EXISTS + /// + public override bool Exists(string path) + { + return mockFileDataAccessor.FileExists(path); + } +#endif + /// public override string GetFullPath(string path) { diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/TestableIO.System.IO.Abstractions.TestingHelpers.csproj b/src/TestableIO.System.IO.Abstractions.TestingHelpers/TestableIO.System.IO.Abstractions.TestingHelpers.csproj index 3aed00eaf..dfe871ef9 100644 --- a/src/TestableIO.System.IO.Abstractions.TestingHelpers/TestableIO.System.IO.Abstractions.TestingHelpers.csproj +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/TestableIO.System.IO.Abstractions.TestingHelpers.csproj @@ -3,7 +3,7 @@ TestableIO.System.IO.Abstractions.TestingHelpers System.IO.Abstractions.TestingHelpers A set of pre-built mocks to help when testing file system interactions. - net6.0;net5.0;netstandard2.1;netstandard2.0;net461 + net7.0;net6.0;net5.0;netstandard2.1;netstandard2.0;net461 icon_256x256.png diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryBase.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryBase.cs index e5b5202da..949d6c0d8 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryBase.cs +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryBase.cs @@ -24,11 +24,21 @@ internal DirectoryBase() { } /// public abstract IDirectoryInfo CreateDirectory(string path); - + +#if FEATURE_UNIX_FILE_MODE + /// + public abstract IDirectoryInfo CreateDirectory(string path, UnixFileMode unixCreateMode); +#endif + #if FEATURE_CREATE_SYMBOLIC_LINK /// public abstract IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget); #endif + +#if FEATURE_CREATE_TEMP_SUBDIRECTORY + /// + public abstract IDirectoryInfo CreateTempSubdirectory(string prefix = null); +#endif /// public abstract void Delete(string path); diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryWrapper.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryWrapper.cs index 922ad659c..5a91107a1 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryWrapper.cs +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryWrapper.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Runtime.Versioning; -using System.Security.AccessControl; namespace System.IO.Abstractions { @@ -21,6 +20,16 @@ public override IDirectoryInfo CreateDirectory(string path) return new DirectoryInfoWrapper(FileSystem, directoryInfo); } +#if FEATURE_UNIX_FILE_MODE + /// + [UnsupportedOSPlatform("windows")] + public override IDirectoryInfo CreateDirectory(string path, UnixFileMode unixCreateMode) + { + return new DirectoryInfoWrapper(FileSystem, + Directory.CreateDirectory(path, unixCreateMode)); + } +#endif + #if FEATURE_CREATE_SYMBOLIC_LINK /// public override IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget) @@ -28,6 +37,15 @@ public override IFileSystemInfo CreateSymbolicLink(string path, string pathToTar return Directory.CreateSymbolicLink(path, pathToTarget).WrapFileSystemInfo(FileSystem); } #endif + +#if FEATURE_CREATE_TEMP_SUBDIRECTORY + /// + public override IDirectoryInfo CreateTempSubdirectory(string prefix = null) + { + return new DirectoryInfoWrapper(FileSystem, + Directory.CreateTempSubdirectory(prefix)); + } +#endif /// public override void Delete(string path) { diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/FileBase.Async.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/FileBase.Async.cs index 90d10d54f..d91efcf92 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/FileBase.Async.cs +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/FileBase.Async.cs @@ -34,6 +34,17 @@ partial class FileBase /// public abstract Task ReadAllTextAsync(string path, Encoding encoding, CancellationToken cancellationToken); + +#if FEATURE_READ_LINES_ASYNC + /// + public abstract IAsyncEnumerable ReadLinesAsync(string path, + CancellationToken cancellationToken = default); + + /// + public abstract IAsyncEnumerable ReadLinesAsync(string path, Encoding encoding, + CancellationToken cancellationToken = default); +#endif + /// public abstract Task WriteAllBytesAsync(string path, byte[] bytes, CancellationToken cancellationToken); diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/FileBase.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/FileBase.cs index 6375fa753..1b5714547 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/FileBase.cs +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/FileBase.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; -using System.Runtime.Versioning; -using System.Security.AccessControl; using System.Text; +using Microsoft.Win32.SafeHandles; namespace System.IO.Abstractions { @@ -9,7 +8,7 @@ namespace System.IO.Abstractions [Serializable] public abstract partial class FileBase : IFile { - /// + /// protected FileBase(IFileSystem fileSystem) { FileSystem = fileSystem; @@ -52,208 +51,92 @@ internal FileBase() { } /// public abstract FileSystemStream Create(string path, int bufferSize, FileOptions options); + #if FEATURE_CREATE_SYMBOLIC_LINK /// public abstract IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget); #endif /// public abstract StreamWriter CreateText(string path); + /// public abstract void Decrypt(string path); + /// public abstract void Delete(string path); + /// public abstract void Encrypt(string path); /// - /// - /// Determines whether the specified file exists. - /// - /// The file to check. - /// if the caller has the required permissions and path contains the name of an existing file; otherwise, . This method also returns if is , an invalid path, or a zero-length string. If the caller does not have sufficient permissions to read the specified file, no exception is thrown and the method returns regardless of the existence of . - /// - /// - /// The Exists method should not be used for path validation, this method merely checks if the file specified in exists. - /// Passing an invalid path to Exists returns . - /// - /// - /// Be aware that another process can potentially do something with the file in between the time you call the Exists method and perform another operation on the file, such as . - /// - /// - /// The parameter is permitted to specify relative or absolute path information. - /// Relative path information is interpreted as relative to the current working directory. - /// To obtain the current working directory, see . - /// - /// - /// If describes a directory, this method returns . Trailing spaces are removed from the parameter before determining if the file exists. - /// - /// - /// The Exists method returns if any error occurs while trying to determine if the specified file exists. - /// This can occur in situations that raise exceptions such as passing a file name with invalid characters or too many characters - /// a failing or missing disk, or if the caller does not have permission to read the file. - /// - /// public abstract bool Exists(string path); - /// - /// - /// Gets the of the file on the path. - /// - /// The path to the file. - /// The of the file on the path. - /// is empty, contains only white spaces, or contains invalid characters. - /// The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. - /// is in an invalid format. - /// represents a file and is invalid, such as being on an unmapped drive, or the file cannot be found. - /// represents a directory and is invalid, such as being on an unmapped drive, or the directory cannot be found. - /// This file is being used by another process. - /// The caller does not have the required permission. + /// public abstract FileAttributes GetAttributes(string path); - /// - /// - /// Returns the creation date and time of the specified file or directory. - /// - /// The file or directory for which to obtain creation date and time information. - /// A structure set to the creation date and time for the specified file or directory. This value is expressed in local time. - /// The caller does not have the required permission. - /// is empty, contains only white spaces, or contains invalid characters. - /// is . - /// The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. - /// is in an invalid format. - /// - /// - /// The parameter is permitted to specify relative or absolute path information. Relative path information is interpreted as relative to the current working directory. To obtain the current working directory, see . - /// - /// - /// If the file described in the parameter does not exist, this method returns 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC), adjusted to local time. - /// - /// - /// NTFS-formatted drives may cache file meta-info, such as file creation time, for a short period of time, which is known as "file tunneling." As a result, it may be necessary to explicitly set the creation time of a file if you are overwriting or replacing an existing file. - /// - /// +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract FileAttributes GetAttributes(SafeFileHandle fileHandle); +#endif + + /// public abstract DateTime GetCreationTime(string path); - /// - /// - /// Returns the creation date and time, in coordinated universal time (UTC), of the specified file or directory. - /// - /// The file or directory for which to obtain creation date and time information. - /// A structure set to the creation date and time for the specified file or directory. This value is expressed in UTC time. - /// The caller does not have the required permission. - /// is empty, contains only white spaces, or contains invalid characters. - /// is . - /// The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. - /// is in an invalid format. - /// - /// - /// The parameter is permitted to specify relative or absolute path information. Relative path information is interpreted as relative to the current working directory. To obtain the current working directory, see . - /// - /// - /// If the file described in the parameter does not exist, this method returns 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC). - /// - /// - /// NTFS-formatted drives may cache file meta-info, such as file creation time, for a short period of time, which is known as "file tunneling." As a result, it may be necessary to explicitly set the creation time of a file if you are overwriting or replacing an existing file. - /// - /// +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract DateTime GetCreationTime(SafeFileHandle fileHandle); +#endif + + /// public abstract DateTime GetCreationTimeUtc(string path); - /// - /// - /// Returns the date and time the specified file or directory was last accessed. - /// - /// The file or directory for which to obtain access date and time information. - /// A structure set to the date and time that the specified file or directory was last accessed. This value is expressed in local time. - /// The caller does not have the required permission. - /// is empty, contains only white spaces, or contains invalid characters. - /// is . - /// The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. - /// is in an invalid format. - /// - /// - /// The parameter is permitted to specify relative or absolute path information. Relative path information is interpreted as relative to the current working directory. To obtain the current working directory, see . - /// - /// - /// If the file described in the parameter does not exist, this method returns 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC), adjusted to local time. - /// - /// - /// NTFS-formatted drives may cache file meta-info, such as file creation time, for a short period of time, which is known as "file tunneling." As a result, it may be necessary to explicitly set the creation time of a file if you are overwriting or replacing an existing file. - /// - /// +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract DateTime GetCreationTimeUtc(SafeFileHandle fileHandle); +#endif + + /// public abstract DateTime GetLastAccessTime(string path); - /// - /// - /// Returns the date and time, in coordinated universal time (UTC), that the specified file or directory was last accessed. - /// - /// The file or directory for which to obtain creation date and time information. - /// A structure set to the date and time that the specified file or directory was last accessed. This value is expressed in UTC time. - /// The caller does not have the required permission. - /// is empty, contains only white spaces, or contains invalid characters. - /// is . - /// The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. - /// is in an invalid format. - /// - /// - /// The parameter is permitted to specify relative or absolute path information. Relative path information is interpreted as relative to the current working directory. To obtain the current working directory, see . - /// - /// - /// If the file described in the parameter does not exist, this method returns 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC). - /// - /// - /// NTFS-formatted drives may cache file meta-info, such as file creation time, for a short period of time, which is known as "file tunneling." As a result, it may be necessary to explicitly set the creation time of a file if you are overwriting or replacing an existing file. - /// - /// +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract DateTime GetLastAccessTime(SafeFileHandle fileHandle); +#endif + + /// public abstract DateTime GetLastAccessTimeUtc(string path); - /// - /// - /// Returns the date and time the specified file or directory was last written to. - /// - /// The file or directory for which to obtain creation date and time information. - /// A structure set to the date and time that the specified file or directory was last written to. This value is expressed in local time. - /// The caller does not have the required permission. - /// is empty, contains only white spaces, or contains invalid characters. - /// is . - /// The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. - /// is in an invalid format. - /// - /// - /// If the file described in the path parameter does not exist, this method returns 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC), adjusted to local time. - /// - /// - /// The parameter is permitted to specify relative or absolute path information. Relative path information is interpreted as relative to the current working directory. To obtain the current working directory, see . - /// - /// - /// NTFS-formatted drives may cache file meta-info, such as file creation time, for a short period of time, which is known as "file tunneling." As a result, it may be necessary to explicitly set the creation time of a file if you are overwriting or replacing an existing file. - /// - /// +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract DateTime GetLastAccessTimeUtc(SafeFileHandle fileHandle); +#endif + + /// public abstract DateTime GetLastWriteTime(string path); - /// - /// - /// Returns the date and time, in coordinated universal time (UTC), that the specified file or directory was last written to. - /// - /// The file or directory for which to obtain creation date and time information. - /// A structure set to the date and time that the specified file or directory was last written to. This value is expressed in local time. - /// The caller does not have the required permission. - /// is empty, contains only white spaces, or contains invalid characters. - /// is . - /// The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. - /// is in an invalid format. - /// - /// - /// If the file described in the path parameter does not exist, this method returns 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC). - /// - /// - /// The parameter is permitted to specify relative or absolute path information. Relative path information is interpreted as relative to the current working directory. To obtain the current working directory, see . - /// - /// - /// NTFS-formatted drives may cache file meta-info, such as file creation time, for a short period of time, which is known as "file tunneling." As a result, it may be necessary to explicitly set the creation time of a file if you are overwriting or replacing an existing file. - /// - /// +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract DateTime GetLastWriteTime(SafeFileHandle fileHandle); +#endif + + /// public abstract DateTime GetLastWriteTimeUtc(string path); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract DateTime GetLastWriteTimeUtc(SafeFileHandle fileHandle); +#endif + +#if FEATURE_UNIX_FILE_MODE + /// + public abstract UnixFileMode GetUnixFileMode(string path); +#endif + +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract UnixFileMode GetUnixFileMode(SafeFileHandle fileHandle); +#endif + /// public abstract void Move(string sourceFileName, string destFileName); @@ -287,22 +170,19 @@ internal FileBase() { } /// public abstract byte[] ReadAllBytes(string path); - - + /// public abstract string[] ReadAllLines(string path); /// public abstract string[] ReadAllLines(string path, Encoding encoding); - - + /// public abstract string ReadAllText(string path); /// public abstract string ReadAllText(string path, Encoding encoding); - - + /// public abstract IEnumerable ReadLines(string path); @@ -320,284 +200,91 @@ internal FileBase() { } public abstract IFileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget); #endif - /// + /// public abstract void SetAttributes(string path, FileAttributes fileAttributes); - /// +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract void SetAttributes(SafeFileHandle fileHandle, FileAttributes fileAttributes); +#endif + + /// public abstract void SetCreationTime(string path, DateTime creationTime); - /// +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract void SetCreationTime(SafeFileHandle fileHandle, DateTime creationTime); +#endif + + /// public abstract void SetCreationTimeUtc(string path, DateTime creationTimeUtc); - /// +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract void SetCreationTimeUtc(SafeFileHandle fileHandle, DateTime creationTimeUtc); +#endif + + /// public abstract void SetLastAccessTime(string path, DateTime lastAccessTime); - /// +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract void SetLastAccessTime(SafeFileHandle fileHandle, DateTime lastAccessTime); +#endif + + /// public abstract void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc); - /// +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract void SetLastAccessTimeUtc(SafeFileHandle fileHandle, DateTime lastAccessTimeUtc); +#endif + + /// public abstract void SetLastWriteTime(string path, DateTime lastWriteTime); - /// +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract void SetLastWriteTime(SafeFileHandle fileHandle, DateTime lastWriteTime); +#endif + + /// public abstract void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc); - /// - /// - /// Creates a new file, writes the specified byte array to the file, and then closes the file. - /// If the target file already exists, it is overwritten. - /// - /// The file to write to. - /// The bytes to write to the file. - /// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . - /// is or contents is empty. - /// - /// The specified path, file name, or both exceed the system-defined maximum length. - /// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. - /// - /// The specified path is invalid (for example, it is on an unmapped drive). - /// An I/O error occurred while opening the file. - /// - /// path specified a file that is read-only. - /// -or- - /// This operation is not supported on the current platform. - /// -or- - /// path specified a directory. - /// -or- - /// The caller does not have the required permission. - /// - /// The file specified in was not found. - /// is in an invalid format. - /// The caller does not have the required permission. - /// - /// Given a byte array and a file path, this method opens the specified file, writes the contents of the byte array to the file, and then closes the file. - /// +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract void SetLastWriteTimeUtc(SafeFileHandle fileHandle, DateTime lastWriteTimeUtc); +#endif + +#if FEATURE_UNIX_FILE_MODE + /// + public abstract void SetUnixFileMode(string path, UnixFileMode mode); +#endif + +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public abstract void SetUnixFileMode(SafeFileHandle fileHandle, UnixFileMode mode); +#endif + + /// public abstract void WriteAllBytes(string path, byte[] bytes); /// - /// - /// Creates a new file, writes a collection of strings to the file, and then closes the file. - /// - /// The file to write to. - /// The lines to write to the file. - /// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . - /// Either or is . - /// The specified path is invalid (for example, it is on an unmapped drive). - /// The file specified in was not found. - /// An I/O error occurred while opening the file. - /// - /// The specified path, file name, or both exceed the system-defined maximum length. - /// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. - /// - /// is in an invalid format. - /// The caller does not have the required permission. - /// - /// specified a file that is read-only. - /// -or- - /// This operation is not supported on the current platform. - /// -or- - /// specified a directory. - /// -or- - /// The caller does not have the required permission. - /// - /// - /// - /// If the target file already exists, it is overwritten. - /// - /// - /// You can use this method to create the contents for a collection class that takes an in its constructor, such as a , , or a class. - /// - /// public abstract void WriteAllLines(string path, IEnumerable contents); /// - /// - /// Creates a new file by using the specified encoding, writes a collection of strings to the file, and then closes the file. - /// - /// The file to write to. - /// The lines to write to the file. - /// The character encoding to use. - /// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . - /// Either , , or is . - /// The specified path is invalid (for example, it is on an unmapped drive). - /// The file specified in was not found. - /// An I/O error occurred while opening the file. - /// - /// The specified path, file name, or both exceed the system-defined maximum length. - /// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. - /// - /// is in an invalid format. - /// The caller does not have the required permission. - /// - /// specified a file that is read-only. - /// -or- - /// This operation is not supported on the current platform. - /// -or- - /// specified a directory. - /// -or- - /// The caller does not have the required permission. - /// - /// - /// - /// If the target file already exists, it is overwritten. - /// - /// - /// You can use this method to create a file that contains the following: - /// - /// - /// The results of a LINQ to Objects query on the lines of a file, as obtained by using the ReadLines method. - /// - /// - /// The contents of a collection that implements an of strings. - /// - /// - /// - /// public abstract void WriteAllLines(string path, IEnumerable contents, Encoding encoding); /// - /// - /// Creates a new file, writes the specified string array to the file by using the specified encoding, and then closes the file. - /// - /// The file to write to. - /// The string array to write to the file. - /// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . - /// Either or is . - /// - /// The specified path, file name, or both exceed the system-defined maximum length. - /// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. - /// - /// The specified path is invalid (for example, it is on an unmapped drive). - /// An I/O error occurred while opening the file. - /// - /// specified a file that is read-only. - /// -or- - /// This operation is not supported on the current platform. - /// -or- - /// specified a directory. - /// -or- - /// The caller does not have the required permission. - /// - /// The file specified in was not found. - /// is in an invalid format. - /// The caller does not have the required permission. - /// - /// - /// If the target file already exists, it is overwritten. - /// - /// - /// The default behavior of the WriteAllLines method is to write out data using UTF-8 encoding without a byte order mark (BOM). If it is necessary to include a UTF-8 identifier, such as a byte order mark, at the beginning of a file, use the method overload with encoding. - /// - /// - /// Given a string array and a file path, this method opens the specified file, writes the string array to the file using the specified encoding, - /// and then closes the file. - /// - /// public abstract void WriteAllLines(string path, string[] contents); /// - /// - /// Creates a new file, writes the specified string array to the file by using the specified encoding, and then closes the file. - /// - /// The file to write to. - /// The string array to write to the file. - /// An object that represents the character encoding applied to the string array. - /// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . - /// Either or is . - /// - /// The specified path, file name, or both exceed the system-defined maximum length. - /// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. - /// - /// The specified path is invalid (for example, it is on an unmapped drive). - /// An I/O error occurred while opening the file. - /// - /// specified a file that is read-only. - /// -or- - /// This operation is not supported on the current platform. - /// -or- - /// specified a directory. - /// -or- - /// The caller does not have the required permission. - /// - /// The file specified in was not found. - /// is in an invalid format. - /// The caller does not have the required permission. - /// - /// - /// If the target file already exists, it is overwritten. - /// - /// - /// Given a string array and a file path, this method opens the specified file, writes the string array to the file using the specified encoding, - /// and then closes the file. - /// - /// public abstract void WriteAllLines(string path, string[] contents, Encoding encoding); /// - /// - /// Creates a new file, writes the specified string to the file using the specified encoding, and then closes the file. If the target file already exists, it is overwritten. - /// - /// The file to write to. - /// The string to write to the file. - /// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . - /// is or contents is empty. - /// - /// The specified path, file name, or both exceed the system-defined maximum length. - /// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. - /// - /// The specified path is invalid (for example, it is on an unmapped drive). - /// An I/O error occurred while opening the file. - /// - /// path specified a file that is read-only. - /// -or- - /// This operation is not supported on the current platform. - /// -or- - /// path specified a directory. - /// -or- - /// The caller does not have the required permission. - /// - /// The file specified in was not found. - /// is in an invalid format. - /// The caller does not have the required permission. - /// - /// This method uses UTF-8 encoding without a Byte-Order Mark (BOM), so using the method will return an empty byte array. - /// If it is necessary to include a UTF-8 identifier, such as a byte order mark, at the beginning of a file, use the method overload with encoding. - /// - /// Given a string and a file path, this method opens the specified file, writes the string to the file, and then closes the file. - /// - /// public abstract void WriteAllText(string path, string contents); /// - /// - /// Creates a new file, writes the specified string to the file using the specified encoding, and then closes the file. If the target file already exists, it is overwritten. - /// - /// The file to write to. - /// The string to write to the file. - /// The encoding to apply to the string. - /// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . - /// is or contents is empty. - /// - /// The specified path, file name, or both exceed the system-defined maximum length. - /// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. - /// - /// The specified path is invalid (for example, it is on an unmapped drive). - /// An I/O error occurred while opening the file. - /// - /// path specified a file that is read-only. - /// -or- - /// This operation is not supported on the current platform. - /// -or- - /// path specified a directory. - /// -or- - /// The caller does not have the required permission. - /// - /// The file specified in was not found. - /// is in an invalid format. - /// The caller does not have the required permission. - /// - /// Given a string and a file path, this method opens the specified file, writes the string to the file using the specified encoding, and then closes the file. - /// The file handle is guaranteed to be closed by this method, even if exceptions are raised. - /// public abstract void WriteAllText(string path, string contents, Encoding encoding); } } diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemInfoBase.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemInfoBase.cs index 7b265eb71..00e8ad716 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemInfoBase.cs +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemInfoBase.cs @@ -1,10 +1,12 @@ -namespace System.IO.Abstractions +using System.Runtime.Versioning; + +namespace System.IO.Abstractions { /// [Serializable] public abstract class FileSystemInfoBase : IFileSystemInfo { - /// + /// protected FileSystemInfoBase(IFileSystem fileSystem) { FileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); @@ -71,5 +73,14 @@ internal FileSystemInfoBase() { } /// public abstract string Name { get; } + +#if FEATURE_UNIX_FILE_MODE + /// + public UnixFileMode UnixFileMode + { + get; + [UnsupportedOSPlatform("windows")] set; + } +#endif } } diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemWatcherBase.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemWatcherBase.cs index fd0b96d4c..5bf4a2bfd 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemWatcherBase.cs +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemWatcherBase.cs @@ -75,6 +75,11 @@ public void Dispose() /// public abstract IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout); +#if FEATURE_FILE_SYSTEM_WATCHER_WAIT_WITH_TIMESPAN + /// + public abstract IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout); +#endif + /// public static implicit operator FileSystemWatcherBase(FileSystemWatcher watcher) { diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemWatcherWrapper.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemWatcherWrapper.cs index 937aecb53..acfe55b3f 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemWatcherWrapper.cs +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemWatcherWrapper.cs @@ -153,6 +153,14 @@ public override IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeTy return new WaitForChangedResultWrapper(watcher.WaitForChanged(changeType, timeout)); } +#if FEATURE_FILE_SYSTEM_WATCHER_WAIT_WITH_TIMESPAN + /// + public override IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout) + { + return new WaitForChangedResultWrapper(watcher.WaitForChanged(changeType, timeout)); + } +#endif + private readonly struct WaitForChangedResultWrapper : IWaitForChangedResult, IEquatable { diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/FileWrapper.Async.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/FileWrapper.Async.cs index 59a661326..ff4f953e2 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/FileWrapper.Async.cs +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/FileWrapper.Async.cs @@ -63,6 +63,18 @@ public override Task ReadAllTextAsync(string path, Encoding encoding, Ca return File.ReadAllTextAsync(path, encoding, cancellationToken); } +#if FEATURE_READ_LINES_ASYNC + /// + public override IAsyncEnumerable ReadLinesAsync(string path, + CancellationToken cancellationToken = default) + => File.ReadLinesAsync(path, cancellationToken); + + /// + public override IAsyncEnumerable ReadLinesAsync(string path, Encoding encoding, + CancellationToken cancellationToken = default) + => File.ReadLinesAsync(path, encoding, cancellationToken); +#endif + /// public override Task WriteAllBytesAsync(string path, byte[] bytes, CancellationToken cancellationToken) { diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/FileWrapper.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/FileWrapper.cs index 1dc42e022..5ee53d494 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/FileWrapper.cs +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/FileWrapper.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Runtime.Versioning; -using System.Security.AccessControl; - using System.Text; +using Microsoft.Win32.SafeHandles; + namespace System.IO.Abstractions { /// @@ -120,42 +120,116 @@ public override FileAttributes GetAttributes(string path) return File.GetAttributes(path); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override FileAttributes GetAttributes(SafeFileHandle fileHandle) + { + return File.GetAttributes(fileHandle); + } +#endif + /// public override DateTime GetCreationTime(string path) { return File.GetCreationTime(path); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override DateTime GetCreationTime(SafeFileHandle fileHandle) + { + return File.GetCreationTime(fileHandle); + } +#endif + /// public override DateTime GetCreationTimeUtc(string path) { return File.GetCreationTimeUtc(path); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override DateTime GetCreationTimeUtc(SafeFileHandle fileHandle) + { + return File.GetCreationTimeUtc(fileHandle); + } +#endif + /// public override DateTime GetLastAccessTime(string path) { return File.GetLastAccessTime(path); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override DateTime GetLastAccessTime(SafeFileHandle fileHandle) + { + return File.GetLastAccessTime(fileHandle); + } +#endif + /// public override DateTime GetLastAccessTimeUtc(string path) { return File.GetLastAccessTimeUtc(path); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override DateTime GetLastAccessTimeUtc(SafeFileHandle fileHandle) + { + return File.GetLastAccessTimeUtc(fileHandle); + } +#endif + /// public override DateTime GetLastWriteTime(string path) { return File.GetLastWriteTime(path); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override DateTime GetLastWriteTime(SafeFileHandle fileHandle) + { + return File.GetLastWriteTime(fileHandle); + } +#endif + /// public override DateTime GetLastWriteTimeUtc(string path) { return File.GetLastWriteTimeUtc(path); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override DateTime GetLastWriteTimeUtc(SafeFileHandle fileHandle) + { + return File.GetLastWriteTimeUtc(fileHandle); + } +#endif + +#if FEATURE_UNIX_FILE_MODE + /// + [UnsupportedOSPlatform("windows")] + public override UnixFileMode GetUnixFileMode(string path) + { + return File.GetUnixFileMode(path); + } +#endif + +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + [UnsupportedOSPlatform("windows")] + public override UnixFileMode GetUnixFileMode(SafeFileHandle fileHandle) + { + return File.GetUnixFileMode(fileHandle); + } +#endif + /// public override void Move(string sourceFileName, string destFileName) { @@ -283,42 +357,116 @@ public override void SetAttributes(string path, FileAttributes fileAttributes) File.SetAttributes(path, fileAttributes); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetAttributes(SafeFileHandle fileHandle, FileAttributes fileAttributes) + { + File.SetAttributes(fileHandle, fileAttributes); + } +#endif + /// public override void SetCreationTime(string path, DateTime creationTime) { File.SetCreationTime(path, creationTime); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetCreationTime(SafeFileHandle fileHandle, DateTime creationTime) + { + File.SetCreationTime(fileHandle, creationTime); + } +#endif + /// public override void SetCreationTimeUtc(string path, DateTime creationTimeUtc) { File.SetCreationTimeUtc(path, creationTimeUtc); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetCreationTimeUtc(SafeFileHandle fileHandle, DateTime creationTimeUtc) + { + File.SetCreationTimeUtc(fileHandle, creationTimeUtc); + } +#endif + /// public override void SetLastAccessTime(string path, DateTime lastAccessTime) { File.SetLastAccessTime(path, lastAccessTime); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetLastAccessTime(SafeFileHandle fileHandle, DateTime lastAccessTime) + { + File.SetLastAccessTime(fileHandle, lastAccessTime); + } +#endif + /// public override void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) { File.SetLastAccessTimeUtc(path, lastAccessTimeUtc); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetLastAccessTimeUtc(SafeFileHandle fileHandle, DateTime lastAccessTimeUtc) + { + File.SetLastAccessTimeUtc(fileHandle, lastAccessTimeUtc); + } +#endif + /// public override void SetLastWriteTime(string path, DateTime lastWriteTime) { File.SetLastWriteTime(path, lastWriteTime); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetLastWriteTime(SafeFileHandle fileHandle, DateTime lastWriteTime) + { + File.SetLastWriteTime(fileHandle, lastWriteTime); + } +#endif + /// public override void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) { File.SetLastWriteTimeUtc(path, lastWriteTimeUtc); } +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + public override void SetLastWriteTimeUtc(SafeFileHandle fileHandle, DateTime lastWriteTimeUtc) + { + File.SetLastWriteTimeUtc(fileHandle, lastWriteTimeUtc); + } +#endif + +#if FEATURE_UNIX_FILE_MODE + /// + [UnsupportedOSPlatform("windows")] + public override void SetUnixFileMode(string path, UnixFileMode mode) + { + File.SetUnixFileMode(path, mode); + } +#endif + +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + [UnsupportedOSPlatform("windows")] + public override void SetUnixFileMode(SafeFileHandle fileHandle, UnixFileMode mode) + { + File.SetUnixFileMode(fileHandle, mode); + } +#endif + /// /// Creates a new file, writes the specified byte array to the file, and then closes the file. /// If the target file already exists, it is overwritten. diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/PathBase.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/PathBase.cs index 37d036053..d3fbd0041 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/PathBase.cs +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/PathBase.cs @@ -1,4 +1,6 @@ -namespace System.IO.Abstractions +using System.Diagnostics.CodeAnalysis; + +namespace System.IO.Abstractions { /// [Serializable] @@ -49,6 +51,11 @@ internal PathBase() { } /// public abstract string Combine(string path1, string path2, string path3, string path4); +#if FEATURE_PATH_EXISTS + /// + public abstract bool Exists(string path); +#endif + /// public abstract string GetDirectoryName(string path); diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/PathWrapper.cs b/src/TestableIO.System.IO.Abstractions.Wrappers/PathWrapper.cs index 9c5151d5d..f749c9898 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/PathWrapper.cs +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/PathWrapper.cs @@ -1,4 +1,6 @@ -namespace System.IO.Abstractions +using System.Diagnostics.CodeAnalysis; + +namespace System.IO.Abstractions { /// [Serializable] @@ -70,6 +72,14 @@ public override string Combine(string path1, string path2, string path3, string return Path.Combine(path1, path2, path3, path4); } +#if FEATURE_PATH_EXISTS + /// + public override bool Exists(string path) + { + return Path.Exists(path); + } +#endif + /// public override string GetDirectoryName(string path) { diff --git a/src/TestableIO.System.IO.Abstractions.Wrappers/TestableIO.System.IO.Abstractions.Wrappers.csproj b/src/TestableIO.System.IO.Abstractions.Wrappers/TestableIO.System.IO.Abstractions.Wrappers.csproj index 7fa65c191..2bc559814 100644 --- a/src/TestableIO.System.IO.Abstractions.Wrappers/TestableIO.System.IO.Abstractions.Wrappers.csproj +++ b/src/TestableIO.System.IO.Abstractions.Wrappers/TestableIO.System.IO.Abstractions.Wrappers.csproj @@ -3,7 +3,7 @@ TestableIO.System.IO.Abstractions.Wrappers System.IO.Abstractions A set of abstractions to help make file system interactions testable. - net6.0;net5.0;netstandard2.1;netstandard2.0;net461 + net7.0;net6.0;net5.0;netstandard2.1;netstandard2.0;net461 icon_256x256.png diff --git a/src/TestableIO.System.IO.Abstractions/IDirectory.cs b/src/TestableIO.System.IO.Abstractions/IDirectory.cs index 63c62276c..a922c4be5 100644 --- a/src/TestableIO.System.IO.Abstractions/IDirectory.cs +++ b/src/TestableIO.System.IO.Abstractions/IDirectory.cs @@ -10,12 +10,22 @@ public interface IDirectory : IFileSystemEntity { /// IDirectoryInfo CreateDirectory(string path); - + +#if FEATURE_UNIX_FILE_MODE + /// + IDirectoryInfo CreateDirectory(string path, UnixFileMode unixCreateMode); +#endif + #if FEATURE_CREATE_SYMBOLIC_LINK /// IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget); #endif +#if FEATURE_CREATE_TEMP_SUBDIRECTORY + /// + IDirectoryInfo CreateTempSubdirectory(string? prefix = null); +#endif + /// void Delete(string path); diff --git a/src/TestableIO.System.IO.Abstractions/IFile.Async.cs b/src/TestableIO.System.IO.Abstractions/IFile.Async.cs index f1e089408..ec1db7f8c 100644 --- a/src/TestableIO.System.IO.Abstractions/IFile.Async.cs +++ b/src/TestableIO.System.IO.Abstractions/IFile.Async.cs @@ -53,6 +53,19 @@ Task ReadAllTextAsync(string path, Encoding encoding, CancellationToken cancellationToken = default); +#if FEATURE_READ_LINES_ASYNC + /// + IAsyncEnumerable ReadLinesAsync(string path, + CancellationToken cancellationToken = + default); + + /// + IAsyncEnumerable ReadLinesAsync(string path, Encoding encoding, + CancellationToken cancellationToken = + default); +#endif + + /// Task WriteAllBytesAsync(string path, byte[] bytes, diff --git a/src/TestableIO.System.IO.Abstractions/IFile.cs b/src/TestableIO.System.IO.Abstractions/IFile.cs index c640129e1..970635a2d 100644 --- a/src/TestableIO.System.IO.Abstractions/IFile.cs +++ b/src/TestableIO.System.IO.Abstractions/IFile.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using Microsoft.Win32.SafeHandles; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime.Versioning; using System.Text; @@ -67,25 +68,71 @@ public interface IFile : IFileSystemEntity /// FileAttributes GetAttributes(string path); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + FileAttributes GetAttributes(SafeFileHandle fileHandle); +#endif /// DateTime GetCreationTime(string path); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + DateTime GetCreationTime(SafeFileHandle fileHandle); +#endif + /// DateTime GetCreationTimeUtc(string path); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + DateTime GetCreationTimeUtc(SafeFileHandle fileHandle); +#endif + /// DateTime GetLastAccessTime(string path); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + DateTime GetLastAccessTime(SafeFileHandle fileHandle); +#endif + /// DateTime GetLastAccessTimeUtc(string path); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + DateTime GetLastAccessTimeUtc(SafeFileHandle fileHandle); +#endif + /// DateTime GetLastWriteTime(string path); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + DateTime GetLastWriteTime(SafeFileHandle fileHandle); +#endif + /// DateTime GetLastWriteTimeUtc(string path); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + DateTime GetLastWriteTimeUtc(SafeFileHandle fileHandle); +#endif + +#if FEATURE_UNIX_FILE_MODE + /// + [UnsupportedOSPlatform("windows")] + UnixFileMode GetUnixFileMode(string path); +#endif + +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + [UnsupportedOSPlatform("windows")] + UnixFileMode GetUnixFileMode(SafeFileHandle fileHandle); +#endif + /// void Move(string sourceFileName, string destFileName); @@ -159,24 +206,71 @@ void Replace(string sourceFileName, /// void SetAttributes(string path, FileAttributes fileAttributes); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + void SetAttributes(SafeFileHandle fileHandle, FileAttributes fileAttributes); +#endif + /// void SetCreationTime(string path, DateTime creationTime); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + void SetCreationTime(SafeFileHandle fileHandle, DateTime creationTime); +#endif + /// void SetCreationTimeUtc(string path, DateTime creationTimeUtc); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + void SetCreationTimeUtc(SafeFileHandle fileHandle, DateTime creationTimeUtc); +#endif + /// void SetLastAccessTime(string path, DateTime lastAccessTime); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + void SetLastAccessTime(SafeFileHandle fileHandle, DateTime lastAccessTime); +#endif + /// void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + void SetLastAccessTimeUtc(SafeFileHandle fileHandle, DateTime lastAccessTimeUtc); +#endif + /// void SetLastWriteTime(string path, DateTime lastWriteTime); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + void SetLastWriteTime(SafeFileHandle fileHandle, DateTime lastWriteTime); +#endif + /// void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc); +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + void SetLastWriteTimeUtc(SafeFileHandle fileHandle, DateTime lastWriteTimeUtc); +#endif + +#if FEATURE_UNIX_FILE_MODE + /// + [UnsupportedOSPlatform("windows")] + void SetUnixFileMode(string path, UnixFileMode mode); +#endif + +#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE + /// + [UnsupportedOSPlatform("windows")] + void SetUnixFileMode(SafeFileHandle fileHandle, UnixFileMode mode); +#endif + /// void WriteAllBytes(string path, byte[] bytes); diff --git a/src/TestableIO.System.IO.Abstractions/IFileSystemInfo.cs b/src/TestableIO.System.IO.Abstractions/IFileSystemInfo.cs index 68e6a25ee..8f2416451 100644 --- a/src/TestableIO.System.IO.Abstractions/IFileSystemInfo.cs +++ b/src/TestableIO.System.IO.Abstractions/IFileSystemInfo.cs @@ -1,4 +1,6 @@ -namespace System.IO.Abstractions +using System.Runtime.Versioning; + +namespace System.IO.Abstractions { /// public interface IFileSystemInfo @@ -46,6 +48,16 @@ public interface IFileSystemInfo /// string Name { get; } +#if FEATURE_UNIX_FILE_MODE + /// + UnixFileMode UnixFileMode + { + get; + [UnsupportedOSPlatform("windows")] + set; + } +#endif + #if FEATURE_CREATE_SYMBOLIC_LINK /// void CreateAsSymbolicLink(string pathToTarget); diff --git a/src/TestableIO.System.IO.Abstractions/IFileSystemWatcher.cs b/src/TestableIO.System.IO.Abstractions/IFileSystemWatcher.cs index 6bed2a203..edd6af6c1 100644 --- a/src/TestableIO.System.IO.Abstractions/IFileSystemWatcher.cs +++ b/src/TestableIO.System.IO.Abstractions/IFileSystemWatcher.cs @@ -64,5 +64,10 @@ public interface IFileSystemWatcher : IFileSystemEntity, IDisposable /// IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout); + +#if FEATURE_FILE_SYSTEM_WATCHER_WAIT_WITH_TIMESPAN + /// + IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout); +#endif } } \ No newline at end of file diff --git a/src/TestableIO.System.IO.Abstractions/IPath.cs b/src/TestableIO.System.IO.Abstractions/IPath.cs index 7a278f4b4..f4a30a397 100644 --- a/src/TestableIO.System.IO.Abstractions/IPath.cs +++ b/src/TestableIO.System.IO.Abstractions/IPath.cs @@ -41,6 +41,11 @@ public interface IPath : IFileSystemEntity bool EndsInDirectorySeparator(string path); #endif +#if FEATURE_PATH_EXISTS + /// + bool Exists([NotNullWhen(true)] string? path); +#endif + #if FEATURE_ADVANCED_PATH_OPERATIONS /// ReadOnlySpan GetDirectoryName(ReadOnlySpan path); diff --git a/src/TestableIO.System.IO.Abstractions/TestableIO.System.IO.Abstractions.csproj b/src/TestableIO.System.IO.Abstractions/TestableIO.System.IO.Abstractions.csproj index 28c7bb594..70a25422d 100644 --- a/src/TestableIO.System.IO.Abstractions/TestableIO.System.IO.Abstractions.csproj +++ b/src/TestableIO.System.IO.Abstractions/TestableIO.System.IO.Abstractions.csproj @@ -3,7 +3,7 @@ TestableIO.System.IO.Abstractions System.IO.Abstractions A set of abstractions to help make file system interactions testable. - net6.0;net5.0;netstandard2.1;netstandard2.0;net461 + net7.0;net6.0;net5.0;netstandard2.1;netstandard2.0;net461 icon_256x256.png enable diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileArgumentPathTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileArgumentPathTests.cs index e58809bc2..860198de7 100644 --- a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileArgumentPathTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileArgumentPathTests.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Microsoft.Win32.SafeHandles; using NUnit.Framework; namespace System.IO.Abstractions.TestingHelpers.Tests @@ -21,12 +22,12 @@ private static IEnumerable> GetFileSystemActionsForArgumentNullExc yield return fs => fs.WriteAllLines(null, new[] { "does not matter" }.ToArray(), Encoding.ASCII); yield return fs => fs.Create(null); yield return fs => fs.Delete(null); - yield return fs => fs.GetCreationTime(null); - yield return fs => fs.GetCreationTimeUtc(null); - yield return fs => fs.GetLastAccessTime(null); - yield return fs => fs.GetLastAccessTimeUtc(null); - yield return fs => fs.GetLastWriteTime(null); - yield return fs => fs.GetLastWriteTimeUtc(null); + yield return fs => fs.GetCreationTime((string)null); + yield return fs => fs.GetCreationTimeUtc((string)null); + yield return fs => fs.GetLastAccessTime((string)null); + yield return fs => fs.GetLastAccessTimeUtc((string)null); + yield return fs => fs.GetLastWriteTime((string)null); + yield return fs => fs.GetLastWriteTimeUtc((string)null); yield return fs => fs.WriteAllText(null, "does not matter"); yield return fs => fs.WriteAllText(null, "does not matter", Encoding.ASCII); yield return fs => fs.Open(null, FileMode.OpenOrCreate); @@ -42,14 +43,14 @@ private static IEnumerable> GetFileSystemActionsForArgumentNullExc yield return fs => fs.ReadAllText(null, Encoding.ASCII); yield return fs => fs.ReadLines(null); yield return fs => fs.ReadLines(null, Encoding.ASCII); - yield return fs => fs.SetAttributes(null, FileAttributes.Archive); - yield return fs => fs.GetAttributes(null); - yield return fs => fs.SetCreationTime(null, DateTime.Now); - yield return fs => fs.SetCreationTimeUtc(null, DateTime.Now); - yield return fs => fs.SetLastAccessTime(null, DateTime.Now); - yield return fs => fs.SetLastAccessTimeUtc(null, DateTime.Now); - yield return fs => fs.SetLastWriteTime(null, DateTime.Now); - yield return fs => fs.SetLastWriteTimeUtc(null, DateTime.Now); + yield return fs => fs.SetAttributes((string)null, FileAttributes.Archive); + yield return fs => fs.GetAttributes((string)null); + yield return fs => fs.SetCreationTime((string)null, DateTime.Now); + yield return fs => fs.SetCreationTimeUtc((string)null, DateTime.Now); + yield return fs => fs.SetLastAccessTime((string)null, DateTime.Now); + yield return fs => fs.SetLastAccessTimeUtc((string)null, DateTime.Now); + yield return fs => fs.SetLastWriteTime((string)null, DateTime.Now); + yield return fs => fs.SetLastWriteTimeUtc((string)null, DateTime.Now); yield return fs => fs.Decrypt(null); yield return fs => fs.Encrypt(null); } diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs index 846689c0c..49f1b4bca 100644 --- a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs @@ -489,6 +489,15 @@ public override IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeTy _ = timeout; return default(IWaitForChangedResult); } + +#if FEATURE_FILE_SYSTEM_WATCHER_WAIT_WITH_TIMESPAN + public override IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout) + { + _ = changeType; + _ = timeout; + return default(IWaitForChangedResult); + } +#endif } } } diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockPathTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockPathTests.cs index 980a996fe..47e487d9b 100644 --- a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockPathTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockPathTests.cs @@ -499,5 +499,63 @@ public void GetRelativePath_Works() Assert.AreEqual(XFS.Path("e\\f.txt"), result); } #endif + +#if FEATURE_PATH_EXISTS + [Test] + public void Exists_Null_ShouldReturnFalse() + { + var fileSystem = new MockFileSystem(); + bool result = fileSystem.Path.Exists(null); + + Assert.IsFalse(result); + } + + [Test] + public void Exists_ShouldWorkWithAbsolutePaths() + { + var fileSystem = new MockFileSystem(); + string path = "some-path"; + string absolutePath = fileSystem.Path.GetFullPath(path); + fileSystem.Directory.CreateDirectory(path); + + bool result = fileSystem.Path.Exists(absolutePath); + + Assert.IsTrue(result); + } + + [Test] + public void Exists_ExistingFile_ShouldReturnTrue() + { + var fileSystem = new MockFileSystem(); + string path = "some-path"; + fileSystem.File.WriteAllText(path, "some content"); + + bool result = fileSystem.Path.Exists(path); + + Assert.IsTrue(result); + } + + [Test] + public void Exists_ExistingDirectory_ShouldReturnTrue() + { + var fileSystem = new MockFileSystem(); + string path = "some-path"; + fileSystem.Directory.CreateDirectory(path); + + bool result = fileSystem.Path.Exists(path); + + Assert.IsTrue(result); + } + + [Test] + public void Exists_ExistingFileOrDirectory_ShouldReturnTrue() + { + var fileSystem = new MockFileSystem(); + string path = "some-path"; + bool result = fileSystem.Path.Exists(path); + + Assert.IsFalse(result); + } +#endif } } diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests.csproj b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests.csproj index 59e9e3750..ff6534465 100644 --- a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests.csproj +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests.csproj @@ -1,6 +1,6 @@  - net6.0;net5.0;netcoreapp3.1 + net7.0;net6.0;net5.0;netcoreapp3.1 $(TargetFrameworks);net461 The unit tests for our pre-built mocks System.IO.Abstractions.TestingHelpers.Tests diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/ApiParityTests.cs b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/ApiParityTests.cs index 1f0037b5e..755bee9b5 100644 --- a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/ApiParityTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/ApiParityTests.cs @@ -107,6 +107,8 @@ public ApiDiff(IEnumerable extraMembers, IEnumerable missingMemb private const string snapshotSuffix = ".NET 5.0"; #elif NET6_0 private const string snapshotSuffix = ".NET 6.0"; +#elif NET7_0 + private const string snapshotSuffix = ".NET 7.0"; #else #error Unknown target framework. #endif diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/TestableIO.System.IO.Abstractions.Wrappers.Tests.csproj b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/TestableIO.System.IO.Abstractions.Wrappers.Tests.csproj index 17e7a3d18..ddc7e34c6 100644 --- a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/TestableIO.System.IO.Abstractions.Wrappers.Tests.csproj +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/TestableIO.System.IO.Abstractions.Wrappers.Tests.csproj @@ -1,6 +1,6 @@  - net6.0;net5.0;netcoreapp3.1 + net7.0;net6.0;net5.0;netcoreapp3.1 $(TargetFrameworks);net461 The unit tests for our the core abstractions System.IO.Abstractions.Tests diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.DirectoryInfo_.NET 7.0.snap b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.DirectoryInfo_.NET 7.0.snap new file mode 100644 index 000000000..c986824b9 --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.DirectoryInfo_.NET 7.0.snap @@ -0,0 +1,9 @@ +{ + "ExtraMembers": [], + "MissingMembers": [ + "System.Object GetLifetimeService()", + "System.Object InitializeLifetimeService()", + "Void .ctor(System.String)", + "Void GetObjectData(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)" + ] +} diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.Directory_.NET 7.0.snap b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.Directory_.NET 7.0.snap new file mode 100644 index 000000000..7cb3b9523 --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.Directory_.NET 7.0.snap @@ -0,0 +1,4 @@ +{ + "ExtraMembers": [], + "MissingMembers": [] +} diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.DriveInfo_.NET 7.0.snap b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.DriveInfo_.NET 7.0.snap new file mode 100644 index 000000000..8c885b472 --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.DriveInfo_.NET 7.0.snap @@ -0,0 +1,7 @@ +{ + "ExtraMembers": [], + "MissingMembers": [ + "System.IO.Abstractions.IDriveInfo[] GetDrives()", + "Void .ctor(System.String)" + ] +} diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileInfo_.NET 7.0.snap b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileInfo_.NET 7.0.snap new file mode 100644 index 000000000..c986824b9 --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileInfo_.NET 7.0.snap @@ -0,0 +1,9 @@ +{ + "ExtraMembers": [], + "MissingMembers": [ + "System.Object GetLifetimeService()", + "System.Object InitializeLifetimeService()", + "Void .ctor(System.String)", + "Void GetObjectData(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)" + ] +} diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileSystemWatcher_.NET 7.0.snap b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileSystemWatcher_.NET 7.0.snap new file mode 100644 index 000000000..cfd946350 --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.FileSystemWatcher_.NET 7.0.snap @@ -0,0 +1,15 @@ +{ + "ExtraMembers": [ + "Void Dispose(Boolean)" + ], + "MissingMembers": [ + "System.EventHandler Disposed", + "System.Object GetLifetimeService()", + "System.Object InitializeLifetimeService()", + "Void .ctor()", + "Void .ctor(System.String)", + "Void .ctor(System.String, System.String)", + "Void add_Disposed(System.EventHandler)", + "Void remove_Disposed(System.EventHandler)" + ] +} diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.File_.NET 7.0.snap b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.File_.NET 7.0.snap new file mode 100644 index 000000000..ce42363ab --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.File_.NET 7.0.snap @@ -0,0 +1,6 @@ +{ + "ExtraMembers": [], + "MissingMembers": [ + "Microsoft.Win32.SafeHandles.SafeFileHandle OpenHandle(System.String, System.IO.FileMode, System.IO.FileAccess, System.IO.FileShare, System.IO.FileOptions, Int64)" + ] +} diff --git a/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.Path_.NET 7.0.snap b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.Path_.NET 7.0.snap new file mode 100644 index 000000000..d83d023ac --- /dev/null +++ b/tests/TestableIO.System.IO.Abstractions.Wrappers.Tests/__snapshots__/ApiParityTests.Path_.NET 7.0.snap @@ -0,0 +1,10 @@ +{ + "ExtraMembers": [ + "Char get_AltDirectorySeparatorChar()", + "Char get_DirectorySeparatorChar()", + "Char get_PathSeparator()", + "Char get_VolumeSeparatorChar()", + "Char[] get_InvalidPathChars()" + ], + "MissingMembers": [] +} diff --git a/version.json b/version.json index 0999bd51e..301497428 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "19.0", + "version": "19.1", "assemblyVersion": { "precision": "major" },