From 519815c2d06f7b9fb9344dee4e90a89ff8fd7377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Tue, 15 Nov 2022 17:54:51 +0100 Subject: [PATCH 1/9] Rename *ExtensionPoint into *Entity --- Source/Testably.Abstractions.Compression/IZipArchive.cs | 2 +- Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs | 2 +- .../Testably.Abstractions.Compression/IZipArchiveFactory.cs | 2 +- Source/Testably.Abstractions.Compression/IZipFile.cs | 2 +- .../ZipArchiveEntryWrapper.cs | 2 +- Source/Testably.Abstractions.Compression/ZipArchiveFactory.cs | 2 +- Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs | 2 +- Source/Testably.Abstractions.Compression/ZipFileWrapper.cs | 2 +- .../Testably.Abstractions.Interface/FileSystem/IDirectory.cs | 2 +- .../FileSystem/IDirectoryInfoFactory.cs | 2 +- .../Testably.Abstractions.Interface/FileSystem/IDriveInfo.cs | 2 +- .../FileSystem/IDriveInfoFactory.cs | 2 +- Source/Testably.Abstractions.Interface/FileSystem/IFile.cs | 2 +- .../FileSystem/IFileInfoFactory.cs | 2 +- .../FileSystem/IFileStreamFactory.cs | 2 +- .../{IFileSystemExtensionPoint.cs => IFileSystemEntity.cs} | 2 +- .../FileSystem/IFileSystemWatcher.cs | 2 +- .../FileSystem/IFileSystemWatcherFactory.cs | 2 +- Source/Testably.Abstractions.Interface/FileSystem/IPath.cs | 2 +- .../Testably.Abstractions.Interface/Helpers/GuidSystemBase.cs | 2 +- .../Testably.Abstractions.Interface/Helpers/PathSystemBase.cs | 2 +- Source/Testably.Abstractions.Interface/RandomSystem/IGuid.cs | 2 +- .../RandomSystem/IRandomFactory.cs | 2 +- ...{IRandomSystemExtensionPoint.cs => IRandomSystemEntity.cs} | 2 +- .../Testably.Abstractions.Interface/TimeSystem/IDateTime.cs | 2 +- Source/Testably.Abstractions.Interface/TimeSystem/ITask.cs | 2 +- Source/Testably.Abstractions.Interface/TimeSystem/IThread.cs | 2 +- .../{ITimeSystemExtensionPoint.cs => ITimeSystemEntity.cs} | 2 +- .../Testably.Abstractions.Testing/FileSystem/ChangeHandler.cs | 2 +- .../FileSystem/DirectoryInfoFactoryMock.cs | 2 +- .../Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs | 2 +- .../FileSystem/DriveInfoFactoryMock.cs | 2 +- .../Testably.Abstractions.Testing/FileSystem/DriveInfoMock.cs | 2 +- .../FileSystem/FileInfoFactoryMock.cs | 2 +- Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs | 2 +- .../FileSystem/FileStreamFactoryMock.cs | 2 +- .../FileSystem/FileSystemWatcherFactoryMock.cs | 2 +- .../FileSystem/FileSystemWatcherMock.cs | 2 +- .../FileSystem/IInterceptionHandler.cs | 2 +- .../FileSystem/INotificationHandler.cs | 2 +- .../FileSystemInitializer/FileInitializer.cs | 2 +- .../FileSystemInitializer/IFileManipulator.cs | 2 +- .../RandomSystem/RandomFactoryMock.cs | 2 +- .../Storage/IStorageContainer.cs | 4 ++-- .../Storage/InMemoryContainer.cs | 4 ++-- Source/Testably.Abstractions.Testing/Storage/NullContainer.cs | 4 ++-- .../Testably.Abstractions.Testing/TimeSystem/DateTimeMock.cs | 2 +- Source/Testably.Abstractions.Testing/TimeSystem/TaskMock.cs | 2 +- Source/Testably.Abstractions.Testing/TimeSystem/ThreadMock.cs | 2 +- .../Testably.Abstractions/FileSystem/DirectoryInfoFactory.cs | 2 +- Source/Testably.Abstractions/FileSystem/DirectoryWrapper.cs | 2 +- Source/Testably.Abstractions/FileSystem/DriveInfoFactory.cs | 2 +- Source/Testably.Abstractions/FileSystem/DriveInfoWrapper.cs | 2 +- Source/Testably.Abstractions/FileSystem/FileInfoFactory.cs | 2 +- Source/Testably.Abstractions/FileSystem/FileStreamFactory.cs | 2 +- .../FileSystem/FileSystemWatcherFactory.cs | 2 +- .../FileSystem/FileSystemWatcherWrapper.cs | 2 +- Source/Testably.Abstractions/FileSystem/FileWrapper.cs | 2 +- Source/Testably.Abstractions/RandomSystem/RandomFactory.cs | 2 +- Source/Testably.Abstractions/TimeSystem/DateTimeWrapper.cs | 2 +- Source/Testably.Abstractions/TimeSystem/TaskWrapper.cs | 2 +- Source/Testably.Abstractions/TimeSystem/ThreadWrapper.cs | 2 +- .../TestHelpers/LockableContainer.cs | 4 ++-- 63 files changed, 67 insertions(+), 67 deletions(-) rename Source/Testably.Abstractions.Interface/FileSystem/{IFileSystemExtensionPoint.cs => IFileSystemEntity.cs} (90%) rename Source/Testably.Abstractions.Interface/RandomSystem/{IRandomSystemExtensionPoint.cs => IRandomSystemEntity.cs} (90%) rename Source/Testably.Abstractions.Interface/TimeSystem/{ITimeSystemExtensionPoint.cs => ITimeSystemEntity.cs} (90%) diff --git a/Source/Testably.Abstractions.Compression/IZipArchive.cs b/Source/Testably.Abstractions.Compression/IZipArchive.cs index 06a975ee5..7495381ff 100644 --- a/Source/Testably.Abstractions.Compression/IZipArchive.cs +++ b/Source/Testably.Abstractions.Compression/IZipArchive.cs @@ -6,7 +6,7 @@ namespace Testably.Abstractions; /// -public interface IZipArchive : IFileSystemExtensionPoint, IDisposable +public interface IZipArchive : IFileSystemEntity, IDisposable { #if FEATURE_ZIPFILE_NET7 /// diff --git a/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs b/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs index fc60d0c16..037b3e64e 100644 --- a/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs +++ b/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs @@ -6,7 +6,7 @@ namespace Testably.Abstractions; /// -public interface IZipArchiveEntry : IFileSystemExtensionPoint +public interface IZipArchiveEntry : IFileSystemEntity { /// IZipArchive Archive { get; } diff --git a/Source/Testably.Abstractions.Compression/IZipArchiveFactory.cs b/Source/Testably.Abstractions.Compression/IZipArchiveFactory.cs index f49c3950f..4fa0d3d15 100644 --- a/Source/Testably.Abstractions.Compression/IZipArchiveFactory.cs +++ b/Source/Testably.Abstractions.Compression/IZipArchiveFactory.cs @@ -6,7 +6,7 @@ namespace Testably.Abstractions; /// -public interface IZipArchiveFactory : IFileSystemExtensionPoint +public interface IZipArchiveFactory : IFileSystemEntity { /// IZipArchive New(Stream stream); diff --git a/Source/Testably.Abstractions.Compression/IZipFile.cs b/Source/Testably.Abstractions.Compression/IZipFile.cs index 8dec91cef..fac7c40e6 100644 --- a/Source/Testably.Abstractions.Compression/IZipFile.cs +++ b/Source/Testably.Abstractions.Compression/IZipFile.cs @@ -5,7 +5,7 @@ namespace Testably.Abstractions; /// -public interface IZipFile : IFileSystemExtensionPoint +public interface IZipFile : IFileSystemEntity { /// void CreateFromDirectory(string sourceDirectoryName, diff --git a/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs b/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs index 9271e5e3a..fa53c6ee2 100644 --- a/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs +++ b/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs @@ -53,7 +53,7 @@ public int ExternalAttributes } #endif - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions.Compression/ZipArchiveFactory.cs b/Source/Testably.Abstractions.Compression/ZipArchiveFactory.cs index d9df18a85..906df81b6 100644 --- a/Source/Testably.Abstractions.Compression/ZipArchiveFactory.cs +++ b/Source/Testably.Abstractions.Compression/ZipArchiveFactory.cs @@ -14,7 +14,7 @@ public ZipArchiveFactory(IFileSystem fileSystem) #region IZipArchiveFactory Members - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs b/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs index 18dd7dc9b..0342a3e1e 100644 --- a/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs +++ b/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs @@ -32,7 +32,7 @@ public string Comment public ReadOnlyCollection Entries => MapToZipArchiveEntries(_instance.Entries); - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs b/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs index 942b02aab..a2933ab14 100644 --- a/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs +++ b/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs @@ -14,7 +14,7 @@ public ZipFileWrapper(IFileSystem fileSystem) #region IZipFile Members - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IDirectory.cs b/Source/Testably.Abstractions.Interface/FileSystem/IDirectory.cs index 83e36578d..6cd897b23 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IDirectory.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IDirectory.cs @@ -8,7 +8,7 @@ namespace Testably.Abstractions.FileSystem; /// /// Abstractions for . /// -public interface IDirectory : IFileSystemExtensionPoint +public interface IDirectory : IFileSystemEntity { /// IDirectoryInfo CreateDirectory(string path); diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IDirectoryInfoFactory.cs b/Source/Testably.Abstractions.Interface/FileSystem/IDirectoryInfoFactory.cs index 4350eaa21..8c35c86cb 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IDirectoryInfoFactory.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IDirectoryInfoFactory.cs @@ -6,7 +6,7 @@ namespace Testably.Abstractions.FileSystem; /// /// Factory for abstracting the creation of . /// -public interface IDirectoryInfoFactory : IFileSystemExtensionPoint +public interface IDirectoryInfoFactory : IFileSystemEntity { /// IDirectoryInfo New(string path); diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IDriveInfo.cs b/Source/Testably.Abstractions.Interface/FileSystem/IDriveInfo.cs index f8735828d..752880723 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IDriveInfo.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IDriveInfo.cs @@ -6,7 +6,7 @@ namespace Testably.Abstractions.FileSystem; /// /// Abstractions for . /// -public interface IDriveInfo : IFileSystemExtensionPoint +public interface IDriveInfo : IFileSystemEntity { /// long AvailableFreeSpace { get; } diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IDriveInfoFactory.cs b/Source/Testably.Abstractions.Interface/FileSystem/IDriveInfoFactory.cs index 6b4d933fc..8d98c74ee 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IDriveInfoFactory.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IDriveInfoFactory.cs @@ -6,7 +6,7 @@ namespace Testably.Abstractions.FileSystem; /// /// Factory for abstracting the creation of . /// -public interface IDriveInfoFactory : IFileSystemExtensionPoint +public interface IDriveInfoFactory : IFileSystemEntity { /// IDriveInfo[] GetDrives(); diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IFile.cs b/Source/Testably.Abstractions.Interface/FileSystem/IFile.cs index 7fccf3848..925013b0e 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IFile.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IFile.cs @@ -16,7 +16,7 @@ namespace Testably.Abstractions.FileSystem; /// /// Abstractions for . /// -public interface IFile : IFileSystemExtensionPoint +public interface IFile : IFileSystemEntity { /// void AppendAllLines(string path, IEnumerable contents); diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IFileInfoFactory.cs b/Source/Testably.Abstractions.Interface/FileSystem/IFileInfoFactory.cs index b0bda18a7..48a816e73 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IFileInfoFactory.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IFileInfoFactory.cs @@ -6,7 +6,7 @@ namespace Testably.Abstractions.FileSystem; /// /// Factory for abstracting the creation of . /// -public interface IFileInfoFactory : IFileSystemExtensionPoint +public interface IFileInfoFactory : IFileSystemEntity { /// IFileInfo New(string fileName); diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IFileStreamFactory.cs b/Source/Testably.Abstractions.Interface/FileSystem/IFileStreamFactory.cs index 1a0f746b2..a4c2f690c 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IFileStreamFactory.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IFileStreamFactory.cs @@ -6,7 +6,7 @@ namespace Testably.Abstractions.FileSystem; /// /// Factory for abstracting creation of . /// -public interface IFileStreamFactory : IFileSystemExtensionPoint +public interface IFileStreamFactory : IFileSystemEntity { /// FileSystemStream New(SafeFileHandle handle, FileAccess access); diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemExtensionPoint.cs b/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemEntity.cs similarity index 90% rename from Source/Testably.Abstractions.Interface/FileSystem/IFileSystemExtensionPoint.cs rename to Source/Testably.Abstractions.Interface/FileSystem/IFileSystemEntity.cs index a9bdbcc62..d280fe53c 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemExtensionPoint.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemEntity.cs @@ -3,7 +3,7 @@ /// /// Interface to support implementing extension methods on top of nested interfaces. /// -public interface IFileSystemExtensionPoint +public interface IFileSystemEntity { /// /// Exposes the underlying file system implementation. diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemWatcher.cs b/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemWatcher.cs index 606240575..687eb378c 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemWatcher.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemWatcher.cs @@ -10,7 +10,7 @@ namespace Testably.Abstractions.FileSystem; /// /// Abstractions for . /// -public interface IFileSystemWatcher : IFileSystemExtensionPoint, IDisposable +public interface IFileSystemWatcher : IFileSystemEntity, IDisposable { /// IContainer? Container { get; } diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemWatcherFactory.cs b/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemWatcherFactory.cs index fafd54a51..6c1465b21 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemWatcherFactory.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemWatcherFactory.cs @@ -6,7 +6,7 @@ namespace Testably.Abstractions.FileSystem; /// /// Factory for abstracting the creation of . /// -public interface IFileSystemWatcherFactory : IFileSystemExtensionPoint +public interface IFileSystemWatcherFactory : IFileSystemEntity { /// IFileSystemWatcher New(); diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IPath.cs b/Source/Testably.Abstractions.Interface/FileSystem/IPath.cs index 632cb36fa..671908142 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IPath.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IPath.cs @@ -9,7 +9,7 @@ namespace Testably.Abstractions.FileSystem; /// /// Abstractions for . /// -public interface IPath : IFileSystemExtensionPoint +public interface IPath : IFileSystemEntity { /// char AltDirectorySeparatorChar { get; } diff --git a/Source/Testably.Abstractions.Interface/Helpers/GuidSystemBase.cs b/Source/Testably.Abstractions.Interface/Helpers/GuidSystemBase.cs index bd3e7584b..38f69a968 100644 --- a/Source/Testably.Abstractions.Interface/Helpers/GuidSystemBase.cs +++ b/Source/Testably.Abstractions.Interface/Helpers/GuidSystemBase.cs @@ -26,7 +26,7 @@ protected GuidSystemBase(IRandomSystem randomSystem) /// public Guid Empty => Guid.Empty; - /// + /// public IRandomSystem RandomSystem { get; } /// diff --git a/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs b/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs index 2f1c07b10..d28b6f7b1 100644 --- a/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs +++ b/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs @@ -32,7 +32,7 @@ public char AltDirectorySeparatorChar public char DirectorySeparatorChar => Path.DirectorySeparatorChar; - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions.Interface/RandomSystem/IGuid.cs b/Source/Testably.Abstractions.Interface/RandomSystem/IGuid.cs index 8ed7eb8a5..be5f49aa6 100644 --- a/Source/Testably.Abstractions.Interface/RandomSystem/IGuid.cs +++ b/Source/Testably.Abstractions.Interface/RandomSystem/IGuid.cs @@ -8,7 +8,7 @@ namespace Testably.Abstractions.RandomSystem; /// /// Abstractions for . /// -public interface IGuid : IRandomSystemExtensionPoint +public interface IGuid : IRandomSystemEntity { /// Guid Empty { get; } diff --git a/Source/Testably.Abstractions.Interface/RandomSystem/IRandomFactory.cs b/Source/Testably.Abstractions.Interface/RandomSystem/IRandomFactory.cs index b66681384..3f7da3d47 100644 --- a/Source/Testably.Abstractions.Interface/RandomSystem/IRandomFactory.cs +++ b/Source/Testably.Abstractions.Interface/RandomSystem/IRandomFactory.cs @@ -3,7 +3,7 @@ /// /// Factory for abstracting creation of . /// -public interface IRandomFactory : IRandomSystemExtensionPoint +public interface IRandomFactory : IRandomSystemEntity { /// /// Provides a thread-safe instance that may be used concurrently from any thread. diff --git a/Source/Testably.Abstractions.Interface/RandomSystem/IRandomSystemExtensionPoint.cs b/Source/Testably.Abstractions.Interface/RandomSystem/IRandomSystemEntity.cs similarity index 90% rename from Source/Testably.Abstractions.Interface/RandomSystem/IRandomSystemExtensionPoint.cs rename to Source/Testably.Abstractions.Interface/RandomSystem/IRandomSystemEntity.cs index f84b84452..925763e5a 100644 --- a/Source/Testably.Abstractions.Interface/RandomSystem/IRandomSystemExtensionPoint.cs +++ b/Source/Testably.Abstractions.Interface/RandomSystem/IRandomSystemEntity.cs @@ -3,7 +3,7 @@ /// /// Interface to support implementing extension methods on top of nested interfaces. /// -public interface IRandomSystemExtensionPoint +public interface IRandomSystemEntity { /// /// Exposes the underlying random system implementation. diff --git a/Source/Testably.Abstractions.Interface/TimeSystem/IDateTime.cs b/Source/Testably.Abstractions.Interface/TimeSystem/IDateTime.cs index 991238f28..329d4335d 100644 --- a/Source/Testably.Abstractions.Interface/TimeSystem/IDateTime.cs +++ b/Source/Testably.Abstractions.Interface/TimeSystem/IDateTime.cs @@ -5,7 +5,7 @@ namespace Testably.Abstractions.TimeSystem; /// /// Abstractions for . /// -public interface IDateTime : ITimeSystemExtensionPoint +public interface IDateTime : ITimeSystemEntity { /// DateTime MaxValue { get; } diff --git a/Source/Testably.Abstractions.Interface/TimeSystem/ITask.cs b/Source/Testably.Abstractions.Interface/TimeSystem/ITask.cs index 054006f7f..47220fd1b 100644 --- a/Source/Testably.Abstractions.Interface/TimeSystem/ITask.cs +++ b/Source/Testably.Abstractions.Interface/TimeSystem/ITask.cs @@ -7,7 +7,7 @@ namespace Testably.Abstractions.TimeSystem; /// /// Abstractions for . /// -public interface ITask : ITimeSystemExtensionPoint +public interface ITask : ITimeSystemEntity { /// Task Delay(int millisecondsDelay); diff --git a/Source/Testably.Abstractions.Interface/TimeSystem/IThread.cs b/Source/Testably.Abstractions.Interface/TimeSystem/IThread.cs index 7ae420dee..6b9ce026c 100644 --- a/Source/Testably.Abstractions.Interface/TimeSystem/IThread.cs +++ b/Source/Testably.Abstractions.Interface/TimeSystem/IThread.cs @@ -5,7 +5,7 @@ namespace Testably.Abstractions.TimeSystem; /// /// Abstractions for . /// -public interface IThread : ITimeSystemExtensionPoint +public interface IThread : ITimeSystemEntity { /// void Sleep(int millisecondsTimeout); diff --git a/Source/Testably.Abstractions.Interface/TimeSystem/ITimeSystemExtensionPoint.cs b/Source/Testably.Abstractions.Interface/TimeSystem/ITimeSystemEntity.cs similarity index 90% rename from Source/Testably.Abstractions.Interface/TimeSystem/ITimeSystemExtensionPoint.cs rename to Source/Testably.Abstractions.Interface/TimeSystem/ITimeSystemEntity.cs index e57fb716d..5bb5d48c4 100644 --- a/Source/Testably.Abstractions.Interface/TimeSystem/ITimeSystemExtensionPoint.cs +++ b/Source/Testably.Abstractions.Interface/TimeSystem/ITimeSystemEntity.cs @@ -3,7 +3,7 @@ /// /// Interface to support implementing extension methods on top of nested interfaces. /// -public interface ITimeSystemExtensionPoint +public interface ITimeSystemEntity { /// /// Exposes the underlying time system implementation. diff --git a/Source/Testably.Abstractions.Testing/FileSystem/ChangeHandler.cs b/Source/Testably.Abstractions.Testing/FileSystem/ChangeHandler.cs index e4593e1b3..cea43baaf 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/ChangeHandler.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/ChangeHandler.cs @@ -23,7 +23,7 @@ public ChangeHandler(MockFileSystem mockFileSystem) #region IInterceptionHandler Members - /// + /// public IFileSystem FileSystem => _mockFileSystem; /// + /// public IFileSystem FileSystem => _fileSystem; diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs index fc7d0fda2..31093366d 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs @@ -20,7 +20,7 @@ internal DirectoryMock(MockFileSystem fileSystem) #region IDirectory Members - /// + /// public IFileSystem FileSystem => _fileSystem; diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs index 81e359510..5385377dc 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs @@ -18,7 +18,7 @@ internal DriveInfoFactoryMock(MockFileSystem fileSystem) #region IDriveInfoFactory Members - /// + /// public IFileSystem FileSystem => _fileSystem; diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoMock.cs index 25cec0d7d..bb7ecd6d4 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoMock.cs @@ -68,7 +68,7 @@ public long AvailableFreeSpace /// public DriveType DriveType { get; private set; } - /// + /// public IFileSystem FileSystem => _fileSystem; diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs index 18666dfc7..42f52f644 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs @@ -17,7 +17,7 @@ internal FileInfoFactoryMock(MockFileSystem fileSystem) #region IFileInfoFactory Members - /// + /// public IFileSystem FileSystem => _fileSystem; diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs index 7962b970c..b62d4f21b 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs @@ -28,7 +28,7 @@ internal FileMock(MockFileSystem fileSystem) #region IFile Members - /// + /// public IFileSystem FileSystem => _fileSystem; diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs index b12535578..e6c295336 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs @@ -22,7 +22,7 @@ internal FileStreamFactoryMock(MockFileSystem fileSystem) #region IFileStreamFactory Members - /// + /// public IFileSystem FileSystem => _fileSystem; diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs index 8e6a2ba20..f028a3e10 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs @@ -17,7 +17,7 @@ internal FileSystemWatcherFactoryMock(MockFileSystem fileSystem) #region IFileSystemWatcherFactory Members - /// + /// public IFileSystem FileSystem => _fileSystem; diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs index cccd7bc6c..b45b9aef4 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs @@ -67,7 +67,7 @@ public bool EnableRaisingEvents } } - /// + /// public IFileSystem FileSystem => _fileSystem; diff --git a/Source/Testably.Abstractions.Testing/FileSystem/IInterceptionHandler.cs b/Source/Testably.Abstractions.Testing/FileSystem/IInterceptionHandler.cs index 09b0b02a3..0d4bb37a7 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/IInterceptionHandler.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/IInterceptionHandler.cs @@ -6,7 +6,7 @@ namespace Testably.Abstractions.Testing.FileSystem; /// /// The interception handler for the . /// -public interface IInterceptionHandler : IFileSystemExtensionPoint +public interface IInterceptionHandler : IFileSystemEntity { /// /// Callback executed before any change in the matching the diff --git a/Source/Testably.Abstractions.Testing/FileSystem/INotificationHandler.cs b/Source/Testably.Abstractions.Testing/FileSystem/INotificationHandler.cs index 993108dce..bcf75ec96 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/INotificationHandler.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/INotificationHandler.cs @@ -6,7 +6,7 @@ namespace Testably.Abstractions.Testing.FileSystem; /// /// The notification handler for the . /// -public interface INotificationHandler : IFileSystemExtensionPoint +public interface INotificationHandler : IFileSystemEntity { /// /// Callback executed when any change in the matching the diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs index 2816f1305..48fbc851b 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs @@ -44,7 +44,7 @@ internal FileManipulator(IFileSystem fileSystem, IFileInfo file) /// public IFileInfo File { get; } - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileManipulator.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileManipulator.cs index a5b969d9d..8156fca22 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileManipulator.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileManipulator.cs @@ -5,7 +5,7 @@ namespace Testably.Abstractions.Testing.FileSystemInitializer; /// /// Manipulates the in the with test data. /// -public interface IFileManipulator : IFileSystemExtensionPoint +public interface IFileManipulator : IFileSystemEntity { /// /// The file to initialize. diff --git a/Source/Testably.Abstractions.Testing/RandomSystem/RandomFactoryMock.cs b/Source/Testably.Abstractions.Testing/RandomSystem/RandomFactoryMock.cs index f5ba07d54..dd62c334b 100644 --- a/Source/Testably.Abstractions.Testing/RandomSystem/RandomFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/RandomSystem/RandomFactoryMock.cs @@ -13,7 +13,7 @@ internal RandomFactoryMock(MockRandomSystem randomSystem) #region IRandomFactory Members - /// + /// public IRandomSystem RandomSystem => _mockRandomSystem; /// diff --git a/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs b/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs index d7c462e25..223f40d69 100644 --- a/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs +++ b/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs @@ -8,8 +8,8 @@ namespace Testably.Abstractions.Testing.Storage; /// /// A container for a stored file or directory in the . /// -internal interface IStorageContainer : IFileSystemExtensionPoint, - ITimeSystemExtensionPoint +internal interface IStorageContainer : IFileSystemEntity, + ITimeSystemEntity { /// FileAttributes Attributes { get; set; } diff --git a/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs b/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs index 5bf820333..2549d3846 100644 --- a/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs +++ b/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs @@ -67,7 +67,7 @@ public FileAttributes Attributes public IFileSystemExtensionContainer ExtensionContainer => _extensionContainer; - /// + /// public IFileSystem FileSystem => _fileSystem; /// @@ -79,7 +79,7 @@ public IFileSystemExtensionContainer ExtensionContainer /// public string? LinkTarget { get; set; } - /// + /// public ITimeSystem TimeSystem => _fileSystem.TimeSystem; /// diff --git a/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs b/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs index 8a4c3fec4..33e1a0544 100644 --- a/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs +++ b/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs @@ -31,7 +31,7 @@ public FileAttributes Attributes /// public IFileSystemExtensionContainer ExtensionContainer { get; } - /// + /// public IFileSystem FileSystem { get; } /// @@ -47,7 +47,7 @@ public string? LinkTarget set => _ = value; } - /// + /// public ITimeSystem TimeSystem { get; } /// diff --git a/Source/Testably.Abstractions.Testing/TimeSystem/DateTimeMock.cs b/Source/Testably.Abstractions.Testing/TimeSystem/DateTimeMock.cs index cb218b3e1..08502c110 100644 --- a/Source/Testably.Abstractions.Testing/TimeSystem/DateTimeMock.cs +++ b/Source/Testably.Abstractions.Testing/TimeSystem/DateTimeMock.cs @@ -36,7 +36,7 @@ public DateTime Now } } - /// + /// public ITimeSystem TimeSystem => _mockTimeSystem; diff --git a/Source/Testably.Abstractions.Testing/TimeSystem/TaskMock.cs b/Source/Testably.Abstractions.Testing/TimeSystem/TaskMock.cs index fca58296d..6c1c0739f 100644 --- a/Source/Testably.Abstractions.Testing/TimeSystem/TaskMock.cs +++ b/Source/Testably.Abstractions.Testing/TimeSystem/TaskMock.cs @@ -20,7 +20,7 @@ internal TaskMock(MockTimeSystem timeSystem, #region ITask Members - /// + /// public ITimeSystem TimeSystem => _mockTimeSystem; diff --git a/Source/Testably.Abstractions.Testing/TimeSystem/ThreadMock.cs b/Source/Testably.Abstractions.Testing/TimeSystem/ThreadMock.cs index 97c198db1..3f4a826a3 100644 --- a/Source/Testably.Abstractions.Testing/TimeSystem/ThreadMock.cs +++ b/Source/Testably.Abstractions.Testing/TimeSystem/ThreadMock.cs @@ -19,7 +19,7 @@ internal ThreadMock(MockTimeSystem timeSystem, #region IThread Members - /// + /// public ITimeSystem TimeSystem => _mockTimeSystem; diff --git a/Source/Testably.Abstractions/FileSystem/DirectoryInfoFactory.cs b/Source/Testably.Abstractions/FileSystem/DirectoryInfoFactory.cs index 39f46a273..f4cfc5518 100644 --- a/Source/Testably.Abstractions/FileSystem/DirectoryInfoFactory.cs +++ b/Source/Testably.Abstractions/FileSystem/DirectoryInfoFactory.cs @@ -12,7 +12,7 @@ internal DirectoryInfoFactory(RealFileSystem fileSystem) #region IDirectoryInfoFactory Members - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions/FileSystem/DirectoryWrapper.cs b/Source/Testably.Abstractions/FileSystem/DirectoryWrapper.cs index 3273f836c..3a66c5af6 100644 --- a/Source/Testably.Abstractions/FileSystem/DirectoryWrapper.cs +++ b/Source/Testably.Abstractions/FileSystem/DirectoryWrapper.cs @@ -14,7 +14,7 @@ internal DirectoryWrapper(RealFileSystem fileSystem) #region IDirectory Members - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions/FileSystem/DriveInfoFactory.cs b/Source/Testably.Abstractions/FileSystem/DriveInfoFactory.cs index 07d2fcdb0..83bdee3fa 100644 --- a/Source/Testably.Abstractions/FileSystem/DriveInfoFactory.cs +++ b/Source/Testably.Abstractions/FileSystem/DriveInfoFactory.cs @@ -13,7 +13,7 @@ internal DriveInfoFactory(RealFileSystem fileSystem) #region IDriveInfoFactory Members - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions/FileSystem/DriveInfoWrapper.cs b/Source/Testably.Abstractions/FileSystem/DriveInfoWrapper.cs index 2c52f7f64..265ead3c6 100644 --- a/Source/Testably.Abstractions/FileSystem/DriveInfoWrapper.cs +++ b/Source/Testably.Abstractions/FileSystem/DriveInfoWrapper.cs @@ -27,7 +27,7 @@ public string DriveFormat public DriveType DriveType => _instance.DriveType; - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions/FileSystem/FileInfoFactory.cs b/Source/Testably.Abstractions/FileSystem/FileInfoFactory.cs index 65660b482..14ac45803 100644 --- a/Source/Testably.Abstractions/FileSystem/FileInfoFactory.cs +++ b/Source/Testably.Abstractions/FileSystem/FileInfoFactory.cs @@ -12,7 +12,7 @@ internal FileInfoFactory(RealFileSystem fileSystem) #region IFileInfoFactory Members - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions/FileSystem/FileStreamFactory.cs b/Source/Testably.Abstractions/FileSystem/FileStreamFactory.cs index 872ca5645..55ff1b24d 100644 --- a/Source/Testably.Abstractions/FileSystem/FileStreamFactory.cs +++ b/Source/Testably.Abstractions/FileSystem/FileStreamFactory.cs @@ -15,7 +15,7 @@ internal FileStreamFactory(RealFileSystem fileSystem) #region IFileStreamFactory Members - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions/FileSystem/FileSystemWatcherFactory.cs b/Source/Testably.Abstractions/FileSystem/FileSystemWatcherFactory.cs index de1c23ce0..dcbf06a2c 100644 --- a/Source/Testably.Abstractions/FileSystem/FileSystemWatcherFactory.cs +++ b/Source/Testably.Abstractions/FileSystem/FileSystemWatcherFactory.cs @@ -12,7 +12,7 @@ internal FileSystemWatcherFactory(RealFileSystem fileSystem) #region IFileSystemWatcherFactory Members - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions/FileSystem/FileSystemWatcherWrapper.cs b/Source/Testably.Abstractions/FileSystem/FileSystemWatcherWrapper.cs index 8f2f31129..bd61dacc2 100644 --- a/Source/Testably.Abstractions/FileSystem/FileSystemWatcherWrapper.cs +++ b/Source/Testably.Abstractions/FileSystem/FileSystemWatcherWrapper.cs @@ -32,7 +32,7 @@ public bool EnableRaisingEvents set => _instance.EnableRaisingEvents = value; } - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions/FileSystem/FileWrapper.cs b/Source/Testably.Abstractions/FileSystem/FileWrapper.cs index 4d6287da3..b98bbabe5 100644 --- a/Source/Testably.Abstractions/FileSystem/FileWrapper.cs +++ b/Source/Testably.Abstractions/FileSystem/FileWrapper.cs @@ -22,7 +22,7 @@ internal FileWrapper(RealFileSystem fileSystem) #region IFile Members - /// + /// public IFileSystem FileSystem { get; } /// diff --git a/Source/Testably.Abstractions/RandomSystem/RandomFactory.cs b/Source/Testably.Abstractions/RandomSystem/RandomFactory.cs index 8aaff7da7..3f241af60 100644 --- a/Source/Testably.Abstractions/RandomSystem/RandomFactory.cs +++ b/Source/Testably.Abstractions/RandomSystem/RandomFactory.cs @@ -15,7 +15,7 @@ internal RandomFactory(RealRandomSystem timeSystem) #region IRandomFactory Members - /// + /// public IRandomSystem RandomSystem { get; } /// diff --git a/Source/Testably.Abstractions/TimeSystem/DateTimeWrapper.cs b/Source/Testably.Abstractions/TimeSystem/DateTimeWrapper.cs index eddbd834f..f6c3d7eb0 100644 --- a/Source/Testably.Abstractions/TimeSystem/DateTimeWrapper.cs +++ b/Source/Testably.Abstractions/TimeSystem/DateTimeWrapper.cs @@ -40,7 +40,7 @@ public DateTime UtcNow public DateTime Today => DateTime.Today; - /// + /// public ITimeSystem TimeSystem { get; } #endregion diff --git a/Source/Testably.Abstractions/TimeSystem/TaskWrapper.cs b/Source/Testably.Abstractions/TimeSystem/TaskWrapper.cs index f7dd1ec35..54e6e32e0 100644 --- a/Source/Testably.Abstractions/TimeSystem/TaskWrapper.cs +++ b/Source/Testably.Abstractions/TimeSystem/TaskWrapper.cs @@ -13,7 +13,7 @@ internal TaskWrapper(RealTimeSystem timeSystem) #region ITask Members - /// + /// public ITimeSystem TimeSystem { get; } /// diff --git a/Source/Testably.Abstractions/TimeSystem/ThreadWrapper.cs b/Source/Testably.Abstractions/TimeSystem/ThreadWrapper.cs index 945a1146f..10c22c4fc 100644 --- a/Source/Testably.Abstractions/TimeSystem/ThreadWrapper.cs +++ b/Source/Testably.Abstractions/TimeSystem/ThreadWrapper.cs @@ -12,7 +12,7 @@ internal ThreadWrapper(RealTimeSystem timeSystem) #region IThread Members - /// + /// public ITimeSystem TimeSystem { get; } /// diff --git a/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs b/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs index df3766533..1f817e3da 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs @@ -51,7 +51,7 @@ public LockableContainer(MockFileSystem fileSystem, public IFileSystemExtensionContainer ExtensionContainer { get; } = new FileSystemExtensionContainer(); - /// + /// public IFileSystem FileSystem { get; } /// @@ -65,7 +65,7 @@ public LockableContainer(MockFileSystem fileSystem, /// public string? LinkTarget { get; set; } - /// + /// public ITimeSystem TimeSystem { get; } /// From 0ec1d99927998f69d9b9056867564e9f6f6c8890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Tue, 15 Nov 2022 18:11:16 +0100 Subject: [PATCH 2/9] Rename ExtensionContainer into Extensibility --- .../DirectoryAclExtensions.cs | 32 ++++++++--------- .../DirectoryInfoAclExtensions.cs | 32 ++++++++--------- .../FileAclExtensions.cs | 24 ++++++------- .../FileInfoAclExtensions.cs | 24 ++++++------- .../FileStreamAclExtensions.cs | 16 ++++----- .../FileSystem/FileSystemStream.cs | 2 +- ...ntainer.cs => IFileSystemExtensibility.cs} | 4 +-- .../FileSystem/IFileSystemInfo.cs | 2 +- .../DefaultAccessControlStrategy.cs | 10 +++--- .../FileSystem/DirectoryInfoMock.cs | 2 +- .../FileSystem/FileStreamMock.cs | 6 ++-- .../FileSystem/FileSystemInfoMock.cs | 6 ++-- .../FileSystem/IAccessControlStrategy.cs | 4 +-- .../FileSystem/NullAccessControlStrategy.cs | 4 +-- ...ontainer.cs => FileSystemExtensibility.cs} | 14 ++++---- .../MockFileSystem.cs | 2 +- .../Storage/IStorage.cs | 8 ++--- .../Storage/IStorageContainer.cs | 2 +- .../Storage/InMemoryContainer.cs | 10 +++--- .../Storage/InMemoryStorage.cs | 6 ++-- .../Storage/NullContainer.cs | 6 ++-- .../FileSystem/FileStreamWrapper.cs | 6 ++-- ...ontainer.cs => FileSystemExtensibility.cs} | 8 ++--- .../FileSystem/FileSystemInfoWrapper.cs | 6 ++-- .../DefaultAccessControlStrategyTests.cs | 4 +-- .../TestHelpers/LockableContainer.cs | 6 ++-- .../FileSystem/FileStream/Tests.cs | 6 ++-- .../FileSystem/FileSystemInfo/Tests.cs | 6 ++-- ...er.cs => FileSystemTests.Extensibility.cs} | 34 +++++++++---------- 29 files changed, 145 insertions(+), 147 deletions(-) rename Source/Testably.Abstractions.Interface/FileSystem/{IFileSystemExtensionContainer.cs => IFileSystemExtensibility.cs} (91%) rename Source/Testably.Abstractions.Testing/Helpers/{FileSystemExtensionContainer.cs => FileSystemExtensibility.cs} (58%) rename Source/Testably.Abstractions/FileSystem/{FileSystemExtensionContainer.cs => FileSystemExtensibility.cs} (75%) rename Tests/Testably.Abstractions.Tests/FileSystem/{FileSystemTests.ExtensionContainer.cs => FileSystemTests.Extensibility.cs} (53%) diff --git a/Source/Testably.Abstractions.AccessControl/DirectoryAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/DirectoryAclExtensions.cs index 086f91db7..48d855cea 100644 --- a/Source/Testably.Abstractions.AccessControl/DirectoryAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/DirectoryAclExtensions.cs @@ -18,9 +18,9 @@ public static void CreateDirectory(this IDirectory directory, { IDirectoryInfo directoryInfo = directory.FileSystem.DirectoryInfo.New(path); - IFileSystemExtensionContainer extensionContainer = - directoryInfo.ExtensionContainer; - if (extensionContainer.HasWrappedInstance(out DirectoryInfo? di)) + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) { di.Create(directorySecurity); } @@ -29,7 +29,7 @@ public static void CreateDirectory(this IDirectory directory, _ = directorySecurity ?? throw new ArgumentNullException(nameof(directorySecurity)); directoryInfo.ThrowIfParentMissing(); directoryInfo.Create(); - directoryInfo.ExtensionContainer.StoreMetadata(AccessControlHelpers.AccessControl, + directoryInfo.Extensibility.StoreMetadata(AccessControlHelpers.AccessControl, directorySecurity); } } @@ -42,11 +42,11 @@ public static DirectorySecurity GetAccessControl( IDirectoryInfo directoryInfo = directory.FileSystem.DirectoryInfo.New(path); directoryInfo.ThrowIfMissing(); - IFileSystemExtensionContainer extensionContainer = - directoryInfo.ExtensionContainer; - return extensionContainer.HasWrappedInstance(out DirectoryInfo? di) + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) ? di.GetAccessControl() - : extensionContainer.RetrieveMetadata( + : extensibility.RetrieveMetadata( AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); } @@ -60,11 +60,11 @@ public static DirectorySecurity GetAccessControl( IDirectoryInfo directoryInfo = directory.FileSystem.DirectoryInfo.New(path); directoryInfo.ThrowIfMissing(); - IFileSystemExtensionContainer extensionContainer = - directoryInfo.ExtensionContainer; - return extensionContainer.HasWrappedInstance(out DirectoryInfo? di) + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) ? di.GetAccessControl(includeSections) - : extensionContainer.RetrieveMetadata( + : extensibility.RetrieveMetadata( AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); } @@ -76,15 +76,15 @@ public static void SetAccessControl(this IDirectory directory, { IDirectoryInfo directoryInfo = directory.FileSystem.DirectoryInfo.New(path); - IFileSystemExtensionContainer extensionContainer = - directoryInfo.ExtensionContainer; - if (extensionContainer.HasWrappedInstance(out DirectoryInfo? di)) + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) { di.SetAccessControl(directorySecurity); } else { - extensionContainer.StoreMetadata(AccessControlHelpers.AccessControl, + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, directorySecurity); } } diff --git a/Source/Testably.Abstractions.AccessControl/DirectoryInfoAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/DirectoryInfoAclExtensions.cs index 6b0ab9bae..af259c8a3 100644 --- a/Source/Testably.Abstractions.AccessControl/DirectoryInfoAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/DirectoryInfoAclExtensions.cs @@ -15,9 +15,9 @@ public static class DirectoryInfoAclExtensions public static void Create(this IDirectoryInfo directoryInfo, DirectorySecurity directorySecurity) { - IFileSystemExtensionContainer extensionContainer = - directoryInfo.ExtensionContainer; - if (extensionContainer.HasWrappedInstance(out DirectoryInfo? di)) + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) { di.Create(directorySecurity); } @@ -26,7 +26,7 @@ public static void Create(this IDirectoryInfo directoryInfo, _ = directorySecurity ?? throw new ArgumentNullException(nameof(directorySecurity)); directoryInfo.ThrowIfParentMissing(); directoryInfo.Create(); - directoryInfo.ExtensionContainer.StoreMetadata(AccessControlHelpers.AccessControl, + directoryInfo.Extensibility.StoreMetadata(AccessControlHelpers.AccessControl, directorySecurity); } } @@ -37,11 +37,11 @@ public static DirectorySecurity GetAccessControl( this IDirectoryInfo directoryInfo) { directoryInfo.ThrowIfMissing(); - IFileSystemExtensionContainer extensionContainer = - directoryInfo.ExtensionContainer; - return extensionContainer.HasWrappedInstance(out DirectoryInfo? di) + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) ? di.GetAccessControl() - : extensionContainer.RetrieveMetadata( + : extensibility.RetrieveMetadata( AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); } @@ -52,11 +52,11 @@ public static DirectorySecurity GetAccessControl( AccessControlSections includeSections) { directoryInfo.ThrowIfMissing(); - IFileSystemExtensionContainer extensionContainer = - directoryInfo.ExtensionContainer; - return extensionContainer.HasWrappedInstance(out DirectoryInfo? di) + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) ? di.GetAccessControl(includeSections) - : extensionContainer.RetrieveMetadata( + : extensibility.RetrieveMetadata( AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); } @@ -65,15 +65,15 @@ public static DirectorySecurity GetAccessControl( public static void SetAccessControl(this IDirectoryInfo directoryInfo, DirectorySecurity directorySecurity) { - IFileSystemExtensionContainer extensionContainer = - directoryInfo.ExtensionContainer; - if (extensionContainer.HasWrappedInstance(out DirectoryInfo? di)) + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) { di.SetAccessControl(directorySecurity); } else { - extensionContainer.StoreMetadata(AccessControlHelpers.AccessControl, + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, directorySecurity); } } diff --git a/Source/Testably.Abstractions.AccessControl/FileAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/FileAclExtensions.cs index f0d4fa8e7..552564643 100644 --- a/Source/Testably.Abstractions.AccessControl/FileAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/FileAclExtensions.cs @@ -16,11 +16,11 @@ public static FileSecurity GetAccessControl( { IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); fileInfo.ThrowIfMissing(); - IFileSystemExtensionContainer extensionContainer = - fileInfo.ExtensionContainer; - return extensionContainer.HasWrappedInstance(out FileInfo? fi) + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out FileInfo? fi) ? fi.GetAccessControl() - : extensionContainer.RetrieveMetadata( + : extensibility.RetrieveMetadata( AccessControlHelpers.AccessControl) ?? new FileSecurity(); } @@ -33,11 +33,11 @@ public static FileSecurity GetAccessControl( { IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); fileInfo.ThrowIfMissing(); - IFileSystemExtensionContainer extensionContainer = - fileInfo.ExtensionContainer; - return extensionContainer.HasWrappedInstance(out FileInfo? fi) + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out FileInfo? fi) ? fi.GetAccessControl(includeSections) - : extensionContainer.RetrieveMetadata( + : extensibility.RetrieveMetadata( AccessControlHelpers.AccessControl) ?? new FileSecurity(); } @@ -48,15 +48,15 @@ public static void SetAccessControl(this IFile file, FileSecurity fileSecurity) { IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); - IFileSystemExtensionContainer extensionContainer = - fileInfo.ExtensionContainer; - if (extensionContainer.HasWrappedInstance(out FileInfo? fi)) + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out FileInfo? fi)) { fi.SetAccessControl(fileSecurity); } else { - extensionContainer.StoreMetadata(AccessControlHelpers.AccessControl, + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, fileSecurity); } } diff --git a/Source/Testably.Abstractions.AccessControl/FileInfoAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/FileInfoAclExtensions.cs index 7c63091a0..c9cb3b14a 100644 --- a/Source/Testably.Abstractions.AccessControl/FileInfoAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/FileInfoAclExtensions.cs @@ -15,11 +15,11 @@ public static FileSecurity GetAccessControl( this IFileInfo fileInfo) { fileInfo.ThrowIfMissing(); - IFileSystemExtensionContainer extensionContainer = - fileInfo.ExtensionContainer; - return extensionContainer.HasWrappedInstance(out FileInfo? fi) + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out FileInfo? fi) ? fi.GetAccessControl() - : extensionContainer.RetrieveMetadata( + : extensibility.RetrieveMetadata( AccessControlHelpers.AccessControl) ?? new FileSecurity(); } @@ -30,11 +30,11 @@ public static FileSecurity GetAccessControl( AccessControlSections includeSections) { fileInfo.ThrowIfMissing(); - IFileSystemExtensionContainer extensionContainer = - fileInfo.ExtensionContainer; - return extensionContainer.HasWrappedInstance(out FileInfo? fi) + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out FileInfo? fi) ? fi.GetAccessControl(includeSections) - : extensionContainer.RetrieveMetadata( + : extensibility.RetrieveMetadata( AccessControlHelpers.AccessControl) ?? new FileSecurity(); } @@ -43,15 +43,15 @@ public static FileSecurity GetAccessControl( public static void SetAccessControl(this IFileInfo fileInfo, FileSecurity fileSecurity) { - IFileSystemExtensionContainer extensionContainer = - fileInfo.ExtensionContainer; - if (extensionContainer.HasWrappedInstance(out FileInfo? fi)) + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out FileInfo? fi)) { fi.SetAccessControl(fileSecurity); } else { - extensionContainer.StoreMetadata(AccessControlHelpers.AccessControl, + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, fileSecurity); } } diff --git a/Source/Testably.Abstractions.AccessControl/FileStreamAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/FileStreamAclExtensions.cs index 0a8a8ed53..27166894a 100644 --- a/Source/Testably.Abstractions.AccessControl/FileStreamAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/FileStreamAclExtensions.cs @@ -13,11 +13,11 @@ public static class FileStreamAclExtensions [SupportedOSPlatform("windows")] public static FileSecurity GetAccessControl(this FileSystemStream fileStream) { - IFileSystemExtensionContainer extensionContainer = - fileStream.ExtensionContainer; - return extensionContainer.HasWrappedInstance(out FileStream? fs) + IFileSystemExtensibility extensibility = + fileStream.Extensibility; + return extensibility.TryGetWrappedInstance(out FileStream? fs) ? fs.GetAccessControl() - : extensionContainer.RetrieveMetadata( + : extensibility.RetrieveMetadata( AccessControlHelpers.AccessControl) ?? new FileSecurity(); } @@ -26,15 +26,15 @@ public static FileSecurity GetAccessControl(this FileSystemStream fileStream) public static void SetAccessControl(this FileSystemStream fileStream, FileSecurity fileSecurity) { - IFileSystemExtensionContainer extensionContainer = - fileStream.ExtensionContainer; - if (extensionContainer.HasWrappedInstance(out FileStream? fs)) + IFileSystemExtensibility extensibility = + fileStream.Extensibility; + if (extensibility.TryGetWrappedInstance(out FileStream? fs)) { fs.SetAccessControl(fileSecurity); } else { - extensionContainer.StoreMetadata(AccessControlHelpers.AccessControl, + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, fileSecurity); } } diff --git a/Source/Testably.Abstractions.Interface/FileSystem/FileSystemStream.cs b/Source/Testably.Abstractions.Interface/FileSystem/FileSystemStream.cs index 8065abffa..0e170c94b 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/FileSystemStream.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/FileSystemStream.cs @@ -88,7 +88,7 @@ protected FileSystemStream(Stream stream, string? path, bool isAsync = false) /// /// A container to support extensions on . /// - public abstract IFileSystemExtensionContainer ExtensionContainer { get; } + public abstract IFileSystemExtensibility Extensibility { get; } /// public override IAsyncResult BeginRead(byte[] buffer, diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemExtensionContainer.cs b/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemExtensibility.cs similarity index 91% rename from Source/Testably.Abstractions.Interface/FileSystem/IFileSystemExtensionContainer.cs rename to Source/Testably.Abstractions.Interface/FileSystem/IFileSystemExtensibility.cs index e2c584674..fe7922b3c 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemExtensionContainer.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemExtensibility.cs @@ -5,7 +5,7 @@ namespace Testably.Abstractions.FileSystem; /// /// A container to support extensions on entities. /// -public interface IFileSystemExtensionContainer +public interface IFileSystemExtensibility { /// /// The wrapped instance on a real file system. @@ -14,7 +14,7 @@ public interface IFileSystemExtensionContainer /// when not on a real file system or if the requested type does not match, /// otherwise the wrapped instance. /// - bool HasWrappedInstance([NotNullWhen(true)] out T? wrappedInstance); + bool TryGetWrappedInstance([NotNullWhen(true)] out T? wrappedInstance); /// /// Stores additional metadata to the . diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemInfo.cs b/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemInfo.cs index e44ab426d..b511941b2 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemInfo.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemInfo.cs @@ -26,7 +26,7 @@ public interface IFileSystemInfo /// /// A container to support extensions on . /// - IFileSystemExtensionContainer ExtensionContainer { get; } + IFileSystemExtensibility Extensibility { get; } /// string FullName { get; } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DefaultAccessControlStrategy.cs b/Source/Testably.Abstractions.Testing/FileSystem/DefaultAccessControlStrategy.cs index 776fa4809..f63132e3c 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DefaultAccessControlStrategy.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DefaultAccessControlStrategy.cs @@ -9,20 +9,20 @@ namespace Testably.Abstractions.Testing.FileSystem; /// public class DefaultAccessControlStrategy : IAccessControlStrategy { - private readonly Func _callback; + private readonly Func _callback; /// /// Initializes a new instance of which takes a callback to determine if /// access should be granted. /// public DefaultAccessControlStrategy( - Func callback) + Func callback) { _callback = callback ?? throw new ArgumentNullException(nameof(callback)); } - /// + /// public bool IsAccessGranted(string fullPath, - IFileSystemExtensionContainer extensionContainer) - => _callback(fullPath, extensionContainer); + IFileSystemExtensibility extensibility) + => _callback(fullPath, extensibility); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs index b1cbb603e..03e064b56 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs @@ -42,7 +42,7 @@ public void Create() Container = FileSystem.Storage.GetOrCreateContainer(Location, InMemoryContainer.NewDirectory, - ExtensionContainer); + Extensibility); ResetCache(!Execute.IsNetFramework); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs index fe1c9374a..aeb375777 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs @@ -21,9 +21,9 @@ public override bool CanRead public override bool CanWrite => _access.HasFlag(FileAccess.Write); - /// - public override IFileSystemExtensionContainer ExtensionContainer - => _container.ExtensionContainer; + /// + public override IFileSystemExtensibility Extensibility + => _container.Extensibility; private readonly FileAccess _access; private readonly IDisposable _accessLock; diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs index b77186811..acebe9cb0 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs @@ -118,9 +118,9 @@ public string Extension } } - /// - public IFileSystemExtensionContainer ExtensionContainer - => Container.ExtensionContainer; + /// + public IFileSystemExtensibility Extensibility + => Container.Extensibility; /// public string FullName => Location.FullPath; diff --git a/Source/Testably.Abstractions.Testing/FileSystem/IAccessControlStrategy.cs b/Source/Testably.Abstractions.Testing/FileSystem/IAccessControlStrategy.cs index 03b207e35..0b741a975 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/IAccessControlStrategy.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/IAccessControlStrategy.cs @@ -11,10 +11,10 @@ public interface IAccessControlStrategy /// Implements a custom strategy to simulate access control (ACL) in the . /// /// The full path to the file or directory. - /// The extension container to store additional data. + /// The extension container to store additional data. /// /// if the access should be granted, otherwise . /// bool IsAccessGranted(string fullPath, - IFileSystemExtensionContainer extensionContainer); + IFileSystemExtensibility extensibility); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/NullAccessControlStrategy.cs b/Source/Testably.Abstractions.Testing/FileSystem/NullAccessControlStrategy.cs index 0bb8f4068..be91f8cd7 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/NullAccessControlStrategy.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/NullAccessControlStrategy.cs @@ -7,7 +7,7 @@ namespace Testably.Abstractions.Testing.FileSystem; /// public class NullAccessControlStrategy : IAccessControlStrategy { - /// - public bool IsAccessGranted(string fullPath, IFileSystemExtensionContainer extensionContainer) + /// + public bool IsAccessGranted(string fullPath, IFileSystemExtensibility extensibility) => true; } diff --git a/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensionContainer.cs b/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensibility.cs similarity index 58% rename from Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensionContainer.cs rename to Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensibility.cs index 7a7ef606a..c72f1c33e 100644 --- a/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensionContainer.cs +++ b/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensibility.cs @@ -4,24 +4,24 @@ namespace Testably.Abstractions.Testing.Helpers; -internal class FileSystemExtensionContainer : IFileSystemExtensionContainer +internal class FileSystemExtensibility : IFileSystemExtensibility { private readonly Dictionary _metadata = new(); - /// - public bool HasWrappedInstance([NotNullWhen(true)] out T? wrappedInstance) + /// + public bool TryGetWrappedInstance([NotNullWhen(true)] out T? wrappedInstance) { wrappedInstance = default; return false; } - /// + /// public void StoreMetadata(string key, T? value) { _metadata[key] = value; } - /// + /// public T? RetrieveMetadata(string key) { if (_metadata.TryGetValue(key, out object? value) && @@ -34,9 +34,9 @@ public void StoreMetadata(string key, T? value) return default; } - internal void CopyMetadataTo(IFileSystemExtensionContainer target) + internal void CopyMetadataTo(IFileSystemExtensibility target) { - if (target is FileSystemExtensionContainer targetContainer) + if (target is FileSystemExtensibility targetContainer) { foreach (KeyValuePair item in _metadata) { diff --git a/Source/Testably.Abstractions.Testing/MockFileSystem.cs b/Source/Testably.Abstractions.Testing/MockFileSystem.cs index d15a330a4..8a1df58b8 100644 --- a/Source/Testably.Abstractions.Testing/MockFileSystem.cs +++ b/Source/Testably.Abstractions.Testing/MockFileSystem.cs @@ -116,7 +116,7 @@ public IPath Path /// The defines a method that receives two values and allows or denies access: ///
/// - The full path of the file or directory as first parameter
- /// - The as second parameter + /// - The as second parameter ///
public MockFileSystem WithAccessControlStrategy(IAccessControlStrategy accessControlStrategy) { diff --git a/Source/Testably.Abstractions.Testing/Storage/IStorage.cs b/Source/Testably.Abstractions.Testing/Storage/IStorage.cs index b6aba2cd3..3f07d9033 100644 --- a/Source/Testably.Abstractions.Testing/Storage/IStorage.cs +++ b/Source/Testably.Abstractions.Testing/Storage/IStorage.cs @@ -121,13 +121,11 @@ IEnumerable EnumerateLocations( ///
/// The location at which to get or create the container. /// The callback used to create a new container at . - /// + /// /// The container at . IStorageContainer GetOrCreateContainer(IStorageLocation location, - Func containerGenerator, - IFileSystemExtensionContainer? - fileSystemExtensionContainer = null); + Func containerGenerator, + IFileSystemExtensibility? fileSystemExtensibility = null); /// /// Moves a specified file or directory to a new location and potentially a new file name.
diff --git a/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs b/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs index 223f40d69..b5dfa27fb 100644 --- a/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs +++ b/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs @@ -20,7 +20,7 @@ internal interface IStorageContainer : IFileSystemEntity, /// /// A container to support extensions on . /// - IFileSystemExtensionContainer ExtensionContainer { get; } + IFileSystemExtensibility Extensibility { get; } /// ITimeContainer LastAccessTime { get; } diff --git a/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs b/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs index 2549d3846..e0f04aed4 100644 --- a/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs +++ b/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs @@ -19,7 +19,7 @@ internal class InMemoryContainer : IStorageContainer private readonly MockFileSystem _fileSystem; private bool _isEncrypted; private readonly IStorageLocation _location; - private readonly FileSystemExtensionContainer _extensionContainer = new(); + private readonly FileSystemExtensibility _extensibility = new(); public InMemoryContainer(FileSystemTypes type, IStorageLocation location, @@ -63,9 +63,9 @@ public FileAttributes Attributes /// public ITimeContainer CreationTime { get; } = new TimeContainer(); - /// - public IFileSystemExtensionContainer ExtensionContainer - => _extensionContainer; + /// + public IFileSystemExtensibility Extensibility + => _extensibility; /// public IFileSystem FileSystem => _fileSystem; @@ -157,7 +157,7 @@ public IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, () => throw ExceptionFactory.AccessToPathDenied()); if (!_fileSystem.AccessControlStrategy - .IsAccessGranted(_location.FullPath, ExtensionContainer)) + .IsAccessGranted(_location.FullPath, Extensibility)) { throw ExceptionFactory.AclAccessToPathDenied(_location.FullPath); } diff --git a/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs b/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs index aa6d63960..4043858ef 100644 --- a/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs +++ b/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs @@ -280,7 +280,7 @@ public IStorageDrive GetOrAddDrive(string driveName) public IStorageContainer GetOrCreateContainer( IStorageLocation location, Func containerGenerator, - IFileSystemExtensionContainer? fileSystemExtensionContainer = null) + IFileSystemExtensibility? fileSystemExtensibility = null) { ChangeDescription? fileSystemChange = null; IStorageContainer container = _containers.GetOrAdd(location, @@ -288,8 +288,8 @@ public IStorageContainer GetOrCreateContainer( { IStorageContainer container = containerGenerator.Invoke(loc, _fileSystem); - (fileSystemExtensionContainer as FileSystemExtensionContainer)? - .CopyMetadataTo(container.ExtensionContainer); + (fileSystemExtensibility as FileSystemExtensibility)? + .CopyMetadataTo(container.Extensibility); if (container.Type == FileSystemTypes.Directory) { CreateParents(_fileSystem, loc); diff --git a/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs b/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs index 33e1a0544..8cb93836e 100644 --- a/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs +++ b/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs @@ -12,7 +12,7 @@ private NullContainer(IFileSystem fileSystem, ITimeSystem timeSystem) { FileSystem = fileSystem; TimeSystem = timeSystem; - ExtensionContainer = new FileSystemExtensionContainer(); + Extensibility = new FileSystemExtensibility(); } #region IStorageContainer Members @@ -28,8 +28,8 @@ public FileAttributes Attributes public IStorageContainer.ITimeContainer CreationTime { get; } = new CreationNullTime(); - /// - public IFileSystemExtensionContainer ExtensionContainer { get; } + /// + public IFileSystemExtensibility Extensibility { get; } /// public IFileSystem FileSystem { get; } diff --git a/Source/Testably.Abstractions/FileSystem/FileStreamWrapper.cs b/Source/Testably.Abstractions/FileSystem/FileStreamWrapper.cs index 448250c66..2a754edfc 100644 --- a/Source/Testably.Abstractions/FileSystem/FileStreamWrapper.cs +++ b/Source/Testably.Abstractions/FileSystem/FileStreamWrapper.cs @@ -8,11 +8,11 @@ public FileStreamWrapper(FileStream fileStream) : base(fileStream, fileStream.Name, fileStream.IsAsync) { - ExtensionContainer = new FileSystemExtensionContainer(fileStream); + Extensibility = new FileSystemExtensibility(fileStream); } - /// - public override IFileSystemExtensionContainer ExtensionContainer + /// + public override IFileSystemExtensibility Extensibility { get; } diff --git a/Source/Testably.Abstractions/FileSystem/FileSystemExtensionContainer.cs b/Source/Testably.Abstractions/FileSystem/FileSystemExtensibility.cs similarity index 75% rename from Source/Testably.Abstractions/FileSystem/FileSystemExtensionContainer.cs rename to Source/Testably.Abstractions/FileSystem/FileSystemExtensibility.cs index 0c5fe7d6f..b6965b179 100644 --- a/Source/Testably.Abstractions/FileSystem/FileSystemExtensionContainer.cs +++ b/Source/Testably.Abstractions/FileSystem/FileSystemExtensibility.cs @@ -3,18 +3,18 @@ namespace Testably.Abstractions.FileSystem; -internal class FileSystemExtensionContainer : IFileSystemExtensionContainer +internal class FileSystemExtensibility : IFileSystemExtensibility { private readonly object _wrappedInstance; private readonly Dictionary _metadata = new(); - public FileSystemExtensionContainer(object wrappedInstance) + public FileSystemExtensibility(object wrappedInstance) { _wrappedInstance = wrappedInstance; } - /// - public bool HasWrappedInstance([NotNullWhen(true)] out T? wrappedInstance) + /// + public bool TryGetWrappedInstance([NotNullWhen(true)] out T? wrappedInstance) { // ReSharper disable once MergeCastWithTypeCheck -- Not possible due to nullable wrappedInstance = _wrappedInstance is T? diff --git a/Source/Testably.Abstractions/FileSystem/FileSystemInfoWrapper.cs b/Source/Testably.Abstractions/FileSystem/FileSystemInfoWrapper.cs index d96594fd8..1623c5334 100644 --- a/Source/Testably.Abstractions/FileSystem/FileSystemInfoWrapper.cs +++ b/Source/Testably.Abstractions/FileSystem/FileSystemInfoWrapper.cs @@ -13,7 +13,7 @@ internal FileSystemInfoWrapper(FileSystemInfo instance, IFileSystem fileSystem) { _instance = instance; _fileSystem = fileSystem; - ExtensionContainer = new FileSystemExtensionContainer(_instance); + Extensibility = new FileSystemExtensibility(_instance); } #region IFileSystemInfo Members @@ -47,8 +47,8 @@ public bool Exists public string Extension => _instance.Extension; - /// - public IFileSystemExtensionContainer ExtensionContainer { get; } + /// + public IFileSystemExtensibility Extensibility { get; } /// public string FullName diff --git a/Tests/Testably.Abstractions.Testing.Tests/FileSystem/DefaultAccessControlStrategyTests.cs b/Tests/Testably.Abstractions.Testing.Tests/FileSystem/DefaultAccessControlStrategyTests.cs index 732cc5197..dbd08202a 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/FileSystem/DefaultAccessControlStrategyTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/FileSystem/DefaultAccessControlStrategyTests.cs @@ -29,7 +29,7 @@ public void IsAccessGranted_ShouldUseCallback() { DefaultAccessControlStrategy sut = new((p, _) => p.StartsWith("a")); - sut.IsAccessGranted("abc", new FileSystemExtensionContainer()).Should().BeTrue(); - sut.IsAccessGranted("xyz", new FileSystemExtensionContainer()).Should().BeFalse(); + sut.IsAccessGranted("abc", new FileSystemExtensibility()).Should().BeTrue(); + sut.IsAccessGranted("xyz", new FileSystemExtensibility()).Should().BeFalse(); } } diff --git a/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs b/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs index 1f817e3da..2127b10de 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs @@ -47,9 +47,9 @@ public LockableContainer(MockFileSystem fileSystem, public IStorageContainer.ITimeContainer CreationTime { get; } = new InMemoryContainer.TimeContainer(); - /// - public IFileSystemExtensionContainer ExtensionContainer { get; } - = new FileSystemExtensionContainer(); + /// + public IFileSystemExtensibility Extensibility { get; } + = new FileSystemExtensibility(); /// public IFileSystem FileSystem { get; } diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/Tests.cs index 03c4cb82c..318ae556e 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/Tests.cs @@ -125,13 +125,13 @@ public async Task CopyToAsync_BufferSizeZero_ShouldThrowArgumentOutOfRangeExcept [SkippableTheory] [AutoData] - public void ExtensionContainer_ShouldWrapFileStreamOnRealFileSystem( + public void Extensibility_ShouldWrapFileStreamOnRealFileSystem( string path) { FileSystem.File.WriteAllText(path, null); using FileSystemStream readStream = FileSystem.File.OpenRead(path); - bool result = readStream.ExtensionContainer - .HasWrappedInstance(out System.IO.FileStream? fileStream); + bool result = readStream.Extensibility + .TryGetWrappedInstance(out System.IO.FileStream? fileStream); if (FileSystem is RealFileSystem) { diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/Tests.cs index 8aeb340ee..30e39ae62 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/Tests.cs @@ -10,13 +10,13 @@ public abstract partial class Tests { [SkippableTheory] [AutoData] - public void ExtensionContainer_ShouldWrapFileSystemInfoOnRealFileSystem( + public void Extensibility_ShouldWrapFileSystemInfoOnRealFileSystem( string path) { FileSystem.File.WriteAllText(path, null); IFileInfo fileInfo = FileSystem.FileInfo.New(path); - bool result = fileInfo.ExtensionContainer - .HasWrappedInstance(out System.IO.FileSystemInfo? fileSystemInfo); + bool result = fileInfo.Extensibility + .TryGetWrappedInstance(out System.IO.FileSystemInfo? fileSystemInfo); if (FileSystem is RealFileSystem) { diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemTests.ExtensionContainer.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemTests.Extensibility.cs similarity index 53% rename from Tests/Testably.Abstractions.Tests/FileSystem/FileSystemTests.ExtensionContainer.cs rename to Tests/Testably.Abstractions.Tests/FileSystem/FileSystemTests.Extensibility.cs index 46fde3fc3..a57dca506 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemTests.ExtensionContainer.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemTests.Extensibility.cs @@ -8,11 +8,11 @@ public abstract partial class FileSystemTests [SkippableTheory] [AutoData] public void - ExtensionContainer_HasWrappedInstance_WithCorrectType_ShouldReturnTrueOnRealFileSystem( + Extensibility_HasWrappedInstance_WithCorrectType_ShouldReturnTrueOnRealFileSystem( string name) { - bool result = FileSystem.FileInfo.New(name).ExtensionContainer - .HasWrappedInstance(out System.IO.FileInfo? fileInfo); + bool result = FileSystem.FileInfo.New(name).Extensibility + .TryGetWrappedInstance(out System.IO.FileInfo? fileInfo); if (FileSystem is RealFileSystem) { @@ -28,11 +28,11 @@ public void [SkippableTheory] [AutoData] public void - ExtensionContainer_HasWrappedInstance_WithIncorrectType_ShouldReturnAlwaysFalse( + Extensibility_HasWrappedInstance_WithIncorrectType_ShouldReturnAlwaysFalse( string name) { - bool result = FileSystem.FileInfo.New(name).ExtensionContainer - .HasWrappedInstance(out System.IO.DirectoryInfo? directoryInfo); + bool result = FileSystem.FileInfo.New(name).Extensibility + .TryGetWrappedInstance(out System.IO.DirectoryInfo? directoryInfo); result.Should().BeFalse(); directoryInfo.Should().BeNull(); @@ -41,11 +41,11 @@ public void [SkippableTheory] [AutoData] public void - ExtensionContainer_RetrieveMetadata_CorrectKeyAndType_ShouldReturnStoredValue( + Extensibility_RetrieveMetadata_CorrectKeyAndType_ShouldReturnStoredValue( string name, DateTime time) { - IFileSystemExtensionContainer sut = FileSystem.FileInfo.New(name) - .ExtensionContainer; + IFileSystemExtensibility sut = FileSystem.FileInfo.New(name) + .Extensibility; sut.StoreMetadata("foo", time); DateTime? result = sut.RetrieveMetadata("foo"); @@ -55,11 +55,11 @@ public void [SkippableTheory] [AutoData] - public void ExtensionContainer_RetrieveMetadata_DifferentKey_ShouldReturnNull( + public void Extensibility_RetrieveMetadata_DifferentKey_ShouldReturnNull( string name) { - IFileSystemExtensionContainer sut = FileSystem.FileInfo.New(name) - .ExtensionContainer; + IFileSystemExtensibility sut = FileSystem.FileInfo.New(name) + .Extensibility; sut.StoreMetadata("foo", DateTime.Now); DateTime? result = sut.RetrieveMetadata("bar"); @@ -69,11 +69,11 @@ public void ExtensionContainer_RetrieveMetadata_DifferentKey_ShouldReturnNull( [SkippableTheory] [AutoData] - public void ExtensionContainer_RetrieveMetadata_DifferentType_ShouldReturnNull( + public void Extensibility_RetrieveMetadata_DifferentType_ShouldReturnNull( string name) { - IFileSystemExtensionContainer sut = FileSystem.FileInfo.New(name) - .ExtensionContainer; + IFileSystemExtensibility sut = FileSystem.FileInfo.New(name) + .Extensibility; sut.StoreMetadata("foo", DateTime.Now); TimeSpan? result = sut.RetrieveMetadata("foo"); @@ -83,10 +83,10 @@ public void ExtensionContainer_RetrieveMetadata_DifferentType_ShouldReturnNull( [SkippableTheory] [AutoData] - public void ExtensionContainer_RetrieveMetadata_NotRegisteredKey_ShouldReturnNull( + public void Extensibility_RetrieveMetadata_NotRegisteredKey_ShouldReturnNull( string name) { - object? result = FileSystem.FileInfo.New(name).ExtensionContainer + object? result = FileSystem.FileInfo.New(name).Extensibility .RetrieveMetadata("foo"); result.Should().BeNull(); From e702d806d249257f569d42b6358169ba97dbe457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Tue, 15 Nov 2022 18:20:34 +0100 Subject: [PATCH 3/9] Use "global using Testably.Abstractions.FileSystem;" --- .../AccessControlHelpers.cs | 101 +- .../DirectoryAclExtensions.cs | 181 +- .../DirectoryInfoAclExtensions.cs | 159 +- .../FileAclExtensions.cs | 125 +- .../FileInfoAclExtensions.cs | 115 +- .../FileStreamAclExtensions.cs | 81 +- .../Usings.cs | 1 + .../IZipArchive.cs | 97 +- .../IZipArchiveEntry.cs | 121 +- .../IZipArchiveFactory.cs | 49 +- .../IZipFile.cs | 123 +- .../Internal/ZipUtilities.cs | 529 +++-- .../Usings.cs | 1 + .../ZipArchiveEntryWrapper.cs | 255 ++- .../ZipArchiveFactory.cs | 81 +- .../ZipArchiveWrapper.cs | 265 ++- .../ZipFileWrapper.cs | 331 ++- .../Helpers/PathSystemBase.cs | 1 + .../FileSystem/ChangeHandler.cs | 143 +- .../DefaultAccessControlStrategy.cs | 55 +- .../FileSystem/DirectoryInfoFactoryMock.cs | 83 +- .../FileSystem/DirectoryInfoMock.cs | 561 +++-- .../FileSystem/DirectoryMock.cs | 943 ++++---- .../FileSystem/DriveInfoFactoryMock.cs | 115 +- .../FileSystem/DriveInfoMock.cs | 413 ++-- .../FileSystem/FileInfoFactoryMock.cs | 103 +- .../FileSystem/FileInfoMock.cs | 565 +++-- .../FileSystem/FileMock.cs | 1933 ++++++++--------- .../FileSystem/FileStreamFactoryMock.cs | 301 ++- .../FileSystem/FileStreamMock.cs | 895 ++++---- .../FileSystem/FileSystemInfoMock.cs | 529 +++-- .../FileSystemWatcherFactoryMock.cs | 157 +- .../FileSystem/FileSystemWatcherMock.cs | 1031 +++++---- .../FileSystem/IInterceptionHandler.cs | 45 +- .../FileSystem/INotificationHandler.cs | 47 +- .../FileSystem/PathMock.cs | 93 +- .../FileSystemInitializer/DirectoryCleaner.cs | 265 ++- .../DirectoryInitializer.cs | 67 +- .../FileSystemInitializer/FileInitializer.cs | 131 +- .../IFileSystemDirectoryInitializer.cs | 45 +- .../IFileSystemFileInitializer.cs | 45 +- .../FileSystemInitializer/Initializer.cs | 249 ++- .../FileSystemInitializerExtensions.cs | 95 +- .../Helpers/FileSystemExtensibility.cs | 93 +- .../MockFileSystem.cs | 307 ++- .../Polyfills/FileFeatureExtensionMethods.cs | 125 +- .../Storage/IStorage.cs | 407 ++-- .../Storage/IStorageContainer.cs | 219 +- .../Storage/IStorageDrive.cs | 95 +- .../Storage/IStorageLocation.cs | 77 +- .../Storage/InMemoryContainer.cs | 747 ++++--- .../Storage/InMemoryStorage.cs | 1435 ++++++------ .../Storage/NullContainer.cs | 393 ++-- .../Testably.Abstractions.Testing/Usings.cs | 1 + .../FileInfoAclExtensionsTests.cs | 125 +- .../FileStreamAclExtensionsTests.cs | 83 +- .../TestHelpers/Usings.cs | 1 + .../TestHelpers/Usings.cs | 1 + .../ZipArchive/ZipArchiveTests.Extensions.cs | 569 +++-- .../ZipArchive/ZipArchiveTests.cs | 169 +- .../ZipArchiveEntryTests.Extensions.cs | 309 ++- .../ZipArchiveEntry/ZipArchiveEntryTests.cs | 497 +++-- .../ParityTests.cs | 393 ++-- .../TestHelpers/Parity.cs | 147 +- .../TestHelpers/Usings.cs | 1 + .../FileSystem/DriveInfoMockTests.cs | 569 +++-- .../FileSystemWatcherFactoryMockTests.cs | 149 +- .../FileSystem/FileSystemWatcherMockTests.cs | 373 ++-- .../Helpers/PathHelperTests.cs | 201 +- .../MockFileSystemTests.cs | 575 +++-- .../Storage/InMemoryStorageTests.cs | 409 ++-- .../TestHelpers/LockableContainer.cs | 293 ++- .../TestHelpers/Usings.cs | 1 + .../Directory/CreateDirectoryTests.cs | 613 +++--- .../FileSystem/Directory/DeleteTests.cs | 481 ++-- .../Directory/EnumerateDirectoriesTests.cs | 499 +++-- .../FileSystem/Directory/ExceptionTests.cs | 489 +++-- .../Directory/GetDirectoriesTests.cs | 429 ++-- .../FileSystem/Directory/MoveTests.cs | 583 +++-- .../Directory/ResolveLinkTargetTests.cs | 401 ++-- .../FileSystem/Directory/Tests.Times.cs | 1027 +++++---- .../FileSystem/Directory/Tests.cs | 253 ++- .../DirectoryInfo/AttributesTests.cs | 93 +- .../FileSystem/DirectoryInfo/DeleteTests.cs | 259 ++- .../EnumerateDirectoriesTests.cs | 341 ++- .../EnumerateFileSystemInfosTests.cs | 409 ++-- .../DirectoryInfo/EnumerateFilesTests.cs | 357 ++- .../DirectoryInfo/ExceptionTests.cs | 301 ++- .../DirectoryInfo/GetDirectoriesTests.cs | 341 ++- .../DirectoryInfo/GetFileSystemInfosTests.cs | 409 ++-- .../FileSystem/DirectoryInfo/GetFilesTests.cs | 357 ++- .../FileSystem/DirectoryInfo/MoveToTests.cs | 373 ++-- .../FileSystem/DirectoryInfo/Tests.cs | 753 ++++--- .../DirectoryInfoFactory/ExceptionTests.cs | 235 +- .../DriveInfoFactory/ExceptionTests.cs | 173 +- .../FileSystem/DriveInfoFactory/Tests.cs | 315 ++- .../FileSystem/File/CopyTests.cs | 649 +++--- .../FileSystem/File/CreateTests.cs | 219 +- .../FileSystem/File/DeleteTests.cs | 143 +- .../File/ExceptionMissingFileTests.cs | 795 ++++--- .../FileSystem/File/ExceptionTests.cs | 843 ++++--- .../FileSystem/File/MoveTests.cs | 493 +++-- .../FileSystem/File/OpenReadTests.cs | 283 ++- .../FileSystem/File/OpenTests.cs | 283 ++- .../FileSystem/File/OpenWriteTests.cs | 141 +- .../FileSystem/File/ReplaceTests.cs | 561 +++-- .../FileSystem/File/ResolveLinkTargetTests.cs | 383 ++-- .../FileSystem/FileInfo/AppendTextTests.cs | 91 +- .../FileSystem/FileInfo/CopyToTests.cs | 469 ++-- .../FileSystem/FileInfo/CreateTests.cs | 111 +- .../FileSystem/FileInfo/CreateTextTests.cs | 125 +- .../FileSystem/FileInfo/DeleteTests.cs | 191 +- .../FileInfo/EncryptDecryptTests.cs | 203 +- .../FileSystem/FileInfo/ExceptionTests.cs | 255 ++- .../FileSystem/FileInfo/LengthTests.cs | 287 ++- .../FileSystem/FileInfo/MoveToTests.cs | 573 +++-- .../FileSystem/FileInfo/OpenReadTests.cs | 79 +- .../FileSystem/FileInfo/OpenTests.cs | 507 +++-- .../FileSystem/FileInfo/OpenTextTests.cs | 81 +- .../FileSystem/FileInfo/OpenWriteTests.cs | 67 +- .../FileSystem/FileInfo/ReplaceTests.cs | 835 ++++--- .../FileSystem/FileInfo/Tests.cs | 323 ++- .../FileInfoFactory/ExceptionTests.cs | 255 ++- .../FileSystem/FileInfoFactory/Tests.cs | 185 +- .../FileSystem/FileStream/AdjustTimesTests.cs | 1019 +++++---- .../FileSystem/FileStream/DisposeTests.cs | 231 +- .../FileSystem/FileStream/FileAccessTests.cs | 485 +++-- .../FileSystem/FileStream/OptionsTests.cs | 217 +- .../FileSystem/FileStream/ReadTests.cs | 551 +++-- .../FileSystem/FileStream/Tests.cs | 599 +++-- .../FileSystem/FileStream/WriteTests.cs | 569 +++-- .../FileStreamFactory/ExceptionTests.cs | 291 ++- .../FileStreamFactory/SafeFileHandleTests.cs | 313 ++- .../FileSystem/FileStreamFactory/Tests.cs | 365 ++-- .../CreateAsSymbolicLinkTests.cs | 89 +- .../FileSystemInfo/ExceptionTests.cs | 149 +- .../FileSystemInfo/ResolveLinkTargetTests.cs | 353 ++- .../FileSystem/FileSystemInfo/Tests.cs | 313 ++- .../FileSystemInfo/UnixFileModeTests.cs | 145 +- .../EnableRaisingEventsTests.cs | 101 +- .../FileSystemWatcher/FilterTests.cs | 223 +- .../IncludeSubdirectoriesTests.cs | 189 +- .../FileSystemWatcher/NotifyFiltersTests.cs | 1067 +++++---- .../FileSystem/FileSystemWatcher/Tests.cs | 331 ++- .../FileSystemWatcher/WaitForChangedTests.cs | 177 +- .../ExceptionTests.cs | 245 ++- .../FileSystemWatcherFactory/Tests.cs | 157 +- .../TestHelpers/FileTestHelper.cs | 161 +- .../TestHelpers/Usings.cs | 1 + 149 files changed, 23710 insertions(+), 23841 deletions(-) create mode 100644 Source/Testably.Abstractions.Compression/Usings.cs diff --git a/Source/Testably.Abstractions.AccessControl/AccessControlHelpers.cs b/Source/Testably.Abstractions.AccessControl/AccessControlHelpers.cs index bc201357b..e1ddebbc1 100644 --- a/Source/Testably.Abstractions.AccessControl/AccessControlHelpers.cs +++ b/Source/Testably.Abstractions.AccessControl/AccessControlHelpers.cs @@ -1,51 +1,50 @@ -using System; -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions; - -internal static class AccessControlHelpers -{ - public const string AccessControl = nameof(AccessControl); - - public static TFileSystemInfo ThrowIfMissing( - this TFileSystemInfo fileSystemInfo) - where TFileSystemInfo : IFileSystemInfo - { - if (!fileSystemInfo.Exists) - { - if (fileSystemInfo is IDirectoryInfo directoryInfo) - { - throw new DirectoryNotFoundException( - $"Could not find a part of the path '{directoryInfo.FullName}'.") - { -#if FEATURE_EXCEPTION_HRESULT - HResult = -2147024893 -#endif - }; - } - - if (fileSystemInfo is IFileInfo fileInfo) - { - throw new FileNotFoundException($"Could not find file '{fileInfo.FullName}'.") - { -#if FEATURE_EXCEPTION_HRESULT - HResult = -2147024894 -#endif - }; - } - } - - return fileSystemInfo; - } - public static IDirectoryInfo ThrowIfParentMissing( - this IDirectoryInfo fileSystemInfo) - { - if (fileSystemInfo.Parent?.Exists != true) - { - throw new UnauthorizedAccessException(); - } - - return fileSystemInfo; - } -} +using System; +using System.IO; + +namespace Testably.Abstractions; + +internal static class AccessControlHelpers +{ + public const string AccessControl = nameof(AccessControl); + + public static TFileSystemInfo ThrowIfMissing( + this TFileSystemInfo fileSystemInfo) + where TFileSystemInfo : IFileSystemInfo + { + if (!fileSystemInfo.Exists) + { + if (fileSystemInfo is IDirectoryInfo directoryInfo) + { + throw new DirectoryNotFoundException( + $"Could not find a part of the path '{directoryInfo.FullName}'.") + { +#if FEATURE_EXCEPTION_HRESULT + HResult = -2147024893 +#endif + }; + } + + if (fileSystemInfo is IFileInfo fileInfo) + { + throw new FileNotFoundException($"Could not find file '{fileInfo.FullName}'.") + { +#if FEATURE_EXCEPTION_HRESULT + HResult = -2147024894 +#endif + }; + } + } + + return fileSystemInfo; + } + public static IDirectoryInfo ThrowIfParentMissing( + this IDirectoryInfo fileSystemInfo) + { + if (fileSystemInfo.Parent?.Exists != true) + { + throw new UnauthorizedAccessException(); + } + + return fileSystemInfo; + } +} diff --git a/Source/Testably.Abstractions.AccessControl/DirectoryAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/DirectoryAclExtensions.cs index 48d855cea..557ff91b2 100644 --- a/Source/Testably.Abstractions.AccessControl/DirectoryAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/DirectoryAclExtensions.cs @@ -1,91 +1,90 @@ -using System; -using System.IO; -using System.Security.AccessControl; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions; - -/// -/// ACL (access control list) extension methods for . -/// -public static class DirectoryAclExtensions -{ - /// - [SupportedOSPlatform("windows")] - public static void CreateDirectory(this IDirectory directory, - string path, - DirectorySecurity directorySecurity) - { - IDirectoryInfo directoryInfo = - directory.FileSystem.DirectoryInfo.New(path); - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) - { - di.Create(directorySecurity); - } - else - { - _ = directorySecurity ?? throw new ArgumentNullException(nameof(directorySecurity)); - directoryInfo.ThrowIfParentMissing(); - directoryInfo.Create(); - directoryInfo.Extensibility.StoreMetadata(AccessControlHelpers.AccessControl, - directorySecurity); - } - } - - /// - [SupportedOSPlatform("windows")] - public static DirectorySecurity GetAccessControl( - this IDirectory directory, string path) - { - IDirectoryInfo directoryInfo = - directory.FileSystem.DirectoryInfo.New(path); - directoryInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) - ? di.GetAccessControl() - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static DirectorySecurity GetAccessControl( - this IDirectory directory, - string path, - AccessControlSections includeSections) - { - IDirectoryInfo directoryInfo = - directory.FileSystem.DirectoryInfo.New(path); - directoryInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) - ? di.GetAccessControl(includeSections) - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static void SetAccessControl(this IDirectory directory, - string path, - DirectorySecurity directorySecurity) - { - IDirectoryInfo directoryInfo = - directory.FileSystem.DirectoryInfo.New(path); - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) - { - di.SetAccessControl(directorySecurity); - } - else - { - extensibility.StoreMetadata(AccessControlHelpers.AccessControl, - directorySecurity); - } - } -} +using System; +using System.IO; +using System.Security.AccessControl; + +namespace Testably.Abstractions; + +/// +/// ACL (access control list) extension methods for . +/// +public static class DirectoryAclExtensions +{ + /// + [SupportedOSPlatform("windows")] + public static void CreateDirectory(this IDirectory directory, + string path, + DirectorySecurity directorySecurity) + { + IDirectoryInfo directoryInfo = + directory.FileSystem.DirectoryInfo.New(path); + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) + { + di.Create(directorySecurity); + } + else + { + _ = directorySecurity ?? throw new ArgumentNullException(nameof(directorySecurity)); + directoryInfo.ThrowIfParentMissing(); + directoryInfo.Create(); + directoryInfo.Extensibility.StoreMetadata(AccessControlHelpers.AccessControl, + directorySecurity); + } + } + + /// + [SupportedOSPlatform("windows")] + public static DirectorySecurity GetAccessControl( + this IDirectory directory, string path) + { + IDirectoryInfo directoryInfo = + directory.FileSystem.DirectoryInfo.New(path); + directoryInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) + ? di.GetAccessControl() + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static DirectorySecurity GetAccessControl( + this IDirectory directory, + string path, + AccessControlSections includeSections) + { + IDirectoryInfo directoryInfo = + directory.FileSystem.DirectoryInfo.New(path); + directoryInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) + ? di.GetAccessControl(includeSections) + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static void SetAccessControl(this IDirectory directory, + string path, + DirectorySecurity directorySecurity) + { + IDirectoryInfo directoryInfo = + directory.FileSystem.DirectoryInfo.New(path); + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) + { + di.SetAccessControl(directorySecurity); + } + else + { + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, + directorySecurity); + } + } +} diff --git a/Source/Testably.Abstractions.AccessControl/DirectoryInfoAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/DirectoryInfoAclExtensions.cs index af259c8a3..db5bdcee1 100644 --- a/Source/Testably.Abstractions.AccessControl/DirectoryInfoAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/DirectoryInfoAclExtensions.cs @@ -1,80 +1,79 @@ -using System; -using System.IO; -using System.Security.AccessControl; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions; - -/// -/// ACL (access control list) extension methods for . -/// -public static class DirectoryInfoAclExtensions -{ - /// - [SupportedOSPlatform("windows")] - public static void Create(this IDirectoryInfo directoryInfo, - DirectorySecurity directorySecurity) - { - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) - { - di.Create(directorySecurity); - } - else - { - _ = directorySecurity ?? throw new ArgumentNullException(nameof(directorySecurity)); - directoryInfo.ThrowIfParentMissing(); - directoryInfo.Create(); - directoryInfo.Extensibility.StoreMetadata(AccessControlHelpers.AccessControl, - directorySecurity); - } - } - - /// - [SupportedOSPlatform("windows")] - public static DirectorySecurity GetAccessControl( - this IDirectoryInfo directoryInfo) - { - directoryInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) - ? di.GetAccessControl() - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static DirectorySecurity GetAccessControl( - this IDirectoryInfo directoryInfo, - AccessControlSections includeSections) - { - directoryInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) - ? di.GetAccessControl(includeSections) - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static void SetAccessControl(this IDirectoryInfo directoryInfo, - DirectorySecurity directorySecurity) - { - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) - { - di.SetAccessControl(directorySecurity); - } - else - { - extensibility.StoreMetadata(AccessControlHelpers.AccessControl, - directorySecurity); - } - } -} +using System; +using System.IO; +using System.Security.AccessControl; + +namespace Testably.Abstractions; + +/// +/// ACL (access control list) extension methods for . +/// +public static class DirectoryInfoAclExtensions +{ + /// + [SupportedOSPlatform("windows")] + public static void Create(this IDirectoryInfo directoryInfo, + DirectorySecurity directorySecurity) + { + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) + { + di.Create(directorySecurity); + } + else + { + _ = directorySecurity ?? throw new ArgumentNullException(nameof(directorySecurity)); + directoryInfo.ThrowIfParentMissing(); + directoryInfo.Create(); + directoryInfo.Extensibility.StoreMetadata(AccessControlHelpers.AccessControl, + directorySecurity); + } + } + + /// + [SupportedOSPlatform("windows")] + public static DirectorySecurity GetAccessControl( + this IDirectoryInfo directoryInfo) + { + directoryInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) + ? di.GetAccessControl() + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static DirectorySecurity GetAccessControl( + this IDirectoryInfo directoryInfo, + AccessControlSections includeSections) + { + directoryInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) + ? di.GetAccessControl(includeSections) + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static void SetAccessControl(this IDirectoryInfo directoryInfo, + DirectorySecurity directorySecurity) + { + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) + { + di.SetAccessControl(directorySecurity); + } + else + { + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, + directorySecurity); + } + } +} diff --git a/Source/Testably.Abstractions.AccessControl/FileAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/FileAclExtensions.cs index 552564643..201bfef89 100644 --- a/Source/Testably.Abstractions.AccessControl/FileAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/FileAclExtensions.cs @@ -1,63 +1,62 @@ -using System.IO; -using System.Security.AccessControl; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions; - -/// -/// ACL (access control list) extension methods for . -/// -public static class FileAclExtensions -{ - /// - [SupportedOSPlatform("windows")] - public static FileSecurity GetAccessControl( - this IFile file, string path) - { - IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); - fileInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - fileInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out FileInfo? fi) - ? fi.GetAccessControl() - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new FileSecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static FileSecurity GetAccessControl( - this IFile file, - string path, - AccessControlSections includeSections) - { - IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); - fileInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - fileInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out FileInfo? fi) - ? fi.GetAccessControl(includeSections) - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new FileSecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static void SetAccessControl(this IFile file, - string path, - FileSecurity fileSecurity) - { - IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); - IFileSystemExtensibility extensibility = - fileInfo.Extensibility; - if (extensibility.TryGetWrappedInstance(out FileInfo? fi)) - { - fi.SetAccessControl(fileSecurity); - } - else - { - extensibility.StoreMetadata(AccessControlHelpers.AccessControl, - fileSecurity); - } - } -} +using System.IO; +using System.Security.AccessControl; + +namespace Testably.Abstractions; + +/// +/// ACL (access control list) extension methods for . +/// +public static class FileAclExtensions +{ + /// + [SupportedOSPlatform("windows")] + public static FileSecurity GetAccessControl( + this IFile file, string path) + { + IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); + fileInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out FileInfo? fi) + ? fi.GetAccessControl() + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new FileSecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static FileSecurity GetAccessControl( + this IFile file, + string path, + AccessControlSections includeSections) + { + IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); + fileInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out FileInfo? fi) + ? fi.GetAccessControl(includeSections) + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new FileSecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static void SetAccessControl(this IFile file, + string path, + FileSecurity fileSecurity) + { + IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out FileInfo? fi)) + { + fi.SetAccessControl(fileSecurity); + } + else + { + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, + fileSecurity); + } + } +} diff --git a/Source/Testably.Abstractions.AccessControl/FileInfoAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/FileInfoAclExtensions.cs index c9cb3b14a..251bb1fbc 100644 --- a/Source/Testably.Abstractions.AccessControl/FileInfoAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/FileInfoAclExtensions.cs @@ -1,58 +1,57 @@ -using System.IO; -using System.Security.AccessControl; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions; - -/// -/// ACL (access control list) extension methods for . -/// -public static class FileInfoAclExtensions -{ - /// - [SupportedOSPlatform("windows")] - public static FileSecurity GetAccessControl( - this IFileInfo fileInfo) - { - fileInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - fileInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out FileInfo? fi) - ? fi.GetAccessControl() - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new FileSecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static FileSecurity GetAccessControl( - this IFileInfo fileInfo, - AccessControlSections includeSections) - { - fileInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - fileInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out FileInfo? fi) - ? fi.GetAccessControl(includeSections) - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new FileSecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static void SetAccessControl(this IFileInfo fileInfo, - FileSecurity fileSecurity) - { - IFileSystemExtensibility extensibility = - fileInfo.Extensibility; - if (extensibility.TryGetWrappedInstance(out FileInfo? fi)) - { - fi.SetAccessControl(fileSecurity); - } - else - { - extensibility.StoreMetadata(AccessControlHelpers.AccessControl, - fileSecurity); - } - } -} +using System.IO; +using System.Security.AccessControl; + +namespace Testably.Abstractions; + +/// +/// ACL (access control list) extension methods for . +/// +public static class FileInfoAclExtensions +{ + /// + [SupportedOSPlatform("windows")] + public static FileSecurity GetAccessControl( + this IFileInfo fileInfo) + { + fileInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out FileInfo? fi) + ? fi.GetAccessControl() + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new FileSecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static FileSecurity GetAccessControl( + this IFileInfo fileInfo, + AccessControlSections includeSections) + { + fileInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out FileInfo? fi) + ? fi.GetAccessControl(includeSections) + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new FileSecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static void SetAccessControl(this IFileInfo fileInfo, + FileSecurity fileSecurity) + { + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out FileInfo? fi)) + { + fi.SetAccessControl(fileSecurity); + } + else + { + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, + fileSecurity); + } + } +} diff --git a/Source/Testably.Abstractions.AccessControl/FileStreamAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/FileStreamAclExtensions.cs index 27166894a..54aa7b80d 100644 --- a/Source/Testably.Abstractions.AccessControl/FileStreamAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/FileStreamAclExtensions.cs @@ -1,41 +1,40 @@ -using System.IO; -using System.Security.AccessControl; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions; - -/// -/// ACL (access control list) extension methods for . -/// -public static class FileStreamAclExtensions -{ - /// - [SupportedOSPlatform("windows")] - public static FileSecurity GetAccessControl(this FileSystemStream fileStream) - { - IFileSystemExtensibility extensibility = - fileStream.Extensibility; - return extensibility.TryGetWrappedInstance(out FileStream? fs) - ? fs.GetAccessControl() - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new FileSecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static void SetAccessControl(this FileSystemStream fileStream, - FileSecurity fileSecurity) - { - IFileSystemExtensibility extensibility = - fileStream.Extensibility; - if (extensibility.TryGetWrappedInstance(out FileStream? fs)) - { - fs.SetAccessControl(fileSecurity); - } - else - { - extensibility.StoreMetadata(AccessControlHelpers.AccessControl, - fileSecurity); - } - } -} +using System.IO; +using System.Security.AccessControl; + +namespace Testably.Abstractions; + +/// +/// ACL (access control list) extension methods for . +/// +public static class FileStreamAclExtensions +{ + /// + [SupportedOSPlatform("windows")] + public static FileSecurity GetAccessControl(this FileSystemStream fileStream) + { + IFileSystemExtensibility extensibility = + fileStream.Extensibility; + return extensibility.TryGetWrappedInstance(out FileStream? fs) + ? fs.GetAccessControl() + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new FileSecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static void SetAccessControl(this FileSystemStream fileStream, + FileSecurity fileSecurity) + { + IFileSystemExtensibility extensibility = + fileStream.Extensibility; + if (extensibility.TryGetWrappedInstance(out FileStream? fs)) + { + fs.SetAccessControl(fileSecurity); + } + else + { + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, + fileSecurity); + } + } +} diff --git a/Source/Testably.Abstractions.AccessControl/Usings.cs b/Source/Testably.Abstractions.AccessControl/Usings.cs index acb44645c..f15db6c3f 100644 --- a/Source/Testably.Abstractions.AccessControl/Usings.cs +++ b/Source/Testably.Abstractions.AccessControl/Usings.cs @@ -3,3 +3,4 @@ #else global using System.Runtime.Versioning; #endif +global using Testably.Abstractions.FileSystem; diff --git a/Source/Testably.Abstractions.Compression/IZipArchive.cs b/Source/Testably.Abstractions.Compression/IZipArchive.cs index 7495381ff..a98808f55 100644 --- a/Source/Testably.Abstractions.Compression/IZipArchive.cs +++ b/Source/Testably.Abstractions.Compression/IZipArchive.cs @@ -1,49 +1,48 @@ -using System; -using System.Collections.ObjectModel; -using System.IO.Compression; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions; - -/// -public interface IZipArchive : IFileSystemEntity, IDisposable -{ -#if FEATURE_ZIPFILE_NET7 - /// - string Comment { get; set; } -#endif - - /// - ReadOnlyCollection Entries { get; } - - /// - ZipArchiveMode Mode { get; } - - /// - IZipArchiveEntry CreateEntry(string entryName); - - /// - IZipArchiveEntry CreateEntry(string entryName, CompressionLevel compressionLevel); - - /// - IZipArchiveEntry CreateEntryFromFile(string sourceFileName, - string entryName); - - /// - IZipArchiveEntry CreateEntryFromFile(string sourceFileName, - string entryName, - CompressionLevel compressionLevel); - - /// - void ExtractToDirectory(string destinationDirectoryName); - -#if FEATURE_COMPRESSION_ADVANCED - /// - void ExtractToDirectory(string destinationDirectoryName, - bool overwriteFiles); -#endif - - /// - IZipArchiveEntry? GetEntry(string entryName); -} +using System; +using System.Collections.ObjectModel; +using System.IO.Compression; + +namespace Testably.Abstractions; + +/// +public interface IZipArchive : IFileSystemEntity, IDisposable +{ +#if FEATURE_ZIPFILE_NET7 + /// + string Comment { get; set; } +#endif + + /// + ReadOnlyCollection Entries { get; } + + /// + ZipArchiveMode Mode { get; } + + /// + IZipArchiveEntry CreateEntry(string entryName); + + /// + IZipArchiveEntry CreateEntry(string entryName, CompressionLevel compressionLevel); + + /// + IZipArchiveEntry CreateEntryFromFile(string sourceFileName, + string entryName); + + /// + IZipArchiveEntry CreateEntryFromFile(string sourceFileName, + string entryName, + CompressionLevel compressionLevel); + + /// + void ExtractToDirectory(string destinationDirectoryName); + +#if FEATURE_COMPRESSION_ADVANCED + /// + void ExtractToDirectory(string destinationDirectoryName, + bool overwriteFiles); +#endif + + /// + IZipArchiveEntry? GetEntry(string entryName); +} diff --git a/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs b/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs index 037b3e64e..d062ec5bb 100644 --- a/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs +++ b/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs @@ -1,61 +1,60 @@ -using System; -using System.IO; -using System.IO.Compression; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions; - -/// -public interface IZipArchiveEntry : IFileSystemEntity -{ - /// - IZipArchive Archive { get; } - -#if FEATURE_ZIPFILE_NET7 - /// - string Comment { get; set; } -#endif - - /// - long CompressedLength { get; } - -#if FEATURE_COMPRESSION_ADVANCED - /// - uint Crc32 { get; } -#endif - -#if FEATURE_COMPRESSION_ADVANCED - /// - int ExternalAttributes { get; set; } -#endif - - /// - string FullName { get; } - -#if FEATURE_ZIPFILE_NET7 - /// - bool IsEncrypted { get; } -#endif - - /// - DateTimeOffset LastWriteTime { get; set; } - - /// - long Length { get; } - - /// - string Name { get; } - - /// - void Delete(); - - /// - void ExtractToFile(string destinationFileName); - - /// - void ExtractToFile(string destinationFileName, - bool overwrite); - - /// - Stream Open(); -} +using System; +using System.IO; +using System.IO.Compression; + +namespace Testably.Abstractions; + +/// +public interface IZipArchiveEntry : IFileSystemEntity +{ + /// + IZipArchive Archive { get; } + +#if FEATURE_ZIPFILE_NET7 + /// + string Comment { get; set; } +#endif + + /// + long CompressedLength { get; } + +#if FEATURE_COMPRESSION_ADVANCED + /// + uint Crc32 { get; } +#endif + +#if FEATURE_COMPRESSION_ADVANCED + /// + int ExternalAttributes { get; set; } +#endif + + /// + string FullName { get; } + +#if FEATURE_ZIPFILE_NET7 + /// + bool IsEncrypted { get; } +#endif + + /// + DateTimeOffset LastWriteTime { get; set; } + + /// + long Length { get; } + + /// + string Name { get; } + + /// + void Delete(); + + /// + void ExtractToFile(string destinationFileName); + + /// + void ExtractToFile(string destinationFileName, + bool overwrite); + + /// + Stream Open(); +} diff --git a/Source/Testably.Abstractions.Compression/IZipArchiveFactory.cs b/Source/Testably.Abstractions.Compression/IZipArchiveFactory.cs index 4fa0d3d15..67676b8e8 100644 --- a/Source/Testably.Abstractions.Compression/IZipArchiveFactory.cs +++ b/Source/Testably.Abstractions.Compression/IZipArchiveFactory.cs @@ -1,25 +1,24 @@ -using System.IO; -using System.IO.Compression; -using System.Text; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions; - -/// -public interface IZipArchiveFactory : IFileSystemEntity -{ - /// - IZipArchive New(Stream stream); - - /// - IZipArchive New(Stream stream, ZipArchiveMode mode); - - /// - IZipArchive New(Stream stream, ZipArchiveMode mode, bool leaveOpen); - - /// - IZipArchive New(Stream stream, - ZipArchiveMode mode, - bool leaveOpen, - Encoding? entryNameEncoding); -} +using System.IO; +using System.IO.Compression; +using System.Text; + +namespace Testably.Abstractions; + +/// +public interface IZipArchiveFactory : IFileSystemEntity +{ + /// + IZipArchive New(Stream stream); + + /// + IZipArchive New(Stream stream, ZipArchiveMode mode); + + /// + IZipArchive New(Stream stream, ZipArchiveMode mode, bool leaveOpen); + + /// + IZipArchive New(Stream stream, + ZipArchiveMode mode, + bool leaveOpen, + Encoding? entryNameEncoding); +} diff --git a/Source/Testably.Abstractions.Compression/IZipFile.cs b/Source/Testably.Abstractions.Compression/IZipFile.cs index fac7c40e6..4fc7f592a 100644 --- a/Source/Testably.Abstractions.Compression/IZipFile.cs +++ b/Source/Testably.Abstractions.Compression/IZipFile.cs @@ -1,62 +1,61 @@ -using System.IO.Compression; -using System.Text; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions; - -/// -public interface IZipFile : IFileSystemEntity -{ - /// - void CreateFromDirectory(string sourceDirectoryName, - string destinationArchiveFileName); - - /// - void CreateFromDirectory(string sourceDirectoryName, - string destinationArchiveFileName, - CompressionLevel compressionLevel, - bool includeBaseDirectory); - - /// - void CreateFromDirectory(string sourceDirectoryName, - string destinationArchiveFileName, - CompressionLevel compressionLevel, - bool includeBaseDirectory, - Encoding entryNameEncoding); - - /// - void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName); - -#if FEATURE_COMPRESSION_OVERWRITE - /// - void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName, - bool overwriteFiles); -#endif - - /// - void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName, - Encoding? entryNameEncoding); - -#if FEATURE_COMPRESSION_OVERWRITE - /// - void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName, - Encoding? entryNameEncoding, - bool overwriteFiles); -#endif - - /// - IZipArchive Open(string archiveFileName, - ZipArchiveMode mode); - - /// - IZipArchive Open(string archiveFileName, - ZipArchiveMode mode, - Encoding? entryNameEncoding); - - /// - IZipArchive OpenRead(string archiveFileName); -} +using System.IO.Compression; +using System.Text; + +namespace Testably.Abstractions; + +/// +public interface IZipFile : IFileSystemEntity +{ + /// + void CreateFromDirectory(string sourceDirectoryName, + string destinationArchiveFileName); + + /// + void CreateFromDirectory(string sourceDirectoryName, + string destinationArchiveFileName, + CompressionLevel compressionLevel, + bool includeBaseDirectory); + + /// + void CreateFromDirectory(string sourceDirectoryName, + string destinationArchiveFileName, + CompressionLevel compressionLevel, + bool includeBaseDirectory, + Encoding entryNameEncoding); + + /// + void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName); + +#if FEATURE_COMPRESSION_OVERWRITE + /// + void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName, + bool overwriteFiles); +#endif + + /// + void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName, + Encoding? entryNameEncoding); + +#if FEATURE_COMPRESSION_OVERWRITE + /// + void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName, + Encoding? entryNameEncoding, + bool overwriteFiles); +#endif + + /// + IZipArchive Open(string archiveFileName, + ZipArchiveMode mode); + + /// + IZipArchive Open(string archiveFileName, + ZipArchiveMode mode, + Encoding? entryNameEncoding); + + /// + IZipArchive OpenRead(string archiveFileName); +} diff --git a/Source/Testably.Abstractions.Compression/Internal/ZipUtilities.cs b/Source/Testably.Abstractions.Compression/Internal/ZipUtilities.cs index 777b021aa..dcb87f566 100644 --- a/Source/Testably.Abstractions.Compression/Internal/ZipUtilities.cs +++ b/Source/Testably.Abstractions.Compression/Internal/ZipUtilities.cs @@ -1,265 +1,264 @@ -using System; -using System.IO; -using System.IO.Compression; -using System.Text; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Internal; - -internal static class ZipUtilities -{ - internal static IZipArchiveEntry CreateEntryFromFile( - IZipArchive destination, - string sourceFileName, - string entryName, - CompressionLevel? compressionLevel = null) - { - if (sourceFileName == null) - { - throw new ArgumentNullException(nameof(sourceFileName)); - } - - using (FileSystemStream fs = destination.FileSystem.FileStream.New(sourceFileName, - FileMode.Open, FileAccess.Read, FileShare.Read)) - { - IZipArchiveEntry entry = compressionLevel.HasValue - ? destination.CreateEntry(entryName, compressionLevel.Value) - : destination.CreateEntry(entryName); - - DateTime lastWrite = - destination.FileSystem.File.GetLastWriteTime(sourceFileName); - - if (lastWrite.Year < 1980 || lastWrite.Year > 2107) - { - lastWrite = new DateTime(1980, 1, 1, 0, 0, 0); - } - - entry.LastWriteTime = lastWrite; - - using (Stream es = entry.Open()) - { - fs.CopyTo(es); - } - - return entry; - } - } - - /// - /// Create a ZipArchive from the files and directories in . - /// - /// - /// - /// - internal static void CreateFromDirectory(IFileSystem fileSystem, - string sourceDirectoryName, - string destinationArchiveFileName, - CompressionLevel? compressionLevel = null, - bool includeBaseDirectory = false, - Encoding? entryNameEncoding = null) - - { - sourceDirectoryName = fileSystem.Path.GetFullPath(sourceDirectoryName); - destinationArchiveFileName = - fileSystem.Path.GetFullPath(destinationArchiveFileName); - - using (ZipArchive archive = Open(fileSystem, destinationArchiveFileName, - ZipArchiveMode.Create, entryNameEncoding)) - { - bool directoryIsEmpty = true; - - IDirectoryInfo di = - fileSystem.DirectoryInfo.New(sourceDirectoryName); - - string basePath = di.FullName; - - if (includeBaseDirectory && di.Parent != null) - { - basePath = di.Parent.FullName; - } - - foreach (IFileSystemInfo file in di - .EnumerateFileSystemInfos("*", SearchOption.AllDirectories)) - { - directoryIsEmpty = false; - - if (file is IFileInfo fileInfo) - { - string entryName = file.FullName - .Substring(basePath.Length + 1) - .Replace("\\", "/"); - ZipArchiveEntry entry = compressionLevel.HasValue - ? archive.CreateEntry(entryName, compressionLevel.Value) - : archive.CreateEntry(entryName); - using Stream stream = entry.Open(); - fileInfo.OpenRead().CopyTo(stream); - } - else if (file is IDirectoryInfo directoryInfo && - directoryInfo.GetFileSystemInfos().Length == 0) - { - #pragma warning disable CA1845 - string entryName = file.FullName.Substring(basePath.Length + 1) + "/"; - #pragma warning restore CA1845 - archive.CreateEntry(entryName); - } - } - - if (includeBaseDirectory && directoryIsEmpty) - { - string entryName = di.Name + "/"; - archive.CreateEntry(entryName); - } - } - } - - internal static void ExtractRelativeToDirectory(this IZipArchiveEntry source, - string destinationDirectoryName, - bool overwrite) - { - if (destinationDirectoryName == null) - { - throw new ArgumentNullException(nameof(destinationDirectoryName)); - } - - string fileDestinationPath = - source.FileSystem.Path.Combine(destinationDirectoryName, - source.FullName.TrimStart( - source.FileSystem.Path.DirectorySeparatorChar, - source.FileSystem.Path.AltDirectorySeparatorChar)); - string? directoryPath = - source.FileSystem.Path.GetDirectoryName(fileDestinationPath); - if (directoryPath != null && - !source.FileSystem.Directory.Exists(directoryPath)) - { - source.FileSystem.Directory.CreateDirectory(directoryPath); - } - - if (source.FullName.EndsWith("/")) - { - if (source.Length != 0) - { - throw new IOException( - "Zip entry name ends in directory separator character but contains data."); - } - - source.FileSystem.Directory.CreateDirectory(fileDestinationPath); - } - else - { - ExtractToFile(source, fileDestinationPath, overwrite); - } - } - - /// - /// Extract the archive at to the - /// . - /// - internal static void ExtractToDirectory(IFileSystem fileSystem, - string sourceArchiveFileName, - string destinationDirectoryName, - Encoding? entryNameEncoding = null, - bool overwriteFiles = false) - { - if (sourceArchiveFileName == null) - { - throw new ArgumentNullException(nameof(sourceArchiveFileName)); - } - - using (ZipArchive archive = Open(fileSystem, sourceArchiveFileName, - ZipArchiveMode.Read, - entryNameEncoding)) - { - ZipArchiveWrapper wrappedArchive = new(fileSystem, archive); - foreach (ZipArchiveEntry entry in archive.Entries) - { - IZipArchiveEntry wrappedEntry = ZipArchiveEntryWrapper.New( - fileSystem, wrappedArchive, entry); - ExtractRelativeToDirectory(wrappedEntry, destinationDirectoryName, - overwriteFiles); - } - } - } - - internal static void ExtractToFile(IZipArchiveEntry source, - string destinationFileName, bool overwrite) - { - FileMode mode = overwrite ? FileMode.Create : FileMode.CreateNew; - - using (FileSystemStream fileStream = source.FileSystem.FileStream - .New(destinationFileName, mode, FileAccess.Write, FileShare.None)) - { - using (Stream entryStream = source.Open()) - { - entryStream.CopyTo(fileStream); - } - } - - try - { - source.FileSystem.File.SetLastWriteTime(destinationFileName, - source.LastWriteTime.DateTime); - } - catch - { - // some OSes might not support setting the last write time - // the extraction should not fail because of that - } - } - - /// - /// Opens a ZipArchive on the specified in the specified - /// . - /// - /// - /// - /// - internal static ZipArchive Open(IFileSystem fileSystem, - string archiveFileName, - ZipArchiveMode mode, - Encoding? entryNameEncoding = null) - { - FileMode fileMode; - FileAccess access; - FileShare fileShare; - - switch (mode) - { - case ZipArchiveMode.Read: - fileMode = FileMode.Open; - access = FileAccess.Read; - fileShare = FileShare.Read; - break; - - case ZipArchiveMode.Create: - fileMode = FileMode.CreateNew; - access = FileAccess.Write; - fileShare = FileShare.None; - break; - - case ZipArchiveMode.Update: - fileMode = FileMode.OpenOrCreate; - access = FileAccess.ReadWrite; - fileShare = FileShare.None; - break; - - default: - throw new ArgumentOutOfRangeException(nameof(mode)); - } - - FileSystemStream fs = fileSystem.FileStream.New(archiveFileName, fileMode, - access, fileShare, bufferSize: 0x1000); - - try - { - return new ZipArchive(fs, mode, leaveOpen: false, - entryNameEncoding: entryNameEncoding); - } - catch - { - fs.Dispose(); - throw; - } - } -} +using System; +using System.IO; +using System.IO.Compression; +using System.Text; + +namespace Testably.Abstractions.Internal; + +internal static class ZipUtilities +{ + internal static IZipArchiveEntry CreateEntryFromFile( + IZipArchive destination, + string sourceFileName, + string entryName, + CompressionLevel? compressionLevel = null) + { + if (sourceFileName == null) + { + throw new ArgumentNullException(nameof(sourceFileName)); + } + + using (FileSystemStream fs = destination.FileSystem.FileStream.New(sourceFileName, + FileMode.Open, FileAccess.Read, FileShare.Read)) + { + IZipArchiveEntry entry = compressionLevel.HasValue + ? destination.CreateEntry(entryName, compressionLevel.Value) + : destination.CreateEntry(entryName); + + DateTime lastWrite = + destination.FileSystem.File.GetLastWriteTime(sourceFileName); + + if (lastWrite.Year < 1980 || lastWrite.Year > 2107) + { + lastWrite = new DateTime(1980, 1, 1, 0, 0, 0); + } + + entry.LastWriteTime = lastWrite; + + using (Stream es = entry.Open()) + { + fs.CopyTo(es); + } + + return entry; + } + } + + /// + /// Create a ZipArchive from the files and directories in . + /// + /// + /// + /// + internal static void CreateFromDirectory(IFileSystem fileSystem, + string sourceDirectoryName, + string destinationArchiveFileName, + CompressionLevel? compressionLevel = null, + bool includeBaseDirectory = false, + Encoding? entryNameEncoding = null) + + { + sourceDirectoryName = fileSystem.Path.GetFullPath(sourceDirectoryName); + destinationArchiveFileName = + fileSystem.Path.GetFullPath(destinationArchiveFileName); + + using (ZipArchive archive = Open(fileSystem, destinationArchiveFileName, + ZipArchiveMode.Create, entryNameEncoding)) + { + bool directoryIsEmpty = true; + + IDirectoryInfo di = + fileSystem.DirectoryInfo.New(sourceDirectoryName); + + string basePath = di.FullName; + + if (includeBaseDirectory && di.Parent != null) + { + basePath = di.Parent.FullName; + } + + foreach (IFileSystemInfo file in di + .EnumerateFileSystemInfos("*", SearchOption.AllDirectories)) + { + directoryIsEmpty = false; + + if (file is IFileInfo fileInfo) + { + string entryName = file.FullName + .Substring(basePath.Length + 1) + .Replace("\\", "/"); + ZipArchiveEntry entry = compressionLevel.HasValue + ? archive.CreateEntry(entryName, compressionLevel.Value) + : archive.CreateEntry(entryName); + using Stream stream = entry.Open(); + fileInfo.OpenRead().CopyTo(stream); + } + else if (file is IDirectoryInfo directoryInfo && + directoryInfo.GetFileSystemInfos().Length == 0) + { + #pragma warning disable CA1845 + string entryName = file.FullName.Substring(basePath.Length + 1) + "/"; + #pragma warning restore CA1845 + archive.CreateEntry(entryName); + } + } + + if (includeBaseDirectory && directoryIsEmpty) + { + string entryName = di.Name + "/"; + archive.CreateEntry(entryName); + } + } + } + + internal static void ExtractRelativeToDirectory(this IZipArchiveEntry source, + string destinationDirectoryName, + bool overwrite) + { + if (destinationDirectoryName == null) + { + throw new ArgumentNullException(nameof(destinationDirectoryName)); + } + + string fileDestinationPath = + source.FileSystem.Path.Combine(destinationDirectoryName, + source.FullName.TrimStart( + source.FileSystem.Path.DirectorySeparatorChar, + source.FileSystem.Path.AltDirectorySeparatorChar)); + string? directoryPath = + source.FileSystem.Path.GetDirectoryName(fileDestinationPath); + if (directoryPath != null && + !source.FileSystem.Directory.Exists(directoryPath)) + { + source.FileSystem.Directory.CreateDirectory(directoryPath); + } + + if (source.FullName.EndsWith("/")) + { + if (source.Length != 0) + { + throw new IOException( + "Zip entry name ends in directory separator character but contains data."); + } + + source.FileSystem.Directory.CreateDirectory(fileDestinationPath); + } + else + { + ExtractToFile(source, fileDestinationPath, overwrite); + } + } + + /// + /// Extract the archive at to the + /// . + /// + internal static void ExtractToDirectory(IFileSystem fileSystem, + string sourceArchiveFileName, + string destinationDirectoryName, + Encoding? entryNameEncoding = null, + bool overwriteFiles = false) + { + if (sourceArchiveFileName == null) + { + throw new ArgumentNullException(nameof(sourceArchiveFileName)); + } + + using (ZipArchive archive = Open(fileSystem, sourceArchiveFileName, + ZipArchiveMode.Read, + entryNameEncoding)) + { + ZipArchiveWrapper wrappedArchive = new(fileSystem, archive); + foreach (ZipArchiveEntry entry in archive.Entries) + { + IZipArchiveEntry wrappedEntry = ZipArchiveEntryWrapper.New( + fileSystem, wrappedArchive, entry); + ExtractRelativeToDirectory(wrappedEntry, destinationDirectoryName, + overwriteFiles); + } + } + } + + internal static void ExtractToFile(IZipArchiveEntry source, + string destinationFileName, bool overwrite) + { + FileMode mode = overwrite ? FileMode.Create : FileMode.CreateNew; + + using (FileSystemStream fileStream = source.FileSystem.FileStream + .New(destinationFileName, mode, FileAccess.Write, FileShare.None)) + { + using (Stream entryStream = source.Open()) + { + entryStream.CopyTo(fileStream); + } + } + + try + { + source.FileSystem.File.SetLastWriteTime(destinationFileName, + source.LastWriteTime.DateTime); + } + catch + { + // some OSes might not support setting the last write time + // the extraction should not fail because of that + } + } + + /// + /// Opens a ZipArchive on the specified in the specified + /// . + /// + /// + /// + /// + internal static ZipArchive Open(IFileSystem fileSystem, + string archiveFileName, + ZipArchiveMode mode, + Encoding? entryNameEncoding = null) + { + FileMode fileMode; + FileAccess access; + FileShare fileShare; + + switch (mode) + { + case ZipArchiveMode.Read: + fileMode = FileMode.Open; + access = FileAccess.Read; + fileShare = FileShare.Read; + break; + + case ZipArchiveMode.Create: + fileMode = FileMode.CreateNew; + access = FileAccess.Write; + fileShare = FileShare.None; + break; + + case ZipArchiveMode.Update: + fileMode = FileMode.OpenOrCreate; + access = FileAccess.ReadWrite; + fileShare = FileShare.None; + break; + + default: + throw new ArgumentOutOfRangeException(nameof(mode)); + } + + FileSystemStream fs = fileSystem.FileStream.New(archiveFileName, fileMode, + access, fileShare, bufferSize: 0x1000); + + try + { + return new ZipArchive(fs, mode, leaveOpen: false, + entryNameEncoding: entryNameEncoding); + } + catch + { + fs.Dispose(); + throw; + } + } +} diff --git a/Source/Testably.Abstractions.Compression/Usings.cs b/Source/Testably.Abstractions.Compression/Usings.cs new file mode 100644 index 000000000..16d0ed6cd --- /dev/null +++ b/Source/Testably.Abstractions.Compression/Usings.cs @@ -0,0 +1 @@ +global using Testably.Abstractions.FileSystem; diff --git a/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs b/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs index fa53c6ee2..384440dd0 100644 --- a/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs +++ b/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs @@ -1,128 +1,127 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.IO.Compression; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Internal; - -namespace Testably.Abstractions; - -internal sealed class ZipArchiveEntryWrapper : IZipArchiveEntry -{ - private readonly ZipArchiveEntry _instance; - - public ZipArchiveEntryWrapper(IFileSystem fileSystem, - IZipArchive archive, - ZipArchiveEntry instance) - { - _instance = instance; - FileSystem = fileSystem; - Archive = archive; - } - - #region IZipArchiveEntry Members - - /// - public IZipArchive Archive { get; } - -#if FEATURE_ZIPFILE_NET7 - /// - public string Comment - { - get => _instance.Comment; - set => _instance.Comment = value; - } -#endif - - /// - public long CompressedLength - => _instance.CompressedLength; - -#if FEATURE_COMPRESSION_ADVANCED - /// - public uint Crc32 - => _instance.Crc32; -#endif - -#if FEATURE_COMPRESSION_ADVANCED - /// - public int ExternalAttributes - { - get => _instance.ExternalAttributes; - set => _instance.ExternalAttributes = value; - } -#endif - - /// - public IFileSystem FileSystem { get; } - - /// - public string FullName - => _instance.FullName; - -#if FEATURE_ZIPFILE_NET7 - /// - public bool IsEncrypted - => _instance.IsEncrypted; -#endif - - /// - public DateTimeOffset LastWriteTime - { - get => _instance.LastWriteTime; - set => _instance.LastWriteTime = value; - } - - /// - public long Length - => _instance.Length; - - /// - public string Name - => _instance.Name; - - /// - public void Delete() - => _instance.Delete(); - - /// - public void ExtractToFile(string destinationFileName) - => Execute.WhenRealFileSystem(FileSystem, - () => _instance.ExtractToFile(destinationFileName), - () => ExtractToFile(destinationFileName, false)); - - /// - public void ExtractToFile(string destinationFileName, bool overwrite) - { - if (destinationFileName == null) - { - throw new ArgumentNullException(nameof(destinationFileName)); - } - - Execute.WhenRealFileSystem(FileSystem, - () => _instance.ExtractToFile(destinationFileName, overwrite), - () => ZipUtilities.ExtractToFile(this, destinationFileName, overwrite)); - } - - /// - public Stream Open() - => _instance.Open(); - - #endregion - - /// - public override string ToString() - => _instance.ToString(); - - [return: NotNullIfNotNull("instance")] - internal static IZipArchiveEntry? New(IFileSystem fileSystem, IZipArchive archive, - ZipArchiveEntry? instance) - { - if (instance == null) - { - return null; - } - - return new ZipArchiveEntryWrapper(fileSystem, archive, instance); - } -} +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.IO.Compression; +using Testably.Abstractions.Internal; + +namespace Testably.Abstractions; + +internal sealed class ZipArchiveEntryWrapper : IZipArchiveEntry +{ + private readonly ZipArchiveEntry _instance; + + public ZipArchiveEntryWrapper(IFileSystem fileSystem, + IZipArchive archive, + ZipArchiveEntry instance) + { + _instance = instance; + FileSystem = fileSystem; + Archive = archive; + } + + #region IZipArchiveEntry Members + + /// + public IZipArchive Archive { get; } + +#if FEATURE_ZIPFILE_NET7 + /// + public string Comment + { + get => _instance.Comment; + set => _instance.Comment = value; + } +#endif + + /// + public long CompressedLength + => _instance.CompressedLength; + +#if FEATURE_COMPRESSION_ADVANCED + /// + public uint Crc32 + => _instance.Crc32; +#endif + +#if FEATURE_COMPRESSION_ADVANCED + /// + public int ExternalAttributes + { + get => _instance.ExternalAttributes; + set => _instance.ExternalAttributes = value; + } +#endif + + /// + public IFileSystem FileSystem { get; } + + /// + public string FullName + => _instance.FullName; + +#if FEATURE_ZIPFILE_NET7 + /// + public bool IsEncrypted + => _instance.IsEncrypted; +#endif + + /// + public DateTimeOffset LastWriteTime + { + get => _instance.LastWriteTime; + set => _instance.LastWriteTime = value; + } + + /// + public long Length + => _instance.Length; + + /// + public string Name + => _instance.Name; + + /// + public void Delete() + => _instance.Delete(); + + /// + public void ExtractToFile(string destinationFileName) + => Execute.WhenRealFileSystem(FileSystem, + () => _instance.ExtractToFile(destinationFileName), + () => ExtractToFile(destinationFileName, false)); + + /// + public void ExtractToFile(string destinationFileName, bool overwrite) + { + if (destinationFileName == null) + { + throw new ArgumentNullException(nameof(destinationFileName)); + } + + Execute.WhenRealFileSystem(FileSystem, + () => _instance.ExtractToFile(destinationFileName, overwrite), + () => ZipUtilities.ExtractToFile(this, destinationFileName, overwrite)); + } + + /// + public Stream Open() + => _instance.Open(); + + #endregion + + /// + public override string ToString() + => _instance.ToString(); + + [return: NotNullIfNotNull("instance")] + internal static IZipArchiveEntry? New(IFileSystem fileSystem, IZipArchive archive, + ZipArchiveEntry? instance) + { + if (instance == null) + { + return null; + } + + return new ZipArchiveEntryWrapper(fileSystem, archive, instance); + } +} diff --git a/Source/Testably.Abstractions.Compression/ZipArchiveFactory.cs b/Source/Testably.Abstractions.Compression/ZipArchiveFactory.cs index 906df81b6..673116f56 100644 --- a/Source/Testably.Abstractions.Compression/ZipArchiveFactory.cs +++ b/Source/Testably.Abstractions.Compression/ZipArchiveFactory.cs @@ -1,41 +1,40 @@ -using System.IO; -using System.IO.Compression; -using System.Text; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions; - -internal sealed class ZipArchiveFactory : IZipArchiveFactory -{ - public ZipArchiveFactory(IFileSystem fileSystem) - { - FileSystem = fileSystem; - } - - #region IZipArchiveFactory Members - - /// - public IFileSystem FileSystem { get; } - - /// - public IZipArchive New(Stream stream) - => new ZipArchiveWrapper(FileSystem, new ZipArchive(stream)); - - /// - public IZipArchive New(Stream stream, ZipArchiveMode mode) - => new ZipArchiveWrapper(FileSystem, new ZipArchive(stream, mode)); - - /// - public IZipArchive New(Stream stream, ZipArchiveMode mode, bool leaveOpen) - => new ZipArchiveWrapper(FileSystem, new ZipArchive(stream, mode, leaveOpen)); - - /// - public IZipArchive New(Stream stream, - ZipArchiveMode mode, - bool leaveOpen, - Encoding? entryNameEncoding) - => new ZipArchiveWrapper(FileSystem, - new ZipArchive(stream, mode, leaveOpen, entryNameEncoding)); - - #endregion -} +using System.IO; +using System.IO.Compression; +using System.Text; + +namespace Testably.Abstractions; + +internal sealed class ZipArchiveFactory : IZipArchiveFactory +{ + public ZipArchiveFactory(IFileSystem fileSystem) + { + FileSystem = fileSystem; + } + + #region IZipArchiveFactory Members + + /// + public IFileSystem FileSystem { get; } + + /// + public IZipArchive New(Stream stream) + => new ZipArchiveWrapper(FileSystem, new ZipArchive(stream)); + + /// + public IZipArchive New(Stream stream, ZipArchiveMode mode) + => new ZipArchiveWrapper(FileSystem, new ZipArchive(stream, mode)); + + /// + public IZipArchive New(Stream stream, ZipArchiveMode mode, bool leaveOpen) + => new ZipArchiveWrapper(FileSystem, new ZipArchive(stream, mode, leaveOpen)); + + /// + public IZipArchive New(Stream stream, + ZipArchiveMode mode, + bool leaveOpen, + Encoding? entryNameEncoding) + => new ZipArchiveWrapper(FileSystem, + new ZipArchive(stream, mode, leaveOpen, entryNameEncoding)); + + #endregion +} diff --git a/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs b/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs index 0342a3e1e..c2343815a 100644 --- a/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs +++ b/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs @@ -1,133 +1,132 @@ -using System; -using System.Collections.ObjectModel; -using System.IO.Compression; -using System.Linq; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Internal; - -namespace Testably.Abstractions; - -internal sealed class ZipArchiveWrapper : IZipArchive -{ - private readonly ZipArchive _instance; - - public ZipArchiveWrapper(IFileSystem fileSystem, ZipArchive instance) - { - _instance = instance; - FileSystem = fileSystem; - } - - #region IZipArchive Members - -#if FEATURE_ZIPFILE_NET7 - /// - public string Comment - { - get => _instance.Comment; - set => _instance.Comment = value; - } -#endif - - /// - public ReadOnlyCollection Entries - => MapToZipArchiveEntries(_instance.Entries); - - /// - public IFileSystem FileSystem { get; } - - /// - public ZipArchiveMode Mode - => _instance.Mode; - - /// - public void Dispose() - => _instance.Dispose(); - - /// - public IZipArchiveEntry CreateEntry(string entryName) - => ZipArchiveEntryWrapper.New(FileSystem, this, _instance.CreateEntry(entryName)); - - /// - public IZipArchiveEntry CreateEntry(string entryName, - CompressionLevel compressionLevel) - => ZipArchiveEntryWrapper.New(FileSystem, this, - _instance.CreateEntry(entryName, compressionLevel)); - - /// - public IZipArchiveEntry CreateEntryFromFile(string sourceFileName, string entryName) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipArchiveEntryWrapper.New(FileSystem, this, - _instance.CreateEntryFromFile( - sourceFileName, - entryName)), - () => ZipUtilities.CreateEntryFromFile(this, - sourceFileName, - entryName)); - - /// - public IZipArchiveEntry CreateEntryFromFile(string sourceFileName, string entryName, - CompressionLevel compressionLevel) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipArchiveEntryWrapper.New(FileSystem, this, - _instance.CreateEntryFromFile( - sourceFileName, - entryName, - compressionLevel)), - () => ZipUtilities.CreateEntryFromFile(this, - sourceFileName, - entryName, - compressionLevel)); - - /// - public void ExtractToDirectory(string destinationDirectoryName) - { - if (destinationDirectoryName == null) - { - throw new ArgumentNullException(nameof(destinationDirectoryName)); - } - - Execute.WhenRealFileSystem(FileSystem, - () => _instance.ExtractToDirectory(destinationDirectoryName), - () => - { - foreach (IZipArchiveEntry entry in Entries) - { - entry.ExtractRelativeToDirectory(destinationDirectoryName, false); - } - }); - } - -#if FEATURE_COMPRESSION_ADVANCED - /// - public void ExtractToDirectory(string destinationDirectoryName, bool overwriteFiles) - { - if (destinationDirectoryName == null) - { - throw new ArgumentNullException(nameof(destinationDirectoryName)); - } - - Execute.WhenRealFileSystem(FileSystem, - () => _instance.ExtractToDirectory(destinationDirectoryName, overwriteFiles), - () => - { - foreach (IZipArchiveEntry entry in Entries) - { - entry.ExtractRelativeToDirectory(destinationDirectoryName, - overwriteFiles); - } - }); - } -#endif - - /// - public IZipArchiveEntry? GetEntry(string entryName) - => ZipArchiveEntryWrapper.New(FileSystem, this, _instance.GetEntry(entryName)); - - #endregion - - private ReadOnlyCollection MapToZipArchiveEntries( - ReadOnlyCollection zipArchiveEntries) - => new(zipArchiveEntries - .Select(e => ZipArchiveEntryWrapper.New(FileSystem, this, e)) - .ToList()); -} +using System; +using System.Collections.ObjectModel; +using System.IO.Compression; +using System.Linq; +using Testably.Abstractions.Internal; + +namespace Testably.Abstractions; + +internal sealed class ZipArchiveWrapper : IZipArchive +{ + private readonly ZipArchive _instance; + + public ZipArchiveWrapper(IFileSystem fileSystem, ZipArchive instance) + { + _instance = instance; + FileSystem = fileSystem; + } + + #region IZipArchive Members + +#if FEATURE_ZIPFILE_NET7 + /// + public string Comment + { + get => _instance.Comment; + set => _instance.Comment = value; + } +#endif + + /// + public ReadOnlyCollection Entries + => MapToZipArchiveEntries(_instance.Entries); + + /// + public IFileSystem FileSystem { get; } + + /// + public ZipArchiveMode Mode + => _instance.Mode; + + /// + public void Dispose() + => _instance.Dispose(); + + /// + public IZipArchiveEntry CreateEntry(string entryName) + => ZipArchiveEntryWrapper.New(FileSystem, this, _instance.CreateEntry(entryName)); + + /// + public IZipArchiveEntry CreateEntry(string entryName, + CompressionLevel compressionLevel) + => ZipArchiveEntryWrapper.New(FileSystem, this, + _instance.CreateEntry(entryName, compressionLevel)); + + /// + public IZipArchiveEntry CreateEntryFromFile(string sourceFileName, string entryName) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipArchiveEntryWrapper.New(FileSystem, this, + _instance.CreateEntryFromFile( + sourceFileName, + entryName)), + () => ZipUtilities.CreateEntryFromFile(this, + sourceFileName, + entryName)); + + /// + public IZipArchiveEntry CreateEntryFromFile(string sourceFileName, string entryName, + CompressionLevel compressionLevel) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipArchiveEntryWrapper.New(FileSystem, this, + _instance.CreateEntryFromFile( + sourceFileName, + entryName, + compressionLevel)), + () => ZipUtilities.CreateEntryFromFile(this, + sourceFileName, + entryName, + compressionLevel)); + + /// + public void ExtractToDirectory(string destinationDirectoryName) + { + if (destinationDirectoryName == null) + { + throw new ArgumentNullException(nameof(destinationDirectoryName)); + } + + Execute.WhenRealFileSystem(FileSystem, + () => _instance.ExtractToDirectory(destinationDirectoryName), + () => + { + foreach (IZipArchiveEntry entry in Entries) + { + entry.ExtractRelativeToDirectory(destinationDirectoryName, false); + } + }); + } + +#if FEATURE_COMPRESSION_ADVANCED + /// + public void ExtractToDirectory(string destinationDirectoryName, bool overwriteFiles) + { + if (destinationDirectoryName == null) + { + throw new ArgumentNullException(nameof(destinationDirectoryName)); + } + + Execute.WhenRealFileSystem(FileSystem, + () => _instance.ExtractToDirectory(destinationDirectoryName, overwriteFiles), + () => + { + foreach (IZipArchiveEntry entry in Entries) + { + entry.ExtractRelativeToDirectory(destinationDirectoryName, + overwriteFiles); + } + }); + } +#endif + + /// + public IZipArchiveEntry? GetEntry(string entryName) + => ZipArchiveEntryWrapper.New(FileSystem, this, _instance.GetEntry(entryName)); + + #endregion + + private ReadOnlyCollection MapToZipArchiveEntries( + ReadOnlyCollection zipArchiveEntries) + => new(zipArchiveEntries + .Select(e => ZipArchiveEntryWrapper.New(FileSystem, this, e)) + .ToList()); +} diff --git a/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs b/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs index a2933ab14..5ef07098a 100644 --- a/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs +++ b/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs @@ -1,166 +1,165 @@ -using System.IO.Compression; -using System.Text; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Internal; - -namespace Testably.Abstractions; - -internal sealed class ZipFileWrapper : IZipFile -{ - public ZipFileWrapper(IFileSystem fileSystem) - { - FileSystem = fileSystem; - } - - #region IZipFile Members - - /// - public IFileSystem FileSystem { get; } - - /// - public void CreateFromDirectory(string sourceDirectoryName, - string destinationArchiveFileName) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.CreateFromDirectory( - sourceDirectoryName, - destinationArchiveFileName), - () => ZipUtilities.CreateFromDirectory( - FileSystem, - sourceDirectoryName, - destinationArchiveFileName)); - - /// - public void CreateFromDirectory(string sourceDirectoryName, - string destinationArchiveFileName, - CompressionLevel compressionLevel, - bool includeBaseDirectory) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.CreateFromDirectory( - sourceDirectoryName, - destinationArchiveFileName, - compressionLevel, - includeBaseDirectory), - () => ZipUtilities.CreateFromDirectory( - FileSystem, - sourceDirectoryName, - destinationArchiveFileName, - compressionLevel, - includeBaseDirectory)); - - /// - public void CreateFromDirectory(string sourceDirectoryName, - string destinationArchiveFileName, - CompressionLevel compressionLevel, - bool includeBaseDirectory, - Encoding entryNameEncoding) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.CreateFromDirectory( - sourceDirectoryName, - destinationArchiveFileName, - compressionLevel, - includeBaseDirectory, - entryNameEncoding), - () => ZipUtilities.CreateFromDirectory( - FileSystem, - sourceDirectoryName, - destinationArchiveFileName, - compressionLevel, - includeBaseDirectory, - entryNameEncoding)); - - /// - public void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.ExtractToDirectory( - sourceArchiveFileName, - destinationDirectoryName), - () => ZipUtilities.ExtractToDirectory( - FileSystem, - sourceArchiveFileName, - destinationDirectoryName)); - -#if FEATURE_COMPRESSION_OVERWRITE - /// - public void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName, - bool overwriteFiles) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.ExtractToDirectory( - sourceArchiveFileName, - destinationDirectoryName, - overwriteFiles), - () => ZipUtilities.ExtractToDirectory( - FileSystem, - sourceArchiveFileName, - destinationDirectoryName, - overwriteFiles: overwriteFiles)); -#endif - - /// - public void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName, - Encoding? entryNameEncoding) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.ExtractToDirectory( - sourceArchiveFileName, - destinationDirectoryName, - entryNameEncoding), - () => ZipUtilities.ExtractToDirectory( - FileSystem, - sourceArchiveFileName, - destinationDirectoryName, - entryNameEncoding: entryNameEncoding)); - -#if FEATURE_COMPRESSION_OVERWRITE - /// - public void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName, - Encoding? entryNameEncoding, - bool overwriteFiles) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.ExtractToDirectory( - sourceArchiveFileName, - destinationDirectoryName, - entryNameEncoding, - overwriteFiles), - () => ZipUtilities.ExtractToDirectory( - FileSystem, - sourceArchiveFileName, - destinationDirectoryName, - entryNameEncoding, - overwriteFiles)); -#endif - - /// - public IZipArchive Open(string archiveFileName, ZipArchiveMode mode) - => new ZipArchiveWrapper(FileSystem, - Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.Open(archiveFileName, mode), - () => ZipUtilities.Open(FileSystem, - archiveFileName, - mode))); - - /// - public IZipArchive Open(string archiveFileName, - ZipArchiveMode mode, - Encoding? entryNameEncoding) - => new ZipArchiveWrapper(FileSystem, - Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.Open(archiveFileName, mode, entryNameEncoding), - () => ZipUtilities.Open(FileSystem, - archiveFileName, - mode, - entryNameEncoding))); - - /// - public IZipArchive OpenRead(string archiveFileName) - => new ZipArchiveWrapper(FileSystem, - Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.OpenRead(archiveFileName), - () => ZipUtilities.Open(FileSystem, - archiveFileName, - ZipArchiveMode.Read))); - - #endregion -} +using System.IO.Compression; +using System.Text; +using Testably.Abstractions.Internal; + +namespace Testably.Abstractions; + +internal sealed class ZipFileWrapper : IZipFile +{ + public ZipFileWrapper(IFileSystem fileSystem) + { + FileSystem = fileSystem; + } + + #region IZipFile Members + + /// + public IFileSystem FileSystem { get; } + + /// + public void CreateFromDirectory(string sourceDirectoryName, + string destinationArchiveFileName) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.CreateFromDirectory( + sourceDirectoryName, + destinationArchiveFileName), + () => ZipUtilities.CreateFromDirectory( + FileSystem, + sourceDirectoryName, + destinationArchiveFileName)); + + /// + public void CreateFromDirectory(string sourceDirectoryName, + string destinationArchiveFileName, + CompressionLevel compressionLevel, + bool includeBaseDirectory) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.CreateFromDirectory( + sourceDirectoryName, + destinationArchiveFileName, + compressionLevel, + includeBaseDirectory), + () => ZipUtilities.CreateFromDirectory( + FileSystem, + sourceDirectoryName, + destinationArchiveFileName, + compressionLevel, + includeBaseDirectory)); + + /// + public void CreateFromDirectory(string sourceDirectoryName, + string destinationArchiveFileName, + CompressionLevel compressionLevel, + bool includeBaseDirectory, + Encoding entryNameEncoding) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.CreateFromDirectory( + sourceDirectoryName, + destinationArchiveFileName, + compressionLevel, + includeBaseDirectory, + entryNameEncoding), + () => ZipUtilities.CreateFromDirectory( + FileSystem, + sourceDirectoryName, + destinationArchiveFileName, + compressionLevel, + includeBaseDirectory, + entryNameEncoding)); + + /// + public void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.ExtractToDirectory( + sourceArchiveFileName, + destinationDirectoryName), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + sourceArchiveFileName, + destinationDirectoryName)); + +#if FEATURE_COMPRESSION_OVERWRITE + /// + public void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName, + bool overwriteFiles) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.ExtractToDirectory( + sourceArchiveFileName, + destinationDirectoryName, + overwriteFiles), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + sourceArchiveFileName, + destinationDirectoryName, + overwriteFiles: overwriteFiles)); +#endif + + /// + public void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName, + Encoding? entryNameEncoding) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.ExtractToDirectory( + sourceArchiveFileName, + destinationDirectoryName, + entryNameEncoding), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + sourceArchiveFileName, + destinationDirectoryName, + entryNameEncoding: entryNameEncoding)); + +#if FEATURE_COMPRESSION_OVERWRITE + /// + public void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName, + Encoding? entryNameEncoding, + bool overwriteFiles) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.ExtractToDirectory( + sourceArchiveFileName, + destinationDirectoryName, + entryNameEncoding, + overwriteFiles), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + sourceArchiveFileName, + destinationDirectoryName, + entryNameEncoding, + overwriteFiles)); +#endif + + /// + public IZipArchive Open(string archiveFileName, ZipArchiveMode mode) + => new ZipArchiveWrapper(FileSystem, + Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.Open(archiveFileName, mode), + () => ZipUtilities.Open(FileSystem, + archiveFileName, + mode))); + + /// + public IZipArchive Open(string archiveFileName, + ZipArchiveMode mode, + Encoding? entryNameEncoding) + => new ZipArchiveWrapper(FileSystem, + Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.Open(archiveFileName, mode, entryNameEncoding), + () => ZipUtilities.Open(FileSystem, + archiveFileName, + mode, + entryNameEncoding))); + + /// + public IZipArchive OpenRead(string archiveFileName) + => new ZipArchiveWrapper(FileSystem, + Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.OpenRead(archiveFileName), + () => ZipUtilities.Open(FileSystem, + archiveFileName, + ZipArchiveMode.Read))); + + #endregion +} diff --git a/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs b/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs index d28b6f7b1..c69b74cdb 100644 --- a/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs +++ b/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using Testably.Abstractions.FileSystem; + #if !NETSTANDARD2_0 using System; #endif diff --git a/Source/Testably.Abstractions.Testing/FileSystem/ChangeHandler.cs b/Source/Testably.Abstractions.Testing/FileSystem/ChangeHandler.cs index cea43baaf..1f28756aa 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/ChangeHandler.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/ChangeHandler.cs @@ -1,72 +1,71 @@ -using System; -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class ChangeHandler : IInterceptionHandler, - INotificationHandler -{ - private readonly Notification.INotificationFactory - _changeOccurredCallbacks = Notification.CreateFactory(); - - private readonly Notification.INotificationFactory - _changeOccurringCallbacks = Notification.CreateFactory(); - - private readonly MockFileSystem _mockFileSystem; - - public ChangeHandler(MockFileSystem mockFileSystem) - { - _mockFileSystem = mockFileSystem; - } - - #region IInterceptionHandler Members - - /// - public IFileSystem FileSystem => _mockFileSystem; - - /// - public MockFileSystem Event( - Action interceptionCallback, - Func? predicate = null) - { - _changeOccurringCallbacks.RegisterCallback(interceptionCallback, predicate); - return _mockFileSystem; - } - - #endregion - - #region INotificationHandler Members - - /// - public Notification.IAwaitableCallback OnEvent( - Action? notificationCallback = null, - Func? predicate = null) - => _changeOccurredCallbacks.RegisterCallback(notificationCallback, predicate); - - #endregion - - internal void NotifyCompletedChange(ChangeDescription? fileSystemChange) - { - if (fileSystemChange != null) - { - _changeOccurredCallbacks.InvokeCallbacks(fileSystemChange); - } - } - - internal ChangeDescription NotifyPendingChange(WatcherChangeTypes changeType, - FileSystemTypes fileSystemType, - NotifyFilters notifyFilters, - IStorageLocation location, - IStorageLocation? oldLocation = - null) - { - ChangeDescription fileSystemChange = - new(changeType, fileSystemType, notifyFilters, location, oldLocation); - _changeOccurringCallbacks.InvokeCallbacks(fileSystemChange); - return fileSystemChange; - } -} +using System; +using System.IO; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class ChangeHandler : IInterceptionHandler, + INotificationHandler +{ + private readonly Notification.INotificationFactory + _changeOccurredCallbacks = Notification.CreateFactory(); + + private readonly Notification.INotificationFactory + _changeOccurringCallbacks = Notification.CreateFactory(); + + private readonly MockFileSystem _mockFileSystem; + + public ChangeHandler(MockFileSystem mockFileSystem) + { + _mockFileSystem = mockFileSystem; + } + + #region IInterceptionHandler Members + + /// + public IFileSystem FileSystem => _mockFileSystem; + + /// + public MockFileSystem Event( + Action interceptionCallback, + Func? predicate = null) + { + _changeOccurringCallbacks.RegisterCallback(interceptionCallback, predicate); + return _mockFileSystem; + } + + #endregion + + #region INotificationHandler Members + + /// + public Notification.IAwaitableCallback OnEvent( + Action? notificationCallback = null, + Func? predicate = null) + => _changeOccurredCallbacks.RegisterCallback(notificationCallback, predicate); + + #endregion + + internal void NotifyCompletedChange(ChangeDescription? fileSystemChange) + { + if (fileSystemChange != null) + { + _changeOccurredCallbacks.InvokeCallbacks(fileSystemChange); + } + } + + internal ChangeDescription NotifyPendingChange(WatcherChangeTypes changeType, + FileSystemTypes fileSystemType, + NotifyFilters notifyFilters, + IStorageLocation location, + IStorageLocation? oldLocation = + null) + { + ChangeDescription fileSystemChange = + new(changeType, fileSystemType, notifyFilters, location, oldLocation); + _changeOccurringCallbacks.InvokeCallbacks(fileSystemChange); + return fileSystemChange; + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DefaultAccessControlStrategy.cs b/Source/Testably.Abstractions.Testing/FileSystem/DefaultAccessControlStrategy.cs index f63132e3c..6c0b81b85 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DefaultAccessControlStrategy.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DefaultAccessControlStrategy.cs @@ -1,28 +1,27 @@ -using System; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// Default implementation of an which uses a callback to determine if access -/// should be granted. -/// -public class DefaultAccessControlStrategy : IAccessControlStrategy -{ - private readonly Func _callback; - - /// - /// Initializes a new instance of which takes a callback to determine if - /// access should be granted. - /// - public DefaultAccessControlStrategy( - Func callback) - { - _callback = callback ?? throw new ArgumentNullException(nameof(callback)); - } - - /// - public bool IsAccessGranted(string fullPath, - IFileSystemExtensibility extensibility) - => _callback(fullPath, extensibility); -} +using System; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// Default implementation of an which uses a callback to determine if access +/// should be granted. +/// +public class DefaultAccessControlStrategy : IAccessControlStrategy +{ + private readonly Func _callback; + + /// + /// Initializes a new instance of which takes a callback to determine if + /// access should be granted. + /// + public DefaultAccessControlStrategy( + Func callback) + { + _callback = callback ?? throw new ArgumentNullException(nameof(callback)); + } + + /// + public bool IsAccessGranted(string fullPath, + IFileSystemExtensibility extensibility) + => _callback(fullPath, extensibility); +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoFactoryMock.cs index 01c2bec2f..322f3d5d9 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoFactoryMock.cs @@ -1,42 +1,41 @@ -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class DirectoryInfoFactoryMock : IDirectoryInfoFactory -{ - private readonly MockFileSystem _fileSystem; - - internal DirectoryInfoFactoryMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - #region IDirectoryInfoFactory Members - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public IDirectoryInfo New(string path) - { - return DirectoryInfoMock.New( - _fileSystem.Storage.GetLocation(path - .EnsureValidArgument(_fileSystem, nameof(path))), - _fileSystem); - } - - /// - [return: NotNullIfNotNull("directoryInfo")] - public IDirectoryInfo? Wrap(DirectoryInfo? directoryInfo) - => DirectoryInfoMock.New( - _fileSystem.Storage.GetLocation( - directoryInfo?.FullName, - directoryInfo?.ToString()), - _fileSystem); - - #endregion -} +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class DirectoryInfoFactoryMock : IDirectoryInfoFactory +{ + private readonly MockFileSystem _fileSystem; + + internal DirectoryInfoFactoryMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + #region IDirectoryInfoFactory Members + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public IDirectoryInfo New(string path) + { + return DirectoryInfoMock.New( + _fileSystem.Storage.GetLocation(path + .EnsureValidArgument(_fileSystem, nameof(path))), + _fileSystem); + } + + /// + [return: NotNullIfNotNull("directoryInfo")] + public IDirectoryInfo? Wrap(DirectoryInfo? directoryInfo) + => DirectoryInfoMock.New( + _fileSystem.Storage.GetLocation( + directoryInfo?.FullName, + directoryInfo?.ToString()), + _fileSystem); + + #endregion +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs index 03e064b56..ef56e09dc 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs @@ -1,281 +1,280 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// A mocked directory in the . -/// -internal sealed class DirectoryInfoMock - : FileSystemInfoMock, IDirectoryInfo -{ - private DirectoryInfoMock(IStorageLocation location, - MockFileSystem fileSystem) - : base(fileSystem, location, FileSystemTypes.Directory) - { - } - - #region IDirectoryInfo Members - - /// - public override bool Exists - => base.Exists && FileSystemType == FileSystemTypes.Directory; - - /// - public IDirectoryInfo? Parent - => New(Location.GetParent(), FileSystem); - - /// - public IDirectoryInfo Root - => New(FileSystem.Storage.GetLocation(string.Empty.PrefixRoot()), - FileSystem); - - /// - public void Create() - { - FullName.EnsureValidFormat(FileSystem); - - Container = FileSystem.Storage.GetOrCreateContainer(Location, - InMemoryContainer.NewDirectory, - Extensibility); - - ResetCache(!Execute.IsNetFramework); - } - - /// - public IDirectoryInfo CreateSubdirectory(string path) - { - DirectoryInfoMock directory = New( - FileSystem.Storage.GetLocation( - FileSystem.Path.Combine(FullName, path - .EnsureValidFormat(FileSystem, nameof(path), - Execute.IsWindows && !Execute.IsNetFramework))), - FileSystem); - directory.Create(); - return directory; - } - - /// - public override void Delete() - { - if (!FileSystem.Storage.DeleteContainer(Location)) - { - throw ExceptionFactory.DirectoryNotFound(Location.FullPath); - } - - ResetCache(!Execute.IsNetFramework); - } - - /// - public void Delete(bool recursive) - { - if (!FileSystem.Storage.DeleteContainer( - FileSystem.Storage.GetLocation(FullName), recursive)) - { - throw ExceptionFactory.DirectoryNotFound(FullName); - } - - ResetCache(!Execute.IsNetFramework); - } - - /// - public IEnumerable EnumerateDirectories() - => EnumerateDirectories( - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly); - - /// - public IEnumerable - EnumerateDirectories(string searchPattern) - => EnumerateDirectories(searchPattern, SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateDirectories( - string searchPattern, SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.Directory, - FullName, - searchPattern, - EnumerationOptionsHelper.FromSearchOption(searchOption)) - .Select(location => New(location, FileSystem)); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IEnumerable EnumerateDirectories( - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateInternal(FileSystemTypes.Directory, - FullName, - searchPattern, - enumerationOptions) - .Select(location => New(location, FileSystem)); -#endif - - /// - public IEnumerable EnumerateFiles() - => EnumerateFiles( - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateFiles(string searchPattern) - => EnumerateFiles(searchPattern, SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateFiles( - string searchPattern, SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.File, - FullName, - searchPattern, - EnumerationOptionsHelper.FromSearchOption(searchOption)) - .Select(location => FileInfoMock.New(location, FileSystem)); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IEnumerable EnumerateFiles( - string searchPattern, EnumerationOptions enumerationOptions) - => EnumerateInternal(FileSystemTypes.File, - FullName, - searchPattern, - enumerationOptions) - .Select(location => FileInfoMock.New(location, FileSystem)); -#endif - - /// - public IEnumerable EnumerateFileSystemInfos() - => EnumerateFileSystemInfos( - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly); - - /// - public IEnumerable - EnumerateFileSystemInfos(string searchPattern) - => EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateFileSystemInfos( - string searchPattern, SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.DirectoryOrFile, - FullName, - searchPattern, - EnumerationOptionsHelper.FromSearchOption(searchOption)) - .Select(location => FileSystemInfoMock.New(location, FileSystem)); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IEnumerable EnumerateFileSystemInfos( - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateInternal(FileSystemTypes.DirectoryOrFile, - FullName, - searchPattern, - enumerationOptions) - .Select(location => FileSystemInfoMock.New(location, FileSystem)); -#endif - - /// - public IDirectoryInfo[] GetDirectories() - => EnumerateDirectories().ToArray(); - - /// - public IDirectoryInfo[] GetDirectories(string searchPattern) - => EnumerateDirectories(searchPattern).ToArray(); - - /// - public IDirectoryInfo[] GetDirectories( - string searchPattern, SearchOption searchOption) - => EnumerateDirectories(searchPattern, searchOption).ToArray(); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IDirectoryInfo[] GetDirectories( - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateDirectories(searchPattern, enumerationOptions).ToArray(); -#endif - - /// - public IFileInfo[] GetFiles() - => EnumerateFiles().ToArray(); - - /// - public IFileInfo[] GetFiles(string searchPattern) - => EnumerateFiles(searchPattern).ToArray(); - - /// - public IFileInfo[] GetFiles(string searchPattern, - SearchOption searchOption) - => EnumerateFiles(searchPattern, searchOption).ToArray(); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IFileInfo[] GetFiles(string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateFiles(searchPattern, enumerationOptions).ToArray(); -#endif - - /// - public IFileSystemInfo[] GetFileSystemInfos() - => EnumerateFileSystemInfos().ToArray(); - - /// - public IFileSystemInfo[] GetFileSystemInfos(string searchPattern) - => EnumerateFileSystemInfos(searchPattern).ToArray(); - - /// - public IFileSystemInfo[] GetFileSystemInfos( - string searchPattern, SearchOption searchOption) - => EnumerateFileSystemInfos(searchPattern, searchOption).ToArray(); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IFileSystemInfo[] GetFileSystemInfos( - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateFileSystemInfos(searchPattern, enumerationOptions).ToArray(); -#endif - - /// - public void MoveTo(string destDirName) - => Location = FileSystem.Storage.Move( - FileSystem.Storage.GetLocation(FullName), - FileSystem.Storage.GetLocation(destDirName - .EnsureValidFormat(FileSystem, nameof(destDirName))), - recursive: true) - ?? throw ExceptionFactory.DirectoryNotFound(FullName); - - #endregion - - [return: NotNullIfNotNull("location")] - internal static new DirectoryInfoMock? New(IStorageLocation? location, - MockFileSystem fileSystem) - { - if (location == null) - { - return null; - } - - return new DirectoryInfoMock(location, fileSystem); - } - - private IEnumerable EnumerateInternal( - FileSystemTypes fileSystemTypes, - string path, - string searchPattern, - EnumerationOptions enumerationOptions) - { - StorageExtensions.AdjustedLocation adjustedLocation = FileSystem.Storage - .AdjustLocationFromSearchPattern( - path.EnsureValidFormat(FileSystem), - searchPattern); - return FileSystem.Storage.EnumerateLocations( - adjustedLocation.Location, - fileSystemTypes, - adjustedLocation.SearchPattern, - enumerationOptions); - } -} +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// A mocked directory in the . +/// +internal sealed class DirectoryInfoMock + : FileSystemInfoMock, IDirectoryInfo +{ + private DirectoryInfoMock(IStorageLocation location, + MockFileSystem fileSystem) + : base(fileSystem, location, FileSystemTypes.Directory) + { + } + + #region IDirectoryInfo Members + + /// + public override bool Exists + => base.Exists && FileSystemType == FileSystemTypes.Directory; + + /// + public IDirectoryInfo? Parent + => New(Location.GetParent(), FileSystem); + + /// + public IDirectoryInfo Root + => New(FileSystem.Storage.GetLocation(string.Empty.PrefixRoot()), + FileSystem); + + /// + public void Create() + { + FullName.EnsureValidFormat(FileSystem); + + Container = FileSystem.Storage.GetOrCreateContainer(Location, + InMemoryContainer.NewDirectory, + Extensibility); + + ResetCache(!Execute.IsNetFramework); + } + + /// + public IDirectoryInfo CreateSubdirectory(string path) + { + DirectoryInfoMock directory = New( + FileSystem.Storage.GetLocation( + FileSystem.Path.Combine(FullName, path + .EnsureValidFormat(FileSystem, nameof(path), + Execute.IsWindows && !Execute.IsNetFramework))), + FileSystem); + directory.Create(); + return directory; + } + + /// + public override void Delete() + { + if (!FileSystem.Storage.DeleteContainer(Location)) + { + throw ExceptionFactory.DirectoryNotFound(Location.FullPath); + } + + ResetCache(!Execute.IsNetFramework); + } + + /// + public void Delete(bool recursive) + { + if (!FileSystem.Storage.DeleteContainer( + FileSystem.Storage.GetLocation(FullName), recursive)) + { + throw ExceptionFactory.DirectoryNotFound(FullName); + } + + ResetCache(!Execute.IsNetFramework); + } + + /// + public IEnumerable EnumerateDirectories() + => EnumerateDirectories( + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly); + + /// + public IEnumerable + EnumerateDirectories(string searchPattern) + => EnumerateDirectories(searchPattern, SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateDirectories( + string searchPattern, SearchOption searchOption) + => EnumerateInternal(FileSystemTypes.Directory, + FullName, + searchPattern, + EnumerationOptionsHelper.FromSearchOption(searchOption)) + .Select(location => New(location, FileSystem)); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IEnumerable EnumerateDirectories( + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateInternal(FileSystemTypes.Directory, + FullName, + searchPattern, + enumerationOptions) + .Select(location => New(location, FileSystem)); +#endif + + /// + public IEnumerable EnumerateFiles() + => EnumerateFiles( + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateFiles(string searchPattern) + => EnumerateFiles(searchPattern, SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateFiles( + string searchPattern, SearchOption searchOption) + => EnumerateInternal(FileSystemTypes.File, + FullName, + searchPattern, + EnumerationOptionsHelper.FromSearchOption(searchOption)) + .Select(location => FileInfoMock.New(location, FileSystem)); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IEnumerable EnumerateFiles( + string searchPattern, EnumerationOptions enumerationOptions) + => EnumerateInternal(FileSystemTypes.File, + FullName, + searchPattern, + enumerationOptions) + .Select(location => FileInfoMock.New(location, FileSystem)); +#endif + + /// + public IEnumerable EnumerateFileSystemInfos() + => EnumerateFileSystemInfos( + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly); + + /// + public IEnumerable + EnumerateFileSystemInfos(string searchPattern) + => EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateFileSystemInfos( + string searchPattern, SearchOption searchOption) + => EnumerateInternal(FileSystemTypes.DirectoryOrFile, + FullName, + searchPattern, + EnumerationOptionsHelper.FromSearchOption(searchOption)) + .Select(location => FileSystemInfoMock.New(location, FileSystem)); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IEnumerable EnumerateFileSystemInfos( + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateInternal(FileSystemTypes.DirectoryOrFile, + FullName, + searchPattern, + enumerationOptions) + .Select(location => FileSystemInfoMock.New(location, FileSystem)); +#endif + + /// + public IDirectoryInfo[] GetDirectories() + => EnumerateDirectories().ToArray(); + + /// + public IDirectoryInfo[] GetDirectories(string searchPattern) + => EnumerateDirectories(searchPattern).ToArray(); + + /// + public IDirectoryInfo[] GetDirectories( + string searchPattern, SearchOption searchOption) + => EnumerateDirectories(searchPattern, searchOption).ToArray(); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IDirectoryInfo[] GetDirectories( + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateDirectories(searchPattern, enumerationOptions).ToArray(); +#endif + + /// + public IFileInfo[] GetFiles() + => EnumerateFiles().ToArray(); + + /// + public IFileInfo[] GetFiles(string searchPattern) + => EnumerateFiles(searchPattern).ToArray(); + + /// + public IFileInfo[] GetFiles(string searchPattern, + SearchOption searchOption) + => EnumerateFiles(searchPattern, searchOption).ToArray(); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IFileInfo[] GetFiles(string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateFiles(searchPattern, enumerationOptions).ToArray(); +#endif + + /// + public IFileSystemInfo[] GetFileSystemInfos() + => EnumerateFileSystemInfos().ToArray(); + + /// + public IFileSystemInfo[] GetFileSystemInfos(string searchPattern) + => EnumerateFileSystemInfos(searchPattern).ToArray(); + + /// + public IFileSystemInfo[] GetFileSystemInfos( + string searchPattern, SearchOption searchOption) + => EnumerateFileSystemInfos(searchPattern, searchOption).ToArray(); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IFileSystemInfo[] GetFileSystemInfos( + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateFileSystemInfos(searchPattern, enumerationOptions).ToArray(); +#endif + + /// + public void MoveTo(string destDirName) + => Location = FileSystem.Storage.Move( + FileSystem.Storage.GetLocation(FullName), + FileSystem.Storage.GetLocation(destDirName + .EnsureValidFormat(FileSystem, nameof(destDirName))), + recursive: true) + ?? throw ExceptionFactory.DirectoryNotFound(FullName); + + #endregion + + [return: NotNullIfNotNull("location")] + internal static new DirectoryInfoMock? New(IStorageLocation? location, + MockFileSystem fileSystem) + { + if (location == null) + { + return null; + } + + return new DirectoryInfoMock(location, fileSystem); + } + + private IEnumerable EnumerateInternal( + FileSystemTypes fileSystemTypes, + string path, + string searchPattern, + EnumerationOptions enumerationOptions) + { + StorageExtensions.AdjustedLocation adjustedLocation = FileSystem.Storage + .AdjustLocationFromSearchPattern( + path.EnsureValidFormat(FileSystem), + searchPattern); + return FileSystem.Storage.EnumerateLocations( + adjustedLocation.Location, + fileSystemTypes, + adjustedLocation.SearchPattern, + enumerationOptions); + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs index 31093366d..2f9d3358d 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs @@ -1,472 +1,471 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class DirectoryMock : IDirectory -{ - private readonly MockFileSystem _fileSystem; - - internal DirectoryMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - #region IDirectory Members - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public IDirectoryInfo CreateDirectory(string path) - { - path.EnsureValidFormat(_fileSystem); - - DirectoryInfoMock directory = DirectoryInfoMock.New( - _fileSystem.Storage.GetLocation(path), - _fileSystem); - directory.Create(); - - return directory; - } - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - public IDirectoryInfo CreateDirectory(string path, UnixFileMode unixCreateMode) - { - IDirectoryInfo directoryInfo = CreateDirectory(path); - #pragma warning disable CA1416 - directoryInfo.UnixFileMode = unixCreateMode; - #pragma warning restore CA1416 - return directoryInfo; - } -#endif - -#if FEATURE_FILESYSTEM_LINK - /// - public IFileSystemInfo CreateSymbolicLink( - string path, string pathToTarget) - { - path.EnsureValidFormat(_fileSystem); - IDirectoryInfo fileSystemInfo = - _fileSystem.DirectoryInfo.New(path); - fileSystemInfo.CreateAsSymbolicLink(pathToTarget); - return fileSystemInfo; - } -#endif - -#if FEATURE_FILESYSTEM_NET7 - /// - public IDirectoryInfo CreateTempSubdirectory(string? prefix = null) - { - string basePath; - - do - { - string localBasePath = _fileSystem.Path.Combine( - _fileSystem.Path.GetTempPath(), - (prefix ?? "") + _fileSystem.Path.GetFileNameWithoutExtension( - _fileSystem.Path.GetRandomFileName())); - Execute.OnMac(() => localBasePath = "/private" + localBasePath); - basePath = localBasePath; - } while (_fileSystem.Directory.Exists(basePath)); - - _fileSystem.Directory.CreateDirectory(basePath); - return _fileSystem.DirectoryInfo.New(basePath); - } -#endif - - /// - public void Delete(string path) - => _fileSystem.DirectoryInfo - .New(path.EnsureValidFormat(FileSystem)) - .Delete(); - - /// - public void Delete(string path, bool recursive) - => _fileSystem.DirectoryInfo - .New(path.EnsureValidFormat(FileSystem)) - .Delete(recursive); - - /// - public IEnumerable EnumerateDirectories(string path) - => EnumerateDirectories(path, - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateDirectories(string path, string searchPattern) - => EnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateDirectories(string path, - string searchPattern, - SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.Directory, - path, - searchPattern, - EnumerationOptionsHelper.FromSearchOption(searchOption)); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IEnumerable EnumerateDirectories(string path, - string searchPattern, - EnumerationOptions - enumerationOptions) - => EnumerateInternal(FileSystemTypes.Directory, - path, - searchPattern, - enumerationOptions); -#endif - - /// - public IEnumerable EnumerateFiles(string path) - => EnumerateFiles(path, - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateFiles(string path, string searchPattern) - => EnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateFiles(string path, - string searchPattern, - SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.File, - path, - searchPattern, - EnumerationOptionsHelper.FromSearchOption(searchOption)); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IEnumerable EnumerateFiles(string path, - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateInternal(FileSystemTypes.File, - path, - searchPattern, - enumerationOptions); -#endif - - /// - public IEnumerable EnumerateFileSystemEntries(string path) - => EnumerateFileSystemEntries(path, - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateFileSystemEntries( - string path, string searchPattern) - => EnumerateFileSystemEntries(path, - searchPattern, - SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateFileSystemEntries(string path, - string searchPattern, - SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.DirectoryOrFile, - path, - searchPattern, - EnumerationOptionsHelper.FromSearchOption(searchOption)); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IEnumerable EnumerateFileSystemEntries(string path, - string searchPattern, - EnumerationOptions - enumerationOptions) - => EnumerateInternal(FileSystemTypes.DirectoryOrFile, - path, - searchPattern, - enumerationOptions); -#endif - - /// - public bool Exists([NotNullWhen(true)] string? path) - => DirectoryInfoMock.New( - _fileSystem.Storage.GetLocation(path), - _fileSystem)?.Exists ?? false; - - /// - public DateTime GetCreationTime(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .CreationTime.Get(DateTimeKind.Local); - - /// - public DateTime GetCreationTimeUtc(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .CreationTime.Get(DateTimeKind.Utc); - - /// - public string GetCurrentDirectory() - => _fileSystem.Storage.CurrentDirectory; - - /// - public string[] GetDirectories(string path) - => EnumerateDirectories(path).ToArray(); - - /// - public string[] GetDirectories(string path, string searchPattern) - => EnumerateDirectories(path, searchPattern).ToArray(); - - /// - public string[] GetDirectories(string path, - string searchPattern, - SearchOption searchOption) - => EnumerateDirectories(path, searchPattern, searchOption).ToArray(); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public string[] GetDirectories(string path, - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateDirectories(path, searchPattern, enumerationOptions).ToArray(); -#endif - - /// - public string GetDirectoryRoot(string path) - => _fileSystem.Path.GetPathRoot( - _fileSystem.Path.GetFullPath(path)) ?? - throw ExceptionFactory.PathIsEmpty(nameof(path)); - - /// - public string[] GetFiles(string path) - => EnumerateFiles(path).ToArray(); - - /// - public string[] GetFiles(string path, string searchPattern) - => EnumerateFiles(path, searchPattern).ToArray(); - - /// - public string[] GetFiles(string path, - string searchPattern, - SearchOption searchOption) - => EnumerateFiles(path, searchPattern, searchOption).ToArray(); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public string[] GetFiles(string path, - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateFiles(path, searchPattern, enumerationOptions).ToArray(); -#endif - - /// - public string[] GetFileSystemEntries(string path) - => EnumerateFileSystemEntries(path).ToArray(); - - /// - public string[] GetFileSystemEntries(string path, string searchPattern) - => EnumerateFileSystemEntries(path, searchPattern).ToArray(); - - /// - public string[] GetFileSystemEntries(string path, - string searchPattern, - SearchOption searchOption) - => EnumerateFileSystemEntries(path, searchPattern, searchOption).ToArray(); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public string[] GetFileSystemEntries(string path, - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateFileSystemEntries(path, searchPattern, enumerationOptions) - .ToArray(); -#endif - - /// - public DateTime GetLastAccessTime(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastAccessTime.Get(DateTimeKind.Local); - - /// - public DateTime GetLastAccessTimeUtc(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastAccessTime.Get(DateTimeKind.Utc); - - /// - public DateTime GetLastWriteTime(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastWriteTime.Get(DateTimeKind.Local); - - /// - public DateTime GetLastWriteTimeUtc(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastWriteTime.Get(DateTimeKind.Utc); - - /// - public string[] GetLogicalDrives() - => _fileSystem.DriveInfo.GetDrives().Select(x => x.Name).ToArray(); - - /// - public IDirectoryInfo? GetParent(string path) - => _fileSystem.DirectoryInfo - .New(path.EnsureValidArgument(_fileSystem, nameof(path))) - .Parent; - - /// - public void Move(string sourceDirName, string destDirName) - => _fileSystem.DirectoryInfo.New(sourceDirName - .EnsureValidFormat(_fileSystem, nameof(sourceDirName))) - .MoveTo(destDirName - .EnsureValidFormat(_fileSystem, nameof(destDirName))); - -#if FEATURE_FILESYSTEM_LINK - /// - public IFileSystemInfo? ResolveLinkTarget( - string linkPath, bool returnFinalTarget) - { - try - { - return _fileSystem.DirectoryInfo.New(linkPath - .EnsureValidFormat(_fileSystem, nameof(linkPath))) - .ResolveLinkTarget(returnFinalTarget); - } - catch (IOException ex) - when (ex.HResult != -2147024773) - { - throw ExceptionFactory.FileNameCannotBeResolved(linkPath); - } - } -#endif - - /// - public void SetCreationTime(string path, DateTime creationTime) - => LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileCreatedTimeException) - .CreationTime = creationTime; - - /// - public void SetCreationTimeUtc(string path, DateTime creationTimeUtc) - => LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileCreatedTimeException) - .CreationTimeUtc = creationTimeUtc; - - /// - public void SetCurrentDirectory(string path) - { - IDirectoryInfo directoryInfo = - _fileSystem.DirectoryInfo.New(path); - if (!directoryInfo.Exists) - { - throw ExceptionFactory.DirectoryNotFound( - FileSystem.Path.GetFullPath(path)); - } - - _fileSystem.Storage.CurrentDirectory = directoryInfo.FullName; - } - - /// - public void SetLastAccessTime(string path, DateTime lastAccessTime) - => LoadDirectoryInfoOrThrowNotFoundException(path, - ThrowMissingFileLastAccessOrLastWriteTimeException) - .LastAccessTime = lastAccessTime; - - /// - public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) - => LoadDirectoryInfoOrThrowNotFoundException(path, - ThrowMissingFileLastAccessOrLastWriteTimeException) - .LastAccessTimeUtc = lastAccessTimeUtc; - - /// - public void SetLastWriteTime(string path, DateTime lastWriteTime) - => LoadDirectoryInfoOrThrowNotFoundException(path, - ThrowMissingFileLastAccessOrLastWriteTimeException) - .LastWriteTime = lastWriteTime; - - /// - public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) - => LoadDirectoryInfoOrThrowNotFoundException(path, - ThrowMissingFileLastAccessOrLastWriteTimeException) - .LastWriteTimeUtc = lastWriteTimeUtc; - - #endregion - - private IDirectoryInfo LoadDirectoryInfoOrThrowNotFoundException( - string path, Action onMissingCallback) - { - IDirectoryInfo directoryInfo = - _fileSystem.DirectoryInfo.New(path.EnsureValidFormat(FileSystem)); - if (!directoryInfo.Exists) - { - onMissingCallback.Invoke(FileSystem, path); - } - - return directoryInfo; - } - - private static void ThrowMissingFileCreatedTimeException(IFileSystem fileSystem, string path) - { -#if NET7_0_OR_GREATER - Execute.OnMac( - () => - throw ExceptionFactory.DirectoryNotFound( - fileSystem.Path.GetFullPath(path)), - () => - throw ExceptionFactory.FileNotFound( - fileSystem.Path.GetFullPath(path))); -#else - Execute.OnWindows( - () => - throw ExceptionFactory.FileNotFound( - fileSystem.Path.GetFullPath(path)), - () => - throw ExceptionFactory.DirectoryNotFound( - fileSystem.Path.GetFullPath(path))); -#endif - } - - private static void ThrowMissingFileLastAccessOrLastWriteTimeException(IFileSystem fileSystem, - string path) - { -#if NET7_0_OR_GREATER - throw ExceptionFactory.FileNotFound( - fileSystem.Path.GetFullPath(path)); -#else - Execute.OnWindows( - () => - throw ExceptionFactory.FileNotFound( - fileSystem.Path.GetFullPath(path)), - () => - throw ExceptionFactory.DirectoryNotFound( - fileSystem.Path.GetFullPath(path))); -#endif - } - - private IEnumerable EnumerateInternal(FileSystemTypes fileSystemTypes, - string path, - string searchPattern, - EnumerationOptions enumerationOptions) - { - StorageExtensions.AdjustedLocation adjustedLocation = _fileSystem.Storage - .AdjustLocationFromSearchPattern( - path.EnsureValidFormat(FileSystem), - searchPattern); - return _fileSystem.Storage.EnumerateLocations( - adjustedLocation.Location, - fileSystemTypes, - adjustedLocation.SearchPattern, - enumerationOptions) - .Select(x => _fileSystem - .GetSubdirectoryPath(x.FullPath, adjustedLocation.GivenPath)); - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class DirectoryMock : IDirectory +{ + private readonly MockFileSystem _fileSystem; + + internal DirectoryMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + #region IDirectory Members + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public IDirectoryInfo CreateDirectory(string path) + { + path.EnsureValidFormat(_fileSystem); + + DirectoryInfoMock directory = DirectoryInfoMock.New( + _fileSystem.Storage.GetLocation(path), + _fileSystem); + directory.Create(); + + return directory; + } + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + public IDirectoryInfo CreateDirectory(string path, UnixFileMode unixCreateMode) + { + IDirectoryInfo directoryInfo = CreateDirectory(path); + #pragma warning disable CA1416 + directoryInfo.UnixFileMode = unixCreateMode; + #pragma warning restore CA1416 + return directoryInfo; + } +#endif + +#if FEATURE_FILESYSTEM_LINK + /// + public IFileSystemInfo CreateSymbolicLink( + string path, string pathToTarget) + { + path.EnsureValidFormat(_fileSystem); + IDirectoryInfo fileSystemInfo = + _fileSystem.DirectoryInfo.New(path); + fileSystemInfo.CreateAsSymbolicLink(pathToTarget); + return fileSystemInfo; + } +#endif + +#if FEATURE_FILESYSTEM_NET7 + /// + public IDirectoryInfo CreateTempSubdirectory(string? prefix = null) + { + string basePath; + + do + { + string localBasePath = _fileSystem.Path.Combine( + _fileSystem.Path.GetTempPath(), + (prefix ?? "") + _fileSystem.Path.GetFileNameWithoutExtension( + _fileSystem.Path.GetRandomFileName())); + Execute.OnMac(() => localBasePath = "/private" + localBasePath); + basePath = localBasePath; + } while (_fileSystem.Directory.Exists(basePath)); + + _fileSystem.Directory.CreateDirectory(basePath); + return _fileSystem.DirectoryInfo.New(basePath); + } +#endif + + /// + public void Delete(string path) + => _fileSystem.DirectoryInfo + .New(path.EnsureValidFormat(FileSystem)) + .Delete(); + + /// + public void Delete(string path, bool recursive) + => _fileSystem.DirectoryInfo + .New(path.EnsureValidFormat(FileSystem)) + .Delete(recursive); + + /// + public IEnumerable EnumerateDirectories(string path) + => EnumerateDirectories(path, + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateDirectories(string path, string searchPattern) + => EnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateDirectories(string path, + string searchPattern, + SearchOption searchOption) + => EnumerateInternal(FileSystemTypes.Directory, + path, + searchPattern, + EnumerationOptionsHelper.FromSearchOption(searchOption)); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IEnumerable EnumerateDirectories(string path, + string searchPattern, + EnumerationOptions + enumerationOptions) + => EnumerateInternal(FileSystemTypes.Directory, + path, + searchPattern, + enumerationOptions); +#endif + + /// + public IEnumerable EnumerateFiles(string path) + => EnumerateFiles(path, + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateFiles(string path, string searchPattern) + => EnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateFiles(string path, + string searchPattern, + SearchOption searchOption) + => EnumerateInternal(FileSystemTypes.File, + path, + searchPattern, + EnumerationOptionsHelper.FromSearchOption(searchOption)); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IEnumerable EnumerateFiles(string path, + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateInternal(FileSystemTypes.File, + path, + searchPattern, + enumerationOptions); +#endif + + /// + public IEnumerable EnumerateFileSystemEntries(string path) + => EnumerateFileSystemEntries(path, + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateFileSystemEntries( + string path, string searchPattern) + => EnumerateFileSystemEntries(path, + searchPattern, + SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateFileSystemEntries(string path, + string searchPattern, + SearchOption searchOption) + => EnumerateInternal(FileSystemTypes.DirectoryOrFile, + path, + searchPattern, + EnumerationOptionsHelper.FromSearchOption(searchOption)); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IEnumerable EnumerateFileSystemEntries(string path, + string searchPattern, + EnumerationOptions + enumerationOptions) + => EnumerateInternal(FileSystemTypes.DirectoryOrFile, + path, + searchPattern, + enumerationOptions); +#endif + + /// + public bool Exists([NotNullWhen(true)] string? path) + => DirectoryInfoMock.New( + _fileSystem.Storage.GetLocation(path), + _fileSystem)?.Exists ?? false; + + /// + public DateTime GetCreationTime(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .CreationTime.Get(DateTimeKind.Local); + + /// + public DateTime GetCreationTimeUtc(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .CreationTime.Get(DateTimeKind.Utc); + + /// + public string GetCurrentDirectory() + => _fileSystem.Storage.CurrentDirectory; + + /// + public string[] GetDirectories(string path) + => EnumerateDirectories(path).ToArray(); + + /// + public string[] GetDirectories(string path, string searchPattern) + => EnumerateDirectories(path, searchPattern).ToArray(); + + /// + public string[] GetDirectories(string path, + string searchPattern, + SearchOption searchOption) + => EnumerateDirectories(path, searchPattern, searchOption).ToArray(); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public string[] GetDirectories(string path, + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateDirectories(path, searchPattern, enumerationOptions).ToArray(); +#endif + + /// + public string GetDirectoryRoot(string path) + => _fileSystem.Path.GetPathRoot( + _fileSystem.Path.GetFullPath(path)) ?? + throw ExceptionFactory.PathIsEmpty(nameof(path)); + + /// + public string[] GetFiles(string path) + => EnumerateFiles(path).ToArray(); + + /// + public string[] GetFiles(string path, string searchPattern) + => EnumerateFiles(path, searchPattern).ToArray(); + + /// + public string[] GetFiles(string path, + string searchPattern, + SearchOption searchOption) + => EnumerateFiles(path, searchPattern, searchOption).ToArray(); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public string[] GetFiles(string path, + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateFiles(path, searchPattern, enumerationOptions).ToArray(); +#endif + + /// + public string[] GetFileSystemEntries(string path) + => EnumerateFileSystemEntries(path).ToArray(); + + /// + public string[] GetFileSystemEntries(string path, string searchPattern) + => EnumerateFileSystemEntries(path, searchPattern).ToArray(); + + /// + public string[] GetFileSystemEntries(string path, + string searchPattern, + SearchOption searchOption) + => EnumerateFileSystemEntries(path, searchPattern, searchOption).ToArray(); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public string[] GetFileSystemEntries(string path, + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateFileSystemEntries(path, searchPattern, enumerationOptions) + .ToArray(); +#endif + + /// + public DateTime GetLastAccessTime(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastAccessTime.Get(DateTimeKind.Local); + + /// + public DateTime GetLastAccessTimeUtc(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastAccessTime.Get(DateTimeKind.Utc); + + /// + public DateTime GetLastWriteTime(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastWriteTime.Get(DateTimeKind.Local); + + /// + public DateTime GetLastWriteTimeUtc(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastWriteTime.Get(DateTimeKind.Utc); + + /// + public string[] GetLogicalDrives() + => _fileSystem.DriveInfo.GetDrives().Select(x => x.Name).ToArray(); + + /// + public IDirectoryInfo? GetParent(string path) + => _fileSystem.DirectoryInfo + .New(path.EnsureValidArgument(_fileSystem, nameof(path))) + .Parent; + + /// + public void Move(string sourceDirName, string destDirName) + => _fileSystem.DirectoryInfo.New(sourceDirName + .EnsureValidFormat(_fileSystem, nameof(sourceDirName))) + .MoveTo(destDirName + .EnsureValidFormat(_fileSystem, nameof(destDirName))); + +#if FEATURE_FILESYSTEM_LINK + /// + public IFileSystemInfo? ResolveLinkTarget( + string linkPath, bool returnFinalTarget) + { + try + { + return _fileSystem.DirectoryInfo.New(linkPath + .EnsureValidFormat(_fileSystem, nameof(linkPath))) + .ResolveLinkTarget(returnFinalTarget); + } + catch (IOException ex) + when (ex.HResult != -2147024773) + { + throw ExceptionFactory.FileNameCannotBeResolved(linkPath); + } + } +#endif + + /// + public void SetCreationTime(string path, DateTime creationTime) + => LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileCreatedTimeException) + .CreationTime = creationTime; + + /// + public void SetCreationTimeUtc(string path, DateTime creationTimeUtc) + => LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileCreatedTimeException) + .CreationTimeUtc = creationTimeUtc; + + /// + public void SetCurrentDirectory(string path) + { + IDirectoryInfo directoryInfo = + _fileSystem.DirectoryInfo.New(path); + if (!directoryInfo.Exists) + { + throw ExceptionFactory.DirectoryNotFound( + FileSystem.Path.GetFullPath(path)); + } + + _fileSystem.Storage.CurrentDirectory = directoryInfo.FullName; + } + + /// + public void SetLastAccessTime(string path, DateTime lastAccessTime) + => LoadDirectoryInfoOrThrowNotFoundException(path, + ThrowMissingFileLastAccessOrLastWriteTimeException) + .LastAccessTime = lastAccessTime; + + /// + public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) + => LoadDirectoryInfoOrThrowNotFoundException(path, + ThrowMissingFileLastAccessOrLastWriteTimeException) + .LastAccessTimeUtc = lastAccessTimeUtc; + + /// + public void SetLastWriteTime(string path, DateTime lastWriteTime) + => LoadDirectoryInfoOrThrowNotFoundException(path, + ThrowMissingFileLastAccessOrLastWriteTimeException) + .LastWriteTime = lastWriteTime; + + /// + public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) + => LoadDirectoryInfoOrThrowNotFoundException(path, + ThrowMissingFileLastAccessOrLastWriteTimeException) + .LastWriteTimeUtc = lastWriteTimeUtc; + + #endregion + + private IDirectoryInfo LoadDirectoryInfoOrThrowNotFoundException( + string path, Action onMissingCallback) + { + IDirectoryInfo directoryInfo = + _fileSystem.DirectoryInfo.New(path.EnsureValidFormat(FileSystem)); + if (!directoryInfo.Exists) + { + onMissingCallback.Invoke(FileSystem, path); + } + + return directoryInfo; + } + + private static void ThrowMissingFileCreatedTimeException(IFileSystem fileSystem, string path) + { +#if NET7_0_OR_GREATER + Execute.OnMac( + () => + throw ExceptionFactory.DirectoryNotFound( + fileSystem.Path.GetFullPath(path)), + () => + throw ExceptionFactory.FileNotFound( + fileSystem.Path.GetFullPath(path))); +#else + Execute.OnWindows( + () => + throw ExceptionFactory.FileNotFound( + fileSystem.Path.GetFullPath(path)), + () => + throw ExceptionFactory.DirectoryNotFound( + fileSystem.Path.GetFullPath(path))); +#endif + } + + private static void ThrowMissingFileLastAccessOrLastWriteTimeException(IFileSystem fileSystem, + string path) + { +#if NET7_0_OR_GREATER + throw ExceptionFactory.FileNotFound( + fileSystem.Path.GetFullPath(path)); +#else + Execute.OnWindows( + () => + throw ExceptionFactory.FileNotFound( + fileSystem.Path.GetFullPath(path)), + () => + throw ExceptionFactory.DirectoryNotFound( + fileSystem.Path.GetFullPath(path))); +#endif + } + + private IEnumerable EnumerateInternal(FileSystemTypes fileSystemTypes, + string path, + string searchPattern, + EnumerationOptions enumerationOptions) + { + StorageExtensions.AdjustedLocation adjustedLocation = _fileSystem.Storage + .AdjustLocationFromSearchPattern( + path.EnsureValidFormat(FileSystem), + searchPattern); + return _fileSystem.Storage.EnumerateLocations( + adjustedLocation.Location, + fileSystemTypes, + adjustedLocation.SearchPattern, + enumerationOptions) + .Select(x => _fileSystem + .GetSubdirectoryPath(x.FullPath, adjustedLocation.GivenPath)); + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs index 5385377dc..be7a64130 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs @@ -1,58 +1,57 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class DriveInfoFactoryMock : IDriveInfoFactory -{ - private readonly MockFileSystem _fileSystem; - - internal DriveInfoFactoryMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - #region IDriveInfoFactory Members - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public IDriveInfo[] GetDrives() - => _fileSystem.Storage.GetDrives() - .Where(x => !x.IsUncPath) - .Cast() - .ToArray(); - - /// - public IDriveInfo New(string driveName) - { - if (driveName == null) - { - throw new ArgumentNullException(nameof(driveName)); - } - - DriveInfoMock driveInfo = DriveInfoMock.New(driveName, _fileSystem); - IStorageDrive? existingDrive = _fileSystem.Storage.GetDrive(driveInfo.Name); - return existingDrive ?? driveInfo; - } - - /// - [return: NotNullIfNotNull("driveInfo")] - public IDriveInfo? Wrap(DriveInfo? driveInfo) - { - if (driveInfo?.Name == null) - { - return null; - } - - return New(driveInfo.Name); - } - - #endregion -} +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class DriveInfoFactoryMock : IDriveInfoFactory +{ + private readonly MockFileSystem _fileSystem; + + internal DriveInfoFactoryMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + #region IDriveInfoFactory Members + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public IDriveInfo[] GetDrives() + => _fileSystem.Storage.GetDrives() + .Where(x => !x.IsUncPath) + .Cast() + .ToArray(); + + /// + public IDriveInfo New(string driveName) + { + if (driveName == null) + { + throw new ArgumentNullException(nameof(driveName)); + } + + DriveInfoMock driveInfo = DriveInfoMock.New(driveName, _fileSystem); + IStorageDrive? existingDrive = _fileSystem.Storage.GetDrive(driveInfo.Name); + return existingDrive ?? driveInfo; + } + + /// + [return: NotNullIfNotNull("driveInfo")] + public IDriveInfo? Wrap(DriveInfo? driveInfo) + { + if (driveInfo?.Name == null) + { + return null; + } + + return New(driveInfo.Name); + } + + #endregion +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoMock.cs index bb7ecd6d4..3058abb20 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoMock.cs @@ -1,207 +1,206 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// Mocked instance of a -/// -internal sealed class DriveInfoMock : IStorageDrive -{ - /// - /// The default . - /// - public const string DefaultDriveFormat = "NTFS"; - - /// - /// The default . - /// - public const DriveType DefaultDriveType = DriveType.Fixed; - - /// - /// The default total size of a mocked . - /// - /// The number is equal to 1GB (1 Gigabyte). - /// - public const long DefaultTotalSize = 1024 * 1024 * 1024; - - private readonly MockFileSystem _fileSystem; - - private long _usedBytes; - private string _volumeLabel = nameof(MockFileSystem); - - private DriveInfoMock(string driveName, MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - - if (driveName.IsUncPath()) - { - IsUncPath = true; - driveName = PathHelper.UncPrefix + - GetTopmostParentDirectory(driveName.Substring(2)); - } - else - { - driveName = ValidateDriveLetter(driveName, fileSystem); - } - - Name = driveName; - TotalSize = DefaultTotalSize; - DriveFormat = DefaultDriveFormat; - DriveType = DefaultDriveType; - IsReady = true; - } - - #region IStorageDrive Members - - /// - public long AvailableFreeSpace - => TotalFreeSpace; - - /// - public string DriveFormat { get; private set; } - - /// - public DriveType DriveType { get; private set; } - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public bool IsReady { get; private set; } - - /// - /// Flag indicating if the drive is a UNC drive - /// - public bool IsUncPath { get; } - - /// - public string Name { get; } - - /// - public IDirectoryInfo RootDirectory - => DirectoryInfoMock.New(_fileSystem.Storage.GetLocation(Name), _fileSystem); - - /// - public long TotalFreeSpace - => TotalSize - _usedBytes; - - /// - public long TotalSize { get; private set; } - - /// - [AllowNull] - public string VolumeLabel - { - get => _volumeLabel; - [SupportedOSPlatform("windows")] - set - { - _volumeLabel = value ?? _volumeLabel; - Execute.NotOnWindows( - () => throw ExceptionFactory.OperationNotSupportedOnThisPlatform()); - } - } - - /// - public IStorageDrive ChangeUsedBytes(long usedBytesDelta) - { - long newUsedBytes = Math.Max(0, _usedBytes + usedBytesDelta); - - if (newUsedBytes > TotalSize) - { - throw ExceptionFactory.NotEnoughDiskSpace(Name); - } - - _usedBytes = newUsedBytes; - - return this; - } - - /// - public IStorageDrive SetDriveFormat( - string driveFormat = DefaultDriveFormat) - { - DriveFormat = driveFormat; - return this; - } - - /// - public IStorageDrive SetDriveType( - DriveType driveType = DefaultDriveType) - { - DriveType = driveType; - return this; - } - - /// - public IStorageDrive SetIsReady(bool isReady = true) - { - IsReady = isReady; - return this; - } - - /// - public IStorageDrive SetTotalSize( - long totalSize = DefaultTotalSize) - { - TotalSize = totalSize; - return this; - } - - #endregion - - /// - public override string ToString() - => Name; - - private string GetTopmostParentDirectory(string path) - { - while (true) - { - string? child = FileSystem.Path.GetDirectoryName(path); - if (string.IsNullOrEmpty(child)) - { - break; - } - - path = child; - } - - return path; - } - - private static string ValidateDriveLetter(string driveName, - IFileSystem fileSystem) - { - if (driveName.Length == 1 && - char.IsLetter(driveName, 0)) - { - return $"{driveName.ToUpperInvariant()}:\\"; - } - - if (fileSystem.Path.IsPathRooted(driveName)) - { - return fileSystem.Path.GetPathRoot(driveName)!; - } - - throw ExceptionFactory.InvalidDriveName(); - } - - [return: NotNullIfNotNull("driveName")] - internal static DriveInfoMock? New(string? driveName, - MockFileSystem fileSystem) - { - if (driveName == null) - { - return null; - } - - return new DriveInfoMock(driveName, fileSystem); - } -} +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// Mocked instance of a +/// +internal sealed class DriveInfoMock : IStorageDrive +{ + /// + /// The default . + /// + public const string DefaultDriveFormat = "NTFS"; + + /// + /// The default . + /// + public const DriveType DefaultDriveType = DriveType.Fixed; + + /// + /// The default total size of a mocked . + /// + /// The number is equal to 1GB (1 Gigabyte). + /// + public const long DefaultTotalSize = 1024 * 1024 * 1024; + + private readonly MockFileSystem _fileSystem; + + private long _usedBytes; + private string _volumeLabel = nameof(MockFileSystem); + + private DriveInfoMock(string driveName, MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + + if (driveName.IsUncPath()) + { + IsUncPath = true; + driveName = PathHelper.UncPrefix + + GetTopmostParentDirectory(driveName.Substring(2)); + } + else + { + driveName = ValidateDriveLetter(driveName, fileSystem); + } + + Name = driveName; + TotalSize = DefaultTotalSize; + DriveFormat = DefaultDriveFormat; + DriveType = DefaultDriveType; + IsReady = true; + } + + #region IStorageDrive Members + + /// + public long AvailableFreeSpace + => TotalFreeSpace; + + /// + public string DriveFormat { get; private set; } + + /// + public DriveType DriveType { get; private set; } + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public bool IsReady { get; private set; } + + /// + /// Flag indicating if the drive is a UNC drive + /// + public bool IsUncPath { get; } + + /// + public string Name { get; } + + /// + public IDirectoryInfo RootDirectory + => DirectoryInfoMock.New(_fileSystem.Storage.GetLocation(Name), _fileSystem); + + /// + public long TotalFreeSpace + => TotalSize - _usedBytes; + + /// + public long TotalSize { get; private set; } + + /// + [AllowNull] + public string VolumeLabel + { + get => _volumeLabel; + [SupportedOSPlatform("windows")] + set + { + _volumeLabel = value ?? _volumeLabel; + Execute.NotOnWindows( + () => throw ExceptionFactory.OperationNotSupportedOnThisPlatform()); + } + } + + /// + public IStorageDrive ChangeUsedBytes(long usedBytesDelta) + { + long newUsedBytes = Math.Max(0, _usedBytes + usedBytesDelta); + + if (newUsedBytes > TotalSize) + { + throw ExceptionFactory.NotEnoughDiskSpace(Name); + } + + _usedBytes = newUsedBytes; + + return this; + } + + /// + public IStorageDrive SetDriveFormat( + string driveFormat = DefaultDriveFormat) + { + DriveFormat = driveFormat; + return this; + } + + /// + public IStorageDrive SetDriveType( + DriveType driveType = DefaultDriveType) + { + DriveType = driveType; + return this; + } + + /// + public IStorageDrive SetIsReady(bool isReady = true) + { + IsReady = isReady; + return this; + } + + /// + public IStorageDrive SetTotalSize( + long totalSize = DefaultTotalSize) + { + TotalSize = totalSize; + return this; + } + + #endregion + + /// + public override string ToString() + => Name; + + private string GetTopmostParentDirectory(string path) + { + while (true) + { + string? child = FileSystem.Path.GetDirectoryName(path); + if (string.IsNullOrEmpty(child)) + { + break; + } + + path = child; + } + + return path; + } + + private static string ValidateDriveLetter(string driveName, + IFileSystem fileSystem) + { + if (driveName.Length == 1 && + char.IsLetter(driveName, 0)) + { + return $"{driveName.ToUpperInvariant()}:\\"; + } + + if (fileSystem.Path.IsPathRooted(driveName)) + { + return fileSystem.Path.GetPathRoot(driveName)!; + } + + throw ExceptionFactory.InvalidDriveName(); + } + + [return: NotNullIfNotNull("driveName")] + internal static DriveInfoMock? New(string? driveName, + MockFileSystem fileSystem) + { + if (driveName == null) + { + return null; + } + + return new DriveInfoMock(driveName, fileSystem); + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs index 42f52f644..ec6235ed2 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs @@ -1,52 +1,51 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class FileInfoFactoryMock : IFileInfoFactory -{ - private readonly MockFileSystem _fileSystem; - - internal FileInfoFactoryMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - #region IFileInfoFactory Members - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public IFileInfo New(string fileName) - { - if (fileName == null) - { - throw new ArgumentNullException(nameof(fileName)); - } - - if (fileName.Trim() == "" && Execute.IsWindows) - { - throw ExceptionFactory.PathIsEmpty("path"); - } - - return FileInfoMock.New( - _fileSystem.Storage.GetLocation(fileName), - _fileSystem); - } - - /// - [return: NotNullIfNotNull("fileInfo")] - public IFileInfo? Wrap(FileInfo? fileInfo) - => FileInfoMock.New( - _fileSystem.Storage.GetLocation( - fileInfo?.FullName, - fileInfo?.ToString()), - _fileSystem); - - #endregion -} +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class FileInfoFactoryMock : IFileInfoFactory +{ + private readonly MockFileSystem _fileSystem; + + internal FileInfoFactoryMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + #region IFileInfoFactory Members + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public IFileInfo New(string fileName) + { + if (fileName == null) + { + throw new ArgumentNullException(nameof(fileName)); + } + + if (fileName.Trim() == "" && Execute.IsWindows) + { + throw ExceptionFactory.PathIsEmpty("path"); + } + + return FileInfoMock.New( + _fileSystem.Storage.GetLocation(fileName), + _fileSystem); + } + + /// + [return: NotNullIfNotNull("fileInfo")] + public IFileInfo? Wrap(FileInfo? fileInfo) + => FileInfoMock.New( + _fileSystem.Storage.GetLocation( + fileInfo?.FullName, + fileInfo?.ToString()), + _fileSystem); + + #endregion +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs index bb1865de8..91441c41f 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs @@ -1,283 +1,282 @@ -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// A mocked file in the . -/// -internal sealed class FileInfoMock - : FileSystemInfoMock, IFileInfo -{ - private FileInfoMock(IStorageLocation location, - MockFileSystem fileSystem) - : base(fileSystem, location, FileSystemTypes.File) - { - } - - #region IFileInfo Members - - /// - public IDirectoryInfo? Directory - => DirectoryInfoMock.New(Location.GetParent(), - FileSystem); - - /// - public string? DirectoryName - => Directory?.FullName; - - /// - public override bool Exists - => base.Exists && FileSystemType == FileSystemTypes.File; - - /// - public bool IsReadOnly - { - get => (Attributes & FileAttributes.ReadOnly) != 0; - set - { - if (value) - { - Attributes |= FileAttributes.ReadOnly; - } - else - { - Attributes &= ~FileAttributes.ReadOnly; - } - } - } - - /// - public long Length - { - get - { - if (Container is NullContainer || - Container.Type != FileSystemTypes.File) - { - throw ExceptionFactory.FileNotFound( - Execute.OnNetFramework( - () => Location.FriendlyName, - () => Location.FullPath)); - } - - return Container.GetBytes().Length; - } - } - - /// - public StreamWriter AppendText() - => new(Open(FileMode.Append, FileAccess.Write)); - - /// - public IFileInfo CopyTo(string destFileName) - { - destFileName.EnsureValidArgument(FileSystem, nameof(destFileName)); - IStorageLocation destinationLocation = FileSystem.Storage.GetLocation(destFileName); - Location.ThrowExceptionIfNotFound(FileSystem); - IStorageLocation location = FileSystem.Storage - .Copy(Location, destinationLocation) - ?? throw ExceptionFactory.FileNotFound(FullName); - return FileSystem.FileInfo.New(location.FullPath); - } - - /// - public IFileInfo CopyTo(string destFileName, bool overwrite) - { - destFileName.EnsureValidArgument(FileSystem, nameof(destFileName)); - IStorageLocation location = FileSystem.Storage.Copy( - Location, - FileSystem.Storage.GetLocation(destFileName), - overwrite) - ?? throw ExceptionFactory.FileNotFound(FullName); - return FileSystem.FileInfo.New(location.FullPath); - } - - /// - public FileSystemStream Create() - { - Execute.NotOnNetFramework(Refresh); - return FileSystem.File.Create(FullName); - } - - /// - public StreamWriter CreateText() - => new(FileSystem.File.Create(FullName)); - - /// - [SupportedOSPlatform("windows")] - public void Decrypt() - => Container.Decrypt(); - - /// - [SupportedOSPlatform("windows")] - public void Encrypt() - => Container.Encrypt(); - - /// - public void MoveTo(string destFileName) - { - Location = FileSystem.Storage.Move( - Location, - FileSystem.Storage.GetLocation(destFileName - .EnsureValidArgument(FileSystem, nameof(destFileName)))) - ?? throw ExceptionFactory.FileNotFound(FullName); - } - -#if FEATURE_FILE_MOVETO_OVERWRITE - /// - public void MoveTo(string destFileName, bool overwrite) - { - Location = FileSystem.Storage.Move( - Location, - FileSystem.Storage.GetLocation(destFileName - .EnsureValidArgument(FileSystem, nameof(destFileName))), - overwrite) - ?? throw ExceptionFactory.FileNotFound(FullName); - } -#endif - - /// - public FileSystemStream Open(FileMode mode) - { - Execute.OnNetFrameworkIf(mode == FileMode.Append, - () => throw ExceptionFactory.AppendAccessOnlyInWriteOnlyMode()); - - return new FileStreamMock( - FileSystem, - FullName, - mode, - mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, - FileShare.None); - } - - /// - public FileSystemStream Open(FileMode mode, FileAccess access) - => new FileStreamMock( - FileSystem, - FullName, - mode, - access, - FileShare.None); - - /// - public FileSystemStream Open(FileMode mode, FileAccess access, FileShare share) - => new FileStreamMock( - FileSystem, - FullName, - mode, - access, - share); - -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - /// - public FileSystemStream Open(FileStreamOptions options) - => FileSystem.File.Open(FullName, options); -#endif - - /// - public FileSystemStream OpenRead() - => new FileStreamMock( - FileSystem, - FullName, - FileMode.Open, - FileAccess.Read); - - /// - public StreamReader OpenText() - => new(OpenRead()); - - /// - public FileSystemStream OpenWrite() - => new FileStreamMock( - FileSystem, - FullName, - FileMode.OpenOrCreate, - FileAccess.Write, - FileShare.None); - - /// - public IFileInfo Replace(string destinationFileName, - string? destinationBackupFileName) - { - IStorageLocation location = - FileSystem - .Storage - .Replace( - Location.ThrowIfNotFound(FileSystem, - () => { }, - () => - { - if (Execute.IsWindows) - { - throw ExceptionFactory.FileNotFound(FullName); - } - - throw ExceptionFactory.DirectoryNotFound(FullName); - }), - FileSystem.Storage - .GetLocation(destinationFileName - .EnsureValidFormat(FileSystem, nameof(destinationFileName))) - .ThrowIfNotFound(FileSystem, - () => { }, - () => - { - if (Execute.IsWindows) - { - throw ExceptionFactory.DirectoryNotFound(FullName); - } - - throw ExceptionFactory.FileNotFound(FullName); - }), - FileSystem.Storage - .GetLocation(destinationBackupFileName) - .ThrowIfNotFound(FileSystem, - () => { }, - () => - { - if (Execute.IsWindows) - { - throw ExceptionFactory.FileNotFound(FullName); - } - - throw ExceptionFactory.DirectoryNotFound(FullName); - })) - ?? throw ExceptionFactory.FileNotFound(FullName); - return FileSystem.FileInfo.New(location.FullPath); - } - - /// - public IFileInfo Replace(string destinationFileName, - string? destinationBackupFileName, - bool ignoreMetadataErrors) - { - IStorageLocation location = FileSystem.Storage.Replace( - Location, - FileSystem.Storage.GetLocation( - destinationFileName - .EnsureValidFormat(FileSystem, - nameof(destinationFileName))), - FileSystem.Storage.GetLocation( - destinationBackupFileName), - ignoreMetadataErrors) - ?? throw ExceptionFactory.FileNotFound(FullName); - return FileSystem.FileInfo.New(location.FullPath); - } - - #endregion - - [return: NotNullIfNotNull("location")] - internal static new FileInfoMock? New(IStorageLocation? location, - MockFileSystem fileSystem) - { - if (location == null) - { - return null; - } - - return new FileInfoMock(location, fileSystem); - } -} +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// A mocked file in the . +/// +internal sealed class FileInfoMock + : FileSystemInfoMock, IFileInfo +{ + private FileInfoMock(IStorageLocation location, + MockFileSystem fileSystem) + : base(fileSystem, location, FileSystemTypes.File) + { + } + + #region IFileInfo Members + + /// + public IDirectoryInfo? Directory + => DirectoryInfoMock.New(Location.GetParent(), + FileSystem); + + /// + public string? DirectoryName + => Directory?.FullName; + + /// + public override bool Exists + => base.Exists && FileSystemType == FileSystemTypes.File; + + /// + public bool IsReadOnly + { + get => (Attributes & FileAttributes.ReadOnly) != 0; + set + { + if (value) + { + Attributes |= FileAttributes.ReadOnly; + } + else + { + Attributes &= ~FileAttributes.ReadOnly; + } + } + } + + /// + public long Length + { + get + { + if (Container is NullContainer || + Container.Type != FileSystemTypes.File) + { + throw ExceptionFactory.FileNotFound( + Execute.OnNetFramework( + () => Location.FriendlyName, + () => Location.FullPath)); + } + + return Container.GetBytes().Length; + } + } + + /// + public StreamWriter AppendText() + => new(Open(FileMode.Append, FileAccess.Write)); + + /// + public IFileInfo CopyTo(string destFileName) + { + destFileName.EnsureValidArgument(FileSystem, nameof(destFileName)); + IStorageLocation destinationLocation = FileSystem.Storage.GetLocation(destFileName); + Location.ThrowExceptionIfNotFound(FileSystem); + IStorageLocation location = FileSystem.Storage + .Copy(Location, destinationLocation) + ?? throw ExceptionFactory.FileNotFound(FullName); + return FileSystem.FileInfo.New(location.FullPath); + } + + /// + public IFileInfo CopyTo(string destFileName, bool overwrite) + { + destFileName.EnsureValidArgument(FileSystem, nameof(destFileName)); + IStorageLocation location = FileSystem.Storage.Copy( + Location, + FileSystem.Storage.GetLocation(destFileName), + overwrite) + ?? throw ExceptionFactory.FileNotFound(FullName); + return FileSystem.FileInfo.New(location.FullPath); + } + + /// + public FileSystemStream Create() + { + Execute.NotOnNetFramework(Refresh); + return FileSystem.File.Create(FullName); + } + + /// + public StreamWriter CreateText() + => new(FileSystem.File.Create(FullName)); + + /// + [SupportedOSPlatform("windows")] + public void Decrypt() + => Container.Decrypt(); + + /// + [SupportedOSPlatform("windows")] + public void Encrypt() + => Container.Encrypt(); + + /// + public void MoveTo(string destFileName) + { + Location = FileSystem.Storage.Move( + Location, + FileSystem.Storage.GetLocation(destFileName + .EnsureValidArgument(FileSystem, nameof(destFileName)))) + ?? throw ExceptionFactory.FileNotFound(FullName); + } + +#if FEATURE_FILE_MOVETO_OVERWRITE + /// + public void MoveTo(string destFileName, bool overwrite) + { + Location = FileSystem.Storage.Move( + Location, + FileSystem.Storage.GetLocation(destFileName + .EnsureValidArgument(FileSystem, nameof(destFileName))), + overwrite) + ?? throw ExceptionFactory.FileNotFound(FullName); + } +#endif + + /// + public FileSystemStream Open(FileMode mode) + { + Execute.OnNetFrameworkIf(mode == FileMode.Append, + () => throw ExceptionFactory.AppendAccessOnlyInWriteOnlyMode()); + + return new FileStreamMock( + FileSystem, + FullName, + mode, + mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, + FileShare.None); + } + + /// + public FileSystemStream Open(FileMode mode, FileAccess access) + => new FileStreamMock( + FileSystem, + FullName, + mode, + access, + FileShare.None); + + /// + public FileSystemStream Open(FileMode mode, FileAccess access, FileShare share) + => new FileStreamMock( + FileSystem, + FullName, + mode, + access, + share); + +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + /// + public FileSystemStream Open(FileStreamOptions options) + => FileSystem.File.Open(FullName, options); +#endif + + /// + public FileSystemStream OpenRead() + => new FileStreamMock( + FileSystem, + FullName, + FileMode.Open, + FileAccess.Read); + + /// + public StreamReader OpenText() + => new(OpenRead()); + + /// + public FileSystemStream OpenWrite() + => new FileStreamMock( + FileSystem, + FullName, + FileMode.OpenOrCreate, + FileAccess.Write, + FileShare.None); + + /// + public IFileInfo Replace(string destinationFileName, + string? destinationBackupFileName) + { + IStorageLocation location = + FileSystem + .Storage + .Replace( + Location.ThrowIfNotFound(FileSystem, + () => { }, + () => + { + if (Execute.IsWindows) + { + throw ExceptionFactory.FileNotFound(FullName); + } + + throw ExceptionFactory.DirectoryNotFound(FullName); + }), + FileSystem.Storage + .GetLocation(destinationFileName + .EnsureValidFormat(FileSystem, nameof(destinationFileName))) + .ThrowIfNotFound(FileSystem, + () => { }, + () => + { + if (Execute.IsWindows) + { + throw ExceptionFactory.DirectoryNotFound(FullName); + } + + throw ExceptionFactory.FileNotFound(FullName); + }), + FileSystem.Storage + .GetLocation(destinationBackupFileName) + .ThrowIfNotFound(FileSystem, + () => { }, + () => + { + if (Execute.IsWindows) + { + throw ExceptionFactory.FileNotFound(FullName); + } + + throw ExceptionFactory.DirectoryNotFound(FullName); + })) + ?? throw ExceptionFactory.FileNotFound(FullName); + return FileSystem.FileInfo.New(location.FullPath); + } + + /// + public IFileInfo Replace(string destinationFileName, + string? destinationBackupFileName, + bool ignoreMetadataErrors) + { + IStorageLocation location = FileSystem.Storage.Replace( + Location, + FileSystem.Storage.GetLocation( + destinationFileName + .EnsureValidFormat(FileSystem, + nameof(destinationFileName))), + FileSystem.Storage.GetLocation( + destinationBackupFileName), + ignoreMetadataErrors) + ?? throw ExceptionFactory.FileNotFound(FullName); + return FileSystem.FileInfo.New(location.FullPath); + } + + #endregion + + [return: NotNullIfNotNull("location")] + internal static new FileInfoMock? New(IStorageLocation? location, + MockFileSystem fileSystem) + { + if (location == null) + { + return null; + } + + return new FileInfoMock(location, fileSystem); + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs index b62d4f21b..6d032b3d8 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs @@ -1,967 +1,966 @@ -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE -using Microsoft.Win32.SafeHandles; -#endif -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Text; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; -#if FEATURE_FILESYSTEM_ASYNC -using System.Threading; -using System.Threading.Tasks; -#endif - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class FileMock : IFile -{ - private readonly MockFileSystem _fileSystem; - - internal FileMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - #region IFile Members - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public void AppendAllLines(string path, IEnumerable contents) - => AppendAllLines(path, contents, Encoding.Default); - - /// - public void AppendAllLines( - string path, - IEnumerable contents, - Encoding encoding) - { - _ = contents ?? throw new ArgumentNullException(nameof(contents)); - _ = encoding ?? throw new ArgumentNullException(nameof(encoding)); - AppendAllText( - path, - contents.Aggregate(string.Empty, (a, b) => a + b + Environment.NewLine), - encoding); - } - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task AppendAllLinesAsync(string path, IEnumerable contents, - CancellationToken cancellationToken = default) - => AppendAllLinesAsync(path, contents, Encoding.Default, cancellationToken); - - /// - public Task AppendAllLinesAsync(string path, IEnumerable contents, - Encoding encoding, - CancellationToken cancellationToken = default) - { - ThrowIfCancelled(cancellationToken); - AppendAllLines(path, contents, encoding); - return Task.CompletedTask; - } -#endif - - /// - public void AppendAllText(string path, string? contents) - => AppendAllText(path, contents, Encoding.Default); - - /// - public void AppendAllText(string path, string? contents, Encoding encoding) - { - IStorageContainer fileInfo = - _fileSystem.Storage.GetOrCreateContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem)), - InMemoryContainer.NewFile); - if (contents != null) - { - using (fileInfo.RequestAccess( - FileAccess.ReadWrite, - FileStreamFactoryMock.DefaultShare)) - { - if (fileInfo.GetBytes().Length == 0) - { - fileInfo.WriteBytes(encoding.GetPreamble()); - } - - fileInfo.AppendBytes(encoding.GetBytes(contents)); - } - } - } - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task AppendAllTextAsync(string path, string? contents, - CancellationToken cancellationToken = default) - => AppendAllTextAsync(path, contents, Encoding.Default, cancellationToken); - - /// - public Task AppendAllTextAsync(string path, string? contents, Encoding encoding, - CancellationToken cancellationToken = default) - { - ThrowIfCancelled(cancellationToken); - AppendAllText(path, contents, encoding); - return Task.CompletedTask; - } -#endif - - /// - public StreamWriter AppendText(string path) - => FileSystem.FileInfo - .New(path.EnsureValidFormat(FileSystem)) - .AppendText(); - - /// - public void Copy(string sourceFileName, string destFileName) - { - sourceFileName.EnsureValidFormat(_fileSystem, nameof(sourceFileName)); - destFileName.EnsureValidFormat(_fileSystem, nameof(destFileName)); - try - { - _fileSystem.FileInfo - .New(sourceFileName) - .CopyTo(destFileName); - } - catch (UnauthorizedAccessException) - { - Execute.OnNetFramework(() - => throw ExceptionFactory.AccessToPathDenied(sourceFileName)); - - throw; - } - } - - /// - public void Copy(string sourceFileName, string destFileName, bool overwrite) - => Execute.OnNetFramework( - () => - { - try - { - _fileSystem.FileInfo.New(sourceFileName - .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) - .CopyTo(destFileName - .EnsureValidFormat(_fileSystem, nameof(destFileName)), - overwrite); - } - catch (UnauthorizedAccessException) - { - throw ExceptionFactory.AccessToPathDenied(sourceFileName); - } - }, - () => - { - _fileSystem.FileInfo.New(sourceFileName - .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) - .CopyTo(destFileName - .EnsureValidFormat(_fileSystem, nameof(destFileName)), overwrite); - }); - - /// - public FileSystemStream Create(string path) - => new FileStreamMock( - _fileSystem, - path, - FileMode.Create, - FileAccess.ReadWrite, - FileShare.None); - - /// - public FileSystemStream Create(string path, int bufferSize) - => new FileStreamMock( - _fileSystem, - path, - FileMode.Create, - FileAccess.ReadWrite, - FileShare.None, - bufferSize); - - /// - public FileSystemStream Create(string path, int bufferSize, FileOptions options) - => new FileStreamMock( - _fileSystem, - path, - FileMode.Create, - FileAccess.ReadWrite, - FileShare.None, - bufferSize, - options); - -#if FEATURE_FILESYSTEM_LINK - /// - public IFileSystemInfo CreateSymbolicLink( - string path, string pathToTarget) - { - path.EnsureValidFormat(_fileSystem); - IFileInfo fileSystemInfo = - _fileSystem.FileInfo.New(path); - fileSystemInfo.CreateAsSymbolicLink(pathToTarget); - return fileSystemInfo; - } -#endif - - /// - public StreamWriter CreateText(string path) - => FileSystem.FileInfo - .New(path.EnsureValidFormat(FileSystem)) - .CreateText(); - - /// - [SupportedOSPlatform("windows")] - public void Decrypt(string path) - { - IStorageContainer container = GetContainerFromPath(path); - container.Decrypt(); - } - - /// - public void Delete(string path) - => _fileSystem.Storage.DeleteContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))); - - /// - [SupportedOSPlatform("windows")] - public void Encrypt(string path) - { - IStorageContainer container = GetContainerFromPath(path); - container.Encrypt(); - } - - /// - public bool Exists([NotNullWhen(true)] string? path) - { - if (string.IsNullOrEmpty(path)) - { - return false; - } - - return FileInfoMock.New( - _fileSystem.Storage.GetLocation(path), - _fileSystem) - .Exists; - } - - /// - public FileAttributes GetAttributes(string path) - { - IStorageContainer container = _fileSystem.Storage - .GetContainer(_fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem)) - .ThrowExceptionIfNotFound(_fileSystem)); - - return container.Attributes; - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public FileAttributes GetAttributes(SafeFileHandle fileHandle) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - return container.Attributes; - } -#endif - - /// - public DateTime GetCreationTime(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .CreationTime.Get(DateTimeKind.Local); - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public DateTime GetCreationTime(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) - .CreationTime.Get(DateTimeKind.Local); -#endif - - /// - public DateTime GetCreationTimeUtc(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .CreationTime.Get(DateTimeKind.Utc); - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public DateTime GetCreationTimeUtc(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) - .CreationTime.Get(DateTimeKind.Utc); -#endif - - /// - public DateTime GetLastAccessTime(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastAccessTime.Get(DateTimeKind.Local); - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public DateTime GetLastAccessTime(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) - .LastAccessTime.Get(DateTimeKind.Local); -#endif - - /// - public DateTime GetLastAccessTimeUtc(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastAccessTime.Get(DateTimeKind.Utc); - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public DateTime GetLastAccessTimeUtc(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) - .LastAccessTime.Get(DateTimeKind.Utc); -#endif - - /// - public DateTime GetLastWriteTime(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastWriteTime.Get(DateTimeKind.Local); - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public DateTime GetLastWriteTime(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) - .LastWriteTime.Get(DateTimeKind.Local); -#endif - - /// - public DateTime GetLastWriteTimeUtc(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastWriteTime.Get(DateTimeKind.Utc); - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public DateTime GetLastWriteTimeUtc(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) - .LastWriteTime.Get(DateTimeKind.Utc); -#endif - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - [UnsupportedOSPlatform("windows")] - public UnixFileMode GetUnixFileMode(string path) - => Execute.OnWindows( - () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform(), - () => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem)) - .ThrowExceptionIfNotFound(_fileSystem)) - .UnixFileMode); -#endif - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - [UnsupportedOSPlatform("windows")] - public UnixFileMode GetUnixFileMode(SafeFileHandle fileHandle) - => Execute.OnWindows( - () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform(), - () => GetContainerFromSafeFileHandle(fileHandle) - .UnixFileMode); -#endif - - /// - public void Move(string sourceFileName, string destFileName) - => _fileSystem.FileInfo.New(sourceFileName - .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) - .MoveTo(destFileName - .EnsureValidFormat(_fileSystem, nameof(destFileName))); - -#if FEATURE_FILE_MOVETO_OVERWRITE - /// - public void Move(string sourceFileName, string destFileName, bool overwrite) - => _fileSystem.FileInfo.New(sourceFileName - .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) - .MoveTo(destFileName - .EnsureValidFormat(_fileSystem, nameof(destFileName)), overwrite); -#endif - - /// - public FileSystemStream Open(string path, FileMode mode) - => new FileStreamMock( - _fileSystem, - path, - mode, - mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, - FileShare.None); - - /// - public FileSystemStream Open(string path, FileMode mode, FileAccess access) - => new FileStreamMock( - _fileSystem, - path, - mode, - access, - FileShare.None); - - /// - public FileSystemStream Open( - string path, - FileMode mode, - FileAccess access, - FileShare share) - => new FileStreamMock( - _fileSystem, - path, - mode, - access, - share); - -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - /// - public FileSystemStream Open(string path, FileStreamOptions options) - => new FileStreamMock( - _fileSystem, - path, - options.Mode, - options.Access, - options.Share, - options.BufferSize, - options.Options); -#endif - - /// - public FileSystemStream OpenRead(string path) - => new FileStreamMock( - _fileSystem, - path, - FileMode.Open, - FileAccess.Read); - - /// - public StreamReader OpenText(string path) - => FileSystem.FileInfo - .New(path.EnsureValidFormat(FileSystem)) - .OpenText(); - - /// - public FileSystemStream OpenWrite(string path) - => new FileStreamMock( - _fileSystem, - path, - FileMode.OpenOrCreate, - FileAccess.Write, - FileShare.None); - - /// - public byte[] ReadAllBytes(string path) - { - IStorageContainer container = GetContainerFromPath(path); - using (container.RequestAccess( - FileAccess.Read, - FileStreamFactoryMock.DefaultShare)) - { - container.AdjustTimes(TimeAdjustments.LastAccessTime); - return container.GetBytes().ToArray(); - } - } - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task ReadAllBytesAsync(string path, - CancellationToken cancellationToken = - default) - { - ThrowIfCancelled(cancellationToken); - return Task.FromResult(ReadAllBytes(path)); - } -#endif - - /// - public string[] ReadAllLines(string path) - => ReadAllLines(path, Encoding.Default); - - /// - public string[] ReadAllLines(string path, Encoding encoding) - => ReadLines(path, encoding).ToArray(); - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task ReadAllLinesAsync( - string path, - CancellationToken cancellationToken = default) - => ReadAllLinesAsync(path, Encoding.Default, cancellationToken); - - /// - public Task ReadAllLinesAsync( - string path, - Encoding encoding, - CancellationToken cancellationToken = default) - { - ThrowIfCancelled(cancellationToken); - return Task.FromResult(ReadAllLines(path, encoding)); - } -#endif - - /// - public string ReadAllText(string path) - => ReadAllText(path, Encoding.Default); - - /// - public string ReadAllText(string path, Encoding encoding) - { - IStorageContainer container = GetContainerFromPath(path); - using (container.RequestAccess( - FileAccess.Read, - FileStreamFactoryMock.DefaultShare)) - { - container.AdjustTimes(TimeAdjustments.LastAccessTime); - using (MemoryStream ms = new(container.GetBytes())) - using (StreamReader sr = new(ms, encoding)) - { - return sr.ReadToEnd(); - } - } - } - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task ReadAllTextAsync( - string path, - CancellationToken cancellationToken = default) - => ReadAllTextAsync(path, Encoding.Default, cancellationToken); - - /// - public Task ReadAllTextAsync( - string path, - Encoding encoding, - CancellationToken cancellationToken = default) - { - ThrowIfCancelled(cancellationToken); - return Task.FromResult(ReadAllText(path, encoding)); - } -#endif - - /// - public IEnumerable ReadLines(string path) - => ReadLines(path, Encoding.Default); - - /// - public IEnumerable ReadLines(string path, Encoding encoding) - => EnumerateLines(ReadAllText(path, encoding)); - -#if FEATURE_FILESYSTEM_NET7 - /// - public IAsyncEnumerable ReadLinesAsync(string path, - CancellationToken cancellationToken = - default) - { - ThrowIfCancelled(cancellationToken); - return ReadAllLines(path).ToAsyncEnumerable(); - } - - /// - public IAsyncEnumerable ReadLinesAsync(string path, Encoding encoding, - CancellationToken cancellationToken = - default) - { - ThrowIfCancelled(cancellationToken); - return ReadAllLines(path, encoding).ToAsyncEnumerable(); - } -#endif - - /// - public void Replace(string sourceFileName, - string destinationFileName, - string? destinationBackupFileName) - => _fileSystem.FileInfo.New(sourceFileName - .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) - .Replace(destinationFileName - .EnsureValidFormat(_fileSystem, nameof(destinationFileName)), - destinationBackupFileName); - - /// - public void Replace(string sourceFileName, - string destinationFileName, - string? destinationBackupFileName, - bool ignoreMetadataErrors) - => _fileSystem.FileInfo.New(sourceFileName - .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) - .Replace(destinationFileName - .EnsureValidFormat(_fileSystem, nameof(destinationFileName)), - destinationBackupFileName, - ignoreMetadataErrors); - -#if FEATURE_FILESYSTEM_LINK - /// - public IFileSystemInfo? ResolveLinkTarget( - string linkPath, bool returnFinalTarget) - { - IStorageLocation location = - _fileSystem.Storage.GetLocation(linkPath - .EnsureValidFormat(_fileSystem, nameof(linkPath))); - Execute.OnWindows( - () => location.ThrowExceptionIfNotFound(_fileSystem), - () => location.ThrowExceptionIfNotFound(_fileSystem, - onDirectoryNotFound: ExceptionFactory.FileNotFound)); - try - { - IStorageLocation? targetLocation = - _fileSystem.Storage.ResolveLinkTarget(location, - returnFinalTarget); - if (targetLocation != null) - { - return FileSystemInfoMock.New(targetLocation, _fileSystem); - } - - return null; - } - catch (IOException) - { - throw ExceptionFactory.FileNameCannotBeResolved(linkPath, - Execute.IsWindows ? -2147022975 : -2146232800); - } - } -#endif - - /// - public void SetAttributes(string path, FileAttributes fileAttributes) - { - IStorageContainer container = GetContainerFromPath(path); - container.Attributes = fileAttributes; - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public void SetAttributes(SafeFileHandle fileHandle, FileAttributes fileAttributes) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.Attributes = fileAttributes; - } -#endif - - /// - public void SetCreationTime(string path, DateTime creationTime) - { - IStorageContainer container = - GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); - container.CreationTime.Set(creationTime, DateTimeKind.Local); - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public void SetCreationTime(SafeFileHandle fileHandle, DateTime creationTime) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.CreationTime.Set(creationTime, DateTimeKind.Local); - } -#endif - - /// - public void SetCreationTimeUtc(string path, DateTime creationTimeUtc) - { - IStorageContainer container = - GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); - container.CreationTime.Set(creationTimeUtc, DateTimeKind.Utc); - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public void SetCreationTimeUtc(SafeFileHandle fileHandle, DateTime creationTimeUtc) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.CreationTime.Set(creationTimeUtc, DateTimeKind.Utc); - } -#endif - - /// - public void SetLastAccessTime(string path, DateTime lastAccessTime) - { - IStorageContainer container = - GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); - container.LastAccessTime.Set(lastAccessTime, DateTimeKind.Local); - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public void SetLastAccessTime(SafeFileHandle fileHandle, DateTime lastAccessTime) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.LastAccessTime.Set(lastAccessTime, DateTimeKind.Local); - } -#endif - - /// - public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) - { - IStorageContainer container = - GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); - container.LastAccessTime.Set(lastAccessTimeUtc, DateTimeKind.Utc); - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public void SetLastAccessTimeUtc(SafeFileHandle fileHandle, - DateTime lastAccessTimeUtc) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.LastAccessTime.Set(lastAccessTimeUtc, DateTimeKind.Utc); - } -#endif - - /// - public void SetLastWriteTime(string path, DateTime lastWriteTime) - { - IStorageContainer container = - GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); - container.LastWriteTime.Set(lastWriteTime, DateTimeKind.Local); - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public void SetLastWriteTime(SafeFileHandle fileHandle, DateTime lastWriteTime) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.LastWriteTime.Set(lastWriteTime, DateTimeKind.Local); - } -#endif - - /// - public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) - { - IStorageContainer container = - GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); - container.LastWriteTime.Set(lastWriteTimeUtc, DateTimeKind.Utc); - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public void SetLastWriteTimeUtc(SafeFileHandle fileHandle, DateTime lastWriteTimeUtc) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.LastWriteTime.Set(lastWriteTimeUtc, DateTimeKind.Utc); - } -#endif - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - [UnsupportedOSPlatform("windows")] - public void SetUnixFileMode(string path, UnixFileMode mode) - { - Execute.OnWindows( - () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform()); - - IStorageContainer container = GetContainerFromPath(path); - container.UnixFileMode = mode; - } -#endif - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - [UnsupportedOSPlatform("windows")] - public void SetUnixFileMode(SafeFileHandle fileHandle, UnixFileMode mode) - { - Execute.OnWindows( - () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform()); - - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.UnixFileMode = mode; - } -#endif - - /// - public void WriteAllBytes(string path, byte[] bytes) - { - _ = bytes ?? throw new ArgumentNullException(nameof(bytes)); - IStorageContainer container = - _fileSystem.Storage.GetOrCreateContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem)), - InMemoryContainer.NewFile); - if (container is not NullContainer) - { - Execute.OnWindowsIf( - container.Attributes.HasFlag(FileAttributes.Hidden), - () => throw ExceptionFactory.AccessToPathDenied()); - using (container.RequestAccess( - FileAccess.Write, - FileStreamFactoryMock.DefaultShare)) - { - container.WriteBytes(bytes); - } - } - } - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task WriteAllBytesAsync(string path, byte[] bytes, - CancellationToken cancellationToken = default) - { - ThrowIfCancelled(cancellationToken); - WriteAllBytes(path, bytes); - return Task.CompletedTask; - } -#endif - - /// - public void WriteAllLines(string path, string[] contents) - => WriteAllLines(path, contents, Encoding.Default); - - /// - public void WriteAllLines(string path, IEnumerable contents) - => WriteAllLines(path, contents, Encoding.Default); - - /// - public void WriteAllLines( - string path, - string[] contents, - Encoding encoding) - => WriteAllLines(path, contents.AsEnumerable(), encoding); - - /// - public void WriteAllLines( - string path, - IEnumerable contents, - Encoding encoding) - => WriteAllText( - path, - contents.Aggregate(string.Empty, (a, b) => a + b + Environment.NewLine), - encoding); - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task WriteAllLinesAsync( - string path, - IEnumerable contents, - CancellationToken cancellationToken = default) - => WriteAllLinesAsync(path, contents, Encoding.Default, cancellationToken); - - /// - public Task WriteAllLinesAsync( - string path, - IEnumerable contents, - Encoding encoding, - CancellationToken cancellationToken = default) - { - ThrowIfCancelled(cancellationToken); - WriteAllLines(path, contents, encoding); - return Task.CompletedTask; - } -#endif - - /// - public void WriteAllText(string path, string? contents) - => WriteAllText(path, contents, Encoding.Default); - - /// - public void WriteAllText(string path, string? contents, Encoding encoding) - { - IStorageContainer container = - _fileSystem.Storage.GetOrCreateContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem)), - InMemoryContainer.NewFile); - if (container is not NullContainer && contents != null) - { - Execute.OnWindowsIf( - container.Attributes.HasFlag(FileAttributes.Hidden), - () => throw ExceptionFactory.AccessToPathDenied()); - using (container.RequestAccess( - FileAccess.Write, - FileStreamFactoryMock.DefaultShare)) - { - container.WriteBytes(encoding.GetPreamble()); - container.AppendBytes(encoding.GetBytes(contents)); - } - } - } - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task WriteAllTextAsync(string path, string? contents, - CancellationToken cancellationToken = default) - => WriteAllTextAsync(path, contents, Encoding.Default, cancellationToken); - - /// - public Task WriteAllTextAsync(string path, string? contents, Encoding encoding, - CancellationToken cancellationToken = default) - { - ThrowIfCancelled(cancellationToken); - WriteAllText(path, contents, encoding); - return Task.CompletedTask; - } -#endif - - #endregion - - private static IEnumerable EnumerateLines(string contents) - { - using (StringReader reader = new(contents)) - { - while (reader.ReadLine() is { } line) - { - yield return line; - } - } - } - - private enum ExceptionMode - { - Default, - FileNotFoundExceptionOnLinuxAndMac - } - - private IStorageContainer GetContainerFromPath(string path, - ExceptionMode exceptionMode = ExceptionMode.Default) - { - path.EnsureValidFormat(FileSystem); - IStorageLocation location = _fileSystem.Storage.GetLocation(path); - if (exceptionMode == ExceptionMode.FileNotFoundExceptionOnLinuxAndMac) - { - Execute.OnWindows( - () => location.ThrowExceptionIfNotFound(_fileSystem), - () => location.ThrowExceptionIfNotFound(_fileSystem, - onDirectoryNotFound: ExceptionFactory.FileNotFound)); - } - - if (exceptionMode == ExceptionMode.Default) - { - location.ThrowExceptionIfNotFound(_fileSystem); - } - - return _fileSystem.Storage.GetContainer(location); - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - private IStorageContainer GetContainerFromSafeFileHandle(SafeFileHandle fileHandle) - { - SafeFileHandleMock safeFileHandleMock = _fileSystem - .SafeFileHandleStrategy.MapSafeFileHandle(fileHandle); - IStorageContainer container = _fileSystem.Storage - .GetContainer(_fileSystem.Storage.GetLocation( - safeFileHandleMock.Path) - .ThrowExceptionIfNotFound(_fileSystem)); - if (container is NullContainer) - { - throw ExceptionFactory.FileNotFound(""); - } - - return container; - } -#endif - -#if FEATURE_FILESYSTEM_ASYNC - private static void ThrowIfCancelled(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - throw ExceptionFactory.TaskWasCanceled(); - } - } -#endif -} +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE +using Microsoft.Win32.SafeHandles; +#endif +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Text; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; +#if FEATURE_FILESYSTEM_ASYNC +using System.Threading; +using System.Threading.Tasks; +#endif + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class FileMock : IFile +{ + private readonly MockFileSystem _fileSystem; + + internal FileMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + #region IFile Members + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public void AppendAllLines(string path, IEnumerable contents) + => AppendAllLines(path, contents, Encoding.Default); + + /// + public void AppendAllLines( + string path, + IEnumerable contents, + Encoding encoding) + { + _ = contents ?? throw new ArgumentNullException(nameof(contents)); + _ = encoding ?? throw new ArgumentNullException(nameof(encoding)); + AppendAllText( + path, + contents.Aggregate(string.Empty, (a, b) => a + b + Environment.NewLine), + encoding); + } + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task AppendAllLinesAsync(string path, IEnumerable contents, + CancellationToken cancellationToken = default) + => AppendAllLinesAsync(path, contents, Encoding.Default, cancellationToken); + + /// + public Task AppendAllLinesAsync(string path, IEnumerable contents, + Encoding encoding, + CancellationToken cancellationToken = default) + { + ThrowIfCancelled(cancellationToken); + AppendAllLines(path, contents, encoding); + return Task.CompletedTask; + } +#endif + + /// + public void AppendAllText(string path, string? contents) + => AppendAllText(path, contents, Encoding.Default); + + /// + public void AppendAllText(string path, string? contents, Encoding encoding) + { + IStorageContainer fileInfo = + _fileSystem.Storage.GetOrCreateContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem)), + InMemoryContainer.NewFile); + if (contents != null) + { + using (fileInfo.RequestAccess( + FileAccess.ReadWrite, + FileStreamFactoryMock.DefaultShare)) + { + if (fileInfo.GetBytes().Length == 0) + { + fileInfo.WriteBytes(encoding.GetPreamble()); + } + + fileInfo.AppendBytes(encoding.GetBytes(contents)); + } + } + } + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task AppendAllTextAsync(string path, string? contents, + CancellationToken cancellationToken = default) + => AppendAllTextAsync(path, contents, Encoding.Default, cancellationToken); + + /// + public Task AppendAllTextAsync(string path, string? contents, Encoding encoding, + CancellationToken cancellationToken = default) + { + ThrowIfCancelled(cancellationToken); + AppendAllText(path, contents, encoding); + return Task.CompletedTask; + } +#endif + + /// + public StreamWriter AppendText(string path) + => FileSystem.FileInfo + .New(path.EnsureValidFormat(FileSystem)) + .AppendText(); + + /// + public void Copy(string sourceFileName, string destFileName) + { + sourceFileName.EnsureValidFormat(_fileSystem, nameof(sourceFileName)); + destFileName.EnsureValidFormat(_fileSystem, nameof(destFileName)); + try + { + _fileSystem.FileInfo + .New(sourceFileName) + .CopyTo(destFileName); + } + catch (UnauthorizedAccessException) + { + Execute.OnNetFramework(() + => throw ExceptionFactory.AccessToPathDenied(sourceFileName)); + + throw; + } + } + + /// + public void Copy(string sourceFileName, string destFileName, bool overwrite) + => Execute.OnNetFramework( + () => + { + try + { + _fileSystem.FileInfo.New(sourceFileName + .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) + .CopyTo(destFileName + .EnsureValidFormat(_fileSystem, nameof(destFileName)), + overwrite); + } + catch (UnauthorizedAccessException) + { + throw ExceptionFactory.AccessToPathDenied(sourceFileName); + } + }, + () => + { + _fileSystem.FileInfo.New(sourceFileName + .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) + .CopyTo(destFileName + .EnsureValidFormat(_fileSystem, nameof(destFileName)), overwrite); + }); + + /// + public FileSystemStream Create(string path) + => new FileStreamMock( + _fileSystem, + path, + FileMode.Create, + FileAccess.ReadWrite, + FileShare.None); + + /// + public FileSystemStream Create(string path, int bufferSize) + => new FileStreamMock( + _fileSystem, + path, + FileMode.Create, + FileAccess.ReadWrite, + FileShare.None, + bufferSize); + + /// + public FileSystemStream Create(string path, int bufferSize, FileOptions options) + => new FileStreamMock( + _fileSystem, + path, + FileMode.Create, + FileAccess.ReadWrite, + FileShare.None, + bufferSize, + options); + +#if FEATURE_FILESYSTEM_LINK + /// + public IFileSystemInfo CreateSymbolicLink( + string path, string pathToTarget) + { + path.EnsureValidFormat(_fileSystem); + IFileInfo fileSystemInfo = + _fileSystem.FileInfo.New(path); + fileSystemInfo.CreateAsSymbolicLink(pathToTarget); + return fileSystemInfo; + } +#endif + + /// + public StreamWriter CreateText(string path) + => FileSystem.FileInfo + .New(path.EnsureValidFormat(FileSystem)) + .CreateText(); + + /// + [SupportedOSPlatform("windows")] + public void Decrypt(string path) + { + IStorageContainer container = GetContainerFromPath(path); + container.Decrypt(); + } + + /// + public void Delete(string path) + => _fileSystem.Storage.DeleteContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))); + + /// + [SupportedOSPlatform("windows")] + public void Encrypt(string path) + { + IStorageContainer container = GetContainerFromPath(path); + container.Encrypt(); + } + + /// + public bool Exists([NotNullWhen(true)] string? path) + { + if (string.IsNullOrEmpty(path)) + { + return false; + } + + return FileInfoMock.New( + _fileSystem.Storage.GetLocation(path), + _fileSystem) + .Exists; + } + + /// + public FileAttributes GetAttributes(string path) + { + IStorageContainer container = _fileSystem.Storage + .GetContainer(_fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem)) + .ThrowExceptionIfNotFound(_fileSystem)); + + return container.Attributes; + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public FileAttributes GetAttributes(SafeFileHandle fileHandle) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + return container.Attributes; + } +#endif + + /// + public DateTime GetCreationTime(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .CreationTime.Get(DateTimeKind.Local); + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public DateTime GetCreationTime(SafeFileHandle fileHandle) + => GetContainerFromSafeFileHandle(fileHandle) + .CreationTime.Get(DateTimeKind.Local); +#endif + + /// + public DateTime GetCreationTimeUtc(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .CreationTime.Get(DateTimeKind.Utc); + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public DateTime GetCreationTimeUtc(SafeFileHandle fileHandle) + => GetContainerFromSafeFileHandle(fileHandle) + .CreationTime.Get(DateTimeKind.Utc); +#endif + + /// + public DateTime GetLastAccessTime(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastAccessTime.Get(DateTimeKind.Local); + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public DateTime GetLastAccessTime(SafeFileHandle fileHandle) + => GetContainerFromSafeFileHandle(fileHandle) + .LastAccessTime.Get(DateTimeKind.Local); +#endif + + /// + public DateTime GetLastAccessTimeUtc(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastAccessTime.Get(DateTimeKind.Utc); + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public DateTime GetLastAccessTimeUtc(SafeFileHandle fileHandle) + => GetContainerFromSafeFileHandle(fileHandle) + .LastAccessTime.Get(DateTimeKind.Utc); +#endif + + /// + public DateTime GetLastWriteTime(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastWriteTime.Get(DateTimeKind.Local); + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public DateTime GetLastWriteTime(SafeFileHandle fileHandle) + => GetContainerFromSafeFileHandle(fileHandle) + .LastWriteTime.Get(DateTimeKind.Local); +#endif + + /// + public DateTime GetLastWriteTimeUtc(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastWriteTime.Get(DateTimeKind.Utc); + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public DateTime GetLastWriteTimeUtc(SafeFileHandle fileHandle) + => GetContainerFromSafeFileHandle(fileHandle) + .LastWriteTime.Get(DateTimeKind.Utc); +#endif + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + [UnsupportedOSPlatform("windows")] + public UnixFileMode GetUnixFileMode(string path) + => Execute.OnWindows( + () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform(), + () => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem)) + .ThrowExceptionIfNotFound(_fileSystem)) + .UnixFileMode); +#endif + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + [UnsupportedOSPlatform("windows")] + public UnixFileMode GetUnixFileMode(SafeFileHandle fileHandle) + => Execute.OnWindows( + () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform(), + () => GetContainerFromSafeFileHandle(fileHandle) + .UnixFileMode); +#endif + + /// + public void Move(string sourceFileName, string destFileName) + => _fileSystem.FileInfo.New(sourceFileName + .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) + .MoveTo(destFileName + .EnsureValidFormat(_fileSystem, nameof(destFileName))); + +#if FEATURE_FILE_MOVETO_OVERWRITE + /// + public void Move(string sourceFileName, string destFileName, bool overwrite) + => _fileSystem.FileInfo.New(sourceFileName + .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) + .MoveTo(destFileName + .EnsureValidFormat(_fileSystem, nameof(destFileName)), overwrite); +#endif + + /// + public FileSystemStream Open(string path, FileMode mode) + => new FileStreamMock( + _fileSystem, + path, + mode, + mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, + FileShare.None); + + /// + public FileSystemStream Open(string path, FileMode mode, FileAccess access) + => new FileStreamMock( + _fileSystem, + path, + mode, + access, + FileShare.None); + + /// + public FileSystemStream Open( + string path, + FileMode mode, + FileAccess access, + FileShare share) + => new FileStreamMock( + _fileSystem, + path, + mode, + access, + share); + +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + /// + public FileSystemStream Open(string path, FileStreamOptions options) + => new FileStreamMock( + _fileSystem, + path, + options.Mode, + options.Access, + options.Share, + options.BufferSize, + options.Options); +#endif + + /// + public FileSystemStream OpenRead(string path) + => new FileStreamMock( + _fileSystem, + path, + FileMode.Open, + FileAccess.Read); + + /// + public StreamReader OpenText(string path) + => FileSystem.FileInfo + .New(path.EnsureValidFormat(FileSystem)) + .OpenText(); + + /// + public FileSystemStream OpenWrite(string path) + => new FileStreamMock( + _fileSystem, + path, + FileMode.OpenOrCreate, + FileAccess.Write, + FileShare.None); + + /// + public byte[] ReadAllBytes(string path) + { + IStorageContainer container = GetContainerFromPath(path); + using (container.RequestAccess( + FileAccess.Read, + FileStreamFactoryMock.DefaultShare)) + { + container.AdjustTimes(TimeAdjustments.LastAccessTime); + return container.GetBytes().ToArray(); + } + } + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task ReadAllBytesAsync(string path, + CancellationToken cancellationToken = + default) + { + ThrowIfCancelled(cancellationToken); + return Task.FromResult(ReadAllBytes(path)); + } +#endif + + /// + public string[] ReadAllLines(string path) + => ReadAllLines(path, Encoding.Default); + + /// + public string[] ReadAllLines(string path, Encoding encoding) + => ReadLines(path, encoding).ToArray(); + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task ReadAllLinesAsync( + string path, + CancellationToken cancellationToken = default) + => ReadAllLinesAsync(path, Encoding.Default, cancellationToken); + + /// + public Task ReadAllLinesAsync( + string path, + Encoding encoding, + CancellationToken cancellationToken = default) + { + ThrowIfCancelled(cancellationToken); + return Task.FromResult(ReadAllLines(path, encoding)); + } +#endif + + /// + public string ReadAllText(string path) + => ReadAllText(path, Encoding.Default); + + /// + public string ReadAllText(string path, Encoding encoding) + { + IStorageContainer container = GetContainerFromPath(path); + using (container.RequestAccess( + FileAccess.Read, + FileStreamFactoryMock.DefaultShare)) + { + container.AdjustTimes(TimeAdjustments.LastAccessTime); + using (MemoryStream ms = new(container.GetBytes())) + using (StreamReader sr = new(ms, encoding)) + { + return sr.ReadToEnd(); + } + } + } + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task ReadAllTextAsync( + string path, + CancellationToken cancellationToken = default) + => ReadAllTextAsync(path, Encoding.Default, cancellationToken); + + /// + public Task ReadAllTextAsync( + string path, + Encoding encoding, + CancellationToken cancellationToken = default) + { + ThrowIfCancelled(cancellationToken); + return Task.FromResult(ReadAllText(path, encoding)); + } +#endif + + /// + public IEnumerable ReadLines(string path) + => ReadLines(path, Encoding.Default); + + /// + public IEnumerable ReadLines(string path, Encoding encoding) + => EnumerateLines(ReadAllText(path, encoding)); + +#if FEATURE_FILESYSTEM_NET7 + /// + public IAsyncEnumerable ReadLinesAsync(string path, + CancellationToken cancellationToken = + default) + { + ThrowIfCancelled(cancellationToken); + return ReadAllLines(path).ToAsyncEnumerable(); + } + + /// + public IAsyncEnumerable ReadLinesAsync(string path, Encoding encoding, + CancellationToken cancellationToken = + default) + { + ThrowIfCancelled(cancellationToken); + return ReadAllLines(path, encoding).ToAsyncEnumerable(); + } +#endif + + /// + public void Replace(string sourceFileName, + string destinationFileName, + string? destinationBackupFileName) + => _fileSystem.FileInfo.New(sourceFileName + .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) + .Replace(destinationFileName + .EnsureValidFormat(_fileSystem, nameof(destinationFileName)), + destinationBackupFileName); + + /// + public void Replace(string sourceFileName, + string destinationFileName, + string? destinationBackupFileName, + bool ignoreMetadataErrors) + => _fileSystem.FileInfo.New(sourceFileName + .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) + .Replace(destinationFileName + .EnsureValidFormat(_fileSystem, nameof(destinationFileName)), + destinationBackupFileName, + ignoreMetadataErrors); + +#if FEATURE_FILESYSTEM_LINK + /// + public IFileSystemInfo? ResolveLinkTarget( + string linkPath, bool returnFinalTarget) + { + IStorageLocation location = + _fileSystem.Storage.GetLocation(linkPath + .EnsureValidFormat(_fileSystem, nameof(linkPath))); + Execute.OnWindows( + () => location.ThrowExceptionIfNotFound(_fileSystem), + () => location.ThrowExceptionIfNotFound(_fileSystem, + onDirectoryNotFound: ExceptionFactory.FileNotFound)); + try + { + IStorageLocation? targetLocation = + _fileSystem.Storage.ResolveLinkTarget(location, + returnFinalTarget); + if (targetLocation != null) + { + return FileSystemInfoMock.New(targetLocation, _fileSystem); + } + + return null; + } + catch (IOException) + { + throw ExceptionFactory.FileNameCannotBeResolved(linkPath, + Execute.IsWindows ? -2147022975 : -2146232800); + } + } +#endif + + /// + public void SetAttributes(string path, FileAttributes fileAttributes) + { + IStorageContainer container = GetContainerFromPath(path); + container.Attributes = fileAttributes; + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public void SetAttributes(SafeFileHandle fileHandle, FileAttributes fileAttributes) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.Attributes = fileAttributes; + } +#endif + + /// + public void SetCreationTime(string path, DateTime creationTime) + { + IStorageContainer container = + GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); + container.CreationTime.Set(creationTime, DateTimeKind.Local); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public void SetCreationTime(SafeFileHandle fileHandle, DateTime creationTime) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.CreationTime.Set(creationTime, DateTimeKind.Local); + } +#endif + + /// + public void SetCreationTimeUtc(string path, DateTime creationTimeUtc) + { + IStorageContainer container = + GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); + container.CreationTime.Set(creationTimeUtc, DateTimeKind.Utc); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public void SetCreationTimeUtc(SafeFileHandle fileHandle, DateTime creationTimeUtc) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.CreationTime.Set(creationTimeUtc, DateTimeKind.Utc); + } +#endif + + /// + public void SetLastAccessTime(string path, DateTime lastAccessTime) + { + IStorageContainer container = + GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); + container.LastAccessTime.Set(lastAccessTime, DateTimeKind.Local); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public void SetLastAccessTime(SafeFileHandle fileHandle, DateTime lastAccessTime) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.LastAccessTime.Set(lastAccessTime, DateTimeKind.Local); + } +#endif + + /// + public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) + { + IStorageContainer container = + GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); + container.LastAccessTime.Set(lastAccessTimeUtc, DateTimeKind.Utc); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public void SetLastAccessTimeUtc(SafeFileHandle fileHandle, + DateTime lastAccessTimeUtc) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.LastAccessTime.Set(lastAccessTimeUtc, DateTimeKind.Utc); + } +#endif + + /// + public void SetLastWriteTime(string path, DateTime lastWriteTime) + { + IStorageContainer container = + GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); + container.LastWriteTime.Set(lastWriteTime, DateTimeKind.Local); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public void SetLastWriteTime(SafeFileHandle fileHandle, DateTime lastWriteTime) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.LastWriteTime.Set(lastWriteTime, DateTimeKind.Local); + } +#endif + + /// + public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) + { + IStorageContainer container = + GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); + container.LastWriteTime.Set(lastWriteTimeUtc, DateTimeKind.Utc); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public void SetLastWriteTimeUtc(SafeFileHandle fileHandle, DateTime lastWriteTimeUtc) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.LastWriteTime.Set(lastWriteTimeUtc, DateTimeKind.Utc); + } +#endif + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + [UnsupportedOSPlatform("windows")] + public void SetUnixFileMode(string path, UnixFileMode mode) + { + Execute.OnWindows( + () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform()); + + IStorageContainer container = GetContainerFromPath(path); + container.UnixFileMode = mode; + } +#endif + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + [UnsupportedOSPlatform("windows")] + public void SetUnixFileMode(SafeFileHandle fileHandle, UnixFileMode mode) + { + Execute.OnWindows( + () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform()); + + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.UnixFileMode = mode; + } +#endif + + /// + public void WriteAllBytes(string path, byte[] bytes) + { + _ = bytes ?? throw new ArgumentNullException(nameof(bytes)); + IStorageContainer container = + _fileSystem.Storage.GetOrCreateContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem)), + InMemoryContainer.NewFile); + if (container is not NullContainer) + { + Execute.OnWindowsIf( + container.Attributes.HasFlag(FileAttributes.Hidden), + () => throw ExceptionFactory.AccessToPathDenied()); + using (container.RequestAccess( + FileAccess.Write, + FileStreamFactoryMock.DefaultShare)) + { + container.WriteBytes(bytes); + } + } + } + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task WriteAllBytesAsync(string path, byte[] bytes, + CancellationToken cancellationToken = default) + { + ThrowIfCancelled(cancellationToken); + WriteAllBytes(path, bytes); + return Task.CompletedTask; + } +#endif + + /// + public void WriteAllLines(string path, string[] contents) + => WriteAllLines(path, contents, Encoding.Default); + + /// + public void WriteAllLines(string path, IEnumerable contents) + => WriteAllLines(path, contents, Encoding.Default); + + /// + public void WriteAllLines( + string path, + string[] contents, + Encoding encoding) + => WriteAllLines(path, contents.AsEnumerable(), encoding); + + /// + public void WriteAllLines( + string path, + IEnumerable contents, + Encoding encoding) + => WriteAllText( + path, + contents.Aggregate(string.Empty, (a, b) => a + b + Environment.NewLine), + encoding); + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task WriteAllLinesAsync( + string path, + IEnumerable contents, + CancellationToken cancellationToken = default) + => WriteAllLinesAsync(path, contents, Encoding.Default, cancellationToken); + + /// + public Task WriteAllLinesAsync( + string path, + IEnumerable contents, + Encoding encoding, + CancellationToken cancellationToken = default) + { + ThrowIfCancelled(cancellationToken); + WriteAllLines(path, contents, encoding); + return Task.CompletedTask; + } +#endif + + /// + public void WriteAllText(string path, string? contents) + => WriteAllText(path, contents, Encoding.Default); + + /// + public void WriteAllText(string path, string? contents, Encoding encoding) + { + IStorageContainer container = + _fileSystem.Storage.GetOrCreateContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem)), + InMemoryContainer.NewFile); + if (container is not NullContainer && contents != null) + { + Execute.OnWindowsIf( + container.Attributes.HasFlag(FileAttributes.Hidden), + () => throw ExceptionFactory.AccessToPathDenied()); + using (container.RequestAccess( + FileAccess.Write, + FileStreamFactoryMock.DefaultShare)) + { + container.WriteBytes(encoding.GetPreamble()); + container.AppendBytes(encoding.GetBytes(contents)); + } + } + } + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task WriteAllTextAsync(string path, string? contents, + CancellationToken cancellationToken = default) + => WriteAllTextAsync(path, contents, Encoding.Default, cancellationToken); + + /// + public Task WriteAllTextAsync(string path, string? contents, Encoding encoding, + CancellationToken cancellationToken = default) + { + ThrowIfCancelled(cancellationToken); + WriteAllText(path, contents, encoding); + return Task.CompletedTask; + } +#endif + + #endregion + + private static IEnumerable EnumerateLines(string contents) + { + using (StringReader reader = new(contents)) + { + while (reader.ReadLine() is { } line) + { + yield return line; + } + } + } + + private enum ExceptionMode + { + Default, + FileNotFoundExceptionOnLinuxAndMac + } + + private IStorageContainer GetContainerFromPath(string path, + ExceptionMode exceptionMode = ExceptionMode.Default) + { + path.EnsureValidFormat(FileSystem); + IStorageLocation location = _fileSystem.Storage.GetLocation(path); + if (exceptionMode == ExceptionMode.FileNotFoundExceptionOnLinuxAndMac) + { + Execute.OnWindows( + () => location.ThrowExceptionIfNotFound(_fileSystem), + () => location.ThrowExceptionIfNotFound(_fileSystem, + onDirectoryNotFound: ExceptionFactory.FileNotFound)); + } + + if (exceptionMode == ExceptionMode.Default) + { + location.ThrowExceptionIfNotFound(_fileSystem); + } + + return _fileSystem.Storage.GetContainer(location); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + private IStorageContainer GetContainerFromSafeFileHandle(SafeFileHandle fileHandle) + { + SafeFileHandleMock safeFileHandleMock = _fileSystem + .SafeFileHandleStrategy.MapSafeFileHandle(fileHandle); + IStorageContainer container = _fileSystem.Storage + .GetContainer(_fileSystem.Storage.GetLocation( + safeFileHandleMock.Path) + .ThrowExceptionIfNotFound(_fileSystem)); + if (container is NullContainer) + { + throw ExceptionFactory.FileNotFound(""); + } + + return container; + } +#endif + +#if FEATURE_FILESYSTEM_ASYNC + private static void ThrowIfCancelled(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + throw ExceptionFactory.TaskWasCanceled(); + } + } +#endif +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs index e6c295336..ca16d054a 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs @@ -1,151 +1,150 @@ -using Microsoft.Win32.SafeHandles; -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; -#if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; -#endif - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class FileStreamFactoryMock : IFileStreamFactory -{ - internal const FileShare DefaultShare = FileShare.Read; - private const int DefaultBufferSize = 4096; - private const bool DefaultUseAsync = false; - private readonly MockFileSystem _fileSystem; - - internal FileStreamFactoryMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - #region IFileStreamFactory Members - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public FileSystemStream New(string path, FileMode mode) - => New(path, - mode, - mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, - DefaultShare, - DefaultBufferSize, - DefaultUseAsync); - - /// - public FileSystemStream New(string path, FileMode mode, FileAccess access) - => New(path, mode, access, DefaultShare, DefaultBufferSize, DefaultUseAsync); - - /// - public FileSystemStream New(string path, - FileMode mode, - FileAccess access, - FileShare share) - => New(path, mode, access, share, DefaultBufferSize, DefaultUseAsync); - - /// - public FileSystemStream New(string path, - FileMode mode, - FileAccess access, - FileShare share, - int bufferSize) - => New(path, mode, access, share, bufferSize, DefaultUseAsync); - - /// - public FileSystemStream New(string path, - FileMode mode, - FileAccess access, - FileShare share, - int bufferSize, - bool useAsync) - => New(path, - mode, - access, - share, - bufferSize, - useAsync ? FileOptions.Asynchronous : FileOptions.None); - - /// - public FileSystemStream New(string path, - FileMode mode, - FileAccess access, - FileShare share, - int bufferSize, - FileOptions options) - => new FileStreamMock(_fileSystem, - path, - mode, - access, - share, - bufferSize, - options); - - /// -#if NET6_0_OR_GREATER - [ExcludeFromCodeCoverage(Justification = "SafeFileHandle cannot be unit tested.")] -#endif - public FileSystemStream New(SafeFileHandle handle, FileAccess access) - { - SafeFileHandleMock safeFileHandleMock = _fileSystem - .SafeFileHandleStrategy.MapSafeFileHandle(handle); - return New( - safeFileHandleMock.Path, - safeFileHandleMock.Mode, - access, - safeFileHandleMock.Share); - } - - /// -#if NET6_0_OR_GREATER - [ExcludeFromCodeCoverage(Justification = "SafeFileHandle cannot be unit tested.")] -#endif - public FileSystemStream New(SafeFileHandle handle, FileAccess access, int bufferSize) - { - SafeFileHandleMock safeFileHandleMock = _fileSystem - .SafeFileHandleStrategy.MapSafeFileHandle(handle); - return New( - safeFileHandleMock.Path, - safeFileHandleMock.Mode, - access, - safeFileHandleMock.Share, - bufferSize); - } - - /// -#if NET6_0_OR_GREATER - [ExcludeFromCodeCoverage(Justification = "SafeFileHandle cannot be unit tested.")] -#endif - public FileSystemStream New(SafeFileHandle handle, FileAccess access, int bufferSize, - bool isAsync) - { - SafeFileHandleMock safeFileHandleMock = _fileSystem - .SafeFileHandleStrategy.MapSafeFileHandle(handle); - return New( - safeFileHandleMock.Path, - safeFileHandleMock.Mode, - access, - safeFileHandleMock.Share, - bufferSize, - isAsync); - } - -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - /// - public FileSystemStream New(string path, FileStreamOptions options) - => New(path, - options.Mode, - options.Access, - options.Share, - options.BufferSize, - options.Options); -#endif - - /// - public FileSystemStream Wrap(FileStream fileStream) - => throw ExceptionFactory.NotSupportedFileStreamWrapping(); - - #endregion -} +using Microsoft.Win32.SafeHandles; +using System.IO; +using Testably.Abstractions.Testing.Helpers; +#if NET6_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class FileStreamFactoryMock : IFileStreamFactory +{ + internal const FileShare DefaultShare = FileShare.Read; + private const int DefaultBufferSize = 4096; + private const bool DefaultUseAsync = false; + private readonly MockFileSystem _fileSystem; + + internal FileStreamFactoryMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + #region IFileStreamFactory Members + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public FileSystemStream New(string path, FileMode mode) + => New(path, + mode, + mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, + DefaultShare, + DefaultBufferSize, + DefaultUseAsync); + + /// + public FileSystemStream New(string path, FileMode mode, FileAccess access) + => New(path, mode, access, DefaultShare, DefaultBufferSize, DefaultUseAsync); + + /// + public FileSystemStream New(string path, + FileMode mode, + FileAccess access, + FileShare share) + => New(path, mode, access, share, DefaultBufferSize, DefaultUseAsync); + + /// + public FileSystemStream New(string path, + FileMode mode, + FileAccess access, + FileShare share, + int bufferSize) + => New(path, mode, access, share, bufferSize, DefaultUseAsync); + + /// + public FileSystemStream New(string path, + FileMode mode, + FileAccess access, + FileShare share, + int bufferSize, + bool useAsync) + => New(path, + mode, + access, + share, + bufferSize, + useAsync ? FileOptions.Asynchronous : FileOptions.None); + + /// + public FileSystemStream New(string path, + FileMode mode, + FileAccess access, + FileShare share, + int bufferSize, + FileOptions options) + => new FileStreamMock(_fileSystem, + path, + mode, + access, + share, + bufferSize, + options); + + /// +#if NET6_0_OR_GREATER + [ExcludeFromCodeCoverage(Justification = "SafeFileHandle cannot be unit tested.")] +#endif + public FileSystemStream New(SafeFileHandle handle, FileAccess access) + { + SafeFileHandleMock safeFileHandleMock = _fileSystem + .SafeFileHandleStrategy.MapSafeFileHandle(handle); + return New( + safeFileHandleMock.Path, + safeFileHandleMock.Mode, + access, + safeFileHandleMock.Share); + } + + /// +#if NET6_0_OR_GREATER + [ExcludeFromCodeCoverage(Justification = "SafeFileHandle cannot be unit tested.")] +#endif + public FileSystemStream New(SafeFileHandle handle, FileAccess access, int bufferSize) + { + SafeFileHandleMock safeFileHandleMock = _fileSystem + .SafeFileHandleStrategy.MapSafeFileHandle(handle); + return New( + safeFileHandleMock.Path, + safeFileHandleMock.Mode, + access, + safeFileHandleMock.Share, + bufferSize); + } + + /// +#if NET6_0_OR_GREATER + [ExcludeFromCodeCoverage(Justification = "SafeFileHandle cannot be unit tested.")] +#endif + public FileSystemStream New(SafeFileHandle handle, FileAccess access, int bufferSize, + bool isAsync) + { + SafeFileHandleMock safeFileHandleMock = _fileSystem + .SafeFileHandleStrategy.MapSafeFileHandle(handle); + return New( + safeFileHandleMock.Path, + safeFileHandleMock.Mode, + access, + safeFileHandleMock.Share, + bufferSize, + isAsync); + } + +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + /// + public FileSystemStream New(string path, FileStreamOptions options) + => New(path, + options.Mode, + options.Access, + options.Share, + options.BufferSize, + options.Options); +#endif + + /// + public FileSystemStream Wrap(FileStream fileStream) + => throw ExceptionFactory.NotSupportedFileStreamWrapping(); + + #endregion +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs index aeb375777..54ac14310 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs @@ -1,448 +1,447 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// A mocked file stream in the . -/// -internal sealed class FileStreamMock : FileSystemStream -{ - /// - public override bool CanRead - => _access.HasFlag(FileAccess.Read); - - /// - public override bool CanWrite - => _access.HasFlag(FileAccess.Write); - - /// - public override IFileSystemExtensibility Extensibility - => _container.Extensibility; - - private readonly FileAccess _access; - private readonly IDisposable _accessLock; - private readonly IStorageContainer _container; - private readonly MockFileSystem _fileSystem; - private readonly long _initialPosition; - private bool _isContentChanged; - private bool _isDisposed; - private readonly FileMode _mode; - private readonly FileOptions _options; - private readonly MemoryStream _stream; - - internal FileStreamMock(MockFileSystem fileSystem, - string? path, - FileMode mode, - FileAccess access, - FileShare share = FileShare.Read, - int bufferSize = 4096, - FileOptions options = FileOptions.None) - : this(new MemoryStream(), - fileSystem, - path.EnsureValidFormat(fileSystem, nameof(path)), - mode, - access, - share, - bufferSize, - options) - { - } - - private FileStreamMock(MemoryStream stream, - MockFileSystem fileSystem, - string? path, - FileMode mode, - FileAccess access, - FileShare share, - int bufferSize, - FileOptions options) - : base( - stream, - path == null ? null : fileSystem.Path.GetFullPath(path), - (options & FileOptions.Asynchronous) != 0) - { - ThrowIfInvalidModeAccess(mode, access); - - _stream = stream; - _fileSystem = fileSystem; - _mode = mode; - _access = access; - _ = bufferSize; - _options = options; - _initialPosition = Position; - - IStorageLocation location = _fileSystem.Storage.GetLocation(Name); - location.ThrowExceptionIfNotFound(_fileSystem, true); - IStorageContainer file = _fileSystem.Storage.GetContainer(location); - if (file is NullContainer) - { - if (_mode.Equals(FileMode.Open) || - _mode.Equals(FileMode.Truncate)) - { - throw ExceptionFactory.FileNotFound( - _fileSystem.Path.GetFullPath(Name)); - } - - file = _fileSystem.Storage.GetOrCreateContainer(location, - InMemoryContainer.NewFile); - } - else if (file.Type == FileSystemTypes.Directory) - { - Execute.OnWindows( - () => - throw ExceptionFactory.AccessToPathDenied( - _fileSystem.Path.GetFullPath(Name)), - () => - throw ExceptionFactory.FileAlreadyExists( - _fileSystem.Path.GetFullPath(Name), 17)); - } - else if (_mode.Equals(FileMode.CreateNew)) - { - throw ExceptionFactory.FileAlreadyExists( - _fileSystem.Path.GetFullPath(Name), - Execute.IsWindows ? -2147024816 : 17); - } - - if (file.Attributes.HasFlag(FileAttributes.ReadOnly) && - access.HasFlag(FileAccess.Write)) - { - throw ExceptionFactory.AccessToPathDenied(location.FullPath); - } - - _accessLock = file.RequestAccess(access, share); - - _container = file; - - InitializeStream(); - } - - /// - public override IAsyncResult BeginRead(byte[] buffer, - int offset, - int count, - AsyncCallback? callback, - object? state) - { - ThrowIfDisposed(); - if (!CanRead) - { - throw ExceptionFactory.StreamDoesNotSupportReading(); - } - - return base.BeginRead(buffer, offset, count, callback, state); - } - - /// - public override IAsyncResult BeginWrite(byte[] buffer, - int offset, - int count, - AsyncCallback? callback, - object? state) - { - ThrowIfDisposed(); - if (!CanWrite) - { - throw ExceptionFactory.StreamDoesNotSupportWriting(); - } - - return base.BeginWrite(buffer, offset, count, callback, state); - } - - /// - public override void CopyTo(Stream destination, int bufferSize) - { - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - base.CopyTo(destination, bufferSize); - } - - /// - public override Task CopyToAsync(Stream destination, int bufferSize, - CancellationToken cancellationToken) - { - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - return base.CopyToAsync(destination, bufferSize, cancellationToken); - } - - /// - public override int EndRead(IAsyncResult asyncResult) - { - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - return base.EndRead(asyncResult); - } - - /// - public override void EndWrite(IAsyncResult asyncResult) - { - _isContentChanged = true; - base.EndWrite(asyncResult); - } - - /// - public override void Flush() - { - ThrowIfDisposed(); - InternalFlush(); - } - - /// - public override Task FlushAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - throw ExceptionFactory.TaskWasCanceled(); - } - - Flush(); - return Task.CompletedTask; - } - - /// - public override int Read(byte[] buffer, int offset, int count) - { - if (!CanRead) - { - throw ExceptionFactory.StreamDoesNotSupportReading(); - } - - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - return base.Read(buffer, offset, count); - } - -#if FEATURE_SPAN - /// - public override int Read(Span buffer) - { - if (!CanRead) - { - throw ExceptionFactory.StreamDoesNotSupportReading(); - } - - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - return base.Read(buffer); - } -#endif - - /// - public override Task ReadAsync(byte[] buffer, int offset, int count, - CancellationToken cancellationToken) - { - if (!CanRead) - { - throw ExceptionFactory.StreamDoesNotSupportReading(); - } - - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - return base.ReadAsync(buffer, offset, count, cancellationToken); - } - -#if FEATURE_SPAN - /// - public override ValueTask ReadAsync(Memory buffer, - CancellationToken cancellationToken = - new()) - { - if (!CanRead) - { - throw ExceptionFactory.StreamDoesNotSupportReading(); - } - - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - return base.ReadAsync(buffer, cancellationToken); - } -#endif - - /// - public override int ReadByte() - { - if (!CanRead) - { - throw ExceptionFactory.StreamDoesNotSupportReading(); - } - - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - return base.ReadByte(); - } - - /// - public override long Seek(long offset, SeekOrigin origin) - { - if (_mode == FileMode.Append && offset <= _initialPosition) - { - throw ExceptionFactory.SeekBackwardNotPossibleInAppendMode(); - } - - return base.Seek(offset, origin); - } - - /// - public override void SetLength(long value) - { - ThrowIfDisposed(); - if (!CanWrite) - { - throw ExceptionFactory.StreamDoesNotSupportWriting(); - } - - base.SetLength(value); - } - - /// - public override void Write(byte[] buffer, int offset, int count) - { - if (!CanWrite) - { - throw ExceptionFactory.StreamDoesNotSupportWriting(); - } - - _isContentChanged = true; - base.Write(buffer, offset, count); - } - -#if FEATURE_SPAN - /// - public override void Write(ReadOnlySpan buffer) - { - if (!CanWrite) - { - throw ExceptionFactory.StreamDoesNotSupportWriting(); - } - - _isContentChanged = true; - base.Write(buffer); - } -#endif - - /// - public override Task WriteAsync(byte[] buffer, int offset, int count, - CancellationToken cancellationToken) - { - if (!CanWrite) - { - throw ExceptionFactory.StreamDoesNotSupportWriting(); - } - - _isContentChanged = true; - return base.WriteAsync(buffer, offset, count, cancellationToken); - } - -#if FEATURE_SPAN - /// - public override ValueTask WriteAsync(ReadOnlyMemory buffer, - CancellationToken cancellationToken = new()) - { - if (!CanWrite) - { - throw ExceptionFactory.StreamDoesNotSupportWriting(); - } - - _isContentChanged = true; - return base.WriteAsync(buffer, cancellationToken); - } -#endif - - /// - public override void WriteByte(byte value) - { - if (!CanWrite) - { - throw ExceptionFactory.StreamDoesNotSupportWriting(); - } - - _isContentChanged = true; - base.WriteByte(value); - } - - /// - protected override void Dispose(bool disposing) - { - if (_isDisposed) - { - return; - } - - _accessLock.Dispose(); - InternalFlush(); - base.Dispose(disposing); - OnClose(); - _isDisposed = true; - } - - private void InitializeStream() - { - if (_mode != FileMode.Create && - _mode != FileMode.Truncate) - { - byte[] existingContents = _container.GetBytes(); - _stream.Write(existingContents, 0, existingContents.Length); - _stream.Seek(0, _mode == FileMode.Append - ? SeekOrigin.End - : SeekOrigin.Begin); - } - else - { - _isContentChanged = true; - } - } - - private void ThrowIfDisposed() - { - if (_isDisposed) - { - throw new ObjectDisposedException("", "Cannot access a closed file."); - } - } - - private void InternalFlush() - { - if (!_isContentChanged) - { - return; - } - - _isContentChanged = false; - long position = _stream.Position; - _stream.Seek(0, SeekOrigin.Begin); - byte[] data = new byte[Length]; - _ = _stream.Read(data, 0, (int)Length); - _stream.Seek(position, SeekOrigin.Begin); - _container.WriteBytes(data); - } - - private void OnClose() - { - if (_options.HasFlag(FileOptions.DeleteOnClose)) - { - _fileSystem.Storage.DeleteContainer( - _fileSystem.Storage.GetLocation(Name)); - } - } - - private static void ThrowIfInvalidModeAccess(FileMode mode, FileAccess access) - { - if (mode == FileMode.Append) - { - if (access == FileAccess.Read) - { - throw ExceptionFactory.InvalidAccessCombination(mode, access); - } - - if (access != FileAccess.Write) - { - throw ExceptionFactory.AppendAccessOnlyInWriteOnlyMode(); - } - } - - if (!access.HasFlag(FileAccess.Write) && - (mode == FileMode.Truncate || mode == FileMode.CreateNew || - mode == FileMode.Create || mode == FileMode.Append)) - { - throw ExceptionFactory.InvalidAccessCombination(mode, access); - } - } -} +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// A mocked file stream in the . +/// +internal sealed class FileStreamMock : FileSystemStream +{ + /// + public override bool CanRead + => _access.HasFlag(FileAccess.Read); + + /// + public override bool CanWrite + => _access.HasFlag(FileAccess.Write); + + /// + public override IFileSystemExtensibility Extensibility + => _container.Extensibility; + + private readonly FileAccess _access; + private readonly IDisposable _accessLock; + private readonly IStorageContainer _container; + private readonly MockFileSystem _fileSystem; + private readonly long _initialPosition; + private bool _isContentChanged; + private bool _isDisposed; + private readonly FileMode _mode; + private readonly FileOptions _options; + private readonly MemoryStream _stream; + + internal FileStreamMock(MockFileSystem fileSystem, + string? path, + FileMode mode, + FileAccess access, + FileShare share = FileShare.Read, + int bufferSize = 4096, + FileOptions options = FileOptions.None) + : this(new MemoryStream(), + fileSystem, + path.EnsureValidFormat(fileSystem, nameof(path)), + mode, + access, + share, + bufferSize, + options) + { + } + + private FileStreamMock(MemoryStream stream, + MockFileSystem fileSystem, + string? path, + FileMode mode, + FileAccess access, + FileShare share, + int bufferSize, + FileOptions options) + : base( + stream, + path == null ? null : fileSystem.Path.GetFullPath(path), + (options & FileOptions.Asynchronous) != 0) + { + ThrowIfInvalidModeAccess(mode, access); + + _stream = stream; + _fileSystem = fileSystem; + _mode = mode; + _access = access; + _ = bufferSize; + _options = options; + _initialPosition = Position; + + IStorageLocation location = _fileSystem.Storage.GetLocation(Name); + location.ThrowExceptionIfNotFound(_fileSystem, true); + IStorageContainer file = _fileSystem.Storage.GetContainer(location); + if (file is NullContainer) + { + if (_mode.Equals(FileMode.Open) || + _mode.Equals(FileMode.Truncate)) + { + throw ExceptionFactory.FileNotFound( + _fileSystem.Path.GetFullPath(Name)); + } + + file = _fileSystem.Storage.GetOrCreateContainer(location, + InMemoryContainer.NewFile); + } + else if (file.Type == FileSystemTypes.Directory) + { + Execute.OnWindows( + () => + throw ExceptionFactory.AccessToPathDenied( + _fileSystem.Path.GetFullPath(Name)), + () => + throw ExceptionFactory.FileAlreadyExists( + _fileSystem.Path.GetFullPath(Name), 17)); + } + else if (_mode.Equals(FileMode.CreateNew)) + { + throw ExceptionFactory.FileAlreadyExists( + _fileSystem.Path.GetFullPath(Name), + Execute.IsWindows ? -2147024816 : 17); + } + + if (file.Attributes.HasFlag(FileAttributes.ReadOnly) && + access.HasFlag(FileAccess.Write)) + { + throw ExceptionFactory.AccessToPathDenied(location.FullPath); + } + + _accessLock = file.RequestAccess(access, share); + + _container = file; + + InitializeStream(); + } + + /// + public override IAsyncResult BeginRead(byte[] buffer, + int offset, + int count, + AsyncCallback? callback, + object? state) + { + ThrowIfDisposed(); + if (!CanRead) + { + throw ExceptionFactory.StreamDoesNotSupportReading(); + } + + return base.BeginRead(buffer, offset, count, callback, state); + } + + /// + public override IAsyncResult BeginWrite(byte[] buffer, + int offset, + int count, + AsyncCallback? callback, + object? state) + { + ThrowIfDisposed(); + if (!CanWrite) + { + throw ExceptionFactory.StreamDoesNotSupportWriting(); + } + + return base.BeginWrite(buffer, offset, count, callback, state); + } + + /// + public override void CopyTo(Stream destination, int bufferSize) + { + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + base.CopyTo(destination, bufferSize); + } + + /// + public override Task CopyToAsync(Stream destination, int bufferSize, + CancellationToken cancellationToken) + { + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + return base.CopyToAsync(destination, bufferSize, cancellationToken); + } + + /// + public override int EndRead(IAsyncResult asyncResult) + { + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + return base.EndRead(asyncResult); + } + + /// + public override void EndWrite(IAsyncResult asyncResult) + { + _isContentChanged = true; + base.EndWrite(asyncResult); + } + + /// + public override void Flush() + { + ThrowIfDisposed(); + InternalFlush(); + } + + /// + public override Task FlushAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + throw ExceptionFactory.TaskWasCanceled(); + } + + Flush(); + return Task.CompletedTask; + } + + /// + public override int Read(byte[] buffer, int offset, int count) + { + if (!CanRead) + { + throw ExceptionFactory.StreamDoesNotSupportReading(); + } + + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + return base.Read(buffer, offset, count); + } + +#if FEATURE_SPAN + /// + public override int Read(Span buffer) + { + if (!CanRead) + { + throw ExceptionFactory.StreamDoesNotSupportReading(); + } + + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + return base.Read(buffer); + } +#endif + + /// + public override Task ReadAsync(byte[] buffer, int offset, int count, + CancellationToken cancellationToken) + { + if (!CanRead) + { + throw ExceptionFactory.StreamDoesNotSupportReading(); + } + + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + return base.ReadAsync(buffer, offset, count, cancellationToken); + } + +#if FEATURE_SPAN + /// + public override ValueTask ReadAsync(Memory buffer, + CancellationToken cancellationToken = + new()) + { + if (!CanRead) + { + throw ExceptionFactory.StreamDoesNotSupportReading(); + } + + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + return base.ReadAsync(buffer, cancellationToken); + } +#endif + + /// + public override int ReadByte() + { + if (!CanRead) + { + throw ExceptionFactory.StreamDoesNotSupportReading(); + } + + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + return base.ReadByte(); + } + + /// + public override long Seek(long offset, SeekOrigin origin) + { + if (_mode == FileMode.Append && offset <= _initialPosition) + { + throw ExceptionFactory.SeekBackwardNotPossibleInAppendMode(); + } + + return base.Seek(offset, origin); + } + + /// + public override void SetLength(long value) + { + ThrowIfDisposed(); + if (!CanWrite) + { + throw ExceptionFactory.StreamDoesNotSupportWriting(); + } + + base.SetLength(value); + } + + /// + public override void Write(byte[] buffer, int offset, int count) + { + if (!CanWrite) + { + throw ExceptionFactory.StreamDoesNotSupportWriting(); + } + + _isContentChanged = true; + base.Write(buffer, offset, count); + } + +#if FEATURE_SPAN + /// + public override void Write(ReadOnlySpan buffer) + { + if (!CanWrite) + { + throw ExceptionFactory.StreamDoesNotSupportWriting(); + } + + _isContentChanged = true; + base.Write(buffer); + } +#endif + + /// + public override Task WriteAsync(byte[] buffer, int offset, int count, + CancellationToken cancellationToken) + { + if (!CanWrite) + { + throw ExceptionFactory.StreamDoesNotSupportWriting(); + } + + _isContentChanged = true; + return base.WriteAsync(buffer, offset, count, cancellationToken); + } + +#if FEATURE_SPAN + /// + public override ValueTask WriteAsync(ReadOnlyMemory buffer, + CancellationToken cancellationToken = new()) + { + if (!CanWrite) + { + throw ExceptionFactory.StreamDoesNotSupportWriting(); + } + + _isContentChanged = true; + return base.WriteAsync(buffer, cancellationToken); + } +#endif + + /// + public override void WriteByte(byte value) + { + if (!CanWrite) + { + throw ExceptionFactory.StreamDoesNotSupportWriting(); + } + + _isContentChanged = true; + base.WriteByte(value); + } + + /// + protected override void Dispose(bool disposing) + { + if (_isDisposed) + { + return; + } + + _accessLock.Dispose(); + InternalFlush(); + base.Dispose(disposing); + OnClose(); + _isDisposed = true; + } + + private void InitializeStream() + { + if (_mode != FileMode.Create && + _mode != FileMode.Truncate) + { + byte[] existingContents = _container.GetBytes(); + _stream.Write(existingContents, 0, existingContents.Length); + _stream.Seek(0, _mode == FileMode.Append + ? SeekOrigin.End + : SeekOrigin.Begin); + } + else + { + _isContentChanged = true; + } + } + + private void ThrowIfDisposed() + { + if (_isDisposed) + { + throw new ObjectDisposedException("", "Cannot access a closed file."); + } + } + + private void InternalFlush() + { + if (!_isContentChanged) + { + return; + } + + _isContentChanged = false; + long position = _stream.Position; + _stream.Seek(0, SeekOrigin.Begin); + byte[] data = new byte[Length]; + _ = _stream.Read(data, 0, (int)Length); + _stream.Seek(position, SeekOrigin.Begin); + _container.WriteBytes(data); + } + + private void OnClose() + { + if (_options.HasFlag(FileOptions.DeleteOnClose)) + { + _fileSystem.Storage.DeleteContainer( + _fileSystem.Storage.GetLocation(Name)); + } + } + + private static void ThrowIfInvalidModeAccess(FileMode mode, FileAccess access) + { + if (mode == FileMode.Append) + { + if (access == FileAccess.Read) + { + throw ExceptionFactory.InvalidAccessCombination(mode, access); + } + + if (access != FileAccess.Write) + { + throw ExceptionFactory.AppendAccessOnlyInWriteOnlyMode(); + } + } + + if (!access.HasFlag(FileAccess.Write) && + (mode == FileMode.Truncate || mode == FileMode.CreateNew || + mode == FileMode.Create || mode == FileMode.Append)) + { + throw ExceptionFactory.InvalidAccessCombination(mode, access); + } + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs index acebe9cb0..f15281ff9 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs @@ -1,265 +1,264 @@ -using System; -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -internal class FileSystemInfoMock : IFileSystemInfo -{ - protected FileSystemTypes FileSystemType { get; } - protected IStorageLocation Location; - protected readonly MockFileSystem FileSystem; - - protected IStorageContainer Container - { - get - { - if (_container is NullContainer) - { - RefreshInternal(); - } - - return _container; - } - set => _container = value; - } - - private bool? _exists; - private bool _isInitialized; - private IStorageContainer _container; - - protected FileSystemInfoMock(MockFileSystem fileSystem, IStorageLocation location, - FileSystemTypes fileSystemType) - { - FileSystem = fileSystem; - Location = location; - _container = fileSystem.Storage.GetContainer(location); - FileSystemType = _container is not NullContainer - ? _container.Type - : fileSystemType; - } - - #region IFileSystemInfo Members - - /// - public FileAttributes Attributes - { - get => Container.Attributes; - set => Container.Attributes = value; - } - -#if FEATURE_FILESYSTEM_LINK - /// - public void CreateAsSymbolicLink(string pathToTarget) - { - FullName.EnsureValidFormat(FileSystem); - pathToTarget.ThrowCommonExceptionsIfPathToTargetIsInvalid(FileSystem); - if (FileSystem.Storage.TryAddContainer(Location, InMemoryContainer.NewFile, - out IStorageContainer? container)) - { - Container = container; - container.LinkTarget = pathToTarget; - } - else - { - throw ExceptionFactory.CannotCreateFileAsAlreadyExists(Location - .FriendlyName); - } - } -#endif - - /// - public DateTime CreationTime - { - get => Container.CreationTime.Get(DateTimeKind.Local); - set => Container.CreationTime.Set(value, DateTimeKind.Local); - } - - /// - public DateTime CreationTimeUtc - { - get => Container.CreationTime.Get(DateTimeKind.Utc); - set => Container.CreationTime.Set(value, DateTimeKind.Utc); - } - - /// - public virtual void Delete() - { - FileSystem.Storage.DeleteContainer(Location); - ResetCache(!Execute.IsNetFramework); - } - - /// - public virtual bool Exists - { - get - { - RefreshInternal(); - _exists ??= !string.IsNullOrWhiteSpace(Location.FriendlyName) && - Container is not NullContainer; - return _exists.Value; - } - } - - /// - public string Extension - { - get - { - if (Location.FullPath.EndsWith(".") && - !Execute.IsWindows) - { - return "."; - } - - return FileSystem.Path.GetExtension(Location.FullPath); - } - } - - /// - public IFileSystemExtensibility Extensibility - => Container.Extensibility; - - /// - public string FullName => Location.FullPath; - - /// - public DateTime LastAccessTime - { - get => Container.LastAccessTime.Get(DateTimeKind.Local); - set => Container.LastAccessTime.Set(value, DateTimeKind.Local); - } - - /// - public DateTime LastAccessTimeUtc - { - get => Container.LastAccessTime.Get(DateTimeKind.Utc); - set => Container.LastAccessTime.Set(value, DateTimeKind.Utc); - } - - /// - public DateTime LastWriteTime - { - get => Container.LastWriteTime.Get(DateTimeKind.Local); - set => Container.LastWriteTime.Set(value, DateTimeKind.Local); - } - - /// - public DateTime LastWriteTimeUtc - { - get => Container.LastWriteTime.Get(DateTimeKind.Utc); - set => Container.LastWriteTime.Set(value, DateTimeKind.Utc); - } - -#if FEATURE_FILESYSTEM_LINK - /// - public string? LinkTarget - => Container.LinkTarget; -#endif - - /// - public string Name - => FileSystem.Path.GetPathRoot(Location.FullPath) == Location.FullPath - ? Location.FullPath - : FileSystem.Path.GetFileName(Location.FullPath.TrimEnd( - FileSystem.Path.DirectorySeparatorChar, - FileSystem.Path.AltDirectorySeparatorChar)); - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - public UnixFileMode UnixFileMode - { - get => Container.UnixFileMode; - [UnsupportedOSPlatform("windows")] - set - { - Execute.OnWindows( - () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform()); - - Container.UnixFileMode = value; - } - } -#endif - - /// - public void Refresh() - { - ResetCache(true); - } - -#if FEATURE_FILESYSTEM_LINK - /// - public IFileSystemInfo? ResolveLinkTarget(bool returnFinalTarget) - { - try - { - IStorageLocation? targetLocation = - FileSystem.Storage.ResolveLinkTarget( - Location, - returnFinalTarget); - if (targetLocation != null) - { - return New(targetLocation, FileSystem); - } - - return null; - } - catch (IOException ex) when (ex.HResult != -2147024773) - { - throw ExceptionFactory.FileNameCannotBeResolved(Location.FullPath, - Execute.IsWindows ? -2147022975 : -2146232800); - } - } -#endif - - #endregion - -#if NETSTANDARD2_0 - /// -#else - /// -#endif - public override string ToString() - => Location.FriendlyName; - - internal static FileSystemInfoMock New(IStorageLocation location, - MockFileSystem fileSystem) - { - IStorageContainer container = fileSystem.Storage.GetContainer(location); - if (container.Type == FileSystemTypes.File) - { - return FileInfoMock.New(location, fileSystem); - } - - if (container.Type == FileSystemTypes.Directory) - { - return DirectoryInfoMock.New(location, fileSystem); - } - - return new FileSystemInfoMock(fileSystem, location, - FileSystemTypes.DirectoryOrFile); - } - - protected void ResetCache(bool resetExistsCache) - { - if (resetExistsCache) - { - _exists = null; - } - - _isInitialized = false; - } - - private void RefreshInternal() - { - if (_isInitialized) - { - return; - } - - Container = FileSystem.Storage.GetContainer(Location); - _isInitialized = true; - } -} +using System; +using System.IO; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +internal class FileSystemInfoMock : IFileSystemInfo +{ + protected FileSystemTypes FileSystemType { get; } + protected IStorageLocation Location; + protected readonly MockFileSystem FileSystem; + + protected IStorageContainer Container + { + get + { + if (_container is NullContainer) + { + RefreshInternal(); + } + + return _container; + } + set => _container = value; + } + + private bool? _exists; + private bool _isInitialized; + private IStorageContainer _container; + + protected FileSystemInfoMock(MockFileSystem fileSystem, IStorageLocation location, + FileSystemTypes fileSystemType) + { + FileSystem = fileSystem; + Location = location; + _container = fileSystem.Storage.GetContainer(location); + FileSystemType = _container is not NullContainer + ? _container.Type + : fileSystemType; + } + + #region IFileSystemInfo Members + + /// + public FileAttributes Attributes + { + get => Container.Attributes; + set => Container.Attributes = value; + } + +#if FEATURE_FILESYSTEM_LINK + /// + public void CreateAsSymbolicLink(string pathToTarget) + { + FullName.EnsureValidFormat(FileSystem); + pathToTarget.ThrowCommonExceptionsIfPathToTargetIsInvalid(FileSystem); + if (FileSystem.Storage.TryAddContainer(Location, InMemoryContainer.NewFile, + out IStorageContainer? container)) + { + Container = container; + container.LinkTarget = pathToTarget; + } + else + { + throw ExceptionFactory.CannotCreateFileAsAlreadyExists(Location + .FriendlyName); + } + } +#endif + + /// + public DateTime CreationTime + { + get => Container.CreationTime.Get(DateTimeKind.Local); + set => Container.CreationTime.Set(value, DateTimeKind.Local); + } + + /// + public DateTime CreationTimeUtc + { + get => Container.CreationTime.Get(DateTimeKind.Utc); + set => Container.CreationTime.Set(value, DateTimeKind.Utc); + } + + /// + public virtual void Delete() + { + FileSystem.Storage.DeleteContainer(Location); + ResetCache(!Execute.IsNetFramework); + } + + /// + public virtual bool Exists + { + get + { + RefreshInternal(); + _exists ??= !string.IsNullOrWhiteSpace(Location.FriendlyName) && + Container is not NullContainer; + return _exists.Value; + } + } + + /// + public string Extension + { + get + { + if (Location.FullPath.EndsWith(".") && + !Execute.IsWindows) + { + return "."; + } + + return FileSystem.Path.GetExtension(Location.FullPath); + } + } + + /// + public IFileSystemExtensibility Extensibility + => Container.Extensibility; + + /// + public string FullName => Location.FullPath; + + /// + public DateTime LastAccessTime + { + get => Container.LastAccessTime.Get(DateTimeKind.Local); + set => Container.LastAccessTime.Set(value, DateTimeKind.Local); + } + + /// + public DateTime LastAccessTimeUtc + { + get => Container.LastAccessTime.Get(DateTimeKind.Utc); + set => Container.LastAccessTime.Set(value, DateTimeKind.Utc); + } + + /// + public DateTime LastWriteTime + { + get => Container.LastWriteTime.Get(DateTimeKind.Local); + set => Container.LastWriteTime.Set(value, DateTimeKind.Local); + } + + /// + public DateTime LastWriteTimeUtc + { + get => Container.LastWriteTime.Get(DateTimeKind.Utc); + set => Container.LastWriteTime.Set(value, DateTimeKind.Utc); + } + +#if FEATURE_FILESYSTEM_LINK + /// + public string? LinkTarget + => Container.LinkTarget; +#endif + + /// + public string Name + => FileSystem.Path.GetPathRoot(Location.FullPath) == Location.FullPath + ? Location.FullPath + : FileSystem.Path.GetFileName(Location.FullPath.TrimEnd( + FileSystem.Path.DirectorySeparatorChar, + FileSystem.Path.AltDirectorySeparatorChar)); + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + public UnixFileMode UnixFileMode + { + get => Container.UnixFileMode; + [UnsupportedOSPlatform("windows")] + set + { + Execute.OnWindows( + () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform()); + + Container.UnixFileMode = value; + } + } +#endif + + /// + public void Refresh() + { + ResetCache(true); + } + +#if FEATURE_FILESYSTEM_LINK + /// + public IFileSystemInfo? ResolveLinkTarget(bool returnFinalTarget) + { + try + { + IStorageLocation? targetLocation = + FileSystem.Storage.ResolveLinkTarget( + Location, + returnFinalTarget); + if (targetLocation != null) + { + return New(targetLocation, FileSystem); + } + + return null; + } + catch (IOException ex) when (ex.HResult != -2147024773) + { + throw ExceptionFactory.FileNameCannotBeResolved(Location.FullPath, + Execute.IsWindows ? -2147022975 : -2146232800); + } + } +#endif + + #endregion + +#if NETSTANDARD2_0 + /// +#else + /// +#endif + public override string ToString() + => Location.FriendlyName; + + internal static FileSystemInfoMock New(IStorageLocation location, + MockFileSystem fileSystem) + { + IStorageContainer container = fileSystem.Storage.GetContainer(location); + if (container.Type == FileSystemTypes.File) + { + return FileInfoMock.New(location, fileSystem); + } + + if (container.Type == FileSystemTypes.Directory) + { + return DirectoryInfoMock.New(location, fileSystem); + } + + return new FileSystemInfoMock(fileSystem, location, + FileSystemTypes.DirectoryOrFile); + } + + protected void ResetCache(bool resetExistsCache) + { + if (resetExistsCache) + { + _exists = null; + } + + _isInitialized = false; + } + + private void RefreshInternal() + { + if (_isInitialized) + { + return; + } + + Container = FileSystem.Storage.GetContainer(Location); + _isInitialized = true; + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs index f028a3e10..b4e25e702 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs @@ -1,79 +1,78 @@ -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class - FileSystemWatcherFactoryMock : IFileSystemWatcherFactory -{ - private readonly MockFileSystem _fileSystem; - - internal FileSystemWatcherFactoryMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - #region IFileSystemWatcherFactory Members - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public IFileSystemWatcher New() - => FileSystemWatcherMock.New(_fileSystem); - - /// - public IFileSystemWatcher New(string path) - { - FileSystemWatcherMock fileSystemWatcherMock = - FileSystemWatcherMock.New(_fileSystem); - fileSystemWatcherMock.Path = path.EnsureValidArgument(_fileSystem); - return fileSystemWatcherMock; - } - - /// - public IFileSystemWatcher New(string path, string filter) - { - FileSystemWatcherMock fileSystemWatcherMock = - FileSystemWatcherMock.New(_fileSystem); - fileSystemWatcherMock.Path = path.EnsureValidArgument(_fileSystem); - fileSystemWatcherMock.Filter = filter; - return fileSystemWatcherMock; - } - - /// - [return: NotNullIfNotNull("fileSystemWatcher")] - // ReSharper disable once ReturnTypeCanBeNotNullable - public IFileSystemWatcher? Wrap(FileSystemWatcher? fileSystemWatcher) - { - if (fileSystemWatcher == null) - { - return null; - } - - FileSystemWatcherMock fileSystemWatcherMock = - FileSystemWatcherMock.New(_fileSystem); - fileSystemWatcherMock.Path = fileSystemWatcher.Path; -#if FEATURE_FILESYSTEMWATCHER_ADVANCED - foreach (string filter in fileSystemWatcher.Filters) - { - fileSystemWatcherMock.Filters.Add(filter); - } -#else - fileSystemWatcherMock.Filter = fileSystemWatcher.Filter; -#endif - fileSystemWatcherMock.NotifyFilter = fileSystemWatcher.NotifyFilter; - fileSystemWatcherMock.IncludeSubdirectories = - fileSystemWatcher.IncludeSubdirectories; - fileSystemWatcherMock.InternalBufferSize = - fileSystemWatcher.InternalBufferSize; - fileSystemWatcherMock.EnableRaisingEvents = - fileSystemWatcher.EnableRaisingEvents; - return fileSystemWatcherMock; - } - - #endregion -} +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class + FileSystemWatcherFactoryMock : IFileSystemWatcherFactory +{ + private readonly MockFileSystem _fileSystem; + + internal FileSystemWatcherFactoryMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + #region IFileSystemWatcherFactory Members + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public IFileSystemWatcher New() + => FileSystemWatcherMock.New(_fileSystem); + + /// + public IFileSystemWatcher New(string path) + { + FileSystemWatcherMock fileSystemWatcherMock = + FileSystemWatcherMock.New(_fileSystem); + fileSystemWatcherMock.Path = path.EnsureValidArgument(_fileSystem); + return fileSystemWatcherMock; + } + + /// + public IFileSystemWatcher New(string path, string filter) + { + FileSystemWatcherMock fileSystemWatcherMock = + FileSystemWatcherMock.New(_fileSystem); + fileSystemWatcherMock.Path = path.EnsureValidArgument(_fileSystem); + fileSystemWatcherMock.Filter = filter; + return fileSystemWatcherMock; + } + + /// + [return: NotNullIfNotNull("fileSystemWatcher")] + // ReSharper disable once ReturnTypeCanBeNotNullable + public IFileSystemWatcher? Wrap(FileSystemWatcher? fileSystemWatcher) + { + if (fileSystemWatcher == null) + { + return null; + } + + FileSystemWatcherMock fileSystemWatcherMock = + FileSystemWatcherMock.New(_fileSystem); + fileSystemWatcherMock.Path = fileSystemWatcher.Path; +#if FEATURE_FILESYSTEMWATCHER_ADVANCED + foreach (string filter in fileSystemWatcher.Filters) + { + fileSystemWatcherMock.Filters.Add(filter); + } +#else + fileSystemWatcherMock.Filter = fileSystemWatcher.Filter; +#endif + fileSystemWatcherMock.NotifyFilter = fileSystemWatcher.NotifyFilter; + fileSystemWatcherMock.IncludeSubdirectories = + fileSystemWatcher.IncludeSubdirectories; + fileSystemWatcherMock.InternalBufferSize = + fileSystemWatcher.InternalBufferSize; + fileSystemWatcherMock.EnableRaisingEvents = + fileSystemWatcher.EnableRaisingEvents; + return fileSystemWatcherMock; + } + + #endregion +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs index b45b9aef4..8e498c8e5 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs @@ -1,516 +1,515 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// Mocked instance of a -/// -public sealed class FileSystemWatcherMock : Component, IFileSystemWatcher -{ - /// - /// Simulated bytes pre message to calculate the size of the blocking collection relative to the - /// . - /// - private const int BytesPerMessage = 128; - - private static string DefaultFilter - => Execute.IsNetFramework ? "*.*" : "*"; - - private CancellationTokenSource? _cancellationTokenSource; - private IDisposable? _changeHandler; - private BlockingCollection _changes; - private bool _enableRaisingEvents; - private readonly MockFileSystem _fileSystem; - private readonly Collection _filters = new(); - private int _internalBufferSize = 8192; - private bool _isInitializing; - private string _path = string.Empty; - - private FileSystemWatcherMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - _changes = - new BlockingCollection(InternalBufferSize / - BytesPerMessage); - } - - private event EventHandler? InternalEvent; - - #region IFileSystemWatcher Members - - /// - public bool EnableRaisingEvents - { - get => _enableRaisingEvents; - set - { - _enableRaisingEvents = value; - if (_enableRaisingEvents) - { - Start(); - } - else - { - Stop(); - } - } - } - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public string Filter - { - get => _filters.Count == 0 - ? DefaultFilter - : _filters[0]; - set - { - _filters.Clear(); - _filters.Add(value); - } - } - -#if FEATURE_FILESYSTEMWATCHER_ADVANCED - /// - public Collection Filters - => _filters; -#endif - - /// - public bool IncludeSubdirectories - { - get; - set; - } - - /// - public int InternalBufferSize - { - get => _internalBufferSize; - set - { - _internalBufferSize = Math.Max(value, 4096); - Restart(); - } - } - - /// - public NotifyFilters NotifyFilter - { - get; - set; - } = NotifyFilters.FileName | - NotifyFilters.DirectoryName | - NotifyFilters.LastWrite; - - /// - public string Path - { - get => _path; - set - { - if (!string.IsNullOrEmpty(value) && - !_fileSystem.Directory.Exists(value)) - { - throw ExceptionFactory.DirectoryNameDoesNotExist(value, nameof(Path)); - } - - _path = value; - } - } - - /// - public ISynchronizeInvoke? SynchronizingObject { get; set; } - - /// - public void BeginInit() - { - _isInitializing = true; - Stop(); - } - - /// - public event FileSystemEventHandler? Changed; - - /// - public event FileSystemEventHandler? Created; - - /// - public event FileSystemEventHandler? Deleted; - - /// - public void EndInit() - { - _isInitializing = false; - Restart(); - } - - /// - public event ErrorEventHandler? Error; - - /// - public event RenamedEventHandler? Renamed; - - /// - public IFileSystemWatcher.IWaitForChangedResult WaitForChanged( - WatcherChangeTypes changeType) - => WaitForChanged(changeType, Timeout.Infinite); - - /// - public IFileSystemWatcher.IWaitForChangedResult WaitForChanged( - WatcherChangeTypes changeType, int timeout) - => WaitForChangedInternal(changeType, TimeSpan.FromMilliseconds(timeout)); - -#if FEATURE_FILESYSTEM_NET7 - /// - public IFileSystemWatcher.IWaitForChangedResult WaitForChanged( - WatcherChangeTypes changeType, TimeSpan timeout) - => WaitForChangedInternal(changeType, timeout); -#endif - - #endregion - - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - Stop(); - } - - base.Dispose(disposing); - } - - internal static FileSystemWatcherMock New(MockFileSystem fileSystem) - => new(fileSystem); - - private bool MatchesFilter(ChangeDescription changeDescription) - { - if (IncludeSubdirectories) - { - if (!changeDescription.Path.StartsWith(Path)) - { - return false; - } - } - else if (FileSystem.Path.GetDirectoryName(changeDescription.Path) != Path) - { - return false; - } - - if ((NotifyFilter & changeDescription.NotifyFilters) == 0) - { - return false; - } - - if (_filters.Count == 0) - { - return true; - } - - return _filters.Any(filter => - EnumerationOptionsHelper.MatchesPattern( - EnumerationOptionsHelper.Compatible, - _fileSystem.Path.GetFileName(changeDescription.Path), - filter)); - } - - private void NotifyChange(ChangeDescription item) - { - InternalEvent?.Invoke(this, item); - if (MatchesFilter(item)) - { - if (item.ChangeType.HasFlag(WatcherChangeTypes.Created)) - { - Created?.Invoke(this, ToFileSystemEventArgs( - item.ChangeType, item.Path, item.Name)); - } - - if (item.ChangeType.HasFlag(WatcherChangeTypes.Deleted)) - { - Deleted?.Invoke(this, ToFileSystemEventArgs( - item.ChangeType, item.Path, item.Name)); - } - - if (item.ChangeType.HasFlag(WatcherChangeTypes.Changed)) - { - Changed?.Invoke(this, ToFileSystemEventArgs( - item.ChangeType, item.Path, item.Name)); - } - - if (item.ChangeType.HasFlag(WatcherChangeTypes.Renamed)) - { - TriggerRenameNotification(item); - } - } - } - - private void Restart() - { - if (_isInitializing) - { - return; - } - - if (EnableRaisingEvents) - { - Stop(); - BlockingCollection changes = _changes; - _changes = - new BlockingCollection(InternalBufferSize / - BytesPerMessage); - changes.Dispose(); - Start(); - } - else - { - BlockingCollection changes = _changes; - _changes = - new BlockingCollection(InternalBufferSize / - BytesPerMessage); - changes.Dispose(); - } - } - - private void Start() - { - if (_isInitializing) - { - return; - } - - Stop(); - CancellationTokenSource cancellationTokenSource = new(); - _cancellationTokenSource = cancellationTokenSource; - _changeHandler = _fileSystem.Notify.OnEvent(c => - { - if (!_changes.TryAdd(c, 100)) - { - Error?.Invoke(this, new ErrorEventArgs( - ExceptionFactory.InternalBufferOverflowException( - InternalBufferSize, _changes.BoundedCapacity))); - } - }); - CancellationToken token = cancellationTokenSource.Token; - Task.Factory.StartNew(() => - { - try - { - while (!token.IsCancellationRequested) - { - if (_changes.TryTake(out ChangeDescription? c, - Timeout.Infinite, - token)) - { - NotifyChange(c); - } - } - } - catch (Exception) - { - //Ignore any exception - } - }, - token, - TaskCreationOptions.LongRunning, - TaskScheduler.Default) - .ContinueWith(_ => - { - cancellationTokenSource.Dispose(); - }, TaskScheduler.Default); - } - - private void Stop() - { - if (_cancellationTokenSource?.IsCancellationRequested == false) - { - _cancellationTokenSource.Cancel(); - } - - _changeHandler?.Dispose(); - } - - private FileSystemEventArgs ToFileSystemEventArgs( - WatcherChangeTypes changeType, - string changePath, - string? changeName) - { - string path = TransformPathAndName( - changePath, - changeName, - out string name); - - return new FileSystemEventArgs(changeType, - path, name); - } - - private string TransformPathAndName( - string changeDescriptionPath, - string? changeDescriptionName, - out string name) - { - string? transformedName = changeDescriptionName; - string? path = changeDescriptionPath; - if (transformedName == null || - _fileSystem.Path.IsPathRooted(changeDescriptionName)) - { - transformedName = _fileSystem.Path.GetFileName(changeDescriptionPath); - path = _fileSystem.Path.GetDirectoryName(path); - } - else if (path.EndsWith(transformedName)) - { - path = path.Substring(0, path.Length - transformedName.Length); - } - - name = transformedName; - return path ?? ""; - } - - private void TriggerRenameNotification(ChangeDescription item) - => Execute.OnWindows( - () => - { - if (TryMakeRenamedEventArgs(item, - out RenamedEventArgs? eventArgs)) - { - Renamed?.Invoke(this, eventArgs); - } - else if (item.OldPath != null) - { - Deleted?.Invoke(this, ToFileSystemEventArgs( - item.ChangeType, item.OldPath, item.OldName)); - Created?.Invoke(this, ToFileSystemEventArgs( - item.ChangeType, item.Path, item.Name)); - } - }, - () => - { - TryMakeRenamedEventArgs(item, - out RenamedEventArgs? eventArgs); - if (eventArgs != null) - { - Renamed?.Invoke(this, eventArgs); - } - }); - - private bool TryMakeRenamedEventArgs( - ChangeDescription changeDescription, - [NotNullWhen(true)] out RenamedEventArgs? eventArgs) - { - if (changeDescription.OldPath == null) - { - eventArgs = null; - return false; - } - - string path = TransformPathAndName( - changeDescription.Path, - changeDescription.Name, - out string name); - - TransformPathAndName( - changeDescription.OldPath, - changeDescription.OldName, - out string oldName); - - eventArgs = new RenamedEventArgs( - changeDescription.ChangeType, - path, - name, - oldName); - return System.IO.Path.GetDirectoryName(changeDescription.Path)? - .Equals(System.IO.Path.GetDirectoryName(changeDescription.OldPath), - InMemoryLocation.StringComparisonMode) ?? true; - } - - private IFileSystemWatcher.IWaitForChangedResult WaitForChangedInternal( - WatcherChangeTypes changeType, TimeSpan timeout) - { - TaskCompletionSource - tcs = new(); - - void EventHandler(object? _, ChangeDescription c) - { - if ((c.ChangeType & changeType) != 0) - { - tcs.TrySetResult(new WaitForChangedResultMock(c.ChangeType, c.Name, - oldName: c.OldName, timedOut: false)); - } - } - - InternalEvent += EventHandler; - try - { - bool wasEnabled = EnableRaisingEvents; - if (!wasEnabled) - { - EnableRaisingEvents = true; - } - - tcs.Task.Wait(timeout); - EnableRaisingEvents = wasEnabled; - } - finally - { - InternalEvent -= EventHandler; - } - -#if NETFRAMEWORK - return tcs.Task.IsCompleted - ? tcs.Task.Result - : WaitForChangedResultMock.TimedOutResult; -#else - return tcs.Task.IsCompletedSuccessfully - ? tcs.Task.Result - : WaitForChangedResultMock.TimedOutResult; -#endif - } - - private struct WaitForChangedResultMock - : IFileSystemWatcher.IWaitForChangedResult - { - public WaitForChangedResultMock( - WatcherChangeTypes changeType, - string? name, - string? oldName, - bool timedOut) - { - ChangeType = changeType; - Name = name; - OldName = oldName; - TimedOut = timedOut; - } - - /// - /// The instance representing a timed out . - /// - public static readonly WaitForChangedResultMock TimedOutResult = - new(changeType: 0, name: null, oldName: null, timedOut: true); - - /// - public WatcherChangeTypes ChangeType { get; } - - /// - public string? Name { get; } - - /// - public string? OldName { get; } - - /// - public bool TimedOut { get; } - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// Mocked instance of a +/// +public sealed class FileSystemWatcherMock : Component, IFileSystemWatcher +{ + /// + /// Simulated bytes pre message to calculate the size of the blocking collection relative to the + /// . + /// + private const int BytesPerMessage = 128; + + private static string DefaultFilter + => Execute.IsNetFramework ? "*.*" : "*"; + + private CancellationTokenSource? _cancellationTokenSource; + private IDisposable? _changeHandler; + private BlockingCollection _changes; + private bool _enableRaisingEvents; + private readonly MockFileSystem _fileSystem; + private readonly Collection _filters = new(); + private int _internalBufferSize = 8192; + private bool _isInitializing; + private string _path = string.Empty; + + private FileSystemWatcherMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + _changes = + new BlockingCollection(InternalBufferSize / + BytesPerMessage); + } + + private event EventHandler? InternalEvent; + + #region IFileSystemWatcher Members + + /// + public bool EnableRaisingEvents + { + get => _enableRaisingEvents; + set + { + _enableRaisingEvents = value; + if (_enableRaisingEvents) + { + Start(); + } + else + { + Stop(); + } + } + } + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public string Filter + { + get => _filters.Count == 0 + ? DefaultFilter + : _filters[0]; + set + { + _filters.Clear(); + _filters.Add(value); + } + } + +#if FEATURE_FILESYSTEMWATCHER_ADVANCED + /// + public Collection Filters + => _filters; +#endif + + /// + public bool IncludeSubdirectories + { + get; + set; + } + + /// + public int InternalBufferSize + { + get => _internalBufferSize; + set + { + _internalBufferSize = Math.Max(value, 4096); + Restart(); + } + } + + /// + public NotifyFilters NotifyFilter + { + get; + set; + } = NotifyFilters.FileName | + NotifyFilters.DirectoryName | + NotifyFilters.LastWrite; + + /// + public string Path + { + get => _path; + set + { + if (!string.IsNullOrEmpty(value) && + !_fileSystem.Directory.Exists(value)) + { + throw ExceptionFactory.DirectoryNameDoesNotExist(value, nameof(Path)); + } + + _path = value; + } + } + + /// + public ISynchronizeInvoke? SynchronizingObject { get; set; } + + /// + public void BeginInit() + { + _isInitializing = true; + Stop(); + } + + /// + public event FileSystemEventHandler? Changed; + + /// + public event FileSystemEventHandler? Created; + + /// + public event FileSystemEventHandler? Deleted; + + /// + public void EndInit() + { + _isInitializing = false; + Restart(); + } + + /// + public event ErrorEventHandler? Error; + + /// + public event RenamedEventHandler? Renamed; + + /// + public IFileSystemWatcher.IWaitForChangedResult WaitForChanged( + WatcherChangeTypes changeType) + => WaitForChanged(changeType, Timeout.Infinite); + + /// + public IFileSystemWatcher.IWaitForChangedResult WaitForChanged( + WatcherChangeTypes changeType, int timeout) + => WaitForChangedInternal(changeType, TimeSpan.FromMilliseconds(timeout)); + +#if FEATURE_FILESYSTEM_NET7 + /// + public IFileSystemWatcher.IWaitForChangedResult WaitForChanged( + WatcherChangeTypes changeType, TimeSpan timeout) + => WaitForChangedInternal(changeType, timeout); +#endif + + #endregion + + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + Stop(); + } + + base.Dispose(disposing); + } + + internal static FileSystemWatcherMock New(MockFileSystem fileSystem) + => new(fileSystem); + + private bool MatchesFilter(ChangeDescription changeDescription) + { + if (IncludeSubdirectories) + { + if (!changeDescription.Path.StartsWith(Path)) + { + return false; + } + } + else if (FileSystem.Path.GetDirectoryName(changeDescription.Path) != Path) + { + return false; + } + + if ((NotifyFilter & changeDescription.NotifyFilters) == 0) + { + return false; + } + + if (_filters.Count == 0) + { + return true; + } + + return _filters.Any(filter => + EnumerationOptionsHelper.MatchesPattern( + EnumerationOptionsHelper.Compatible, + _fileSystem.Path.GetFileName(changeDescription.Path), + filter)); + } + + private void NotifyChange(ChangeDescription item) + { + InternalEvent?.Invoke(this, item); + if (MatchesFilter(item)) + { + if (item.ChangeType.HasFlag(WatcherChangeTypes.Created)) + { + Created?.Invoke(this, ToFileSystemEventArgs( + item.ChangeType, item.Path, item.Name)); + } + + if (item.ChangeType.HasFlag(WatcherChangeTypes.Deleted)) + { + Deleted?.Invoke(this, ToFileSystemEventArgs( + item.ChangeType, item.Path, item.Name)); + } + + if (item.ChangeType.HasFlag(WatcherChangeTypes.Changed)) + { + Changed?.Invoke(this, ToFileSystemEventArgs( + item.ChangeType, item.Path, item.Name)); + } + + if (item.ChangeType.HasFlag(WatcherChangeTypes.Renamed)) + { + TriggerRenameNotification(item); + } + } + } + + private void Restart() + { + if (_isInitializing) + { + return; + } + + if (EnableRaisingEvents) + { + Stop(); + BlockingCollection changes = _changes; + _changes = + new BlockingCollection(InternalBufferSize / + BytesPerMessage); + changes.Dispose(); + Start(); + } + else + { + BlockingCollection changes = _changes; + _changes = + new BlockingCollection(InternalBufferSize / + BytesPerMessage); + changes.Dispose(); + } + } + + private void Start() + { + if (_isInitializing) + { + return; + } + + Stop(); + CancellationTokenSource cancellationTokenSource = new(); + _cancellationTokenSource = cancellationTokenSource; + _changeHandler = _fileSystem.Notify.OnEvent(c => + { + if (!_changes.TryAdd(c, 100)) + { + Error?.Invoke(this, new ErrorEventArgs( + ExceptionFactory.InternalBufferOverflowException( + InternalBufferSize, _changes.BoundedCapacity))); + } + }); + CancellationToken token = cancellationTokenSource.Token; + Task.Factory.StartNew(() => + { + try + { + while (!token.IsCancellationRequested) + { + if (_changes.TryTake(out ChangeDescription? c, + Timeout.Infinite, + token)) + { + NotifyChange(c); + } + } + } + catch (Exception) + { + //Ignore any exception + } + }, + token, + TaskCreationOptions.LongRunning, + TaskScheduler.Default) + .ContinueWith(_ => + { + cancellationTokenSource.Dispose(); + }, TaskScheduler.Default); + } + + private void Stop() + { + if (_cancellationTokenSource?.IsCancellationRequested == false) + { + _cancellationTokenSource.Cancel(); + } + + _changeHandler?.Dispose(); + } + + private FileSystemEventArgs ToFileSystemEventArgs( + WatcherChangeTypes changeType, + string changePath, + string? changeName) + { + string path = TransformPathAndName( + changePath, + changeName, + out string name); + + return new FileSystemEventArgs(changeType, + path, name); + } + + private string TransformPathAndName( + string changeDescriptionPath, + string? changeDescriptionName, + out string name) + { + string? transformedName = changeDescriptionName; + string? path = changeDescriptionPath; + if (transformedName == null || + _fileSystem.Path.IsPathRooted(changeDescriptionName)) + { + transformedName = _fileSystem.Path.GetFileName(changeDescriptionPath); + path = _fileSystem.Path.GetDirectoryName(path); + } + else if (path.EndsWith(transformedName)) + { + path = path.Substring(0, path.Length - transformedName.Length); + } + + name = transformedName; + return path ?? ""; + } + + private void TriggerRenameNotification(ChangeDescription item) + => Execute.OnWindows( + () => + { + if (TryMakeRenamedEventArgs(item, + out RenamedEventArgs? eventArgs)) + { + Renamed?.Invoke(this, eventArgs); + } + else if (item.OldPath != null) + { + Deleted?.Invoke(this, ToFileSystemEventArgs( + item.ChangeType, item.OldPath, item.OldName)); + Created?.Invoke(this, ToFileSystemEventArgs( + item.ChangeType, item.Path, item.Name)); + } + }, + () => + { + TryMakeRenamedEventArgs(item, + out RenamedEventArgs? eventArgs); + if (eventArgs != null) + { + Renamed?.Invoke(this, eventArgs); + } + }); + + private bool TryMakeRenamedEventArgs( + ChangeDescription changeDescription, + [NotNullWhen(true)] out RenamedEventArgs? eventArgs) + { + if (changeDescription.OldPath == null) + { + eventArgs = null; + return false; + } + + string path = TransformPathAndName( + changeDescription.Path, + changeDescription.Name, + out string name); + + TransformPathAndName( + changeDescription.OldPath, + changeDescription.OldName, + out string oldName); + + eventArgs = new RenamedEventArgs( + changeDescription.ChangeType, + path, + name, + oldName); + return System.IO.Path.GetDirectoryName(changeDescription.Path)? + .Equals(System.IO.Path.GetDirectoryName(changeDescription.OldPath), + InMemoryLocation.StringComparisonMode) ?? true; + } + + private IFileSystemWatcher.IWaitForChangedResult WaitForChangedInternal( + WatcherChangeTypes changeType, TimeSpan timeout) + { + TaskCompletionSource + tcs = new(); + + void EventHandler(object? _, ChangeDescription c) + { + if ((c.ChangeType & changeType) != 0) + { + tcs.TrySetResult(new WaitForChangedResultMock(c.ChangeType, c.Name, + oldName: c.OldName, timedOut: false)); + } + } + + InternalEvent += EventHandler; + try + { + bool wasEnabled = EnableRaisingEvents; + if (!wasEnabled) + { + EnableRaisingEvents = true; + } + + tcs.Task.Wait(timeout); + EnableRaisingEvents = wasEnabled; + } + finally + { + InternalEvent -= EventHandler; + } + +#if NETFRAMEWORK + return tcs.Task.IsCompleted + ? tcs.Task.Result + : WaitForChangedResultMock.TimedOutResult; +#else + return tcs.Task.IsCompletedSuccessfully + ? tcs.Task.Result + : WaitForChangedResultMock.TimedOutResult; +#endif + } + + private struct WaitForChangedResultMock + : IFileSystemWatcher.IWaitForChangedResult + { + public WaitForChangedResultMock( + WatcherChangeTypes changeType, + string? name, + string? oldName, + bool timedOut) + { + ChangeType = changeType; + Name = name; + OldName = oldName; + TimedOut = timedOut; + } + + /// + /// The instance representing a timed out . + /// + public static readonly WaitForChangedResultMock TimedOutResult = + new(changeType: 0, name: null, oldName: null, timedOut: true); + + /// + public WatcherChangeTypes ChangeType { get; } + + /// + public string? Name { get; } + + /// + public string? OldName { get; } + + /// + public bool TimedOut { get; } + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/IInterceptionHandler.cs b/Source/Testably.Abstractions.Testing/FileSystem/IInterceptionHandler.cs index 0d4bb37a7..4a9b92c0c 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/IInterceptionHandler.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/IInterceptionHandler.cs @@ -1,23 +1,22 @@ -using System; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// The interception handler for the . -/// -public interface IInterceptionHandler : IFileSystemEntity -{ - /// - /// Callback executed before any change in the matching the - /// is about to occur. - /// - /// The callback to execute before the change occurred. - /// - /// (optional) A predicate used to filter which callbacks should be intercepted.
- /// If set to (default value) all callbacks are intercepted. - /// - /// This allows e.g. to throw custom exceptions instead. - MockFileSystem Event(Action interceptionCallback, - Func? predicate = null); -} +using System; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// The interception handler for the . +/// +public interface IInterceptionHandler : IFileSystemEntity +{ + /// + /// Callback executed before any change in the matching the + /// is about to occur. + /// + /// The callback to execute before the change occurred. + /// + /// (optional) A predicate used to filter which callbacks should be intercepted.
+ /// If set to (default value) all callbacks are intercepted. + /// + /// This allows e.g. to throw custom exceptions instead. + MockFileSystem Event(Action interceptionCallback, + Func? predicate = null); +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/INotificationHandler.cs b/Source/Testably.Abstractions.Testing/FileSystem/INotificationHandler.cs index bcf75ec96..d73c8a34f 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/INotificationHandler.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/INotificationHandler.cs @@ -1,24 +1,23 @@ -using System; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// The notification handler for the . -/// -public interface INotificationHandler : IFileSystemEntity -{ - /// - /// Callback executed when any change in the matching the - /// occurred. - /// - /// The callback to execute after the change occurred. - /// - /// (optional) A predicate used to filter which callbacks should be notified.
- /// If set to (default value) all callbacks are notified. - /// - /// An to un-register the callback on dispose. - Notification.IAwaitableCallback OnEvent( - Action? notificationCallback = null, - Func? predicate = null); -} +using System; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// The notification handler for the . +/// +public interface INotificationHandler : IFileSystemEntity +{ + /// + /// Callback executed when any change in the matching the + /// occurred. + /// + /// The callback to execute after the change occurred. + /// + /// (optional) A predicate used to filter which callbacks should be notified.
+ /// If set to (default value) all callbacks are notified. + /// + /// An to un-register the callback on dispose. + Notification.IAwaitableCallback OnEvent( + Action? notificationCallback = null, + Func? predicate = null); +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/PathMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/PathMock.cs index f78fc42a2..7e682355d 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/PathMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/PathMock.cs @@ -1,47 +1,46 @@ -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Helpers; -#if FEATURE_FILESYSTEM_NET7 -using System.Diagnostics.CodeAnalysis; -using Testably.Abstractions.Testing.Storage; -#endif - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class PathMock : PathSystemBase -{ - private readonly MockFileSystem _fileSystem; - - internal PathMock(MockFileSystem fileSystem) - : base(fileSystem) - { - _fileSystem = fileSystem; - } - -#if FEATURE_FILESYSTEM_NET7 - /// - public override bool Exists([NotNullWhen(true)] string? path) - { - if (string.IsNullOrEmpty(path)) - { - return false; - } - - return _fileSystem.Storage.GetContainer(_fileSystem.Storage.GetLocation(path)) - is not NullContainer; - } -#endif - - /// - public override string GetFullPath(string path) - { - if (string.IsNullOrEmpty(path)) - { - return string.Empty; - } - - return Path.GetFullPath(Path.Combine( - _fileSystem.Storage.CurrentDirectory, - path)); - } -} +using System.IO; +using Testably.Abstractions.Helpers; +#if FEATURE_FILESYSTEM_NET7 +using System.Diagnostics.CodeAnalysis; +using Testably.Abstractions.Testing.Storage; +#endif + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class PathMock : PathSystemBase +{ + private readonly MockFileSystem _fileSystem; + + internal PathMock(MockFileSystem fileSystem) + : base(fileSystem) + { + _fileSystem = fileSystem; + } + +#if FEATURE_FILESYSTEM_NET7 + /// + public override bool Exists([NotNullWhen(true)] string? path) + { + if (string.IsNullOrEmpty(path)) + { + return false; + } + + return _fileSystem.Storage.GetContainer(_fileSystem.Storage.GetLocation(path)) + is not NullContainer; + } +#endif + + /// + public override string GetFullPath(string path) + { + if (string.IsNullOrEmpty(path)) + { + return string.Empty; + } + + return Path.GetFullPath(Path.Combine( + _fileSystem.Storage.CurrentDirectory, + path)); + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryCleaner.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryCleaner.cs index cb60a3206..ce97e80dc 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryCleaner.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryCleaner.cs @@ -1,133 +1,132 @@ -using System; -using System.IO; -using System.Threading; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; - -namespace Testably.Abstractions.Testing.FileSystemInitializer; - -internal sealed class DirectoryCleaner : IDirectoryCleaner -{ - private readonly IFileSystem _fileSystem; - private readonly Action? _logger; - - public DirectoryCleaner(IFileSystem fileSystem, Action? logger) - { - _fileSystem = fileSystem; - _logger = logger; - BasePath = InitializeBasePath(); - } - - #region IDirectoryCleaner Members - - /// - public string BasePath { get; } - - /// - public void Dispose() - { - ITimeSystem? timeSystem = (_fileSystem as MockFileSystem)?.TimeSystem; - try - { - // It is important to reset the current directory, as otherwise deleting the BasePath - // results in a IOException, because the process cannot access the file. - _fileSystem.Directory.SetCurrentDirectory(_fileSystem.Path.GetTempPath()); - - _logger?.Invoke($"Cleaning up '{BasePath}'..."); - for (int i = 10; i >= 0; i--) - { - try - { - ForceDeleteDirectory(BasePath); - break; - } - catch (Exception) - { - if (i == 0) - { - throw; - } - - _logger?.Invoke( - $" Force delete failed! Retry again {i} times in 100ms..."); - if (timeSystem == null) - { - Thread.Sleep(100); - } - else - { - timeSystem.Thread.Sleep(100); - } - } - } - - _logger?.Invoke("Cleanup was successful :-)"); - } - catch (Exception ex) - { - _logger?.Invoke($"Could not clean up '{BasePath}' because: {ex}"); - } - } - - #endregion - - /// - /// Force deletes the directory at the given .
- /// Removes the flag, if necessary. - /// - /// If is set (default ), the sub directories are force deleted as - /// well. - ///
- private void ForceDeleteDirectory(string path, bool recursive = true) - { - if (!_fileSystem.Directory.Exists(path)) - { - return; - } - - IDirectoryInfo directory = _fileSystem.DirectoryInfo.New(path); - directory.Attributes = FileAttributes.Normal; - - foreach (IFileInfo info in directory.EnumerateFiles( - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly)) - { - info.Attributes = FileAttributes.Normal; - info.Delete(); - } - - if (recursive) - { - foreach (IDirectoryInfo info in - directory.EnumerateDirectories( - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly)) - { - ForceDeleteDirectory(info.FullName, recursive); - } - } - - _fileSystem.Directory.Delete(path); - } - - private string InitializeBasePath() - { - string basePath; - - do - { - string localBasePath = _fileSystem.Path.Combine( - _fileSystem.Path.GetTempPath(), - _fileSystem.Path.GetFileNameWithoutExtension(_fileSystem.Path - .GetRandomFileName())); - Execute.OnMac(() => localBasePath = "/private" + localBasePath); - basePath = localBasePath; - } while (_fileSystem.Directory.Exists(basePath)); - - _fileSystem.Directory.CreateDirectory(basePath); - - _logger?.Invoke($"Use '{basePath}' as current directory."); - _fileSystem.Directory.SetCurrentDirectory(basePath); - return basePath; - } -} +using System; +using System.IO; +using System.Threading; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.FileSystemInitializer; + +internal sealed class DirectoryCleaner : IDirectoryCleaner +{ + private readonly IFileSystem _fileSystem; + private readonly Action? _logger; + + public DirectoryCleaner(IFileSystem fileSystem, Action? logger) + { + _fileSystem = fileSystem; + _logger = logger; + BasePath = InitializeBasePath(); + } + + #region IDirectoryCleaner Members + + /// + public string BasePath { get; } + + /// + public void Dispose() + { + ITimeSystem? timeSystem = (_fileSystem as MockFileSystem)?.TimeSystem; + try + { + // It is important to reset the current directory, as otherwise deleting the BasePath + // results in a IOException, because the process cannot access the file. + _fileSystem.Directory.SetCurrentDirectory(_fileSystem.Path.GetTempPath()); + + _logger?.Invoke($"Cleaning up '{BasePath}'..."); + for (int i = 10; i >= 0; i--) + { + try + { + ForceDeleteDirectory(BasePath); + break; + } + catch (Exception) + { + if (i == 0) + { + throw; + } + + _logger?.Invoke( + $" Force delete failed! Retry again {i} times in 100ms..."); + if (timeSystem == null) + { + Thread.Sleep(100); + } + else + { + timeSystem.Thread.Sleep(100); + } + } + } + + _logger?.Invoke("Cleanup was successful :-)"); + } + catch (Exception ex) + { + _logger?.Invoke($"Could not clean up '{BasePath}' because: {ex}"); + } + } + + #endregion + + /// + /// Force deletes the directory at the given .
+ /// Removes the flag, if necessary. + /// + /// If is set (default ), the sub directories are force deleted as + /// well. + ///
+ private void ForceDeleteDirectory(string path, bool recursive = true) + { + if (!_fileSystem.Directory.Exists(path)) + { + return; + } + + IDirectoryInfo directory = _fileSystem.DirectoryInfo.New(path); + directory.Attributes = FileAttributes.Normal; + + foreach (IFileInfo info in directory.EnumerateFiles( + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly)) + { + info.Attributes = FileAttributes.Normal; + info.Delete(); + } + + if (recursive) + { + foreach (IDirectoryInfo info in + directory.EnumerateDirectories( + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly)) + { + ForceDeleteDirectory(info.FullName, recursive); + } + } + + _fileSystem.Directory.Delete(path); + } + + private string InitializeBasePath() + { + string basePath; + + do + { + string localBasePath = _fileSystem.Path.Combine( + _fileSystem.Path.GetTempPath(), + _fileSystem.Path.GetFileNameWithoutExtension(_fileSystem.Path + .GetRandomFileName())); + Execute.OnMac(() => localBasePath = "/private" + localBasePath); + basePath = localBasePath; + } while (_fileSystem.Directory.Exists(basePath)); + + _fileSystem.Directory.CreateDirectory(basePath); + + _logger?.Invoke($"Use '{basePath}' as current directory."); + _fileSystem.Directory.SetCurrentDirectory(basePath); + return basePath; + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryInitializer.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryInitializer.cs index 9bd37a105..43589ca48 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryInitializer.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryInitializer.cs @@ -1,34 +1,33 @@ -using System; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Testing.FileSystemInitializer; - -internal sealed class DirectoryInitializer - : Initializer, - IFileSystemDirectoryInitializer - where TFileSystem : IFileSystem -{ - public DirectoryInitializer(Initializer initializer, - IDirectoryInfo directory) - : base(initializer) - { - Directory = directory; - } - - #region IFileSystemDirectoryInitializer Members - - /// - public IDirectoryInfo Directory { get; } - - /// - public IFileSystemDirectoryInitializer Initialized( - Action> subdirectoryInitializer) - { - Initializer initializer = new(this, Directory); - subdirectoryInitializer.Invoke(initializer); - return this; - } - - #endregion -} +using System; + +namespace Testably.Abstractions.Testing.FileSystemInitializer; + +internal sealed class DirectoryInitializer + : Initializer, + IFileSystemDirectoryInitializer + where TFileSystem : IFileSystem +{ + public DirectoryInitializer(Initializer initializer, + IDirectoryInfo directory) + : base(initializer) + { + Directory = directory; + } + + #region IFileSystemDirectoryInitializer Members + + /// + public IDirectoryInfo Directory { get; } + + /// + public IFileSystemDirectoryInitializer Initialized( + Action> subdirectoryInitializer) + { + Initializer initializer = new(this, Directory); + subdirectoryInitializer.Invoke(initializer); + return this; + } + + #endregion +} diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs index 48fbc851b..1fdb3cc21 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs @@ -1,66 +1,65 @@ -using System; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Testing.FileSystemInitializer; - -internal sealed class FileInitializer - : Initializer, - IFileSystemFileInitializer - where TFileSystem : IFileSystem -{ - public FileInitializer(Initializer initializer, - IFileInfo file) - : base(initializer) - { - File = file; - } - - #region IFileSystemFileInitializer Members - - /// - public IFileInfo File { get; } - - /// - public IFileSystemFileInitializer Which( - Action fileManipulation) - { - FileManipulator fileManipulator = new(FileSystem, File); - fileManipulation.Invoke(fileManipulator); - return this; - } - - #endregion - - private sealed class FileManipulator : IFileManipulator - { - internal FileManipulator(IFileSystem fileSystem, IFileInfo file) - { - FileSystem = fileSystem; - File = file; - } - - #region IFileManipulator Members - - /// - public IFileInfo File { get; } - - /// - public IFileSystem FileSystem { get; } - - /// - public IFileManipulator HasBytesContent(byte[] bytes) - { - FileSystem.File.WriteAllBytes(File.FullName, bytes); - return this; - } - - /// - public IFileManipulator HasStringContent(string contents) - { - FileSystem.File.WriteAllText(File.FullName, contents); - return this; - } - - #endregion - } -} +using System; + +namespace Testably.Abstractions.Testing.FileSystemInitializer; + +internal sealed class FileInitializer + : Initializer, + IFileSystemFileInitializer + where TFileSystem : IFileSystem +{ + public FileInitializer(Initializer initializer, + IFileInfo file) + : base(initializer) + { + File = file; + } + + #region IFileSystemFileInitializer Members + + /// + public IFileInfo File { get; } + + /// + public IFileSystemFileInitializer Which( + Action fileManipulation) + { + FileManipulator fileManipulator = new(FileSystem, File); + fileManipulation.Invoke(fileManipulator); + return this; + } + + #endregion + + private sealed class FileManipulator : IFileManipulator + { + internal FileManipulator(IFileSystem fileSystem, IFileInfo file) + { + FileSystem = fileSystem; + File = file; + } + + #region IFileManipulator Members + + /// + public IFileInfo File { get; } + + /// + public IFileSystem FileSystem { get; } + + /// + public IFileManipulator HasBytesContent(byte[] bytes) + { + FileSystem.File.WriteAllBytes(File.FullName, bytes); + return this; + } + + /// + public IFileManipulator HasStringContent(string contents) + { + FileSystem.File.WriteAllText(File.FullName, contents); + return this; + } + + #endregion + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemDirectoryInitializer.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemDirectoryInitializer.cs index 2cb7fccb7..32e43b01f 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemDirectoryInitializer.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemDirectoryInitializer.cs @@ -1,23 +1,22 @@ -using System; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Testing.FileSystemInitializer; - -/// -/// Initializes a directory in the with test data. -/// -public interface IFileSystemDirectoryInitializer - : IFileSystemInitializer - where TFileSystem : IFileSystem -{ - /// - /// The directory to initialize. - /// - public IDirectoryInfo Directory { get; } - - /// - /// Initializes the subdirectory in the with test data. - /// - public IFileSystemDirectoryInitializer Initialized( - Action> subdirectoryInitializer); -} +using System; + +namespace Testably.Abstractions.Testing.FileSystemInitializer; + +/// +/// Initializes a directory in the with test data. +/// +public interface IFileSystemDirectoryInitializer + : IFileSystemInitializer + where TFileSystem : IFileSystem +{ + /// + /// The directory to initialize. + /// + public IDirectoryInfo Directory { get; } + + /// + /// Initializes the subdirectory in the with test data. + /// + public IFileSystemDirectoryInitializer Initialized( + Action> subdirectoryInitializer); +} diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemFileInitializer.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemFileInitializer.cs index ee049dad1..d7b8fccf3 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemFileInitializer.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemFileInitializer.cs @@ -1,23 +1,22 @@ -using System; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Testing.FileSystemInitializer; - -/// -/// Initializes a file in the with test data. -/// -public interface IFileSystemFileInitializer - : IFileSystemInitializer - where TFileSystem : IFileSystem -{ - /// - /// The file to initialize. - /// - public IFileInfo File { get; } - - /// - /// Manipulates the in the with test data. - /// - public IFileSystemFileInitializer Which( - Action fileManipulation); -} +using System; + +namespace Testably.Abstractions.Testing.FileSystemInitializer; + +/// +/// Initializes a file in the with test data. +/// +public interface IFileSystemFileInitializer + : IFileSystemInitializer + where TFileSystem : IFileSystem +{ + /// + /// The file to initialize. + /// + public IFileInfo File { get; } + + /// + /// Manipulates the in the with test data. + /// + public IFileSystemFileInitializer Which( + Action fileManipulation); +} diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/Initializer.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/Initializer.cs index 2ecfb3ddf..116eeb85e 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/Initializer.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/Initializer.cs @@ -1,125 +1,124 @@ -using System.Collections.Generic; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.RandomSystem; -using Testably.Abstractions.Testing.Helpers; - -namespace Testably.Abstractions.Testing.FileSystemInitializer; - -internal class Initializer - : IFileSystemInitializer - where TFileSystem : IFileSystem -{ - private readonly string _basePath; - - private readonly Dictionary - _initializedFileSystemInfos = new(); - - public Initializer(TFileSystem fileSystem, string basePath) - { - _basePath = basePath; - FileSystem = fileSystem; - } - - protected Initializer(Initializer parent) - { - FileSystem = parent.FileSystem; - _initializedFileSystemInfos = parent._initializedFileSystemInfos; - _basePath = parent._basePath; - } - - internal Initializer(Initializer parent, - IDirectoryInfo subdirectory) - { - FileSystem = parent.FileSystem; - _initializedFileSystemInfos = parent._initializedFileSystemInfos; - _basePath = FileSystem.Path.Combine(parent._basePath, subdirectory.Name); - } - - #region IFileSystemInitializer Members - - /// - public IDirectoryInfo BaseDirectory - => FileSystem.DirectoryInfo.New(_basePath); - - /// - public TFileSystem FileSystem { get; } - - /// - public IFileSystemInfo this[int index] - => _initializedFileSystemInfos[index]; - - /// - public IFileSystemFileInitializer WithAFile(string? extension = null) - { - IRandom random = (FileSystem as MockFileSystem)? - .RandomSystem.Random.Shared ?? RandomFactory.Shared; - string fileName; - do - { - fileName = - $"{random.GenerateFileName()}-{random.Next(10000)}.{random.GenerateFileExtension(extension)}"; - } while (FileSystem.File.Exists( - FileSystem.Path.Combine(_basePath, fileName))); - - return WithFile(fileName); - } - - /// - public IFileSystemDirectoryInitializer WithASubdirectory() - { - IRandom random = (FileSystem as MockFileSystem)? - .RandomSystem.Random.Shared ?? RandomFactory.Shared; - string directoryName; - do - { - directoryName = - $"{random.GenerateFileName()}-{random.Next(10000)}"; - } while (FileSystem.Directory.Exists( - FileSystem.Path.Combine(_basePath, directoryName))); - - return WithSubdirectory(directoryName); - } - - /// - public IFileSystemFileInitializer WithFile(string fileName) - { - IFileInfo fileInfo = FileSystem.FileInfo.New( - FileSystem.Path.Combine(_basePath, fileName)); - if (fileInfo.Exists) - { - throw new TestingException( - $"The file '{fileInfo.FullName}' already exists!"); - } - - FileSystem.File.WriteAllText(fileInfo.FullName, null); - _initializedFileSystemInfos.Add( - _initializedFileSystemInfos.Count, - fileInfo); - - fileInfo.Refresh(); - return new FileInitializer(this, fileInfo); - } - - /// - public IFileSystemDirectoryInitializer WithSubdirectory( - string directoryName) - { - IDirectoryInfo directoryInfo = FileSystem.DirectoryInfo.New( - FileSystem.Path.Combine(_basePath, directoryName)); - if (directoryInfo.Exists) - { - throw new TestingException( - $"The directory '{directoryInfo.FullName}' already exists!"); - } - - FileSystem.Directory.CreateDirectory(directoryInfo.FullName); - _initializedFileSystemInfos.Add( - _initializedFileSystemInfos.Count, - directoryInfo); - - directoryInfo.Refresh(); - return new DirectoryInitializer(this, directoryInfo); - } - - #endregion -} +using System.Collections.Generic; +using Testably.Abstractions.RandomSystem; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.FileSystemInitializer; + +internal class Initializer + : IFileSystemInitializer + where TFileSystem : IFileSystem +{ + private readonly string _basePath; + + private readonly Dictionary + _initializedFileSystemInfos = new(); + + public Initializer(TFileSystem fileSystem, string basePath) + { + _basePath = basePath; + FileSystem = fileSystem; + } + + protected Initializer(Initializer parent) + { + FileSystem = parent.FileSystem; + _initializedFileSystemInfos = parent._initializedFileSystemInfos; + _basePath = parent._basePath; + } + + internal Initializer(Initializer parent, + IDirectoryInfo subdirectory) + { + FileSystem = parent.FileSystem; + _initializedFileSystemInfos = parent._initializedFileSystemInfos; + _basePath = FileSystem.Path.Combine(parent._basePath, subdirectory.Name); + } + + #region IFileSystemInitializer Members + + /// + public IDirectoryInfo BaseDirectory + => FileSystem.DirectoryInfo.New(_basePath); + + /// + public TFileSystem FileSystem { get; } + + /// + public IFileSystemInfo this[int index] + => _initializedFileSystemInfos[index]; + + /// + public IFileSystemFileInitializer WithAFile(string? extension = null) + { + IRandom random = (FileSystem as MockFileSystem)? + .RandomSystem.Random.Shared ?? RandomFactory.Shared; + string fileName; + do + { + fileName = + $"{random.GenerateFileName()}-{random.Next(10000)}.{random.GenerateFileExtension(extension)}"; + } while (FileSystem.File.Exists( + FileSystem.Path.Combine(_basePath, fileName))); + + return WithFile(fileName); + } + + /// + public IFileSystemDirectoryInitializer WithASubdirectory() + { + IRandom random = (FileSystem as MockFileSystem)? + .RandomSystem.Random.Shared ?? RandomFactory.Shared; + string directoryName; + do + { + directoryName = + $"{random.GenerateFileName()}-{random.Next(10000)}"; + } while (FileSystem.Directory.Exists( + FileSystem.Path.Combine(_basePath, directoryName))); + + return WithSubdirectory(directoryName); + } + + /// + public IFileSystemFileInitializer WithFile(string fileName) + { + IFileInfo fileInfo = FileSystem.FileInfo.New( + FileSystem.Path.Combine(_basePath, fileName)); + if (fileInfo.Exists) + { + throw new TestingException( + $"The file '{fileInfo.FullName}' already exists!"); + } + + FileSystem.File.WriteAllText(fileInfo.FullName, null); + _initializedFileSystemInfos.Add( + _initializedFileSystemInfos.Count, + fileInfo); + + fileInfo.Refresh(); + return new FileInitializer(this, fileInfo); + } + + /// + public IFileSystemDirectoryInitializer WithSubdirectory( + string directoryName) + { + IDirectoryInfo directoryInfo = FileSystem.DirectoryInfo.New( + FileSystem.Path.Combine(_basePath, directoryName)); + if (directoryInfo.Exists) + { + throw new TestingException( + $"The directory '{directoryInfo.FullName}' already exists!"); + } + + FileSystem.Directory.CreateDirectory(directoryInfo.FullName); + _initializedFileSystemInfos.Add( + _initializedFileSystemInfos.Count, + directoryInfo); + + directoryInfo.Refresh(); + return new DirectoryInitializer(this, directoryInfo); + } + + #endregion +} diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializerExtensions.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializerExtensions.cs index c5f3af6c0..cb7dc0c35 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializerExtensions.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializerExtensions.cs @@ -1,48 +1,47 @@ -using System; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Testing; - -/// -/// Initializes the with test data. -/// -public static class FileSystemInitializerExtensions -{ - /// - /// Initializes the in the working directory with test data. - /// - public static IFileSystemInitializer Initialize( - this TFileSystem fileSystem) - where TFileSystem : IFileSystem - => fileSystem.InitializeIn("."); - - /// - /// Initializes the in the with test data. - /// - public static IFileSystemInitializer InitializeIn( - this TFileSystem fileSystem, - string basePath) - where TFileSystem : IFileSystem - { - fileSystem.Directory.CreateDirectory(basePath); - fileSystem.Directory.SetCurrentDirectory(basePath); - return new Initializer(fileSystem, "."); - } - - /// - /// Sets the current directory to a new temporary directory.
- /// and all relative paths will use this directory. - ///
- /// The file system. - /// (optional) A callback to log the cleanup process. - /// - /// A that will - /// force delete all content in the temporary directory on dispose. - /// - public static IDirectoryCleaner SetCurrentDirectoryToEmptyTemporaryDirectory( - this IFileSystem fileSystem, Action? logger = null) - { - return new DirectoryCleaner(fileSystem, logger); - } -} +using System; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Testing; + +/// +/// Initializes the with test data. +/// +public static class FileSystemInitializerExtensions +{ + /// + /// Initializes the in the working directory with test data. + /// + public static IFileSystemInitializer Initialize( + this TFileSystem fileSystem) + where TFileSystem : IFileSystem + => fileSystem.InitializeIn("."); + + /// + /// Initializes the in the with test data. + /// + public static IFileSystemInitializer InitializeIn( + this TFileSystem fileSystem, + string basePath) + where TFileSystem : IFileSystem + { + fileSystem.Directory.CreateDirectory(basePath); + fileSystem.Directory.SetCurrentDirectory(basePath); + return new Initializer(fileSystem, "."); + } + + /// + /// Sets the current directory to a new temporary directory.
+ /// and all relative paths will use this directory. + ///
+ /// The file system. + /// (optional) A callback to log the cleanup process. + /// + /// A that will + /// force delete all content in the temporary directory on dispose. + /// + public static IDirectoryCleaner SetCurrentDirectoryToEmptyTemporaryDirectory( + this IFileSystem fileSystem, Action? logger = null) + { + return new DirectoryCleaner(fileSystem, logger); + } +} diff --git a/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensibility.cs b/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensibility.cs index c72f1c33e..5a027e5ee 100644 --- a/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensibility.cs +++ b/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensibility.cs @@ -1,47 +1,46 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Testing.Helpers; - -internal class FileSystemExtensibility : IFileSystemExtensibility -{ - private readonly Dictionary _metadata = new(); - - /// - public bool TryGetWrappedInstance([NotNullWhen(true)] out T? wrappedInstance) - { - wrappedInstance = default; - return false; - } - - /// - public void StoreMetadata(string key, T? value) - { - _metadata[key] = value; - } - - /// - public T? RetrieveMetadata(string key) - { - if (_metadata.TryGetValue(key, out object? value) && - // ReSharper disable once MergeCastWithTypeCheck -- Not possible due to nullable - value is T?) - { - return (T?)value; - } - - return default; - } - - internal void CopyMetadataTo(IFileSystemExtensibility target) - { - if (target is FileSystemExtensibility targetContainer) - { - foreach (KeyValuePair item in _metadata) - { - targetContainer._metadata[item.Key] = item.Value; - } - } - } -} +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Testably.Abstractions.Testing.Helpers; + +internal class FileSystemExtensibility : IFileSystemExtensibility +{ + private readonly Dictionary _metadata = new(); + + /// + public bool TryGetWrappedInstance([NotNullWhen(true)] out T? wrappedInstance) + { + wrappedInstance = default; + return false; + } + + /// + public void StoreMetadata(string key, T? value) + { + _metadata[key] = value; + } + + /// + public T? RetrieveMetadata(string key) + { + if (_metadata.TryGetValue(key, out object? value) && + // ReSharper disable once MergeCastWithTypeCheck -- Not possible due to nullable + value is T?) + { + return (T?)value; + } + + return default; + } + + internal void CopyMetadataTo(IFileSystemExtensibility target) + { + if (target is FileSystemExtensibility targetContainer) + { + foreach (KeyValuePair item in _metadata) + { + targetContainer._metadata[item.Key] = item.Value; + } + } + } +} diff --git a/Source/Testably.Abstractions.Testing/MockFileSystem.cs b/Source/Testably.Abstractions.Testing/MockFileSystem.cs index 8a1df58b8..0edd1a11d 100644 --- a/Source/Testably.Abstractions.Testing/MockFileSystem.cs +++ b/Source/Testably.Abstractions.Testing/MockFileSystem.cs @@ -1,154 +1,153 @@ -using Microsoft.Win32.SafeHandles; -using System; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystem; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing; - -/// -/// A test helper for simulating the file system. Implements . -/// -public sealed class MockFileSystem : IFileSystem -{ - /// - /// Intercept events in the before they occur. - /// - public IInterceptionHandler Intercept => ChangeHandler; - - /// - /// Get notified of events in the after they occurred. - /// - public INotificationHandler Notify => ChangeHandler; - - /// - /// The used random system. - /// - public IRandomSystem RandomSystem { get; } - - /// - /// The used time system. - /// - public ITimeSystem TimeSystem { get; } - - /// - /// The change handler used to notify about events occurring in the . - /// - internal ChangeHandler ChangeHandler { get; } - - /// - /// The underlying storage of directories and files. - /// - internal IStorage Storage => _storage; - - private readonly DirectoryMock _directoryMock; - private readonly FileMock _fileMock; - private readonly PathMock _pathMock; - private readonly InMemoryStorage _storage; - - internal IAccessControlStrategy AccessControlStrategy - { - get; - private set; - } - - internal ISafeFileHandleStrategy SafeFileHandleStrategy - { - get; - private set; - } - - /// - /// Initializes the . - /// - public MockFileSystem() - { - RandomSystem = new MockRandomSystem(); - TimeSystem = new MockTimeSystem(TimeProvider.Now()); - _pathMock = new PathMock(this); - _storage = new InMemoryStorage(this); - ChangeHandler = new ChangeHandler(this); - _directoryMock = new DirectoryMock(this); - _fileMock = new FileMock(this); - DirectoryInfo = new DirectoryInfoFactoryMock(this); - DriveInfo = new DriveInfoFactoryMock(this); - FileInfo = new FileInfoFactoryMock(this); - FileStream = new FileStreamFactoryMock(this); - FileSystemWatcher = new FileSystemWatcherFactoryMock(this); - SafeFileHandleStrategy = new NullSafeFileHandleStrategy(); - AccessControlStrategy = new NullAccessControlStrategy(); - } - - #region IFileSystem Members - - /// - public IDirectory Directory - => _directoryMock; - - /// - public IDirectoryInfoFactory DirectoryInfo { get; } - - /// - public IDriveInfoFactory DriveInfo { get; } - - /// - public IFile File - => _fileMock; - - /// - public IFileInfoFactory FileInfo { get; } - - /// - public IFileStreamFactory FileStream { get; } - - /// - public IFileSystemWatcherFactory FileSystemWatcher { get; } - - /// - public IPath Path - => _pathMock; - - #endregion - - /// - /// Implements a custom access control (ACL) mechanism. - /// - /// The defines a method that receives two values and allows or denies access: - ///
- /// - The full path of the file or directory as first parameter
- /// - The as second parameter - ///
- public MockFileSystem WithAccessControlStrategy(IAccessControlStrategy accessControlStrategy) - { - AccessControlStrategy = accessControlStrategy; - return this; - } - - /// - /// Changes the parameters of the specified . - /// - /// If the does not exist, it will be created/mounted. - /// - public MockFileSystem WithDrive(string? drive, - Action? driveCallback = null) - { - IStorageDrive driveInfoMock = - drive == null - ? Storage.MainDrive - : Storage.GetOrAddDrive(drive); - driveCallback?.Invoke(driveInfoMock); - return this; - } - - /// - /// Registers the strategy how to deal with s in the . - /// - /// Defaults to , if nothing is provided. - /// - public MockFileSystem WithSafeFileHandleStrategy( - ISafeFileHandleStrategy safeFileHandleStrategy) - { - SafeFileHandleStrategy = safeFileHandleStrategy; - return this; - } -} +using Microsoft.Win32.SafeHandles; +using System; +using Testably.Abstractions.Testing.FileSystem; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing; + +/// +/// A test helper for simulating the file system. Implements . +/// +public sealed class MockFileSystem : IFileSystem +{ + /// + /// Intercept events in the before they occur. + /// + public IInterceptionHandler Intercept => ChangeHandler; + + /// + /// Get notified of events in the after they occurred. + /// + public INotificationHandler Notify => ChangeHandler; + + /// + /// The used random system. + /// + public IRandomSystem RandomSystem { get; } + + /// + /// The used time system. + /// + public ITimeSystem TimeSystem { get; } + + /// + /// The change handler used to notify about events occurring in the . + /// + internal ChangeHandler ChangeHandler { get; } + + /// + /// The underlying storage of directories and files. + /// + internal IStorage Storage => _storage; + + private readonly DirectoryMock _directoryMock; + private readonly FileMock _fileMock; + private readonly PathMock _pathMock; + private readonly InMemoryStorage _storage; + + internal IAccessControlStrategy AccessControlStrategy + { + get; + private set; + } + + internal ISafeFileHandleStrategy SafeFileHandleStrategy + { + get; + private set; + } + + /// + /// Initializes the . + /// + public MockFileSystem() + { + RandomSystem = new MockRandomSystem(); + TimeSystem = new MockTimeSystem(TimeProvider.Now()); + _pathMock = new PathMock(this); + _storage = new InMemoryStorage(this); + ChangeHandler = new ChangeHandler(this); + _directoryMock = new DirectoryMock(this); + _fileMock = new FileMock(this); + DirectoryInfo = new DirectoryInfoFactoryMock(this); + DriveInfo = new DriveInfoFactoryMock(this); + FileInfo = new FileInfoFactoryMock(this); + FileStream = new FileStreamFactoryMock(this); + FileSystemWatcher = new FileSystemWatcherFactoryMock(this); + SafeFileHandleStrategy = new NullSafeFileHandleStrategy(); + AccessControlStrategy = new NullAccessControlStrategy(); + } + + #region IFileSystem Members + + /// + public IDirectory Directory + => _directoryMock; + + /// + public IDirectoryInfoFactory DirectoryInfo { get; } + + /// + public IDriveInfoFactory DriveInfo { get; } + + /// + public IFile File + => _fileMock; + + /// + public IFileInfoFactory FileInfo { get; } + + /// + public IFileStreamFactory FileStream { get; } + + /// + public IFileSystemWatcherFactory FileSystemWatcher { get; } + + /// + public IPath Path + => _pathMock; + + #endregion + + /// + /// Implements a custom access control (ACL) mechanism. + /// + /// The defines a method that receives two values and allows or denies access: + ///
+ /// - The full path of the file or directory as first parameter
+ /// - The as second parameter + ///
+ public MockFileSystem WithAccessControlStrategy(IAccessControlStrategy accessControlStrategy) + { + AccessControlStrategy = accessControlStrategy; + return this; + } + + /// + /// Changes the parameters of the specified . + /// + /// If the does not exist, it will be created/mounted. + /// + public MockFileSystem WithDrive(string? drive, + Action? driveCallback = null) + { + IStorageDrive driveInfoMock = + drive == null + ? Storage.MainDrive + : Storage.GetOrAddDrive(drive); + driveCallback?.Invoke(driveInfoMock); + return this; + } + + /// + /// Registers the strategy how to deal with s in the . + /// + /// Defaults to , if nothing is provided. + /// + public MockFileSystem WithSafeFileHandleStrategy( + ISafeFileHandleStrategy safeFileHandleStrategy) + { + SafeFileHandleStrategy = safeFileHandleStrategy; + return this; + } +} diff --git a/Source/Testably.Abstractions.Testing/Polyfills/FileFeatureExtensionMethods.cs b/Source/Testably.Abstractions.Testing/Polyfills/FileFeatureExtensionMethods.cs index ebf523e01..11d7edd8c 100644 --- a/Source/Testably.Abstractions.Testing/Polyfills/FileFeatureExtensionMethods.cs +++ b/Source/Testably.Abstractions.Testing/Polyfills/FileFeatureExtensionMethods.cs @@ -1,63 +1,62 @@ -#if !FEATURE_PATH_ADVANCED -using System.Diagnostics.CodeAnalysis; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; - -// ReSharper disable once CheckNamespace -namespace Testably.Abstractions.Testing; - -/// -/// Provides extension methods to simplify writing platform independent tests. -/// -[ExcludeFromCodeCoverage] -internal static class FileFeatureExtensionMethods -{ - /// - /// Trims one trailing directory separator beyond the root of the path. - /// - internal static string TrimEndingDirectorySeparator( - this IPath pathSystem, - string path) - { - return TrimEndingDirectorySeparator(path, pathSystem.DirectorySeparatorChar, - pathSystem.AltDirectorySeparatorChar); - } - - internal static string TrimEndingDirectorySeparator( - string path, char directorySeparatorChar, char altDirectorySeparatorChar) - { - if (string.IsNullOrEmpty(path)) - { - return path; - } - - string trimmed = path.TrimEnd(directorySeparatorChar, - altDirectorySeparatorChar); - - return Execute.OnWindows( - () => - { - if (trimmed.Length == 2 - && char.IsLetter(trimmed[0]) - && trimmed[1] == ':') - { - return trimmed + directorySeparatorChar; - } - - return null; - }, - () => - { - if ((path[0] == directorySeparatorChar || - path[0] == altDirectorySeparatorChar) - && trimmed == "") - { - return directorySeparatorChar.ToString(); - } - - return null; - }) - ?? trimmed; - } -} -#endif +#if !FEATURE_PATH_ADVANCED +using System.Diagnostics.CodeAnalysis; +using Testably.Abstractions.Testing.Helpers; + +// ReSharper disable once CheckNamespace +namespace Testably.Abstractions.Testing; + +/// +/// Provides extension methods to simplify writing platform independent tests. +/// +[ExcludeFromCodeCoverage] +internal static class FileFeatureExtensionMethods +{ + /// + /// Trims one trailing directory separator beyond the root of the path. + /// + internal static string TrimEndingDirectorySeparator( + this IPath pathSystem, + string path) + { + return TrimEndingDirectorySeparator(path, pathSystem.DirectorySeparatorChar, + pathSystem.AltDirectorySeparatorChar); + } + + internal static string TrimEndingDirectorySeparator( + string path, char directorySeparatorChar, char altDirectorySeparatorChar) + { + if (string.IsNullOrEmpty(path)) + { + return path; + } + + string trimmed = path.TrimEnd(directorySeparatorChar, + altDirectorySeparatorChar); + + return Execute.OnWindows( + () => + { + if (trimmed.Length == 2 + && char.IsLetter(trimmed[0]) + && trimmed[1] == ':') + { + return trimmed + directorySeparatorChar; + } + + return null; + }, + () => + { + if ((path[0] == directorySeparatorChar || + path[0] == altDirectorySeparatorChar) + && trimmed == "") + { + return directorySeparatorChar.ToString(); + } + + return null; + }) + ?? trimmed; + } +} +#endif diff --git a/Source/Testably.Abstractions.Testing/Storage/IStorage.cs b/Source/Testably.Abstractions.Testing/Storage/IStorage.cs index 3f07d9033..c0bbe2723 100644 --- a/Source/Testably.Abstractions.Testing/Storage/IStorage.cs +++ b/Source/Testably.Abstractions.Testing/Storage/IStorage.cs @@ -1,204 +1,203 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; - -namespace Testably.Abstractions.Testing.Storage; - -/// -/// The container storing the current data of the in memory. -/// -internal interface IStorage -{ - /// - /// The current directory used in and - /// - /// - string CurrentDirectory { get; set; } - - /// - /// The main drive. - /// - IStorageDrive MainDrive { get; } - - /// - /// Copies a specified file to a new location.
- /// This method does work across volumes. - ///
- /// The source location. - /// The destination location. - /// - /// to overwrite the , - /// otherwise . - /// - /// - /// The new location of the file.
- /// Returns when the does not exist. - ///
- IStorageLocation? Copy(IStorageLocation source, - IStorageLocation destination, - bool overwrite = false); - - /// - /// Deletes the container stored at . - /// - /// The location at which the container is located. - /// (optional) Is set, also child-containers are deleted recursively. - /// , if the container was found and deleted, otherwise . - bool DeleteContainer(IStorageLocation location, bool recursive = false); - - /// - /// Enumerate the locations of files or directories stored under . - /// - /// The parent location in which the files or directories are searched for. - /// The type of the container (file, directory or both). - /// - /// (optional) The expression to filter the name of the locations. - /// - /// Defaults to "*" which matches any name. - /// - /// - /// (optional) The enumeration options. - /// - /// Defaults to which uses the `Compatible` enumeration options:
- /// - Search only in Top-Directory
- /// - Use Win32 expression MatchType - /// - /// - /// The location of files or directories that match the , - /// and . - /// - IEnumerable EnumerateLocations( - IStorageLocation location, - FileSystemTypes type, - string searchPattern = EnumerationOptionsHelper.DefaultSearchPattern, - EnumerationOptions? enumerationOptions = null); - - /// - /// Gets the container at . - /// - /// If the container is not found, returns a . - /// - /// The location at which to look for the container. - /// - /// , if is null. Otherwise it returns the found container or - /// . - /// - [return: NotNullIfNotNull("location")] - IStorageContainer? GetContainer(IStorageLocation? location); - - /// - /// Returns the drive if it is present.
- /// Returns , if the drive does not exist. - ///
- IStorageDrive? GetDrive(string? driveName); - - /// - /// Returns the drives that are present. - /// - IEnumerable GetDrives(); - - /// - /// Gets the location for the given . - /// - /// The path. - /// (optional) the friendly name for the . - /// The that corresponds to . - [return: NotNullIfNotNull("path")] - IStorageLocation? GetLocation(string? path, string? friendlyName = null); - - /// - /// Returns the drives that are present. - /// - IStorageDrive GetOrAddDrive(string driveName); - - /// - /// Returns an existing container at .
- /// If no container exists yet, creates a new container at this using the - /// and returns the new generated container. - ///
- /// The location at which to get or create the container. - /// The callback used to create a new container at . - /// - /// The container at . - IStorageContainer GetOrCreateContainer(IStorageLocation location, - Func containerGenerator, - IFileSystemExtensibility? fileSystemExtensibility = null); - - /// - /// Moves a specified file or directory to a new location and potentially a new file name.
- /// This method does work across volumes. - ///
- /// The source location. - /// The destination location. - /// - /// to overwrite the , - /// otherwise . - /// - /// - /// to recursively move child elements the , - /// otherwise . - /// - /// - /// The new location of the file or directory.
- /// Returns when the does not exist. - ///
- IStorageLocation? Move(IStorageLocation source, - IStorageLocation destination, - bool overwrite = false, - bool recursive = false); - - /// - /// Replaces the file with the and moving it to the - /// location.
- /// This method does work across volumes. - ///
- /// The source location. - /// The destination location. - /// The backup location. - /// - /// to ignore merge errors (such as attributes and access control lists (ACLs)) from the - /// replaced file; - /// otherwise . - /// - /// - /// The new location of the file.
- /// Returns when the or the does - /// not exist. - ///
- IStorageLocation? Replace(IStorageLocation source, - IStorageLocation destination, - IStorageLocation? backup, - bool ignoreMetadataErrors = false); - -#if FEATURE_FILESYSTEM_LINK - /// - /// Resolves the link target of the container stored at . - /// - /// The location to start looking up the link targets. - /// - /// (optional) to follow links to the final target; to return the - /// immediate next link. - /// - /// The location of the link target. - IStorageLocation? ResolveLinkTarget(IStorageLocation location, - bool returnFinalTarget = false); -#endif - - /// - /// Tries to add a new container at using the . - /// - /// The location at which to add a new container. - /// The callback used to create a new container at . - /// (out) The created container if successful, otherwise . - /// - /// if the container could be created at , - /// otherwise . - /// - bool TryAddContainer(IStorageLocation location, - Func - containerGenerator, - [NotNullWhen(true)] out IStorageContainer? container); -} +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.Storage; + +/// +/// The container storing the current data of the in memory. +/// +internal interface IStorage +{ + /// + /// The current directory used in and + /// + /// + string CurrentDirectory { get; set; } + + /// + /// The main drive. + /// + IStorageDrive MainDrive { get; } + + /// + /// Copies a specified file to a new location.
+ /// This method does work across volumes. + ///
+ /// The source location. + /// The destination location. + /// + /// to overwrite the , + /// otherwise . + /// + /// + /// The new location of the file.
+ /// Returns when the does not exist. + ///
+ IStorageLocation? Copy(IStorageLocation source, + IStorageLocation destination, + bool overwrite = false); + + /// + /// Deletes the container stored at . + /// + /// The location at which the container is located. + /// (optional) Is set, also child-containers are deleted recursively. + /// , if the container was found and deleted, otherwise . + bool DeleteContainer(IStorageLocation location, bool recursive = false); + + /// + /// Enumerate the locations of files or directories stored under . + /// + /// The parent location in which the files or directories are searched for. + /// The type of the container (file, directory or both). + /// + /// (optional) The expression to filter the name of the locations. + /// + /// Defaults to "*" which matches any name. + /// + /// + /// (optional) The enumeration options. + /// + /// Defaults to which uses the `Compatible` enumeration options:
+ /// - Search only in Top-Directory
+ /// - Use Win32 expression MatchType + /// + /// + /// The location of files or directories that match the , + /// and . + /// + IEnumerable EnumerateLocations( + IStorageLocation location, + FileSystemTypes type, + string searchPattern = EnumerationOptionsHelper.DefaultSearchPattern, + EnumerationOptions? enumerationOptions = null); + + /// + /// Gets the container at . + /// + /// If the container is not found, returns a . + /// + /// The location at which to look for the container. + /// + /// , if is null. Otherwise it returns the found container or + /// . + /// + [return: NotNullIfNotNull("location")] + IStorageContainer? GetContainer(IStorageLocation? location); + + /// + /// Returns the drive if it is present.
+ /// Returns , if the drive does not exist. + ///
+ IStorageDrive? GetDrive(string? driveName); + + /// + /// Returns the drives that are present. + /// + IEnumerable GetDrives(); + + /// + /// Gets the location for the given . + /// + /// The path. + /// (optional) the friendly name for the . + /// The that corresponds to . + [return: NotNullIfNotNull("path")] + IStorageLocation? GetLocation(string? path, string? friendlyName = null); + + /// + /// Returns the drives that are present. + /// + IStorageDrive GetOrAddDrive(string driveName); + + /// + /// Returns an existing container at .
+ /// If no container exists yet, creates a new container at this using the + /// and returns the new generated container. + ///
+ /// The location at which to get or create the container. + /// The callback used to create a new container at . + /// + /// The container at . + IStorageContainer GetOrCreateContainer(IStorageLocation location, + Func containerGenerator, + IFileSystemExtensibility? fileSystemExtensibility = null); + + /// + /// Moves a specified file or directory to a new location and potentially a new file name.
+ /// This method does work across volumes. + ///
+ /// The source location. + /// The destination location. + /// + /// to overwrite the , + /// otherwise . + /// + /// + /// to recursively move child elements the , + /// otherwise . + /// + /// + /// The new location of the file or directory.
+ /// Returns when the does not exist. + ///
+ IStorageLocation? Move(IStorageLocation source, + IStorageLocation destination, + bool overwrite = false, + bool recursive = false); + + /// + /// Replaces the file with the and moving it to the + /// location.
+ /// This method does work across volumes. + ///
+ /// The source location. + /// The destination location. + /// The backup location. + /// + /// to ignore merge errors (such as attributes and access control lists (ACLs)) from the + /// replaced file; + /// otherwise . + /// + /// + /// The new location of the file.
+ /// Returns when the or the does + /// not exist. + ///
+ IStorageLocation? Replace(IStorageLocation source, + IStorageLocation destination, + IStorageLocation? backup, + bool ignoreMetadataErrors = false); + +#if FEATURE_FILESYSTEM_LINK + /// + /// Resolves the link target of the container stored at . + /// + /// The location to start looking up the link targets. + /// + /// (optional) to follow links to the final target; to return the + /// immediate next link. + /// + /// The location of the link target. + IStorageLocation? ResolveLinkTarget(IStorageLocation location, + bool returnFinalTarget = false); +#endif + + /// + /// Tries to add a new container at using the . + /// + /// The location at which to add a new container. + /// The callback used to create a new container at . + /// (out) The created container if successful, otherwise . + /// + /// if the container could be created at , + /// otherwise . + /// + bool TryAddContainer(IStorageLocation location, + Func + containerGenerator, + [NotNullWhen(true)] out IStorageContainer? container); +} diff --git a/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs b/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs index b5dfa27fb..a5c48650d 100644 --- a/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs +++ b/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs @@ -1,110 +1,109 @@ -using System; -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.TimeSystem; - -namespace Testably.Abstractions.Testing.Storage; - -/// -/// A container for a stored file or directory in the . -/// -internal interface IStorageContainer : IFileSystemEntity, - ITimeSystemEntity -{ - /// - FileAttributes Attributes { get; set; } - - /// - ITimeContainer CreationTime { get; } - - /// - /// A container to support extensions on . - /// - IFileSystemExtensibility Extensibility { get; } - - /// - ITimeContainer LastAccessTime { get; } - - /// - ITimeContainer LastWriteTime { get; } - - /// - /// If this instance represents a link, returns the link target's path, otherwise returns . - /// - string? LinkTarget { get; set; } - - /// - /// The type of the container indicates if it is a or - /// . - /// - FileSystemTypes Type { get; } - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - /// Gets or sets the Unix file mode for the current file or directory.
- /// See also: - ///
- UnixFileMode UnixFileMode { get; set; } -#endif - - /// - /// Appends the to the . - /// - void AppendBytes(byte[] bytes); - - /// - /// Clears the content of the . - /// - /// This is used to delete the file. - /// - void ClearBytes(); - - /// - /// Decrypts the file content and removes the attribute. - /// - /// Does nothing if the file is not encrypted. - /// - void Decrypt(); - - /// - /// Encrypts the file content and adds the attribute. - /// - /// Does nothing if the file is already encrypted. - /// - void Encrypt(); - - /// - /// Gets the bytes in the . - /// - byte[] GetBytes(); - - /// - /// Requests access to this file with the given . - /// - /// An that is used to release the access lock on dispose. - IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, - bool deleteAccess = false, - bool ignoreMetadataErrors = true, - int? hResult = null); - - /// - /// Writes the to the . - /// - void WriteBytes(byte[] bytes); - - /// - /// A container to allow reading/writing s with consistent . - /// - public interface ITimeContainer - { - /// - /// Get the in the given . - /// - DateTime Get(DateTimeKind kind); - - /// - /// Set the to the in the given . - /// - void Set(DateTime time, DateTimeKind kind); - } -} +using System; +using System.IO; +using Testably.Abstractions.TimeSystem; + +namespace Testably.Abstractions.Testing.Storage; + +/// +/// A container for a stored file or directory in the . +/// +internal interface IStorageContainer : IFileSystemEntity, + ITimeSystemEntity +{ + /// + FileAttributes Attributes { get; set; } + + /// + ITimeContainer CreationTime { get; } + + /// + /// A container to support extensions on . + /// + IFileSystemExtensibility Extensibility { get; } + + /// + ITimeContainer LastAccessTime { get; } + + /// + ITimeContainer LastWriteTime { get; } + + /// + /// If this instance represents a link, returns the link target's path, otherwise returns . + /// + string? LinkTarget { get; set; } + + /// + /// The type of the container indicates if it is a or + /// . + /// + FileSystemTypes Type { get; } + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + /// Gets or sets the Unix file mode for the current file or directory.
+ /// See also: + ///
+ UnixFileMode UnixFileMode { get; set; } +#endif + + /// + /// Appends the to the . + /// + void AppendBytes(byte[] bytes); + + /// + /// Clears the content of the . + /// + /// This is used to delete the file. + /// + void ClearBytes(); + + /// + /// Decrypts the file content and removes the attribute. + /// + /// Does nothing if the file is not encrypted. + /// + void Decrypt(); + + /// + /// Encrypts the file content and adds the attribute. + /// + /// Does nothing if the file is already encrypted. + /// + void Encrypt(); + + /// + /// Gets the bytes in the . + /// + byte[] GetBytes(); + + /// + /// Requests access to this file with the given . + /// + /// An that is used to release the access lock on dispose. + IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, + bool deleteAccess = false, + bool ignoreMetadataErrors = true, + int? hResult = null); + + /// + /// Writes the to the . + /// + void WriteBytes(byte[] bytes); + + /// + /// A container to allow reading/writing s with consistent . + /// + public interface ITimeContainer + { + /// + /// Get the in the given . + /// + DateTime Get(DateTimeKind kind); + + /// + /// Set the to the in the given . + /// + void Set(DateTime time, DateTimeKind kind); + } +} diff --git a/Source/Testably.Abstractions.Testing/Storage/IStorageDrive.cs b/Source/Testably.Abstractions.Testing/Storage/IStorageDrive.cs index 5f69637eb..917ccab4a 100644 --- a/Source/Testably.Abstractions.Testing/Storage/IStorageDrive.cs +++ b/Source/Testably.Abstractions.Testing/Storage/IStorageDrive.cs @@ -1,48 +1,47 @@ -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystem; - -namespace Testably.Abstractions.Testing.Storage; - -/// -/// A which allows to be manipulated. -/// -public interface IStorageDrive : IDriveInfo -{ - /// - /// Flag indicating if the drive is a UNC drive - /// - bool IsUncPath { get; } - - /// - /// Changes the currently used bytes by . - /// - /// Throws an if the becomes - /// negative. - /// - IStorageDrive ChangeUsedBytes(long usedBytesDelta); - - /// - /// Changes the of the mocked . - /// - IStorageDrive SetDriveFormat( - string driveFormat = DriveInfoMock.DefaultDriveFormat); - - /// - /// Changes the of the mocked . - /// - IStorageDrive SetDriveType( - DriveType driveType = DriveInfoMock.DefaultDriveType); - - /// - /// Changes the property of the mocked - /// . - /// - IStorageDrive SetIsReady(bool isReady = true); - - /// - /// Changes the total size of the mocked . - /// - IStorageDrive SetTotalSize( - long totalSize = DriveInfoMock.DefaultTotalSize); -} +using System.IO; +using Testably.Abstractions.Testing.FileSystem; + +namespace Testably.Abstractions.Testing.Storage; + +/// +/// A which allows to be manipulated. +/// +public interface IStorageDrive : IDriveInfo +{ + /// + /// Flag indicating if the drive is a UNC drive + /// + bool IsUncPath { get; } + + /// + /// Changes the currently used bytes by . + /// + /// Throws an if the becomes + /// negative. + /// + IStorageDrive ChangeUsedBytes(long usedBytesDelta); + + /// + /// Changes the of the mocked . + /// + IStorageDrive SetDriveFormat( + string driveFormat = DriveInfoMock.DefaultDriveFormat); + + /// + /// Changes the of the mocked . + /// + IStorageDrive SetDriveType( + DriveType driveType = DriveInfoMock.DefaultDriveType); + + /// + /// Changes the property of the mocked + /// . + /// + IStorageDrive SetIsReady(bool isReady = true); + + /// + /// Changes the total size of the mocked . + /// + IStorageDrive SetTotalSize( + long totalSize = DriveInfoMock.DefaultTotalSize); +} diff --git a/Source/Testably.Abstractions.Testing/Storage/IStorageLocation.cs b/Source/Testably.Abstractions.Testing/Storage/IStorageLocation.cs index b36c6eaab..41df415b0 100644 --- a/Source/Testably.Abstractions.Testing/Storage/IStorageLocation.cs +++ b/Source/Testably.Abstractions.Testing/Storage/IStorageLocation.cs @@ -1,39 +1,38 @@ -using System; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Testing.Storage; - -/// -/// The location where are located in the . -/// -internal interface IStorageLocation : IEquatable -{ - /// - /// The in which the is stored. - /// - IStorageDrive? Drive { get; } - - /// - /// The friendly name from the location of the file or directory. - /// - string FriendlyName { get; } - - /// - /// The full path of the location of the file or directory. - /// - string FullPath { get; } - - /// - /// Flag indicating if the location corresponds to a root directory. - /// - bool IsRooted { get; } - - /// - /// Get the parent location. - /// - /// - /// The parent or when the location corresponds to a root - /// directory. - /// - IStorageLocation? GetParent(); -} +using System; + +namespace Testably.Abstractions.Testing.Storage; + +/// +/// The location where are located in the . +/// +internal interface IStorageLocation : IEquatable +{ + /// + /// The in which the is stored. + /// + IStorageDrive? Drive { get; } + + /// + /// The friendly name from the location of the file or directory. + /// + string FriendlyName { get; } + + /// + /// The full path of the location of the file or directory. + /// + string FullPath { get; } + + /// + /// Flag indicating if the location corresponds to a root directory. + /// + bool IsRooted { get; } + + /// + /// Get the parent location. + /// + /// + /// The parent or when the location corresponds to a root + /// directory. + /// + IStorageLocation? GetParent(); +} diff --git a/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs b/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs index e0f04aed4..49ac53c5e 100644 --- a/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs +++ b/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs @@ -1,374 +1,373 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.TimeSystem; -using static Testably.Abstractions.Testing.Storage.IStorageContainer; - -namespace Testably.Abstractions.Testing.Storage; - -internal class InMemoryContainer : IStorageContainer -{ - private FileAttributes _attributes; - private byte[] _bytes = Array.Empty(); - private readonly ConcurrentDictionary _fileHandles = new(); - private readonly MockFileSystem _fileSystem; - private bool _isEncrypted; - private readonly IStorageLocation _location; - private readonly FileSystemExtensibility _extensibility = new(); - - public InMemoryContainer(FileSystemTypes type, - IStorageLocation location, - MockFileSystem fileSystem) - { - _location = location; - _fileSystem = fileSystem; - Type = type; - this.AdjustTimes(TimeAdjustments.All); - } - - #region IStorageContainer Members - - /// - public FileAttributes Attributes - { - get => AdjustAttributes(_attributes); - set - { - value &= Execute.OnWindows( - () => FileAttributes.Directory | - FileAttributes.ReadOnly | - FileAttributes.Archive | - FileAttributes.Hidden | - FileAttributes.NoScrubData | - FileAttributes.NotContentIndexed | - FileAttributes.Offline | - FileAttributes.System | - FileAttributes.Temporary, - () => Execute.OnLinux( - () => FileAttributes.Directory | - FileAttributes.ReadOnly, - () => FileAttributes.Hidden | - FileAttributes.Directory | - FileAttributes.ReadOnly)); - - _attributes = value; - } - } - - /// - public ITimeContainer CreationTime { get; } = new TimeContainer(); - - /// - public IFileSystemExtensibility Extensibility - => _extensibility; - - /// - public IFileSystem FileSystem => _fileSystem; - - /// - public ITimeContainer LastAccessTime { get; } = new TimeContainer(); - - /// - public ITimeContainer LastWriteTime { get; } = new TimeContainer(); - - /// - public string? LinkTarget { get; set; } - - /// - public ITimeSystem TimeSystem => _fileSystem.TimeSystem; - - /// - public FileSystemTypes Type { get; } - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - public UnixFileMode UnixFileMode { get; set; } = (UnixFileMode)(-1); -#endif - - /// - public void AppendBytes(byte[] bytes) - { - WriteBytes(_bytes.Concat(bytes).ToArray()); - } - - /// - public void ClearBytes() - { - _location.Drive?.ChangeUsedBytes(0 - _bytes.Length); - _bytes = Array.Empty(); - } - - /// - public void Decrypt() - { - if (!_isEncrypted) - { - return; - } - - using (RequestAccess(FileAccess.Write, FileShare.Read)) - { - _isEncrypted = false; - WriteBytes(EncryptionHelper.Decrypt(GetBytes())); - } - } - - /// - public void Encrypt() - { - if (_isEncrypted) - { - return; - } - - using (RequestAccess(FileAccess.Write, FileShare.Read)) - { - _isEncrypted = true; - WriteBytes(EncryptionHelper.Encrypt(GetBytes())); - } - } - - /// - public byte[] GetBytes() => _bytes; - - /// - public IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, - bool deleteAccess = false, - bool ignoreMetadataErrors = true, - int? hResult = null) - { - if (_location.Drive == null) - { - throw ExceptionFactory.DirectoryNotFound(_location.FullPath); - } - - if (!_location.Drive.IsReady) - { - throw ExceptionFactory.NetworkPathNotFound(_location.FullPath); - } - - Execute.OnWindowsIf( - !ignoreMetadataErrors && Attributes.HasFlag(FileAttributes.ReadOnly), - () => throw ExceptionFactory.AccessToPathDenied()); - - if (!_fileSystem.AccessControlStrategy - .IsAccessGranted(_location.FullPath, Extensibility)) - { - throw ExceptionFactory.AclAccessToPathDenied(_location.FullPath); - } - - if (CanGetAccess(access, share, deleteAccess)) - { - Guid guid = Guid.NewGuid(); - FileHandle fileHandle = new(guid, ReleaseAccess, access, share, deleteAccess); - _fileHandles.TryAdd(guid, fileHandle); - return fileHandle; - } - - throw ExceptionFactory.ProcessCannotAccessTheFile(_location.FullPath, - hResult ?? -2147024864); - } - - /// - public void WriteBytes(byte[] bytes) - { - NotifyFilters notifyFilters = NotifyFilters.LastAccess | - NotifyFilters.LastWrite | - NotifyFilters.Size; - Execute.OnLinux(() - => notifyFilters |= NotifyFilters.Security); - Execute.OnMac(() - => notifyFilters |= NotifyFilters.CreationTime); - - TimeAdjustments timeAdjustment = TimeAdjustments.LastWriteTime; - Execute.OnWindows(() - => timeAdjustment |= TimeAdjustments.LastAccessTime); - - ChangeDescription fileSystemChange = - _fileSystem.ChangeHandler.NotifyPendingChange(WatcherChangeTypes.Changed, - FileSystemTypes.File, - notifyFilters, - _location); - _location.Drive?.ChangeUsedBytes(bytes.Length - _bytes.Length); - _bytes = bytes; - this.AdjustTimes(timeAdjustment); - _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); - } - - #endregion - - /// - /// Create a new directory on the . - /// - public static IStorageContainer NewDirectory(IStorageLocation location, - MockFileSystem fileSystem) - { - return new InMemoryContainer(FileSystemTypes.Directory, location, - fileSystem); - } - - /// - /// Create a new file on the . - /// - public static IStorageContainer NewFile(IStorageLocation location, - MockFileSystem fileSystem) - { - return new InMemoryContainer(FileSystemTypes.File, location, - fileSystem); - } - - internal FileAttributes AdjustAttributes(FileAttributes attributes) - { - if (Path.GetFileName(_location.FullPath).StartsWith(".")) - { - FileAttributes attr = attributes; - attributes = Execute.OnLinux( - () => attr | FileAttributes.Hidden, - () => attr); - } - -#if FEATURE_FILESYSTEM_LINK - if (LinkTarget != null) - { - attributes |= FileAttributes.ReparsePoint; - } -#endif - - if (_isEncrypted) - { - attributes |= FileAttributes.Encrypted; - } - - if (attributes == 0) - { - return FileAttributes.Normal; - } - - return attributes; - } - - private bool CanGetAccess(FileAccess access, FileShare share, bool deleteAccess) - { - foreach (KeyValuePair fileHandle in _fileHandles) - { - if (!fileHandle.Value.GrantAccess(access, share, deleteAccess)) - { - return false; - } - } - - return true; - } - - private void ReleaseAccess(Guid guid) - { - _fileHandles.TryRemove(guid, out _); - } - - internal sealed class TimeContainer : ITimeContainer - { - private DateTime _time; - - #region ITimeContainer Members - - /// - public DateTime Get(DateTimeKind kind) - => kind switch - { - DateTimeKind.Utc => _time.ToUniversalTime(), - DateTimeKind.Local => _time.ToLocalTime(), - _ => _time - }; - - /// - public void Set(DateTime time, DateTimeKind kind) - { - if (time.Kind == DateTimeKind.Unspecified) - { - _time = DateTime.SpecifyKind(time, kind); - } - else - { - _time = time; - } - } - - #endregion - } - - private sealed class FileHandle : IStorageAccessHandle - { - private readonly Guid _key; - private readonly Action _releaseCallback; - - public FileHandle(Guid key, Action releaseCallback, FileAccess access, - FileShare share, bool deleteAccess) - { - _releaseCallback = releaseCallback; - Access = access; - DeleteAccess = deleteAccess; - Share = Execute.OnWindows( - () => share, - () => share == FileShare.None - ? FileShare.None - : FileShare.ReadWrite); - - _key = key; - } - - #region IStorageAccessHandle Members - - /// - public FileAccess Access { get; } - - /// - public bool DeleteAccess { get; } - - /// - public FileShare Share { get; } - - /// - public void Dispose() - { - _releaseCallback.Invoke(_key); - } - - #endregion - - public bool GrantAccess(FileAccess access, FileShare share, bool deleteAccess) - { - FileShare usedShare = share; - Execute.NotOnWindows(() - => usedShare = FileShare.ReadWrite); - if (deleteAccess) - { - return !Execute.IsWindows || Share == FileShare.Delete; - } - - return CheckAccessWithShare(access, Share) && - CheckAccessWithShare(Access, usedShare); - } - - /// - public override string ToString() - => $"{Access} | {Share}"; - - private static bool CheckAccessWithShare(FileAccess access, FileShare share) - { - switch (access) - { - case FileAccess.Read: - return share.HasFlag(FileShare.Read); - case FileAccess.Write: - return share.HasFlag(FileShare.Write); - default: - return share == FileShare.ReadWrite; - } - } - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystem; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.TimeSystem; +using static Testably.Abstractions.Testing.Storage.IStorageContainer; + +namespace Testably.Abstractions.Testing.Storage; + +internal class InMemoryContainer : IStorageContainer +{ + private FileAttributes _attributes; + private byte[] _bytes = Array.Empty(); + private readonly ConcurrentDictionary _fileHandles = new(); + private readonly MockFileSystem _fileSystem; + private bool _isEncrypted; + private readonly IStorageLocation _location; + private readonly FileSystemExtensibility _extensibility = new(); + + public InMemoryContainer(FileSystemTypes type, + IStorageLocation location, + MockFileSystem fileSystem) + { + _location = location; + _fileSystem = fileSystem; + Type = type; + this.AdjustTimes(TimeAdjustments.All); + } + + #region IStorageContainer Members + + /// + public FileAttributes Attributes + { + get => AdjustAttributes(_attributes); + set + { + value &= Execute.OnWindows( + () => FileAttributes.Directory | + FileAttributes.ReadOnly | + FileAttributes.Archive | + FileAttributes.Hidden | + FileAttributes.NoScrubData | + FileAttributes.NotContentIndexed | + FileAttributes.Offline | + FileAttributes.System | + FileAttributes.Temporary, + () => Execute.OnLinux( + () => FileAttributes.Directory | + FileAttributes.ReadOnly, + () => FileAttributes.Hidden | + FileAttributes.Directory | + FileAttributes.ReadOnly)); + + _attributes = value; + } + } + + /// + public ITimeContainer CreationTime { get; } = new TimeContainer(); + + /// + public IFileSystemExtensibility Extensibility + => _extensibility; + + /// + public IFileSystem FileSystem => _fileSystem; + + /// + public ITimeContainer LastAccessTime { get; } = new TimeContainer(); + + /// + public ITimeContainer LastWriteTime { get; } = new TimeContainer(); + + /// + public string? LinkTarget { get; set; } + + /// + public ITimeSystem TimeSystem => _fileSystem.TimeSystem; + + /// + public FileSystemTypes Type { get; } + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + public UnixFileMode UnixFileMode { get; set; } = (UnixFileMode)(-1); +#endif + + /// + public void AppendBytes(byte[] bytes) + { + WriteBytes(_bytes.Concat(bytes).ToArray()); + } + + /// + public void ClearBytes() + { + _location.Drive?.ChangeUsedBytes(0 - _bytes.Length); + _bytes = Array.Empty(); + } + + /// + public void Decrypt() + { + if (!_isEncrypted) + { + return; + } + + using (RequestAccess(FileAccess.Write, FileShare.Read)) + { + _isEncrypted = false; + WriteBytes(EncryptionHelper.Decrypt(GetBytes())); + } + } + + /// + public void Encrypt() + { + if (_isEncrypted) + { + return; + } + + using (RequestAccess(FileAccess.Write, FileShare.Read)) + { + _isEncrypted = true; + WriteBytes(EncryptionHelper.Encrypt(GetBytes())); + } + } + + /// + public byte[] GetBytes() => _bytes; + + /// + public IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, + bool deleteAccess = false, + bool ignoreMetadataErrors = true, + int? hResult = null) + { + if (_location.Drive == null) + { + throw ExceptionFactory.DirectoryNotFound(_location.FullPath); + } + + if (!_location.Drive.IsReady) + { + throw ExceptionFactory.NetworkPathNotFound(_location.FullPath); + } + + Execute.OnWindowsIf( + !ignoreMetadataErrors && Attributes.HasFlag(FileAttributes.ReadOnly), + () => throw ExceptionFactory.AccessToPathDenied()); + + if (!_fileSystem.AccessControlStrategy + .IsAccessGranted(_location.FullPath, Extensibility)) + { + throw ExceptionFactory.AclAccessToPathDenied(_location.FullPath); + } + + if (CanGetAccess(access, share, deleteAccess)) + { + Guid guid = Guid.NewGuid(); + FileHandle fileHandle = new(guid, ReleaseAccess, access, share, deleteAccess); + _fileHandles.TryAdd(guid, fileHandle); + return fileHandle; + } + + throw ExceptionFactory.ProcessCannotAccessTheFile(_location.FullPath, + hResult ?? -2147024864); + } + + /// + public void WriteBytes(byte[] bytes) + { + NotifyFilters notifyFilters = NotifyFilters.LastAccess | + NotifyFilters.LastWrite | + NotifyFilters.Size; + Execute.OnLinux(() + => notifyFilters |= NotifyFilters.Security); + Execute.OnMac(() + => notifyFilters |= NotifyFilters.CreationTime); + + TimeAdjustments timeAdjustment = TimeAdjustments.LastWriteTime; + Execute.OnWindows(() + => timeAdjustment |= TimeAdjustments.LastAccessTime); + + ChangeDescription fileSystemChange = + _fileSystem.ChangeHandler.NotifyPendingChange(WatcherChangeTypes.Changed, + FileSystemTypes.File, + notifyFilters, + _location); + _location.Drive?.ChangeUsedBytes(bytes.Length - _bytes.Length); + _bytes = bytes; + this.AdjustTimes(timeAdjustment); + _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); + } + + #endregion + + /// + /// Create a new directory on the . + /// + public static IStorageContainer NewDirectory(IStorageLocation location, + MockFileSystem fileSystem) + { + return new InMemoryContainer(FileSystemTypes.Directory, location, + fileSystem); + } + + /// + /// Create a new file on the . + /// + public static IStorageContainer NewFile(IStorageLocation location, + MockFileSystem fileSystem) + { + return new InMemoryContainer(FileSystemTypes.File, location, + fileSystem); + } + + internal FileAttributes AdjustAttributes(FileAttributes attributes) + { + if (Path.GetFileName(_location.FullPath).StartsWith(".")) + { + FileAttributes attr = attributes; + attributes = Execute.OnLinux( + () => attr | FileAttributes.Hidden, + () => attr); + } + +#if FEATURE_FILESYSTEM_LINK + if (LinkTarget != null) + { + attributes |= FileAttributes.ReparsePoint; + } +#endif + + if (_isEncrypted) + { + attributes |= FileAttributes.Encrypted; + } + + if (attributes == 0) + { + return FileAttributes.Normal; + } + + return attributes; + } + + private bool CanGetAccess(FileAccess access, FileShare share, bool deleteAccess) + { + foreach (KeyValuePair fileHandle in _fileHandles) + { + if (!fileHandle.Value.GrantAccess(access, share, deleteAccess)) + { + return false; + } + } + + return true; + } + + private void ReleaseAccess(Guid guid) + { + _fileHandles.TryRemove(guid, out _); + } + + internal sealed class TimeContainer : ITimeContainer + { + private DateTime _time; + + #region ITimeContainer Members + + /// + public DateTime Get(DateTimeKind kind) + => kind switch + { + DateTimeKind.Utc => _time.ToUniversalTime(), + DateTimeKind.Local => _time.ToLocalTime(), + _ => _time + }; + + /// + public void Set(DateTime time, DateTimeKind kind) + { + if (time.Kind == DateTimeKind.Unspecified) + { + _time = DateTime.SpecifyKind(time, kind); + } + else + { + _time = time; + } + } + + #endregion + } + + private sealed class FileHandle : IStorageAccessHandle + { + private readonly Guid _key; + private readonly Action _releaseCallback; + + public FileHandle(Guid key, Action releaseCallback, FileAccess access, + FileShare share, bool deleteAccess) + { + _releaseCallback = releaseCallback; + Access = access; + DeleteAccess = deleteAccess; + Share = Execute.OnWindows( + () => share, + () => share == FileShare.None + ? FileShare.None + : FileShare.ReadWrite); + + _key = key; + } + + #region IStorageAccessHandle Members + + /// + public FileAccess Access { get; } + + /// + public bool DeleteAccess { get; } + + /// + public FileShare Share { get; } + + /// + public void Dispose() + { + _releaseCallback.Invoke(_key); + } + + #endregion + + public bool GrantAccess(FileAccess access, FileShare share, bool deleteAccess) + { + FileShare usedShare = share; + Execute.NotOnWindows(() + => usedShare = FileShare.ReadWrite); + if (deleteAccess) + { + return !Execute.IsWindows || Share == FileShare.Delete; + } + + return CheckAccessWithShare(access, Share) && + CheckAccessWithShare(Access, usedShare); + } + + /// + public override string ToString() + => $"{Access} | {Share}"; + + private static bool CheckAccessWithShare(FileAccess access, FileShare share) + { + switch (access) + { + case FileAccess.Read: + return share.HasFlag(FileShare.Read); + case FileAccess.Write: + return share.HasFlag(FileShare.Write); + default: + return share == FileShare.ReadWrite; + } + } + } +} diff --git a/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs b/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs index 4043858ef..3b6bab25b 100644 --- a/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs +++ b/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs @@ -1,718 +1,717 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystem; -using Testably.Abstractions.Testing.Helpers; - -namespace Testably.Abstractions.Testing.Storage; - -/// -/// The container storing the current data of the in memory. -/// -internal sealed class InMemoryStorage : IStorage -{ - private readonly ConcurrentDictionary - _containers = new(); - - private readonly ConcurrentDictionary _drives = - new(StringComparer.OrdinalIgnoreCase); - - private readonly MockFileSystem _fileSystem; - - public InMemoryStorage(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - MainDrive = DriveInfoMock.New(CurrentDirectory, _fileSystem); - _drives.TryAdd(MainDrive.Name, MainDrive); - } - - #region IStorage Members - - /// - public string CurrentDirectory { get; set; } = string.Empty.PrefixRoot(); - - /// - public IStorageDrive MainDrive { get; } - - /// - public IStorageLocation? Copy(IStorageLocation source, - IStorageLocation destination, - bool overwrite = false) - { - ThrowIfParentDoesNotExist(destination, _ => ExceptionFactory.DirectoryNotFound()); - - if (!_containers.TryGetValue(source, - out IStorageContainer? sourceContainer)) - { - return null; - } - - if (sourceContainer.Type != FileSystemTypes.File) - { - throw ExceptionFactory.AccessToPathDenied(source.FullPath); - } - - using (_ = sourceContainer.RequestAccess(FileAccess.ReadWrite, FileShare.None)) - { - if (overwrite && - _containers.TryRemove(destination, - out IStorageContainer? existingContainer)) - { - existingContainer.ClearBytes(); - } - - IStorageContainer copiedContainer = - InMemoryContainer.NewFile(destination, _fileSystem); - if (_containers.TryAdd(destination, copiedContainer)) - { - copiedContainer.WriteBytes(sourceContainer.GetBytes().ToArray()); - Execute.NotOnWindows(() - => sourceContainer.AdjustTimes(TimeAdjustments.LastAccessTime)); - - copiedContainer.Attributes = sourceContainer.Attributes; - Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, - () => copiedContainer.Attributes |= FileAttributes.Archive); - Execute.NotOnWindows( - () => copiedContainer.CreationTime.Set( - sourceContainer.CreationTime.Get(DateTimeKind.Local), - DateTimeKind.Local)); - copiedContainer.LastWriteTime.Set( - sourceContainer.LastWriteTime.Get(DateTimeKind.Local), - DateTimeKind.Local); - return destination; - } - - throw ExceptionFactory.CannotCreateFileWhenAlreadyExists(Execute.IsWindows - ? -2147024816 - : 17); - } - } - - /// - public bool DeleteContainer(IStorageLocation location, bool recursive = false) - { - if (!_containers.TryGetValue(location, out IStorageContainer? container)) - { - IStorageLocation? parentLocation = location.GetParent(); - if (parentLocation != null && - !_containers.TryGetValue(parentLocation, out _)) - { - throw ExceptionFactory.DirectoryNotFound(parentLocation.FullPath); - } - - return false; - } - - if (container.Type == FileSystemTypes.Directory) - { - IEnumerable children = - EnumerateLocations(location, FileSystemTypes.DirectoryOrFile); - if (recursive) - { - foreach (IStorageLocation key in children) - { - DeleteContainer(key); - } - } - else if (children.Any()) - { - throw ExceptionFactory.DirectoryNotEmpty(location.FullPath); - } - } - - NotifyFilters notifyFilters = - container.Type == FileSystemTypes.Directory - ? NotifyFilters.DirectoryName - : NotifyFilters.FileName; - ChangeDescription fileSystemChange = - _fileSystem.ChangeHandler.NotifyPendingChange(WatcherChangeTypes.Deleted, - container.Type, - notifyFilters, location); - - using (container.RequestAccess(FileAccess.Write, FileShare.ReadWrite, - deleteAccess: true)) - { - if (_containers.TryRemove(location, out IStorageContainer? removed)) - { - removed.ClearBytes(); - _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); - CheckAndAdjustParentDirectoryTimes(location); - return true; - } - } - - return false; - } - - /// - public IEnumerable EnumerateLocations( - IStorageLocation location, - FileSystemTypes type, - string searchPattern = EnumerationOptionsHelper.DefaultSearchPattern, - EnumerationOptions? enumerationOptions = null) - { - ValidateExpression(searchPattern); - if (!_containers.ContainsKey(location)) - { - throw ExceptionFactory.DirectoryNotFound(location.FullPath); - } - - enumerationOptions ??= EnumerationOptionsHelper.Compatible; - - string fullPath = location.FullPath; -#if NETSTANDARD2_0 - if (!fullPath.EndsWith($"{_fileSystem.Path.DirectorySeparatorChar}")) -#else - if (!fullPath.EndsWith(_fileSystem.Path.DirectorySeparatorChar)) -#endif - { - fullPath += _fileSystem.Path.DirectorySeparatorChar; - } - - foreach (KeyValuePair item in _containers - .Where(x => x.Key.FullPath.StartsWith(fullPath, - InMemoryLocation.StringComparisonMode) && - !x.Key.Equals(location))) - { - string? parentPath = - _fileSystem.Path.GetDirectoryName( - item.Key.FullPath.TrimEnd(_fileSystem.Path - .DirectorySeparatorChar)); - if (!enumerationOptions.RecurseSubdirectories && - parentPath?.Equals(location.FullPath, - InMemoryLocation.StringComparisonMode) != true) - { - continue; - } - - if (!EnumerationOptionsHelper.MatchesPattern(enumerationOptions, - _fileSystem.Path.GetFileName(item.Key.FullPath), searchPattern)) - { - continue; - } - - if (type.HasFlag(item.Value.Type)) - { - yield return item.Key; - } - } - } - - /// - [return: NotNullIfNotNull("location")] - public IStorageContainer? GetContainer(IStorageLocation? location) - { - if (location == null) - { - return null; - } - - if (_containers.TryGetValue(location, out IStorageContainer? container)) - { - return container; - } - - return NullContainer.New(_fileSystem); - } - - /// - public IStorageDrive? GetDrive(string? driveName) - { - if (string.IsNullOrWhiteSpace(driveName)) - { - return null; - } - - if (!driveName.IsUncPath()) - { - driveName = _fileSystem.Path.GetPathRoot(driveName); - - if (string.IsNullOrEmpty(driveName)) - { - return null; - } - } - - DriveInfoMock drive = DriveInfoMock.New(driveName, _fileSystem); - if (_drives.TryGetValue(drive.Name, out IStorageDrive? d)) - { - return d; - } - - return null; - } - - /// - public IEnumerable GetDrives() - => _drives.Values; - - /// - [return: NotNullIfNotNull("path")] - public IStorageLocation? GetLocation(string? path, string? friendlyName = null) - { - if (path == null) - { - return null; - } - - IStorageDrive? drive = _fileSystem.Storage.GetDrive(path); - if (drive == null && - !_fileSystem.Path.IsPathRooted(path)) - { - drive = _fileSystem.Storage.MainDrive; - } - - return InMemoryLocation.New(drive, _fileSystem.Path.GetFullPath(path), path); - } - - /// - public IStorageDrive GetOrAddDrive(string driveName) - { - DriveInfoMock drive = DriveInfoMock.New(driveName, _fileSystem); - return _drives.GetOrAdd(drive.Name, _ => drive); - } - - /// - public IStorageContainer GetOrCreateContainer( - IStorageLocation location, - Func containerGenerator, - IFileSystemExtensibility? fileSystemExtensibility = null) - { - ChangeDescription? fileSystemChange = null; - IStorageContainer container = _containers.GetOrAdd(location, - loc => - { - IStorageContainer container = - containerGenerator.Invoke(loc, _fileSystem); - (fileSystemExtensibility as FileSystemExtensibility)? - .CopyMetadataTo(container.Extensibility); - if (container.Type == FileSystemTypes.Directory) - { - CreateParents(_fileSystem, loc); - } - else - { - IStorageLocation? parentLocation = loc.GetParent(); - if (parentLocation is { IsRooted: false } && - !_containers.ContainsKey(parentLocation)) - { - throw ExceptionFactory.DirectoryNotFound(loc.FullPath); - } - } - - CheckAndAdjustParentDirectoryTimes(loc); - - using (container.RequestAccess(FileAccess.Write, FileShare.ReadWrite)) - { - fileSystemChange = _fileSystem.ChangeHandler.NotifyPendingChange( - WatcherChangeTypes.Created, - container.Type, - NotifyFilters.DirectoryName, location); - } - - return container; - }); - _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); - return container; - } - - /// - public IStorageLocation? Move(IStorageLocation source, - IStorageLocation destination, - bool overwrite = false, - bool recursive = false) - { - ThrowIfParentDoesNotExist(destination, _ => ExceptionFactory.DirectoryNotFound()); - - List rollbacks = new(); - try - { - return MoveInternal(source, destination, overwrite, recursive, null, - rollbacks); - } - catch (Exception) - { - foreach (Rollback rollback in rollbacks) - { - rollback.Execute(); - } - - throw; - } - } - - /// - public IStorageLocation? Replace(IStorageLocation source, - IStorageLocation destination, - IStorageLocation? backup, - bool ignoreMetadataErrors = false) - { - ThrowIfParentDoesNotExist(destination, location => Execute.OnWindows( - () => ExceptionFactory.DirectoryNotFound(location.FullPath), - () => ExceptionFactory.FileNotFound(location.FullPath))); - - if (!_containers.TryGetValue(source, - out IStorageContainer? sourceContainer)) - { - return null; - } - - if (!_containers.TryGetValue(destination, - out IStorageContainer? destinationContainer)) - { - return null; - } - - if (sourceContainer.Type != FileSystemTypes.File || - destinationContainer.Type != FileSystemTypes.File) - { - throw ExceptionFactory.AccessToPathDenied(source.FullPath); - } - - using (_ = sourceContainer.RequestAccess(FileAccess.ReadWrite, FileShare.None, - ignoreMetadataErrors: ignoreMetadataErrors)) - { - using (_ = destinationContainer.RequestAccess(FileAccess.ReadWrite, - FileShare.None, ignoreMetadataErrors: ignoreMetadataErrors)) - { - if (_containers.TryRemove(destination, - out IStorageContainer? existingDestinationContainer)) - { - int destinationBytesLength = - existingDestinationContainer.GetBytes().Length; - destination.Drive?.ChangeUsedBytes(-1 * destinationBytesLength); - if (backup != null && - _containers.TryAdd(backup, existingDestinationContainer)) - { - Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, - () => existingDestinationContainer.Attributes |= - FileAttributes.Archive); - backup.Drive?.ChangeUsedBytes(destinationBytesLength); - } - - if (_containers.TryRemove(source, - out IStorageContainer? existingSourceContainer)) - { - int sourceBytesLength = existingSourceContainer.GetBytes().Length; - source.Drive?.ChangeUsedBytes(-1 * sourceBytesLength); - destination.Drive?.ChangeUsedBytes(sourceBytesLength); - Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, - () => existingSourceContainer.Attributes |= - FileAttributes.Archive); - existingSourceContainer.CreationTime.Set( - existingDestinationContainer.CreationTime.Get( - DateTimeKind.Utc), - DateTimeKind.Utc); - _containers.TryAdd(destination, existingSourceContainer); - return destination; - } - } - - return null; - } - } - } - -#if FEATURE_FILESYSTEM_LINK - /// - public IStorageLocation? ResolveLinkTarget(IStorageLocation location, - bool returnFinalTarget = false) - { - if (_containers.TryGetValue(location, - out IStorageContainer? initialContainer) && - initialContainer.LinkTarget != null) - { - IStorageLocation nextLocation = - _fileSystem.Storage.GetLocation(initialContainer.LinkTarget); - if (_containers.TryGetValue(nextLocation, - out IStorageContainer? container)) - { - if (returnFinalTarget) - { - nextLocation = ResolveFinalLinkTarget(container, location) ?? - nextLocation; - } - - return nextLocation; - } - - return nextLocation; - } - - return null; - } -#endif - - /// - public bool TryAddContainer( - IStorageLocation location, - Func containerGenerator, - [NotNullWhen(true)] out IStorageContainer? container) - { - IStorageLocation? parentLocation = location.GetParent(); - if (parentLocation != null && - !parentLocation.IsRooted && - !_containers.ContainsKey(parentLocation)) - { - throw ExceptionFactory.DirectoryNotFound(location.FullPath); - } - - ChangeDescription? fileSystemChange = null; - - container = _containers.GetOrAdd( - location, - _ => - { - IStorageContainer container = - containerGenerator(location, _fileSystem); - using (container.RequestAccess(FileAccess.Write, FileShare.ReadWrite)) - { - fileSystemChange = _fileSystem.ChangeHandler.NotifyPendingChange( - WatcherChangeTypes.Created, - container.Type, - NotifyFilters.DirectoryName, location); - } - - CheckAndAdjustParentDirectoryTimes(location); - return container; - }); - - if (fileSystemChange != null) - { - _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); - return true; - } - - container = null; - return false; - } - - #endregion - - private void CheckAndAdjustParentDirectoryTimes(IStorageLocation location) - { - IStorageContainer? parentContainer = GetContainer(location.GetParent()); - if (parentContainer != null && parentContainer is not NullContainer) - { - Execute.NotOnWindowsIf(parentContainer.Attributes.HasFlag(FileAttributes.ReadOnly), - () => throw ExceptionFactory.AccessToPathDenied(location.FullPath)); - TimeAdjustments timeAdjustment = TimeAdjustments.LastWriteTime; - Execute.OnWindows(() - => timeAdjustment |= TimeAdjustments.LastAccessTime); - parentContainer.AdjustTimes(timeAdjustment); - } - } - - private void CreateParents(MockFileSystem fileSystem, IStorageLocation location) - { - List parents = new(); - string? parent = fileSystem.Path.GetDirectoryName( - location.FullPath.TrimEnd(fileSystem.Path.DirectorySeparatorChar, - fileSystem.Path.AltDirectorySeparatorChar)); - while (!string.IsNullOrEmpty(parent)) - { - parents.Add(parent); - parent = fileSystem.Path.GetDirectoryName(parent); - } - - parents.Reverse(); - - List accessHandles = new(); - try - { - foreach (string? parentPath in parents) - { - ChangeDescription? fileSystemChange = null; - IStorageLocation parentLocation = - _fileSystem.Storage.GetLocation(parentPath); - _ = _containers.AddOrUpdate( - parentLocation, - loc => - { - IStorageContainer container = - InMemoryContainer.NewDirectory(loc, _fileSystem); - - accessHandles.Add(container.RequestAccess(FileAccess.Write, - FileShare.ReadWrite)); - fileSystemChange = - fileSystem.ChangeHandler.NotifyPendingChange( - WatcherChangeTypes.Created, - container.Type, - NotifyFilters.DirectoryName, parentLocation); - return container; - }, - (_, f) => f); - fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); - } - } - finally - { - foreach (IStorageAccessHandle accessHandle in accessHandles) - { - accessHandle.Dispose(); - } - } - } - - private IStorageLocation? MoveInternal(IStorageLocation source, - IStorageLocation destination, - bool overwrite, - bool recursive, - FileSystemTypes? sourceType, - List? rollbacks = null) - { - if (!_containers.TryGetValue(source, - out IStorageContainer? container)) - { - return null; - } - - if (container.Type == FileSystemTypes.Directory && - source.FullPath.Equals(destination.FullPath, Execute.IsNetFramework - ? StringComparison.OrdinalIgnoreCase - : StringComparison.Ordinal)) - { - throw ExceptionFactory.MoveSourceMustBeDifferentThanDestination(); - } - - sourceType ??= container.Type; - - List children = - EnumerateLocations(source, FileSystemTypes.DirectoryOrFile).ToList(); - if (children.Any() && !recursive) - { - throw ExceptionFactory.DirectoryNotEmpty(source.FullPath); - } - - using (container.RequestAccess(FileAccess.Write, FileShare.None, - hResult: sourceType == FileSystemTypes.Directory ? -2147024891 : -2147024864)) - { - if (children.Any() && recursive) - { - foreach (IStorageLocation child in children) - { - IStorageLocation childDestination = _fileSystem - .GetMoveLocation(child, source, destination); - MoveInternal(child, childDestination, overwrite, recursive, - sourceType, - rollbacks: rollbacks); - } - } - - ChangeDescription fileSystemChange = - _fileSystem.ChangeHandler.NotifyPendingChange(WatcherChangeTypes.Renamed, - container.Type, - NotifyFilters.FileName, - destination, - source); - if (_containers.TryRemove(source, out IStorageContainer? sourceContainer)) - { - if (overwrite && - _containers.TryRemove(destination, - out IStorageContainer? existingContainer)) - { - existingContainer.ClearBytes(); - } - - if (_containers.TryAdd(destination, sourceContainer)) - { - int bytesLength = sourceContainer.GetBytes().Length; - source.Drive?.ChangeUsedBytes(-1 * bytesLength); - destination.Drive?.ChangeUsedBytes(bytesLength); - Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, - () => sourceContainer.Attributes |= FileAttributes.Archive); - rollbacks?.Add(new Rollback( - () => MoveInternal(destination, source, true, false, - sourceType))); - _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); - return destination; - } - - _containers.TryAdd(source, sourceContainer); - throw ExceptionFactory.CannotCreateFileWhenAlreadyExists( - sourceType == FileSystemTypes.Directory - ? -2147024891 - : Execute.IsWindows - ? -2147024713 - : 17); - } - } - - return source; - } - -#if FEATURE_FILESYSTEM_LINK - private IStorageLocation? ResolveFinalLinkTarget(IStorageContainer container, - IStorageLocation originalLocation) - { - int maxResolveLinks = Execute.IsWindows ? 63 : 40; - IStorageLocation? nextLocation = null; - for (int i = 1; i < maxResolveLinks; i++) - { - if (container.LinkTarget == null) - { - break; - } - - nextLocation = _fileSystem.Storage.GetLocation(container.LinkTarget); - if (!_containers.TryGetValue(nextLocation, - out IStorageContainer? nextContainer)) - { - return nextLocation; - } - - container = nextContainer; - } - - if (container.LinkTarget != null) - { - throw ExceptionFactory.FileNameCannotBeResolved( - originalLocation.FullPath); - } - - return nextLocation; - } -#endif - - private void ThrowIfParentDoesNotExist(IStorageLocation location, - Func - exceptionCallback) - { - IStorageLocation? parentLocation = location.GetParent(); - if (parentLocation != null && - _fileSystem.Path.GetPathRoot(parentLocation.FullPath) != - parentLocation.FullPath && - !_containers.TryGetValue(parentLocation, out _)) - { - throw exceptionCallback(parentLocation); - } - } - - private static void ValidateExpression(string expression) - { - if (expression.Contains('\0')) - { - throw ExceptionFactory.PathHasIllegalCharacters(expression); - } - } - - private sealed class Rollback - { - private readonly Action _onRollback; - - public Rollback(Action onRollback) - { - _onRollback = onRollback; - } - - public void Execute() - { - _onRollback.Invoke(); - } - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystem; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.Storage; + +/// +/// The container storing the current data of the in memory. +/// +internal sealed class InMemoryStorage : IStorage +{ + private readonly ConcurrentDictionary + _containers = new(); + + private readonly ConcurrentDictionary _drives = + new(StringComparer.OrdinalIgnoreCase); + + private readonly MockFileSystem _fileSystem; + + public InMemoryStorage(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + MainDrive = DriveInfoMock.New(CurrentDirectory, _fileSystem); + _drives.TryAdd(MainDrive.Name, MainDrive); + } + + #region IStorage Members + + /// + public string CurrentDirectory { get; set; } = string.Empty.PrefixRoot(); + + /// + public IStorageDrive MainDrive { get; } + + /// + public IStorageLocation? Copy(IStorageLocation source, + IStorageLocation destination, + bool overwrite = false) + { + ThrowIfParentDoesNotExist(destination, _ => ExceptionFactory.DirectoryNotFound()); + + if (!_containers.TryGetValue(source, + out IStorageContainer? sourceContainer)) + { + return null; + } + + if (sourceContainer.Type != FileSystemTypes.File) + { + throw ExceptionFactory.AccessToPathDenied(source.FullPath); + } + + using (_ = sourceContainer.RequestAccess(FileAccess.ReadWrite, FileShare.None)) + { + if (overwrite && + _containers.TryRemove(destination, + out IStorageContainer? existingContainer)) + { + existingContainer.ClearBytes(); + } + + IStorageContainer copiedContainer = + InMemoryContainer.NewFile(destination, _fileSystem); + if (_containers.TryAdd(destination, copiedContainer)) + { + copiedContainer.WriteBytes(sourceContainer.GetBytes().ToArray()); + Execute.NotOnWindows(() + => sourceContainer.AdjustTimes(TimeAdjustments.LastAccessTime)); + + copiedContainer.Attributes = sourceContainer.Attributes; + Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, + () => copiedContainer.Attributes |= FileAttributes.Archive); + Execute.NotOnWindows( + () => copiedContainer.CreationTime.Set( + sourceContainer.CreationTime.Get(DateTimeKind.Local), + DateTimeKind.Local)); + copiedContainer.LastWriteTime.Set( + sourceContainer.LastWriteTime.Get(DateTimeKind.Local), + DateTimeKind.Local); + return destination; + } + + throw ExceptionFactory.CannotCreateFileWhenAlreadyExists(Execute.IsWindows + ? -2147024816 + : 17); + } + } + + /// + public bool DeleteContainer(IStorageLocation location, bool recursive = false) + { + if (!_containers.TryGetValue(location, out IStorageContainer? container)) + { + IStorageLocation? parentLocation = location.GetParent(); + if (parentLocation != null && + !_containers.TryGetValue(parentLocation, out _)) + { + throw ExceptionFactory.DirectoryNotFound(parentLocation.FullPath); + } + + return false; + } + + if (container.Type == FileSystemTypes.Directory) + { + IEnumerable children = + EnumerateLocations(location, FileSystemTypes.DirectoryOrFile); + if (recursive) + { + foreach (IStorageLocation key in children) + { + DeleteContainer(key); + } + } + else if (children.Any()) + { + throw ExceptionFactory.DirectoryNotEmpty(location.FullPath); + } + } + + NotifyFilters notifyFilters = + container.Type == FileSystemTypes.Directory + ? NotifyFilters.DirectoryName + : NotifyFilters.FileName; + ChangeDescription fileSystemChange = + _fileSystem.ChangeHandler.NotifyPendingChange(WatcherChangeTypes.Deleted, + container.Type, + notifyFilters, location); + + using (container.RequestAccess(FileAccess.Write, FileShare.ReadWrite, + deleteAccess: true)) + { + if (_containers.TryRemove(location, out IStorageContainer? removed)) + { + removed.ClearBytes(); + _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); + CheckAndAdjustParentDirectoryTimes(location); + return true; + } + } + + return false; + } + + /// + public IEnumerable EnumerateLocations( + IStorageLocation location, + FileSystemTypes type, + string searchPattern = EnumerationOptionsHelper.DefaultSearchPattern, + EnumerationOptions? enumerationOptions = null) + { + ValidateExpression(searchPattern); + if (!_containers.ContainsKey(location)) + { + throw ExceptionFactory.DirectoryNotFound(location.FullPath); + } + + enumerationOptions ??= EnumerationOptionsHelper.Compatible; + + string fullPath = location.FullPath; +#if NETSTANDARD2_0 + if (!fullPath.EndsWith($"{_fileSystem.Path.DirectorySeparatorChar}")) +#else + if (!fullPath.EndsWith(_fileSystem.Path.DirectorySeparatorChar)) +#endif + { + fullPath += _fileSystem.Path.DirectorySeparatorChar; + } + + foreach (KeyValuePair item in _containers + .Where(x => x.Key.FullPath.StartsWith(fullPath, + InMemoryLocation.StringComparisonMode) && + !x.Key.Equals(location))) + { + string? parentPath = + _fileSystem.Path.GetDirectoryName( + item.Key.FullPath.TrimEnd(_fileSystem.Path + .DirectorySeparatorChar)); + if (!enumerationOptions.RecurseSubdirectories && + parentPath?.Equals(location.FullPath, + InMemoryLocation.StringComparisonMode) != true) + { + continue; + } + + if (!EnumerationOptionsHelper.MatchesPattern(enumerationOptions, + _fileSystem.Path.GetFileName(item.Key.FullPath), searchPattern)) + { + continue; + } + + if (type.HasFlag(item.Value.Type)) + { + yield return item.Key; + } + } + } + + /// + [return: NotNullIfNotNull("location")] + public IStorageContainer? GetContainer(IStorageLocation? location) + { + if (location == null) + { + return null; + } + + if (_containers.TryGetValue(location, out IStorageContainer? container)) + { + return container; + } + + return NullContainer.New(_fileSystem); + } + + /// + public IStorageDrive? GetDrive(string? driveName) + { + if (string.IsNullOrWhiteSpace(driveName)) + { + return null; + } + + if (!driveName.IsUncPath()) + { + driveName = _fileSystem.Path.GetPathRoot(driveName); + + if (string.IsNullOrEmpty(driveName)) + { + return null; + } + } + + DriveInfoMock drive = DriveInfoMock.New(driveName, _fileSystem); + if (_drives.TryGetValue(drive.Name, out IStorageDrive? d)) + { + return d; + } + + return null; + } + + /// + public IEnumerable GetDrives() + => _drives.Values; + + /// + [return: NotNullIfNotNull("path")] + public IStorageLocation? GetLocation(string? path, string? friendlyName = null) + { + if (path == null) + { + return null; + } + + IStorageDrive? drive = _fileSystem.Storage.GetDrive(path); + if (drive == null && + !_fileSystem.Path.IsPathRooted(path)) + { + drive = _fileSystem.Storage.MainDrive; + } + + return InMemoryLocation.New(drive, _fileSystem.Path.GetFullPath(path), path); + } + + /// + public IStorageDrive GetOrAddDrive(string driveName) + { + DriveInfoMock drive = DriveInfoMock.New(driveName, _fileSystem); + return _drives.GetOrAdd(drive.Name, _ => drive); + } + + /// + public IStorageContainer GetOrCreateContainer( + IStorageLocation location, + Func containerGenerator, + IFileSystemExtensibility? fileSystemExtensibility = null) + { + ChangeDescription? fileSystemChange = null; + IStorageContainer container = _containers.GetOrAdd(location, + loc => + { + IStorageContainer container = + containerGenerator.Invoke(loc, _fileSystem); + (fileSystemExtensibility as FileSystemExtensibility)? + .CopyMetadataTo(container.Extensibility); + if (container.Type == FileSystemTypes.Directory) + { + CreateParents(_fileSystem, loc); + } + else + { + IStorageLocation? parentLocation = loc.GetParent(); + if (parentLocation is { IsRooted: false } && + !_containers.ContainsKey(parentLocation)) + { + throw ExceptionFactory.DirectoryNotFound(loc.FullPath); + } + } + + CheckAndAdjustParentDirectoryTimes(loc); + + using (container.RequestAccess(FileAccess.Write, FileShare.ReadWrite)) + { + fileSystemChange = _fileSystem.ChangeHandler.NotifyPendingChange( + WatcherChangeTypes.Created, + container.Type, + NotifyFilters.DirectoryName, location); + } + + return container; + }); + _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); + return container; + } + + /// + public IStorageLocation? Move(IStorageLocation source, + IStorageLocation destination, + bool overwrite = false, + bool recursive = false) + { + ThrowIfParentDoesNotExist(destination, _ => ExceptionFactory.DirectoryNotFound()); + + List rollbacks = new(); + try + { + return MoveInternal(source, destination, overwrite, recursive, null, + rollbacks); + } + catch (Exception) + { + foreach (Rollback rollback in rollbacks) + { + rollback.Execute(); + } + + throw; + } + } + + /// + public IStorageLocation? Replace(IStorageLocation source, + IStorageLocation destination, + IStorageLocation? backup, + bool ignoreMetadataErrors = false) + { + ThrowIfParentDoesNotExist(destination, location => Execute.OnWindows( + () => ExceptionFactory.DirectoryNotFound(location.FullPath), + () => ExceptionFactory.FileNotFound(location.FullPath))); + + if (!_containers.TryGetValue(source, + out IStorageContainer? sourceContainer)) + { + return null; + } + + if (!_containers.TryGetValue(destination, + out IStorageContainer? destinationContainer)) + { + return null; + } + + if (sourceContainer.Type != FileSystemTypes.File || + destinationContainer.Type != FileSystemTypes.File) + { + throw ExceptionFactory.AccessToPathDenied(source.FullPath); + } + + using (_ = sourceContainer.RequestAccess(FileAccess.ReadWrite, FileShare.None, + ignoreMetadataErrors: ignoreMetadataErrors)) + { + using (_ = destinationContainer.RequestAccess(FileAccess.ReadWrite, + FileShare.None, ignoreMetadataErrors: ignoreMetadataErrors)) + { + if (_containers.TryRemove(destination, + out IStorageContainer? existingDestinationContainer)) + { + int destinationBytesLength = + existingDestinationContainer.GetBytes().Length; + destination.Drive?.ChangeUsedBytes(-1 * destinationBytesLength); + if (backup != null && + _containers.TryAdd(backup, existingDestinationContainer)) + { + Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, + () => existingDestinationContainer.Attributes |= + FileAttributes.Archive); + backup.Drive?.ChangeUsedBytes(destinationBytesLength); + } + + if (_containers.TryRemove(source, + out IStorageContainer? existingSourceContainer)) + { + int sourceBytesLength = existingSourceContainer.GetBytes().Length; + source.Drive?.ChangeUsedBytes(-1 * sourceBytesLength); + destination.Drive?.ChangeUsedBytes(sourceBytesLength); + Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, + () => existingSourceContainer.Attributes |= + FileAttributes.Archive); + existingSourceContainer.CreationTime.Set( + existingDestinationContainer.CreationTime.Get( + DateTimeKind.Utc), + DateTimeKind.Utc); + _containers.TryAdd(destination, existingSourceContainer); + return destination; + } + } + + return null; + } + } + } + +#if FEATURE_FILESYSTEM_LINK + /// + public IStorageLocation? ResolveLinkTarget(IStorageLocation location, + bool returnFinalTarget = false) + { + if (_containers.TryGetValue(location, + out IStorageContainer? initialContainer) && + initialContainer.LinkTarget != null) + { + IStorageLocation nextLocation = + _fileSystem.Storage.GetLocation(initialContainer.LinkTarget); + if (_containers.TryGetValue(nextLocation, + out IStorageContainer? container)) + { + if (returnFinalTarget) + { + nextLocation = ResolveFinalLinkTarget(container, location) ?? + nextLocation; + } + + return nextLocation; + } + + return nextLocation; + } + + return null; + } +#endif + + /// + public bool TryAddContainer( + IStorageLocation location, + Func containerGenerator, + [NotNullWhen(true)] out IStorageContainer? container) + { + IStorageLocation? parentLocation = location.GetParent(); + if (parentLocation != null && + !parentLocation.IsRooted && + !_containers.ContainsKey(parentLocation)) + { + throw ExceptionFactory.DirectoryNotFound(location.FullPath); + } + + ChangeDescription? fileSystemChange = null; + + container = _containers.GetOrAdd( + location, + _ => + { + IStorageContainer container = + containerGenerator(location, _fileSystem); + using (container.RequestAccess(FileAccess.Write, FileShare.ReadWrite)) + { + fileSystemChange = _fileSystem.ChangeHandler.NotifyPendingChange( + WatcherChangeTypes.Created, + container.Type, + NotifyFilters.DirectoryName, location); + } + + CheckAndAdjustParentDirectoryTimes(location); + return container; + }); + + if (fileSystemChange != null) + { + _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); + return true; + } + + container = null; + return false; + } + + #endregion + + private void CheckAndAdjustParentDirectoryTimes(IStorageLocation location) + { + IStorageContainer? parentContainer = GetContainer(location.GetParent()); + if (parentContainer != null && parentContainer is not NullContainer) + { + Execute.NotOnWindowsIf(parentContainer.Attributes.HasFlag(FileAttributes.ReadOnly), + () => throw ExceptionFactory.AccessToPathDenied(location.FullPath)); + TimeAdjustments timeAdjustment = TimeAdjustments.LastWriteTime; + Execute.OnWindows(() + => timeAdjustment |= TimeAdjustments.LastAccessTime); + parentContainer.AdjustTimes(timeAdjustment); + } + } + + private void CreateParents(MockFileSystem fileSystem, IStorageLocation location) + { + List parents = new(); + string? parent = fileSystem.Path.GetDirectoryName( + location.FullPath.TrimEnd(fileSystem.Path.DirectorySeparatorChar, + fileSystem.Path.AltDirectorySeparatorChar)); + while (!string.IsNullOrEmpty(parent)) + { + parents.Add(parent); + parent = fileSystem.Path.GetDirectoryName(parent); + } + + parents.Reverse(); + + List accessHandles = new(); + try + { + foreach (string? parentPath in parents) + { + ChangeDescription? fileSystemChange = null; + IStorageLocation parentLocation = + _fileSystem.Storage.GetLocation(parentPath); + _ = _containers.AddOrUpdate( + parentLocation, + loc => + { + IStorageContainer container = + InMemoryContainer.NewDirectory(loc, _fileSystem); + + accessHandles.Add(container.RequestAccess(FileAccess.Write, + FileShare.ReadWrite)); + fileSystemChange = + fileSystem.ChangeHandler.NotifyPendingChange( + WatcherChangeTypes.Created, + container.Type, + NotifyFilters.DirectoryName, parentLocation); + return container; + }, + (_, f) => f); + fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); + } + } + finally + { + foreach (IStorageAccessHandle accessHandle in accessHandles) + { + accessHandle.Dispose(); + } + } + } + + private IStorageLocation? MoveInternal(IStorageLocation source, + IStorageLocation destination, + bool overwrite, + bool recursive, + FileSystemTypes? sourceType, + List? rollbacks = null) + { + if (!_containers.TryGetValue(source, + out IStorageContainer? container)) + { + return null; + } + + if (container.Type == FileSystemTypes.Directory && + source.FullPath.Equals(destination.FullPath, Execute.IsNetFramework + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal)) + { + throw ExceptionFactory.MoveSourceMustBeDifferentThanDestination(); + } + + sourceType ??= container.Type; + + List children = + EnumerateLocations(source, FileSystemTypes.DirectoryOrFile).ToList(); + if (children.Any() && !recursive) + { + throw ExceptionFactory.DirectoryNotEmpty(source.FullPath); + } + + using (container.RequestAccess(FileAccess.Write, FileShare.None, + hResult: sourceType == FileSystemTypes.Directory ? -2147024891 : -2147024864)) + { + if (children.Any() && recursive) + { + foreach (IStorageLocation child in children) + { + IStorageLocation childDestination = _fileSystem + .GetMoveLocation(child, source, destination); + MoveInternal(child, childDestination, overwrite, recursive, + sourceType, + rollbacks: rollbacks); + } + } + + ChangeDescription fileSystemChange = + _fileSystem.ChangeHandler.NotifyPendingChange(WatcherChangeTypes.Renamed, + container.Type, + NotifyFilters.FileName, + destination, + source); + if (_containers.TryRemove(source, out IStorageContainer? sourceContainer)) + { + if (overwrite && + _containers.TryRemove(destination, + out IStorageContainer? existingContainer)) + { + existingContainer.ClearBytes(); + } + + if (_containers.TryAdd(destination, sourceContainer)) + { + int bytesLength = sourceContainer.GetBytes().Length; + source.Drive?.ChangeUsedBytes(-1 * bytesLength); + destination.Drive?.ChangeUsedBytes(bytesLength); + Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, + () => sourceContainer.Attributes |= FileAttributes.Archive); + rollbacks?.Add(new Rollback( + () => MoveInternal(destination, source, true, false, + sourceType))); + _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); + return destination; + } + + _containers.TryAdd(source, sourceContainer); + throw ExceptionFactory.CannotCreateFileWhenAlreadyExists( + sourceType == FileSystemTypes.Directory + ? -2147024891 + : Execute.IsWindows + ? -2147024713 + : 17); + } + } + + return source; + } + +#if FEATURE_FILESYSTEM_LINK + private IStorageLocation? ResolveFinalLinkTarget(IStorageContainer container, + IStorageLocation originalLocation) + { + int maxResolveLinks = Execute.IsWindows ? 63 : 40; + IStorageLocation? nextLocation = null; + for (int i = 1; i < maxResolveLinks; i++) + { + if (container.LinkTarget == null) + { + break; + } + + nextLocation = _fileSystem.Storage.GetLocation(container.LinkTarget); + if (!_containers.TryGetValue(nextLocation, + out IStorageContainer? nextContainer)) + { + return nextLocation; + } + + container = nextContainer; + } + + if (container.LinkTarget != null) + { + throw ExceptionFactory.FileNameCannotBeResolved( + originalLocation.FullPath); + } + + return nextLocation; + } +#endif + + private void ThrowIfParentDoesNotExist(IStorageLocation location, + Func + exceptionCallback) + { + IStorageLocation? parentLocation = location.GetParent(); + if (parentLocation != null && + _fileSystem.Path.GetPathRoot(parentLocation.FullPath) != + parentLocation.FullPath && + !_containers.TryGetValue(parentLocation, out _)) + { + throw exceptionCallback(parentLocation); + } + } + + private static void ValidateExpression(string expression) + { + if (expression.Contains('\0')) + { + throw ExceptionFactory.PathHasIllegalCharacters(expression); + } + } + + private sealed class Rollback + { + private readonly Action _onRollback; + + public Rollback(Action onRollback) + { + _onRollback = onRollback; + } + + public void Execute() + { + _onRollback.Invoke(); + } + } +} diff --git a/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs b/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs index 8cb93836e..3d18c5f8e 100644 --- a/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs +++ b/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs @@ -1,197 +1,196 @@ -using System; -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.TimeSystem; - -namespace Testably.Abstractions.Testing.Storage; - -internal sealed class NullContainer : IStorageContainer -{ - private NullContainer(IFileSystem fileSystem, ITimeSystem timeSystem) - { - FileSystem = fileSystem; - TimeSystem = timeSystem; - Extensibility = new FileSystemExtensibility(); - } - - #region IStorageContainer Members - - /// - public FileAttributes Attributes - { - get => (FileAttributes)(-1); - set => throw ExceptionFactory.FileNotFound(string.Empty); - } - - /// - public IStorageContainer.ITimeContainer CreationTime { get; } = - new CreationNullTime(); - - /// - public IFileSystemExtensibility Extensibility { get; } - - /// - public IFileSystem FileSystem { get; } - - /// - public IStorageContainer.ITimeContainer LastAccessTime { get; } = new NullTime(); - - /// - public IStorageContainer.ITimeContainer LastWriteTime { get; } = new NullTime(); - - /// - public string? LinkTarget - { - get => null; - set => _ = value; - } - - /// - public ITimeSystem TimeSystem { get; } - - /// - public FileSystemTypes Type - => FileSystemTypes.DirectoryOrFile; - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - public UnixFileMode UnixFileMode { get; set; } = (UnixFileMode)(-1); -#endif - - /// - public void AppendBytes(byte[] bytes) - { - // Do nothing in NullContainer - } - - /// - public void ClearBytes() - { - // Do nothing in NullContainer - } - - /// - public void Decrypt() - { - // Do nothing in NullContainer - } - - /// - public void Encrypt() - { - // Do nothing in NullContainer - } - - /// - public byte[] GetBytes() - => Array.Empty(); - - /// - public IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, - bool deleteAccess = false, - bool ignoreMetadataErrors = true, - int? hResult = null) - => new NullStorageAccessHandle(access, share, deleteAccess); - - /// - public void WriteBytes(byte[] bytes) - { - // Do nothing in NullContainer - } - - #endregion - - internal static IStorageContainer New(MockFileSystem fileSystem) - => new NullContainer(fileSystem, fileSystem.TimeSystem); - - private sealed class NullStorageAccessHandle : IStorageAccessHandle - { - public NullStorageAccessHandle(FileAccess access, FileShare share, - bool deleteAccess) - { - Access = access; - Share = share; - DeleteAccess = deleteAccess; - } - - #region IStorageAccessHandle Members - - /// - public FileAccess Access { get; } - - /// - public bool DeleteAccess { get; } - - /// - public FileShare Share { get; } - - /// - public void Dispose() - { - // Nothing to do! - } - - #endregion - } - - /// - /// The default time returned by the file system if no time has been set. - /// : - /// A file time is a 64-bit value that represents the number of 100-nanosecond intervals that have elapsed - /// since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC). - /// - private class NullTime : IStorageContainer.ITimeContainer - { - private readonly DateTime _time = - new(1601, 01, 01, 00, 00, 00, DateTimeKind.Utc); - - #region ITimeContainer Members - - /// - public DateTime Get(DateTimeKind kind) - => kind switch - { - DateTimeKind.Utc => _time.ToUniversalTime(), - DateTimeKind.Local => _time.ToLocalTime(), - _ => _time - }; - - /// - public virtual void Set(DateTime time, DateTimeKind kind) - { -#if NET7_0_OR_GREATER - throw ExceptionFactory.FileNotFound(string.Empty); -#else - Execute.OnWindows(() - => throw ExceptionFactory.FileNotFound(string.Empty)); - - throw ExceptionFactory.DirectoryNotFound(string.Empty); -#endif - } - - #endregion - } - - /// - /// Overrides the setter of as different exceptions are thrown for MacOS starting with .NET 7. - /// - private sealed class CreationNullTime : NullTime - { - /// - public override void Set(DateTime time, DateTimeKind kind) - { -#if NET7_0_OR_GREATER - Execute.OnMac(() - => throw ExceptionFactory.DirectoryNotFound(string.Empty)); - - throw ExceptionFactory.FileNotFound(string.Empty); -#else - Execute.OnWindows(() - => throw ExceptionFactory.FileNotFound(string.Empty)); - - throw ExceptionFactory.DirectoryNotFound(string.Empty); -#endif - } - } -} +using System; +using System.IO; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.TimeSystem; + +namespace Testably.Abstractions.Testing.Storage; + +internal sealed class NullContainer : IStorageContainer +{ + private NullContainer(IFileSystem fileSystem, ITimeSystem timeSystem) + { + FileSystem = fileSystem; + TimeSystem = timeSystem; + Extensibility = new FileSystemExtensibility(); + } + + #region IStorageContainer Members + + /// + public FileAttributes Attributes + { + get => (FileAttributes)(-1); + set => throw ExceptionFactory.FileNotFound(string.Empty); + } + + /// + public IStorageContainer.ITimeContainer CreationTime { get; } = + new CreationNullTime(); + + /// + public IFileSystemExtensibility Extensibility { get; } + + /// + public IFileSystem FileSystem { get; } + + /// + public IStorageContainer.ITimeContainer LastAccessTime { get; } = new NullTime(); + + /// + public IStorageContainer.ITimeContainer LastWriteTime { get; } = new NullTime(); + + /// + public string? LinkTarget + { + get => null; + set => _ = value; + } + + /// + public ITimeSystem TimeSystem { get; } + + /// + public FileSystemTypes Type + => FileSystemTypes.DirectoryOrFile; + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + public UnixFileMode UnixFileMode { get; set; } = (UnixFileMode)(-1); +#endif + + /// + public void AppendBytes(byte[] bytes) + { + // Do nothing in NullContainer + } + + /// + public void ClearBytes() + { + // Do nothing in NullContainer + } + + /// + public void Decrypt() + { + // Do nothing in NullContainer + } + + /// + public void Encrypt() + { + // Do nothing in NullContainer + } + + /// + public byte[] GetBytes() + => Array.Empty(); + + /// + public IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, + bool deleteAccess = false, + bool ignoreMetadataErrors = true, + int? hResult = null) + => new NullStorageAccessHandle(access, share, deleteAccess); + + /// + public void WriteBytes(byte[] bytes) + { + // Do nothing in NullContainer + } + + #endregion + + internal static IStorageContainer New(MockFileSystem fileSystem) + => new NullContainer(fileSystem, fileSystem.TimeSystem); + + private sealed class NullStorageAccessHandle : IStorageAccessHandle + { + public NullStorageAccessHandle(FileAccess access, FileShare share, + bool deleteAccess) + { + Access = access; + Share = share; + DeleteAccess = deleteAccess; + } + + #region IStorageAccessHandle Members + + /// + public FileAccess Access { get; } + + /// + public bool DeleteAccess { get; } + + /// + public FileShare Share { get; } + + /// + public void Dispose() + { + // Nothing to do! + } + + #endregion + } + + /// + /// The default time returned by the file system if no time has been set. + /// : + /// A file time is a 64-bit value that represents the number of 100-nanosecond intervals that have elapsed + /// since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC). + /// + private class NullTime : IStorageContainer.ITimeContainer + { + private readonly DateTime _time = + new(1601, 01, 01, 00, 00, 00, DateTimeKind.Utc); + + #region ITimeContainer Members + + /// + public DateTime Get(DateTimeKind kind) + => kind switch + { + DateTimeKind.Utc => _time.ToUniversalTime(), + DateTimeKind.Local => _time.ToLocalTime(), + _ => _time + }; + + /// + public virtual void Set(DateTime time, DateTimeKind kind) + { +#if NET7_0_OR_GREATER + throw ExceptionFactory.FileNotFound(string.Empty); +#else + Execute.OnWindows(() + => throw ExceptionFactory.FileNotFound(string.Empty)); + + throw ExceptionFactory.DirectoryNotFound(string.Empty); +#endif + } + + #endregion + } + + /// + /// Overrides the setter of as different exceptions are thrown for MacOS starting with .NET 7. + /// + private sealed class CreationNullTime : NullTime + { + /// + public override void Set(DateTime time, DateTimeKind kind) + { +#if NET7_0_OR_GREATER + Execute.OnMac(() + => throw ExceptionFactory.DirectoryNotFound(string.Empty)); + + throw ExceptionFactory.FileNotFound(string.Empty); +#else + Execute.OnWindows(() + => throw ExceptionFactory.FileNotFound(string.Empty)); + + throw ExceptionFactory.DirectoryNotFound(string.Empty); +#endif + } + } +} diff --git a/Source/Testably.Abstractions.Testing/Usings.cs b/Source/Testably.Abstractions.Testing/Usings.cs index acb44645c..f15db6c3f 100644 --- a/Source/Testably.Abstractions.Testing/Usings.cs +++ b/Source/Testably.Abstractions.Testing/Usings.cs @@ -3,3 +3,4 @@ #else global using System.Runtime.Versioning; #endif +global using Testably.Abstractions.FileSystem; diff --git a/Tests/Testably.Abstractions.AccessControl.Tests/FileInfoAclExtensionsTests.cs b/Tests/Testably.Abstractions.AccessControl.Tests/FileInfoAclExtensionsTests.cs index b959a7dce..bb7eda994 100644 --- a/Tests/Testably.Abstractions.AccessControl.Tests/FileInfoAclExtensionsTests.cs +++ b/Tests/Testably.Abstractions.AccessControl.Tests/FileInfoAclExtensionsTests.cs @@ -1,63 +1,62 @@ -using System.Security.AccessControl; -using Testably.Abstractions.AccessControl.Tests.TestHelpers; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.AccessControl.Tests; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class FileInfoAclExtensionsTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableFact] - public void GetAccessControl_ShouldBeInitializedWithNotNullValue() - { - Skip.IfNot(Test.RunsOnWindows); - - FileSystem.File.WriteAllText("foo", null); - IFileInfo fileInfo = FileSystem.FileInfo.New("foo"); - - #pragma warning disable CA1416 - FileSecurity result = fileInfo.GetAccessControl(); - #pragma warning restore CA1416 - - result.Should().NotBeNull(); - } - - [SkippableFact] - public void GetAccessControl_WithAccessControlSections_ShouldBeInitializedWithNotNullValue() - { - Skip.IfNot(Test.RunsOnWindows); - - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText("foo", null); - IFileInfo fileInfo = FileSystem.FileInfo.New("foo"); - - #pragma warning disable CA1416 - FileSecurity result = fileInfo.GetAccessControl(AccessControlSections.All); - #pragma warning restore CA1416 - - result.Should().NotBeNull(); - } - - [SkippableFact] - public void SetAccessControl_ShouldChangeAccessControl() - { - Skip.IfNot(Test.RunsOnWindows); - - FileSystem.File.WriteAllText("foo", null); - #pragma warning disable CA1416 - FileSecurity originalAccessControl = - FileSystem.FileInfo.New("foo").GetAccessControl(); - FileSystem.FileInfo.New("foo").SetAccessControl(originalAccessControl); - - FileSecurity currentAccessControl = - FileSystem.FileInfo.New("foo") - .GetAccessControl(AccessControlSections.Access); - #pragma warning restore CA1416 - - currentAccessControl.HasSameAccessRightsAs(originalAccessControl) - .Should().BeTrue(); - } -} +using System.Security.AccessControl; +using Testably.Abstractions.AccessControl.Tests.TestHelpers; + +namespace Testably.Abstractions.AccessControl.Tests; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class FileInfoAclExtensionsTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableFact] + public void GetAccessControl_ShouldBeInitializedWithNotNullValue() + { + Skip.IfNot(Test.RunsOnWindows); + + FileSystem.File.WriteAllText("foo", null); + IFileInfo fileInfo = FileSystem.FileInfo.New("foo"); + + #pragma warning disable CA1416 + FileSecurity result = fileInfo.GetAccessControl(); + #pragma warning restore CA1416 + + result.Should().NotBeNull(); + } + + [SkippableFact] + public void GetAccessControl_WithAccessControlSections_ShouldBeInitializedWithNotNullValue() + { + Skip.IfNot(Test.RunsOnWindows); + + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText("foo", null); + IFileInfo fileInfo = FileSystem.FileInfo.New("foo"); + + #pragma warning disable CA1416 + FileSecurity result = fileInfo.GetAccessControl(AccessControlSections.All); + #pragma warning restore CA1416 + + result.Should().NotBeNull(); + } + + [SkippableFact] + public void SetAccessControl_ShouldChangeAccessControl() + { + Skip.IfNot(Test.RunsOnWindows); + + FileSystem.File.WriteAllText("foo", null); + #pragma warning disable CA1416 + FileSecurity originalAccessControl = + FileSystem.FileInfo.New("foo").GetAccessControl(); + FileSystem.FileInfo.New("foo").SetAccessControl(originalAccessControl); + + FileSecurity currentAccessControl = + FileSystem.FileInfo.New("foo") + .GetAccessControl(AccessControlSections.Access); + #pragma warning restore CA1416 + + currentAccessControl.HasSameAccessRightsAs(originalAccessControl) + .Should().BeTrue(); + } +} diff --git a/Tests/Testably.Abstractions.AccessControl.Tests/FileStreamAclExtensionsTests.cs b/Tests/Testably.Abstractions.AccessControl.Tests/FileStreamAclExtensionsTests.cs index d54358f53..186350eb2 100644 --- a/Tests/Testably.Abstractions.AccessControl.Tests/FileStreamAclExtensionsTests.cs +++ b/Tests/Testably.Abstractions.AccessControl.Tests/FileStreamAclExtensionsTests.cs @@ -1,42 +1,41 @@ -using System.Security.AccessControl; -using Testably.Abstractions.AccessControl.Tests.TestHelpers; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.AccessControl.Tests; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class FileStreamAclExtensionsTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableFact] - public void GetAccessControl_ShouldBeInitializedWithNotNullValue() - { - Skip.IfNot(Test.RunsOnWindows); - - FileSystemStream fileStream = FileSystem.File.Create("foo"); - - #pragma warning disable CA1416 - FileSecurity result = fileStream.GetAccessControl(); - #pragma warning restore CA1416 - - result.Should().NotBeNull(); - } - - [SkippableFact] - public void SetAccessControl_ShouldChangeAccessControl() - { - Skip.IfNot(Test.RunsOnWindows); - - FileSystemStream fileStream = FileSystem.File.Create("foo"); - #pragma warning disable CA1416 - FileSecurity originalAccessControl = fileStream.GetAccessControl(); - fileStream.SetAccessControl(originalAccessControl); - - FileSecurity currentAccessControl = fileStream.GetAccessControl(); - #pragma warning restore CA1416 - - currentAccessControl.HasSameAccessRightsAs(originalAccessControl) - .Should().BeTrue(); - } -} +using System.Security.AccessControl; +using Testably.Abstractions.AccessControl.Tests.TestHelpers; + +namespace Testably.Abstractions.AccessControl.Tests; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class FileStreamAclExtensionsTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableFact] + public void GetAccessControl_ShouldBeInitializedWithNotNullValue() + { + Skip.IfNot(Test.RunsOnWindows); + + FileSystemStream fileStream = FileSystem.File.Create("foo"); + + #pragma warning disable CA1416 + FileSecurity result = fileStream.GetAccessControl(); + #pragma warning restore CA1416 + + result.Should().NotBeNull(); + } + + [SkippableFact] + public void SetAccessControl_ShouldChangeAccessControl() + { + Skip.IfNot(Test.RunsOnWindows); + + FileSystemStream fileStream = FileSystem.File.Create("foo"); + #pragma warning disable CA1416 + FileSecurity originalAccessControl = fileStream.GetAccessControl(); + fileStream.SetAccessControl(originalAccessControl); + + FileSecurity currentAccessControl = fileStream.GetAccessControl(); + #pragma warning restore CA1416 + + currentAccessControl.HasSameAccessRightsAs(originalAccessControl) + .Should().BeTrue(); + } +} diff --git a/Tests/Testably.Abstractions.AccessControl.Tests/TestHelpers/Usings.cs b/Tests/Testably.Abstractions.AccessControl.Tests/TestHelpers/Usings.cs index dcc5be452..c3ee3af35 100644 --- a/Tests/Testably.Abstractions.AccessControl.Tests/TestHelpers/Usings.cs +++ b/Tests/Testably.Abstractions.AccessControl.Tests/TestHelpers/Usings.cs @@ -1,5 +1,6 @@ global using FluentAssertions; global using System; +global using Testably.Abstractions.FileSystem; global using Testably.Abstractions.Testing; global using Testably.Abstractions.TestHelpers; global using Xunit; diff --git a/Tests/Testably.Abstractions.Compression.Tests/TestHelpers/Usings.cs b/Tests/Testably.Abstractions.Compression.Tests/TestHelpers/Usings.cs index a675f8771..1ac664d84 100644 --- a/Tests/Testably.Abstractions.Compression.Tests/TestHelpers/Usings.cs +++ b/Tests/Testably.Abstractions.Compression.Tests/TestHelpers/Usings.cs @@ -1,6 +1,7 @@ global using AutoFixture.Xunit2; global using FluentAssertions; global using System; +global using Testably.Abstractions.FileSystem; global using Testably.Abstractions.TestHelpers; global using Testably.Abstractions.Testing; global using Xunit; diff --git a/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.Extensions.cs b/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.Extensions.cs index c6980c3e1..1877b8db9 100644 --- a/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.Extensions.cs +++ b/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.Extensions.cs @@ -1,285 +1,284 @@ -using System.IO; -using System.IO.Compression; -using System.Linq; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Compression.Tests.ZipArchive; - -public abstract partial class ZipArchiveTests - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [InlineData("2000-01-01T12:14:15")] - [InlineData("1980-01-01T00:00:00")] - [InlineData("2107-12-31T23:59:59")] - public void CreateEntryFromFile_LastWriteTime_ShouldBeCopiedFromFile( - string lastWriteTimeString) - { - DateTime lastWriteTime = DateTime.Parse(lastWriteTimeString); - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar"); - FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); - FileSystem.File.SetLastWriteTime("bar/foo.txt", lastWriteTime); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - archive.Entries.Count.Should().Be(0); - - archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt", - CompressionLevel.NoCompression); - - IZipArchiveEntry entry = archive.Entries.Single(); - entry.LastWriteTime.DateTime.Should().Be(lastWriteTime); - } - - [SkippableTheory] - [InlineData("1930-06-21T14:15:16")] - [InlineData("1979-12-31T00:00:00")] - [InlineData("2108-01-01T00:00:00")] - [InlineData("2208-01-01T00:00:00")] - public void CreateEntryFromFile_LastWriteTimeOutOfRange_ShouldBeFirstJanuary1980( - string lastWriteTimeString) - { - DateTime expectedTime = new(1980, 1, 1, 0, 0, 0); - DateTime lastWriteTime = DateTime.Parse(lastWriteTimeString).ToUniversalTime(); - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar"); - FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); - FileSystem.File.SetLastWriteTime("bar/foo.txt", lastWriteTime); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - archive.Entries.Count.Should().Be(0); - - archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt", - CompressionLevel.NoCompression); - - IZipArchiveEntry entry = archive.Entries.Single(); - entry.LastWriteTime.DateTime.Should().Be(expectedTime); - } - - [SkippableFact] - public void CreateEntryFromFile_NullEntryName_ShouldThrowArgumentNullException() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar"); - FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - archive.Entries.Count.Should().Be(0); - - Exception? exception = Record.Exception(() => - { - archive.CreateEntryFromFile("bar/foo.txt", null!); - }); - - exception.Should().BeOfType() - .Which.ParamName.Should().Be("entryName"); - } - - [SkippableFact] - public void CreateEntryFromFile_NullSourceFileName_ShouldThrowArgumentNullException() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar"); - FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - archive.Entries.Count.Should().Be(0); - - Exception? exception = Record.Exception(() => - { - archive.CreateEntryFromFile(null!, "foo/bar.txt", - CompressionLevel.NoCompression); - }); - - exception.Should().BeOfType() - .Which.ParamName.Should().Be("sourceFileName"); - } - - [SkippableFact] - public void CreateEntryFromFile_ReadOnlyArchive_ShouldThrowNotSupportedException() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar"); - FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - archive.Entries.Count.Should().Be(0); - - Exception? exception = Record.Exception(() => - { - archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt"); - }); - - exception.Should().BeOfType(); - } - - [SkippableFact] - public void CreateEntryFromFile_ShouldCreateEntryWithFileContent() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar"); - FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - archive.Entries.Count.Should().Be(0); - - archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt", - CompressionLevel.NoCompression); - - IZipArchiveEntry entry = archive.Entries.Single(); - entry.FullName.Should().Be("foo/bar.txt"); - - entry.ExtractToFile("test.txt"); - FileSystem.File.ReadAllText("test.txt").Should().Be("FooFooFoo"); - } - - [SkippableTheory] - [AutoData] - public void ExtractToDirectory_DestinationNull_ShouldThrowArgumentNullException( - CompressionLevel compressionLevel) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); - - Exception? exception = Record.Exception(() => - { - using IZipArchive archive = - FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); - - archive.ExtractToDirectory(null!); - }); - - exception.Should().BeOfType() - .Which.ParamName.Should().Be("destinationDirectoryName"); - } - -#if FEATURE_COMPRESSION_ADVANCED - [SkippableTheory] - [AutoData] - public void - ExtractToDirectory_DestinationNull_WithOverwrite_ShouldThrowArgumentNullException( - CompressionLevel compressionLevel) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); - - Exception? exception = Record.Exception(() => - { - using IZipArchive archive = - FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); - - archive.ExtractToDirectory(null!, true); - }); - - exception.Should().BeOfType(); - } -#endif - - [SkippableFact] - public void ExtractToDirectory_WithoutOverwrite_ShouldThrowIOException() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar").Initialized(s => s - .WithFile("foo.txt")); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - Exception? exception = Record.Exception(() => - { - archive.ExtractToDirectory("bar"); - }); - - exception.Should().BeOfType() - .Which.Message.Should() - .Contain($"'{FileSystem.Path.GetFullPath("bar/foo.txt")}'"); - FileSystem.File.ReadAllText("bar/foo.txt") - .Should().NotBe("FooFooFoo"); - } - - [SkippableFact] - public void ExtractToDirectory_ShouldExtractFilesAndDirectories() - { - FileSystem.Initialize() - .WithSubdirectory("foo").Initialized(s => s - .WithSubdirectory("bar") - .WithFile("bar.txt")); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - archive.ExtractToDirectory("bar"); - - FileSystem.File.ReadAllText("bar/foo.txt") - .Should().Be("FooFooFoo"); - FileSystem.Directory.Exists("bar/bar") - .Should().BeTrue(); - FileSystem.File.Exists("bar/bar.txt") - .Should().BeTrue(); - } - -#if FEATURE_COMPRESSION_ADVANCED - [SkippableFact] - public void ExtractToDirectory_WithOverwrite_ShouldOverwriteExistingFile() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar").Initialized(s => s - .WithFile("foo.txt")); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - archive.ExtractToDirectory("bar", true); - - FileSystem.File.ReadAllText("bar/foo.txt") - .Should().Be("FooFooFoo"); - } -#endif -} +using System.IO; +using System.IO.Compression; +using System.Linq; + +namespace Testably.Abstractions.Compression.Tests.ZipArchive; + +public abstract partial class ZipArchiveTests + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [InlineData("2000-01-01T12:14:15")] + [InlineData("1980-01-01T00:00:00")] + [InlineData("2107-12-31T23:59:59")] + public void CreateEntryFromFile_LastWriteTime_ShouldBeCopiedFromFile( + string lastWriteTimeString) + { + DateTime lastWriteTime = DateTime.Parse(lastWriteTimeString); + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar"); + FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); + FileSystem.File.SetLastWriteTime("bar/foo.txt", lastWriteTime); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + archive.Entries.Count.Should().Be(0); + + archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt", + CompressionLevel.NoCompression); + + IZipArchiveEntry entry = archive.Entries.Single(); + entry.LastWriteTime.DateTime.Should().Be(lastWriteTime); + } + + [SkippableTheory] + [InlineData("1930-06-21T14:15:16")] + [InlineData("1979-12-31T00:00:00")] + [InlineData("2108-01-01T00:00:00")] + [InlineData("2208-01-01T00:00:00")] + public void CreateEntryFromFile_LastWriteTimeOutOfRange_ShouldBeFirstJanuary1980( + string lastWriteTimeString) + { + DateTime expectedTime = new(1980, 1, 1, 0, 0, 0); + DateTime lastWriteTime = DateTime.Parse(lastWriteTimeString).ToUniversalTime(); + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar"); + FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); + FileSystem.File.SetLastWriteTime("bar/foo.txt", lastWriteTime); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + archive.Entries.Count.Should().Be(0); + + archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt", + CompressionLevel.NoCompression); + + IZipArchiveEntry entry = archive.Entries.Single(); + entry.LastWriteTime.DateTime.Should().Be(expectedTime); + } + + [SkippableFact] + public void CreateEntryFromFile_NullEntryName_ShouldThrowArgumentNullException() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar"); + FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + archive.Entries.Count.Should().Be(0); + + Exception? exception = Record.Exception(() => + { + archive.CreateEntryFromFile("bar/foo.txt", null!); + }); + + exception.Should().BeOfType() + .Which.ParamName.Should().Be("entryName"); + } + + [SkippableFact] + public void CreateEntryFromFile_NullSourceFileName_ShouldThrowArgumentNullException() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar"); + FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + archive.Entries.Count.Should().Be(0); + + Exception? exception = Record.Exception(() => + { + archive.CreateEntryFromFile(null!, "foo/bar.txt", + CompressionLevel.NoCompression); + }); + + exception.Should().BeOfType() + .Which.ParamName.Should().Be("sourceFileName"); + } + + [SkippableFact] + public void CreateEntryFromFile_ReadOnlyArchive_ShouldThrowNotSupportedException() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar"); + FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + archive.Entries.Count.Should().Be(0); + + Exception? exception = Record.Exception(() => + { + archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt"); + }); + + exception.Should().BeOfType(); + } + + [SkippableFact] + public void CreateEntryFromFile_ShouldCreateEntryWithFileContent() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar"); + FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + archive.Entries.Count.Should().Be(0); + + archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt", + CompressionLevel.NoCompression); + + IZipArchiveEntry entry = archive.Entries.Single(); + entry.FullName.Should().Be("foo/bar.txt"); + + entry.ExtractToFile("test.txt"); + FileSystem.File.ReadAllText("test.txt").Should().Be("FooFooFoo"); + } + + [SkippableTheory] + [AutoData] + public void ExtractToDirectory_DestinationNull_ShouldThrowArgumentNullException( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); + + Exception? exception = Record.Exception(() => + { + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + archive.ExtractToDirectory(null!); + }); + + exception.Should().BeOfType() + .Which.ParamName.Should().Be("destinationDirectoryName"); + } + +#if FEATURE_COMPRESSION_ADVANCED + [SkippableTheory] + [AutoData] + public void + ExtractToDirectory_DestinationNull_WithOverwrite_ShouldThrowArgumentNullException( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); + + Exception? exception = Record.Exception(() => + { + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + archive.ExtractToDirectory(null!, true); + }); + + exception.Should().BeOfType(); + } +#endif + + [SkippableFact] + public void ExtractToDirectory_WithoutOverwrite_ShouldThrowIOException() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar").Initialized(s => s + .WithFile("foo.txt")); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + Exception? exception = Record.Exception(() => + { + archive.ExtractToDirectory("bar"); + }); + + exception.Should().BeOfType() + .Which.Message.Should() + .Contain($"'{FileSystem.Path.GetFullPath("bar/foo.txt")}'"); + FileSystem.File.ReadAllText("bar/foo.txt") + .Should().NotBe("FooFooFoo"); + } + + [SkippableFact] + public void ExtractToDirectory_ShouldExtractFilesAndDirectories() + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithSubdirectory("bar") + .WithFile("bar.txt")); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + archive.ExtractToDirectory("bar"); + + FileSystem.File.ReadAllText("bar/foo.txt") + .Should().Be("FooFooFoo"); + FileSystem.Directory.Exists("bar/bar") + .Should().BeTrue(); + FileSystem.File.Exists("bar/bar.txt") + .Should().BeTrue(); + } + +#if FEATURE_COMPRESSION_ADVANCED + [SkippableFact] + public void ExtractToDirectory_WithOverwrite_ShouldOverwriteExistingFile() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar").Initialized(s => s + .WithFile("foo.txt")); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + archive.ExtractToDirectory("bar", true); + + FileSystem.File.ReadAllText("bar/foo.txt") + .Should().Be("FooFooFoo"); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.cs b/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.cs index 42d3de0dc..318e6d052 100644 --- a/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.cs +++ b/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.cs @@ -1,85 +1,84 @@ -using System.IO.Compression; -#if FEATURE_ZIPFILE_NET7 -using Testably.Abstractions.FileSystem; -#endif - -namespace Testably.Abstractions.Compression.Tests.ZipArchive; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ZipArchiveTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ -#if FEATURE_ZIPFILE_NET7 - [SkippableTheory] - [AutoData] - public void Comment_ShouldBeSettable(string comment) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - archive.Comment = comment; - - archive.Comment.Should().Be(comment); - } - - [SkippableFact] - public void Comment_ShouldBeInitializedEmpty() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - archive.Comment.Should().Be(""); - } -#endif - - [SkippableTheory] - [AutoData] - public void FileSystemExtension_ShouldBeSet( - CompressionLevel compressionLevel) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); - - using IZipArchive archive = - FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); - - archive.FileSystem.Should().Be(FileSystem); - } - - [SkippableFact] - public void GetEntry_WhenNameIsNotFound_ShouldReturnNull() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - true); - - using IZipArchive archive = - FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); - - archive.GetEntry("bar.txt").Should().BeNull(); - archive.GetEntry("foo.txt").Should().BeNull(); - archive.GetEntry("foo/foo.txt").Should().NotBeNull(); - } -} +using System.IO.Compression; +#if FEATURE_ZIPFILE_NET7 +#endif + +namespace Testably.Abstractions.Compression.Tests.ZipArchive; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ZipArchiveTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ +#if FEATURE_ZIPFILE_NET7 + [SkippableTheory] + [AutoData] + public void Comment_ShouldBeSettable(string comment) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + archive.Comment = comment; + + archive.Comment.Should().Be(comment); + } + + [SkippableFact] + public void Comment_ShouldBeInitializedEmpty() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + archive.Comment.Should().Be(""); + } +#endif + + [SkippableTheory] + [AutoData] + public void FileSystemExtension_ShouldBeSet( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); + + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + archive.FileSystem.Should().Be(FileSystem); + } + + [SkippableFact] + public void GetEntry_WhenNameIsNotFound_ShouldReturnNull() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + true); + + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + archive.GetEntry("bar.txt").Should().BeNull(); + archive.GetEntry("foo.txt").Should().BeNull(); + archive.GetEntry("foo/foo.txt").Should().NotBeNull(); + } +} diff --git a/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.Extensions.cs b/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.Extensions.cs index fa3b5af30..4753ef5f3 100644 --- a/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.Extensions.cs +++ b/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.Extensions.cs @@ -1,155 +1,154 @@ -using System.IO; -using System.IO.Compression; -using System.Linq; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Compression.Tests.ZipArchiveEntry; - -public abstract partial class ZipArchiveEntryTests - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void ExtractToFile_DestinationNull_ShouldThrowArgumentNullException( - CompressionLevel compressionLevel) - { - FileSystem.Initialize() - .WithSubdirectory("foo").Initialized(s => s - .WithAFile()); - - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); - - Exception? exception = Record.Exception(() => - { - using IZipArchive archive = - FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); - - archive.Entries.Single().ExtractToFile(null!); - }); - - exception.Should().BeOfType() - .Which.ParamName.Should().Be("destinationFileName"); - } - - [SkippableTheory] - [AutoData] - public void - ExtractToFile_DestinationNull_WithOverwrite_ShouldThrowArgumentNullException( - CompressionLevel compressionLevel) - { - FileSystem.Initialize() - .WithSubdirectory("foo").Initialized(s => s - .WithAFile()); - - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); - - Exception? exception = Record.Exception(() => - { - using IZipArchive archive = - FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); - - archive.Entries.Single().ExtractToFile(null!, true); - }); - - exception.Should().BeOfType(); - } - - [SkippableFact] - public void ExtractToFile_WithoutOverwrite_ShouldThrowIOException() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar").Initialized(s => s - .WithFile("bar.txt")); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - IZipArchiveEntry entry = archive.Entries.Single(); - - Exception? exception = Record.Exception(() => - { - entry.ExtractToFile("bar/bar.txt"); - }); - - exception.Should().BeOfType() - .Which.Message.Should() - .Contain($"'{FileSystem.Path.GetFullPath("bar/bar.txt")}'"); - FileSystem.File.ReadAllText("bar/bar.txt") - .Should().NotBe("FooFooFoo"); - } - - [SkippableFact] - public void ExtractToFile_WithOverwrite_ShouldOverwriteExistingFile() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar").Initialized(s => s - .WithFile("bar.txt")); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - IZipArchiveEntry entry = archive.Entries.Single(); - - entry.ExtractToFile("bar/bar.txt", true); - - FileSystem.File.ReadAllText("bar/bar.txt") - .Should().Be("FooFooFoo"); - } - - [SkippableFact] - public void ExtractToFile_IncorrectEntryType_ShouldThrowIOException() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo.txt", "some content"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - archive.CreateEntryFromFile("foo.txt", "foo/"); - archive.Dispose(); - - using FileSystemStream stream2 = FileSystem.File.OpenRead("destination.zip"); - IZipArchive archive2 = FileSystem.ZipArchive().New(stream2, ZipArchiveMode.Read); - - Exception? exception = Record.Exception(() => - { - archive2.ExtractToDirectory("bar"); - }); - - exception.Should().BeOfType(); - } - - [SkippableFact] - public void - ExtractToFile_AccessLengthOnWritableStream_ShouldThrowInvalidOperationException() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo.txt", "some content"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - archive.CreateEntryFromFile("foo.txt", "foo/"); - - Exception? exception = Record.Exception(() => - { - archive.ExtractToDirectory("bar"); - }); - - exception.Should().BeOfType(); - } -} +using System.IO; +using System.IO.Compression; +using System.Linq; + +namespace Testably.Abstractions.Compression.Tests.ZipArchiveEntry; + +public abstract partial class ZipArchiveEntryTests + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void ExtractToFile_DestinationNull_ShouldThrowArgumentNullException( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithAFile()); + + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); + + Exception? exception = Record.Exception(() => + { + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + archive.Entries.Single().ExtractToFile(null!); + }); + + exception.Should().BeOfType() + .Which.ParamName.Should().Be("destinationFileName"); + } + + [SkippableTheory] + [AutoData] + public void + ExtractToFile_DestinationNull_WithOverwrite_ShouldThrowArgumentNullException( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithAFile()); + + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); + + Exception? exception = Record.Exception(() => + { + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + archive.Entries.Single().ExtractToFile(null!, true); + }); + + exception.Should().BeOfType(); + } + + [SkippableFact] + public void ExtractToFile_WithoutOverwrite_ShouldThrowIOException() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar").Initialized(s => s + .WithFile("bar.txt")); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + IZipArchiveEntry entry = archive.Entries.Single(); + + Exception? exception = Record.Exception(() => + { + entry.ExtractToFile("bar/bar.txt"); + }); + + exception.Should().BeOfType() + .Which.Message.Should() + .Contain($"'{FileSystem.Path.GetFullPath("bar/bar.txt")}'"); + FileSystem.File.ReadAllText("bar/bar.txt") + .Should().NotBe("FooFooFoo"); + } + + [SkippableFact] + public void ExtractToFile_WithOverwrite_ShouldOverwriteExistingFile() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar").Initialized(s => s + .WithFile("bar.txt")); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + IZipArchiveEntry entry = archive.Entries.Single(); + + entry.ExtractToFile("bar/bar.txt", true); + + FileSystem.File.ReadAllText("bar/bar.txt") + .Should().Be("FooFooFoo"); + } + + [SkippableFact] + public void ExtractToFile_IncorrectEntryType_ShouldThrowIOException() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo.txt", "some content"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + archive.CreateEntryFromFile("foo.txt", "foo/"); + archive.Dispose(); + + using FileSystemStream stream2 = FileSystem.File.OpenRead("destination.zip"); + IZipArchive archive2 = FileSystem.ZipArchive().New(stream2, ZipArchiveMode.Read); + + Exception? exception = Record.Exception(() => + { + archive2.ExtractToDirectory("bar"); + }); + + exception.Should().BeOfType(); + } + + [SkippableFact] + public void + ExtractToFile_AccessLengthOnWritableStream_ShouldThrowInvalidOperationException() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo.txt", "some content"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + archive.CreateEntryFromFile("foo.txt", "foo/"); + + Exception? exception = Record.Exception(() => + { + archive.ExtractToDirectory("bar"); + }); + + exception.Should().BeOfType(); + } +} diff --git a/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.cs b/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.cs index 02b672b83..087840d23 100644 --- a/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.cs +++ b/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.cs @@ -1,249 +1,248 @@ -using System.IO; -using System.IO.Compression; -using System.Linq; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Compression.Tests.ZipArchiveEntry; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ZipArchiveEntryTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableFact] - public void Archive_ShouldBeSetToArchive() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - IZipArchiveEntry entry = archive.Entries.Single(); - - entry.Archive.Should().Be(archive); - } - - [SkippableFact] - public void CompressedLength_WithNoCompression_ShouldBeFileLength() - { - Skip.If(Test.IsNetFramework, "Test is brittle on .NET Framework."); - - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - archive.Entries.Single().Length.Should().Be(9); - archive.Entries.Single().CompressedLength.Should().Be(9); - } - -#if FEATURE_ZIPFILE_NET7 - [SkippableTheory] - [AutoData] - public void Comment_ShouldBeSettable(string comment) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - IZipArchiveEntry entry = archive.Entries.Single(); - - entry.Comment = comment; - - entry.Comment.Should().Be(comment); - } - - [SkippableFact] - public void Comment_ShouldBeInitializedEmpty() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - IZipArchiveEntry entry = archive.Entries.Single(); - - entry.Comment.Should().Be(""); - } -#endif - - [SkippableFact] - public void CompressedLength_WithOptimalCompressionLevel_ShouldBeLessThanFileLength() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.Optimal, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - archive.Entries.Single().Length.Should().Be(9); - archive.Entries.Single().CompressedLength.Should().BeLessThan(9); - } - -#if FEATURE_COMPRESSION_ADVANCED - [SkippableFact] - public void Crc32_ShouldBeCalculatedFromTheFileContent() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); - FileSystem.File.WriteAllText("foo/foo2.txt", "Some other text"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - IZipArchiveEntry entry1 = archive.Entries[0]; - IZipArchiveEntry entry2 = archive.Entries[1]; - - entry1.Crc32.Should().NotBe(entry2.Crc32); - } -#endif - -#if FEATURE_COMPRESSION_ADVANCED - [SkippableTheory] - [AutoData] - public void ExternalAttributes_ShouldBeSettable(int externalAttributes) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); - FileSystem.File.WriteAllText("foo/foo2.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - IZipArchiveEntry entry1 = archive.Entries[0]; - IZipArchiveEntry entry2 = archive.Entries[1]; - - entry1.ExternalAttributes = externalAttributes; - entry1.ExternalAttributes.Should().Be(externalAttributes); - entry2.ExternalAttributes.Should().NotBe(externalAttributes); - } -#endif - - [SkippableFact] - public void FileSystemExtension_ShouldBeSet() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - IZipArchiveEntry entry = archive.Entries.Single(); - - entry.FileSystem.Should().Be(FileSystem); - } - - [SkippableFact] - public void FullName_ShouldIncludeDirectory() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - true); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - IZipArchiveEntry entry = archive.Entries.Single(); - - entry.FullName.Should().Be("foo/foo.txt"); - entry.Name.Should().Be("foo.txt"); - } - - [SkippableTheory] - [AutoData] - public void LastWriteTime_ReadOnlyArchive_ShouldThrowNotSupportedException( - DateTime lastWriteTime) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); - FileSystem.File.WriteAllText("foo/foo2.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - IZipArchiveEntry entry1 = archive.Entries[0]; - Exception? exception = Record.Exception(() => - { - entry1.LastWriteTime = lastWriteTime; - }); - - exception.Should().BeOfType(); - } - - [SkippableTheory] - [AutoData] - public void LastWriteTime_ShouldBeSettable(DateTime lastWriteTime) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); - FileSystem.File.WriteAllText("foo/foo2.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - - IZipArchiveEntry entry1 = archive.Entries[0]; - IZipArchiveEntry entry2 = archive.Entries[1]; - - entry1.LastWriteTime = lastWriteTime; - entry1.LastWriteTime.Should().Be(lastWriteTime); - entry2.LastWriteTime.Should().NotBe(lastWriteTime); - } -} +using System.IO; +using System.IO.Compression; +using System.Linq; + +namespace Testably.Abstractions.Compression.Tests.ZipArchiveEntry; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ZipArchiveEntryTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableFact] + public void Archive_ShouldBeSetToArchive() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + IZipArchiveEntry entry = archive.Entries.Single(); + + entry.Archive.Should().Be(archive); + } + + [SkippableFact] + public void CompressedLength_WithNoCompression_ShouldBeFileLength() + { + Skip.If(Test.IsNetFramework, "Test is brittle on .NET Framework."); + + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + archive.Entries.Single().Length.Should().Be(9); + archive.Entries.Single().CompressedLength.Should().Be(9); + } + +#if FEATURE_ZIPFILE_NET7 + [SkippableTheory] + [AutoData] + public void Comment_ShouldBeSettable(string comment) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + IZipArchiveEntry entry = archive.Entries.Single(); + + entry.Comment = comment; + + entry.Comment.Should().Be(comment); + } + + [SkippableFact] + public void Comment_ShouldBeInitializedEmpty() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + IZipArchiveEntry entry = archive.Entries.Single(); + + entry.Comment.Should().Be(""); + } +#endif + + [SkippableFact] + public void CompressedLength_WithOptimalCompressionLevel_ShouldBeLessThanFileLength() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.Optimal, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + archive.Entries.Single().Length.Should().Be(9); + archive.Entries.Single().CompressedLength.Should().BeLessThan(9); + } + +#if FEATURE_COMPRESSION_ADVANCED + [SkippableFact] + public void Crc32_ShouldBeCalculatedFromTheFileContent() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); + FileSystem.File.WriteAllText("foo/foo2.txt", "Some other text"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + IZipArchiveEntry entry1 = archive.Entries[0]; + IZipArchiveEntry entry2 = archive.Entries[1]; + + entry1.Crc32.Should().NotBe(entry2.Crc32); + } +#endif + +#if FEATURE_COMPRESSION_ADVANCED + [SkippableTheory] + [AutoData] + public void ExternalAttributes_ShouldBeSettable(int externalAttributes) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); + FileSystem.File.WriteAllText("foo/foo2.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + IZipArchiveEntry entry1 = archive.Entries[0]; + IZipArchiveEntry entry2 = archive.Entries[1]; + + entry1.ExternalAttributes = externalAttributes; + entry1.ExternalAttributes.Should().Be(externalAttributes); + entry2.ExternalAttributes.Should().NotBe(externalAttributes); + } +#endif + + [SkippableFact] + public void FileSystemExtension_ShouldBeSet() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + IZipArchiveEntry entry = archive.Entries.Single(); + + entry.FileSystem.Should().Be(FileSystem); + } + + [SkippableFact] + public void FullName_ShouldIncludeDirectory() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + true); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + IZipArchiveEntry entry = archive.Entries.Single(); + + entry.FullName.Should().Be("foo/foo.txt"); + entry.Name.Should().Be("foo.txt"); + } + + [SkippableTheory] + [AutoData] + public void LastWriteTime_ReadOnlyArchive_ShouldThrowNotSupportedException( + DateTime lastWriteTime) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); + FileSystem.File.WriteAllText("foo/foo2.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + IZipArchiveEntry entry1 = archive.Entries[0]; + Exception? exception = Record.Exception(() => + { + entry1.LastWriteTime = lastWriteTime; + }); + + exception.Should().BeOfType(); + } + + [SkippableTheory] + [AutoData] + public void LastWriteTime_ShouldBeSettable(DateTime lastWriteTime) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); + FileSystem.File.WriteAllText("foo/foo2.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + + IZipArchiveEntry entry1 = archive.Entries[0]; + IZipArchiveEntry entry2 = archive.Entries[1]; + + entry1.LastWriteTime = lastWriteTime; + entry1.LastWriteTime.Should().Be(lastWriteTime); + entry2.LastWriteTime.Should().NotBe(lastWriteTime); + } +} diff --git a/Tests/Testably.Abstractions.Parity.Tests/ParityTests.cs b/Tests/Testably.Abstractions.Parity.Tests/ParityTests.cs index b34d5482a..bc86f1f01 100644 --- a/Tests/Testably.Abstractions.Parity.Tests/ParityTests.cs +++ b/Tests/Testably.Abstractions.Parity.Tests/ParityTests.cs @@ -1,197 +1,196 @@ -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.RandomSystem; -using Xunit.Abstractions; - -namespace Testably.Abstractions.Parity.Tests; - -public abstract class ParityTests -{ - #region Test Setup - - public TestHelpers.Parity Parity { get; } - - private readonly ITestOutputHelper _testOutputHelper; - - protected ParityTests(TestHelpers.Parity parity, - ITestOutputHelper testOutputHelper) - { - Parity = parity; - _testOutputHelper = testOutputHelper; - } - - #endregion - - [Fact] - public void IDirectory_EnsureParityWith_Directory() - { - List parityErrors = Parity.Directory - .GetErrorsToStaticType( - typeof(Directory), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IDirectoryInfoAndIDirectoryInfoFactory_EnsureParityWith_DirectoryInfo() - { - List parityErrors = Parity.DirectoryInfo - .GetErrorsToInstanceType( - typeof(DirectoryInfo), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IDriveInfoAndIDriveInfoFactory_EnsureParityWith_DriveInfo() - { - List parityErrors = Parity.Drive - .GetErrorsToInstanceType( - typeof(DriveInfo), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IFile_EnsureParityWith_File() - { - List parityErrors = Parity.File - .GetErrorsToStaticType( - typeof(File), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IFileInfoAndIFileInfoFactory_EnsureParityWith_FileInfo() - { - List parityErrors = Parity.FileInfo - .GetErrorsToInstanceType( - typeof(FileInfo), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IFileSystemInfo_EnsureParityWith_FileSystemInfo() - { - List parityErrors = Parity.FileSystemInfo - .GetErrorsToInstanceType( - typeof(FileSystemInfo), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void - IFileSystemWatcherAndIFileSystemWatcherFactory_EnsureParityWith_FileSystemWatcher() - { - List parityErrors = Parity.FileSystemWatcher - .GetErrorsToInstanceType( - typeof(FileSystemWatcher), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IGuid_EnsureParityWith_Guid() - { - List parityErrors = Parity.Guid - .GetErrorsToStaticType( - typeof(Guid), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IPath_EnsureParityWith_Path() - { - List parityErrors = Parity.Path - .GetErrorsToStaticType( - typeof(Path), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IRandomAndIRandomFactory_EnsureParityWith_Random() - { - List parityErrors = Parity.Random - .GetErrorsToInstanceType( - typeof(Random), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IZipArchive_EnsureParityWith_ZipArchive() - { - List parityErrors = Parity.ZipArchive - .GetErrorsToInstanceType( - typeof(ZipArchive), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IZipArchive_EnsureParityWith_ZipFileExtensions() - { - List parityErrors = Parity.ZipArchive - .GetErrorsToExtensionMethods( - typeof(ZipFileExtensions), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IZipArchiveEntry_EnsureParityWith_ZipArchiveEntry() - { - List parityErrors = Parity.ZipArchiveEntry - .GetErrorsToInstanceType( - typeof(ZipArchiveEntry), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IZipArchiveEntry_EnsureParityWith_ZipFileExtensions() - { - List parityErrors = Parity.ZipArchiveEntry - .GetErrorsToExtensionMethods( - typeof(ZipFileExtensions), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IZipFile_EnsureParityWith_ZipFile() - { - List parityErrors = Parity.ZipFile - .GetErrorsToStaticType( - typeof(ZipFile), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } -} +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using Testably.Abstractions.RandomSystem; +using Xunit.Abstractions; + +namespace Testably.Abstractions.Parity.Tests; + +public abstract class ParityTests +{ + #region Test Setup + + public TestHelpers.Parity Parity { get; } + + private readonly ITestOutputHelper _testOutputHelper; + + protected ParityTests(TestHelpers.Parity parity, + ITestOutputHelper testOutputHelper) + { + Parity = parity; + _testOutputHelper = testOutputHelper; + } + + #endregion + + [Fact] + public void IDirectory_EnsureParityWith_Directory() + { + List parityErrors = Parity.Directory + .GetErrorsToStaticType( + typeof(Directory), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IDirectoryInfoAndIDirectoryInfoFactory_EnsureParityWith_DirectoryInfo() + { + List parityErrors = Parity.DirectoryInfo + .GetErrorsToInstanceType( + typeof(DirectoryInfo), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IDriveInfoAndIDriveInfoFactory_EnsureParityWith_DriveInfo() + { + List parityErrors = Parity.Drive + .GetErrorsToInstanceType( + typeof(DriveInfo), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IFile_EnsureParityWith_File() + { + List parityErrors = Parity.File + .GetErrorsToStaticType( + typeof(File), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IFileInfoAndIFileInfoFactory_EnsureParityWith_FileInfo() + { + List parityErrors = Parity.FileInfo + .GetErrorsToInstanceType( + typeof(FileInfo), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IFileSystemInfo_EnsureParityWith_FileSystemInfo() + { + List parityErrors = Parity.FileSystemInfo + .GetErrorsToInstanceType( + typeof(FileSystemInfo), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void + IFileSystemWatcherAndIFileSystemWatcherFactory_EnsureParityWith_FileSystemWatcher() + { + List parityErrors = Parity.FileSystemWatcher + .GetErrorsToInstanceType( + typeof(FileSystemWatcher), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IGuid_EnsureParityWith_Guid() + { + List parityErrors = Parity.Guid + .GetErrorsToStaticType( + typeof(Guid), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IPath_EnsureParityWith_Path() + { + List parityErrors = Parity.Path + .GetErrorsToStaticType( + typeof(Path), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IRandomAndIRandomFactory_EnsureParityWith_Random() + { + List parityErrors = Parity.Random + .GetErrorsToInstanceType( + typeof(Random), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IZipArchive_EnsureParityWith_ZipArchive() + { + List parityErrors = Parity.ZipArchive + .GetErrorsToInstanceType( + typeof(ZipArchive), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IZipArchive_EnsureParityWith_ZipFileExtensions() + { + List parityErrors = Parity.ZipArchive + .GetErrorsToExtensionMethods( + typeof(ZipFileExtensions), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IZipArchiveEntry_EnsureParityWith_ZipArchiveEntry() + { + List parityErrors = Parity.ZipArchiveEntry + .GetErrorsToInstanceType( + typeof(ZipArchiveEntry), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IZipArchiveEntry_EnsureParityWith_ZipFileExtensions() + { + List parityErrors = Parity.ZipArchiveEntry + .GetErrorsToExtensionMethods( + typeof(ZipFileExtensions), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IZipFile_EnsureParityWith_ZipFile() + { + List parityErrors = Parity.ZipFile + .GetErrorsToStaticType( + typeof(ZipFile), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } +} diff --git a/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/Parity.cs b/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/Parity.cs index 54a0387eb..a49b4b08e 100644 --- a/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/Parity.cs +++ b/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/Parity.cs @@ -1,74 +1,73 @@ -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.IO; -using System.IO.Compression; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Parity.Tests.TestHelpers; - -public class Parity -{ - public static readonly ReadOnlyDictionary AcceptedTypeMapping = new( - new Dictionary - { - { - nameof(FileStream), nameof(FileSystemStream) - } - }); - - public ParityCheck Directory { get; } = new(); - - public ParityCheck DirectoryInfo { get; } = new(excludeMethods: new[] - { - typeof(DirectoryInfo).GetMethod(nameof(System.IO.DirectoryInfo - .GetObjectData)), - typeof(DirectoryInfo).GetMethod(nameof(System.IO.DirectoryInfo.ToString)) - }); - - public ParityCheck Drive { get; } = new(excludeMethods: new[] - { - typeof(DriveInfo).GetMethod(nameof(DriveInfo.ToString)) - }); - - public ParityCheck File { get; } = new(); - - public ParityCheck FileInfo { get; } = new(excludeMethods: new[] - { - typeof(FileInfo).GetMethod(nameof(System.IO.FileInfo.GetObjectData)), - typeof(FileInfo).GetMethod(nameof(System.IO.FileInfo.ToString)) - }); - - public ParityCheck FileStream { get; } = new(); - - public ParityCheck FileSystemInfo { get; } = new(excludeMethods: new[] - { - typeof(FileSystemInfo).GetMethod( - nameof(System.IO.FileSystemInfo.GetObjectData)), - typeof(FileSystemInfo).GetMethod(nameof(ToString)) - }); - - public ParityCheck FileSystemWatcher { get; } = new(excludeMethods: new[] - { - typeof(FileSystemWatcher).GetMethod( - nameof(System.IO.FileSystemWatcher.ToString)) - }); - - public ParityCheck Guid { get; } = new(); - - public ParityCheck Path { get; } = new(excludeFields: new[] - { - #pragma warning disable CS0618 - typeof(Path).GetField(nameof(System.IO.Path.InvalidPathChars)) - #pragma warning restore CS0618 - }); - - public ParityCheck Random { get; } = new(); - public ParityCheck ZipArchive { get; } = new(); - - public ParityCheck ZipArchiveEntry { get; } = new(excludeMethods: new[] - { - typeof(ZipArchiveEntry).GetMethod(nameof(ToString)) - }); - - public ParityCheck ZipFile { get; } = new(); -} +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.IO.Compression; + +namespace Testably.Abstractions.Parity.Tests.TestHelpers; + +public class Parity +{ + public static readonly ReadOnlyDictionary AcceptedTypeMapping = new( + new Dictionary + { + { + nameof(FileStream), nameof(FileSystemStream) + } + }); + + public ParityCheck Directory { get; } = new(); + + public ParityCheck DirectoryInfo { get; } = new(excludeMethods: new[] + { + typeof(DirectoryInfo).GetMethod(nameof(System.IO.DirectoryInfo + .GetObjectData)), + typeof(DirectoryInfo).GetMethod(nameof(System.IO.DirectoryInfo.ToString)) + }); + + public ParityCheck Drive { get; } = new(excludeMethods: new[] + { + typeof(DriveInfo).GetMethod(nameof(DriveInfo.ToString)) + }); + + public ParityCheck File { get; } = new(); + + public ParityCheck FileInfo { get; } = new(excludeMethods: new[] + { + typeof(FileInfo).GetMethod(nameof(System.IO.FileInfo.GetObjectData)), + typeof(FileInfo).GetMethod(nameof(System.IO.FileInfo.ToString)) + }); + + public ParityCheck FileStream { get; } = new(); + + public ParityCheck FileSystemInfo { get; } = new(excludeMethods: new[] + { + typeof(FileSystemInfo).GetMethod( + nameof(System.IO.FileSystemInfo.GetObjectData)), + typeof(FileSystemInfo).GetMethod(nameof(ToString)) + }); + + public ParityCheck FileSystemWatcher { get; } = new(excludeMethods: new[] + { + typeof(FileSystemWatcher).GetMethod( + nameof(System.IO.FileSystemWatcher.ToString)) + }); + + public ParityCheck Guid { get; } = new(); + + public ParityCheck Path { get; } = new(excludeFields: new[] + { + #pragma warning disable CS0618 + typeof(Path).GetField(nameof(System.IO.Path.InvalidPathChars)) + #pragma warning restore CS0618 + }); + + public ParityCheck Random { get; } = new(); + public ParityCheck ZipArchive { get; } = new(); + + public ParityCheck ZipArchiveEntry { get; } = new(excludeMethods: new[] + { + typeof(ZipArchiveEntry).GetMethod(nameof(ToString)) + }); + + public ParityCheck ZipFile { get; } = new(); +} diff --git a/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/Usings.cs b/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/Usings.cs index 1c2653bb4..168110d56 100644 --- a/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/Usings.cs +++ b/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/Usings.cs @@ -1,3 +1,4 @@ global using FluentAssertions; global using System; +global using Testably.Abstractions.FileSystem; global using Xunit; diff --git a/Tests/Testably.Abstractions.Testing.Tests/FileSystem/DriveInfoMockTests.cs b/Tests/Testably.Abstractions.Testing.Tests/FileSystem/DriveInfoMockTests.cs index 77092af39..0d000f451 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/FileSystem/DriveInfoMockTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/FileSystem/DriveInfoMockTests.cs @@ -1,285 +1,284 @@ -using System.IO; -using System.Linq; -using System.Text; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystem; - -namespace Testably.Abstractions.Testing.Tests.FileSystem; - -public class DriveInfoMockTests -{ - #region Test Setup - - public MockFileSystem FileSystem { get; } - - public DriveInfoMockTests() - { - FileSystem = new MockFileSystem(); - } - - #endregion - - [SkippableTheory] - [AutoData] - public void AvailableFreeSpace_CannotGetNegative(long size) - { - FileSystem.WithDrive(d => d.SetTotalSize(size)); - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - - FileSystem.WithDrive(d => d.ChangeUsedBytes(-1)); - - drive.AvailableFreeSpace.Should().Be(size); - } - - [SkippableTheory] - [AutoData] - public void AvailableFreeSpace_NotEnoughSpace_ShouldThrowIOException( - int fileSize, string path) - { - byte[] bytes = new byte[fileSize]; - FileSystem.WithDrive(d => d.SetTotalSize(fileSize - 1)); - FileSystem.RandomSystem.Random.Shared.NextBytes(bytes); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.WriteAllBytes(path, bytes); - }); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - exception.Should().BeOfType() - .Which.Message.Should().Contain($"'{drive.Name}'"); - drive.AvailableFreeSpace.Should().Be(fileSize - 1); - } - - [SkippableTheory] - [AutoData] - public void AvailableFreeSpace_ShouldBeChangedWhenAppendingToAFile( - string fileContent1, string fileContent2, int expectedRemainingBytes, - string path, Encoding encoding) - { - int fileSize1 = encoding.GetPreamble().Length + encoding.GetBytes(fileContent1).Length; - int fileSize2 = encoding.GetBytes(fileContent2).Length; - FileSystem.WithDrive(d - => d.SetTotalSize(fileSize1 + fileSize2 + expectedRemainingBytes)); - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - - FileSystem.File.WriteAllText(path, fileContent1, encoding); - drive.AvailableFreeSpace.Should().Be(expectedRemainingBytes + fileSize2); - FileSystem.File.AppendAllText(path, fileContent2, encoding); - - drive.AvailableFreeSpace.Should().Be(expectedRemainingBytes); - } - - [SkippableTheory] - [InlineAutoData(0)] - [InlineAutoData(1)] - [InlineAutoData(10)] - public void AvailableFreeSpace_ShouldBeChangedWhenWorkingWithStreams( - int reduceLength, string path, string previousContent) - { - FileSystem.File.WriteAllText(path, previousContent); - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - long previousFreeSpace = drive.AvailableFreeSpace; - - FileSystemStream stream = FileSystem.File.OpenWrite(path); - using (StreamWriter streamWriter = new(stream)) - { - streamWriter.Write("new-content"); - stream.SetLength(stream.Length - reduceLength); - } - - drive.AvailableFreeSpace.Should().Be(previousFreeSpace + reduceLength); - } - - [SkippableTheory] - [AutoData] - public void AvailableFreeSpace_ShouldBeReducedByWritingToFile( - int fileSize, string path) - { - byte[] bytes = new byte[fileSize]; - FileSystem.WithDrive(d => d.SetTotalSize(fileSize)); - FileSystem.RandomSystem.Random.Shared.NextBytes(bytes); - - FileSystem.File.WriteAllBytes(path, bytes); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - - drive.AvailableFreeSpace.Should().Be(0); - } - - [SkippableTheory] - [AutoData] - public void AvailableFreeSpace_ShouldBeReleasedWhenDeletingAFile( - int fileSize, string path) - { - byte[] bytes = new byte[fileSize]; - FileSystem.WithDrive(d => d.SetTotalSize(fileSize)); - FileSystem.RandomSystem.Random.Shared.NextBytes(bytes); - - FileSystem.File.WriteAllBytes(path, bytes); - FileSystem.File.Delete(path); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - - drive.AvailableFreeSpace.Should().Be(fileSize); - } - - [SkippableTheory] - [AutoData] - public void AvailableFreeSpace_ShouldBeSetTotalSize(long size) - { - FileSystem.WithDrive(d => d.SetTotalSize(size)); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - - drive.AvailableFreeSpace.Should().Be(size); - } - - [SkippableTheory] - [InlineData(@"//foo", @"//foo")] - [InlineData(@"//foo/bar", @"//foo")] - [InlineData(@"//foo/bar/xyz", @"//foo")] - public void New_DriveNameWithUncPath_ShouldUseTopMostDirectory( - string driveName, string expectedName) - { - expectedName = expectedName - .Replace('/', FileSystem.Path.DirectorySeparatorChar); - - IDriveInfo drive = - DriveInfoMock.New(driveName, FileSystem); - - drive.Name.Should().Be(expectedName); - } - - [SkippableTheory] - [InlineData("foo")] - public void New_InvalidDriveName_ShouldThrowArgumentException(string driveName) - { - Exception? exception = Record.Exception(() => - { - DriveInfoMock.New(driveName, FileSystem); - }); - - exception.Should().BeOfType(); - } - - [SkippableFact] - public void New_Null_ShouldReturnNull() - { - IDriveInfo? drive = - DriveInfoMock.New(null, FileSystem); - - drive.Should().BeNull(); - } - - [SkippableFact] - public void New_UncPath_ShouldSetFlag() - { - IDriveInfo drive = - DriveInfoMock.New(@"//foo", FileSystem); - - (drive as DriveInfoMock)?.IsUncPath.Should().BeTrue(); - } - - [SkippableTheory] - [InlineData("C", "C:\\")] - [InlineData("d", "D:\\")] - public void New_ValidDriveName_ShouldAppendColonAndSlash( - string driveName, string expectedDriveName) - { - DriveInfoMock result = - DriveInfoMock.New(driveName, FileSystem); - - result.Name.Should().Be(expectedDriveName); - } - - [SkippableTheory] - [AutoData] - public void NotReady_AccessDirectory_ShouldThrowIOException( - string path) - { - FileSystem.WithDrive(d => d.SetIsReady(false)); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.CreateDirectory(path); - }); - - exception.Should().BeOfType(); - } - - [SkippableTheory] - [AutoData] - public void NotReady_AccessFile_ShouldThrowIOException( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - FileSystem.WithDrive(d => d.SetIsReady(false)); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.ReadAllText(path); - }); - - exception.Should().BeOfType(); - } - - [SkippableFact] - public void SetDriveFormat_Default_ShouldBeNTFS() - { - FileSystem.WithDrive(d => d.SetDriveFormat()); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - drive.DriveFormat.Should().Be("NTFS"); - } - - [SkippableTheory] - [AutoData] - public void SetDriveFormat_ShouldChangeDriveFormat(string driveFormat) - { - FileSystem.WithDrive(d => d.SetDriveFormat(driveFormat)); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - drive.DriveFormat.Should().Be(driveFormat); - } - - [SkippableFact] - public void SetDriveType_Default_ShouldBeFixed() - { - FileSystem.WithDrive(d => d.SetDriveType()); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - drive.DriveType.Should().Be(DriveType.Fixed); - } - - [SkippableTheory] - [AutoData] - public void SetDriveType_ShouldChangeDriveType(DriveType driveType) - { - FileSystem.WithDrive(d => d.SetDriveType(driveType)); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - drive.DriveType.Should().Be(driveType); - } - - [SkippableTheory] - [InlineData(true)] - [InlineData(false)] - public void SetIsReady_ShouldChangeIsReady(bool isReady) - { - FileSystem.WithDrive(d => d.SetIsReady(isReady)); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - drive.IsReady.Should().Be(isReady); - } - - [SkippableFact] - public void SetTotalSize_Default_ShouldBe1Gigabyte() - { - FileSystem.WithDrive(d => d.SetTotalSize()); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - - drive.AvailableFreeSpace.Should().Be(1024 * 1024 * 1024); - } -} +using System.IO; +using System.Linq; +using System.Text; +using Testably.Abstractions.Testing.FileSystem; + +namespace Testably.Abstractions.Testing.Tests.FileSystem; + +public class DriveInfoMockTests +{ + #region Test Setup + + public MockFileSystem FileSystem { get; } + + public DriveInfoMockTests() + { + FileSystem = new MockFileSystem(); + } + + #endregion + + [SkippableTheory] + [AutoData] + public void AvailableFreeSpace_CannotGetNegative(long size) + { + FileSystem.WithDrive(d => d.SetTotalSize(size)); + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + + FileSystem.WithDrive(d => d.ChangeUsedBytes(-1)); + + drive.AvailableFreeSpace.Should().Be(size); + } + + [SkippableTheory] + [AutoData] + public void AvailableFreeSpace_NotEnoughSpace_ShouldThrowIOException( + int fileSize, string path) + { + byte[] bytes = new byte[fileSize]; + FileSystem.WithDrive(d => d.SetTotalSize(fileSize - 1)); + FileSystem.RandomSystem.Random.Shared.NextBytes(bytes); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.WriteAllBytes(path, bytes); + }); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + exception.Should().BeOfType() + .Which.Message.Should().Contain($"'{drive.Name}'"); + drive.AvailableFreeSpace.Should().Be(fileSize - 1); + } + + [SkippableTheory] + [AutoData] + public void AvailableFreeSpace_ShouldBeChangedWhenAppendingToAFile( + string fileContent1, string fileContent2, int expectedRemainingBytes, + string path, Encoding encoding) + { + int fileSize1 = encoding.GetPreamble().Length + encoding.GetBytes(fileContent1).Length; + int fileSize2 = encoding.GetBytes(fileContent2).Length; + FileSystem.WithDrive(d + => d.SetTotalSize(fileSize1 + fileSize2 + expectedRemainingBytes)); + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + + FileSystem.File.WriteAllText(path, fileContent1, encoding); + drive.AvailableFreeSpace.Should().Be(expectedRemainingBytes + fileSize2); + FileSystem.File.AppendAllText(path, fileContent2, encoding); + + drive.AvailableFreeSpace.Should().Be(expectedRemainingBytes); + } + + [SkippableTheory] + [InlineAutoData(0)] + [InlineAutoData(1)] + [InlineAutoData(10)] + public void AvailableFreeSpace_ShouldBeChangedWhenWorkingWithStreams( + int reduceLength, string path, string previousContent) + { + FileSystem.File.WriteAllText(path, previousContent); + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + long previousFreeSpace = drive.AvailableFreeSpace; + + FileSystemStream stream = FileSystem.File.OpenWrite(path); + using (StreamWriter streamWriter = new(stream)) + { + streamWriter.Write("new-content"); + stream.SetLength(stream.Length - reduceLength); + } + + drive.AvailableFreeSpace.Should().Be(previousFreeSpace + reduceLength); + } + + [SkippableTheory] + [AutoData] + public void AvailableFreeSpace_ShouldBeReducedByWritingToFile( + int fileSize, string path) + { + byte[] bytes = new byte[fileSize]; + FileSystem.WithDrive(d => d.SetTotalSize(fileSize)); + FileSystem.RandomSystem.Random.Shared.NextBytes(bytes); + + FileSystem.File.WriteAllBytes(path, bytes); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + + drive.AvailableFreeSpace.Should().Be(0); + } + + [SkippableTheory] + [AutoData] + public void AvailableFreeSpace_ShouldBeReleasedWhenDeletingAFile( + int fileSize, string path) + { + byte[] bytes = new byte[fileSize]; + FileSystem.WithDrive(d => d.SetTotalSize(fileSize)); + FileSystem.RandomSystem.Random.Shared.NextBytes(bytes); + + FileSystem.File.WriteAllBytes(path, bytes); + FileSystem.File.Delete(path); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + + drive.AvailableFreeSpace.Should().Be(fileSize); + } + + [SkippableTheory] + [AutoData] + public void AvailableFreeSpace_ShouldBeSetTotalSize(long size) + { + FileSystem.WithDrive(d => d.SetTotalSize(size)); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + + drive.AvailableFreeSpace.Should().Be(size); + } + + [SkippableTheory] + [InlineData(@"//foo", @"//foo")] + [InlineData(@"//foo/bar", @"//foo")] + [InlineData(@"//foo/bar/xyz", @"//foo")] + public void New_DriveNameWithUncPath_ShouldUseTopMostDirectory( + string driveName, string expectedName) + { + expectedName = expectedName + .Replace('/', FileSystem.Path.DirectorySeparatorChar); + + IDriveInfo drive = + DriveInfoMock.New(driveName, FileSystem); + + drive.Name.Should().Be(expectedName); + } + + [SkippableTheory] + [InlineData("foo")] + public void New_InvalidDriveName_ShouldThrowArgumentException(string driveName) + { + Exception? exception = Record.Exception(() => + { + DriveInfoMock.New(driveName, FileSystem); + }); + + exception.Should().BeOfType(); + } + + [SkippableFact] + public void New_Null_ShouldReturnNull() + { + IDriveInfo? drive = + DriveInfoMock.New(null, FileSystem); + + drive.Should().BeNull(); + } + + [SkippableFact] + public void New_UncPath_ShouldSetFlag() + { + IDriveInfo drive = + DriveInfoMock.New(@"//foo", FileSystem); + + (drive as DriveInfoMock)?.IsUncPath.Should().BeTrue(); + } + + [SkippableTheory] + [InlineData("C", "C:\\")] + [InlineData("d", "D:\\")] + public void New_ValidDriveName_ShouldAppendColonAndSlash( + string driveName, string expectedDriveName) + { + DriveInfoMock result = + DriveInfoMock.New(driveName, FileSystem); + + result.Name.Should().Be(expectedDriveName); + } + + [SkippableTheory] + [AutoData] + public void NotReady_AccessDirectory_ShouldThrowIOException( + string path) + { + FileSystem.WithDrive(d => d.SetIsReady(false)); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.CreateDirectory(path); + }); + + exception.Should().BeOfType(); + } + + [SkippableTheory] + [AutoData] + public void NotReady_AccessFile_ShouldThrowIOException( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + FileSystem.WithDrive(d => d.SetIsReady(false)); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.ReadAllText(path); + }); + + exception.Should().BeOfType(); + } + + [SkippableFact] + public void SetDriveFormat_Default_ShouldBeNTFS() + { + FileSystem.WithDrive(d => d.SetDriveFormat()); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + drive.DriveFormat.Should().Be("NTFS"); + } + + [SkippableTheory] + [AutoData] + public void SetDriveFormat_ShouldChangeDriveFormat(string driveFormat) + { + FileSystem.WithDrive(d => d.SetDriveFormat(driveFormat)); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + drive.DriveFormat.Should().Be(driveFormat); + } + + [SkippableFact] + public void SetDriveType_Default_ShouldBeFixed() + { + FileSystem.WithDrive(d => d.SetDriveType()); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + drive.DriveType.Should().Be(DriveType.Fixed); + } + + [SkippableTheory] + [AutoData] + public void SetDriveType_ShouldChangeDriveType(DriveType driveType) + { + FileSystem.WithDrive(d => d.SetDriveType(driveType)); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + drive.DriveType.Should().Be(driveType); + } + + [SkippableTheory] + [InlineData(true)] + [InlineData(false)] + public void SetIsReady_ShouldChangeIsReady(bool isReady) + { + FileSystem.WithDrive(d => d.SetIsReady(isReady)); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + drive.IsReady.Should().Be(isReady); + } + + [SkippableFact] + public void SetTotalSize_Default_ShouldBe1Gigabyte() + { + FileSystem.WithDrive(d => d.SetTotalSize()); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + + drive.AvailableFreeSpace.Should().Be(1024 * 1024 * 1024); + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherFactoryMockTests.cs b/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherFactoryMockTests.cs index 63b8fa8c3..bf9d84301 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherFactoryMockTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherFactoryMockTests.cs @@ -1,75 +1,74 @@ -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Testing.Tests.FileSystem; - -public sealed class FileSystemWatcherFactoryMockTests : IDisposable -{ - public string BasePath => _directoryCleaner.BasePath; - public MockFileSystem FileSystem { get; } - public RealFileSystem RealFileSystem { get; } - private readonly IDirectoryCleaner _directoryCleaner; - - public FileSystemWatcherFactoryMockTests() - { - FileSystem = new MockFileSystem(); - RealFileSystem = new RealFileSystem(); - _directoryCleaner = RealFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); - FileSystem.InitializeIn(RealFileSystem.Directory.GetCurrentDirectory()); - FileSystem.Directory.SetCurrentDirectory(RealFileSystem.Directory - .GetCurrentDirectory()); - } - - #region IDisposable Members - - /// - public void Dispose() - => _directoryCleaner.Dispose(); - - #endregion - - [SkippableTheory] - [AutoData] - public void Wrap_ShouldUsePropertiesFromFileSystemWatcher( - string path, bool includeSubdirectories, NotifyFilters notifyFilter, - int internalBufferSize, bool enableRaisingEvents, string filter) - { - FileSystem.Directory.CreateDirectory(path); - RealFileSystem.Directory.CreateDirectory(path); - FileSystemWatcher fileSystemWatcher = new(); - fileSystemWatcher.Path = path; - fileSystemWatcher.IncludeSubdirectories = includeSubdirectories; - fileSystemWatcher.NotifyFilter = notifyFilter; - fileSystemWatcher.InternalBufferSize = internalBufferSize; - fileSystemWatcher.EnableRaisingEvents = enableRaisingEvents; - fileSystemWatcher.Filter = filter; - - IFileSystemWatcher result = FileSystem.FileSystemWatcher.Wrap(fileSystemWatcher); - - result.Path.Should().Be(fileSystemWatcher.Path); - result.IncludeSubdirectories.Should().Be(fileSystemWatcher.IncludeSubdirectories); - result.NotifyFilter.Should().Be(fileSystemWatcher.NotifyFilter); - result.InternalBufferSize.Should().Be(fileSystemWatcher.InternalBufferSize); - result.EnableRaisingEvents.Should().Be(fileSystemWatcher.EnableRaisingEvents); - result.Filter.Should().Be(fileSystemWatcher.Filter); - } - -#if FEATURE_FILESYSTEMWATCHER_ADVANCED - [SkippableTheory] - [AutoData] - public void Wrap_WithFilters_ShouldUsePropertiesFromFileSystemWatcher( - string[] filters) - { - FileSystemWatcher fileSystemWatcher = new("."); - foreach (string filter in filters) - { - fileSystemWatcher.Filters.Add(filter); - } - - IFileSystemWatcher result = FileSystem.FileSystemWatcher.Wrap(fileSystemWatcher); - - result.Filters.Should().BeEquivalentTo(filters); - } -#endif -} +using System.IO; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Testing.Tests.FileSystem; + +public sealed class FileSystemWatcherFactoryMockTests : IDisposable +{ + public string BasePath => _directoryCleaner.BasePath; + public MockFileSystem FileSystem { get; } + public RealFileSystem RealFileSystem { get; } + private readonly IDirectoryCleaner _directoryCleaner; + + public FileSystemWatcherFactoryMockTests() + { + FileSystem = new MockFileSystem(); + RealFileSystem = new RealFileSystem(); + _directoryCleaner = RealFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); + FileSystem.InitializeIn(RealFileSystem.Directory.GetCurrentDirectory()); + FileSystem.Directory.SetCurrentDirectory(RealFileSystem.Directory + .GetCurrentDirectory()); + } + + #region IDisposable Members + + /// + public void Dispose() + => _directoryCleaner.Dispose(); + + #endregion + + [SkippableTheory] + [AutoData] + public void Wrap_ShouldUsePropertiesFromFileSystemWatcher( + string path, bool includeSubdirectories, NotifyFilters notifyFilter, + int internalBufferSize, bool enableRaisingEvents, string filter) + { + FileSystem.Directory.CreateDirectory(path); + RealFileSystem.Directory.CreateDirectory(path); + FileSystemWatcher fileSystemWatcher = new(); + fileSystemWatcher.Path = path; + fileSystemWatcher.IncludeSubdirectories = includeSubdirectories; + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.InternalBufferSize = internalBufferSize; + fileSystemWatcher.EnableRaisingEvents = enableRaisingEvents; + fileSystemWatcher.Filter = filter; + + IFileSystemWatcher result = FileSystem.FileSystemWatcher.Wrap(fileSystemWatcher); + + result.Path.Should().Be(fileSystemWatcher.Path); + result.IncludeSubdirectories.Should().Be(fileSystemWatcher.IncludeSubdirectories); + result.NotifyFilter.Should().Be(fileSystemWatcher.NotifyFilter); + result.InternalBufferSize.Should().Be(fileSystemWatcher.InternalBufferSize); + result.EnableRaisingEvents.Should().Be(fileSystemWatcher.EnableRaisingEvents); + result.Filter.Should().Be(fileSystemWatcher.Filter); + } + +#if FEATURE_FILESYSTEMWATCHER_ADVANCED + [SkippableTheory] + [AutoData] + public void Wrap_WithFilters_ShouldUsePropertiesFromFileSystemWatcher( + string[] filters) + { + FileSystemWatcher fileSystemWatcher = new("."); + foreach (string filter in filters) + { + fileSystemWatcher.Filters.Add(filter); + } + + IFileSystemWatcher result = FileSystem.FileSystemWatcher.Wrap(fileSystemWatcher); + + result.Filters.Should().BeEquivalentTo(filters); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherMockTests.cs b/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherMockTests.cs index 5d415ba06..eba35d186 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherMockTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherMockTests.cs @@ -1,187 +1,186 @@ -using System.IO; -using System.Threading; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Testing.Tests.FileSystem; - -public sealed class FileSystemWatcherMockTests : IDisposable -{ - /// - /// Default number of messages before the buffer overflows is 64:
- /// internal buffer size / bytes per message = 8192 / 128 = 64 - ///
- public static readonly int DefaultMaxMessages = 64; - - public string BasePath => _directoryCleaner.BasePath; - public MockFileSystem FileSystem { get; } - private readonly IDirectoryCleaner _directoryCleaner; - - public FileSystemWatcherMockTests() - { - FileSystem = new MockFileSystem(); - _directoryCleaner = FileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); - FileSystem.Initialize(); - } - - #region IDisposable Members - - /// - public void Dispose() - => _directoryCleaner.Dispose(); - - #endregion - - [SkippableTheory] - [AutoData] - public void Error_DefaultTo64Messages_ShouldBeTriggeredWhenBufferOverflows( - string path) - { - FileSystem.Directory.CreateDirectory(path); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - ManualResetEventSlim block1 = new(); - ManualResetEventSlim block2 = new(); - ErrorEventArgs? result = null; - fileSystemWatcher.Error += (_, eventArgs) => - { - result = eventArgs; - block1.Set(); - block2.Set(); - }; - fileSystemWatcher.Deleted += (_, _) => - { - block1.Wait(10000); - }; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.Delete(path); - for (int i = 0; i <= DefaultMaxMessages; i++) - { - if (block1.IsSet) - { - break; - } - - FileSystem.Directory.CreateDirectory($"{i}_{path}"); - } - - block2.Wait(10000).Should().BeTrue(); - fileSystemWatcher.Dispose(); - result.Should().NotBeNull(); - result!.GetException().Should().BeOfType(); - } - - [SkippableTheory] - [InlineAutoData(4096)] - [InlineAutoData(8192)] - public void Error_ShouldBeTriggeredWhenBufferOverflows( - int internalBufferSize, string path) - { - int maxMessages = internalBufferSize / 128; - FileSystem.Directory.CreateDirectory(path); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - ManualResetEventSlim block1 = new(); - ManualResetEventSlim block2 = new(); - ErrorEventArgs? result = null; - fileSystemWatcher.Error += (_, eventArgs) => - { - result = eventArgs; - block1.Set(); - block2.Set(); - }; - fileSystemWatcher.Deleted += (_, _) => - { - block1.Wait(5000); - }; - fileSystemWatcher.EnableRaisingEvents = true; - fileSystemWatcher.InternalBufferSize = internalBufferSize; - FileSystem.Directory.Delete(path); - for (int i = 0; i <= maxMessages; i++) - { - if (block1.IsSet) - { - break; - } - - FileSystem.Directory.CreateDirectory($"{i}_{path}"); - } - - block2.Wait(5000).Should().BeTrue(); - fileSystemWatcher.Dispose(); - result.Should().NotBeNull(); - result!.GetException().Should().BeOfType(); - } - -#if FEATURE_FILESYSTEMWATCHER_ADVANCED - [SkippableTheory] - [AutoData] - public void Filter_ShouldResetFiltersToOnlyContainASingleValue( - string[] filters, string expectedFilter) - { - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - foreach (string filter in filters) - { - fileSystemWatcher.Filters.Add(filter); - } - - fileSystemWatcher.Filters.Count.Should().Be(filters.Length); - - fileSystemWatcher.Filter = expectedFilter; - - fileSystemWatcher.Filters.Count.Should().Be(1); - fileSystemWatcher.Filters.Should().ContainSingle(expectedFilter); - fileSystemWatcher.Filter.Should().Be(expectedFilter); - } -#endif - - [SkippableTheory] - [AutoData] - public void InternalBufferSize_ShouldResetQueue(string path1, string path2) - { - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - ManualResetEventSlim block1 = new(); - ManualResetEventSlim block2 = new(); - ErrorEventArgs? result = null; - fileSystemWatcher.Error += (_, eventArgs) => - { - result = eventArgs; - block1.Set(); - block2.Set(); - }; - fileSystemWatcher.Created += (_, _) => - { - block1.Wait(100); - }; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.CreateDirectory(path1); - for (int i = 0; i < DefaultMaxMessages; i++) - { - if (block1.IsSet) - { - break; - } - - FileSystem.Directory.CreateDirectory($"{i}_{path1}"); - } - - fileSystemWatcher.InternalBufferSize = 4196; - - FileSystem.Directory.CreateDirectory(path2); - for (int i = 0; i < 4196 / 128; i++) - { - if (block1.IsSet) - { - break; - } - - FileSystem.Directory.CreateDirectory($"{i}_{path2}"); - } - - block2.Wait(100).Should().BeFalse(); - fileSystemWatcher.Dispose(); - result.Should().BeNull(); - } -} +using System.IO; +using System.Threading; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Testing.Tests.FileSystem; + +public sealed class FileSystemWatcherMockTests : IDisposable +{ + /// + /// Default number of messages before the buffer overflows is 64:
+ /// internal buffer size / bytes per message = 8192 / 128 = 64 + ///
+ public static readonly int DefaultMaxMessages = 64; + + public string BasePath => _directoryCleaner.BasePath; + public MockFileSystem FileSystem { get; } + private readonly IDirectoryCleaner _directoryCleaner; + + public FileSystemWatcherMockTests() + { + FileSystem = new MockFileSystem(); + _directoryCleaner = FileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); + FileSystem.Initialize(); + } + + #region IDisposable Members + + /// + public void Dispose() + => _directoryCleaner.Dispose(); + + #endregion + + [SkippableTheory] + [AutoData] + public void Error_DefaultTo64Messages_ShouldBeTriggeredWhenBufferOverflows( + string path) + { + FileSystem.Directory.CreateDirectory(path); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + ManualResetEventSlim block1 = new(); + ManualResetEventSlim block2 = new(); + ErrorEventArgs? result = null; + fileSystemWatcher.Error += (_, eventArgs) => + { + result = eventArgs; + block1.Set(); + block2.Set(); + }; + fileSystemWatcher.Deleted += (_, _) => + { + block1.Wait(10000); + }; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.Delete(path); + for (int i = 0; i <= DefaultMaxMessages; i++) + { + if (block1.IsSet) + { + break; + } + + FileSystem.Directory.CreateDirectory($"{i}_{path}"); + } + + block2.Wait(10000).Should().BeTrue(); + fileSystemWatcher.Dispose(); + result.Should().NotBeNull(); + result!.GetException().Should().BeOfType(); + } + + [SkippableTheory] + [InlineAutoData(4096)] + [InlineAutoData(8192)] + public void Error_ShouldBeTriggeredWhenBufferOverflows( + int internalBufferSize, string path) + { + int maxMessages = internalBufferSize / 128; + FileSystem.Directory.CreateDirectory(path); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + ManualResetEventSlim block1 = new(); + ManualResetEventSlim block2 = new(); + ErrorEventArgs? result = null; + fileSystemWatcher.Error += (_, eventArgs) => + { + result = eventArgs; + block1.Set(); + block2.Set(); + }; + fileSystemWatcher.Deleted += (_, _) => + { + block1.Wait(5000); + }; + fileSystemWatcher.EnableRaisingEvents = true; + fileSystemWatcher.InternalBufferSize = internalBufferSize; + FileSystem.Directory.Delete(path); + for (int i = 0; i <= maxMessages; i++) + { + if (block1.IsSet) + { + break; + } + + FileSystem.Directory.CreateDirectory($"{i}_{path}"); + } + + block2.Wait(5000).Should().BeTrue(); + fileSystemWatcher.Dispose(); + result.Should().NotBeNull(); + result!.GetException().Should().BeOfType(); + } + +#if FEATURE_FILESYSTEMWATCHER_ADVANCED + [SkippableTheory] + [AutoData] + public void Filter_ShouldResetFiltersToOnlyContainASingleValue( + string[] filters, string expectedFilter) + { + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + foreach (string filter in filters) + { + fileSystemWatcher.Filters.Add(filter); + } + + fileSystemWatcher.Filters.Count.Should().Be(filters.Length); + + fileSystemWatcher.Filter = expectedFilter; + + fileSystemWatcher.Filters.Count.Should().Be(1); + fileSystemWatcher.Filters.Should().ContainSingle(expectedFilter); + fileSystemWatcher.Filter.Should().Be(expectedFilter); + } +#endif + + [SkippableTheory] + [AutoData] + public void InternalBufferSize_ShouldResetQueue(string path1, string path2) + { + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + ManualResetEventSlim block1 = new(); + ManualResetEventSlim block2 = new(); + ErrorEventArgs? result = null; + fileSystemWatcher.Error += (_, eventArgs) => + { + result = eventArgs; + block1.Set(); + block2.Set(); + }; + fileSystemWatcher.Created += (_, _) => + { + block1.Wait(100); + }; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.CreateDirectory(path1); + for (int i = 0; i < DefaultMaxMessages; i++) + { + if (block1.IsSet) + { + break; + } + + FileSystem.Directory.CreateDirectory($"{i}_{path1}"); + } + + fileSystemWatcher.InternalBufferSize = 4196; + + FileSystem.Directory.CreateDirectory(path2); + for (int i = 0; i < 4196 / 128; i++) + { + if (block1.IsSet) + { + break; + } + + FileSystem.Directory.CreateDirectory($"{i}_{path2}"); + } + + block2.Wait(100).Should().BeFalse(); + fileSystemWatcher.Dispose(); + result.Should().BeNull(); + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Helpers/PathHelperTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Helpers/PathHelperTests.cs index f1662f0d3..2bf3cf326 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/Helpers/PathHelperTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/Helpers/PathHelperTests.cs @@ -1,101 +1,100 @@ -using Moq; -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Tests.TestHelpers; - -namespace Testably.Abstractions.Testing.Tests.Helpers; - -public class PathHelperTests -{ - [Theory] - [AutoData] - public void IsUncPath_AltDirectorySeparatorChar_ShouldReturnTrue(string path) - { - string prefix = new(Path.AltDirectorySeparatorChar, 2); - path = prefix + path; - - bool result = path.IsUncPath(); - - result.Should().BeTrue(); - } - - [Theory] - [AutoData] - public void IsUncPath_DirectorySeparatorChar_ShouldReturnTrue(string path) - { - string prefix = new(Path.DirectorySeparatorChar, 2); - path = prefix + path; - - bool result = path.IsUncPath(); - - result.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void IsUncPath_MixedDirectorySeparatorChars_ShouldReturnFalse(string path) - { - Skip.IfNot(Test.RunsOnWindows, - "Mac and Linux don't have distinctive directory separator chars."); - - path = $"{Path.AltDirectorySeparatorChar}{Path.DirectorySeparatorChar}{path}"; - - bool result = path.IsUncPath(); - - result.Should().BeFalse(); - } - - [Fact] - public void IsUncPath_Null_ShouldReturnFalse() - { - string? path = null; - - bool result = path!.IsUncPath(); - - result.Should().BeFalse(); - } - - [Fact] - public void - ThrowCommonExceptionsIfPathIsInvalid_StartWithNull_ShouldThrowArgumentException() - { - string path = "\0foo"; - - Exception? exception = Record.Exception(() => - { - path.EnsureValidFormat(new MockFileSystem()); - }); - - exception.Should().BeOfType() - .Which.Message.Should().Contain($"'{path}'"); - } - - [Theory] - [AutoData] - public void ThrowCommonExceptionsIfPathIsInvalid_WithInvalidCharacters( - char[] invalidChars) - { - Mock mockFileSystem = new(); - Mock pathSystemMock = new(); - mockFileSystem.Setup(m => m.Path).Returns(pathSystemMock.Object); - pathSystemMock.Setup(m => m.GetInvalidPathChars()).Returns(invalidChars); - pathSystemMock - .Setup(m => m.GetFullPath(It.IsAny())) - .Returns(s => s); - string path = invalidChars[0] + "foo"; - - Exception? exception = Record.Exception(() => - { - path.EnsureValidFormat(mockFileSystem.Object); - }); - -#if NETFRAMEWORK - exception.Should().BeOfType() - .Which.Message.Should().Contain($"'{path}'"); -#else - exception.Should().BeOfType() - .Which.Message.Should().Contain($"'{path}'"); -#endif - } -} +using Moq; +using System.IO; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Helpers; + +public class PathHelperTests +{ + [Theory] + [AutoData] + public void IsUncPath_AltDirectorySeparatorChar_ShouldReturnTrue(string path) + { + string prefix = new(Path.AltDirectorySeparatorChar, 2); + path = prefix + path; + + bool result = path.IsUncPath(); + + result.Should().BeTrue(); + } + + [Theory] + [AutoData] + public void IsUncPath_DirectorySeparatorChar_ShouldReturnTrue(string path) + { + string prefix = new(Path.DirectorySeparatorChar, 2); + path = prefix + path; + + bool result = path.IsUncPath(); + + result.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void IsUncPath_MixedDirectorySeparatorChars_ShouldReturnFalse(string path) + { + Skip.IfNot(Test.RunsOnWindows, + "Mac and Linux don't have distinctive directory separator chars."); + + path = $"{Path.AltDirectorySeparatorChar}{Path.DirectorySeparatorChar}{path}"; + + bool result = path.IsUncPath(); + + result.Should().BeFalse(); + } + + [Fact] + public void IsUncPath_Null_ShouldReturnFalse() + { + string? path = null; + + bool result = path!.IsUncPath(); + + result.Should().BeFalse(); + } + + [Fact] + public void + ThrowCommonExceptionsIfPathIsInvalid_StartWithNull_ShouldThrowArgumentException() + { + string path = "\0foo"; + + Exception? exception = Record.Exception(() => + { + path.EnsureValidFormat(new MockFileSystem()); + }); + + exception.Should().BeOfType() + .Which.Message.Should().Contain($"'{path}'"); + } + + [Theory] + [AutoData] + public void ThrowCommonExceptionsIfPathIsInvalid_WithInvalidCharacters( + char[] invalidChars) + { + Mock mockFileSystem = new(); + Mock pathSystemMock = new(); + mockFileSystem.Setup(m => m.Path).Returns(pathSystemMock.Object); + pathSystemMock.Setup(m => m.GetInvalidPathChars()).Returns(invalidChars); + pathSystemMock + .Setup(m => m.GetFullPath(It.IsAny())) + .Returns(s => s); + string path = invalidChars[0] + "foo"; + + Exception? exception = Record.Exception(() => + { + path.EnsureValidFormat(mockFileSystem.Object); + }); + +#if NETFRAMEWORK + exception.Should().BeOfType() + .Which.Message.Should().Contain($"'{path}'"); +#else + exception.Should().BeOfType() + .Which.Message.Should().Contain($"'{path}'"); +#endif + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/MockFileSystemTests.cs b/Tests/Testably.Abstractions.Testing.Tests/MockFileSystemTests.cs index 42afcdc38..8e2756a20 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/MockFileSystemTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/MockFileSystemTests.cs @@ -1,288 +1,287 @@ -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Tests.TestHelpers; -#if NET6_0_OR_GREATER -using Microsoft.Win32.SafeHandles; -#endif - -namespace Testably.Abstractions.Testing.Tests; - -public class MockFileSystemTests -{ - [SkippableTheory] - [AutoData] - public void FileSystemMock_File_Decrypt(string path, string contents) - { - MockFileSystem sut = new(); - sut.File.WriteAllText(path, contents); - #pragma warning disable CA1416 - sut.File.Encrypt(path); - #pragma warning restore CA1416 - - #pragma warning disable CA1416 - sut.File.Decrypt(path); - #pragma warning restore CA1416 - - sut.File.ReadAllText(path).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void FileSystemMock_File_Encrypt(string path, string contents) - { - MockFileSystem sut = new(); - sut.File.WriteAllText(path, contents); - - #pragma warning disable CA1416 - sut.File.Encrypt(path); - #pragma warning restore CA1416 - - sut.File.ReadAllText(path).Should().NotBe(contents); - } - - [SkippableTheory] - [AutoData] - public void FileSystemMock_FileInfo_Decrypt(string path, string contents) - { - MockFileSystem sut = new(); - sut.File.WriteAllText(path, contents); - #pragma warning disable CA1416 - sut.FileInfo.New(path).Encrypt(); - #pragma warning restore CA1416 - - #pragma warning disable CA1416 - sut.FileInfo.New(path).Decrypt(); - #pragma warning restore CA1416 - - sut.File.ReadAllText(path).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void FileSystemMock_FileInfo_Encrypt(string path, string contents) - { - MockFileSystem sut = new(); - sut.File.WriteAllText(path, contents); - - #pragma warning disable CA1416 - sut.FileInfo.New(path).Encrypt(); - #pragma warning restore CA1416 - - sut.File.ReadAllText(path).Should().NotBe(contents); - } - - [SkippableFact] - public void FileSystemMock_ShouldBeInitializedWithASingleDefaultDrive() - { - string expectedDriveName = "".PrefixRoot(); - MockFileSystem sut = new(); - - IDriveInfo[] drives = sut.DriveInfo.GetDrives(); - IDriveInfo drive = drives.Single(); - - drive.Name.Should().Be(expectedDriveName); - drive.AvailableFreeSpace.Should().BeGreaterThan(0); - drive.DriveFormat.Should() - .Be(DriveInfoMock.DefaultDriveFormat); - drive.DriveType.Should() - .Be(DriveInfoMock.DefaultDriveType); - drive.VolumeLabel.Should().NotBeNullOrEmpty(); - } - - [SkippableTheory] - [AutoData] - public void WithAccessControl_Denied_CreateDirectoryShouldThrowIOException( - string path) - { - MockFileSystem sut = new(); - sut.Initialize(); - sut.WithAccessControlStrategy(new DefaultAccessControlStrategy((_, _) => false)); - - Exception? exception = Record.Exception(() => - { - sut.Directory.CreateDirectory(path); - }); - - exception.Should().BeOfType(); - } - - [SkippableTheory] - [AutoData] - public void WithAccessControl_ShouldConsiderPath( - string allowedPath, string deniedPath) - { - MockFileSystem sut = new(); - sut.Initialize(); - sut.WithAccessControlStrategy(new DefaultAccessControlStrategy((p, _) - => p == sut.Path.GetFullPath(allowedPath))); - - sut.Directory.CreateDirectory(allowedPath); - Exception? exception = Record.Exception(() => - { - sut.Directory.CreateDirectory(deniedPath); - }); - - exception.Should().BeOfType(); - } - - [SkippableTheory] - [InlineData("D:\\")] - public void WithDrive_Duplicate_ShouldUpdateExistingDrive(string driveName) - { - Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); - - MockFileSystem sut = new(); - sut.WithDrive(driveName, d => d.SetTotalSize(100)); - sut.DriveInfo.GetDrives().Length.Should().Be(2); - IDriveInfo drive = sut.DriveInfo.GetDrives().Single(x => x.Name == driveName); - drive.TotalSize.Should().Be(100); - - sut.WithDrive(driveName, d => d.SetTotalSize(200)); - sut.DriveInfo.GetDrives().Length.Should().Be(2); - drive.TotalSize.Should().Be(200); - } - - [SkippableFact] - public void WithDrive_ExistingName_ShouldUpdateDrive() - { - string driveName = "".PrefixRoot(); - MockFileSystem sut = new(); - sut.WithDrive(driveName); - - IDriveInfo[] drives = sut.DriveInfo.GetDrives(); - - drives.Length.Should().Be(1); - drives.Should().ContainSingle(d => d.Name == driveName); - } - - [SkippableTheory] - [InlineData("D:\\")] - public void WithDrive_NewName_ShouldCreateNewDrives(string driveName) - { - Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); - - MockFileSystem sut = new(); - sut.WithDrive(driveName); - - IDriveInfo[] drives = sut.DriveInfo.GetDrives(); - - drives.Length.Should().Be(2); - drives.Should().ContainSingle(d => d.Name == driveName); - } - - [SkippableTheory] - [AutoData] - public void WithDrive_WithCallback_ShouldUpdateDrive(long totalSize) - { - MockFileSystem sut = new(); - sut.WithDrive(d => d.SetTotalSize(totalSize)); - - IDriveInfo drive = sut.DriveInfo.GetDrives().Single(); - - drive.TotalSize.Should().Be(totalSize); - drive.TotalFreeSpace.Should().Be(totalSize); - drive.AvailableFreeSpace.Should().Be(totalSize); - } - - [SkippableTheory] - [AutoData] - public void WithUncDrive_ShouldCreateUncDrive( - string path, string contents) - { - MockFileSystem sut = new(); - sut.WithUncDrive("UNC-Path"); - string fullPath = sut.Path.Combine("//UNC-Path", path); - sut.File.WriteAllText(fullPath, contents); - - string result = sut.File.ReadAllText(fullPath); - result.Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void WithUncDrive_ShouldNotBeIncludedInGetDrives( - string server) - { - MockFileSystem sut = new(); - string uncPrefix = new(sut.Path.DirectorySeparatorChar, 2); - string uncDrive = $"{uncPrefix}{server}"; - - sut.WithUncDrive(uncDrive); - - sut.Directory.GetLogicalDrives().Length.Should().Be(1); - sut.DriveInfo.GetDrives().Length.Should().Be(1); - } - - [SkippableTheory] - [AutoData] - public void WithUncDrive_WriteBytes_ShouldReduceAvailableFreeSpace( - string server, string path, byte[] bytes) - { - MockFileSystem sut = new(); - string uncPrefix = new(sut.Path.DirectorySeparatorChar, 2); - string uncDrive = $"{uncPrefix}{server}"; - sut.WithUncDrive(uncDrive); - IDriveInfo drive = sut.DriveInfo.New(uncDrive); - long previousFreeSpace = drive.AvailableFreeSpace; - - sut.File.WriteAllBytes(Path.Combine(uncDrive, path), bytes); - - drive.AvailableFreeSpace.Should().Be(previousFreeSpace - bytes.Length); - } - - [SkippableTheory] - [AutoData] - public void WriteAllText_OnUncPath_ShouldThrowDirectoryNotFoundException( - string path, string contents) - { - MockFileSystem sut = new(); - string fullPath = sut.Path.Combine("//UNC-Path", path); - Exception? exception = Record.Exception(() => - { - sut.File.WriteAllText(fullPath, contents); - }); - - exception.Should().BeOfType(); - } - -#if NET6_0_OR_GREATER - [SkippableTheory] - [AutoData] - public void - WithSafeFileHandleStrategy_DefaultStrategy_ShouldUseMappedSafeFileHandleMock( - string path, string contents) - { - MockFileSystem sut = new(); - sut.File.WriteAllText(path, contents); - sut.WithSafeFileHandleStrategy( - new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); - - using FileSystemStream stream = - sut.FileStream.New(new SafeFileHandle(), FileAccess.Read); - using StreamReader streamReader = new(stream); - string result = streamReader.ReadToEnd(); - - result.Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void WithSafeFileHandleStrategy_NullStrategy_ShouldThrowException( - string path, string contents) - { - MockFileSystem sut = new(); - sut.File.WriteAllText(path, contents); - - Exception? exception = Record.Exception(() => - { - sut.FileStream.New(new SafeFileHandle(), FileAccess.Read); - }); - - exception.Should().BeOfType() - .Which.ParamName.Should().Be("handle"); - } -#endif -} +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystem; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Tests.TestHelpers; +#if NET6_0_OR_GREATER +using Microsoft.Win32.SafeHandles; +#endif + +namespace Testably.Abstractions.Testing.Tests; + +public class MockFileSystemTests +{ + [SkippableTheory] + [AutoData] + public void FileSystemMock_File_Decrypt(string path, string contents) + { + MockFileSystem sut = new(); + sut.File.WriteAllText(path, contents); + #pragma warning disable CA1416 + sut.File.Encrypt(path); + #pragma warning restore CA1416 + + #pragma warning disable CA1416 + sut.File.Decrypt(path); + #pragma warning restore CA1416 + + sut.File.ReadAllText(path).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void FileSystemMock_File_Encrypt(string path, string contents) + { + MockFileSystem sut = new(); + sut.File.WriteAllText(path, contents); + + #pragma warning disable CA1416 + sut.File.Encrypt(path); + #pragma warning restore CA1416 + + sut.File.ReadAllText(path).Should().NotBe(contents); + } + + [SkippableTheory] + [AutoData] + public void FileSystemMock_FileInfo_Decrypt(string path, string contents) + { + MockFileSystem sut = new(); + sut.File.WriteAllText(path, contents); + #pragma warning disable CA1416 + sut.FileInfo.New(path).Encrypt(); + #pragma warning restore CA1416 + + #pragma warning disable CA1416 + sut.FileInfo.New(path).Decrypt(); + #pragma warning restore CA1416 + + sut.File.ReadAllText(path).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void FileSystemMock_FileInfo_Encrypt(string path, string contents) + { + MockFileSystem sut = new(); + sut.File.WriteAllText(path, contents); + + #pragma warning disable CA1416 + sut.FileInfo.New(path).Encrypt(); + #pragma warning restore CA1416 + + sut.File.ReadAllText(path).Should().NotBe(contents); + } + + [SkippableFact] + public void FileSystemMock_ShouldBeInitializedWithASingleDefaultDrive() + { + string expectedDriveName = "".PrefixRoot(); + MockFileSystem sut = new(); + + IDriveInfo[] drives = sut.DriveInfo.GetDrives(); + IDriveInfo drive = drives.Single(); + + drive.Name.Should().Be(expectedDriveName); + drive.AvailableFreeSpace.Should().BeGreaterThan(0); + drive.DriveFormat.Should() + .Be(DriveInfoMock.DefaultDriveFormat); + drive.DriveType.Should() + .Be(DriveInfoMock.DefaultDriveType); + drive.VolumeLabel.Should().NotBeNullOrEmpty(); + } + + [SkippableTheory] + [AutoData] + public void WithAccessControl_Denied_CreateDirectoryShouldThrowIOException( + string path) + { + MockFileSystem sut = new(); + sut.Initialize(); + sut.WithAccessControlStrategy(new DefaultAccessControlStrategy((_, _) => false)); + + Exception? exception = Record.Exception(() => + { + sut.Directory.CreateDirectory(path); + }); + + exception.Should().BeOfType(); + } + + [SkippableTheory] + [AutoData] + public void WithAccessControl_ShouldConsiderPath( + string allowedPath, string deniedPath) + { + MockFileSystem sut = new(); + sut.Initialize(); + sut.WithAccessControlStrategy(new DefaultAccessControlStrategy((p, _) + => p == sut.Path.GetFullPath(allowedPath))); + + sut.Directory.CreateDirectory(allowedPath); + Exception? exception = Record.Exception(() => + { + sut.Directory.CreateDirectory(deniedPath); + }); + + exception.Should().BeOfType(); + } + + [SkippableTheory] + [InlineData("D:\\")] + public void WithDrive_Duplicate_ShouldUpdateExistingDrive(string driveName) + { + Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); + + MockFileSystem sut = new(); + sut.WithDrive(driveName, d => d.SetTotalSize(100)); + sut.DriveInfo.GetDrives().Length.Should().Be(2); + IDriveInfo drive = sut.DriveInfo.GetDrives().Single(x => x.Name == driveName); + drive.TotalSize.Should().Be(100); + + sut.WithDrive(driveName, d => d.SetTotalSize(200)); + sut.DriveInfo.GetDrives().Length.Should().Be(2); + drive.TotalSize.Should().Be(200); + } + + [SkippableFact] + public void WithDrive_ExistingName_ShouldUpdateDrive() + { + string driveName = "".PrefixRoot(); + MockFileSystem sut = new(); + sut.WithDrive(driveName); + + IDriveInfo[] drives = sut.DriveInfo.GetDrives(); + + drives.Length.Should().Be(1); + drives.Should().ContainSingle(d => d.Name == driveName); + } + + [SkippableTheory] + [InlineData("D:\\")] + public void WithDrive_NewName_ShouldCreateNewDrives(string driveName) + { + Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); + + MockFileSystem sut = new(); + sut.WithDrive(driveName); + + IDriveInfo[] drives = sut.DriveInfo.GetDrives(); + + drives.Length.Should().Be(2); + drives.Should().ContainSingle(d => d.Name == driveName); + } + + [SkippableTheory] + [AutoData] + public void WithDrive_WithCallback_ShouldUpdateDrive(long totalSize) + { + MockFileSystem sut = new(); + sut.WithDrive(d => d.SetTotalSize(totalSize)); + + IDriveInfo drive = sut.DriveInfo.GetDrives().Single(); + + drive.TotalSize.Should().Be(totalSize); + drive.TotalFreeSpace.Should().Be(totalSize); + drive.AvailableFreeSpace.Should().Be(totalSize); + } + + [SkippableTheory] + [AutoData] + public void WithUncDrive_ShouldCreateUncDrive( + string path, string contents) + { + MockFileSystem sut = new(); + sut.WithUncDrive("UNC-Path"); + string fullPath = sut.Path.Combine("//UNC-Path", path); + sut.File.WriteAllText(fullPath, contents); + + string result = sut.File.ReadAllText(fullPath); + result.Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void WithUncDrive_ShouldNotBeIncludedInGetDrives( + string server) + { + MockFileSystem sut = new(); + string uncPrefix = new(sut.Path.DirectorySeparatorChar, 2); + string uncDrive = $"{uncPrefix}{server}"; + + sut.WithUncDrive(uncDrive); + + sut.Directory.GetLogicalDrives().Length.Should().Be(1); + sut.DriveInfo.GetDrives().Length.Should().Be(1); + } + + [SkippableTheory] + [AutoData] + public void WithUncDrive_WriteBytes_ShouldReduceAvailableFreeSpace( + string server, string path, byte[] bytes) + { + MockFileSystem sut = new(); + string uncPrefix = new(sut.Path.DirectorySeparatorChar, 2); + string uncDrive = $"{uncPrefix}{server}"; + sut.WithUncDrive(uncDrive); + IDriveInfo drive = sut.DriveInfo.New(uncDrive); + long previousFreeSpace = drive.AvailableFreeSpace; + + sut.File.WriteAllBytes(Path.Combine(uncDrive, path), bytes); + + drive.AvailableFreeSpace.Should().Be(previousFreeSpace - bytes.Length); + } + + [SkippableTheory] + [AutoData] + public void WriteAllText_OnUncPath_ShouldThrowDirectoryNotFoundException( + string path, string contents) + { + MockFileSystem sut = new(); + string fullPath = sut.Path.Combine("//UNC-Path", path); + Exception? exception = Record.Exception(() => + { + sut.File.WriteAllText(fullPath, contents); + }); + + exception.Should().BeOfType(); + } + +#if NET6_0_OR_GREATER + [SkippableTheory] + [AutoData] + public void + WithSafeFileHandleStrategy_DefaultStrategy_ShouldUseMappedSafeFileHandleMock( + string path, string contents) + { + MockFileSystem sut = new(); + sut.File.WriteAllText(path, contents); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); + + using FileSystemStream stream = + sut.FileStream.New(new SafeFileHandle(), FileAccess.Read); + using StreamReader streamReader = new(stream); + string result = streamReader.ReadToEnd(); + + result.Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void WithSafeFileHandleStrategy_NullStrategy_ShouldThrowException( + string path, string contents) + { + MockFileSystem sut = new(); + sut.File.WriteAllText(path, contents); + + Exception? exception = Record.Exception(() => + { + sut.FileStream.New(new SafeFileHandle(), FileAccess.Read); + }); + + exception.Should().BeOfType() + .Which.ParamName.Should().Be("handle"); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Storage/InMemoryStorageTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Storage/InMemoryStorageTests.cs index e8d2a48cd..ff67c680d 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/Storage/InMemoryStorageTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/Storage/InMemoryStorageTests.cs @@ -1,205 +1,204 @@ -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.RandomSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; -using Testably.Abstractions.Testing.Tests.TestHelpers; - -namespace Testably.Abstractions.Testing.Tests.Storage; - -public class InMemoryStorageTests -{ - #region Test Setup - - internal MockFileSystem FileSystem { get; } - internal IStorage Storage { get; } - - public InMemoryStorageTests() - { - FileSystem = new MockFileSystem(); - Storage = new InMemoryStorage(FileSystem); - } - - #endregion - - [Theory] - [AutoData] - public void Copy_Overwrite_ShouldAdjustAvailableFreeSpace( - int file1Size, int file2Size) - { - MockFileSystem fileSystem = new(); - IDriveInfo mainDrive = fileSystem.DriveInfo.New("".PrefixRoot()); - IRandom random = RandomFactory.Shared; - byte[] file1Content = new byte[file1Size]; - byte[] file2Content = new byte[file2Size]; - random.NextBytes(file1Content); - random.NextBytes(file2Content); - - fileSystem.File.WriteAllBytes("foo", file1Content); - fileSystem.File.WriteAllBytes("bar", file2Content); - long availableFreeSpaceBefore = mainDrive.AvailableFreeSpace; - - fileSystem.File.Copy("foo", "bar", true); - - long availableFreeSpaceAfter = mainDrive.AvailableFreeSpace; - availableFreeSpaceAfter.Should() - .Be(availableFreeSpaceBefore + file2Size - file1Size); - } - - [Theory] - [AutoData] - public void Replace_WithoutBackup_ShouldNotChangeAvailableFreeSpace( - int file1Size, int file2Size) - { - MockFileSystem fileSystem = new(); - IDriveInfo mainDrive = fileSystem.DriveInfo.New("".PrefixRoot()); - IRandom random = RandomFactory.Shared; - byte[] file1Content = new byte[file1Size]; - byte[] file2Content = new byte[file2Size]; - random.NextBytes(file1Content); - random.NextBytes(file2Content); - - fileSystem.File.WriteAllBytes("foo", file1Content); - fileSystem.File.WriteAllBytes("bar", file2Content); - long availableFreeSpaceBefore = mainDrive.AvailableFreeSpace; - - fileSystem.File.Replace("foo", "bar", "backup"); - - long availableFreeSpaceAfter = mainDrive.AvailableFreeSpace; - availableFreeSpaceAfter.Should() - .Be(availableFreeSpaceBefore); - } - - [Theory] - [AutoData] - public void Replace_WithBackup_ShouldChangeAvailableFreeSpace( - int file1Size, int file2Size, int file3Size) - { - MockFileSystem fileSystem = new(); - IDriveInfo mainDrive = fileSystem.DriveInfo.New("".PrefixRoot()); - IRandom random = RandomFactory.Shared; - byte[] file1Content = new byte[file1Size]; - byte[] file2Content = new byte[file2Size]; - byte[] file3Content = new byte[file3Size]; - random.NextBytes(file1Content); - random.NextBytes(file2Content); - random.NextBytes(file3Content); - - fileSystem.File.WriteAllBytes("foo", file1Content); - fileSystem.File.WriteAllBytes("bar", file2Content); - fileSystem.File.WriteAllBytes("backup", file3Content); - long availableFreeSpaceBefore = mainDrive.AvailableFreeSpace; - - fileSystem.File.Replace("foo", "bar", "backup", true); - - long availableFreeSpaceAfter = mainDrive.AvailableFreeSpace; - availableFreeSpaceAfter.Should() - .Be(availableFreeSpaceBefore + file2Size); - } - - [Fact] - public void CurrentDirectory_ShouldBeInitializedToDefaultRoot() - { - string expectedRoot = string.Empty.PrefixRoot(); - Storage.CurrentDirectory.Should().Be(expectedRoot); - } - - [Fact] - public void Delete_RaceCondition_ShouldReturnFalse() - { - MockFileSystem fileSystem = new(); - fileSystem.Directory.CreateDirectory("foo"); - bool isFirstDeletion = true; - fileSystem.Intercept.Deleting(FileSystemTypes.Directory, _ => - { - if (isFirstDeletion) - { - isFirstDeletion = false; - fileSystem.Directory.Delete("foo"); - } - }); - - Exception? exception = Record.Exception(() => - { - fileSystem.Directory.Delete("foo"); - }); - - exception.Should().BeOfType(); - } - - [Theory] - [AutoData] - public void Move_RequestDeniedForChild_ShouldRollback( - string locationPath, string destinationPath) - { - IStorageLocation location = Storage.GetLocation(locationPath); - IStorageLocation destination = Storage.GetLocation(destinationPath); - IStorageLocation child1Location = - Storage.GetLocation(Path.Combine(locationPath, "foo")); - IStorageLocation child2Location = - Storage.GetLocation(Path.Combine(locationPath, "bar")); - LockableContainer lockedContainer = new(FileSystem); - Storage.TryAddContainer( - location, - InMemoryContainer.NewDirectory, - out _); - Storage.TryAddContainer( - child1Location, - InMemoryContainer.NewFile, - out _); - Storage.TryAddContainer( - child2Location, - (_, _) => lockedContainer, - out _); - - lockedContainer.IsLocked = true; - - Exception? exception = Record.Exception(() => - { - Storage.Move(location, destination, recursive: true); - }); - - Storage.GetContainer(location).Should().NotBeOfType(); - Storage.GetContainer(child1Location).Should().NotBeOfType(); - Storage.GetContainer(child2Location).Should().NotBeOfType(); - exception.Should().BeOfType(); - } - - [Theory] - [AutoData] - public void TryAddContainer_ShouldNotifyWhenAdded(string path) - { - bool receivedNotification = false; - FileSystem.Notify.OnEvent(_ => receivedNotification = true); - IStorageLocation location = Storage.GetLocation(path); - bool result = Storage.TryAddContainer( - location, - InMemoryContainer.NewDirectory, - out IStorageContainer? container); - - result.Should().BeTrue(); - receivedNotification.Should().BeTrue(); - container!.Type.Should().Be(FileSystemTypes.Directory); - } - - [Theory] - [AutoData] - public void TryAddContainer_ShouldNotNotifyWhenExistsPreviously(string path) - { - IStorageLocation location = Storage.GetLocation(path); - Storage.TryAddContainer( - location, - InMemoryContainer.NewDirectory, - out _); - bool receivedNotification = false; - FileSystem.Notify.OnEvent(_ => receivedNotification = true); - bool result = Storage.TryAddContainer( - location, - InMemoryContainer.NewDirectory, - out IStorageContainer? container); - - result.Should().BeFalse(); - receivedNotification.Should().BeFalse(); - container.Should().BeNull(); - } -} +using System.IO; +using Testably.Abstractions.RandomSystem; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Storage; + +public class InMemoryStorageTests +{ + #region Test Setup + + internal MockFileSystem FileSystem { get; } + internal IStorage Storage { get; } + + public InMemoryStorageTests() + { + FileSystem = new MockFileSystem(); + Storage = new InMemoryStorage(FileSystem); + } + + #endregion + + [Theory] + [AutoData] + public void Copy_Overwrite_ShouldAdjustAvailableFreeSpace( + int file1Size, int file2Size) + { + MockFileSystem fileSystem = new(); + IDriveInfo mainDrive = fileSystem.DriveInfo.New("".PrefixRoot()); + IRandom random = RandomFactory.Shared; + byte[] file1Content = new byte[file1Size]; + byte[] file2Content = new byte[file2Size]; + random.NextBytes(file1Content); + random.NextBytes(file2Content); + + fileSystem.File.WriteAllBytes("foo", file1Content); + fileSystem.File.WriteAllBytes("bar", file2Content); + long availableFreeSpaceBefore = mainDrive.AvailableFreeSpace; + + fileSystem.File.Copy("foo", "bar", true); + + long availableFreeSpaceAfter = mainDrive.AvailableFreeSpace; + availableFreeSpaceAfter.Should() + .Be(availableFreeSpaceBefore + file2Size - file1Size); + } + + [Theory] + [AutoData] + public void Replace_WithoutBackup_ShouldNotChangeAvailableFreeSpace( + int file1Size, int file2Size) + { + MockFileSystem fileSystem = new(); + IDriveInfo mainDrive = fileSystem.DriveInfo.New("".PrefixRoot()); + IRandom random = RandomFactory.Shared; + byte[] file1Content = new byte[file1Size]; + byte[] file2Content = new byte[file2Size]; + random.NextBytes(file1Content); + random.NextBytes(file2Content); + + fileSystem.File.WriteAllBytes("foo", file1Content); + fileSystem.File.WriteAllBytes("bar", file2Content); + long availableFreeSpaceBefore = mainDrive.AvailableFreeSpace; + + fileSystem.File.Replace("foo", "bar", "backup"); + + long availableFreeSpaceAfter = mainDrive.AvailableFreeSpace; + availableFreeSpaceAfter.Should() + .Be(availableFreeSpaceBefore); + } + + [Theory] + [AutoData] + public void Replace_WithBackup_ShouldChangeAvailableFreeSpace( + int file1Size, int file2Size, int file3Size) + { + MockFileSystem fileSystem = new(); + IDriveInfo mainDrive = fileSystem.DriveInfo.New("".PrefixRoot()); + IRandom random = RandomFactory.Shared; + byte[] file1Content = new byte[file1Size]; + byte[] file2Content = new byte[file2Size]; + byte[] file3Content = new byte[file3Size]; + random.NextBytes(file1Content); + random.NextBytes(file2Content); + random.NextBytes(file3Content); + + fileSystem.File.WriteAllBytes("foo", file1Content); + fileSystem.File.WriteAllBytes("bar", file2Content); + fileSystem.File.WriteAllBytes("backup", file3Content); + long availableFreeSpaceBefore = mainDrive.AvailableFreeSpace; + + fileSystem.File.Replace("foo", "bar", "backup", true); + + long availableFreeSpaceAfter = mainDrive.AvailableFreeSpace; + availableFreeSpaceAfter.Should() + .Be(availableFreeSpaceBefore + file2Size); + } + + [Fact] + public void CurrentDirectory_ShouldBeInitializedToDefaultRoot() + { + string expectedRoot = string.Empty.PrefixRoot(); + Storage.CurrentDirectory.Should().Be(expectedRoot); + } + + [Fact] + public void Delete_RaceCondition_ShouldReturnFalse() + { + MockFileSystem fileSystem = new(); + fileSystem.Directory.CreateDirectory("foo"); + bool isFirstDeletion = true; + fileSystem.Intercept.Deleting(FileSystemTypes.Directory, _ => + { + if (isFirstDeletion) + { + isFirstDeletion = false; + fileSystem.Directory.Delete("foo"); + } + }); + + Exception? exception = Record.Exception(() => + { + fileSystem.Directory.Delete("foo"); + }); + + exception.Should().BeOfType(); + } + + [Theory] + [AutoData] + public void Move_RequestDeniedForChild_ShouldRollback( + string locationPath, string destinationPath) + { + IStorageLocation location = Storage.GetLocation(locationPath); + IStorageLocation destination = Storage.GetLocation(destinationPath); + IStorageLocation child1Location = + Storage.GetLocation(Path.Combine(locationPath, "foo")); + IStorageLocation child2Location = + Storage.GetLocation(Path.Combine(locationPath, "bar")); + LockableContainer lockedContainer = new(FileSystem); + Storage.TryAddContainer( + location, + InMemoryContainer.NewDirectory, + out _); + Storage.TryAddContainer( + child1Location, + InMemoryContainer.NewFile, + out _); + Storage.TryAddContainer( + child2Location, + (_, _) => lockedContainer, + out _); + + lockedContainer.IsLocked = true; + + Exception? exception = Record.Exception(() => + { + Storage.Move(location, destination, recursive: true); + }); + + Storage.GetContainer(location).Should().NotBeOfType(); + Storage.GetContainer(child1Location).Should().NotBeOfType(); + Storage.GetContainer(child2Location).Should().NotBeOfType(); + exception.Should().BeOfType(); + } + + [Theory] + [AutoData] + public void TryAddContainer_ShouldNotifyWhenAdded(string path) + { + bool receivedNotification = false; + FileSystem.Notify.OnEvent(_ => receivedNotification = true); + IStorageLocation location = Storage.GetLocation(path); + bool result = Storage.TryAddContainer( + location, + InMemoryContainer.NewDirectory, + out IStorageContainer? container); + + result.Should().BeTrue(); + receivedNotification.Should().BeTrue(); + container!.Type.Should().Be(FileSystemTypes.Directory); + } + + [Theory] + [AutoData] + public void TryAddContainer_ShouldNotNotifyWhenExistsPreviously(string path) + { + IStorageLocation location = Storage.GetLocation(path); + Storage.TryAddContainer( + location, + InMemoryContainer.NewDirectory, + out _); + bool receivedNotification = false; + FileSystem.Notify.OnEvent(_ => receivedNotification = true); + bool result = Storage.TryAddContainer( + location, + InMemoryContainer.NewDirectory, + out IStorageContainer? container); + + result.Should().BeFalse(); + receivedNotification.Should().BeFalse(); + container.Should().BeNull(); + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs b/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs index 2127b10de..366edfeaa 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs @@ -1,147 +1,146 @@ -using System.IO; -using System.Linq; -using System.Security.AccessControl; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; -using Testably.Abstractions.TimeSystem; - -namespace Testably.Abstractions.Testing.Tests.TestHelpers; - -/// -/// A for testing purposes. -/// -/// Set to to simulate a locked file -/// ( throws an ). -/// -internal sealed class LockableContainer : IStorageContainer -{ - /// - /// Simulate a locked file, if set to .
- /// In this case throws an - /// , - /// otherwise it will succeed. - ///
- public bool IsLocked { get; set; } - - private byte[] _bytes = Array.Empty(); - - public LockableContainer(MockFileSystem fileSystem, - FileSystemTypes containerType = - FileSystemTypes.DirectoryOrFile) - { - FileSystem = fileSystem; - TimeSystem = fileSystem.TimeSystem; - Type = containerType; - } - - #region IStorageContainer Members - - /// - public FileSystemSecurity? AccessControl { get; set; } - - /// - public FileAttributes Attributes { get; set; } - - /// - public IStorageContainer.ITimeContainer CreationTime { get; } - = new InMemoryContainer.TimeContainer(); - - /// - public IFileSystemExtensibility Extensibility { get; } - = new FileSystemExtensibility(); - - /// - public IFileSystem FileSystem { get; } - - /// - public IStorageContainer.ITimeContainer LastAccessTime { get; } - = new InMemoryContainer.TimeContainer(); - - /// - public IStorageContainer.ITimeContainer LastWriteTime { get; } - = new InMemoryContainer.TimeContainer(); - - /// - public string? LinkTarget { get; set; } - - /// - public ITimeSystem TimeSystem { get; } - - /// - public FileSystemTypes Type { get; } - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - public UnixFileMode UnixFileMode { get; set; } -#endif - - /// - public void AppendBytes(byte[] bytes) - => WriteBytes(_bytes.Concat(bytes).ToArray()); - - /// - public void ClearBytes() - => _bytes = Array.Empty(); - - /// - public void Decrypt() - => throw new NotSupportedException(); - - /// - public void Encrypt() - => throw new NotSupportedException(); - - /// - public byte[] GetBytes() - => _bytes; - - /// - public IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, - bool deleteAccess = false, - bool ignoreMetadataErrors = true, - int? hResult = null) - { - if (IsLocked) - { - throw ExceptionFactory.ProcessCannotAccessTheFile("", hResult ?? -1); - } - - return new AccessHandle(access, share, deleteAccess); - } - - /// - public void WriteBytes(byte[] bytes) - => _bytes = bytes; - - #endregion - - private class AccessHandle : IStorageAccessHandle - { - public AccessHandle(FileAccess access, FileShare share, bool deleteAccess) - { - Access = access; - Share = share; - DeleteAccess = deleteAccess; - } - - #region IStorageAccessHandle Members - - /// - public FileAccess Access { get; } - - /// - public bool DeleteAccess { get; } - - /// - public FileShare Share { get; } - - /// - public void Dispose() - { - // Nothing to do - } - - #endregion - } -} +using System.IO; +using System.Linq; +using System.Security.AccessControl; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; +using Testably.Abstractions.TimeSystem; + +namespace Testably.Abstractions.Testing.Tests.TestHelpers; + +/// +/// A for testing purposes. +/// +/// Set to to simulate a locked file +/// ( throws an ). +/// +internal sealed class LockableContainer : IStorageContainer +{ + /// + /// Simulate a locked file, if set to .
+ /// In this case throws an + /// , + /// otherwise it will succeed. + ///
+ public bool IsLocked { get; set; } + + private byte[] _bytes = Array.Empty(); + + public LockableContainer(MockFileSystem fileSystem, + FileSystemTypes containerType = + FileSystemTypes.DirectoryOrFile) + { + FileSystem = fileSystem; + TimeSystem = fileSystem.TimeSystem; + Type = containerType; + } + + #region IStorageContainer Members + + /// + public FileSystemSecurity? AccessControl { get; set; } + + /// + public FileAttributes Attributes { get; set; } + + /// + public IStorageContainer.ITimeContainer CreationTime { get; } + = new InMemoryContainer.TimeContainer(); + + /// + public IFileSystemExtensibility Extensibility { get; } + = new FileSystemExtensibility(); + + /// + public IFileSystem FileSystem { get; } + + /// + public IStorageContainer.ITimeContainer LastAccessTime { get; } + = new InMemoryContainer.TimeContainer(); + + /// + public IStorageContainer.ITimeContainer LastWriteTime { get; } + = new InMemoryContainer.TimeContainer(); + + /// + public string? LinkTarget { get; set; } + + /// + public ITimeSystem TimeSystem { get; } + + /// + public FileSystemTypes Type { get; } + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + public UnixFileMode UnixFileMode { get; set; } +#endif + + /// + public void AppendBytes(byte[] bytes) + => WriteBytes(_bytes.Concat(bytes).ToArray()); + + /// + public void ClearBytes() + => _bytes = Array.Empty(); + + /// + public void Decrypt() + => throw new NotSupportedException(); + + /// + public void Encrypt() + => throw new NotSupportedException(); + + /// + public byte[] GetBytes() + => _bytes; + + /// + public IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, + bool deleteAccess = false, + bool ignoreMetadataErrors = true, + int? hResult = null) + { + if (IsLocked) + { + throw ExceptionFactory.ProcessCannotAccessTheFile("", hResult ?? -1); + } + + return new AccessHandle(access, share, deleteAccess); + } + + /// + public void WriteBytes(byte[] bytes) + => _bytes = bytes; + + #endregion + + private class AccessHandle : IStorageAccessHandle + { + public AccessHandle(FileAccess access, FileShare share, bool deleteAccess) + { + Access = access; + Share = share; + DeleteAccess = deleteAccess; + } + + #region IStorageAccessHandle Members + + /// + public FileAccess Access { get; } + + /// + public bool DeleteAccess { get; } + + /// + public FileShare Share { get; } + + /// + public void Dispose() + { + // Nothing to do + } + + #endregion + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/Usings.cs b/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/Usings.cs index a6320dd0f..9ef088684 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/Usings.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/Usings.cs @@ -1,4 +1,5 @@ global using AutoFixture.Xunit2; global using FluentAssertions; global using System; +global using Testably.Abstractions.FileSystem; global using Xunit; diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/CreateDirectoryTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/CreateDirectoryTests.cs index 5dbff1ec1..ac7d4df08 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/CreateDirectoryTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/CreateDirectoryTests.cs @@ -1,307 +1,306 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class CreateDirectoryTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void CreateDirectory_AlreadyExisting_ShouldDoNothing(string path) - { - FileSystem.Directory.CreateDirectory(path); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.CreateDirectory(path); - }); - - exception.Should().BeNull(); - FileSystem.Directory.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void CreateDirectory_ReadOnlyParent_ShouldStillCreateDirectory(string parent, - string subdirectory) - { - string subdirectoryPath = FileSystem.Path.Combine(parent, subdirectory); - FileSystem.Directory.CreateDirectory(parent); - FileSystem.DirectoryInfo.New(parent).Attributes = FileAttributes.ReadOnly; - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.CreateDirectory(subdirectoryPath); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeNull(); - FileSystem.Directory.Exists(subdirectoryPath).Should().BeTrue(); - FileSystem.DirectoryInfo.New(parent).Attributes - .Should().HaveFlag(FileAttributes.ReadOnly); - } - else - { - exception.Should().BeException(hResult: -2147024891); - FileSystem.Directory.Exists(subdirectoryPath).Should().BeFalse(); - } - } - - [SkippableFact] - public void CreateDirectory_Root_ShouldNotThrowException() - { - string path = FileTestHelper.RootDrive(); - FileSystem.Directory.CreateDirectory(path); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.CreateDirectory(path); - }); - - exception.Should().BeNull(); - FileSystem.Directory.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void CreateDirectory_ShouldTrimTrailingSpaces_OnWindows(string path) - { - string pathWithSpaces = path + " "; - - IDirectoryInfo result = FileSystem.Directory.CreateDirectory(pathWithSpaces); - - if (Test.RunsOnWindows) - { - result.Name.Should().Be(path); - } - else - { - result.Name.Should().Be(pathWithSpaces); - } - } - - [SkippableTheory] - [AutoData] - public void CreateDirectory_ShouldAdjustTimes(string path, string subdirectoryName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - string subdirectoryPath = FileSystem.Path.Combine(path, subdirectoryName); - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.Directory.CreateDirectory(path); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - - FileSystem.Directory.CreateDirectory(subdirectoryPath); - - DateTime creationTime = FileSystem.Directory.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.Directory.GetLastAccessTimeUtc(path); - DateTime lastWriteTime = FileSystem.Directory.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - } - - [SkippableTheory] - [AutoData] - public void CreateDirectory_ShouldAdjustTimesOnlyForDirectParentDirectory( - string rootPath) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - string subdirectoryLevel1Path = - FileSystem.Path.Combine(rootPath, "lvl1"); - string subdirectoryLevel2Path = - FileSystem.Path.Combine(subdirectoryLevel1Path, "lvl2"); - string subdirectoryLevel3Path = - FileSystem.Path.Combine(subdirectoryLevel2Path, "lvl3"); - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.Directory.CreateDirectory(subdirectoryLevel2Path); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - - FileSystem.Directory.CreateDirectory(subdirectoryLevel3Path); - - foreach (string path in new[] - { - rootPath, subdirectoryLevel1Path - }) - { - DateTime lastAccessTime = - FileSystem.Directory.GetLastAccessTimeUtc(path); - DateTime lastWriteTime = - FileSystem.Directory.GetLastWriteTimeUtc(path); - - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - } - - [SkippableTheory] - [AutoData] - public void CreateDirectory_ShouldSetCreationTime(string path) - { - DateTime start = TimeSystem.DateTime.Now; - - FileSystem.Directory.CreateDirectory(path); - - DateTime result = FileSystem.Directory.GetCreationTime(path); - result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.Should().BeOnOrBefore(TimeSystem.DateTime.Now); - result.Kind.Should().Be(DateTimeKind.Local); - } - - [SkippableTheory] - [AutoData] - public void CreateDirectory_ShouldSetCreationTimeUtc(string path) - { - DateTime start = TimeSystem.DateTime.UtcNow; - - FileSystem.Directory.CreateDirectory(path); - - DateTime result = FileSystem.Directory.GetCreationTimeUtc(path); - result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.Should().BeOnOrBefore(TimeSystem.DateTime.UtcNow); - result.Kind.Should().Be(DateTimeKind.Utc); - } - - [SkippableFact] - public void CreateDirectory_NullCharacter_ShouldThrowArgumentException() - { - string path = "foo\0bar"; - Exception? exception = - Record.Exception(() => FileSystem.Directory.CreateDirectory(path)); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableFact] - public void CreateDirectory_ShouldCreateDirectoryInBasePath() - { - IDirectoryInfo result = FileSystem.Directory.CreateDirectory("foo"); - bool exists = FileSystem.Directory.Exists("foo"); - - exists.Should().BeTrue(); - result.FullName.Should().StartWith(BasePath); - } - - [SkippableTheory] - [AutoData] - public void CreateDirectory_ShouldCreateParentDirectories( - string directoryLevel1, string directoryLevel2, string directoryLevel3) - { - string path = - FileSystem.Path.Combine(directoryLevel1, directoryLevel2, directoryLevel3); - - IDirectoryInfo result = FileSystem.Directory.CreateDirectory(path); - - result.Name.Should().Be(directoryLevel3); - result.Parent!.Name.Should().Be(directoryLevel2); - result.Parent.Parent!.Name.Should().Be(directoryLevel1); - result.Exists.Should().BeTrue(); - result.Parent.Exists.Should().BeTrue(); - result.Parent.Parent.Exists.Should().BeTrue(); - } - -#if NETFRAMEWORK - [SkippableTheory] - [InlineData("/")] - [InlineData("\\")] - public void CreateDirectory_TrailingDirectorySeparator_ShouldNotBeTrimmed( - string suffix) - { - string nameWithSuffix = "foobar" + suffix; - string expectedName = nameWithSuffix; - expectedName = expectedName.TrimEnd(' '); - - IDirectoryInfo result = - FileSystem.Directory.CreateDirectory(nameWithSuffix); - - result.Name.Should().Be(expectedName.TrimEnd( - FileSystem.Path.DirectorySeparatorChar, - FileSystem.Path.AltDirectorySeparatorChar)); - result.FullName.Should().Be(System.IO.Path.Combine(BasePath, expectedName - .Replace(FileSystem.Path.AltDirectorySeparatorChar, - FileSystem.Path.DirectorySeparatorChar))); - FileSystem.Directory.Exists(nameWithSuffix).Should().BeTrue(); - } - - [SkippableTheory] - [InlineData("")] - [InlineData(" ")] - public void CreateDirectory_EmptyOrWhitespace_ShouldReturnEmptyString( - string suffix) - { - string nameWithSuffix = "foobar" + suffix; - string expectedName = nameWithSuffix; - expectedName = expectedName.TrimEnd(' '); - - IDirectoryInfo result = - FileSystem.Directory.CreateDirectory(nameWithSuffix); - - result.Name.Should().Be(expectedName.TrimEnd( - FileSystem.Path.DirectorySeparatorChar, - FileSystem.Path.AltDirectorySeparatorChar)); - result.FullName.Should().Be(System.IO.Path.Combine(BasePath, expectedName - .Replace(FileSystem.Path.AltDirectorySeparatorChar, - FileSystem.Path.DirectorySeparatorChar))); - FileSystem.Directory.Exists(nameWithSuffix).Should().BeTrue(); - } -#else - [SkippableTheory] - [InlineData("")] - [InlineData(" ")] - [InlineData("/")] - [InlineData("\\")] - public void CreateDirectory_TrailingDirectorySeparator_ShouldNotBeTrimmed( - string suffix) - { - string nameWithSuffix = "foobar" + suffix; - string expectedName = nameWithSuffix; - if (Test.RunsOnWindows) - { - expectedName = expectedName.TrimEnd(' '); - } - else if (suffix == "\\") - { - //This case is only supported on Windows - return; - } - - IDirectoryInfo result = - FileSystem.Directory.CreateDirectory(nameWithSuffix); - - result.Name.Should().Be(expectedName.TrimEnd( - FileSystem.Path.DirectorySeparatorChar, - FileSystem.Path.AltDirectorySeparatorChar)); - result.FullName.Should().Be(System.IO.Path.Combine(BasePath, expectedName - .Replace(FileSystem.Path.AltDirectorySeparatorChar, - FileSystem.Path.DirectorySeparatorChar))); - FileSystem.Directory.Exists(nameWithSuffix).Should().BeTrue(); - } -#endif -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class CreateDirectoryTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void CreateDirectory_AlreadyExisting_ShouldDoNothing(string path) + { + FileSystem.Directory.CreateDirectory(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.CreateDirectory(path); + }); + + exception.Should().BeNull(); + FileSystem.Directory.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void CreateDirectory_ReadOnlyParent_ShouldStillCreateDirectory(string parent, + string subdirectory) + { + string subdirectoryPath = FileSystem.Path.Combine(parent, subdirectory); + FileSystem.Directory.CreateDirectory(parent); + FileSystem.DirectoryInfo.New(parent).Attributes = FileAttributes.ReadOnly; + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.CreateDirectory(subdirectoryPath); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeNull(); + FileSystem.Directory.Exists(subdirectoryPath).Should().BeTrue(); + FileSystem.DirectoryInfo.New(parent).Attributes + .Should().HaveFlag(FileAttributes.ReadOnly); + } + else + { + exception.Should().BeException(hResult: -2147024891); + FileSystem.Directory.Exists(subdirectoryPath).Should().BeFalse(); + } + } + + [SkippableFact] + public void CreateDirectory_Root_ShouldNotThrowException() + { + string path = FileTestHelper.RootDrive(); + FileSystem.Directory.CreateDirectory(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.CreateDirectory(path); + }); + + exception.Should().BeNull(); + FileSystem.Directory.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void CreateDirectory_ShouldTrimTrailingSpaces_OnWindows(string path) + { + string pathWithSpaces = path + " "; + + IDirectoryInfo result = FileSystem.Directory.CreateDirectory(pathWithSpaces); + + if (Test.RunsOnWindows) + { + result.Name.Should().Be(path); + } + else + { + result.Name.Should().Be(pathWithSpaces); + } + } + + [SkippableTheory] + [AutoData] + public void CreateDirectory_ShouldAdjustTimes(string path, string subdirectoryName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + string subdirectoryPath = FileSystem.Path.Combine(path, subdirectoryName); + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.Directory.CreateDirectory(path); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + + FileSystem.Directory.CreateDirectory(subdirectoryPath); + + DateTime creationTime = FileSystem.Directory.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.Directory.GetLastAccessTimeUtc(path); + DateTime lastWriteTime = FileSystem.Directory.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + } + + [SkippableTheory] + [AutoData] + public void CreateDirectory_ShouldAdjustTimesOnlyForDirectParentDirectory( + string rootPath) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + string subdirectoryLevel1Path = + FileSystem.Path.Combine(rootPath, "lvl1"); + string subdirectoryLevel2Path = + FileSystem.Path.Combine(subdirectoryLevel1Path, "lvl2"); + string subdirectoryLevel3Path = + FileSystem.Path.Combine(subdirectoryLevel2Path, "lvl3"); + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.Directory.CreateDirectory(subdirectoryLevel2Path); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + + FileSystem.Directory.CreateDirectory(subdirectoryLevel3Path); + + foreach (string path in new[] + { + rootPath, subdirectoryLevel1Path + }) + { + DateTime lastAccessTime = + FileSystem.Directory.GetLastAccessTimeUtc(path); + DateTime lastWriteTime = + FileSystem.Directory.GetLastWriteTimeUtc(path); + + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + } + + [SkippableTheory] + [AutoData] + public void CreateDirectory_ShouldSetCreationTime(string path) + { + DateTime start = TimeSystem.DateTime.Now; + + FileSystem.Directory.CreateDirectory(path); + + DateTime result = FileSystem.Directory.GetCreationTime(path); + result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.Should().BeOnOrBefore(TimeSystem.DateTime.Now); + result.Kind.Should().Be(DateTimeKind.Local); + } + + [SkippableTheory] + [AutoData] + public void CreateDirectory_ShouldSetCreationTimeUtc(string path) + { + DateTime start = TimeSystem.DateTime.UtcNow; + + FileSystem.Directory.CreateDirectory(path); + + DateTime result = FileSystem.Directory.GetCreationTimeUtc(path); + result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.Should().BeOnOrBefore(TimeSystem.DateTime.UtcNow); + result.Kind.Should().Be(DateTimeKind.Utc); + } + + [SkippableFact] + public void CreateDirectory_NullCharacter_ShouldThrowArgumentException() + { + string path = "foo\0bar"; + Exception? exception = + Record.Exception(() => FileSystem.Directory.CreateDirectory(path)); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableFact] + public void CreateDirectory_ShouldCreateDirectoryInBasePath() + { + IDirectoryInfo result = FileSystem.Directory.CreateDirectory("foo"); + bool exists = FileSystem.Directory.Exists("foo"); + + exists.Should().BeTrue(); + result.FullName.Should().StartWith(BasePath); + } + + [SkippableTheory] + [AutoData] + public void CreateDirectory_ShouldCreateParentDirectories( + string directoryLevel1, string directoryLevel2, string directoryLevel3) + { + string path = + FileSystem.Path.Combine(directoryLevel1, directoryLevel2, directoryLevel3); + + IDirectoryInfo result = FileSystem.Directory.CreateDirectory(path); + + result.Name.Should().Be(directoryLevel3); + result.Parent!.Name.Should().Be(directoryLevel2); + result.Parent.Parent!.Name.Should().Be(directoryLevel1); + result.Exists.Should().BeTrue(); + result.Parent.Exists.Should().BeTrue(); + result.Parent.Parent.Exists.Should().BeTrue(); + } + +#if NETFRAMEWORK + [SkippableTheory] + [InlineData("/")] + [InlineData("\\")] + public void CreateDirectory_TrailingDirectorySeparator_ShouldNotBeTrimmed( + string suffix) + { + string nameWithSuffix = "foobar" + suffix; + string expectedName = nameWithSuffix; + expectedName = expectedName.TrimEnd(' '); + + IDirectoryInfo result = + FileSystem.Directory.CreateDirectory(nameWithSuffix); + + result.Name.Should().Be(expectedName.TrimEnd( + FileSystem.Path.DirectorySeparatorChar, + FileSystem.Path.AltDirectorySeparatorChar)); + result.FullName.Should().Be(System.IO.Path.Combine(BasePath, expectedName + .Replace(FileSystem.Path.AltDirectorySeparatorChar, + FileSystem.Path.DirectorySeparatorChar))); + FileSystem.Directory.Exists(nameWithSuffix).Should().BeTrue(); + } + + [SkippableTheory] + [InlineData("")] + [InlineData(" ")] + public void CreateDirectory_EmptyOrWhitespace_ShouldReturnEmptyString( + string suffix) + { + string nameWithSuffix = "foobar" + suffix; + string expectedName = nameWithSuffix; + expectedName = expectedName.TrimEnd(' '); + + IDirectoryInfo result = + FileSystem.Directory.CreateDirectory(nameWithSuffix); + + result.Name.Should().Be(expectedName.TrimEnd( + FileSystem.Path.DirectorySeparatorChar, + FileSystem.Path.AltDirectorySeparatorChar)); + result.FullName.Should().Be(System.IO.Path.Combine(BasePath, expectedName + .Replace(FileSystem.Path.AltDirectorySeparatorChar, + FileSystem.Path.DirectorySeparatorChar))); + FileSystem.Directory.Exists(nameWithSuffix).Should().BeTrue(); + } +#else + [SkippableTheory] + [InlineData("")] + [InlineData(" ")] + [InlineData("/")] + [InlineData("\\")] + public void CreateDirectory_TrailingDirectorySeparator_ShouldNotBeTrimmed( + string suffix) + { + string nameWithSuffix = "foobar" + suffix; + string expectedName = nameWithSuffix; + if (Test.RunsOnWindows) + { + expectedName = expectedName.TrimEnd(' '); + } + else if (suffix == "\\") + { + //This case is only supported on Windows + return; + } + + IDirectoryInfo result = + FileSystem.Directory.CreateDirectory(nameWithSuffix); + + result.Name.Should().Be(expectedName.TrimEnd( + FileSystem.Path.DirectorySeparatorChar, + FileSystem.Path.AltDirectorySeparatorChar)); + result.FullName.Should().Be(System.IO.Path.Combine(BasePath, expectedName + .Replace(FileSystem.Path.AltDirectorySeparatorChar, + FileSystem.Path.DirectorySeparatorChar))); + FileSystem.Directory.Exists(nameWithSuffix).Should().BeTrue(); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/DeleteTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/DeleteTests.cs index 70c8c18a0..d51057553 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/DeleteTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/DeleteTests.cs @@ -1,241 +1,240 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class DeleteTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - Delete_CaseDifferentPath_ShouldThrowDirectoryNotFoundException_OnLinux( - string directoryName) - { - directoryName = directoryName.ToLowerInvariant(); - FileSystem.Directory.CreateDirectory(directoryName.ToUpperInvariant()); - string expectedPath = System.IO.Path.Combine(BasePath, directoryName); - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Delete(directoryName); - }); - - if (Test.RunsOnLinux) - { - exception.Should().BeException($"'{expectedPath}'", - hResult: -2147024893); - } - else - { - exception.Should().BeNull(); - FileSystem.Directory.Exists(directoryName.ToUpperInvariant()) - .Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void Delete_FullPath_ShouldDeleteDirectory(string directoryName) - { - IDirectoryInfo result = - FileSystem.Directory.CreateDirectory(directoryName); - - FileSystem.Directory.Delete(result.FullName); - - FileSystem.Directory.Exists(directoryName).Should().BeFalse(); - result.Exists.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string directoryName) - { - string expectedPath = System.IO.Path.Combine(BasePath, directoryName); - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Delete(directoryName); - }); - - exception.Should().BeException($"'{expectedPath}'", - hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Delete_Recursive_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string directoryName) - { - string expectedPath = System.IO.Path.Combine(BasePath, directoryName); - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Delete(directoryName, true); - }); - - exception.Should().BeException($"'{expectedPath}'", - hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Delete_Recursive_WithOpenFile_ShouldThrowIOException_OnWindows( - string path, string filename) - { - FileSystem.Initialize() - .WithSubdirectory(path); - string filePath = FileSystem.Path.Combine(path, filename); - FileSystemStream openFile = FileSystem.File.OpenWrite(filePath); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Delete(path, true); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException($"{filename}'", - hResult: -2147024864); - FileSystem.File.Exists(filePath).Should().BeTrue(); - } - else - { - exception.Should().BeNull(); - FileSystem.File.Exists(filePath).Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void - Delete_Recursive_WithSimilarNamedFile_ShouldOnlyDeleteDirectoryAndItsContents( - string subdirectory) - { - string fileName = $"{subdirectory}.txt"; - FileSystem.Initialize() - .WithSubdirectory(subdirectory).Initialized(s => s - .WithAFile() - .WithASubdirectory()) - .WithFile(fileName); - - FileSystem.Directory.Delete(subdirectory, true); - - FileSystem.Directory.Exists(subdirectory).Should().BeFalse(); - FileSystem.File.Exists(fileName).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void Delete_Recursive_WithSubdirectory_ShouldDeleteDirectoryWithContent( - string path, string subdirectory) - { - string subdirectoryPath = FileSystem.Path.Combine(path, subdirectory); - FileSystem.Directory.CreateDirectory(subdirectoryPath); - FileSystem.Directory.Exists(path).Should().BeTrue(); - - FileSystem.Directory.Delete(path, true); - - FileSystem.Directory.Exists(path).Should().BeFalse(); - FileSystem.Directory.Exists(subdirectoryPath).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Delete_ShouldAdjustTimes(string path, string subdirectoryName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - string subdirectoryPath = FileSystem.Path.Combine(path, subdirectoryName); - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.Directory.CreateDirectory(subdirectoryPath); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - - FileSystem.Directory.Delete(subdirectoryPath); - - DateTime creationTime = FileSystem.Directory.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.Directory.GetLastAccessTimeUtc(path); - DateTime lastWriteTime = FileSystem.Directory.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - } - - [SkippableTheory] - [AutoData] - public void Delete_ShouldDeleteDirectory(string directoryName) - { - IDirectoryInfo result = - FileSystem.Directory.CreateDirectory(directoryName); - - FileSystem.Directory.Delete(directoryName); - - bool exists = FileSystem.Directory.Exists(directoryName); - - exists.Should().BeFalse(); - result.Exists.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Delete_WithSimilarNamedFile_ShouldOnlyDeleteDirectory( - string subdirectory) - { - string fileName = $"{subdirectory}.txt"; - FileSystem.Initialize() - .WithSubdirectory(subdirectory) - .WithFile(fileName); - - FileSystem.Directory.Delete(subdirectory); - - FileSystem.Directory.Exists(subdirectory).Should().BeFalse(); - FileSystem.File.Exists(fileName).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void Delete_WithSubdirectory_ShouldThrowIOException_AndNotDeleteDirectory( - string path, string subdirectory) - { - FileSystem.Directory.CreateDirectory(FileSystem.Path.Combine(path, subdirectory)); - FileSystem.Directory.Exists(path).Should().BeTrue(); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Delete(path); - }); - - exception.Should().BeException( - hResult: Test.RunsOnWindows ? -2147024751 : Test.RunsOnMac ? 66 : 39, - // Path information only included in exception message on Windows and not in .NET Framework - messageContains: !Test.RunsOnWindows || Test.IsNetFramework - ? null - : $"'{System.IO.Path.Combine(BasePath, path)}'"); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class DeleteTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + Delete_CaseDifferentPath_ShouldThrowDirectoryNotFoundException_OnLinux( + string directoryName) + { + directoryName = directoryName.ToLowerInvariant(); + FileSystem.Directory.CreateDirectory(directoryName.ToUpperInvariant()); + string expectedPath = System.IO.Path.Combine(BasePath, directoryName); + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Delete(directoryName); + }); + + if (Test.RunsOnLinux) + { + exception.Should().BeException($"'{expectedPath}'", + hResult: -2147024893); + } + else + { + exception.Should().BeNull(); + FileSystem.Directory.Exists(directoryName.ToUpperInvariant()) + .Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void Delete_FullPath_ShouldDeleteDirectory(string directoryName) + { + IDirectoryInfo result = + FileSystem.Directory.CreateDirectory(directoryName); + + FileSystem.Directory.Delete(result.FullName); + + FileSystem.Directory.Exists(directoryName).Should().BeFalse(); + result.Exists.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string directoryName) + { + string expectedPath = System.IO.Path.Combine(BasePath, directoryName); + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Delete(directoryName); + }); + + exception.Should().BeException($"'{expectedPath}'", + hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Delete_Recursive_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string directoryName) + { + string expectedPath = System.IO.Path.Combine(BasePath, directoryName); + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Delete(directoryName, true); + }); + + exception.Should().BeException($"'{expectedPath}'", + hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Delete_Recursive_WithOpenFile_ShouldThrowIOException_OnWindows( + string path, string filename) + { + FileSystem.Initialize() + .WithSubdirectory(path); + string filePath = FileSystem.Path.Combine(path, filename); + FileSystemStream openFile = FileSystem.File.OpenWrite(filePath); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Delete(path, true); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException($"{filename}'", + hResult: -2147024864); + FileSystem.File.Exists(filePath).Should().BeTrue(); + } + else + { + exception.Should().BeNull(); + FileSystem.File.Exists(filePath).Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void + Delete_Recursive_WithSimilarNamedFile_ShouldOnlyDeleteDirectoryAndItsContents( + string subdirectory) + { + string fileName = $"{subdirectory}.txt"; + FileSystem.Initialize() + .WithSubdirectory(subdirectory).Initialized(s => s + .WithAFile() + .WithASubdirectory()) + .WithFile(fileName); + + FileSystem.Directory.Delete(subdirectory, true); + + FileSystem.Directory.Exists(subdirectory).Should().BeFalse(); + FileSystem.File.Exists(fileName).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void Delete_Recursive_WithSubdirectory_ShouldDeleteDirectoryWithContent( + string path, string subdirectory) + { + string subdirectoryPath = FileSystem.Path.Combine(path, subdirectory); + FileSystem.Directory.CreateDirectory(subdirectoryPath); + FileSystem.Directory.Exists(path).Should().BeTrue(); + + FileSystem.Directory.Delete(path, true); + + FileSystem.Directory.Exists(path).Should().BeFalse(); + FileSystem.Directory.Exists(subdirectoryPath).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Delete_ShouldAdjustTimes(string path, string subdirectoryName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + string subdirectoryPath = FileSystem.Path.Combine(path, subdirectoryName); + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.Directory.CreateDirectory(subdirectoryPath); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + + FileSystem.Directory.Delete(subdirectoryPath); + + DateTime creationTime = FileSystem.Directory.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.Directory.GetLastAccessTimeUtc(path); + DateTime lastWriteTime = FileSystem.Directory.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + } + + [SkippableTheory] + [AutoData] + public void Delete_ShouldDeleteDirectory(string directoryName) + { + IDirectoryInfo result = + FileSystem.Directory.CreateDirectory(directoryName); + + FileSystem.Directory.Delete(directoryName); + + bool exists = FileSystem.Directory.Exists(directoryName); + + exists.Should().BeFalse(); + result.Exists.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Delete_WithSimilarNamedFile_ShouldOnlyDeleteDirectory( + string subdirectory) + { + string fileName = $"{subdirectory}.txt"; + FileSystem.Initialize() + .WithSubdirectory(subdirectory) + .WithFile(fileName); + + FileSystem.Directory.Delete(subdirectory); + + FileSystem.Directory.Exists(subdirectory).Should().BeFalse(); + FileSystem.File.Exists(fileName).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void Delete_WithSubdirectory_ShouldThrowIOException_AndNotDeleteDirectory( + string path, string subdirectory) + { + FileSystem.Directory.CreateDirectory(FileSystem.Path.Combine(path, subdirectory)); + FileSystem.Directory.Exists(path).Should().BeTrue(); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Delete(path); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147024751 : Test.RunsOnMac ? 66 : 39, + // Path information only included in exception message on Windows and not in .NET Framework + messageContains: !Test.RunsOnWindows || Test.IsNetFramework + ? null + : $"'{System.IO.Path.Combine(BasePath, path)}'"); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/EnumerateDirectoriesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/EnumerateDirectoriesTests.cs index f68124a1b..4e4ae0778 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/EnumerateDirectoriesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/EnumerateDirectoriesTests.cs @@ -1,250 +1,249 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class EnumerateDirectoriesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableFact] - public void EnumerateDirectories_AbsolutePath_ShouldNotIncludeTrailingSlash() - { - FileSystem.Directory.CreateDirectory("foo"); - FileSystem.Directory.CreateDirectory("bar"); - - List result = FileSystem.Directory - .EnumerateDirectories(BasePath) - .ToList(); - - result.Should().Contain(FileSystem.Path.Combine(BasePath, "foo")); - result.Should().Contain(FileSystem.Path.Combine(BasePath, "bar")); - } - - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string path) - { - string expectedPath = System.IO.Path.Combine(BasePath, path); - Exception? exception = - Record.Exception(() - => FileSystem.Directory.EnumerateDirectories(path).ToList()); - - exception.Should().BeException( - $"'{expectedPath}'", - hResult: -2147024893); - FileSystem.Directory.Exists(path).Should().BeFalse(); - } - - [SkippableFact] - public void EnumerateDirectories_RelativePath_ShouldNotIncludeTrailingSlash() - { - string path = "."; - FileSystem.Directory.CreateDirectory("foo"); - FileSystem.Directory.CreateDirectory("bar"); - - List result = FileSystem.Directory - .EnumerateDirectories(path) - .ToList(); - - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - result.Should().Contain(FileSystem.Path.Combine(path, "bar")); - } - - [SkippableFact] - public void - EnumerateDirectories_RelativePathToParentDirectory_ShouldNotIncludeTrailingSlash() - { - string path = "foo/.."; - FileSystem.Directory.CreateDirectory("foo"); - FileSystem.Directory.CreateDirectory("bar"); - - List result = FileSystem.Directory - .EnumerateDirectories(path) - .ToList(); - - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - result.Should().Contain(FileSystem.Path.Combine(path, "bar")); - } - - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_SearchOptionAllDirectories_FullPath_ShouldReturnAllSubdirectoriesWithFullPath( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory - .EnumerateDirectories(baseDirectory.FullName, "*", SearchOption.AllDirectories) - .ToList(); - - result.Count.Should().Be(3); - result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo")); - result.Should() - .Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo", "xyz")); - result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "bar")); - } - - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory - .EnumerateDirectories(path, "*", SearchOption.AllDirectories).ToList(); - - result.Count.Should().Be(3); - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); - result.Should().Contain(FileSystem.Path.Combine(path, "bar")); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void EnumerateDirectories_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string subdirectoryName) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory("foo"); - baseDirectory.CreateSubdirectory(subdirectoryName); - - List result = FileSystem.Directory - .EnumerateDirectories("foo", searchPattern).ToList(); - - if (expectToBeFound) - { - result.Should().ContainSingle( - FileSystem.Path.Combine("foo", subdirectoryName), - $"it should match {searchPattern}"); - } - else - { - result.Should() - .BeEmpty($"{subdirectoryName} should not match {searchPattern}"); - } - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory - .EnumerateDirectories(path, "XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }).ToList(); - - result.Count.Should().Be(1); - result.Should().NotContain(FileSystem.Path.Combine(path, "foo")); - result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); - result.Should().NotContain(FileSystem.Path.Combine(path, "bar")); - } -#endif - - [SkippableTheory] - [AutoData] - public void EnumerateDirectories_WithNewline_ShouldThrowArgumentException( - string path) - { - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = FileSystem.Directory.EnumerateDirectories(path, searchPattern) - .FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory.EnumerateDirectories(path).ToList(); - - result.Count.Should().Be(2); - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - result.Should().NotContain(FileSystem.Path.Combine(path, "foo", "xyz")); - result.Should().Contain(FileSystem.Path.Combine(path, "bar")); - } - - [SkippableTheory] - [AutoData] - public void EnumerateDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo"); - baseDirectory.CreateSubdirectory("bar"); - - IEnumerable result = - FileSystem.Directory.EnumerateDirectories(path, "foo"); - - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - } - - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar/xyz"); - - IEnumerable result = FileSystem.Directory - .EnumerateDirectories(path, "xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(2); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class EnumerateDirectoriesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableFact] + public void EnumerateDirectories_AbsolutePath_ShouldNotIncludeTrailingSlash() + { + FileSystem.Directory.CreateDirectory("foo"); + FileSystem.Directory.CreateDirectory("bar"); + + List result = FileSystem.Directory + .EnumerateDirectories(BasePath) + .ToList(); + + result.Should().Contain(FileSystem.Path.Combine(BasePath, "foo")); + result.Should().Contain(FileSystem.Path.Combine(BasePath, "bar")); + } + + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string path) + { + string expectedPath = System.IO.Path.Combine(BasePath, path); + Exception? exception = + Record.Exception(() + => FileSystem.Directory.EnumerateDirectories(path).ToList()); + + exception.Should().BeException( + $"'{expectedPath}'", + hResult: -2147024893); + FileSystem.Directory.Exists(path).Should().BeFalse(); + } + + [SkippableFact] + public void EnumerateDirectories_RelativePath_ShouldNotIncludeTrailingSlash() + { + string path = "."; + FileSystem.Directory.CreateDirectory("foo"); + FileSystem.Directory.CreateDirectory("bar"); + + List result = FileSystem.Directory + .EnumerateDirectories(path) + .ToList(); + + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + result.Should().Contain(FileSystem.Path.Combine(path, "bar")); + } + + [SkippableFact] + public void + EnumerateDirectories_RelativePathToParentDirectory_ShouldNotIncludeTrailingSlash() + { + string path = "foo/.."; + FileSystem.Directory.CreateDirectory("foo"); + FileSystem.Directory.CreateDirectory("bar"); + + List result = FileSystem.Directory + .EnumerateDirectories(path) + .ToList(); + + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + result.Should().Contain(FileSystem.Path.Combine(path, "bar")); + } + + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_SearchOptionAllDirectories_FullPath_ShouldReturnAllSubdirectoriesWithFullPath( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory + .EnumerateDirectories(baseDirectory.FullName, "*", SearchOption.AllDirectories) + .ToList(); + + result.Count.Should().Be(3); + result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo")); + result.Should() + .Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo", "xyz")); + result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "bar")); + } + + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory + .EnumerateDirectories(path, "*", SearchOption.AllDirectories).ToList(); + + result.Count.Should().Be(3); + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); + result.Should().Contain(FileSystem.Path.Combine(path, "bar")); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void EnumerateDirectories_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string subdirectoryName) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory("foo"); + baseDirectory.CreateSubdirectory(subdirectoryName); + + List result = FileSystem.Directory + .EnumerateDirectories("foo", searchPattern).ToList(); + + if (expectToBeFound) + { + result.Should().ContainSingle( + FileSystem.Path.Combine("foo", subdirectoryName), + $"it should match {searchPattern}"); + } + else + { + result.Should() + .BeEmpty($"{subdirectoryName} should not match {searchPattern}"); + } + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory + .EnumerateDirectories(path, "XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }).ToList(); + + result.Count.Should().Be(1); + result.Should().NotContain(FileSystem.Path.Combine(path, "foo")); + result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); + result.Should().NotContain(FileSystem.Path.Combine(path, "bar")); + } +#endif + + [SkippableTheory] + [AutoData] + public void EnumerateDirectories_WithNewline_ShouldThrowArgumentException( + string path) + { + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = FileSystem.Directory.EnumerateDirectories(path, searchPattern) + .FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory.EnumerateDirectories(path).ToList(); + + result.Count.Should().Be(2); + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + result.Should().NotContain(FileSystem.Path.Combine(path, "foo", "xyz")); + result.Should().Contain(FileSystem.Path.Combine(path, "bar")); + } + + [SkippableTheory] + [AutoData] + public void EnumerateDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo"); + baseDirectory.CreateSubdirectory("bar"); + + IEnumerable result = + FileSystem.Directory.EnumerateDirectories(path, "foo"); + + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + } + + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar/xyz"); + + IEnumerable result = FileSystem.Directory + .EnumerateDirectories(path, "xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(2); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ExceptionTests.cs index a83e854f5..6ca506b71 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ExceptionTests.cs @@ -1,245 +1,244 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetDirectoryCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.Directory); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetDirectoryCallbacks), parameters: " ")] - public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.Directory); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetDirectoryCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.Directory); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetDirectoryCallbacks), parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.Directory); - }); - - if (!Test.RunsOnWindows) - { - if (exception is IOException ioException) - { - ioException.HResult.Should().NotBe(-2147024809, - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - else - { - if (Test.IsNetFramework) - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should().BeException( - hResult: -2147024773, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - } - - #region Helpers - - public static IEnumerable GetDirectoryCallbacks(string? path) - => GetDirectoryCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string ParamName, - Expression> Callback)> - GetDirectoryCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.CreateDirectory(value)); -#if FEATURE_FILESYSTEM_UNIXFILEMODE - if (!Test.RunsOnWindows) - { - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.CreateDirectory(value, UnixFileMode.None)); - } -#endif -#if FEATURE_FILESYSTEM_LINK - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.CreateSymbolicLink(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "pathToTarget", directory - => directory.CreateSymbolicLink("foo", value)); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.Delete(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.Delete(value, true)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateDirectories(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateDirectories(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateDirectories(value, "foo", SearchOption.AllDirectories)); -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateDirectories(value, "foo", new EnumerationOptions())); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFiles(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFiles(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFiles(value, "foo", SearchOption.AllDirectories)); -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFiles(value, "foo", new EnumerationOptions())); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFileSystemEntries(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFileSystemEntries(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFileSystemEntries(value, "foo", - SearchOption.AllDirectories)); -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFileSystemEntries(value, "foo", - new EnumerationOptions())); -#endif - // `Directory.Exists` doesn't throw an exception on `null` - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetCreationTime(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetCreationTimeUtc(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetDirectories(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetDirectories(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetDirectories(value, "foo", SearchOption.AllDirectories)); -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetDirectories(value, "foo", new EnumerationOptions())); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFiles(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFiles(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFiles(value, "foo", SearchOption.AllDirectories)); -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFiles(value, "foo", new EnumerationOptions())); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFileSystemEntries(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFileSystemEntries(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFileSystemEntries(value, "foo", SearchOption.AllDirectories)); -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFileSystemEntries(value, "foo", new EnumerationOptions())); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetLastAccessTime(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetLastAccessTimeUtc(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetLastWriteTime(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetLastWriteTimeUtc(value)); - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "path", - directory - => directory.GetParent(value)); - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "sourceDirName", - directory - => directory.Move(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destDirName", - directory - => directory.Move("foo", value)); -#if FEATURE_FILESYSTEM_LINK - yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "linkPath", - directory - => directory.ResolveLinkTarget(value, false)); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.SetCreationTime(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.SetCreationTimeUtc(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.SetLastAccessTime(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.SetLastAccessTimeUtc(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.SetLastWriteTime(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.SetLastWriteTimeUtc(value, DateTime.Now)); - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetDirectoryCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.Directory); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetDirectoryCallbacks), parameters: " ")] + public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.Directory); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetDirectoryCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.Directory); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetDirectoryCallbacks), parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.Directory); + }); + + if (!Test.RunsOnWindows) + { + if (exception is IOException ioException) + { + ioException.HResult.Should().NotBe(-2147024809, + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + else + { + if (Test.IsNetFramework) + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should().BeException( + hResult: -2147024773, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + } + + #region Helpers + + public static IEnumerable GetDirectoryCallbacks(string? path) + => GetDirectoryCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string ParamName, + Expression> Callback)> + GetDirectoryCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.CreateDirectory(value)); +#if FEATURE_FILESYSTEM_UNIXFILEMODE + if (!Test.RunsOnWindows) + { + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.CreateDirectory(value, UnixFileMode.None)); + } +#endif +#if FEATURE_FILESYSTEM_LINK + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.CreateSymbolicLink(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "pathToTarget", directory + => directory.CreateSymbolicLink("foo", value)); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.Delete(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.Delete(value, true)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateDirectories(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateDirectories(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateDirectories(value, "foo", SearchOption.AllDirectories)); +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateDirectories(value, "foo", new EnumerationOptions())); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFiles(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFiles(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFiles(value, "foo", SearchOption.AllDirectories)); +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFiles(value, "foo", new EnumerationOptions())); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFileSystemEntries(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFileSystemEntries(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFileSystemEntries(value, "foo", + SearchOption.AllDirectories)); +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFileSystemEntries(value, "foo", + new EnumerationOptions())); +#endif + // `Directory.Exists` doesn't throw an exception on `null` + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetCreationTime(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetCreationTimeUtc(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetDirectories(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetDirectories(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetDirectories(value, "foo", SearchOption.AllDirectories)); +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetDirectories(value, "foo", new EnumerationOptions())); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFiles(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFiles(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFiles(value, "foo", SearchOption.AllDirectories)); +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFiles(value, "foo", new EnumerationOptions())); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFileSystemEntries(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFileSystemEntries(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFileSystemEntries(value, "foo", SearchOption.AllDirectories)); +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFileSystemEntries(value, "foo", new EnumerationOptions())); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetLastAccessTime(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetLastAccessTimeUtc(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetLastWriteTime(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetLastWriteTimeUtc(value)); + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "path", + directory + => directory.GetParent(value)); + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "sourceDirName", + directory + => directory.Move(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destDirName", + directory + => directory.Move("foo", value)); +#if FEATURE_FILESYSTEM_LINK + yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "linkPath", + directory + => directory.ResolveLinkTarget(value, false)); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.SetCreationTime(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.SetCreationTimeUtc(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.SetLastAccessTime(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.SetLastAccessTimeUtc(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.SetLastWriteTime(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.SetLastWriteTimeUtc(value, DateTime.Now)); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/GetDirectoriesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/GetDirectoriesTests.cs index f3e969512..c52b7e066 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/GetDirectoriesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/GetDirectoriesTests.cs @@ -1,215 +1,214 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class GetDirectoriesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - GetDirectories_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string path) - { - string expectedPath = System.IO.Path.Combine(BasePath, path); - Exception? exception = - Record.Exception(() - => FileSystem.Directory.GetDirectories(path).ToList()); - - exception.Should().BeException($"'{expectedPath}'", - hResult: -2147024893); - FileSystem.Directory.Exists(path).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void - GetDirectories_SearchOptionAllDirectories_FullPath_ShouldReturnAllSubdirectoriesWithFullPath( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory - .GetDirectories(baseDirectory.FullName, "*", SearchOption.AllDirectories) - .ToList(); - - result.Count.Should().Be(3); - result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo")); - result.Should() - .Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo", "xyz")); - result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "bar")); - } - - [SkippableTheory] - [AutoData] - public void - GetDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory - .GetDirectories(path, "*", SearchOption.AllDirectories).ToList(); - - result.Count.Should().Be(3); - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); - result.Should().Contain(FileSystem.Path.Combine(path, "bar")); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void GetDirectories_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string subdirectoryName) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory("foo"); - baseDirectory.CreateSubdirectory(subdirectoryName); - - List result = FileSystem.Directory - .GetDirectories("foo", searchPattern).ToList(); - - if (expectToBeFound) - { - result.Should().ContainSingle( - FileSystem.Path.Combine("foo", subdirectoryName), - $"it should match {searchPattern}"); - } - else - { - result.Should() - .BeEmpty($"{subdirectoryName} should not match {searchPattern}"); - } - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableTheory] - [AutoData] - public void - GetDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory - .GetDirectories(path, "XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }).ToList(); - - result.Count.Should().Be(1); - result.Should().NotContain(FileSystem.Path.Combine(path, "foo")); - result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); - result.Should().NotContain(FileSystem.Path.Combine(path, "bar")); - } -#endif - - [SkippableTheory] - [AutoData] - public void GetDirectories_WithNewline_ShouldThrowArgumentException( - string path) - { - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = FileSystem.Directory.GetDirectories(path, searchPattern) - .FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableTheory] - [AutoData] - public void - GetDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory.GetDirectories(path).ToList(); - - result.Count.Should().Be(2); - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - result.Should().NotContain(FileSystem.Path.Combine(path, "foo", "xyz")); - result.Should().Contain(FileSystem.Path.Combine(path, "bar")); - } - - [SkippableFact] - public void GetDirectories_WithRelativePath_ShouldReturnRelativePaths() - { - string path = $"foo{FileSystem.Path.DirectorySeparatorChar}bar"; - FileSystem.Directory.CreateDirectory(path); - - string[] result = FileSystem.Directory.GetDirectories("foo"); - - result.Should().BeEquivalentTo(path); - } - - [SkippableTheory] - [AutoData] - public void GetDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo"); - baseDirectory.CreateSubdirectory("bar"); - - IEnumerable result = - FileSystem.Directory.GetDirectories(path, "foo"); - - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - } - - [SkippableTheory] - [AutoData] - public void - GetDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar/xyz"); - - IEnumerable result = FileSystem.Directory - .GetDirectories(path, "xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(2); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class GetDirectoriesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + GetDirectories_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string path) + { + string expectedPath = System.IO.Path.Combine(BasePath, path); + Exception? exception = + Record.Exception(() + => FileSystem.Directory.GetDirectories(path).ToList()); + + exception.Should().BeException($"'{expectedPath}'", + hResult: -2147024893); + FileSystem.Directory.Exists(path).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void + GetDirectories_SearchOptionAllDirectories_FullPath_ShouldReturnAllSubdirectoriesWithFullPath( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory + .GetDirectories(baseDirectory.FullName, "*", SearchOption.AllDirectories) + .ToList(); + + result.Count.Should().Be(3); + result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo")); + result.Should() + .Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo", "xyz")); + result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "bar")); + } + + [SkippableTheory] + [AutoData] + public void + GetDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory + .GetDirectories(path, "*", SearchOption.AllDirectories).ToList(); + + result.Count.Should().Be(3); + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); + result.Should().Contain(FileSystem.Path.Combine(path, "bar")); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void GetDirectories_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string subdirectoryName) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory("foo"); + baseDirectory.CreateSubdirectory(subdirectoryName); + + List result = FileSystem.Directory + .GetDirectories("foo", searchPattern).ToList(); + + if (expectToBeFound) + { + result.Should().ContainSingle( + FileSystem.Path.Combine("foo", subdirectoryName), + $"it should match {searchPattern}"); + } + else + { + result.Should() + .BeEmpty($"{subdirectoryName} should not match {searchPattern}"); + } + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableTheory] + [AutoData] + public void + GetDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory + .GetDirectories(path, "XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }).ToList(); + + result.Count.Should().Be(1); + result.Should().NotContain(FileSystem.Path.Combine(path, "foo")); + result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); + result.Should().NotContain(FileSystem.Path.Combine(path, "bar")); + } +#endif + + [SkippableTheory] + [AutoData] + public void GetDirectories_WithNewline_ShouldThrowArgumentException( + string path) + { + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = FileSystem.Directory.GetDirectories(path, searchPattern) + .FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableTheory] + [AutoData] + public void + GetDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory.GetDirectories(path).ToList(); + + result.Count.Should().Be(2); + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + result.Should().NotContain(FileSystem.Path.Combine(path, "foo", "xyz")); + result.Should().Contain(FileSystem.Path.Combine(path, "bar")); + } + + [SkippableFact] + public void GetDirectories_WithRelativePath_ShouldReturnRelativePaths() + { + string path = $"foo{FileSystem.Path.DirectorySeparatorChar}bar"; + FileSystem.Directory.CreateDirectory(path); + + string[] result = FileSystem.Directory.GetDirectories("foo"); + + result.Should().BeEquivalentTo(path); + } + + [SkippableTheory] + [AutoData] + public void GetDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo"); + baseDirectory.CreateSubdirectory("bar"); + + IEnumerable result = + FileSystem.Directory.GetDirectories(path, "foo"); + + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + } + + [SkippableTheory] + [AutoData] + public void + GetDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar/xyz"); + + IEnumerable result = FileSystem.Directory + .GetDirectories(path, "xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(2); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/MoveTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/MoveTests.cs index efa392545..277e1fa5c 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/MoveTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/MoveTests.cs @@ -1,292 +1,291 @@ -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystemInitializer; -#if !NETFRAMEWORK -#endif - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class MoveTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Move_CaseOnlyChange_ShouldMoveDirectoryWithContent(string path) - { - Skip.If(Test.IsNetFramework); - - string source = path.ToLowerInvariant(); - string destination = path.ToUpperInvariant(); - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - - FileSystem.Directory.Move(source, destination); - - FileSystem.Directory.Exists(source).Should().Be(!Test.RunsOnLinux); - FileSystem.Directory.Exists(destination).Should().BeTrue(); - FileSystem.Directory.GetDirectories(".").Should() - .ContainSingle(d => d.Contains(destination)); - FileSystem.Directory.GetFiles(destination, initialized[1].Name) - .Should().ContainSingle(); - FileSystem.Directory.GetDirectories(destination, initialized[2].Name) - .Should().ContainSingle(); - FileSystem.Directory.GetFiles(destination, initialized[3].Name, - SearchOption.AllDirectories) - .Should().ContainSingle(); - FileSystem.Directory.GetDirectories(destination, initialized[4].Name, - SearchOption.AllDirectories) - .Should().ContainSingle(); - } - - [SkippableTheory] - [AutoData] - public void Move_CaseOnlyChange_ShouldThrowIOException_OnNetFramework(string path) - { - Skip.IfNot(Test.IsNetFramework); - - string source = path.ToLowerInvariant(); - string destination = path.ToUpperInvariant(); - FileSystem.Initialize() - .WithSubdirectory(source); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Move(source, destination); - }); - - exception.Should().BeException(hResult: -2146232800); - } - - [SkippableTheory] - [AutoData] - public void Move_DestinationDoesNotExist_ShouldThrowDirectoryNotFoundException( - string source) - { - FileSystem.InitializeIn(source) - .WithAFile(); - string destination = FileTestHelper.RootDrive("not-existing/path"); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Move(source, destination); - }); - - exception.Should().BeException(hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Move_ShouldMoveAttributes(string source, string destination) - { - FileSystem.Initialize() - .WithSubdirectory(source); - FileSystem.DirectoryInfo.New(source).Attributes |= FileAttributes.System; - FileAttributes expectedAttributes = - FileSystem.DirectoryInfo.New(source).Attributes; - - FileSystem.Directory.Move(source, destination); - - FileSystem.DirectoryInfo.New(destination).Attributes - .Should().Be(expectedAttributes); - } - - [SkippableTheory] - [AutoData] - public void Move_ShouldMoveDirectoryWithContent(string source, string destination) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - - FileSystem.Directory.Move(source, destination); - - FileSystem.Directory.Exists(source).Should().BeFalse(); - FileSystem.Directory.Exists(destination).Should().BeTrue(); - FileSystem.Directory.GetFiles(destination, initialized[1].Name) - .Should().ContainSingle(); - FileSystem.Directory.GetDirectories(destination, initialized[2].Name) - .Should().ContainSingle(); - FileSystem.Directory.GetFiles(destination, initialized[3].Name, - SearchOption.AllDirectories) - .Should().ContainSingle(); - FileSystem.Directory.GetDirectories(destination, initialized[4].Name, - SearchOption.AllDirectories) - .Should().ContainSingle(); - } - - [SkippableTheory] - [AutoData] - public void Move_ShouldNotAdjustTimes(string source, string destination) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - - FileSystem.Directory.Move(source, destination); - - DateTime creationTime = FileSystem.Directory.GetCreationTimeUtc(destination); - DateTime lastAccessTime = FileSystem.Directory.GetLastAccessTimeUtc(destination); - DateTime lastWriteTime = FileSystem.Directory.GetLastWriteTimeUtc(destination); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - [SkippableTheory] - [AutoData] - public void Move_SourceAndDestinationIdentical_ShouldThrowIOException(string path) - { - FileSystem.Initialize() - .WithSubdirectory(path); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Move(path, path); - }); - - exception.Should().BeException(hResult: -2146232800); - } - - [SkippableTheory] - [AutoData] - public void Move_WithLockedFile_ShouldThrowIOException_AndNotMoveDirectoryAtAll_OnWindows( - string source, string destination) - { - Skip.IfNot(Test.RunsOnWindows); - - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, - FileMode.Open, - FileAccess.Read, - FileShare.Read); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Move(source, destination); - }); - - exception.Should().BeException(hResult: -2147024891); - FileSystem.Directory.Exists(source).Should().BeTrue(); - FileSystem.Directory.Exists(destination).Should().BeFalse(); - IDirectoryInfo sourceDirectory = - FileSystem.DirectoryInfo.New(source); - sourceDirectory.GetFiles(initialized[1].Name) - .Should().ContainSingle(); - sourceDirectory.GetDirectories(initialized[2].Name) - .Should().ContainSingle(); - sourceDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - sourceDirectory - .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - } - - [SkippableTheory] - [AutoData] - public void Move_WithLockedFile_ShouldStillMoveDirectory_NotOnWindows( - string source, string destination) - { - Skip.If(Test.RunsOnWindows); - - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, - FileMode.Open, - FileAccess.Read, - FileShare.Read); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Move(source, destination); - }); - - exception.Should().BeNull(); - FileSystem.Directory.Exists(source).Should().BeFalse(); - FileSystem.Directory.Exists(destination).Should().BeTrue(); - IDirectoryInfo destinationDirectory = - FileSystem.DirectoryInfo.New(destination); - destinationDirectory.GetFiles(initialized[1].Name) - .Should().ContainSingle(); - destinationDirectory.GetDirectories(initialized[2].Name) - .Should().ContainSingle(); - destinationDirectory - .GetFiles(initialized[3].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - destinationDirectory - .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - } - - [SkippableTheory] - [AutoData] - public void Move_WithReadOnlyFile_ShouldMoveDirectoryWithContent( - string source, string destination) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - initialized[3].Attributes = FileAttributes.ReadOnly; - - FileSystem.Directory.Move(source, destination); - - FileSystem.Directory.Exists(source).Should().BeFalse(); - FileSystem.Directory.Exists(destination).Should().BeTrue(); - IDirectoryInfo destinationDirectory = - FileSystem.DirectoryInfo.New(destination); - destinationDirectory.GetFiles(initialized[1].Name) - .Should().ContainSingle(); - destinationDirectory.GetDirectories(initialized[2].Name) - .Should().ContainSingle(); - destinationDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) - .Should().ContainSingle().Which.Attributes.Should() - .HaveFlag(FileAttributes.ReadOnly); - destinationDirectory - .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - } -} +using System.IO; +using Testably.Abstractions.Testing.FileSystemInitializer; +#if !NETFRAMEWORK +#endif + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class MoveTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Move_CaseOnlyChange_ShouldMoveDirectoryWithContent(string path) + { + Skip.If(Test.IsNetFramework); + + string source = path.ToLowerInvariant(); + string destination = path.ToUpperInvariant(); + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + + FileSystem.Directory.Move(source, destination); + + FileSystem.Directory.Exists(source).Should().Be(!Test.RunsOnLinux); + FileSystem.Directory.Exists(destination).Should().BeTrue(); + FileSystem.Directory.GetDirectories(".").Should() + .ContainSingle(d => d.Contains(destination)); + FileSystem.Directory.GetFiles(destination, initialized[1].Name) + .Should().ContainSingle(); + FileSystem.Directory.GetDirectories(destination, initialized[2].Name) + .Should().ContainSingle(); + FileSystem.Directory.GetFiles(destination, initialized[3].Name, + SearchOption.AllDirectories) + .Should().ContainSingle(); + FileSystem.Directory.GetDirectories(destination, initialized[4].Name, + SearchOption.AllDirectories) + .Should().ContainSingle(); + } + + [SkippableTheory] + [AutoData] + public void Move_CaseOnlyChange_ShouldThrowIOException_OnNetFramework(string path) + { + Skip.IfNot(Test.IsNetFramework); + + string source = path.ToLowerInvariant(); + string destination = path.ToUpperInvariant(); + FileSystem.Initialize() + .WithSubdirectory(source); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Move(source, destination); + }); + + exception.Should().BeException(hResult: -2146232800); + } + + [SkippableTheory] + [AutoData] + public void Move_DestinationDoesNotExist_ShouldThrowDirectoryNotFoundException( + string source) + { + FileSystem.InitializeIn(source) + .WithAFile(); + string destination = FileTestHelper.RootDrive("not-existing/path"); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Move(source, destination); + }); + + exception.Should().BeException(hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Move_ShouldMoveAttributes(string source, string destination) + { + FileSystem.Initialize() + .WithSubdirectory(source); + FileSystem.DirectoryInfo.New(source).Attributes |= FileAttributes.System; + FileAttributes expectedAttributes = + FileSystem.DirectoryInfo.New(source).Attributes; + + FileSystem.Directory.Move(source, destination); + + FileSystem.DirectoryInfo.New(destination).Attributes + .Should().Be(expectedAttributes); + } + + [SkippableTheory] + [AutoData] + public void Move_ShouldMoveDirectoryWithContent(string source, string destination) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + + FileSystem.Directory.Move(source, destination); + + FileSystem.Directory.Exists(source).Should().BeFalse(); + FileSystem.Directory.Exists(destination).Should().BeTrue(); + FileSystem.Directory.GetFiles(destination, initialized[1].Name) + .Should().ContainSingle(); + FileSystem.Directory.GetDirectories(destination, initialized[2].Name) + .Should().ContainSingle(); + FileSystem.Directory.GetFiles(destination, initialized[3].Name, + SearchOption.AllDirectories) + .Should().ContainSingle(); + FileSystem.Directory.GetDirectories(destination, initialized[4].Name, + SearchOption.AllDirectories) + .Should().ContainSingle(); + } + + [SkippableTheory] + [AutoData] + public void Move_ShouldNotAdjustTimes(string source, string destination) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + + FileSystem.Directory.Move(source, destination); + + DateTime creationTime = FileSystem.Directory.GetCreationTimeUtc(destination); + DateTime lastAccessTime = FileSystem.Directory.GetLastAccessTimeUtc(destination); + DateTime lastWriteTime = FileSystem.Directory.GetLastWriteTimeUtc(destination); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + [SkippableTheory] + [AutoData] + public void Move_SourceAndDestinationIdentical_ShouldThrowIOException(string path) + { + FileSystem.Initialize() + .WithSubdirectory(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Move(path, path); + }); + + exception.Should().BeException(hResult: -2146232800); + } + + [SkippableTheory] + [AutoData] + public void Move_WithLockedFile_ShouldThrowIOException_AndNotMoveDirectoryAtAll_OnWindows( + string source, string destination) + { + Skip.IfNot(Test.RunsOnWindows); + + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, + FileMode.Open, + FileAccess.Read, + FileShare.Read); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Move(source, destination); + }); + + exception.Should().BeException(hResult: -2147024891); + FileSystem.Directory.Exists(source).Should().BeTrue(); + FileSystem.Directory.Exists(destination).Should().BeFalse(); + IDirectoryInfo sourceDirectory = + FileSystem.DirectoryInfo.New(source); + sourceDirectory.GetFiles(initialized[1].Name) + .Should().ContainSingle(); + sourceDirectory.GetDirectories(initialized[2].Name) + .Should().ContainSingle(); + sourceDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + sourceDirectory + .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + } + + [SkippableTheory] + [AutoData] + public void Move_WithLockedFile_ShouldStillMoveDirectory_NotOnWindows( + string source, string destination) + { + Skip.If(Test.RunsOnWindows); + + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, + FileMode.Open, + FileAccess.Read, + FileShare.Read); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Move(source, destination); + }); + + exception.Should().BeNull(); + FileSystem.Directory.Exists(source).Should().BeFalse(); + FileSystem.Directory.Exists(destination).Should().BeTrue(); + IDirectoryInfo destinationDirectory = + FileSystem.DirectoryInfo.New(destination); + destinationDirectory.GetFiles(initialized[1].Name) + .Should().ContainSingle(); + destinationDirectory.GetDirectories(initialized[2].Name) + .Should().ContainSingle(); + destinationDirectory + .GetFiles(initialized[3].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + destinationDirectory + .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + } + + [SkippableTheory] + [AutoData] + public void Move_WithReadOnlyFile_ShouldMoveDirectoryWithContent( + string source, string destination) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + initialized[3].Attributes = FileAttributes.ReadOnly; + + FileSystem.Directory.Move(source, destination); + + FileSystem.Directory.Exists(source).Should().BeFalse(); + FileSystem.Directory.Exists(destination).Should().BeTrue(); + IDirectoryInfo destinationDirectory = + FileSystem.DirectoryInfo.New(destination); + destinationDirectory.GetFiles(initialized[1].Name) + .Should().ContainSingle(); + destinationDirectory.GetDirectories(initialized[2].Name) + .Should().ContainSingle(); + destinationDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) + .Should().ContainSingle().Which.Attributes.Should() + .HaveFlag(FileAttributes.ReadOnly); + destinationDirectory + .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ResolveLinkTargetTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ResolveLinkTargetTests.cs index 44fcfc586..6e19de0cf 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ResolveLinkTargetTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ResolveLinkTargetTests.cs @@ -1,201 +1,200 @@ -#if FEATURE_FILESYSTEM_LINK -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ResolveLinkTargetTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - #region Test Setup - - /// - /// The maximum number of symbolic links that are followed.
- /// - ///
- private static int MaxResolveLinks => - Test.RunsOnWindows ? 63 : 40; - - #endregion - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_AbsolutePath_ShouldFollowSymbolicLink( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.Directory.CreateDirectory(pathToTarget); - FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(path, false); - - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void - ResolveLinkTarget_FileWithDifferentCase_ShouldReturnPathToMissingDirectory( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.Directory.CreateDirectory(pathToTarget); - FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); - FileSystem.Directory.Delete(pathToTarget); - FileSystem.Directory.CreateDirectory(pathToTarget.ToUpper()); - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(path, false); - - if (!Test.RunsOnLinux) - { - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeTrue(); - } - else - { - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FinalTarget_ShouldFollowSymbolicLinkToFinalTarget( - string path, string pathToFinalTarget) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - int maxLinks = MaxResolveLinks; - - FileSystem.Directory.CreateDirectory(pathToFinalTarget); - string previousPath = pathToFinalTarget; - for (int i = 0; i < maxLinks; i++) - { - string newPath = $"{path}-{i}"; - FileSystem.Directory.CreateSymbolicLink(newPath, - System.IO.Path.Combine(BasePath, previousPath)); - previousPath = newPath; - } - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(previousPath, true); - - target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToFinalTarget)); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FinalTargetWithTooManyLevels_ShouldThrowIOException( - string path, string pathToFinalTarget) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - int maxLinks = MaxResolveLinks + 1; - FileSystem.Directory.CreateDirectory(pathToFinalTarget); - string previousPath = pathToFinalTarget; - for (int i = 0; i < maxLinks; i++) - { - string newPath = $"{path}-{i}"; - FileSystem.Directory.CreateSymbolicLink(newPath, - System.IO.Path.Combine(BasePath, previousPath)); - previousPath = newPath; - } - - Exception? exception = Record.Exception(() => - { - _ = FileSystem.Directory.ResolveLinkTarget(previousPath, true); - }); - - exception.Should().BeException($"'{previousPath}'", - hResult: -2147022975); - } - - [SkippableTheory] - [AutoData] - public void - ResolveLinkTarget_MissingDirectoryInLinkChain_ShouldReturnPathToMissingDirectory( - string path, string pathToFinalTarget, string pathToMissingDirectory) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Directory.CreateDirectory(pathToFinalTarget); - FileSystem.Directory.CreateSymbolicLink(pathToMissingDirectory, - System.IO.Path.Combine(BasePath, pathToFinalTarget)); - FileSystem.Directory.CreateSymbolicLink(path, - System.IO.Path.Combine(BasePath, pathToMissingDirectory)); - FileSystem.Directory.Delete(pathToMissingDirectory); - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(path, true); - - target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToMissingDirectory)); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_NormalDirectory_ShouldReturnNull( - string path) - { - FileSystem.Directory.CreateDirectory(path); - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(path, false); - - target.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_NormalFile_ShouldReturnNull( - string path) - { - FileSystem.File.WriteAllText(path, null); - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(path, false); - - target.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_RelativePath_ShouldFollowSymbolicLinkUnderWindows( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.Directory.CreateDirectory(pathToTarget); - FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(path, false); - - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_TargetDeletedAfterLinkCreation_ShouldReturnNull( - string path, string pathToTarget) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.Directory.CreateDirectory(pathToTarget); - FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); - FileSystem.Directory.Delete(pathToTarget); - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(path, false); - - target!.FullName.Should().Be(targetFullPath); - - target.Exists.Should().BeFalse(); - } -} -#endif +#if FEATURE_FILESYSTEM_LINK +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ResolveLinkTargetTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + #region Test Setup + + /// + /// The maximum number of symbolic links that are followed.
+ /// + ///
+ private static int MaxResolveLinks => + Test.RunsOnWindows ? 63 : 40; + + #endregion + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_AbsolutePath_ShouldFollowSymbolicLink( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.Directory.CreateDirectory(pathToTarget); + FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(path, false); + + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void + ResolveLinkTarget_FileWithDifferentCase_ShouldReturnPathToMissingDirectory( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.Directory.CreateDirectory(pathToTarget); + FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); + FileSystem.Directory.Delete(pathToTarget); + FileSystem.Directory.CreateDirectory(pathToTarget.ToUpper()); + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(path, false); + + if (!Test.RunsOnLinux) + { + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeTrue(); + } + else + { + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FinalTarget_ShouldFollowSymbolicLinkToFinalTarget( + string path, string pathToFinalTarget) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + int maxLinks = MaxResolveLinks; + + FileSystem.Directory.CreateDirectory(pathToFinalTarget); + string previousPath = pathToFinalTarget; + for (int i = 0; i < maxLinks; i++) + { + string newPath = $"{path}-{i}"; + FileSystem.Directory.CreateSymbolicLink(newPath, + System.IO.Path.Combine(BasePath, previousPath)); + previousPath = newPath; + } + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(previousPath, true); + + target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToFinalTarget)); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FinalTargetWithTooManyLevels_ShouldThrowIOException( + string path, string pathToFinalTarget) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + int maxLinks = MaxResolveLinks + 1; + FileSystem.Directory.CreateDirectory(pathToFinalTarget); + string previousPath = pathToFinalTarget; + for (int i = 0; i < maxLinks; i++) + { + string newPath = $"{path}-{i}"; + FileSystem.Directory.CreateSymbolicLink(newPath, + System.IO.Path.Combine(BasePath, previousPath)); + previousPath = newPath; + } + + Exception? exception = Record.Exception(() => + { + _ = FileSystem.Directory.ResolveLinkTarget(previousPath, true); + }); + + exception.Should().BeException($"'{previousPath}'", + hResult: -2147022975); + } + + [SkippableTheory] + [AutoData] + public void + ResolveLinkTarget_MissingDirectoryInLinkChain_ShouldReturnPathToMissingDirectory( + string path, string pathToFinalTarget, string pathToMissingDirectory) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Directory.CreateDirectory(pathToFinalTarget); + FileSystem.Directory.CreateSymbolicLink(pathToMissingDirectory, + System.IO.Path.Combine(BasePath, pathToFinalTarget)); + FileSystem.Directory.CreateSymbolicLink(path, + System.IO.Path.Combine(BasePath, pathToMissingDirectory)); + FileSystem.Directory.Delete(pathToMissingDirectory); + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(path, true); + + target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToMissingDirectory)); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_NormalDirectory_ShouldReturnNull( + string path) + { + FileSystem.Directory.CreateDirectory(path); + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(path, false); + + target.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_NormalFile_ShouldReturnNull( + string path) + { + FileSystem.File.WriteAllText(path, null); + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(path, false); + + target.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_RelativePath_ShouldFollowSymbolicLinkUnderWindows( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.Directory.CreateDirectory(pathToTarget); + FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(path, false); + + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_TargetDeletedAfterLinkCreation_ShouldReturnNull( + string path, string pathToTarget) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.Directory.CreateDirectory(pathToTarget); + FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); + FileSystem.Directory.Delete(pathToTarget); + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(path, false); + + target!.FullName.Should().Be(targetFullPath); + + target.Exists.Should().BeFalse(); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.Times.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.Times.cs index 4e7ce3ed1..6617bd623 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.Times.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.Times.cs @@ -1,514 +1,513 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -public abstract partial class Tests - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void GetCreationTime_PathNotFound_ShouldReturnNullTime(string path) - { - DateTime expectedTime = FileTestHelper.NullTime.ToLocalTime(); - - DateTime result = FileSystem.Directory.GetCreationTime(path); - - result.Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void GetCreationTimeUtc_PathNotFound_ShouldReturnNullTime(string path) - { - DateTime expectedTime = FileTestHelper.NullTime.ToUniversalTime(); - - DateTime result = FileSystem.Directory.GetCreationTimeUtc(path); - - result.Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void GetLastAccessTime_PathNotFound_ShouldReturnNullTime(string path) - { - DateTime expectedTime = FileTestHelper.NullTime.ToLocalTime(); - - DateTime result = FileSystem.Directory.GetLastAccessTime(path); - - result.Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void GetLastAccessTimeUtc_PathNotFound_ShouldReturnNullTime(string path) - { - DateTime expectedTime = FileTestHelper.NullTime.ToUniversalTime(); - - DateTime result = FileSystem.Directory.GetLastAccessTimeUtc(path); - - result.Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void GetLastWriteTime_PathNotFound_ShouldReturnNullTime(string path) - { - DateTime expectedTime = FileTestHelper.NullTime.ToLocalTime(); - - DateTime result = FileSystem.Directory.GetLastWriteTime(path); - - result.Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void GetLastWriteTimeUtc_PathNotFound_ShouldReturnNullTime(string path) - { - DateTime expectedTime = FileTestHelper.NullTime.ToUniversalTime(); - - DateTime result = FileSystem.Directory.GetLastWriteTimeUtc(path); - - result.Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void LastAccessTime_CreateSubDirectory_ShouldUpdateLastAccessAndLastWriteTime( - string path, string subPath) - { - DateTime start = TimeSystem.DateTime.Now; - IDirectoryInfo result = FileSystem.Directory.CreateDirectory(path); - TimeSystem.Thread.Sleep(100); - DateTime sleepTime = TimeSystem.DateTime.Now; - FileSystem.Directory.CreateDirectory(FileSystem.Path.Combine(path, subPath)); - - result.CreationTime.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.CreationTime.Should().BeBefore(sleepTime); - // Last Access Time is only updated on Windows - if (Test.RunsOnWindows) - { - result.LastAccessTime.Should() - .BeOnOrAfter(sleepTime.ApplySystemClockTolerance()); - result.LastAccessTime.Should().BeOnOrBefore(TimeSystem.DateTime.Now); - } - else - { - result.LastAccessTime.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.LastAccessTime.Should().BeBefore(sleepTime); - } - - result.LastWriteTime.Should().BeOnOrAfter(sleepTime.ApplySystemClockTolerance()); - result.LastWriteTime.Should().BeOnOrBefore(TimeSystem.DateTime.Now); - } - - [SkippableTheory] - [AutoData] - public void LastAccessTime_ShouldBeSet(string path) - { - DateTime start = TimeSystem.DateTime.Now; - - FileSystem.Directory.CreateDirectory(path); - - DateTime result = FileSystem.Directory.GetLastAccessTime(path); - result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.Should().BeOnOrBefore(TimeSystem.DateTime.Now); - result.Kind.Should().Be(DateTimeKind.Local); - } - - [SkippableTheory] - [AutoData] - public void LastAccessTimeUtc_ShouldBeSet(string path) - { - DateTime start = TimeSystem.DateTime.UtcNow; - - FileSystem.Directory.CreateDirectory(path); - - DateTime result = FileSystem.Directory.GetLastAccessTimeUtc(path); - result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.Should().BeOnOrBefore(TimeSystem.DateTime.UtcNow); - result.Kind.Should().Be(DateTimeKind.Utc); - } - - [SkippableTheory] - [AutoData] - public void LastWriteTime_ShouldBeSet(string path) - { - DateTime start = TimeSystem.DateTime.Now; - - FileSystem.Directory.CreateDirectory(path); - - DateTime result = FileSystem.Directory.GetLastWriteTime(path); - result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.Should().BeOnOrBefore(TimeSystem.DateTime.Now); - result.Kind.Should().Be(DateTimeKind.Local); - } - - [SkippableTheory] - [AutoData] - public void LastWriteTimeUtc_ShouldBeSet(string path) - { - DateTime start = TimeSystem.DateTime.UtcNow; - - FileSystem.Directory.CreateDirectory(path); - - DateTime result = FileSystem.Directory.GetLastWriteTimeUtc(path); - result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.Should().BeOnOrBefore(TimeSystem.DateTime.UtcNow); - result.Kind.Should().Be(DateTimeKind.Utc); - } - - [SkippableTheory] - [AutoData] - public void SetCreationTime_PathNotFound_ShouldThrowCorrectException( - string path, DateTime creationTime) - { - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.SetCreationTime(path, creationTime); - }); - - if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - else - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024893); - } - } - - [SkippableTheory] - [AutoData] - public void SetCreationTime_ShouldChangeCreationTime( - string path, DateTime creationTime) - { - Skip.IfNot(Test.RunsOnWindows, - "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); - - creationTime = creationTime.ToLocalTime(); - DateTime expectedTime = creationTime.ToUniversalTime(); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetCreationTime(path, creationTime); - - FileSystem.Directory.GetCreationTimeUtc(path) - .Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void SetCreationTime_Unspecified_ShouldChangeCreationTime( - string path, DateTime creationTime) - { - Skip.IfNot(Test.RunsOnWindows, - "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); - - creationTime = DateTime.SpecifyKind(creationTime, DateTimeKind.Unspecified); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetCreationTime(path, creationTime); - - FileSystem.Directory.GetCreationTimeUtc(path) - .Should().Be(creationTime.ToUniversalTime()); - FileSystem.Directory.GetCreationTime(path) - .Should().Be(creationTime); - FileSystem.Directory.GetCreationTime(path).Kind - .Should().NotBe(DateTimeKind.Unspecified); - } - - [SkippableTheory] - [AutoData] - public void SetCreationTimeUtc_PathNotFound_ShouldThrowCorrectException( - string path, DateTime creationTime) - { - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.SetCreationTimeUtc(path, creationTime); - }); - - if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - else - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024893); - } - } - - [SkippableTheory] - [AutoData] - public void SetCreationTimeUtc_ShouldChangeCreationTime( - string path, DateTime creationTime) - { - Skip.IfNot(Test.RunsOnWindows, - "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); - - creationTime = creationTime.ToUniversalTime(); - DateTime expectedTime = creationTime.ToLocalTime(); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetCreationTimeUtc(path, creationTime); - - FileSystem.Directory.GetCreationTime(path) - .Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void SetCreationTimeUtc_Unspecified_ShouldChangeCreationTime( - string path, DateTime creationTime) - { - Skip.IfNot(Test.RunsOnWindows, - "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); - - creationTime = DateTime.SpecifyKind(creationTime, DateTimeKind.Unspecified); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetCreationTimeUtc(path, creationTime); - - FileSystem.Directory.GetCreationTimeUtc(path) - .Should().Be(creationTime); - FileSystem.Directory.GetCreationTime(path) - .Should().Be(creationTime.ToLocalTime()); - FileSystem.Directory.GetCreationTime(path).Kind - .Should().NotBe(DateTimeKind.Unspecified); - } - - [SkippableTheory] - [AutoData] - public void SetLastAccessTime_PathNotFound_ShouldThrowCorrectException( - string path, DateTime lastAccessTime) - { - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.SetLastAccessTime(path, lastAccessTime); - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - else - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024893); - } - } - - [SkippableTheory] - [AutoData] - public void SetLastAccessTime_ShouldChangeLastAccessTime( - string path, DateTime lastAccessTime) - { - lastAccessTime = lastAccessTime.ToLocalTime(); - DateTime expectedTime = lastAccessTime.ToUniversalTime(); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastAccessTime(path, lastAccessTime); - - FileSystem.Directory.GetLastAccessTimeUtc(path) - .Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void SetLastAccessTime_Unspecified_ShouldChangeLastAccessTime( - string path, DateTime lastAccessTime) - { - lastAccessTime = DateTime.SpecifyKind(lastAccessTime, DateTimeKind.Unspecified); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastAccessTime(path, lastAccessTime); - - FileSystem.Directory.GetLastAccessTimeUtc(path) - .Should().Be(lastAccessTime.ToUniversalTime()); - FileSystem.Directory.GetLastAccessTime(path) - .Should().Be(lastAccessTime); - FileSystem.Directory.GetLastAccessTime(path).Kind - .Should().NotBe(DateTimeKind.Unspecified); - } - - [SkippableTheory] - [AutoData] - public void SetLastAccessTimeUtc_PathNotFound_ShouldThrowCorrectException( - string path, DateTime lastAccessTime) - { - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.SetLastAccessTimeUtc(path, lastAccessTime); - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - else - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024893); - } - } - - [SkippableTheory] - [AutoData] - public void SetLastAccessTimeUtc_ShouldChangeLastAccessTime( - string path, DateTime lastAccessTime) - { - lastAccessTime = lastAccessTime.ToUniversalTime(); - DateTime expectedTime = lastAccessTime.ToLocalTime(); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastAccessTimeUtc(path, lastAccessTime); - - FileSystem.Directory.GetLastAccessTime(path) - .Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void SetLastAccessTimeUtc_Unspecified_ShouldChangeLastAccessTime( - string path, DateTime lastAccessTime) - { - lastAccessTime = DateTime.SpecifyKind(lastAccessTime, DateTimeKind.Unspecified); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastAccessTimeUtc(path, lastAccessTime); - - FileSystem.Directory.GetLastAccessTimeUtc(path) - .Should().Be(lastAccessTime); - FileSystem.Directory.GetLastAccessTime(path) - .Should().Be(lastAccessTime.ToLocalTime()); - FileSystem.Directory.GetLastAccessTime(path).Kind - .Should().NotBe(DateTimeKind.Unspecified); - } - - [SkippableTheory] - [AutoData] - public void SetLastWriteTime_PathNotFound_ShouldThrowCorrectException( - string path, DateTime lastWriteTime) - { - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.SetLastWriteTime(path, lastWriteTime); - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - else - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024893); - } - } - - [SkippableTheory] - [AutoData] - public void SetLastWriteTime_ShouldChangeLastWriteTime( - string path, DateTime lastWriteTime) - { - lastWriteTime = lastWriteTime.ToLocalTime(); - DateTime expectedTime = lastWriteTime.ToUniversalTime(); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastWriteTime(path, lastWriteTime); - - FileSystem.Directory.GetLastWriteTimeUtc(path) - .Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void SetLastWriteTime_Unspecified_ShouldChangeLastWriteTime( - string path, DateTime lastWriteTime) - { - lastWriteTime = DateTime.SpecifyKind(lastWriteTime, DateTimeKind.Unspecified); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastWriteTime(path, lastWriteTime); - - FileSystem.Directory.GetLastWriteTimeUtc(path) - .Should().Be(lastWriteTime.ToUniversalTime()); - FileSystem.Directory.GetLastWriteTime(path) - .Should().Be(lastWriteTime); - FileSystem.Directory.GetLastWriteTime(path).Kind - .Should().NotBe(DateTimeKind.Unspecified); - } - - [SkippableTheory] - [AutoData] - public void SetLastWriteTimeUtc_PathNotFound_ShouldThrowCorrectException( - string path, DateTime lastWriteTime) - { - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.SetLastWriteTimeUtc(path, lastWriteTime); - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - else - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024893); - } - } - - [SkippableTheory] - [AutoData] - public void SetLastWriteTimeUtc_ShouldChangeLastWriteTime( - string path, DateTime lastWriteTime) - { - lastWriteTime = lastWriteTime.ToUniversalTime(); - DateTime expectedTime = lastWriteTime.ToLocalTime(); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastWriteTimeUtc(path, lastWriteTime); - - FileSystem.Directory.GetLastWriteTime(path) - .Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void SetLastWriteTimeUtc_Unspecified_ShouldChangeLastWriteTime( - string path, DateTime lastWriteTime) - { - lastWriteTime = DateTime.SpecifyKind(lastWriteTime, DateTimeKind.Unspecified); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastWriteTimeUtc(path, lastWriteTime); - - FileSystem.Directory.GetLastWriteTimeUtc(path) - .Should().Be(lastWriteTime); - FileSystem.Directory.GetLastWriteTime(path) - .Should().Be(lastWriteTime.ToLocalTime()); - FileSystem.Directory.GetLastWriteTime(path).Kind - .Should().NotBe(DateTimeKind.Unspecified); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +public abstract partial class Tests + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void GetCreationTime_PathNotFound_ShouldReturnNullTime(string path) + { + DateTime expectedTime = FileTestHelper.NullTime.ToLocalTime(); + + DateTime result = FileSystem.Directory.GetCreationTime(path); + + result.Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void GetCreationTimeUtc_PathNotFound_ShouldReturnNullTime(string path) + { + DateTime expectedTime = FileTestHelper.NullTime.ToUniversalTime(); + + DateTime result = FileSystem.Directory.GetCreationTimeUtc(path); + + result.Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void GetLastAccessTime_PathNotFound_ShouldReturnNullTime(string path) + { + DateTime expectedTime = FileTestHelper.NullTime.ToLocalTime(); + + DateTime result = FileSystem.Directory.GetLastAccessTime(path); + + result.Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void GetLastAccessTimeUtc_PathNotFound_ShouldReturnNullTime(string path) + { + DateTime expectedTime = FileTestHelper.NullTime.ToUniversalTime(); + + DateTime result = FileSystem.Directory.GetLastAccessTimeUtc(path); + + result.Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void GetLastWriteTime_PathNotFound_ShouldReturnNullTime(string path) + { + DateTime expectedTime = FileTestHelper.NullTime.ToLocalTime(); + + DateTime result = FileSystem.Directory.GetLastWriteTime(path); + + result.Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void GetLastWriteTimeUtc_PathNotFound_ShouldReturnNullTime(string path) + { + DateTime expectedTime = FileTestHelper.NullTime.ToUniversalTime(); + + DateTime result = FileSystem.Directory.GetLastWriteTimeUtc(path); + + result.Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void LastAccessTime_CreateSubDirectory_ShouldUpdateLastAccessAndLastWriteTime( + string path, string subPath) + { + DateTime start = TimeSystem.DateTime.Now; + IDirectoryInfo result = FileSystem.Directory.CreateDirectory(path); + TimeSystem.Thread.Sleep(100); + DateTime sleepTime = TimeSystem.DateTime.Now; + FileSystem.Directory.CreateDirectory(FileSystem.Path.Combine(path, subPath)); + + result.CreationTime.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.CreationTime.Should().BeBefore(sleepTime); + // Last Access Time is only updated on Windows + if (Test.RunsOnWindows) + { + result.LastAccessTime.Should() + .BeOnOrAfter(sleepTime.ApplySystemClockTolerance()); + result.LastAccessTime.Should().BeOnOrBefore(TimeSystem.DateTime.Now); + } + else + { + result.LastAccessTime.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.LastAccessTime.Should().BeBefore(sleepTime); + } + + result.LastWriteTime.Should().BeOnOrAfter(sleepTime.ApplySystemClockTolerance()); + result.LastWriteTime.Should().BeOnOrBefore(TimeSystem.DateTime.Now); + } + + [SkippableTheory] + [AutoData] + public void LastAccessTime_ShouldBeSet(string path) + { + DateTime start = TimeSystem.DateTime.Now; + + FileSystem.Directory.CreateDirectory(path); + + DateTime result = FileSystem.Directory.GetLastAccessTime(path); + result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.Should().BeOnOrBefore(TimeSystem.DateTime.Now); + result.Kind.Should().Be(DateTimeKind.Local); + } + + [SkippableTheory] + [AutoData] + public void LastAccessTimeUtc_ShouldBeSet(string path) + { + DateTime start = TimeSystem.DateTime.UtcNow; + + FileSystem.Directory.CreateDirectory(path); + + DateTime result = FileSystem.Directory.GetLastAccessTimeUtc(path); + result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.Should().BeOnOrBefore(TimeSystem.DateTime.UtcNow); + result.Kind.Should().Be(DateTimeKind.Utc); + } + + [SkippableTheory] + [AutoData] + public void LastWriteTime_ShouldBeSet(string path) + { + DateTime start = TimeSystem.DateTime.Now; + + FileSystem.Directory.CreateDirectory(path); + + DateTime result = FileSystem.Directory.GetLastWriteTime(path); + result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.Should().BeOnOrBefore(TimeSystem.DateTime.Now); + result.Kind.Should().Be(DateTimeKind.Local); + } + + [SkippableTheory] + [AutoData] + public void LastWriteTimeUtc_ShouldBeSet(string path) + { + DateTime start = TimeSystem.DateTime.UtcNow; + + FileSystem.Directory.CreateDirectory(path); + + DateTime result = FileSystem.Directory.GetLastWriteTimeUtc(path); + result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.Should().BeOnOrBefore(TimeSystem.DateTime.UtcNow); + result.Kind.Should().Be(DateTimeKind.Utc); + } + + [SkippableTheory] + [AutoData] + public void SetCreationTime_PathNotFound_ShouldThrowCorrectException( + string path, DateTime creationTime) + { + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.SetCreationTime(path, creationTime); + }); + + if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + else + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024893); + } + } + + [SkippableTheory] + [AutoData] + public void SetCreationTime_ShouldChangeCreationTime( + string path, DateTime creationTime) + { + Skip.IfNot(Test.RunsOnWindows, + "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); + + creationTime = creationTime.ToLocalTime(); + DateTime expectedTime = creationTime.ToUniversalTime(); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetCreationTime(path, creationTime); + + FileSystem.Directory.GetCreationTimeUtc(path) + .Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void SetCreationTime_Unspecified_ShouldChangeCreationTime( + string path, DateTime creationTime) + { + Skip.IfNot(Test.RunsOnWindows, + "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); + + creationTime = DateTime.SpecifyKind(creationTime, DateTimeKind.Unspecified); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetCreationTime(path, creationTime); + + FileSystem.Directory.GetCreationTimeUtc(path) + .Should().Be(creationTime.ToUniversalTime()); + FileSystem.Directory.GetCreationTime(path) + .Should().Be(creationTime); + FileSystem.Directory.GetCreationTime(path).Kind + .Should().NotBe(DateTimeKind.Unspecified); + } + + [SkippableTheory] + [AutoData] + public void SetCreationTimeUtc_PathNotFound_ShouldThrowCorrectException( + string path, DateTime creationTime) + { + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.SetCreationTimeUtc(path, creationTime); + }); + + if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + else + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024893); + } + } + + [SkippableTheory] + [AutoData] + public void SetCreationTimeUtc_ShouldChangeCreationTime( + string path, DateTime creationTime) + { + Skip.IfNot(Test.RunsOnWindows, + "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); + + creationTime = creationTime.ToUniversalTime(); + DateTime expectedTime = creationTime.ToLocalTime(); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetCreationTimeUtc(path, creationTime); + + FileSystem.Directory.GetCreationTime(path) + .Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void SetCreationTimeUtc_Unspecified_ShouldChangeCreationTime( + string path, DateTime creationTime) + { + Skip.IfNot(Test.RunsOnWindows, + "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); + + creationTime = DateTime.SpecifyKind(creationTime, DateTimeKind.Unspecified); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetCreationTimeUtc(path, creationTime); + + FileSystem.Directory.GetCreationTimeUtc(path) + .Should().Be(creationTime); + FileSystem.Directory.GetCreationTime(path) + .Should().Be(creationTime.ToLocalTime()); + FileSystem.Directory.GetCreationTime(path).Kind + .Should().NotBe(DateTimeKind.Unspecified); + } + + [SkippableTheory] + [AutoData] + public void SetLastAccessTime_PathNotFound_ShouldThrowCorrectException( + string path, DateTime lastAccessTime) + { + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.SetLastAccessTime(path, lastAccessTime); + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + else + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024893); + } + } + + [SkippableTheory] + [AutoData] + public void SetLastAccessTime_ShouldChangeLastAccessTime( + string path, DateTime lastAccessTime) + { + lastAccessTime = lastAccessTime.ToLocalTime(); + DateTime expectedTime = lastAccessTime.ToUniversalTime(); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastAccessTime(path, lastAccessTime); + + FileSystem.Directory.GetLastAccessTimeUtc(path) + .Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void SetLastAccessTime_Unspecified_ShouldChangeLastAccessTime( + string path, DateTime lastAccessTime) + { + lastAccessTime = DateTime.SpecifyKind(lastAccessTime, DateTimeKind.Unspecified); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastAccessTime(path, lastAccessTime); + + FileSystem.Directory.GetLastAccessTimeUtc(path) + .Should().Be(lastAccessTime.ToUniversalTime()); + FileSystem.Directory.GetLastAccessTime(path) + .Should().Be(lastAccessTime); + FileSystem.Directory.GetLastAccessTime(path).Kind + .Should().NotBe(DateTimeKind.Unspecified); + } + + [SkippableTheory] + [AutoData] + public void SetLastAccessTimeUtc_PathNotFound_ShouldThrowCorrectException( + string path, DateTime lastAccessTime) + { + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.SetLastAccessTimeUtc(path, lastAccessTime); + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + else + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024893); + } + } + + [SkippableTheory] + [AutoData] + public void SetLastAccessTimeUtc_ShouldChangeLastAccessTime( + string path, DateTime lastAccessTime) + { + lastAccessTime = lastAccessTime.ToUniversalTime(); + DateTime expectedTime = lastAccessTime.ToLocalTime(); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastAccessTimeUtc(path, lastAccessTime); + + FileSystem.Directory.GetLastAccessTime(path) + .Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void SetLastAccessTimeUtc_Unspecified_ShouldChangeLastAccessTime( + string path, DateTime lastAccessTime) + { + lastAccessTime = DateTime.SpecifyKind(lastAccessTime, DateTimeKind.Unspecified); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastAccessTimeUtc(path, lastAccessTime); + + FileSystem.Directory.GetLastAccessTimeUtc(path) + .Should().Be(lastAccessTime); + FileSystem.Directory.GetLastAccessTime(path) + .Should().Be(lastAccessTime.ToLocalTime()); + FileSystem.Directory.GetLastAccessTime(path).Kind + .Should().NotBe(DateTimeKind.Unspecified); + } + + [SkippableTheory] + [AutoData] + public void SetLastWriteTime_PathNotFound_ShouldThrowCorrectException( + string path, DateTime lastWriteTime) + { + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.SetLastWriteTime(path, lastWriteTime); + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + else + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024893); + } + } + + [SkippableTheory] + [AutoData] + public void SetLastWriteTime_ShouldChangeLastWriteTime( + string path, DateTime lastWriteTime) + { + lastWriteTime = lastWriteTime.ToLocalTime(); + DateTime expectedTime = lastWriteTime.ToUniversalTime(); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastWriteTime(path, lastWriteTime); + + FileSystem.Directory.GetLastWriteTimeUtc(path) + .Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void SetLastWriteTime_Unspecified_ShouldChangeLastWriteTime( + string path, DateTime lastWriteTime) + { + lastWriteTime = DateTime.SpecifyKind(lastWriteTime, DateTimeKind.Unspecified); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastWriteTime(path, lastWriteTime); + + FileSystem.Directory.GetLastWriteTimeUtc(path) + .Should().Be(lastWriteTime.ToUniversalTime()); + FileSystem.Directory.GetLastWriteTime(path) + .Should().Be(lastWriteTime); + FileSystem.Directory.GetLastWriteTime(path).Kind + .Should().NotBe(DateTimeKind.Unspecified); + } + + [SkippableTheory] + [AutoData] + public void SetLastWriteTimeUtc_PathNotFound_ShouldThrowCorrectException( + string path, DateTime lastWriteTime) + { + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.SetLastWriteTimeUtc(path, lastWriteTime); + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + else + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024893); + } + } + + [SkippableTheory] + [AutoData] + public void SetLastWriteTimeUtc_ShouldChangeLastWriteTime( + string path, DateTime lastWriteTime) + { + lastWriteTime = lastWriteTime.ToUniversalTime(); + DateTime expectedTime = lastWriteTime.ToLocalTime(); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastWriteTimeUtc(path, lastWriteTime); + + FileSystem.Directory.GetLastWriteTime(path) + .Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void SetLastWriteTimeUtc_Unspecified_ShouldChangeLastWriteTime( + string path, DateTime lastWriteTime) + { + lastWriteTime = DateTime.SpecifyKind(lastWriteTime, DateTimeKind.Unspecified); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastWriteTimeUtc(path, lastWriteTime); + + FileSystem.Directory.GetLastWriteTimeUtc(path) + .Should().Be(lastWriteTime); + FileSystem.Directory.GetLastWriteTime(path) + .Should().Be(lastWriteTime.ToLocalTime()); + FileSystem.Directory.GetLastWriteTime(path).Kind + .Should().NotBe(DateTimeKind.Unspecified); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.cs index 03ce88b4e..1aba527a8 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.cs @@ -1,127 +1,126 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ -#if FEATURE_FILESYSTEM_NET7 - [SkippableFact] - public void CreateTempSubdirectory_ShouldCreateTheTemporaryDirectory() - { - IDirectoryInfo result = FileSystem.Directory.CreateTempSubdirectory(); - - result.Exists.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void CreateTempSubdirectory_WithPrefix_ShouldStartWithPrefix(string prefix) - { - IDirectoryInfo result = FileSystem.Directory.CreateTempSubdirectory(prefix); - - result.Name.Should().StartWith(prefix); - } -#endif - [SkippableFact] - public void GetCurrentDirectory_ShouldNotBeRooted() - { - string result = FileSystem.Directory.GetCurrentDirectory(); - - result.Should().NotBe(FileTestHelper.RootDrive()); - } - - [SkippableTheory] - [AutoData] - public void GetDirectoryRoot_ShouldReturnRoot(string path) - { - string root = FileTestHelper.RootDrive(); - string rootedPath = root + path; - - string result = FileSystem.Directory.GetDirectoryRoot(rootedPath); - - result.Should().Be(root); - } - - [SkippableFact] - public void GetLogicalDrives_ShouldNotBeEmpty() - { - string[] result = FileSystem.Directory.GetLogicalDrives(); - - result.Should().NotBeEmpty(); - result.Should().Contain(FileTestHelper.RootDrive()); - } - - [SkippableTheory] - [AutoData] - public void GetParent_ArbitraryPaths_ShouldNotBeNull(string path1, - string path2, - string path3) - { - string path = FileSystem.Path.Combine(path1, path2, path3); - IDirectoryInfo expectedParent = FileSystem.DirectoryInfo.New( - FileSystem.Path.Combine(path1, path2)); - - IDirectoryInfo? result = FileSystem.Directory.GetParent(path); - - result.Should().NotBeNull(); - result!.FullName.Should().Be(expectedParent.FullName); - } - - [SkippableFact] - public void GetParent_Root_ShouldReturnNull() - { - string path = FileTestHelper.RootDrive(); - - IDirectoryInfo? result = FileSystem.Directory.GetParent(path); - - result.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void - SetCurrentDirectory_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string path) - { - string previousCurrentDirectory = FileSystem.Directory.GetCurrentDirectory(); - try - { - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.SetCurrentDirectory(path); - }); - - exception.Should().BeException(hResult: -2147024893); - FileSystem.Directory.GetCurrentDirectory().Should() - .Be(previousCurrentDirectory); - } - finally - { - FileSystem.Directory.SetCurrentDirectory(previousCurrentDirectory); - } - } - - [SkippableTheory] - [AutoData] - public void SetCurrentDirectory_RelativePath_ShouldBeFullyQualified(string path) - { - string previousCurrentDirectory = FileSystem.Directory.GetCurrentDirectory(); - try - { - string expectedPath = FileSystem.Path.GetFullPath(path); - FileSystem.Directory.CreateDirectory(path); - FileSystem.Directory.SetCurrentDirectory(path); - string result = FileSystem.Directory.GetCurrentDirectory(); - - result.Should().Be(expectedPath); - } - finally - { - FileSystem.Directory.SetCurrentDirectory(previousCurrentDirectory); - } - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ +#if FEATURE_FILESYSTEM_NET7 + [SkippableFact] + public void CreateTempSubdirectory_ShouldCreateTheTemporaryDirectory() + { + IDirectoryInfo result = FileSystem.Directory.CreateTempSubdirectory(); + + result.Exists.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void CreateTempSubdirectory_WithPrefix_ShouldStartWithPrefix(string prefix) + { + IDirectoryInfo result = FileSystem.Directory.CreateTempSubdirectory(prefix); + + result.Name.Should().StartWith(prefix); + } +#endif + [SkippableFact] + public void GetCurrentDirectory_ShouldNotBeRooted() + { + string result = FileSystem.Directory.GetCurrentDirectory(); + + result.Should().NotBe(FileTestHelper.RootDrive()); + } + + [SkippableTheory] + [AutoData] + public void GetDirectoryRoot_ShouldReturnRoot(string path) + { + string root = FileTestHelper.RootDrive(); + string rootedPath = root + path; + + string result = FileSystem.Directory.GetDirectoryRoot(rootedPath); + + result.Should().Be(root); + } + + [SkippableFact] + public void GetLogicalDrives_ShouldNotBeEmpty() + { + string[] result = FileSystem.Directory.GetLogicalDrives(); + + result.Should().NotBeEmpty(); + result.Should().Contain(FileTestHelper.RootDrive()); + } + + [SkippableTheory] + [AutoData] + public void GetParent_ArbitraryPaths_ShouldNotBeNull(string path1, + string path2, + string path3) + { + string path = FileSystem.Path.Combine(path1, path2, path3); + IDirectoryInfo expectedParent = FileSystem.DirectoryInfo.New( + FileSystem.Path.Combine(path1, path2)); + + IDirectoryInfo? result = FileSystem.Directory.GetParent(path); + + result.Should().NotBeNull(); + result!.FullName.Should().Be(expectedParent.FullName); + } + + [SkippableFact] + public void GetParent_Root_ShouldReturnNull() + { + string path = FileTestHelper.RootDrive(); + + IDirectoryInfo? result = FileSystem.Directory.GetParent(path); + + result.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void + SetCurrentDirectory_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string path) + { + string previousCurrentDirectory = FileSystem.Directory.GetCurrentDirectory(); + try + { + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.SetCurrentDirectory(path); + }); + + exception.Should().BeException(hResult: -2147024893); + FileSystem.Directory.GetCurrentDirectory().Should() + .Be(previousCurrentDirectory); + } + finally + { + FileSystem.Directory.SetCurrentDirectory(previousCurrentDirectory); + } + } + + [SkippableTheory] + [AutoData] + public void SetCurrentDirectory_RelativePath_ShouldBeFullyQualified(string path) + { + string previousCurrentDirectory = FileSystem.Directory.GetCurrentDirectory(); + try + { + string expectedPath = FileSystem.Path.GetFullPath(path); + FileSystem.Directory.CreateDirectory(path); + FileSystem.Directory.SetCurrentDirectory(path); + string result = FileSystem.Directory.GetCurrentDirectory(); + + result.Should().Be(expectedPath); + } + finally + { + FileSystem.Directory.SetCurrentDirectory(previousCurrentDirectory); + } + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/AttributesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/AttributesTests.cs index 1a0fb745d..3e1af25c0 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/AttributesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/AttributesTests.cs @@ -1,47 +1,46 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class AttributesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Attributes_WhenFileIsExisting_SetterShouldChangeAttributesOnFileSystem( - string path, FileAttributes attributes) - { - FileSystem.Directory.CreateDirectory(path); - IDirectoryInfo sut1 = FileSystem.DirectoryInfo.New(path); - IDirectoryInfo sut2 = FileSystem.DirectoryInfo.New(path); - - sut1.Attributes = attributes; - FileAttributes expectedAttributes = sut1.Attributes; - - sut2.Attributes.Should().Be(expectedAttributes); - } - - [SkippableFact] - public void Attributes_WhenFileIsMissing_SetterShouldThrowFileNotFoundException() - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("missing file"); - - Exception? exception = Record.Exception(() => - { - sut.Attributes = FileAttributes.Normal; - }); - - exception.Should().BeException(hResult: -2147024894); - } - - [SkippableFact] - public void Attributes_WhenFileIsMissing_ShouldReturnMinusOne() - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("missing file"); - FileAttributes expected = (FileAttributes)(-1); - - sut.Attributes.Should().Be(expected); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class AttributesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Attributes_WhenFileIsExisting_SetterShouldChangeAttributesOnFileSystem( + string path, FileAttributes attributes) + { + FileSystem.Directory.CreateDirectory(path); + IDirectoryInfo sut1 = FileSystem.DirectoryInfo.New(path); + IDirectoryInfo sut2 = FileSystem.DirectoryInfo.New(path); + + sut1.Attributes = attributes; + FileAttributes expectedAttributes = sut1.Attributes; + + sut2.Attributes.Should().Be(expectedAttributes); + } + + [SkippableFact] + public void Attributes_WhenFileIsMissing_SetterShouldThrowFileNotFoundException() + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("missing file"); + + Exception? exception = Record.Exception(() => + { + sut.Attributes = FileAttributes.Normal; + }); + + exception.Should().BeException(hResult: -2147024894); + } + + [SkippableFact] + public void Attributes_WhenFileIsMissing_ShouldReturnMinusOne() + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("missing file"); + FileAttributes expected = (FileAttributes)(-1); + + sut.Attributes.Should().Be(expected); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/DeleteTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/DeleteTests.cs index 6fda2cb3a..ff4f3f64c 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/DeleteTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/DeleteTests.cs @@ -1,130 +1,129 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class DeleteTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException(string path) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - sut.Exists.Should().BeFalse(); - - Exception? exception = Record.Exception(() => - { - sut.Delete(); - }); - - exception.Should().BeException($"'{sut.FullName}'", - hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Delete_Recursive_WithOpenFile_ShouldThrowIOException( - string path, string filename) - { - FileSystem.Initialize() - .WithSubdirectory(path); - string filePath = FileSystem.Path.Combine(path, filename); - FileSystemStream openFile = FileSystem.File.OpenWrite(filePath); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - Exception? exception = Record.Exception(() => - { - sut.Delete(true); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException($"{filename}'", - hResult: -2147024864); - FileSystem.File.Exists(filePath).Should().BeTrue(); - } - else - { - exception.Should().BeNull(); - FileSystem.File.Exists(filePath).Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void Delete_Recursive_WithSubdirectory_ShouldDeleteDirectoryWithContent( - string path, string subdirectory) - { - string subdirectoryPath = FileSystem.Path.Combine(path, subdirectory); - FileSystem.Directory.CreateDirectory(subdirectoryPath); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - sut.Exists.Should().BeTrue(); - - sut.Delete(true); - -#if NETFRAMEWORK - // The DirectoryInfo is not updated in .NET Framework! - sut.Exists.Should().BeTrue(); -#else - sut.Exists.Should().BeFalse(); -#endif - FileSystem.Directory.Exists(sut.FullName).Should().BeFalse(); - FileSystem.Directory.Exists(subdirectoryPath).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Delete_ShouldDeleteDirectory(string path) - { - FileSystem.Directory.CreateDirectory(path); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - sut.Exists.Should().BeTrue(); - - sut.Delete(); - -#if NETFRAMEWORK - // The DirectoryInfo is not updated in .NET Framework! - sut.Exists.Should().BeTrue(); -#else - sut.Exists.Should().BeFalse(); -#endif - FileSystem.Directory.Exists(sut.FullName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Delete_WithSubdirectory_ShouldThrowIOException_AndNotDeleteDirectory( - string path, string subdirectory) - { - FileSystem.Directory.CreateDirectory(FileSystem.Path.Combine(path, subdirectory)); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - sut.Exists.Should().BeTrue(); - - Exception? exception = Record.Exception(() => - { - sut.Delete(); - }); - - exception.Should().BeException( - hResult: Test.RunsOnWindows ? -2147024751 : Test.RunsOnMac ? 66 : 39, - // Path information only included in exception message on Windows and not in .NET Framework - messageContains: !Test.RunsOnWindows || Test.IsNetFramework - ? null - : $"'{sut.FullName}'"); - - sut.Exists.Should().BeTrue(); - FileSystem.Directory.Exists(sut.FullName).Should().BeTrue(); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class DeleteTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException(string path) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + sut.Exists.Should().BeFalse(); + + Exception? exception = Record.Exception(() => + { + sut.Delete(); + }); + + exception.Should().BeException($"'{sut.FullName}'", + hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Delete_Recursive_WithOpenFile_ShouldThrowIOException( + string path, string filename) + { + FileSystem.Initialize() + .WithSubdirectory(path); + string filePath = FileSystem.Path.Combine(path, filename); + FileSystemStream openFile = FileSystem.File.OpenWrite(filePath); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + Exception? exception = Record.Exception(() => + { + sut.Delete(true); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException($"{filename}'", + hResult: -2147024864); + FileSystem.File.Exists(filePath).Should().BeTrue(); + } + else + { + exception.Should().BeNull(); + FileSystem.File.Exists(filePath).Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void Delete_Recursive_WithSubdirectory_ShouldDeleteDirectoryWithContent( + string path, string subdirectory) + { + string subdirectoryPath = FileSystem.Path.Combine(path, subdirectory); + FileSystem.Directory.CreateDirectory(subdirectoryPath); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + sut.Exists.Should().BeTrue(); + + sut.Delete(true); + +#if NETFRAMEWORK + // The DirectoryInfo is not updated in .NET Framework! + sut.Exists.Should().BeTrue(); +#else + sut.Exists.Should().BeFalse(); +#endif + FileSystem.Directory.Exists(sut.FullName).Should().BeFalse(); + FileSystem.Directory.Exists(subdirectoryPath).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Delete_ShouldDeleteDirectory(string path) + { + FileSystem.Directory.CreateDirectory(path); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + sut.Exists.Should().BeTrue(); + + sut.Delete(); + +#if NETFRAMEWORK + // The DirectoryInfo is not updated in .NET Framework! + sut.Exists.Should().BeTrue(); +#else + sut.Exists.Should().BeFalse(); +#endif + FileSystem.Directory.Exists(sut.FullName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Delete_WithSubdirectory_ShouldThrowIOException_AndNotDeleteDirectory( + string path, string subdirectory) + { + FileSystem.Directory.CreateDirectory(FileSystem.Path.Combine(path, subdirectory)); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + sut.Exists.Should().BeTrue(); + + Exception? exception = Record.Exception(() => + { + sut.Delete(); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147024751 : Test.RunsOnMac ? 66 : 39, + // Path information only included in exception message on Windows and not in .NET Framework + messageContains: !Test.RunsOnWindows || Test.IsNetFramework + ? null + : $"'{sut.FullName}'"); + + sut.Exists.Should().BeTrue(); + FileSystem.Directory.Exists(sut.FullName).Should().BeTrue(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateDirectoriesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateDirectoriesTests.cs index 92eb2a6f6..85f89c640 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateDirectoriesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateDirectoriesTests.cs @@ -1,171 +1,170 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class EnumerateDirectoriesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( - string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithSubdirectory("foo/xyz") - .WithSubdirectory("bar")); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IDirectoryInfo[] result = baseDirectory - .EnumerateDirectories("*", SearchOption.AllDirectories).ToArray(); - - result.Length.Should().Be(3); - result.Should().Contain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "bar"); - result.Should().Contain(d => d.Name == "xyz"); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void EnumerateDirectories_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string subdirectoryName) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory("foo"); - baseDirectory.CreateSubdirectory(subdirectoryName); - - IDirectoryInfo[] result = baseDirectory - .EnumerateDirectories(searchPattern).ToArray(); - - if (expectToBeFound) - { - result.Should().ContainSingle(d => d.Name == subdirectoryName, - $"it should match '{searchPattern}'"); - } - else - { - result.Should() - .BeEmpty($"{subdirectoryName} should not match '{searchPattern}'"); - } - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - IDirectoryInfo[] result = baseDirectory - .EnumerateDirectories("XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }).ToArray(); - - result.Length.Should().Be(1); - result.Should().NotContain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "xyz"); - result.Should().NotContain(d => d.Name == "bar"); - } -#endif - - [SkippableTheory] - [AutoData] - public void EnumerateDirectories_WithNewline_ShouldThrowArgumentException( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.DirectoryInfo.New(path); - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = baseDirectory.EnumerateDirectories(searchPattern).FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - IDirectoryInfo[] result = baseDirectory - .EnumerateDirectories().ToArray(); - - result.Length.Should().Be(2); - result.Should().Contain(d => d.Name == "foo"); - result.Should().NotContain(d => d.Name == "xyz"); - result.Should().Contain(d => d.Name == "bar"); - } - - [SkippableTheory] - [AutoData] - public void EnumerateDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo"); - baseDirectory.CreateSubdirectory("bar"); - - IEnumerable result = baseDirectory - .EnumerateDirectories("foo"); - - result.Should().ContainSingle(d => d.Name == "foo"); - } - - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar/xyz"); - - IEnumerable result = baseDirectory - .EnumerateDirectories("xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(2); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class EnumerateDirectoriesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( + string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithSubdirectory("foo/xyz") + .WithSubdirectory("bar")); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IDirectoryInfo[] result = baseDirectory + .EnumerateDirectories("*", SearchOption.AllDirectories).ToArray(); + + result.Length.Should().Be(3); + result.Should().Contain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "bar"); + result.Should().Contain(d => d.Name == "xyz"); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void EnumerateDirectories_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string subdirectoryName) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory("foo"); + baseDirectory.CreateSubdirectory(subdirectoryName); + + IDirectoryInfo[] result = baseDirectory + .EnumerateDirectories(searchPattern).ToArray(); + + if (expectToBeFound) + { + result.Should().ContainSingle(d => d.Name == subdirectoryName, + $"it should match '{searchPattern}'"); + } + else + { + result.Should() + .BeEmpty($"{subdirectoryName} should not match '{searchPattern}'"); + } + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + IDirectoryInfo[] result = baseDirectory + .EnumerateDirectories("XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }).ToArray(); + + result.Length.Should().Be(1); + result.Should().NotContain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "xyz"); + result.Should().NotContain(d => d.Name == "bar"); + } +#endif + + [SkippableTheory] + [AutoData] + public void EnumerateDirectories_WithNewline_ShouldThrowArgumentException( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.DirectoryInfo.New(path); + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = baseDirectory.EnumerateDirectories(searchPattern).FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + IDirectoryInfo[] result = baseDirectory + .EnumerateDirectories().ToArray(); + + result.Length.Should().Be(2); + result.Should().Contain(d => d.Name == "foo"); + result.Should().NotContain(d => d.Name == "xyz"); + result.Should().Contain(d => d.Name == "bar"); + } + + [SkippableTheory] + [AutoData] + public void EnumerateDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo"); + baseDirectory.CreateSubdirectory("bar"); + + IEnumerable result = baseDirectory + .EnumerateDirectories("foo"); + + result.Should().ContainSingle(d => d.Name == "foo"); + } + + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar/xyz"); + + IEnumerable result = baseDirectory + .EnumerateDirectories("xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(2); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFileSystemInfosTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFileSystemInfosTests.cs index 9727335be..333cd052a 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFileSystemInfosTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFileSystemInfosTests.cs @@ -1,205 +1,204 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class EnumerateFileSystemInfosTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - EnumerateFileSystemInfos_SearchOptionAllFiles_ShouldReturnAllFiles( - string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithASubdirectory().Initialized(d => d - .WithAFile() - .WithAFile()) - .WithASubdirectory() - .WithAFile()); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IFileSystemInfo[] result = baseDirectory - .EnumerateFileSystemInfos("*", SearchOption.AllDirectories).ToArray(); - - result.Length.Should().Be(5); - result.Should().Contain(d => d.Name == initialized[1].Name); - result.Should().Contain(d => d.Name == initialized[2].Name); - result.Should().Contain(d => d.Name == initialized[3].Name); - result.Should().Contain(d => d.Name == initialized[4].Name); - result.Should().Contain(d => d.Name == initialized[5].Name); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void EnumerateFileSystemInfos_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string fileName) - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile(fileName) - .BaseDirectory; - - IFileSystemInfo[] result = baseDirectory - .EnumerateFileSystemInfos(searchPattern).ToArray(); - - if (expectToBeFound) - { - result.Should().ContainSingle(d => d.Name == fileName, - $"it should match '{searchPattern}'"); - } - else - { - result.Should() - .BeEmpty($"{fileName} should not match '{searchPattern}'"); - } - } - - [SkippableTheory] - [AutoData] - public void - EnumerateFileSystemInfos_ShouldMatchTypes(string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithASubdirectory() - .WithAFile()); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IFileSystemInfo[] result = baseDirectory - .EnumerateFileSystemInfos("*").ToArray(); - - result.Length.Should().Be(2); - result.Should().Contain(d - => d.Name == initialized[1].Name && d is IDirectoryInfo); - result.Should().Contain(d - => d.Name == initialized[2].Name && d is IFileInfo); - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableFact] - public void - EnumerateFileSystemInfos_WithEnumerationOptions_ShouldConsiderSetOptions() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithAFile() - .BaseDirectory; - - IFileSystemInfo[] result = baseDirectory - .EnumerateFileSystemInfos("XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }).ToArray(); - - result.Length.Should().Be(1); - result.Should().NotContain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "xyz"); - result.Should().NotContain(d => d.Name == "bar"); - } -#endif - - [SkippableTheory] - [AutoData] - public void EnumerateFileSystemInfos_WithNewline_ShouldThrowArgumentException( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.DirectoryInfo.New(path); - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = baseDirectory.EnumerateFileSystemInfos(searchPattern).FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableFact] - public void - EnumerateFileSystemInfos_WithoutSearchString_ShouldReturnAllDirectFilesAndDirectories() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithSubdirectory("muh").Initialized(s => s - .WithFile("xyz")) - .WithFile("bar") - .BaseDirectory; - - IFileSystemInfo[] result = baseDirectory - .EnumerateFileSystemInfos().ToArray(); - - result.Length.Should().Be(3); - result.Should().Contain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "muh"); - result.Should().NotContain(d => d.Name == "xyz"); - result.Should().Contain(d => d.Name == "bar"); - } - - [SkippableFact] - public void EnumerateFileSystemInfos_WithSearchPattern_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithFile("bar") - .BaseDirectory; - - IEnumerable result = baseDirectory - .EnumerateFileSystemInfos("foo").ToArray(); - - result.Should().ContainSingle(d => d.Name == "foo"); - result.Count().Should().Be(1); - } - - [SkippableFact] - public void - EnumerateFileSystemInfos_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithSubdirectory("xyz").Initialized(s => s - .WithAFile()) - .BaseDirectory; - - IEnumerable result = baseDirectory - .EnumerateFileSystemInfos("xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(3); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class EnumerateFileSystemInfosTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + EnumerateFileSystemInfos_SearchOptionAllFiles_ShouldReturnAllFiles( + string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithASubdirectory().Initialized(d => d + .WithAFile() + .WithAFile()) + .WithASubdirectory() + .WithAFile()); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IFileSystemInfo[] result = baseDirectory + .EnumerateFileSystemInfos("*", SearchOption.AllDirectories).ToArray(); + + result.Length.Should().Be(5); + result.Should().Contain(d => d.Name == initialized[1].Name); + result.Should().Contain(d => d.Name == initialized[2].Name); + result.Should().Contain(d => d.Name == initialized[3].Name); + result.Should().Contain(d => d.Name == initialized[4].Name); + result.Should().Contain(d => d.Name == initialized[5].Name); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void EnumerateFileSystemInfos_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string fileName) + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile(fileName) + .BaseDirectory; + + IFileSystemInfo[] result = baseDirectory + .EnumerateFileSystemInfos(searchPattern).ToArray(); + + if (expectToBeFound) + { + result.Should().ContainSingle(d => d.Name == fileName, + $"it should match '{searchPattern}'"); + } + else + { + result.Should() + .BeEmpty($"{fileName} should not match '{searchPattern}'"); + } + } + + [SkippableTheory] + [AutoData] + public void + EnumerateFileSystemInfos_ShouldMatchTypes(string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithASubdirectory() + .WithAFile()); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IFileSystemInfo[] result = baseDirectory + .EnumerateFileSystemInfos("*").ToArray(); + + result.Length.Should().Be(2); + result.Should().Contain(d + => d.Name == initialized[1].Name && d is IDirectoryInfo); + result.Should().Contain(d + => d.Name == initialized[2].Name && d is IFileInfo); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void + EnumerateFileSystemInfos_WithEnumerationOptions_ShouldConsiderSetOptions() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithAFile() + .BaseDirectory; + + IFileSystemInfo[] result = baseDirectory + .EnumerateFileSystemInfos("XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }).ToArray(); + + result.Length.Should().Be(1); + result.Should().NotContain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "xyz"); + result.Should().NotContain(d => d.Name == "bar"); + } +#endif + + [SkippableTheory] + [AutoData] + public void EnumerateFileSystemInfos_WithNewline_ShouldThrowArgumentException( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.DirectoryInfo.New(path); + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = baseDirectory.EnumerateFileSystemInfos(searchPattern).FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableFact] + public void + EnumerateFileSystemInfos_WithoutSearchString_ShouldReturnAllDirectFilesAndDirectories() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithSubdirectory("muh").Initialized(s => s + .WithFile("xyz")) + .WithFile("bar") + .BaseDirectory; + + IFileSystemInfo[] result = baseDirectory + .EnumerateFileSystemInfos().ToArray(); + + result.Length.Should().Be(3); + result.Should().Contain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "muh"); + result.Should().NotContain(d => d.Name == "xyz"); + result.Should().Contain(d => d.Name == "bar"); + } + + [SkippableFact] + public void EnumerateFileSystemInfos_WithSearchPattern_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithFile("bar") + .BaseDirectory; + + IEnumerable result = baseDirectory + .EnumerateFileSystemInfos("foo").ToArray(); + + result.Should().ContainSingle(d => d.Name == "foo"); + result.Count().Should().Be(1); + } + + [SkippableFact] + public void + EnumerateFileSystemInfos_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithSubdirectory("xyz").Initialized(s => s + .WithAFile()) + .BaseDirectory; + + IEnumerable result = baseDirectory + .EnumerateFileSystemInfos("xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(3); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFilesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFilesTests.cs index 53e8fbb92..a9f957b67 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFilesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFilesTests.cs @@ -1,179 +1,178 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class EnumerateFilesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - EnumerateFiles_SearchOptionAllFiles_ShouldReturnAllFiles( - string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithASubdirectory().Initialized(d => d - .WithAFile() - .WithAFile()) - .WithASubdirectory() - .WithAFile()); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IFileInfo[] result = baseDirectory - .EnumerateFiles("*", SearchOption.AllDirectories).ToArray(); - - result.Length.Should().Be(3); - result.Should().Contain(d => d.Name == initialized[2].Name); - result.Should().Contain(d => d.Name == initialized[3].Name); - result.Should().Contain(d => d.Name == initialized[5].Name); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void EnumerateFiles_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string fileName) - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile(fileName) - .BaseDirectory; - - IFileInfo[] result = baseDirectory - .EnumerateFiles(searchPattern).ToArray(); - - if (expectToBeFound) - { - result.Should().ContainSingle(d => d.Name == fileName, - $"it should match '{searchPattern}'"); - } - else - { - result.Should() - .BeEmpty($"{fileName} should not match '{searchPattern}'"); - } - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableFact] - public void - EnumerateFiles_WithEnumerationOptions_ShouldConsiderSetOptions() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithAFile() - .BaseDirectory; - - IFileInfo[] result = baseDirectory - .EnumerateFiles("XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }).ToArray(); - - result.Length.Should().Be(1); - result.Should().NotContain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "xyz"); - result.Should().NotContain(d => d.Name == "bar"); - } -#endif - - [SkippableTheory] - [AutoData] - public void EnumerateFiles_WithNewline_ShouldThrowArgumentException( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.DirectoryInfo.New(path); - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = baseDirectory.EnumerateFiles(searchPattern).FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableFact] - public void - EnumerateFiles_WithoutSearchString_ShouldReturnAllDirectFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithFile("bar") - .BaseDirectory; - - IFileInfo[] result = baseDirectory - .EnumerateFiles().ToArray(); - - result.Length.Should().Be(2); - result.Should().Contain(d => d.Name == "foo"); - result.Should().NotContain(d => d.Name == "xyz"); - result.Should().Contain(d => d.Name == "bar"); - } - - [SkippableFact] - public void EnumerateFiles_WithSearchPattern_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithFile("bar") - .BaseDirectory; - - IEnumerable result = baseDirectory - .EnumerateFiles("foo").ToArray(); - - result.Should().ContainSingle(d => d.Name == "foo"); - result.Count().Should().Be(1); - } - - [SkippableFact] - public void - EnumerateFiles_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithSubdirectory("xyz").Initialized(s => s - .WithAFile()) - .BaseDirectory; - - IEnumerable result = baseDirectory - .EnumerateFiles("xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(2); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class EnumerateFilesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + EnumerateFiles_SearchOptionAllFiles_ShouldReturnAllFiles( + string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithASubdirectory().Initialized(d => d + .WithAFile() + .WithAFile()) + .WithASubdirectory() + .WithAFile()); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IFileInfo[] result = baseDirectory + .EnumerateFiles("*", SearchOption.AllDirectories).ToArray(); + + result.Length.Should().Be(3); + result.Should().Contain(d => d.Name == initialized[2].Name); + result.Should().Contain(d => d.Name == initialized[3].Name); + result.Should().Contain(d => d.Name == initialized[5].Name); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void EnumerateFiles_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string fileName) + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile(fileName) + .BaseDirectory; + + IFileInfo[] result = baseDirectory + .EnumerateFiles(searchPattern).ToArray(); + + if (expectToBeFound) + { + result.Should().ContainSingle(d => d.Name == fileName, + $"it should match '{searchPattern}'"); + } + else + { + result.Should() + .BeEmpty($"{fileName} should not match '{searchPattern}'"); + } + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void + EnumerateFiles_WithEnumerationOptions_ShouldConsiderSetOptions() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithAFile() + .BaseDirectory; + + IFileInfo[] result = baseDirectory + .EnumerateFiles("XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }).ToArray(); + + result.Length.Should().Be(1); + result.Should().NotContain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "xyz"); + result.Should().NotContain(d => d.Name == "bar"); + } +#endif + + [SkippableTheory] + [AutoData] + public void EnumerateFiles_WithNewline_ShouldThrowArgumentException( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.DirectoryInfo.New(path); + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = baseDirectory.EnumerateFiles(searchPattern).FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableFact] + public void + EnumerateFiles_WithoutSearchString_ShouldReturnAllDirectFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithFile("bar") + .BaseDirectory; + + IFileInfo[] result = baseDirectory + .EnumerateFiles().ToArray(); + + result.Length.Should().Be(2); + result.Should().Contain(d => d.Name == "foo"); + result.Should().NotContain(d => d.Name == "xyz"); + result.Should().Contain(d => d.Name == "bar"); + } + + [SkippableFact] + public void EnumerateFiles_WithSearchPattern_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithFile("bar") + .BaseDirectory; + + IEnumerable result = baseDirectory + .EnumerateFiles("foo").ToArray(); + + result.Should().ContainSingle(d => d.Name == "foo"); + result.Count().Should().Be(1); + } + + [SkippableFact] + public void + EnumerateFiles_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithSubdirectory("xyz").Initialized(s => s + .WithAFile()) + .BaseDirectory; + + IEnumerable result = baseDirectory + .EnumerateFiles("xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(2); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/ExceptionTests.cs index fa2eec027..962b49095 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/ExceptionTests.cs @@ -1,151 +1,150 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetDirectoryInfoCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetDirectoryInfoCallbacks), parameters: " ")] - public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); - }); - - if (Test.IsNetFramework) - { - exception.Should() - .BeNull( - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should().BeException( - hResult: -2147024809, - paramName: paramName, - because: - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - - [Theory] - [MemberData(nameof(GetDirectoryInfoCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetDirectoryInfoCallbacks), - parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); - }); - - if (!Test.RunsOnWindows) - { - if (exception is IOException ioException) - { - ioException.HResult.Should().NotBe(-2147024809, - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - else - { - if (Test.IsNetFramework) - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should().BeException( - hResult: -2147024773, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - } - - #region Helpers - - public static IEnumerable GetDirectoryInfoCallbacks(string? path) - => GetDirectoryInfoCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetDirectoryInfoCallbackTestParameters(string value) - { - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.InvalidPath, "path", - directoryInfo - => directoryInfo.CreateSubdirectory(value)); -#if FEATURE_FILESYSTEM_LINK - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "pathToTarget", - directoryInfo - => directoryInfo.CreateAsSymbolicLink(value)); -#endif - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destDirName", - directoryInfo - => directoryInfo.MoveTo(value)); - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetDirectoryInfoCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetDirectoryInfoCallbacks), parameters: " ")] + public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); + }); + + if (Test.IsNetFramework) + { + exception.Should() + .BeNull( + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should().BeException( + hResult: -2147024809, + paramName: paramName, + because: + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + + [Theory] + [MemberData(nameof(GetDirectoryInfoCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetDirectoryInfoCallbacks), + parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); + }); + + if (!Test.RunsOnWindows) + { + if (exception is IOException ioException) + { + ioException.HResult.Should().NotBe(-2147024809, + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + else + { + if (Test.IsNetFramework) + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should().BeException( + hResult: -2147024773, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + } + + #region Helpers + + public static IEnumerable GetDirectoryInfoCallbacks(string? path) + => GetDirectoryInfoCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetDirectoryInfoCallbackTestParameters(string value) + { + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.InvalidPath, "path", + directoryInfo + => directoryInfo.CreateSubdirectory(value)); +#if FEATURE_FILESYSTEM_LINK + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "pathToTarget", + directoryInfo + => directoryInfo.CreateAsSymbolicLink(value)); +#endif + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destDirName", + directoryInfo + => directoryInfo.MoveTo(value)); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetDirectoriesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetDirectoriesTests.cs index d3c8318ae..6849c81f9 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetDirectoriesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetDirectoriesTests.cs @@ -1,171 +1,170 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class GetDirectoriesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - GetDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( - string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithSubdirectory("foo/xyz") - .WithSubdirectory("bar")); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IDirectoryInfo[] result = baseDirectory - .GetDirectories("*", SearchOption.AllDirectories); - - result.Length.Should().Be(3); - result.Should().Contain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "bar"); - result.Should().Contain(d => d.Name == "xyz"); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void GetDirectories_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string subdirectoryName) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory("foo"); - baseDirectory.CreateSubdirectory(subdirectoryName); - - IDirectoryInfo[] result = baseDirectory - .GetDirectories(searchPattern); - - if (expectToBeFound) - { - result.Should().ContainSingle(d => d.Name == subdirectoryName, - $"it should match '{searchPattern}'"); - } - else - { - result.Should() - .BeEmpty($"{subdirectoryName} should not match '{searchPattern}'"); - } - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableTheory] - [AutoData] - public void - GetDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - IDirectoryInfo[] result = baseDirectory - .GetDirectories("XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }); - - result.Length.Should().Be(1); - result.Should().NotContain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "xyz"); - result.Should().NotContain(d => d.Name == "bar"); - } -#endif - - [SkippableTheory] - [AutoData] - public void GetDirectories_WithNewline_ShouldThrowArgumentException( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.DirectoryInfo.New(path); - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = baseDirectory.GetDirectories(searchPattern).FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableTheory] - [AutoData] - public void - GetDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - IDirectoryInfo[] result = baseDirectory - .GetDirectories(); - - result.Length.Should().Be(2); - result.Should().Contain(d => d.Name == "foo"); - result.Should().NotContain(d => d.Name == "xyz"); - result.Should().Contain(d => d.Name == "bar"); - } - - [SkippableTheory] - [AutoData] - public void GetDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo"); - baseDirectory.CreateSubdirectory("bar"); - - IEnumerable result = baseDirectory - .GetDirectories("foo"); - - result.Should().ContainSingle(d => d.Name == "foo"); - } - - [SkippableTheory] - [AutoData] - public void - GetDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar/xyz"); - - IEnumerable result = baseDirectory - .GetDirectories("xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(2); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class GetDirectoriesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + GetDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( + string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithSubdirectory("foo/xyz") + .WithSubdirectory("bar")); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IDirectoryInfo[] result = baseDirectory + .GetDirectories("*", SearchOption.AllDirectories); + + result.Length.Should().Be(3); + result.Should().Contain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "bar"); + result.Should().Contain(d => d.Name == "xyz"); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void GetDirectories_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string subdirectoryName) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory("foo"); + baseDirectory.CreateSubdirectory(subdirectoryName); + + IDirectoryInfo[] result = baseDirectory + .GetDirectories(searchPattern); + + if (expectToBeFound) + { + result.Should().ContainSingle(d => d.Name == subdirectoryName, + $"it should match '{searchPattern}'"); + } + else + { + result.Should() + .BeEmpty($"{subdirectoryName} should not match '{searchPattern}'"); + } + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableTheory] + [AutoData] + public void + GetDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + IDirectoryInfo[] result = baseDirectory + .GetDirectories("XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }); + + result.Length.Should().Be(1); + result.Should().NotContain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "xyz"); + result.Should().NotContain(d => d.Name == "bar"); + } +#endif + + [SkippableTheory] + [AutoData] + public void GetDirectories_WithNewline_ShouldThrowArgumentException( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.DirectoryInfo.New(path); + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = baseDirectory.GetDirectories(searchPattern).FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableTheory] + [AutoData] + public void + GetDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + IDirectoryInfo[] result = baseDirectory + .GetDirectories(); + + result.Length.Should().Be(2); + result.Should().Contain(d => d.Name == "foo"); + result.Should().NotContain(d => d.Name == "xyz"); + result.Should().Contain(d => d.Name == "bar"); + } + + [SkippableTheory] + [AutoData] + public void GetDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo"); + baseDirectory.CreateSubdirectory("bar"); + + IEnumerable result = baseDirectory + .GetDirectories("foo"); + + result.Should().ContainSingle(d => d.Name == "foo"); + } + + [SkippableTheory] + [AutoData] + public void + GetDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar/xyz"); + + IEnumerable result = baseDirectory + .GetDirectories("xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(2); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFileSystemInfosTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFileSystemInfosTests.cs index 2455e462d..fd491eaff 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFileSystemInfosTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFileSystemInfosTests.cs @@ -1,205 +1,204 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class GetFileSystemInfosTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - GetFileSystemInfos_SearchOptionAllFiles_ShouldReturnAllFiles( - string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithASubdirectory().Initialized(d => d - .WithAFile() - .WithAFile()) - .WithASubdirectory() - .WithAFile()); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IFileSystemInfo[] result = baseDirectory - .GetFileSystemInfos("*", SearchOption.AllDirectories); - - result.Length.Should().Be(5); - result.Should().Contain(d => d.Name == initialized[1].Name); - result.Should().Contain(d => d.Name == initialized[2].Name); - result.Should().Contain(d => d.Name == initialized[3].Name); - result.Should().Contain(d => d.Name == initialized[4].Name); - result.Should().Contain(d => d.Name == initialized[5].Name); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void GetFileSystemInfos_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string fileName) - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile(fileName) - .BaseDirectory; - - IFileSystemInfo[] result = baseDirectory - .GetFileSystemInfos(searchPattern); - - if (expectToBeFound) - { - result.Should().ContainSingle(d => d.Name == fileName, - $"it should match '{searchPattern}'"); - } - else - { - result.Should() - .BeEmpty($"{fileName} should not match '{searchPattern}'"); - } - } - - [SkippableTheory] - [AutoData] - public void - GetFileSystemInfos_ShouldMatchTypes(string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithASubdirectory() - .WithAFile()); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IFileSystemInfo[] result = baseDirectory - .GetFileSystemInfos("*"); - - result.Length.Should().Be(2); - result.Should().Contain(d - => d.Name == initialized[1].Name && d is IDirectoryInfo); - result.Should().Contain(d - => d.Name == initialized[2].Name && d is IFileInfo); - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableFact] - public void - GetFileSystemInfos_WithEnumerationOptions_ShouldConsiderSetOptions() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithAFile() - .BaseDirectory; - - IFileSystemInfo[] result = baseDirectory - .GetFileSystemInfos("XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }); - - result.Length.Should().Be(1); - result.Should().NotContain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "xyz"); - result.Should().NotContain(d => d.Name == "bar"); - } -#endif - - [SkippableTheory] - [AutoData] - public void GetFileSystemInfos_WithNewline_ShouldThrowArgumentException( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.DirectoryInfo.New(path); - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = baseDirectory.GetFileSystemInfos(searchPattern).FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableFact] - public void - GetFileSystemInfos_WithoutSearchString_ShouldReturnAllDirectFilesAndDirectories() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithSubdirectory("muh").Initialized(s => s - .WithFile("xyz")) - .WithFile("bar") - .BaseDirectory; - - IFileSystemInfo[] result = baseDirectory - .GetFileSystemInfos(); - - result.Length.Should().Be(3); - result.Should().Contain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "muh"); - result.Should().NotContain(d => d.Name == "xyz"); - result.Should().Contain(d => d.Name == "bar"); - } - - [SkippableFact] - public void GetFileSystemInfos_WithSearchPattern_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithFile("bar") - .BaseDirectory; - - IEnumerable result = baseDirectory - .GetFileSystemInfos("foo"); - - result.Should().ContainSingle(d => d.Name == "foo"); - result.Count().Should().Be(1); - } - - [SkippableFact] - public void - GetFileSystemInfos_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithSubdirectory("xyz").Initialized(s => s - .WithAFile()) - .BaseDirectory; - - IEnumerable result = baseDirectory - .GetFileSystemInfos("xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(3); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class GetFileSystemInfosTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + GetFileSystemInfos_SearchOptionAllFiles_ShouldReturnAllFiles( + string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithASubdirectory().Initialized(d => d + .WithAFile() + .WithAFile()) + .WithASubdirectory() + .WithAFile()); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IFileSystemInfo[] result = baseDirectory + .GetFileSystemInfos("*", SearchOption.AllDirectories); + + result.Length.Should().Be(5); + result.Should().Contain(d => d.Name == initialized[1].Name); + result.Should().Contain(d => d.Name == initialized[2].Name); + result.Should().Contain(d => d.Name == initialized[3].Name); + result.Should().Contain(d => d.Name == initialized[4].Name); + result.Should().Contain(d => d.Name == initialized[5].Name); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void GetFileSystemInfos_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string fileName) + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile(fileName) + .BaseDirectory; + + IFileSystemInfo[] result = baseDirectory + .GetFileSystemInfos(searchPattern); + + if (expectToBeFound) + { + result.Should().ContainSingle(d => d.Name == fileName, + $"it should match '{searchPattern}'"); + } + else + { + result.Should() + .BeEmpty($"{fileName} should not match '{searchPattern}'"); + } + } + + [SkippableTheory] + [AutoData] + public void + GetFileSystemInfos_ShouldMatchTypes(string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithASubdirectory() + .WithAFile()); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IFileSystemInfo[] result = baseDirectory + .GetFileSystemInfos("*"); + + result.Length.Should().Be(2); + result.Should().Contain(d + => d.Name == initialized[1].Name && d is IDirectoryInfo); + result.Should().Contain(d + => d.Name == initialized[2].Name && d is IFileInfo); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void + GetFileSystemInfos_WithEnumerationOptions_ShouldConsiderSetOptions() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithAFile() + .BaseDirectory; + + IFileSystemInfo[] result = baseDirectory + .GetFileSystemInfos("XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }); + + result.Length.Should().Be(1); + result.Should().NotContain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "xyz"); + result.Should().NotContain(d => d.Name == "bar"); + } +#endif + + [SkippableTheory] + [AutoData] + public void GetFileSystemInfos_WithNewline_ShouldThrowArgumentException( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.DirectoryInfo.New(path); + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = baseDirectory.GetFileSystemInfos(searchPattern).FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableFact] + public void + GetFileSystemInfos_WithoutSearchString_ShouldReturnAllDirectFilesAndDirectories() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithSubdirectory("muh").Initialized(s => s + .WithFile("xyz")) + .WithFile("bar") + .BaseDirectory; + + IFileSystemInfo[] result = baseDirectory + .GetFileSystemInfos(); + + result.Length.Should().Be(3); + result.Should().Contain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "muh"); + result.Should().NotContain(d => d.Name == "xyz"); + result.Should().Contain(d => d.Name == "bar"); + } + + [SkippableFact] + public void GetFileSystemInfos_WithSearchPattern_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithFile("bar") + .BaseDirectory; + + IEnumerable result = baseDirectory + .GetFileSystemInfos("foo"); + + result.Should().ContainSingle(d => d.Name == "foo"); + result.Count().Should().Be(1); + } + + [SkippableFact] + public void + GetFileSystemInfos_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithSubdirectory("xyz").Initialized(s => s + .WithAFile()) + .BaseDirectory; + + IEnumerable result = baseDirectory + .GetFileSystemInfos("xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(3); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFilesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFilesTests.cs index bf776d24a..37d71e9e0 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFilesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFilesTests.cs @@ -1,179 +1,178 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class GetFilesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - GetFiles_SearchOptionAllFiles_ShouldReturnAllFiles( - string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithASubdirectory().Initialized(d => d - .WithAFile() - .WithAFile()) - .WithASubdirectory() - .WithAFile()); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IFileInfo[] result = baseDirectory - .GetFiles("*", SearchOption.AllDirectories); - - result.Length.Should().Be(3); - result.Should().Contain(d => d.Name == initialized[2].Name); - result.Should().Contain(d => d.Name == initialized[3].Name); - result.Should().Contain(d => d.Name == initialized[5].Name); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void GetFiles_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string fileName) - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile(fileName) - .BaseDirectory; - - IFileInfo[] result = baseDirectory - .GetFiles(searchPattern); - - if (expectToBeFound) - { - result.Should().ContainSingle(d => d.Name == fileName, - $"it should match '{searchPattern}'"); - } - else - { - result.Should() - .BeEmpty($"{fileName} should not match '{searchPattern}'"); - } - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableFact] - public void - GetFiles_WithEnumerationOptions_ShouldConsiderSetOptions() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithAFile() - .BaseDirectory; - - IFileInfo[] result = baseDirectory - .GetFiles("XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }); - - result.Length.Should().Be(1); - result.Should().NotContain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "xyz"); - result.Should().NotContain(d => d.Name == "bar"); - } -#endif - - [SkippableTheory] - [AutoData] - public void GetFiles_WithNewline_ShouldThrowArgumentException( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.DirectoryInfo.New(path); - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = baseDirectory.GetFiles(searchPattern).FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableFact] - public void - GetFiles_WithoutSearchString_ShouldReturnAllDirectFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithFile("bar") - .BaseDirectory; - - IFileInfo[] result = baseDirectory - .GetFiles(); - - result.Length.Should().Be(2); - result.Should().Contain(d => d.Name == "foo"); - result.Should().NotContain(d => d.Name == "xyz"); - result.Should().Contain(d => d.Name == "bar"); - } - - [SkippableFact] - public void GetFiles_WithSearchPattern_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithFile("bar") - .BaseDirectory; - - IEnumerable result = baseDirectory - .GetFiles("foo"); - - result.Should().ContainSingle(d => d.Name == "foo"); - result.Count().Should().Be(1); - } - - [SkippableFact] - public void - GetFiles_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithSubdirectory("xyz").Initialized(s => s - .WithAFile()) - .BaseDirectory; - - IEnumerable result = baseDirectory - .GetFiles("xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(2); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class GetFilesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + GetFiles_SearchOptionAllFiles_ShouldReturnAllFiles( + string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithASubdirectory().Initialized(d => d + .WithAFile() + .WithAFile()) + .WithASubdirectory() + .WithAFile()); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IFileInfo[] result = baseDirectory + .GetFiles("*", SearchOption.AllDirectories); + + result.Length.Should().Be(3); + result.Should().Contain(d => d.Name == initialized[2].Name); + result.Should().Contain(d => d.Name == initialized[3].Name); + result.Should().Contain(d => d.Name == initialized[5].Name); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void GetFiles_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string fileName) + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile(fileName) + .BaseDirectory; + + IFileInfo[] result = baseDirectory + .GetFiles(searchPattern); + + if (expectToBeFound) + { + result.Should().ContainSingle(d => d.Name == fileName, + $"it should match '{searchPattern}'"); + } + else + { + result.Should() + .BeEmpty($"{fileName} should not match '{searchPattern}'"); + } + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void + GetFiles_WithEnumerationOptions_ShouldConsiderSetOptions() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithAFile() + .BaseDirectory; + + IFileInfo[] result = baseDirectory + .GetFiles("XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }); + + result.Length.Should().Be(1); + result.Should().NotContain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "xyz"); + result.Should().NotContain(d => d.Name == "bar"); + } +#endif + + [SkippableTheory] + [AutoData] + public void GetFiles_WithNewline_ShouldThrowArgumentException( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.DirectoryInfo.New(path); + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = baseDirectory.GetFiles(searchPattern).FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableFact] + public void + GetFiles_WithoutSearchString_ShouldReturnAllDirectFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithFile("bar") + .BaseDirectory; + + IFileInfo[] result = baseDirectory + .GetFiles(); + + result.Length.Should().Be(2); + result.Should().Contain(d => d.Name == "foo"); + result.Should().NotContain(d => d.Name == "xyz"); + result.Should().Contain(d => d.Name == "bar"); + } + + [SkippableFact] + public void GetFiles_WithSearchPattern_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithFile("bar") + .BaseDirectory; + + IEnumerable result = baseDirectory + .GetFiles("foo"); + + result.Should().ContainSingle(d => d.Name == "foo"); + result.Count().Should().Be(1); + } + + [SkippableFact] + public void + GetFiles_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithSubdirectory("xyz").Initialized(s => s + .WithAFile()) + .BaseDirectory; + + IEnumerable result = baseDirectory + .GetFiles("xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(2); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/MoveToTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/MoveToTests.cs index 692133445..816d2f7b0 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/MoveToTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/MoveToTests.cs @@ -1,187 +1,186 @@ -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystemInitializer; -#if !NETFRAMEWORK -#endif - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class MoveToTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void MoveTo_ShouldMoveDirectoryWithContent(string source, string destination) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); - - sut.MoveTo(destination); - - FileSystem.Directory.Exists(source).Should().BeFalse(); - FileSystem.Directory.Exists(destination).Should().BeTrue(); - FileSystem.Directory.GetFiles(destination, initialized[1].Name) - .Should().ContainSingle(); - FileSystem.Directory.GetDirectories(destination, initialized[2].Name) - .Should().ContainSingle(); - FileSystem.Directory.GetFiles(destination, initialized[3].Name, - SearchOption.AllDirectories) - .Should().ContainSingle(); - FileSystem.Directory.GetDirectories(destination, initialized[4].Name, - SearchOption.AllDirectories) - .Should().ContainSingle(); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_ShouldUpdatePropertiesOfDirectoryInfo( - string source, string destination) - { - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); - - sut.MoveTo(destination); - - sut.FullName.TrimEnd(FileSystem.Path.DirectorySeparatorChar) - .Should().Be(FileSystem.Path.GetFullPath(destination)); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_WithLockedFile_ShouldThrowIOException_AndNotMoveDirectory_OnWindows( - string source, string destination) - { - Skip.IfNot(Test.RunsOnWindows); - - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); - using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, - FileMode.Open, - FileAccess.Read, - FileShare.Read); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(destination); - }); - - if (Test.IsNetFramework) - { - // On .NET Framework the HResult is "-2146232800", but only in `DirectoryInfo.MoveTo` (not in `Directory.Move`) - // This peculiar deviation is not supported by the FileSystemMock. - exception.Should().BeException(); - } - else - { - exception.Should().BeException(hResult: -2147024891); - } - - FileSystem.Directory.Exists(source).Should().BeTrue(); - FileSystem.Directory.Exists(destination).Should().BeFalse(); - IDirectoryInfo sourceDirectory = - FileSystem.DirectoryInfo.New(source); - sourceDirectory.GetFiles(initialized[1].Name) - .Should().ContainSingle(); - sourceDirectory.GetDirectories(initialized[2].Name) - .Should().ContainSingle(); - sourceDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - sourceDirectory - .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_WithLockedFile_ShouldMoveDirectory_NotOnWindows( - string source, string destination) - { - Skip.If(Test.RunsOnWindows); - - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); - using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, - FileMode.Open, - FileAccess.Read, - FileShare.Read); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(destination); - }); - - exception.Should().BeNull(); - FileSystem.Directory.Exists(source).Should().BeFalse(); - FileSystem.Directory.Exists(destination).Should().BeTrue(); - IDirectoryInfo destinationDirectory = - FileSystem.DirectoryInfo.New(destination); - destinationDirectory.GetFiles(initialized[1].Name) - .Should().ContainSingle(); - destinationDirectory.GetDirectories(initialized[2].Name) - .Should().ContainSingle(); - destinationDirectory - .GetFiles(initialized[3].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - destinationDirectory - .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_WithReadOnlyFile_ShouldMoveDirectoryWithContent( - string source, string destination) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - initialized[3].Attributes = FileAttributes.ReadOnly; - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); - - sut.MoveTo(destination); - - FileSystem.Directory.Exists(source).Should().BeFalse(); - FileSystem.Directory.Exists(destination).Should().BeTrue(); - IDirectoryInfo destinationDirectory = - FileSystem.DirectoryInfo.New(destination); - destinationDirectory.GetFiles(initialized[1].Name) - .Should().ContainSingle(); - destinationDirectory.GetDirectories(initialized[2].Name) - .Should().ContainSingle(); - destinationDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) - .Should().ContainSingle().Which.Attributes.Should() - .HaveFlag(FileAttributes.ReadOnly); - destinationDirectory - .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - } -} +using System.IO; +using Testably.Abstractions.Testing.FileSystemInitializer; +#if !NETFRAMEWORK +#endif + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class MoveToTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void MoveTo_ShouldMoveDirectoryWithContent(string source, string destination) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); + + sut.MoveTo(destination); + + FileSystem.Directory.Exists(source).Should().BeFalse(); + FileSystem.Directory.Exists(destination).Should().BeTrue(); + FileSystem.Directory.GetFiles(destination, initialized[1].Name) + .Should().ContainSingle(); + FileSystem.Directory.GetDirectories(destination, initialized[2].Name) + .Should().ContainSingle(); + FileSystem.Directory.GetFiles(destination, initialized[3].Name, + SearchOption.AllDirectories) + .Should().ContainSingle(); + FileSystem.Directory.GetDirectories(destination, initialized[4].Name, + SearchOption.AllDirectories) + .Should().ContainSingle(); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_ShouldUpdatePropertiesOfDirectoryInfo( + string source, string destination) + { + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); + + sut.MoveTo(destination); + + sut.FullName.TrimEnd(FileSystem.Path.DirectorySeparatorChar) + .Should().Be(FileSystem.Path.GetFullPath(destination)); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_WithLockedFile_ShouldThrowIOException_AndNotMoveDirectory_OnWindows( + string source, string destination) + { + Skip.IfNot(Test.RunsOnWindows); + + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); + using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, + FileMode.Open, + FileAccess.Read, + FileShare.Read); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(destination); + }); + + if (Test.IsNetFramework) + { + // On .NET Framework the HResult is "-2146232800", but only in `DirectoryInfo.MoveTo` (not in `Directory.Move`) + // This peculiar deviation is not supported by the FileSystemMock. + exception.Should().BeException(); + } + else + { + exception.Should().BeException(hResult: -2147024891); + } + + FileSystem.Directory.Exists(source).Should().BeTrue(); + FileSystem.Directory.Exists(destination).Should().BeFalse(); + IDirectoryInfo sourceDirectory = + FileSystem.DirectoryInfo.New(source); + sourceDirectory.GetFiles(initialized[1].Name) + .Should().ContainSingle(); + sourceDirectory.GetDirectories(initialized[2].Name) + .Should().ContainSingle(); + sourceDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + sourceDirectory + .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_WithLockedFile_ShouldMoveDirectory_NotOnWindows( + string source, string destination) + { + Skip.If(Test.RunsOnWindows); + + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); + using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, + FileMode.Open, + FileAccess.Read, + FileShare.Read); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(destination); + }); + + exception.Should().BeNull(); + FileSystem.Directory.Exists(source).Should().BeFalse(); + FileSystem.Directory.Exists(destination).Should().BeTrue(); + IDirectoryInfo destinationDirectory = + FileSystem.DirectoryInfo.New(destination); + destinationDirectory.GetFiles(initialized[1].Name) + .Should().ContainSingle(); + destinationDirectory.GetDirectories(initialized[2].Name) + .Should().ContainSingle(); + destinationDirectory + .GetFiles(initialized[3].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + destinationDirectory + .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_WithReadOnlyFile_ShouldMoveDirectoryWithContent( + string source, string destination) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + initialized[3].Attributes = FileAttributes.ReadOnly; + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); + + sut.MoveTo(destination); + + FileSystem.Directory.Exists(source).Should().BeFalse(); + FileSystem.Directory.Exists(destination).Should().BeTrue(); + IDirectoryInfo destinationDirectory = + FileSystem.DirectoryInfo.New(destination); + destinationDirectory.GetFiles(initialized[1].Name) + .Should().ContainSingle(); + destinationDirectory.GetDirectories(initialized[2].Name) + .Should().ContainSingle(); + destinationDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) + .Should().ContainSingle().Which.Attributes.Should() + .HaveFlag(FileAttributes.ReadOnly); + destinationDirectory + .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/Tests.cs index 34267eb13..f20703a31 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/Tests.cs @@ -1,377 +1,376 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [InlineData("foo")] - [InlineData("foo/")] - public void Extension_ShouldReturnEmptyString(string path) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - - string result = sut.Extension; - - result.Should().BeEmpty(); - } - - [SkippableTheory] - [InlineData(@"/temp\\folder")] - [InlineData(@"/temp/folder")] - [InlineData(@"/temp/\\/folder")] - public void FullName_ShouldNotNormalizePathOnLinux(string path) - { - Skip.If(Test.RunsOnWindows); - - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - - sut.FullName.Should().Be(path); - } - - [SkippableTheory] - [InlineData("foo")] - [InlineData("foo/")] - public void FullName_ShouldReturnFullPath(string path) - { - string expectedPath = FileSystem.Path.GetFullPath(path); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - - sut.FullName.Should().Be(expectedPath); - } - - [SkippableTheory] - [InlineData(@"\\unc\folder", @"\\unc\folder")] - [InlineData(@"\\unc/folder\\foo", @"\\unc\folder\foo")] - [InlineData(@"c:\temp\\folder", @"c:\temp\folder")] - [InlineData(@"c:\temp//folder", @"c:\temp\folder")] - [InlineData(@"c:\temp//\\///folder", @"c:\temp\folder")] - public void FullName_ShouldReturnNormalizedPath_OnWindows( - string path, string expectedPath) - { - Skip.IfNot(Test.RunsOnWindows); - - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - - sut.FullName.Should().Be(expectedPath); - } - - [SkippableTheory] - [AutoData] - public void FullName_ShouldTrimTrailingSpaces_OnWindows(string path) - { - path = FileSystem.Path.GetFullPath(path); - string pathWithSpaces = path + " "; - - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(pathWithSpaces); - - if (Test.RunsOnWindows) - { - sut.FullName.Should().Be(path); - } - else - { - sut.FullName.Should().Be(pathWithSpaces); - } - } - - [SkippableTheory] - [AutoData] - public void - MissingFile_Attributes_ShouldAlwaysBeNegativeOne_AndSetterShouldThrowFileNotFoundException( - FileAttributes fileAttributes) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); - sut.Attributes.Should().Be((FileAttributes)(-1)); - Exception? exception = Record.Exception(() => - { - sut.Attributes = fileAttributes; - }); - exception.Should().BeException(hResult: -2147024894); - sut.Attributes.Should().Be((FileAttributes)(-1)); - } - - [SkippableTheory] - [AutoData] - public void - MissingFile_CreationTime_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( - DateTime creationTime) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); - sut.CreationTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); - Exception? exception = Record.Exception(() => - { - sut.CreationTime = creationTime; - }); - - if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) - { - exception.Should().BeException(hResult: -2147024894); - } - else - { - exception.Should().BeException(hResult: -2147024893); - } - - sut.CreationTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); - } - - [SkippableTheory] - [AutoData] - public void - MissingFile_CreationTimeUtc_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( - DateTime creationTimeUtc) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); - sut.CreationTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); - Exception? exception = Record.Exception(() => - { - sut.CreationTimeUtc = creationTimeUtc; - }); - - if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) - { - exception.Should().BeException(hResult: -2147024894); - } - else - { - exception.Should().BeException(hResult: -2147024893); - } - - sut.CreationTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); - } - - [SkippableTheory] - [AutoData] - public void - MissingFile_LastAccessTime_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( - DateTime lastAccessTime) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); - sut.LastAccessTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); - Exception? exception = Record.Exception(() => - { - sut.LastAccessTime = lastAccessTime; - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException(hResult: -2147024894); - } - else - { - exception.Should().BeException(hResult: -2147024893); - } - - sut.LastAccessTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); - } - - [SkippableTheory] - [AutoData] - public void - MissingFile_LastAccessTimeUtc_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( - DateTime lastAccessTimeUtc) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); - sut.LastAccessTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); - Exception? exception = Record.Exception(() => - { - sut.LastAccessTimeUtc = lastAccessTimeUtc; - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException(hResult: -2147024894); - } - else - { - exception.Should().BeException(hResult: -2147024893); - } - - sut.LastAccessTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); - } - - [SkippableTheory] - [AutoData] - public void - MissingFile_LastWriteTime_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( - DateTime lastWriteTime) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); - sut.LastWriteTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); - Exception? exception = Record.Exception(() => - { - sut.LastWriteTime = lastWriteTime; - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException(hResult: -2147024894); - } - else - { - exception.Should().BeException(hResult: -2147024893); - } - - sut.LastWriteTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); - } - - [SkippableTheory] - [AutoData] - public void - MissingFile_LastWriteTimeUtc_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( - DateTime lastWriteTimeUtc) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); - sut.LastWriteTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); - Exception? exception = Record.Exception(() => - { - sut.LastWriteTimeUtc = lastWriteTimeUtc; - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException(hResult: -2147024894); - } - else - { - exception.Should().BeException(hResult: -2147024893); - } - - sut.LastWriteTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); - } - - [SkippableTheory] - [AutoData] - public void Name_ShouldTrimTrailingSpaces_OnWindows(string path) - { - string pathWithSpaces = path + " "; - - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(pathWithSpaces); - - if (Test.RunsOnWindows) - { - sut.Name.Should().Be(path); - } - else - { - sut.Name.Should().Be(pathWithSpaces); - } - } - - [SkippableTheory] - [AutoData] - public void Parent_ArbitraryPaths_ShouldNotBeNull(string path1, - string path2, - string path3) - { - string path = FileSystem.Path.Combine(path1, path2, path3); - - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - - sut.Parent.Should().NotBeNull(); - sut.Parent!.Exists.Should().BeFalse(); - sut.Parent.Parent.Should().NotBeNull(); - sut.Parent.Parent!.Exists.Should().BeFalse(); - } - - [SkippableFact] - [AutoData] - public void Parent_Root_ShouldBeNull() - { - IDirectoryInfo sut = - FileSystem.DirectoryInfo.New(FileTestHelper.RootDrive()); - - sut.Parent.Should().BeNull(); - } - - [SkippableTheory] - [InlineAutoData("./foo/bar", "foo")] - [InlineAutoData("./foo", ".")] - public void Parent_ToString_ShouldBeAbsolutePathOnNetCore( - string path, string expectedParent) - { - Skip.If(Test.IsNetFramework); - - FileSystem.Initialize(); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - sut.ToString().Should().Be(path); - - IDirectoryInfo? parent = sut.Parent; - - parent.Should().NotBeNull(); - if (Test.IsNetFramework) - { - parent!.ToString().Should().Be(expectedParent); - } - else - { - parent!.ToString().Should().Be(FileSystem.Path.GetFullPath(expectedParent)); - } - } - - [SkippableTheory] - [InlineAutoData("./foo/bar", "foo")] - [InlineAutoData("./foo", "bar", "bar")] - public void Parent_ToString_ShouldBeDirectoryNameOnNetFramework( - string path, string expectedParent, string directory) - { - Skip.IfNot(Test.IsNetFramework); - - FileSystem.InitializeIn(directory); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - sut.ToString().Should().Be(path); - - IDirectoryInfo? parent = sut.Parent; - - parent.Should().NotBeNull(); - if (Test.IsNetFramework) - { - parent!.ToString().Should().Be(expectedParent); - } - else - { - parent!.ToString().Should().Be(FileSystem.Path.GetFullPath(expectedParent)); - } - } - - [SkippableFact] - [AutoData] - public void Root_Name_ShouldBeCorrect() - { - string rootName = FileTestHelper.RootDrive(); - IDirectoryInfo sut = - FileSystem.DirectoryInfo.New(rootName); - - sut.FullName.Should().Be(rootName); - sut.Name.Should().Be(rootName); - } - - [SkippableTheory] - [AutoData] - public void Root_ShouldExist(string path) - { - string expectedRoot = FileTestHelper.RootDrive(); - IDirectoryInfo result = FileSystem.DirectoryInfo.New(path); - - result.Root.Exists.Should().BeTrue(); - result.Root.FullName.Should().Be(expectedRoot); - } - - [SkippableTheory] - [InlineData("/foo")] - [InlineData("./foo")] - [InlineData("foo")] - public void ToString_ShouldReturnProvidedPath(string path) - { - IDirectoryInfo directoryInfo = FileSystem.DirectoryInfo.New(path); - - string? result = directoryInfo.ToString(); - - result.Should().Be(path); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [InlineData("foo")] + [InlineData("foo/")] + public void Extension_ShouldReturnEmptyString(string path) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + + string result = sut.Extension; + + result.Should().BeEmpty(); + } + + [SkippableTheory] + [InlineData(@"/temp\\folder")] + [InlineData(@"/temp/folder")] + [InlineData(@"/temp/\\/folder")] + public void FullName_ShouldNotNormalizePathOnLinux(string path) + { + Skip.If(Test.RunsOnWindows); + + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + + sut.FullName.Should().Be(path); + } + + [SkippableTheory] + [InlineData("foo")] + [InlineData("foo/")] + public void FullName_ShouldReturnFullPath(string path) + { + string expectedPath = FileSystem.Path.GetFullPath(path); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + + sut.FullName.Should().Be(expectedPath); + } + + [SkippableTheory] + [InlineData(@"\\unc\folder", @"\\unc\folder")] + [InlineData(@"\\unc/folder\\foo", @"\\unc\folder\foo")] + [InlineData(@"c:\temp\\folder", @"c:\temp\folder")] + [InlineData(@"c:\temp//folder", @"c:\temp\folder")] + [InlineData(@"c:\temp//\\///folder", @"c:\temp\folder")] + public void FullName_ShouldReturnNormalizedPath_OnWindows( + string path, string expectedPath) + { + Skip.IfNot(Test.RunsOnWindows); + + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + + sut.FullName.Should().Be(expectedPath); + } + + [SkippableTheory] + [AutoData] + public void FullName_ShouldTrimTrailingSpaces_OnWindows(string path) + { + path = FileSystem.Path.GetFullPath(path); + string pathWithSpaces = path + " "; + + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(pathWithSpaces); + + if (Test.RunsOnWindows) + { + sut.FullName.Should().Be(path); + } + else + { + sut.FullName.Should().Be(pathWithSpaces); + } + } + + [SkippableTheory] + [AutoData] + public void + MissingFile_Attributes_ShouldAlwaysBeNegativeOne_AndSetterShouldThrowFileNotFoundException( + FileAttributes fileAttributes) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); + sut.Attributes.Should().Be((FileAttributes)(-1)); + Exception? exception = Record.Exception(() => + { + sut.Attributes = fileAttributes; + }); + exception.Should().BeException(hResult: -2147024894); + sut.Attributes.Should().Be((FileAttributes)(-1)); + } + + [SkippableTheory] + [AutoData] + public void + MissingFile_CreationTime_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( + DateTime creationTime) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); + sut.CreationTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); + Exception? exception = Record.Exception(() => + { + sut.CreationTime = creationTime; + }); + + if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) + { + exception.Should().BeException(hResult: -2147024894); + } + else + { + exception.Should().BeException(hResult: -2147024893); + } + + sut.CreationTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); + } + + [SkippableTheory] + [AutoData] + public void + MissingFile_CreationTimeUtc_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( + DateTime creationTimeUtc) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); + sut.CreationTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); + Exception? exception = Record.Exception(() => + { + sut.CreationTimeUtc = creationTimeUtc; + }); + + if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) + { + exception.Should().BeException(hResult: -2147024894); + } + else + { + exception.Should().BeException(hResult: -2147024893); + } + + sut.CreationTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); + } + + [SkippableTheory] + [AutoData] + public void + MissingFile_LastAccessTime_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( + DateTime lastAccessTime) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); + sut.LastAccessTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); + Exception? exception = Record.Exception(() => + { + sut.LastAccessTime = lastAccessTime; + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException(hResult: -2147024894); + } + else + { + exception.Should().BeException(hResult: -2147024893); + } + + sut.LastAccessTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); + } + + [SkippableTheory] + [AutoData] + public void + MissingFile_LastAccessTimeUtc_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( + DateTime lastAccessTimeUtc) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); + sut.LastAccessTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); + Exception? exception = Record.Exception(() => + { + sut.LastAccessTimeUtc = lastAccessTimeUtc; + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException(hResult: -2147024894); + } + else + { + exception.Should().BeException(hResult: -2147024893); + } + + sut.LastAccessTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); + } + + [SkippableTheory] + [AutoData] + public void + MissingFile_LastWriteTime_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( + DateTime lastWriteTime) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); + sut.LastWriteTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); + Exception? exception = Record.Exception(() => + { + sut.LastWriteTime = lastWriteTime; + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException(hResult: -2147024894); + } + else + { + exception.Should().BeException(hResult: -2147024893); + } + + sut.LastWriteTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); + } + + [SkippableTheory] + [AutoData] + public void + MissingFile_LastWriteTimeUtc_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( + DateTime lastWriteTimeUtc) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); + sut.LastWriteTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); + Exception? exception = Record.Exception(() => + { + sut.LastWriteTimeUtc = lastWriteTimeUtc; + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException(hResult: -2147024894); + } + else + { + exception.Should().BeException(hResult: -2147024893); + } + + sut.LastWriteTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); + } + + [SkippableTheory] + [AutoData] + public void Name_ShouldTrimTrailingSpaces_OnWindows(string path) + { + string pathWithSpaces = path + " "; + + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(pathWithSpaces); + + if (Test.RunsOnWindows) + { + sut.Name.Should().Be(path); + } + else + { + sut.Name.Should().Be(pathWithSpaces); + } + } + + [SkippableTheory] + [AutoData] + public void Parent_ArbitraryPaths_ShouldNotBeNull(string path1, + string path2, + string path3) + { + string path = FileSystem.Path.Combine(path1, path2, path3); + + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + + sut.Parent.Should().NotBeNull(); + sut.Parent!.Exists.Should().BeFalse(); + sut.Parent.Parent.Should().NotBeNull(); + sut.Parent.Parent!.Exists.Should().BeFalse(); + } + + [SkippableFact] + [AutoData] + public void Parent_Root_ShouldBeNull() + { + IDirectoryInfo sut = + FileSystem.DirectoryInfo.New(FileTestHelper.RootDrive()); + + sut.Parent.Should().BeNull(); + } + + [SkippableTheory] + [InlineAutoData("./foo/bar", "foo")] + [InlineAutoData("./foo", ".")] + public void Parent_ToString_ShouldBeAbsolutePathOnNetCore( + string path, string expectedParent) + { + Skip.If(Test.IsNetFramework); + + FileSystem.Initialize(); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + sut.ToString().Should().Be(path); + + IDirectoryInfo? parent = sut.Parent; + + parent.Should().NotBeNull(); + if (Test.IsNetFramework) + { + parent!.ToString().Should().Be(expectedParent); + } + else + { + parent!.ToString().Should().Be(FileSystem.Path.GetFullPath(expectedParent)); + } + } + + [SkippableTheory] + [InlineAutoData("./foo/bar", "foo")] + [InlineAutoData("./foo", "bar", "bar")] + public void Parent_ToString_ShouldBeDirectoryNameOnNetFramework( + string path, string expectedParent, string directory) + { + Skip.IfNot(Test.IsNetFramework); + + FileSystem.InitializeIn(directory); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + sut.ToString().Should().Be(path); + + IDirectoryInfo? parent = sut.Parent; + + parent.Should().NotBeNull(); + if (Test.IsNetFramework) + { + parent!.ToString().Should().Be(expectedParent); + } + else + { + parent!.ToString().Should().Be(FileSystem.Path.GetFullPath(expectedParent)); + } + } + + [SkippableFact] + [AutoData] + public void Root_Name_ShouldBeCorrect() + { + string rootName = FileTestHelper.RootDrive(); + IDirectoryInfo sut = + FileSystem.DirectoryInfo.New(rootName); + + sut.FullName.Should().Be(rootName); + sut.Name.Should().Be(rootName); + } + + [SkippableTheory] + [AutoData] + public void Root_ShouldExist(string path) + { + string expectedRoot = FileTestHelper.RootDrive(); + IDirectoryInfo result = FileSystem.DirectoryInfo.New(path); + + result.Root.Exists.Should().BeTrue(); + result.Root.FullName.Should().Be(expectedRoot); + } + + [SkippableTheory] + [InlineData("/foo")] + [InlineData("./foo")] + [InlineData("foo")] + public void ToString_ShouldReturnProvidedPath(string path) + { + IDirectoryInfo directoryInfo = FileSystem.DirectoryInfo.New(path); + + string? result = directoryInfo.ToString(); + + result.Should().Be(path); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfoFactory/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfoFactory/ExceptionTests.cs index e900804d4..7ab85d3a6 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfoFactory/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfoFactory/ExceptionTests.cs @@ -1,118 +1,117 @@ -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfoFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), parameters: " ")] - public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), - parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowArgumentException_OnNetFramework( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo); - }); - - if (Test.IsNetFramework) - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should() - .BeNull( - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - - #region Helpers - - public static IEnumerable GetDirectoryInfoFactoryCallbacks(string? path) - => GetDirectoryInfoFactoryCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetDirectoryInfoFactoryCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.All, "path", directoryInfoFactory - => directoryInfoFactory.New(value)); - } - - #endregion -} +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfoFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), parameters: " ")] + public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), + parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowArgumentException_OnNetFramework( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo); + }); + + if (Test.IsNetFramework) + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should() + .BeNull( + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + + #region Helpers + + public static IEnumerable GetDirectoryInfoFactoryCallbacks(string? path) + => GetDirectoryInfoFactoryCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetDirectoryInfoFactoryCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.All, "path", directoryInfoFactory + => directoryInfoFactory.New(value)); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/ExceptionTests.cs index 6cd95d3e1..3681beffa 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/ExceptionTests.cs @@ -1,87 +1,86 @@ -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.DriveInfoFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [InlineData("?invalid-drive-name")] - [InlineData("invalid")] - [InlineData(" ")] - public void New_WhenDriveNameIsInvalid_ShouldThrowArgumentException( - string driveName) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - FileSystem.DriveInfo.New(driveName); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [Theory] - [MemberData(nameof(GetDriveInfoFactoryCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DriveInfo); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetDriveInfoFactoryCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DriveInfo); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - #region Helpers - - public static IEnumerable GetDriveInfoFactoryCallbacks(string? path) - => GetDriveInfoFactoryCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string ParamName, - Expression> Callback)> - GetDriveInfoFactoryCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.All, "driveName", driveInfoFactory - => driveInfoFactory.New(value)); - } - - #endregion -} +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.DriveInfoFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [InlineData("?invalid-drive-name")] + [InlineData("invalid")] + [InlineData(" ")] + public void New_WhenDriveNameIsInvalid_ShouldThrowArgumentException( + string driveName) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + FileSystem.DriveInfo.New(driveName); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [Theory] + [MemberData(nameof(GetDriveInfoFactoryCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DriveInfo); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetDriveInfoFactoryCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DriveInfo); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + #region Helpers + + public static IEnumerable GetDriveInfoFactoryCallbacks(string? path) + => GetDriveInfoFactoryCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string ParamName, + Expression> Callback)> + GetDriveInfoFactoryCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.All, "driveName", driveInfoFactory + => driveInfoFactory.New(value)); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/Tests.cs index cb22dcab5..93e5ca047 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/Tests.cs @@ -1,158 +1,157 @@ -using System.IO; -using System.Linq; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.DriveInfoFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableFact] - public void GetDrives_ShouldNotBeEmpty() - { - IDriveInfo[] result = FileSystem.DriveInfo.GetDrives(); - - result.Should().NotBeEmpty(); - } - - [SkippableTheory] - [AutoData] - public void MissingDrive_CreateDirectoryInfo_ShouldOnlyThrowWhenAccessingData( - string path, string subPath) - { - Skip.IfNot(Test.RunsOnWindows); - - IDriveInfo driveInfo = GetUnmappedDrive(); - - path = $"{driveInfo.Name}{path}"; - IDirectoryInfo directoryInfo = - FileSystem.DirectoryInfo.New(FileSystem.Path.Combine(path, subPath)); - IDirectoryInfo? parent = directoryInfo.Parent; - - Exception? exception = Record.Exception(() => - { - _ = parent!.EnumerateDirectories().ToArray(); - }); - - exception.Should().BeException($"'{path}'", - hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void MissingDrive_WriteAllBytes_ShouldThrowDirectoryNotFoundException( - string path, byte[] contents) - { - Skip.IfNot(Test.RunsOnWindows); - - IDriveInfo driveInfo = GetUnmappedDrive(); - - path = $"{driveInfo.Name}{path}"; - - Exception? exception = Record.Exception(() => - { - FileSystem.File.WriteAllBytes(path, contents); - }); - - exception.Should().BeException($"'{path}'", - hResult: -2147024893); - } - - [SkippableFact] - public void New_DefaultDrive_ShouldBeFixed() - { - IDriveInfo result = - FileSystem.DriveInfo.New(FileTestHelper.RootDrive()); - - result.AvailableFreeSpace.Should().BeGreaterThan(0); - result.DriveFormat.Should().NotBeNull(); - result.DriveType.Should().Be(DriveType.Fixed); - result.IsReady.Should().BeTrue(); - result.RootDirectory.FullName.Should().Be(FileTestHelper.RootDrive()); - result.TotalFreeSpace.Should().BeGreaterThan(0); - result.TotalSize.Should().BeGreaterThan(0); - result.VolumeLabel.Should().NotBeEmpty(); - } - - [SkippableTheory] - [AutoData] - public void New_InvalidDriveName_ShouldThrowArgumentException( - string invalidDriveName) - { - Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); - - Exception? exception = Record.Exception(() => - { - _ = FileSystem.DriveInfo.New(invalidDriveName); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableTheory] - [InlineData('A')] - [InlineData('C')] - [InlineData('X')] - public void New_WithDriveLetter_ShouldReturnDriveInfo(char driveLetter) - { - Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); - - IDriveInfo result = FileSystem.DriveInfo.New($"{driveLetter}"); - - result.Name.Should().Be($"{driveLetter}:\\"); - } - - [SkippableTheory] - [InlineAutoData('A')] - [InlineAutoData('C')] - [InlineAutoData('Y')] - public void New_WithRootedPath_ShouldReturnDriveInfo(char driveLetter, string path) - { - Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); - - string rootedPath = FileTestHelper.RootDrive(path, driveLetter); - - IDriveInfo result = FileSystem.DriveInfo.New(rootedPath); - - result.Name.Should().Be($"{driveLetter}:\\"); - } - - [SkippableFact] - public void Wrap_Null_ShouldReturnNull() - { - IDriveInfo? result = FileSystem.DriveInfo.Wrap(null); - - result.Should().BeNull(); - } - - [SkippableFact] - public void Wrap_ShouldReturnDriveInfoWithSameName() - { - System.IO.DriveInfo driveInfo = System.IO.DriveInfo.GetDrives().First(); - - IDriveInfo result = FileSystem.DriveInfo.Wrap(driveInfo); - - result.Name.Should().Be(driveInfo.Name); - } - - #region Helpers - - private IDriveInfo GetUnmappedDrive() - { - IDriveInfo? driveInfo = null; - for (char c = 'A'; c <= 'Z'; c++) - { - driveInfo = FileSystem.DriveInfo.New($"{c}"); - if (FileSystem.DriveInfo.GetDrives().All(d => d.Name != driveInfo.Name)) - { - break; - } - } - - return driveInfo ?? throw new NotSupportedException("No unmapped drive found!"); - } - - #endregion -} +using System.IO; +using System.Linq; + +namespace Testably.Abstractions.Tests.FileSystem.DriveInfoFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableFact] + public void GetDrives_ShouldNotBeEmpty() + { + IDriveInfo[] result = FileSystem.DriveInfo.GetDrives(); + + result.Should().NotBeEmpty(); + } + + [SkippableTheory] + [AutoData] + public void MissingDrive_CreateDirectoryInfo_ShouldOnlyThrowWhenAccessingData( + string path, string subPath) + { + Skip.IfNot(Test.RunsOnWindows); + + IDriveInfo driveInfo = GetUnmappedDrive(); + + path = $"{driveInfo.Name}{path}"; + IDirectoryInfo directoryInfo = + FileSystem.DirectoryInfo.New(FileSystem.Path.Combine(path, subPath)); + IDirectoryInfo? parent = directoryInfo.Parent; + + Exception? exception = Record.Exception(() => + { + _ = parent!.EnumerateDirectories().ToArray(); + }); + + exception.Should().BeException($"'{path}'", + hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void MissingDrive_WriteAllBytes_ShouldThrowDirectoryNotFoundException( + string path, byte[] contents) + { + Skip.IfNot(Test.RunsOnWindows); + + IDriveInfo driveInfo = GetUnmappedDrive(); + + path = $"{driveInfo.Name}{path}"; + + Exception? exception = Record.Exception(() => + { + FileSystem.File.WriteAllBytes(path, contents); + }); + + exception.Should().BeException($"'{path}'", + hResult: -2147024893); + } + + [SkippableFact] + public void New_DefaultDrive_ShouldBeFixed() + { + IDriveInfo result = + FileSystem.DriveInfo.New(FileTestHelper.RootDrive()); + + result.AvailableFreeSpace.Should().BeGreaterThan(0); + result.DriveFormat.Should().NotBeNull(); + result.DriveType.Should().Be(DriveType.Fixed); + result.IsReady.Should().BeTrue(); + result.RootDirectory.FullName.Should().Be(FileTestHelper.RootDrive()); + result.TotalFreeSpace.Should().BeGreaterThan(0); + result.TotalSize.Should().BeGreaterThan(0); + result.VolumeLabel.Should().NotBeEmpty(); + } + + [SkippableTheory] + [AutoData] + public void New_InvalidDriveName_ShouldThrowArgumentException( + string invalidDriveName) + { + Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); + + Exception? exception = Record.Exception(() => + { + _ = FileSystem.DriveInfo.New(invalidDriveName); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableTheory] + [InlineData('A')] + [InlineData('C')] + [InlineData('X')] + public void New_WithDriveLetter_ShouldReturnDriveInfo(char driveLetter) + { + Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); + + IDriveInfo result = FileSystem.DriveInfo.New($"{driveLetter}"); + + result.Name.Should().Be($"{driveLetter}:\\"); + } + + [SkippableTheory] + [InlineAutoData('A')] + [InlineAutoData('C')] + [InlineAutoData('Y')] + public void New_WithRootedPath_ShouldReturnDriveInfo(char driveLetter, string path) + { + Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); + + string rootedPath = FileTestHelper.RootDrive(path, driveLetter); + + IDriveInfo result = FileSystem.DriveInfo.New(rootedPath); + + result.Name.Should().Be($"{driveLetter}:\\"); + } + + [SkippableFact] + public void Wrap_Null_ShouldReturnNull() + { + IDriveInfo? result = FileSystem.DriveInfo.Wrap(null); + + result.Should().BeNull(); + } + + [SkippableFact] + public void Wrap_ShouldReturnDriveInfoWithSameName() + { + System.IO.DriveInfo driveInfo = System.IO.DriveInfo.GetDrives().First(); + + IDriveInfo result = FileSystem.DriveInfo.Wrap(driveInfo); + + result.Name.Should().Be(driveInfo.Name); + } + + #region Helpers + + private IDriveInfo GetUnmappedDrive() + { + IDriveInfo? driveInfo = null; + for (char c = 'A'; c <= 'Z'; c++) + { + driveInfo = FileSystem.DriveInfo.New($"{c}"); + if (FileSystem.DriveInfo.GetDrives().All(d => d.Name != driveInfo.Name)) + { + break; + } + } + + return driveInfo ?? throw new NotSupportedException("No unmapped drive found!"); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/CopyTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/CopyTests.cs index 6f8e356a6..5ec714cd0 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/CopyTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/CopyTests.cs @@ -1,325 +1,324 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class CopyTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - Copy_DestinationDirectoryDoesNotExist_ShouldThrowDirectoryNotFoundException( - string source) - { - FileSystem.Initialize() - .WithFile(source); - string destination = FileTestHelper.RootDrive("not-existing/path/foo.txt"); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Copy(source, destination); - }); - - exception.Should().BeException(hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Copy_DestinationExists_ShouldThrowIOException_AndNotCopyFile( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Copy(sourceName, destinationName); - }); - - exception.Should().BeException(hResult: Test.RunsOnWindows ? -2147024816 : 17); - - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - } - -#if FEATURE_FILE_MOVETO_OVERWRITE - [SkippableTheory] - [AutoData] - public void Copy_DestinationExists_WithOverwrite_ShouldOverwriteDestination( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - - FileSystem.File.Copy(sourceName, destinationName, true); - - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - } -#endif - - [SkippableTheory] - [InlineData(@"0:\something\demo.txt", @"C:\elsewhere\demo.txt")] - [InlineData(@"C:\something\demo.txt", @"^:\elsewhere\demo.txt")] - [InlineData(@"C:\something\demo.txt", @"C:\elsewhere:\demo.txt")] - public void - Copy_InvalidDriveName_ShouldThrowNotSupportedException( - string source, string destination) - { - Skip.IfNot(Test.IsNetFramework); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Copy(source, destination); - }); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [InlineData(@"C::\something\demo.txt", @"C:\elsewhere\demo.txt")] - public void - Copy_InvalidPath_ShouldThrowCorrectException( - string source, string destination) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Copy(source, destination); - }); - - if (Test.IsNetFramework) - { - exception.Should().BeException(hResult: -2146233067); - } - else - { - exception.Should().BeException(hResult: -2147024773); - } - } - - [SkippableTheory] - [AutoData] - public void Copy_ReadOnly_ShouldCopyFile( - string sourceName, string destinationName, string contents) - { - FileSystem.File.WriteAllText(sourceName, contents); - FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); - - FileSystem.File.Copy(sourceName, destinationName); - - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.GetAttributes(destinationName) - .Should().HaveFlag(FileAttributes.ReadOnly); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void Copy_ShouldAdjustTimes( - string source, string destination) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllText(source, "foo"); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - - FileSystem.File.Copy(source, destination); - - DateTime sourceCreationTime = FileSystem.File.GetCreationTimeUtc(source); - DateTime sourceLastAccessTime = FileSystem.File.GetLastAccessTimeUtc(source); - DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTimeUtc(source); - DateTime destinationCreationTime = - FileSystem.File.GetCreationTimeUtc(destination); - DateTime destinationLastAccessTime = - FileSystem.File.GetLastAccessTimeUtc(destination); - DateTime destinationLastWriteTime = - FileSystem.File.GetLastWriteTimeUtc(destination); - - sourceCreationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - if (Test.RunsOnWindows) - { - sourceLastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - else - { - sourceLastAccessTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - } - - sourceLastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - if (Test.RunsOnWindows) - { - destinationCreationTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - } - else - { - destinationCreationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - destinationLastAccessTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - destinationLastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - [SkippableTheory] - [AutoData] - public void Copy_ShouldCloneBinaryContent( - string source, string destination, byte[] original) - { - FileSystem.File.WriteAllBytes(source, original); - - FileSystem.File.Copy(source, destination); - - using (FileSystemStream stream = - FileSystem.File.Open(source, FileMode.Open, FileAccess.ReadWrite)) - { - BinaryWriter binaryWriter = new(stream); - - binaryWriter.Seek(0, SeekOrigin.Begin); - binaryWriter.Write("Some text"); - } - - FileSystem.File.ReadAllBytes(destination).Should() - .BeEquivalentTo(original); - FileSystem.File.ReadAllBytes(destination).Should() - .NotBeEquivalentTo(FileSystem.File.ReadAllBytes(source)); - } - - [SkippableTheory] - [AutoData] - public void Copy_ShouldCloneTextContent( - string source, string destination, string contents) - { - FileSystem.File.WriteAllText(source, contents); - - FileSystem.File.Copy(source, destination); - - using (FileSystemStream stream = - FileSystem.File.Open(source, FileMode.Open, FileAccess.ReadWrite)) - { - BinaryWriter binaryWriter = new(stream); - - binaryWriter.Seek(0, SeekOrigin.Begin); - binaryWriter.Write("Some text"); - } - - FileSystem.File.ReadAllText(source).Should() - .NotBe(FileSystem.File.ReadAllText(destination)); - } - - [SkippableTheory] - [AutoData] - public void Copy_ShouldCopyFileWithContent( - string sourceName, string destinationName, string contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText(sourceName, contents); - - TimeSystem.Thread.Sleep(1000); - - FileSystem.File.Copy(sourceName, destinationName); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(contents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void Copy_SourceIsDirectory_ShouldThrowUnauthorizedAccessException_AndNotCopyFile( - string sourceName, - string destinationName) - { - FileSystem.Directory.CreateDirectory(sourceName); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Copy(sourceName, destinationName); - }); - - exception.Should().BeException( - hResult: -2147024891, - messageContains: Test.IsNetFramework - ? $"'{sourceName}'" - : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); - FileSystem.Directory.Exists(sourceName).Should().BeTrue(); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Copy_SourceLocked_ShouldThrowIOException( - string sourceName, - string destinationName) - { - FileSystem.File.WriteAllText(sourceName, null); - using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, - FileAccess.Read, FileShare.None); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Copy(sourceName, destinationName); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024864); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - else - { - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void Copy_SourceMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName) - { - Exception? exception = Record.Exception(() => - { - FileSystem.File.Copy(sourceName, destinationName); - }); - - exception.Should().BeException( - hResult: -2147024894, - messageContains: Test.IsNetFramework - ? null - : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class CopyTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + Copy_DestinationDirectoryDoesNotExist_ShouldThrowDirectoryNotFoundException( + string source) + { + FileSystem.Initialize() + .WithFile(source); + string destination = FileTestHelper.RootDrive("not-existing/path/foo.txt"); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Copy(source, destination); + }); + + exception.Should().BeException(hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Copy_DestinationExists_ShouldThrowIOException_AndNotCopyFile( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Copy(sourceName, destinationName); + }); + + exception.Should().BeException(hResult: Test.RunsOnWindows ? -2147024816 : 17); + + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + } + +#if FEATURE_FILE_MOVETO_OVERWRITE + [SkippableTheory] + [AutoData] + public void Copy_DestinationExists_WithOverwrite_ShouldOverwriteDestination( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + + FileSystem.File.Copy(sourceName, destinationName, true); + + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + } +#endif + + [SkippableTheory] + [InlineData(@"0:\something\demo.txt", @"C:\elsewhere\demo.txt")] + [InlineData(@"C:\something\demo.txt", @"^:\elsewhere\demo.txt")] + [InlineData(@"C:\something\demo.txt", @"C:\elsewhere:\demo.txt")] + public void + Copy_InvalidDriveName_ShouldThrowNotSupportedException( + string source, string destination) + { + Skip.IfNot(Test.IsNetFramework); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Copy(source, destination); + }); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [InlineData(@"C::\something\demo.txt", @"C:\elsewhere\demo.txt")] + public void + Copy_InvalidPath_ShouldThrowCorrectException( + string source, string destination) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Copy(source, destination); + }); + + if (Test.IsNetFramework) + { + exception.Should().BeException(hResult: -2146233067); + } + else + { + exception.Should().BeException(hResult: -2147024773); + } + } + + [SkippableTheory] + [AutoData] + public void Copy_ReadOnly_ShouldCopyFile( + string sourceName, string destinationName, string contents) + { + FileSystem.File.WriteAllText(sourceName, contents); + FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); + + FileSystem.File.Copy(sourceName, destinationName); + + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.GetAttributes(destinationName) + .Should().HaveFlag(FileAttributes.ReadOnly); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void Copy_ShouldAdjustTimes( + string source, string destination) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllText(source, "foo"); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + + FileSystem.File.Copy(source, destination); + + DateTime sourceCreationTime = FileSystem.File.GetCreationTimeUtc(source); + DateTime sourceLastAccessTime = FileSystem.File.GetLastAccessTimeUtc(source); + DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTimeUtc(source); + DateTime destinationCreationTime = + FileSystem.File.GetCreationTimeUtc(destination); + DateTime destinationLastAccessTime = + FileSystem.File.GetLastAccessTimeUtc(destination); + DateTime destinationLastWriteTime = + FileSystem.File.GetLastWriteTimeUtc(destination); + + sourceCreationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + if (Test.RunsOnWindows) + { + sourceLastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + else + { + sourceLastAccessTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + } + + sourceLastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + if (Test.RunsOnWindows) + { + destinationCreationTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + } + else + { + destinationCreationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + destinationLastAccessTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + destinationLastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + [SkippableTheory] + [AutoData] + public void Copy_ShouldCloneBinaryContent( + string source, string destination, byte[] original) + { + FileSystem.File.WriteAllBytes(source, original); + + FileSystem.File.Copy(source, destination); + + using (FileSystemStream stream = + FileSystem.File.Open(source, FileMode.Open, FileAccess.ReadWrite)) + { + BinaryWriter binaryWriter = new(stream); + + binaryWriter.Seek(0, SeekOrigin.Begin); + binaryWriter.Write("Some text"); + } + + FileSystem.File.ReadAllBytes(destination).Should() + .BeEquivalentTo(original); + FileSystem.File.ReadAllBytes(destination).Should() + .NotBeEquivalentTo(FileSystem.File.ReadAllBytes(source)); + } + + [SkippableTheory] + [AutoData] + public void Copy_ShouldCloneTextContent( + string source, string destination, string contents) + { + FileSystem.File.WriteAllText(source, contents); + + FileSystem.File.Copy(source, destination); + + using (FileSystemStream stream = + FileSystem.File.Open(source, FileMode.Open, FileAccess.ReadWrite)) + { + BinaryWriter binaryWriter = new(stream); + + binaryWriter.Seek(0, SeekOrigin.Begin); + binaryWriter.Write("Some text"); + } + + FileSystem.File.ReadAllText(source).Should() + .NotBe(FileSystem.File.ReadAllText(destination)); + } + + [SkippableTheory] + [AutoData] + public void Copy_ShouldCopyFileWithContent( + string sourceName, string destinationName, string contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText(sourceName, contents); + + TimeSystem.Thread.Sleep(1000); + + FileSystem.File.Copy(sourceName, destinationName); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(contents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void Copy_SourceIsDirectory_ShouldThrowUnauthorizedAccessException_AndNotCopyFile( + string sourceName, + string destinationName) + { + FileSystem.Directory.CreateDirectory(sourceName); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Copy(sourceName, destinationName); + }); + + exception.Should().BeException( + hResult: -2147024891, + messageContains: Test.IsNetFramework + ? $"'{sourceName}'" + : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); + FileSystem.Directory.Exists(sourceName).Should().BeTrue(); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Copy_SourceLocked_ShouldThrowIOException( + string sourceName, + string destinationName) + { + FileSystem.File.WriteAllText(sourceName, null); + using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, + FileAccess.Read, FileShare.None); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Copy(sourceName, destinationName); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024864); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + else + { + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void Copy_SourceMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName) + { + Exception? exception = Record.Exception(() => + { + FileSystem.File.Copy(sourceName, destinationName); + }); + + exception.Should().BeException( + hResult: -2147024894, + messageContains: Test.IsNetFramework + ? null + : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/CreateTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/CreateTests.cs index a977e723e..fb052c091 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/CreateTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/CreateTests.cs @@ -1,110 +1,109 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class CreateTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Create_ExistingFile_ShouldBeOverwritten( - string path, string originalContent, string newContent) - { - FileSystem.File.WriteAllText(path, originalContent); - - using FileSystemStream stream = FileSystem.File.Create(path); - - using StreamWriter streamWriter = new(stream); - streamWriter.Write(newContent); - - streamWriter.Dispose(); - stream.Dispose(); - FileSystem.File.ReadAllText(path).Should().Be(newContent); - } - - [SkippableTheory] - [AutoData] - public void Create_ReadOnlyFile_ShouldThrowUnauthorizedAccessException( - string path, string content) - { - FileSystem.File.WriteAllText(path, content); - FileSystem.File.SetAttributes(path, FileAttributes.ReadOnly); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Create(path); - }); - - exception.Should().BeException(hResult: -2147024891); - } - - [SkippableTheory] - [AutoData] - public void Create_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string missingDirectory, string fileName) - { - string filePath = FileSystem.Path.Combine(missingDirectory, fileName); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Create(filePath); - }); - - exception.Should().BeException(hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Create_MissingFile_ShouldCreateFile(string path) - { - using FileSystemStream stream = FileSystem.File.Create(path); - - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void Create_ShouldUseReadWriteAccessAndNoneShare(string path) - { - using FileSystemStream stream = FileSystem.File.Create(path); - - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - stream.CanRead.Should().BeTrue(); - stream.CanWrite.Should().BeTrue(); - stream.CanSeek.Should().BeTrue(); - stream.CanTimeout.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Create_WithBufferSize_ShouldUseReadWriteAccessAndNoneShare( - string path, int bufferSize) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = FileSystem.File.Create(path, bufferSize); - - stream.IsAsync.Should().BeFalse(); - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } - - [SkippableTheory] - [AutoData] - public void Create_WithBufferSizeAndFileOptions_ShouldUseReadWriteAccessAndNoneShare( - string path, int bufferSize) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = - FileSystem.File.Create(path, bufferSize, FileOptions.Asynchronous); - - stream.IsAsync.Should().BeTrue(); - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class CreateTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Create_ExistingFile_ShouldBeOverwritten( + string path, string originalContent, string newContent) + { + FileSystem.File.WriteAllText(path, originalContent); + + using FileSystemStream stream = FileSystem.File.Create(path); + + using StreamWriter streamWriter = new(stream); + streamWriter.Write(newContent); + + streamWriter.Dispose(); + stream.Dispose(); + FileSystem.File.ReadAllText(path).Should().Be(newContent); + } + + [SkippableTheory] + [AutoData] + public void Create_ReadOnlyFile_ShouldThrowUnauthorizedAccessException( + string path, string content) + { + FileSystem.File.WriteAllText(path, content); + FileSystem.File.SetAttributes(path, FileAttributes.ReadOnly); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Create(path); + }); + + exception.Should().BeException(hResult: -2147024891); + } + + [SkippableTheory] + [AutoData] + public void Create_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string missingDirectory, string fileName) + { + string filePath = FileSystem.Path.Combine(missingDirectory, fileName); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Create(filePath); + }); + + exception.Should().BeException(hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Create_MissingFile_ShouldCreateFile(string path) + { + using FileSystemStream stream = FileSystem.File.Create(path); + + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void Create_ShouldUseReadWriteAccessAndNoneShare(string path) + { + using FileSystemStream stream = FileSystem.File.Create(path); + + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + stream.CanRead.Should().BeTrue(); + stream.CanWrite.Should().BeTrue(); + stream.CanSeek.Should().BeTrue(); + stream.CanTimeout.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Create_WithBufferSize_ShouldUseReadWriteAccessAndNoneShare( + string path, int bufferSize) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = FileSystem.File.Create(path, bufferSize); + + stream.IsAsync.Should().BeFalse(); + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } + + [SkippableTheory] + [AutoData] + public void Create_WithBufferSizeAndFileOptions_ShouldUseReadWriteAccessAndNoneShare( + string path, int bufferSize) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = + FileSystem.File.Create(path, bufferSize, FileOptions.Asynchronous); + + stream.IsAsync.Should().BeTrue(); + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/DeleteTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/DeleteTests.cs index 317cbbf73..592b4116d 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/DeleteTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/DeleteTests.cs @@ -1,72 +1,71 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class DeleteTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string missingDirectory, string fileName) - { - string filePath = FileSystem.Path.Combine(missingDirectory, fileName); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Delete(filePath); - }); - - exception.Should().BeException(hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Delete_MissingFile_ShouldDoNothing( - string fileName) - { - Exception? exception = Record.Exception(() => - { - FileSystem.File.Delete(fileName); - }); - - exception.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void Delete_WithOpenFile_ShouldThrowIOException_OnWindows(string filename) - { - FileSystem.Initialize(); - FileSystemStream openFile = FileSystem.File.OpenWrite(filename); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - Exception? exception = Record.Exception(() => - { - FileSystem.File.Delete(filename); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException($"{filename}'", - hResult: -2147024864); - FileSystem.File.Exists(filename).Should().BeTrue(); - } - else - { - exception.Should().BeNull(); - FileSystem.File.Exists(filename).Should().BeFalse(); - } - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class DeleteTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string missingDirectory, string fileName) + { + string filePath = FileSystem.Path.Combine(missingDirectory, fileName); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Delete(filePath); + }); + + exception.Should().BeException(hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Delete_MissingFile_ShouldDoNothing( + string fileName) + { + Exception? exception = Record.Exception(() => + { + FileSystem.File.Delete(fileName); + }); + + exception.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void Delete_WithOpenFile_ShouldThrowIOException_OnWindows(string filename) + { + FileSystem.Initialize(); + FileSystemStream openFile = FileSystem.File.OpenWrite(filename); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + Exception? exception = Record.Exception(() => + { + FileSystem.File.Delete(filename); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException($"{filename}'", + hResult: -2147024864); + FileSystem.File.Exists(filename).Should().BeTrue(); + } + else + { + exception.Should().BeNull(); + FileSystem.File.Exists(filename).Should().BeFalse(); + } + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionMissingFileTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionMissingFileTests.cs index 988bd9f95..cf8731c48 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionMissingFileTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionMissingFileTests.cs @@ -1,398 +1,397 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Threading; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionMissingFileTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetFileCallbacks), parameters: (int)MissingFileTestCase.FileMissing)] - public void Operations_WhenFileIsMissing_ShouldThrowFileNotFoundException( - Expression> callback) - { - string path = "missing-file.txt"; - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.File, path); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException( - hResult: -2147024894, - because: $"\n{callback}\n was called with a missing file"); - } - else - { - exception.Should() - .BeFileOrDirectoryNotFoundException( - $"\n{callback}\n was called with a missing file"); - } - } - - [Theory] - [MemberData(nameof(GetFileCallbacks), parameters: (int)MissingFileTestCase.DirectoryMissing)] - public void Operations_WhenDirectoryIsMissing_ShouldThrowDirectoryNotFoundException( - Expression> callback) - { - string path = FileSystem.Path.Combine("missing-directory", "file.txt"); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.File, path); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException( - hResult: -2147024893, - because: $"\n{callback}\n was called with a missing directory"); - } - else - { - exception.Should() - .BeFileOrDirectoryNotFoundException( - $"\n{callback}\n was called with a missing directory"); - } - } - - #region Helpers - - [Flags] - public enum MissingFileTestCase - { - FileMissing = 1, - DirectoryMissing = 2, - All = FileMissing | DirectoryMissing - } - - public enum ExpectedExceptionType - { - Default, - } - - public static IEnumerable GetFileCallbacks(int testCases) - => GetFileCallbackTestParameters() - .Where(item => (item.TestCase & (MissingFileTestCase)testCases) != 0) - .Select(item => new object?[] - { - item.Callback - }); - - private static IEnumerable<(MissingFileTestCase TestCase, ExpectedExceptionType ExceptionType, - Expression> Callback)> - GetFileCallbackTestParameters() - { - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.GetAttributes(path)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.SetAttributes(path, FileAttributes.ReadOnly)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.Copy(path, "destination.txt")); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllLines(path, new[] - { - "foo" - })); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllLines(path, new[] - { - "foo" - })); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllLines(path, new[] - { - "foo" - }, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllLinesAsync(path, new[] - { - "foo" - }, CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllLinesAsync(path, new[] - { - "foo" - }, Encoding.UTF8, - CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllText(path, "foo")); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllText(path, "foo", Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllTextAsync(path, "foo", CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllTextAsync(path, "foo", Encoding.UTF8, - CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendText(path)); - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Copy(path, "foo")); - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Copy("foo", path)); - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Copy(path, "foo", false)); - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Copy("foo", path, false)); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.Create(path)); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.Create(path, 1023)); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.Create(path, 1023, FileOptions.None)); -#if FEATURE_FILESYSTEM_LINK - if (Test.RunsOnWindows) - { - yield return (MissingFileTestCase.DirectoryMissing, - ExpectedExceptionType.Default, - (file, path) - => file.CreateSymbolicLink(path, "foo")); - } -#endif - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.CreateText(path)); - - if (Test.RunsOnWindows) - { - #pragma warning disable CA1416 - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.Decrypt(path)); - #pragma warning restore CA1416 - } - - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.Delete(path)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.GetAttributes(path)); - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Move(path, "foo")); - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Move("foo", path)); -#if FEATURE_FILE_MOVETO_OVERWRITE - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Move(path, "foo", false)); - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Move("foo", path, false)); -#endif - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.Open(path, FileMode.Open)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.Open(path, FileMode.Open, FileAccess.ReadWrite)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None)); -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.Open(path, new FileStreamOptions())); -#endif - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.OpenRead(path)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.OpenText(path)); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.OpenWrite(path)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllBytes(path)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllBytesAsync(path, CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllLines(path)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllLines(path, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllLinesAsync(path, CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllLinesAsync(path, Encoding.UTF8, CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllText(path)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllText(path, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllTextAsync(path, CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllTextAsync(path, Encoding.UTF8, CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadLines(path)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadLines(path, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_NET7 - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadLinesAsync(path, CancellationToken.None)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadLinesAsync(path, Encoding.UTF8, CancellationToken.None)); -#endif -#if FEATURE_FILESYSTEM_LINK - yield return (MissingFileTestCase.All, - ExpectedExceptionType.Default, - (file, path) - => file.ResolveLinkTarget(path, false)); -#endif - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.SetAttributes(path, FileAttributes.ReadOnly)); - yield return (MissingFileTestCase.All, - ExpectedExceptionType.Default, - (file, path) - => file.SetCreationTime(path, DateTime.Now)); - yield return (MissingFileTestCase.All, - ExpectedExceptionType.Default, - (file, path) - => file.SetCreationTimeUtc(path, DateTime.Now)); - yield return (MissingFileTestCase.All, - ExpectedExceptionType.Default, - (file, path) - => file.SetLastAccessTime(path, DateTime.Now)); - yield return (MissingFileTestCase.All, - ExpectedExceptionType.Default, - (file, path) - => file.SetLastAccessTimeUtc(path, DateTime.Now)); - yield return (MissingFileTestCase.All, - ExpectedExceptionType.Default, - (file, path) - => file.SetLastWriteTime(path, DateTime.Now)); - yield return (MissingFileTestCase.All, - ExpectedExceptionType.Default, - (file, path) - => file.SetLastWriteTimeUtc(path, DateTime.Now)); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllBytes(path, new byte[] - { - 0, 1 - })); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllBytesAsync(path, new byte[] - { - 0, 1 - }, - CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllLines(path, new[] - { - "foo" - })); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllLines(path, new[] - { - "foo" - }, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllLinesAsync(path, new[] - { - "foo" - }, CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllLinesAsync(path, new[] - { - "foo" - }, Encoding.UTF8, - CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllText(path, "foo")); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllText(path, "foo", Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllTextAsync(path, "foo", CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllTextAsync(path, "foo", Encoding.UTF8, - CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionMissingFileTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetFileCallbacks), parameters: (int)MissingFileTestCase.FileMissing)] + public void Operations_WhenFileIsMissing_ShouldThrowFileNotFoundException( + Expression> callback) + { + string path = "missing-file.txt"; + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.File, path); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException( + hResult: -2147024894, + because: $"\n{callback}\n was called with a missing file"); + } + else + { + exception.Should() + .BeFileOrDirectoryNotFoundException( + $"\n{callback}\n was called with a missing file"); + } + } + + [Theory] + [MemberData(nameof(GetFileCallbacks), parameters: (int)MissingFileTestCase.DirectoryMissing)] + public void Operations_WhenDirectoryIsMissing_ShouldThrowDirectoryNotFoundException( + Expression> callback) + { + string path = FileSystem.Path.Combine("missing-directory", "file.txt"); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.File, path); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException( + hResult: -2147024893, + because: $"\n{callback}\n was called with a missing directory"); + } + else + { + exception.Should() + .BeFileOrDirectoryNotFoundException( + $"\n{callback}\n was called with a missing directory"); + } + } + + #region Helpers + + [Flags] + public enum MissingFileTestCase + { + FileMissing = 1, + DirectoryMissing = 2, + All = FileMissing | DirectoryMissing + } + + public enum ExpectedExceptionType + { + Default, + } + + public static IEnumerable GetFileCallbacks(int testCases) + => GetFileCallbackTestParameters() + .Where(item => (item.TestCase & (MissingFileTestCase)testCases) != 0) + .Select(item => new object?[] + { + item.Callback + }); + + private static IEnumerable<(MissingFileTestCase TestCase, ExpectedExceptionType ExceptionType, + Expression> Callback)> + GetFileCallbackTestParameters() + { + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.GetAttributes(path)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.SetAttributes(path, FileAttributes.ReadOnly)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.Copy(path, "destination.txt")); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllLines(path, new[] + { + "foo" + })); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllLines(path, new[] + { + "foo" + })); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllLines(path, new[] + { + "foo" + }, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllLinesAsync(path, new[] + { + "foo" + }, CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllLinesAsync(path, new[] + { + "foo" + }, Encoding.UTF8, + CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllText(path, "foo")); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllText(path, "foo", Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllTextAsync(path, "foo", CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllTextAsync(path, "foo", Encoding.UTF8, + CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendText(path)); + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Copy(path, "foo")); + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Copy("foo", path)); + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Copy(path, "foo", false)); + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Copy("foo", path, false)); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.Create(path)); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.Create(path, 1023)); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.Create(path, 1023, FileOptions.None)); +#if FEATURE_FILESYSTEM_LINK + if (Test.RunsOnWindows) + { + yield return (MissingFileTestCase.DirectoryMissing, + ExpectedExceptionType.Default, + (file, path) + => file.CreateSymbolicLink(path, "foo")); + } +#endif + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.CreateText(path)); + + if (Test.RunsOnWindows) + { + #pragma warning disable CA1416 + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.Decrypt(path)); + #pragma warning restore CA1416 + } + + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.Delete(path)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.GetAttributes(path)); + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Move(path, "foo")); + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Move("foo", path)); +#if FEATURE_FILE_MOVETO_OVERWRITE + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Move(path, "foo", false)); + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Move("foo", path, false)); +#endif + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.Open(path, FileMode.Open)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.Open(path, FileMode.Open, FileAccess.ReadWrite)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None)); +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.Open(path, new FileStreamOptions())); +#endif + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.OpenRead(path)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.OpenText(path)); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.OpenWrite(path)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllBytes(path)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllBytesAsync(path, CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllLines(path)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllLines(path, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllLinesAsync(path, CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllLinesAsync(path, Encoding.UTF8, CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllText(path)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllText(path, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllTextAsync(path, CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllTextAsync(path, Encoding.UTF8, CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadLines(path)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadLines(path, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_NET7 + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadLinesAsync(path, CancellationToken.None)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadLinesAsync(path, Encoding.UTF8, CancellationToken.None)); +#endif +#if FEATURE_FILESYSTEM_LINK + yield return (MissingFileTestCase.All, + ExpectedExceptionType.Default, + (file, path) + => file.ResolveLinkTarget(path, false)); +#endif + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.SetAttributes(path, FileAttributes.ReadOnly)); + yield return (MissingFileTestCase.All, + ExpectedExceptionType.Default, + (file, path) + => file.SetCreationTime(path, DateTime.Now)); + yield return (MissingFileTestCase.All, + ExpectedExceptionType.Default, + (file, path) + => file.SetCreationTimeUtc(path, DateTime.Now)); + yield return (MissingFileTestCase.All, + ExpectedExceptionType.Default, + (file, path) + => file.SetLastAccessTime(path, DateTime.Now)); + yield return (MissingFileTestCase.All, + ExpectedExceptionType.Default, + (file, path) + => file.SetLastAccessTimeUtc(path, DateTime.Now)); + yield return (MissingFileTestCase.All, + ExpectedExceptionType.Default, + (file, path) + => file.SetLastWriteTime(path, DateTime.Now)); + yield return (MissingFileTestCase.All, + ExpectedExceptionType.Default, + (file, path) + => file.SetLastWriteTimeUtc(path, DateTime.Now)); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllBytes(path, new byte[] + { + 0, 1 + })); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllBytesAsync(path, new byte[] + { + 0, 1 + }, + CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllLines(path, new[] + { + "foo" + })); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllLines(path, new[] + { + "foo" + }, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllLinesAsync(path, new[] + { + "foo" + }, CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllLinesAsync(path, new[] + { + "foo" + }, Encoding.UTF8, + CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllText(path, "foo")); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllText(path, "foo", Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllTextAsync(path, "foo", CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllTextAsync(path, "foo", Encoding.UTF8, + CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionTests.cs index a7fd67150..261217eb5 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionTests.cs @@ -1,422 +1,421 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Threading; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetFileCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.File); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileCallbacks), parameters: " ")] - public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.File); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetFileCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.File); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileCallbacks), parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.File); - }); - - if (!Test.RunsOnWindows) - { - if (exception is IOException ioException) - { - ioException.HResult.Should().NotBe(-2147024809, - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - else - { - if (Test.IsNetFramework) - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should().BeException( - hResult: -2147024773, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - } - - #region Helpers - - public static IEnumerable GetFileCallbacks(string? path) - => GetFileCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetFileCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllLines(value, new[] - { - "foo" - })); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllLines(value, new[] - { - "foo" - }, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllLinesAsync(value, new[] - { - "foo" - }, CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllLinesAsync(value, new[] - { - "foo" - }, Encoding.UTF8, - CancellationToken.None).GetAwaiter().GetResult()); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllText(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllText(value, "foo", Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllTextAsync(value, "foo", CancellationToken.None).GetAwaiter() - .GetResult()); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllTextAsync(value, "foo", Encoding.UTF8, - CancellationToken.None).GetAwaiter().GetResult()); -#endif - yield return (ExceptionTestHelper.TestTypes.NullOrInvalidPath, "path", file - => file.AppendText(value)); - yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "sourceFileName", - file - => file.Copy(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file - => file.Copy("foo", value)); - yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "sourceFileName", - file - => file.Copy(value, "foo", false)); - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file - => file.Copy("foo", value, false)); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file - => file.Copy(value, "foo")); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file - => file.Copy("foo", value)); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file - => file.Copy(value, "foo", false)); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file - => file.Copy("foo", value, false)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Create(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Create(value, 1023)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Create(value, 1023, FileOptions.None)); -#if FEATURE_FILESYSTEM_LINK - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.CreateSymbolicLink(value, "foo")); -#endif - yield return (ExceptionTestHelper.TestTypes.NullOrInvalidPath, "path", file - => file.CreateText(value)); - - if (Test.RunsOnWindows) - { - #pragma warning disable CA1416 - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "path", file - => file.Decrypt(value)); - #pragma warning restore CA1416 - } - - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Delete(value)); - if (Test.RunsOnWindows) - { - #pragma warning disable CA1416 - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "path", file - => file.Encrypt(value)); - #pragma warning restore CA1416 - } - - // `File.Exists` doesn't throw an exception on `null` - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetAttributes(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetCreationTime(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetCreationTimeUtc(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetLastAccessTime(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetLastAccessTimeUtc(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetLastWriteTime(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetLastWriteTimeUtc(value)); -#if FEATURE_FILESYSTEM_UNIXFILEMODE - if (!Test.RunsOnWindows) - { - #pragma warning disable CA1416 - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetUnixFileMode(value)); - #pragma warning restore CA1416 - } -#endif - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "sourceFileName", file - => file.Move(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file - => file.Move("foo", value)); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file - => file.Move(value, "foo")); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file - => file.Move("foo", value)); -#if FEATURE_FILE_MOVETO_OVERWRITE - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "sourceFileName", file - => file.Move(value, "foo", false)); - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file - => file.Move("foo", value, false)); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file - => file.Move(value, "foo", false)); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file - => file.Move("foo", value, false)); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Open(value, FileMode.Open)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Open(value, FileMode.Open, FileAccess.ReadWrite)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Open(value, FileMode.Open, FileAccess.ReadWrite, FileShare.None)); -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Open(value, new FileStreamOptions())); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.OpenRead(value)); - yield return (ExceptionTestHelper.TestTypes.NullOrInvalidPath, "path", file - => file.OpenText(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.OpenWrite(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllBytes(value)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllBytesAsync(value, CancellationToken.None).GetAwaiter() - .GetResult()); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllLines(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllLines(value, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllLinesAsync(value, CancellationToken.None).GetAwaiter() - .GetResult()); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllLinesAsync(value, Encoding.UTF8, CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllText(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllText(value, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllTextAsync(value, CancellationToken.None).GetAwaiter() - .GetResult()); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllTextAsync(value, Encoding.UTF8, CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadLines(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadLines(value, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_NET7 - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadLinesAsync(value, CancellationToken.None)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadLinesAsync(value, Encoding.UTF8, CancellationToken.None)); -#endif - yield return ( - ExceptionTestHelper.TestTypes.AllExceptInvalidPath | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file - => file.Replace(value, "foo", "bar")); - yield return ( - ExceptionTestHelper.TestTypes.All | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destinationFileName", - file - => file.Replace("foo", value, "bar")); - yield return ( - ExceptionTestHelper.TestTypes.AllExceptInvalidPath | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file - => file.Replace(value, "foo", "bar", false)); - yield return ( - ExceptionTestHelper.TestTypes.All | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destinationFileName", - file - => file.Replace("foo", value, "bar", false)); -#if FEATURE_FILESYSTEM_LINK - yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "linkPath", file - => file.ResolveLinkTarget(value, false)); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetAttributes(value, FileAttributes.ReadOnly)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetCreationTime(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetCreationTimeUtc(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetLastAccessTime(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetLastAccessTimeUtc(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetLastWriteTime(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetLastWriteTimeUtc(value, DateTime.Now)); -#if FEATURE_FILESYSTEM_UNIXFILEMODE - if (!Test.RunsOnWindows) - { - #pragma warning disable CA1416 - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetUnixFileMode(value, UnixFileMode.None)); - #pragma warning restore CA1416 - } -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllBytes(value, new byte[] - { - 0, 1 - })); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllBytesAsync(value, new byte[] - { - 0, 1 - }, - CancellationToken.None).GetAwaiter().GetResult()); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllLines(value, new[] - { - "foo" - })); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllLines(value, new[] - { - "foo" - }, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllLinesAsync(value, new[] - { - "foo" - }, CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllLinesAsync(value, new[] - { - "foo" - }, Encoding.UTF8, - CancellationToken.None).GetAwaiter().GetResult()); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllText(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllText(value, "foo", Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllTextAsync(value, "foo", CancellationToken.None).GetAwaiter() - .GetResult()); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllTextAsync(value, "foo", Encoding.UTF8, - CancellationToken.None).GetAwaiter().GetResult()); -#endif - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetFileCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.File); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileCallbacks), parameters: " ")] + public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.File); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetFileCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.File); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileCallbacks), parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.File); + }); + + if (!Test.RunsOnWindows) + { + if (exception is IOException ioException) + { + ioException.HResult.Should().NotBe(-2147024809, + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + else + { + if (Test.IsNetFramework) + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should().BeException( + hResult: -2147024773, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + } + + #region Helpers + + public static IEnumerable GetFileCallbacks(string? path) + => GetFileCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetFileCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllLines(value, new[] + { + "foo" + })); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllLines(value, new[] + { + "foo" + }, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllLinesAsync(value, new[] + { + "foo" + }, CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllLinesAsync(value, new[] + { + "foo" + }, Encoding.UTF8, + CancellationToken.None).GetAwaiter().GetResult()); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllText(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllText(value, "foo", Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllTextAsync(value, "foo", CancellationToken.None).GetAwaiter() + .GetResult()); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllTextAsync(value, "foo", Encoding.UTF8, + CancellationToken.None).GetAwaiter().GetResult()); +#endif + yield return (ExceptionTestHelper.TestTypes.NullOrInvalidPath, "path", file + => file.AppendText(value)); + yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "sourceFileName", + file + => file.Copy(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file + => file.Copy("foo", value)); + yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "sourceFileName", + file + => file.Copy(value, "foo", false)); + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file + => file.Copy("foo", value, false)); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file + => file.Copy(value, "foo")); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file + => file.Copy("foo", value)); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file + => file.Copy(value, "foo", false)); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file + => file.Copy("foo", value, false)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Create(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Create(value, 1023)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Create(value, 1023, FileOptions.None)); +#if FEATURE_FILESYSTEM_LINK + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.CreateSymbolicLink(value, "foo")); +#endif + yield return (ExceptionTestHelper.TestTypes.NullOrInvalidPath, "path", file + => file.CreateText(value)); + + if (Test.RunsOnWindows) + { + #pragma warning disable CA1416 + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "path", file + => file.Decrypt(value)); + #pragma warning restore CA1416 + } + + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Delete(value)); + if (Test.RunsOnWindows) + { + #pragma warning disable CA1416 + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "path", file + => file.Encrypt(value)); + #pragma warning restore CA1416 + } + + // `File.Exists` doesn't throw an exception on `null` + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetAttributes(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetCreationTime(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetCreationTimeUtc(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetLastAccessTime(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetLastAccessTimeUtc(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetLastWriteTime(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetLastWriteTimeUtc(value)); +#if FEATURE_FILESYSTEM_UNIXFILEMODE + if (!Test.RunsOnWindows) + { + #pragma warning disable CA1416 + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetUnixFileMode(value)); + #pragma warning restore CA1416 + } +#endif + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "sourceFileName", file + => file.Move(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file + => file.Move("foo", value)); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file + => file.Move(value, "foo")); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file + => file.Move("foo", value)); +#if FEATURE_FILE_MOVETO_OVERWRITE + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "sourceFileName", file + => file.Move(value, "foo", false)); + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file + => file.Move("foo", value, false)); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file + => file.Move(value, "foo", false)); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file + => file.Move("foo", value, false)); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Open(value, FileMode.Open)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Open(value, FileMode.Open, FileAccess.ReadWrite)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Open(value, FileMode.Open, FileAccess.ReadWrite, FileShare.None)); +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Open(value, new FileStreamOptions())); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.OpenRead(value)); + yield return (ExceptionTestHelper.TestTypes.NullOrInvalidPath, "path", file + => file.OpenText(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.OpenWrite(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllBytes(value)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllBytesAsync(value, CancellationToken.None).GetAwaiter() + .GetResult()); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllLines(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllLines(value, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllLinesAsync(value, CancellationToken.None).GetAwaiter() + .GetResult()); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllLinesAsync(value, Encoding.UTF8, CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllText(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllText(value, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllTextAsync(value, CancellationToken.None).GetAwaiter() + .GetResult()); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllTextAsync(value, Encoding.UTF8, CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadLines(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadLines(value, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_NET7 + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadLinesAsync(value, CancellationToken.None)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadLinesAsync(value, Encoding.UTF8, CancellationToken.None)); +#endif + yield return ( + ExceptionTestHelper.TestTypes.AllExceptInvalidPath | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file + => file.Replace(value, "foo", "bar")); + yield return ( + ExceptionTestHelper.TestTypes.All | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destinationFileName", + file + => file.Replace("foo", value, "bar")); + yield return ( + ExceptionTestHelper.TestTypes.AllExceptInvalidPath | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file + => file.Replace(value, "foo", "bar", false)); + yield return ( + ExceptionTestHelper.TestTypes.All | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destinationFileName", + file + => file.Replace("foo", value, "bar", false)); +#if FEATURE_FILESYSTEM_LINK + yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "linkPath", file + => file.ResolveLinkTarget(value, false)); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetAttributes(value, FileAttributes.ReadOnly)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetCreationTime(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetCreationTimeUtc(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetLastAccessTime(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetLastAccessTimeUtc(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetLastWriteTime(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetLastWriteTimeUtc(value, DateTime.Now)); +#if FEATURE_FILESYSTEM_UNIXFILEMODE + if (!Test.RunsOnWindows) + { + #pragma warning disable CA1416 + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetUnixFileMode(value, UnixFileMode.None)); + #pragma warning restore CA1416 + } +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllBytes(value, new byte[] + { + 0, 1 + })); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllBytesAsync(value, new byte[] + { + 0, 1 + }, + CancellationToken.None).GetAwaiter().GetResult()); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllLines(value, new[] + { + "foo" + })); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllLines(value, new[] + { + "foo" + }, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllLinesAsync(value, new[] + { + "foo" + }, CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllLinesAsync(value, new[] + { + "foo" + }, Encoding.UTF8, + CancellationToken.None).GetAwaiter().GetResult()); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllText(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllText(value, "foo", Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllTextAsync(value, "foo", CancellationToken.None).GetAwaiter() + .GetResult()); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllTextAsync(value, "foo", Encoding.UTF8, + CancellationToken.None).GetAwaiter().GetResult()); +#endif + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/MoveTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/MoveTests.cs index c6f6b6298..fa725a24b 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/MoveTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/MoveTests.cs @@ -1,247 +1,246 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class MoveTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Move_CaseOnlyChange_ShouldMoveFileWithContent( - string name, string contents) - { - string sourceName = name.ToLowerInvariant(); - string destinationName = name.ToUpperInvariant(); - FileSystem.File.WriteAllText(sourceName, contents); - - FileSystem.File.Move(sourceName, destinationName); - - if (Test.RunsOnLinux) - { - // sourceName and destinationName are considered different only on Linux - FileSystem.File.Exists(sourceName).Should().BeFalse(); - } - - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void - Move_DestinationDirectoryDoesNotExist_ShouldThrowDirectoryNotFoundException( - string source) - { - FileSystem.Initialize() - .WithFile(source); - string destination = FileTestHelper.RootDrive("not-existing/path"); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Move(source, destination); - }); - - exception.Should().BeException(hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Move_DestinationExists_ShouldThrowIOException_AndNotMoveFile( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Move(sourceName, destinationName); - }); - - exception.Should().BeException( - hResult: Test.RunsOnWindows ? -2147024713 : 17); - - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - } - -#if FEATURE_FILE_MOVETO_OVERWRITE - [SkippableTheory] - [AutoData] - public void Move_DestinationExists_WithOverwrite_ShouldOverwriteDestination( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - - FileSystem.File.Move(sourceName, destinationName, true); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - } -#endif - - [SkippableTheory] - [AutoData] - public void Move_ReadOnly_ShouldMoveFile( - string sourceName, string destinationName, string contents) - { - FileSystem.File.WriteAllText(sourceName, contents); - FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); - - FileSystem.File.Move(sourceName, destinationName); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.GetAttributes(destinationName) - .Should().HaveFlag(FileAttributes.ReadOnly); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void Move_ShouldMoveFileWithContent( - string sourceName, string destinationName, string contents) - { - FileSystem.File.WriteAllText(sourceName, contents); - - FileSystem.File.Move(sourceName, destinationName); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void Move_ShouldNotAdjustTimes(string source, string destination) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllText(source, "foo"); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - - FileSystem.File.Move(source, destination); - - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(destination); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(destination); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(destination); - - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - [SkippableTheory] - [AutoData] - public void Move_SourceAndDestinationIdentical_ShouldNotThrowException(string path) - { - FileSystem.Initialize() - .WithFile(path); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Move(path, path); - }); - - exception.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void Move_SourceDirectoryMissing_ShouldThrowFileNotFoundException( - string missingDirectory, - string sourceName, - string destinationName) - { - string sourcePath = FileSystem.Path.Combine(missingDirectory, sourceName); - Exception? exception = Record.Exception(() => - { - FileSystem.File.Move(sourcePath, destinationName); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(sourcePath)}'", - hResult: -2147024894); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Move_SourceLocked_ShouldThrowIOException_OnWindows( - string sourceName, - string destinationName) - { - FileSystem.File.WriteAllText(sourceName, null); - using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, - FileAccess.Read, FileShare.Read); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Move(sourceName, destinationName); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException( - hResult: -2147024864); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - else - { - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - } - } - - [SkippableTheory] - [AutoData] - public void Move_SourceMissing_CopyToItself_ShouldThrowFileNotFoundException( - string sourceName) - { - Exception? exception = Record.Exception(() => - { - FileSystem.File.Move(sourceName, sourceName); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(sourceName)}'", - hResult: -2147024894); - } - - [SkippableTheory] - [AutoData] - public void Move_SourceMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName) - { - Exception? exception = Record.Exception(() => - { - FileSystem.File.Move(sourceName, destinationName); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(sourceName)}'", - hResult: -2147024894); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class MoveTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Move_CaseOnlyChange_ShouldMoveFileWithContent( + string name, string contents) + { + string sourceName = name.ToLowerInvariant(); + string destinationName = name.ToUpperInvariant(); + FileSystem.File.WriteAllText(sourceName, contents); + + FileSystem.File.Move(sourceName, destinationName); + + if (Test.RunsOnLinux) + { + // sourceName and destinationName are considered different only on Linux + FileSystem.File.Exists(sourceName).Should().BeFalse(); + } + + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void + Move_DestinationDirectoryDoesNotExist_ShouldThrowDirectoryNotFoundException( + string source) + { + FileSystem.Initialize() + .WithFile(source); + string destination = FileTestHelper.RootDrive("not-existing/path"); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Move(source, destination); + }); + + exception.Should().BeException(hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Move_DestinationExists_ShouldThrowIOException_AndNotMoveFile( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Move(sourceName, destinationName); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147024713 : 17); + + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + } + +#if FEATURE_FILE_MOVETO_OVERWRITE + [SkippableTheory] + [AutoData] + public void Move_DestinationExists_WithOverwrite_ShouldOverwriteDestination( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + + FileSystem.File.Move(sourceName, destinationName, true); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + } +#endif + + [SkippableTheory] + [AutoData] + public void Move_ReadOnly_ShouldMoveFile( + string sourceName, string destinationName, string contents) + { + FileSystem.File.WriteAllText(sourceName, contents); + FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); + + FileSystem.File.Move(sourceName, destinationName); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.GetAttributes(destinationName) + .Should().HaveFlag(FileAttributes.ReadOnly); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void Move_ShouldMoveFileWithContent( + string sourceName, string destinationName, string contents) + { + FileSystem.File.WriteAllText(sourceName, contents); + + FileSystem.File.Move(sourceName, destinationName); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void Move_ShouldNotAdjustTimes(string source, string destination) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllText(source, "foo"); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + + FileSystem.File.Move(source, destination); + + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(destination); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(destination); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(destination); + + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + [SkippableTheory] + [AutoData] + public void Move_SourceAndDestinationIdentical_ShouldNotThrowException(string path) + { + FileSystem.Initialize() + .WithFile(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Move(path, path); + }); + + exception.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void Move_SourceDirectoryMissing_ShouldThrowFileNotFoundException( + string missingDirectory, + string sourceName, + string destinationName) + { + string sourcePath = FileSystem.Path.Combine(missingDirectory, sourceName); + Exception? exception = Record.Exception(() => + { + FileSystem.File.Move(sourcePath, destinationName); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(sourcePath)}'", + hResult: -2147024894); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Move_SourceLocked_ShouldThrowIOException_OnWindows( + string sourceName, + string destinationName) + { + FileSystem.File.WriteAllText(sourceName, null); + using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, + FileAccess.Read, FileShare.Read); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Move(sourceName, destinationName); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException( + hResult: -2147024864); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + else + { + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + } + } + + [SkippableTheory] + [AutoData] + public void Move_SourceMissing_CopyToItself_ShouldThrowFileNotFoundException( + string sourceName) + { + Exception? exception = Record.Exception(() => + { + FileSystem.File.Move(sourceName, sourceName); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(sourceName)}'", + hResult: -2147024894); + } + + [SkippableTheory] + [AutoData] + public void Move_SourceMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName) + { + Exception? exception = Record.Exception(() => + { + FileSystem.File.Move(sourceName, destinationName); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(sourceName)}'", + hResult: -2147024894); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenReadTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenReadTests.cs index 3cae5e389..2ba2939f5 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenReadTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenReadTests.cs @@ -1,142 +1,141 @@ -using System.IO; -using System.Threading.Tasks; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OpenReadTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void OpenRead_MissingFile_ShouldThrowFileNotFoundException(string path) - { - Exception? exception = Record.Exception(() => - { - _ = FileSystem.File.OpenRead(path); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - - [SkippableTheory] - [AutoData] - public void OpenRead_ShouldUseReadAccessAndReadShare(string path) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Read); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be( - Test.RunsOnWindows ? FileShare.Read : FileShare.ReadWrite); - stream.CanRead.Should().BeTrue(); - stream.CanWrite.Should().BeFalse(); - stream.CanSeek.Should().BeTrue(); - stream.CanTimeout.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void OpenRead_SetLength_ShouldThrowNotSupportedException(string path) - { - FileSystem.File.WriteAllText(path, null); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - stream.SetLength(3); - }); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [AutoData] - public void OpenRead_Write_ShouldThrowNotSupportedException(string path, byte[] bytes) - { - FileSystem.File.WriteAllText(path, null); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - stream.Write(bytes, 0, bytes.Length); - }); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [AutoData] - public async Task OpenRead_WriteAsync_ShouldThrowNotSupportedException( - string path, byte[] bytes) - { - // ReSharper disable once MethodHasAsyncOverload - FileSystem.File.WriteAllText(path, null); - - Exception? exception = await Record.ExceptionAsync(async () => - { - // ReSharper disable once UseAwaitUsing - using FileSystemStream stream = FileSystem.File.OpenRead(path); - #pragma warning disable CA1835 - await stream.WriteAsync(bytes, 0, bytes.Length); - #pragma warning restore CA1835 - }); - - exception.Should().BeException(hResult: -2146233067); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public async Task OpenRead_WriteAsyncWithMemory_ShouldThrowNotSupportedException( - string path, byte[] bytes) - { - await FileSystem.File.WriteAllTextAsync(path, null); - - Exception? exception = await Record.ExceptionAsync(async () => - { - await using FileSystemStream stream = FileSystem.File.OpenRead(path); - await stream.WriteAsync(bytes.AsMemory()); - }); - - exception.Should().BeException(hResult: -2146233067); - } -#endif - - [SkippableTheory] - [AutoData] - public void OpenRead_WriteByte_ShouldThrowNotSupportedException(string path) - { - FileSystem.File.WriteAllText(path, null); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - stream.WriteByte(0); - }); - - exception.Should().BeException(hResult: -2146233067); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public void OpenRead_WriteWithSpan_ShouldThrowNotSupportedException(string path, byte[] bytes) - { - FileSystem.File.WriteAllText(path, null); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - stream.Write(bytes.AsSpan()); - }); - - exception.Should().BeException(hResult: -2146233067); - } -#endif -} +using System.IO; +using System.Threading.Tasks; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OpenReadTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void OpenRead_MissingFile_ShouldThrowFileNotFoundException(string path) + { + Exception? exception = Record.Exception(() => + { + _ = FileSystem.File.OpenRead(path); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + + [SkippableTheory] + [AutoData] + public void OpenRead_ShouldUseReadAccessAndReadShare(string path) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Read); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be( + Test.RunsOnWindows ? FileShare.Read : FileShare.ReadWrite); + stream.CanRead.Should().BeTrue(); + stream.CanWrite.Should().BeFalse(); + stream.CanSeek.Should().BeTrue(); + stream.CanTimeout.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void OpenRead_SetLength_ShouldThrowNotSupportedException(string path) + { + FileSystem.File.WriteAllText(path, null); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + stream.SetLength(3); + }); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [AutoData] + public void OpenRead_Write_ShouldThrowNotSupportedException(string path, byte[] bytes) + { + FileSystem.File.WriteAllText(path, null); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + stream.Write(bytes, 0, bytes.Length); + }); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [AutoData] + public async Task OpenRead_WriteAsync_ShouldThrowNotSupportedException( + string path, byte[] bytes) + { + // ReSharper disable once MethodHasAsyncOverload + FileSystem.File.WriteAllText(path, null); + + Exception? exception = await Record.ExceptionAsync(async () => + { + // ReSharper disable once UseAwaitUsing + using FileSystemStream stream = FileSystem.File.OpenRead(path); + #pragma warning disable CA1835 + await stream.WriteAsync(bytes, 0, bytes.Length); + #pragma warning restore CA1835 + }); + + exception.Should().BeException(hResult: -2146233067); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public async Task OpenRead_WriteAsyncWithMemory_ShouldThrowNotSupportedException( + string path, byte[] bytes) + { + await FileSystem.File.WriteAllTextAsync(path, null); + + Exception? exception = await Record.ExceptionAsync(async () => + { + await using FileSystemStream stream = FileSystem.File.OpenRead(path); + await stream.WriteAsync(bytes.AsMemory()); + }); + + exception.Should().BeException(hResult: -2146233067); + } +#endif + + [SkippableTheory] + [AutoData] + public void OpenRead_WriteByte_ShouldThrowNotSupportedException(string path) + { + FileSystem.File.WriteAllText(path, null); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + stream.WriteByte(0); + }); + + exception.Should().BeException(hResult: -2146233067); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public void OpenRead_WriteWithSpan_ShouldThrowNotSupportedException(string path, byte[] bytes) + { + FileSystem.File.WriteAllText(path, null); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + stream.Write(bytes.AsSpan()); + }); + + exception.Should().BeException(hResult: -2146233067); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenTests.cs index 7265e3d3a..0d2e8c24a 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenTests.cs @@ -1,142 +1,141 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OpenTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Open_ExistingFileWithCreateNewMode_ShouldThrowIOException( - string path) - { - FileSystem.File.WriteAllText(path, null); - - Exception? exception = Record.Exception(() => - { - _ = FileSystem.File.Open(path, FileMode.CreateNew); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: Test.RunsOnWindows ? -2147024816 : 17); - } - - [SkippableTheory] - [AutoData] - public void Open_MissingFileAndIncorrectMode_ShouldThrowFileNotFoundException( - string path) - { - Exception? exception = Record.Exception(() => - { - _ = FileSystem.File.Open(path, FileMode.Open); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - - [SkippableTheory] - [InlineAutoData(FileMode.Open, FileAccess.Read)] - [InlineAutoData(FileMode.OpenOrCreate, FileAccess.ReadWrite)] - public void Open_ShouldNotAdjustTimes(FileMode mode, FileAccess access, string path) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllText(path, "foo"); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - - FileSystemStream stream = FileSystem.File.Open(path, mode, access); - stream.Dispose(); - - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - [SkippableTheory] - [InlineAutoData(FileMode.Append, FileAccess.Write)] - [InlineAutoData(FileMode.Open, FileAccess.ReadWrite)] - [InlineAutoData(FileMode.Create, FileAccess.ReadWrite)] - public void Open_ShouldUseExpectedAccessDependingOnMode( - FileMode mode, - FileAccess expectedAccess, - string path) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = FileSystem.File.Open(path, mode); - - FileTestHelper.CheckFileAccess(stream).Should().Be(expectedAccess); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } - - [SkippableTheory] - [InlineAutoData(FileAccess.Read, FileShare.Write)] - [InlineAutoData(FileAccess.Write, FileShare.Read)] - public void Open_ShouldUseGivenAccessAndShare(string path, - FileAccess access, - FileShare share) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = - FileSystem.File.Open(path, FileMode.Open, access, share); - - FileTestHelper.CheckFileAccess(stream).Should().Be(access); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); - } - - [SkippableTheory] - [AutoData] - public void Open_ShouldUseNoneShareAsDefault(string path, - FileAccess access) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = FileSystem.File.Open(path, FileMode.Open, access); - - FileTestHelper.CheckFileAccess(stream).Should().Be(access); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } - -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - [SkippableTheory] - [InlineAutoData(FileAccess.Read, FileShare.Write)] - [InlineAutoData(FileAccess.Write, FileShare.Read)] - public void Open_WithFileStreamOptions_ShouldUseGivenAccessAndShare( - string path, - FileAccess access, - FileShare share) - { - FileSystem.File.WriteAllText(path, null); - FileStreamOptions options = new() - { - Mode = FileMode.Open, - Access = access, - Share = share - }; - - using FileSystemStream stream = FileSystem.File.Open(path, options); - - FileTestHelper.CheckFileAccess(stream).Should().Be(access); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); - } -#endif -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OpenTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Open_ExistingFileWithCreateNewMode_ShouldThrowIOException( + string path) + { + FileSystem.File.WriteAllText(path, null); + + Exception? exception = Record.Exception(() => + { + _ = FileSystem.File.Open(path, FileMode.CreateNew); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: Test.RunsOnWindows ? -2147024816 : 17); + } + + [SkippableTheory] + [AutoData] + public void Open_MissingFileAndIncorrectMode_ShouldThrowFileNotFoundException( + string path) + { + Exception? exception = Record.Exception(() => + { + _ = FileSystem.File.Open(path, FileMode.Open); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + + [SkippableTheory] + [InlineAutoData(FileMode.Open, FileAccess.Read)] + [InlineAutoData(FileMode.OpenOrCreate, FileAccess.ReadWrite)] + public void Open_ShouldNotAdjustTimes(FileMode mode, FileAccess access, string path) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllText(path, "foo"); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + + FileSystemStream stream = FileSystem.File.Open(path, mode, access); + stream.Dispose(); + + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + [SkippableTheory] + [InlineAutoData(FileMode.Append, FileAccess.Write)] + [InlineAutoData(FileMode.Open, FileAccess.ReadWrite)] + [InlineAutoData(FileMode.Create, FileAccess.ReadWrite)] + public void Open_ShouldUseExpectedAccessDependingOnMode( + FileMode mode, + FileAccess expectedAccess, + string path) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = FileSystem.File.Open(path, mode); + + FileTestHelper.CheckFileAccess(stream).Should().Be(expectedAccess); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } + + [SkippableTheory] + [InlineAutoData(FileAccess.Read, FileShare.Write)] + [InlineAutoData(FileAccess.Write, FileShare.Read)] + public void Open_ShouldUseGivenAccessAndShare(string path, + FileAccess access, + FileShare share) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = + FileSystem.File.Open(path, FileMode.Open, access, share); + + FileTestHelper.CheckFileAccess(stream).Should().Be(access); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); + } + + [SkippableTheory] + [AutoData] + public void Open_ShouldUseNoneShareAsDefault(string path, + FileAccess access) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = FileSystem.File.Open(path, FileMode.Open, access); + + FileTestHelper.CheckFileAccess(stream).Should().Be(access); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } + +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + [SkippableTheory] + [InlineAutoData(FileAccess.Read, FileShare.Write)] + [InlineAutoData(FileAccess.Write, FileShare.Read)] + public void Open_WithFileStreamOptions_ShouldUseGivenAccessAndShare( + string path, + FileAccess access, + FileShare share) + { + FileSystem.File.WriteAllText(path, null); + FileStreamOptions options = new() + { + Mode = FileMode.Open, + Access = access, + Share = share + }; + + using FileSystemStream stream = FileSystem.File.Open(path, options); + + FileTestHelper.CheckFileAccess(stream).Should().Be(access); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenWriteTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenWriteTests.cs index f3aaa3c3a..9bc204cf3 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenWriteTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenWriteTests.cs @@ -1,71 +1,70 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OpenWriteTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void OpenWrite_MissingFile_ShouldCreateFile(string path) - { - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void OpenWrite_ShouldOverwriteExistingFile(string path, string previousContent) - { - FileSystem.File.WriteAllText(path, previousContent); - - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - using (StreamWriter streamWriter = new(stream)) - { - streamWriter.Write("new-content"); - } - - stream.Dispose(); - string result = FileSystem.File.ReadAllText(path); - - result.Should().StartWith("new-content"); - result.Length.Should().Be(previousContent.Length); - } - - [SkippableTheory] - [AutoData] - public void OpenWrite_ShouldUseWriteAccessAndNoneShare(string path) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Write); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - stream.CanRead.Should().BeFalse(); - stream.CanWrite.Should().BeTrue(); - stream.CanSeek.Should().BeTrue(); - stream.CanTimeout.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void OpenWrite_StreamShouldNotThrowExceptionWhenReading(string path) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - _ = stream.ReadByte(); - }); - - exception.Should().BeNull(); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OpenWriteTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void OpenWrite_MissingFile_ShouldCreateFile(string path) + { + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void OpenWrite_ShouldOverwriteExistingFile(string path, string previousContent) + { + FileSystem.File.WriteAllText(path, previousContent); + + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + using (StreamWriter streamWriter = new(stream)) + { + streamWriter.Write("new-content"); + } + + stream.Dispose(); + string result = FileSystem.File.ReadAllText(path); + + result.Should().StartWith("new-content"); + result.Length.Should().Be(previousContent.Length); + } + + [SkippableTheory] + [AutoData] + public void OpenWrite_ShouldUseWriteAccessAndNoneShare(string path) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Write); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + stream.CanRead.Should().BeFalse(); + stream.CanWrite.Should().BeTrue(); + stream.CanSeek.Should().BeTrue(); + stream.CanTimeout.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void OpenWrite_StreamShouldNotThrowExceptionWhenReading(string path) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + _ = stream.ReadByte(); + }); + + exception.Should().BeNull(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/ReplaceTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/ReplaceTests.cs index a2c65ecb3..b0b9f9350 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/ReplaceTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/ReplaceTests.cs @@ -1,281 +1,280 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ReplaceTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - Replace_DestinationDirectoryDoesNotExist_ShouldThrowCorrectException( - string source) - { - FileSystem.Initialize() - .WithFile(source); - string destination = FileTestHelper.RootDrive("not-existing/path/foo.txt"); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Replace(source, destination, null); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024893); - } - else - { - exception.Should().BeException(hResult: -2147024894); - } - } - - [SkippableTheory] - [AutoData] - public void Replace_DestinationIsDirectory_ShouldThrowUnauthorizedAccessException( - string sourceName, - string destinationName, - string backupName) - { - FileSystem.File.WriteAllText(sourceName, null); - FileSystem.Directory.CreateDirectory(destinationName); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Replace(sourceName, destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024891); - } - - [SkippableTheory] - [AutoData] - public void Replace_DestinationMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName, - string backupName) - { - FileSystem.File.WriteAllText(sourceName, null); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Replace(sourceName, destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024894); - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Replace_ReadOnly_WithIgnoreMetadataError_ShouldReplaceFile( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); - - FileSystem.File.Replace(sourceName, destinationName, backupName, true); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.GetAttributes(destinationName) - .Should().HaveFlag(FileAttributes.ReadOnly); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - - [SkippableTheory] - [AutoData] - public void - Replace_ReadOnly_WithoutIgnoreMetadataError_ShouldThrowUnauthorizedAccessException_OnWindows( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Replace(sourceName, destinationName, backupName); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024891); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - FileSystem.File.GetAttributes(destinationName) - .Should().NotHaveFlag(FileAttributes.ReadOnly); - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - else - { - exception.Should().BeNull(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - } - - [SkippableTheory] - [AutoData] - public void Replace_ShouldReplaceFile( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - - FileSystem.File.Replace(sourceName, destinationName, backupName); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - - [SkippableTheory] - [AutoData] - public void Replace_SourceIsDirectory_ShouldThrowUnauthorizedAccessException( - string sourceName, - string destinationName, - string backupName) - { - Skip.IfNot(Test.RunsOnWindows, "Tests sometimes throw IOException on Linux"); - - FileSystem.Directory.CreateDirectory(sourceName); - FileSystem.File.WriteAllText(destinationName, null); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Replace(sourceName, destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024891); - } - - [SkippableTheory] - [AutoData] - public void Replace_SourceLocked_ShouldThrowIOException_OnWindows( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, - FileAccess.Read, FileShare.Read); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Replace(sourceName, destinationName, backupName); - }); - - stream.Dispose(); - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024864); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - FileSystem.File.GetAttributes(destinationName) - .Should().NotHaveFlag(FileAttributes.ReadOnly); - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - else - { - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - } - - [SkippableTheory] - [AutoData] - public void Replace_SourceMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName, - string backupName) - { - FileSystem.File.WriteAllText(destinationName, null); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Replace(sourceName, destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024894); - if (Test.RunsOnWindows) - { - // Behaviour on Linux/MacOS is uncertain - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void Replace_WithExistingBackupFile_ShouldIgnoreBackup( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents, - string backupContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - FileSystem.File.WriteAllText(backupName, backupContents); - - FileSystem.File.Replace(sourceName, destinationName, null); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(backupContents); - } - - [SkippableTheory] - [AutoData] - public void Replace_WithoutBackup_ShouldReplaceFile( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - - FileSystem.File.Replace(sourceName, destinationName, null); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ReplaceTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + Replace_DestinationDirectoryDoesNotExist_ShouldThrowCorrectException( + string source) + { + FileSystem.Initialize() + .WithFile(source); + string destination = FileTestHelper.RootDrive("not-existing/path/foo.txt"); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Replace(source, destination, null); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024893); + } + else + { + exception.Should().BeException(hResult: -2147024894); + } + } + + [SkippableTheory] + [AutoData] + public void Replace_DestinationIsDirectory_ShouldThrowUnauthorizedAccessException( + string sourceName, + string destinationName, + string backupName) + { + FileSystem.File.WriteAllText(sourceName, null); + FileSystem.Directory.CreateDirectory(destinationName); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Replace(sourceName, destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024891); + } + + [SkippableTheory] + [AutoData] + public void Replace_DestinationMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName, + string backupName) + { + FileSystem.File.WriteAllText(sourceName, null); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Replace(sourceName, destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024894); + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Replace_ReadOnly_WithIgnoreMetadataError_ShouldReplaceFile( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); + + FileSystem.File.Replace(sourceName, destinationName, backupName, true); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.GetAttributes(destinationName) + .Should().HaveFlag(FileAttributes.ReadOnly); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + + [SkippableTheory] + [AutoData] + public void + Replace_ReadOnly_WithoutIgnoreMetadataError_ShouldThrowUnauthorizedAccessException_OnWindows( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Replace(sourceName, destinationName, backupName); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024891); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + FileSystem.File.GetAttributes(destinationName) + .Should().NotHaveFlag(FileAttributes.ReadOnly); + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + else + { + exception.Should().BeNull(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + } + + [SkippableTheory] + [AutoData] + public void Replace_ShouldReplaceFile( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + + FileSystem.File.Replace(sourceName, destinationName, backupName); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + + [SkippableTheory] + [AutoData] + public void Replace_SourceIsDirectory_ShouldThrowUnauthorizedAccessException( + string sourceName, + string destinationName, + string backupName) + { + Skip.IfNot(Test.RunsOnWindows, "Tests sometimes throw IOException on Linux"); + + FileSystem.Directory.CreateDirectory(sourceName); + FileSystem.File.WriteAllText(destinationName, null); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Replace(sourceName, destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024891); + } + + [SkippableTheory] + [AutoData] + public void Replace_SourceLocked_ShouldThrowIOException_OnWindows( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, + FileAccess.Read, FileShare.Read); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Replace(sourceName, destinationName, backupName); + }); + + stream.Dispose(); + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024864); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + FileSystem.File.GetAttributes(destinationName) + .Should().NotHaveFlag(FileAttributes.ReadOnly); + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + else + { + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + } + + [SkippableTheory] + [AutoData] + public void Replace_SourceMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName, + string backupName) + { + FileSystem.File.WriteAllText(destinationName, null); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Replace(sourceName, destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024894); + if (Test.RunsOnWindows) + { + // Behaviour on Linux/MacOS is uncertain + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void Replace_WithExistingBackupFile_ShouldIgnoreBackup( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents, + string backupContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + FileSystem.File.WriteAllText(backupName, backupContents); + + FileSystem.File.Replace(sourceName, destinationName, null); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(backupContents); + } + + [SkippableTheory] + [AutoData] + public void Replace_WithoutBackup_ShouldReplaceFile( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + + FileSystem.File.Replace(sourceName, destinationName, null); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/ResolveLinkTargetTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/ResolveLinkTargetTests.cs index 774e8cbdf..9833c379d 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/ResolveLinkTargetTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/ResolveLinkTargetTests.cs @@ -1,192 +1,191 @@ -#if FEATURE_FILESYSTEM_LINK -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ResolveLinkTargetTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - #region Test Setup - - /// - /// The maximum number of symbolic links that are followed.
- /// - ///
- private static int MaxResolveLinks => - Test.RunsOnWindows ? 63 : 40; - - #endregion - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_AbsolutePath_ShouldFollowSymbolicLink( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget, null); - FileSystem.File.CreateSymbolicLink(path, targetFullPath); - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(path, false); - - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FileWithDifferentCase_ShouldReturnPathToMissingFile( - string path, string pathToTarget, string contents) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget, "foo"); - FileSystem.File.CreateSymbolicLink(path, targetFullPath); - FileSystem.File.Delete(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget.ToUpper(), contents); - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(path, false); - - if (!Test.RunsOnLinux) - { - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeTrue(); - FileSystem.File.ReadAllText(target.FullName).Should().Be(contents); - } - else - { - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FinalTarget_ShouldFollowSymbolicLinkToFinalTarget( - string path, string pathToFinalTarget) - { - int maxLinks = MaxResolveLinks; - - FileSystem.File.WriteAllText(pathToFinalTarget, null); - string previousPath = pathToFinalTarget; - for (int i = 0; i < maxLinks; i++) - { - string newPath = $"{path}-{i}"; - FileSystem.File.CreateSymbolicLink(newPath, - System.IO.Path.Combine(BasePath, previousPath)); - previousPath = newPath; - } - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(previousPath, true); - - target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToFinalTarget)); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FinalTargetWithTooManyLevels_ShouldThrowIOException( - string path, string pathToFinalTarget) - { - int maxLinks = MaxResolveLinks + 1; - FileSystem.File.WriteAllText(pathToFinalTarget, null); - string previousPath = pathToFinalTarget; - for (int i = 0; i < maxLinks; i++) - { - string newPath = $"{path}-{i}"; - FileSystem.File.CreateSymbolicLink(newPath, - System.IO.Path.Combine(BasePath, previousPath)); - previousPath = newPath; - } - - Exception? exception = Record.Exception(() => - { - _ = FileSystem.File.ResolveLinkTarget(previousPath, true); - }); - - exception.Should().BeException($"'{previousPath}'", - hResult: Test.RunsOnWindows ? -2147022975 : -2146232800); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_MissingFileInLinkChain_ShouldReturnPathToMissingFile( - string path, string pathToFinalTarget, string pathToMissingFile) - { - FileSystem.File.WriteAllText(pathToFinalTarget, null); - FileSystem.File.CreateSymbolicLink(pathToMissingFile, - System.IO.Path.Combine(BasePath, pathToFinalTarget)); - FileSystem.File.CreateSymbolicLink(path, - System.IO.Path.Combine(BasePath, pathToMissingFile)); - FileSystem.File.Delete(pathToMissingFile); - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(path, true); - - target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToMissingFile)); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_NormalDirectory_ShouldReturnNull( - string path) - { - FileSystem.Directory.CreateDirectory(path); - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(path, false); - - target.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_NormalFile_ShouldReturnNull( - string path) - { - FileSystem.File.WriteAllText(path, null); - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(path, false); - - target.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_RelativePath_ShouldFollowSymbolicLinkUnderWindows( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget, null); - FileSystem.File.CreateSymbolicLink(path, targetFullPath); - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(path, false); - - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_TargetDeletedAfterLinkCreation_ShouldReturnNull( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget, null); - FileSystem.File.CreateSymbolicLink(path, targetFullPath); - FileSystem.File.Delete(pathToTarget); - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(path, false); - - target!.FullName.Should().Be(targetFullPath); - - target.Exists.Should().BeFalse(); - } -} -#endif +#if FEATURE_FILESYSTEM_LINK +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ResolveLinkTargetTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + #region Test Setup + + /// + /// The maximum number of symbolic links that are followed.
+ /// + ///
+ private static int MaxResolveLinks => + Test.RunsOnWindows ? 63 : 40; + + #endregion + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_AbsolutePath_ShouldFollowSymbolicLink( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget, null); + FileSystem.File.CreateSymbolicLink(path, targetFullPath); + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(path, false); + + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FileWithDifferentCase_ShouldReturnPathToMissingFile( + string path, string pathToTarget, string contents) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget, "foo"); + FileSystem.File.CreateSymbolicLink(path, targetFullPath); + FileSystem.File.Delete(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget.ToUpper(), contents); + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(path, false); + + if (!Test.RunsOnLinux) + { + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeTrue(); + FileSystem.File.ReadAllText(target.FullName).Should().Be(contents); + } + else + { + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FinalTarget_ShouldFollowSymbolicLinkToFinalTarget( + string path, string pathToFinalTarget) + { + int maxLinks = MaxResolveLinks; + + FileSystem.File.WriteAllText(pathToFinalTarget, null); + string previousPath = pathToFinalTarget; + for (int i = 0; i < maxLinks; i++) + { + string newPath = $"{path}-{i}"; + FileSystem.File.CreateSymbolicLink(newPath, + System.IO.Path.Combine(BasePath, previousPath)); + previousPath = newPath; + } + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(previousPath, true); + + target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToFinalTarget)); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FinalTargetWithTooManyLevels_ShouldThrowIOException( + string path, string pathToFinalTarget) + { + int maxLinks = MaxResolveLinks + 1; + FileSystem.File.WriteAllText(pathToFinalTarget, null); + string previousPath = pathToFinalTarget; + for (int i = 0; i < maxLinks; i++) + { + string newPath = $"{path}-{i}"; + FileSystem.File.CreateSymbolicLink(newPath, + System.IO.Path.Combine(BasePath, previousPath)); + previousPath = newPath; + } + + Exception? exception = Record.Exception(() => + { + _ = FileSystem.File.ResolveLinkTarget(previousPath, true); + }); + + exception.Should().BeException($"'{previousPath}'", + hResult: Test.RunsOnWindows ? -2147022975 : -2146232800); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_MissingFileInLinkChain_ShouldReturnPathToMissingFile( + string path, string pathToFinalTarget, string pathToMissingFile) + { + FileSystem.File.WriteAllText(pathToFinalTarget, null); + FileSystem.File.CreateSymbolicLink(pathToMissingFile, + System.IO.Path.Combine(BasePath, pathToFinalTarget)); + FileSystem.File.CreateSymbolicLink(path, + System.IO.Path.Combine(BasePath, pathToMissingFile)); + FileSystem.File.Delete(pathToMissingFile); + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(path, true); + + target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToMissingFile)); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_NormalDirectory_ShouldReturnNull( + string path) + { + FileSystem.Directory.CreateDirectory(path); + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(path, false); + + target.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_NormalFile_ShouldReturnNull( + string path) + { + FileSystem.File.WriteAllText(path, null); + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(path, false); + + target.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_RelativePath_ShouldFollowSymbolicLinkUnderWindows( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget, null); + FileSystem.File.CreateSymbolicLink(path, targetFullPath); + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(path, false); + + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_TargetDeletedAfterLinkCreation_ShouldReturnNull( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget, null); + FileSystem.File.CreateSymbolicLink(path, targetFullPath); + FileSystem.File.Delete(pathToTarget); + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(path, false); + + target!.FullName.Should().Be(targetFullPath); + + target.Exists.Should().BeFalse(); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/AppendTextTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/AppendTextTests.cs index 9dae9e85d..142c3f610 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/AppendTextTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/AppendTextTests.cs @@ -1,46 +1,45 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class AppendTextTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void AppendText_MissingFile_ShouldCreateFile( - string path, string appendText) - { - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - using (StreamWriter stream = fileInfo.AppendText()) - { - stream.Write(appendText); - } - - string result = FileSystem.File.ReadAllText(path); - - result.Should().Be(appendText); - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void AppendText_ShouldAddTextToExistingFile( - string path, string contents, string appendText) - { - FileSystem.File.WriteAllText(path, contents); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - using (StreamWriter stream = fileInfo.AppendText()) - { - stream.Write(appendText); - } - - string result = FileSystem.File.ReadAllText(path); - - result.Should().Be(contents + appendText); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class AppendTextTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void AppendText_MissingFile_ShouldCreateFile( + string path, string appendText) + { + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + using (StreamWriter stream = fileInfo.AppendText()) + { + stream.Write(appendText); + } + + string result = FileSystem.File.ReadAllText(path); + + result.Should().Be(appendText); + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void AppendText_ShouldAddTextToExistingFile( + string path, string contents, string appendText) + { + FileSystem.File.WriteAllText(path, contents); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + using (StreamWriter stream = fileInfo.AppendText()) + { + stream.Write(appendText); + } + + string result = FileSystem.File.ReadAllText(path); + + result.Should().Be(contents + appendText); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CopyToTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CopyToTests.cs index 9c11ab467..549b05d3f 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CopyToTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CopyToTests.cs @@ -1,235 +1,234 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class CopyToTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void CopyTo_DestinationExists_ShouldThrowIOException_AndNotCopyFile( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.CopyTo(destinationName); - }); - - exception.Should().BeException( - hResult: Test.RunsOnWindows ? -2147024816 : 17); - sut.Exists.Should().BeTrue(); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - } - -#if FEATURE_FILE_MOVETO_OVERWRITE - [SkippableTheory] - [AutoData] - public void CopyTo_DestinationExists_WithOverwrite_ShouldOverwriteDestination( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - IFileInfo result = sut.CopyTo(destinationName, true); - - sut.Exists.Should().BeTrue(); - sut.FullName.Should().Be(FileSystem.Path.GetFullPath(sourceName)); - result.Exists.Should().BeTrue(); - result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - } -#endif - - [SkippableTheory] - [AutoData] - public void CopyTo_ReadOnly_ShouldCopyFile( - string sourceName, string destinationName, string contents) - { - FileSystem.File.WriteAllText(sourceName, contents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - sut.IsReadOnly = true; - - sut.CopyTo(destinationName); - - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.GetAttributes(destinationName) - .Should().HaveFlag(FileAttributes.ReadOnly); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void CopyTo_ShouldAddArchiveAttribute_OnWindows( - string sourceName, - string destinationName, - string contents, - FileAttributes fileAttributes) - { - FileSystem.File.WriteAllText(sourceName, contents); - FileSystem.File.SetAttributes(sourceName, fileAttributes); - FileAttributes expectedAttributes = FileSystem.File.GetAttributes(sourceName); - if (Test.RunsOnWindows) - { - expectedAttributes |= FileAttributes.Archive; - } - - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - IFileInfo result = sut.CopyTo(destinationName); - - result.Attributes.Should().Be(expectedAttributes); - FileSystem.File.GetAttributes(destinationName) - .Should().Be(expectedAttributes); - } - - [SkippableTheory] - [AutoData] - public void CopyTo_ShouldCopyFileWithContent( - string sourceName, string destinationName, string contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText(sourceName, contents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - TimeSystem.Thread.Sleep(1000); - - IFileInfo result = sut.CopyTo(destinationName); - - sut.FullName.Should().Be(FileSystem.Path.GetFullPath(sourceName)); - sut.Exists.Should().BeTrue(); - result.Exists.Should().BeTrue(); - result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(contents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void CopyTo_ShouldKeepMetadata( - string sourceName, - string destinationName, - string contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText(sourceName, contents); - DateTime sourceCreationTime = FileSystem.File.GetCreationTime(sourceName); - DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTime(sourceName); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - TimeSystem.Thread.Sleep(1000); - - DateTime updatedTime = TimeSystem.DateTime.Now; - sut.CopyTo(destinationName); - - if (Test.RunsOnWindows) - { - FileSystem.File.GetCreationTime(destinationName) - .Should().BeOnOrAfter(updatedTime.ApplySystemClockTolerance()); - } - else - { - FileSystem.File.GetCreationTime(destinationName) - .Should().Be(sourceCreationTime); - } - - FileSystem.File.GetLastAccessTime(destinationName) - .Should().BeOnOrAfter(updatedTime.ApplySystemClockTolerance()); - FileSystem.File.GetLastWriteTime(destinationName) - .Should().Be(sourceLastWriteTime); - } - - [SkippableTheory] - [AutoData] - public void CopyTo_SourceIsDirectory_ShouldThrowUnauthorizedAccessException_AndNotCopyFile( - string sourceName, - string destinationName) - { - FileSystem.Directory.CreateDirectory(sourceName); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.CopyTo(destinationName); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(sourceName)}'", - hResult: -2147024891); - FileSystem.Directory.Exists(sourceName).Should().BeTrue(); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void CopyTo_SourceLocked_ShouldThrowIOException( - string sourceName, - string destinationName) - { - FileSystem.File.WriteAllText(sourceName, null); - using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, - FileAccess.Read, FileShare.None); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.CopyTo(destinationName); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024864); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - else - { - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void CopyTo_SourceMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName) - { - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.CopyTo(destinationName); - }); - - exception.Should().BeException( - hResult: -2147024894, - messageContains: Test.IsNetFramework - ? null - : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class CopyToTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void CopyTo_DestinationExists_ShouldThrowIOException_AndNotCopyFile( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.CopyTo(destinationName); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147024816 : 17); + sut.Exists.Should().BeTrue(); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + } + +#if FEATURE_FILE_MOVETO_OVERWRITE + [SkippableTheory] + [AutoData] + public void CopyTo_DestinationExists_WithOverwrite_ShouldOverwriteDestination( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + IFileInfo result = sut.CopyTo(destinationName, true); + + sut.Exists.Should().BeTrue(); + sut.FullName.Should().Be(FileSystem.Path.GetFullPath(sourceName)); + result.Exists.Should().BeTrue(); + result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + } +#endif + + [SkippableTheory] + [AutoData] + public void CopyTo_ReadOnly_ShouldCopyFile( + string sourceName, string destinationName, string contents) + { + FileSystem.File.WriteAllText(sourceName, contents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + sut.IsReadOnly = true; + + sut.CopyTo(destinationName); + + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.GetAttributes(destinationName) + .Should().HaveFlag(FileAttributes.ReadOnly); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void CopyTo_ShouldAddArchiveAttribute_OnWindows( + string sourceName, + string destinationName, + string contents, + FileAttributes fileAttributes) + { + FileSystem.File.WriteAllText(sourceName, contents); + FileSystem.File.SetAttributes(sourceName, fileAttributes); + FileAttributes expectedAttributes = FileSystem.File.GetAttributes(sourceName); + if (Test.RunsOnWindows) + { + expectedAttributes |= FileAttributes.Archive; + } + + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + IFileInfo result = sut.CopyTo(destinationName); + + result.Attributes.Should().Be(expectedAttributes); + FileSystem.File.GetAttributes(destinationName) + .Should().Be(expectedAttributes); + } + + [SkippableTheory] + [AutoData] + public void CopyTo_ShouldCopyFileWithContent( + string sourceName, string destinationName, string contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText(sourceName, contents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + TimeSystem.Thread.Sleep(1000); + + IFileInfo result = sut.CopyTo(destinationName); + + sut.FullName.Should().Be(FileSystem.Path.GetFullPath(sourceName)); + sut.Exists.Should().BeTrue(); + result.Exists.Should().BeTrue(); + result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(contents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void CopyTo_ShouldKeepMetadata( + string sourceName, + string destinationName, + string contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText(sourceName, contents); + DateTime sourceCreationTime = FileSystem.File.GetCreationTime(sourceName); + DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTime(sourceName); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + TimeSystem.Thread.Sleep(1000); + + DateTime updatedTime = TimeSystem.DateTime.Now; + sut.CopyTo(destinationName); + + if (Test.RunsOnWindows) + { + FileSystem.File.GetCreationTime(destinationName) + .Should().BeOnOrAfter(updatedTime.ApplySystemClockTolerance()); + } + else + { + FileSystem.File.GetCreationTime(destinationName) + .Should().Be(sourceCreationTime); + } + + FileSystem.File.GetLastAccessTime(destinationName) + .Should().BeOnOrAfter(updatedTime.ApplySystemClockTolerance()); + FileSystem.File.GetLastWriteTime(destinationName) + .Should().Be(sourceLastWriteTime); + } + + [SkippableTheory] + [AutoData] + public void CopyTo_SourceIsDirectory_ShouldThrowUnauthorizedAccessException_AndNotCopyFile( + string sourceName, + string destinationName) + { + FileSystem.Directory.CreateDirectory(sourceName); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.CopyTo(destinationName); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(sourceName)}'", + hResult: -2147024891); + FileSystem.Directory.Exists(sourceName).Should().BeTrue(); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void CopyTo_SourceLocked_ShouldThrowIOException( + string sourceName, + string destinationName) + { + FileSystem.File.WriteAllText(sourceName, null); + using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, + FileAccess.Read, FileShare.None); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.CopyTo(destinationName); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024864); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + else + { + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void CopyTo_SourceMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName) + { + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.CopyTo(destinationName); + }); + + exception.Should().BeException( + hResult: -2147024894, + messageContains: Test.IsNetFramework + ? null + : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTests.cs index b8e55adc8..1fac81244 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTests.cs @@ -1,56 +1,55 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class CreateTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Create_MissingFile_ShouldCreateFile(string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - FileSystem.File.Exists(path).Should().BeFalse(); - - using FileSystemStream stream = sut.Create(); - - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void Create_ShouldRefreshExistsCache_ExceptOnNetFramework(string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - sut.Exists.Should().BeFalse(); - - using FileSystemStream stream = sut.Create(); - - if (Test.IsNetFramework) - { - sut.Exists.Should().BeFalse(); - } - else - { - sut.Exists.Should().BeTrue(); - } - - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void Create_ShouldUseReadWriteAccessAndNoneShare(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Create(); - - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class CreateTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Create_MissingFile_ShouldCreateFile(string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + FileSystem.File.Exists(path).Should().BeFalse(); + + using FileSystemStream stream = sut.Create(); + + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void Create_ShouldRefreshExistsCache_ExceptOnNetFramework(string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + sut.Exists.Should().BeFalse(); + + using FileSystemStream stream = sut.Create(); + + if (Test.IsNetFramework) + { + sut.Exists.Should().BeFalse(); + } + else + { + sut.Exists.Should().BeTrue(); + } + + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void Create_ShouldUseReadWriteAccessAndNoneShare(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Create(); + + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTextTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTextTests.cs index 2502b8e96..6c0dae4ec 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTextTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTextTests.cs @@ -1,63 +1,62 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class CreateTextTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void CreateText_MissingFile_ShouldCreateFile( - string path, string appendText) - { - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - using (StreamWriter stream = fileInfo.CreateText()) - { - stream.Write(appendText); - } - - string result = FileSystem.File.ReadAllText(path); - - result.Should().Be(appendText); - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void CreateText_ShouldNotRefreshExistsCache( - string path, string appendText) - { - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - fileInfo.Exists.Should().BeFalse(); - - using (StreamWriter stream = fileInfo.CreateText()) - { - stream.Write(appendText); - } - - fileInfo.Exists.Should().BeFalse(); - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void CreateText_ShouldReplaceTextInExistingFile( - string path, string contents, string appendText) - { - FileSystem.File.WriteAllText(path, contents); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - using (StreamWriter stream = fileInfo.CreateText()) - { - stream.Write(appendText); - } - - string result = FileSystem.File.ReadAllText(path); - - result.Should().Be(appendText); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class CreateTextTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void CreateText_MissingFile_ShouldCreateFile( + string path, string appendText) + { + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + using (StreamWriter stream = fileInfo.CreateText()) + { + stream.Write(appendText); + } + + string result = FileSystem.File.ReadAllText(path); + + result.Should().Be(appendText); + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void CreateText_ShouldNotRefreshExistsCache( + string path, string appendText) + { + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + fileInfo.Exists.Should().BeFalse(); + + using (StreamWriter stream = fileInfo.CreateText()) + { + stream.Write(appendText); + } + + fileInfo.Exists.Should().BeFalse(); + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void CreateText_ShouldReplaceTextInExistingFile( + string path, string contents, string appendText) + { + FileSystem.File.WriteAllText(path, contents); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + using (StreamWriter stream = fileInfo.CreateText()) + { + stream.Write(appendText); + } + + string result = FileSystem.File.ReadAllText(path); + + result.Should().Be(appendText); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/DeleteTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/DeleteTests.cs index f47c879ee..d8de2f6a3 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/DeleteTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/DeleteTests.cs @@ -1,96 +1,95 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class DeleteTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string missingDirectory, string fileName) - { - string filePath = FileSystem.Path.Combine(missingDirectory, fileName); - - Exception? exception = Record.Exception(() => - { - FileSystem.FileInfo.New(filePath).Delete(); - }); - - exception.Should().BeException(hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Delete_MissingFile_ShouldDoNothing( - string fileName) - { - Exception? exception = Record.Exception(() => - { - FileSystem.FileInfo.New(fileName).Delete(); - }); - - exception.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void Delete_ShouldRefreshExistsCache_ExceptOnNetFramework(string path) - { - FileSystem.File.WriteAllText(path, "some content"); - IFileInfo sut = FileSystem.FileInfo.New(path); - sut.Exists.Should().BeTrue(); - - sut.Delete(); - - if (Test.IsNetFramework) - { - sut.Exists.Should().BeTrue(); - } - else - { - sut.Exists.Should().BeFalse(); - } - - FileSystem.File.Exists(path).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Delete_WithOpenFile_ShouldThrowIOException_OnWindows(string filename) - { - FileSystem.Initialize(); - FileSystemStream openFile = FileSystem.File.OpenWrite(filename); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - IFileInfo sut = FileSystem.FileInfo.New(filename); - Exception? exception = Record.Exception(() => - { - sut.Delete(); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException( - messageContains: $"{filename}'", - hResult: -2147024864); - FileSystem.File.Exists(filename).Should().BeTrue(); - } - else - { - exception.Should().BeNull(); - FileSystem.File.Exists(filename).Should().BeFalse(); - } - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class DeleteTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string missingDirectory, string fileName) + { + string filePath = FileSystem.Path.Combine(missingDirectory, fileName); + + Exception? exception = Record.Exception(() => + { + FileSystem.FileInfo.New(filePath).Delete(); + }); + + exception.Should().BeException(hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Delete_MissingFile_ShouldDoNothing( + string fileName) + { + Exception? exception = Record.Exception(() => + { + FileSystem.FileInfo.New(fileName).Delete(); + }); + + exception.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void Delete_ShouldRefreshExistsCache_ExceptOnNetFramework(string path) + { + FileSystem.File.WriteAllText(path, "some content"); + IFileInfo sut = FileSystem.FileInfo.New(path); + sut.Exists.Should().BeTrue(); + + sut.Delete(); + + if (Test.IsNetFramework) + { + sut.Exists.Should().BeTrue(); + } + else + { + sut.Exists.Should().BeFalse(); + } + + FileSystem.File.Exists(path).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Delete_WithOpenFile_ShouldThrowIOException_OnWindows(string filename) + { + FileSystem.Initialize(); + FileSystemStream openFile = FileSystem.File.OpenWrite(filename); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + IFileInfo sut = FileSystem.FileInfo.New(filename); + Exception? exception = Record.Exception(() => + { + sut.Delete(); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException( + messageContains: $"{filename}'", + hResult: -2147024864); + FileSystem.File.Exists(filename).Should().BeTrue(); + } + else + { + exception.Should().BeNull(); + FileSystem.File.Exists(filename).Should().BeFalse(); + } + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/EncryptDecryptTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/EncryptDecryptTests.cs index 72b11b5ad..1ed9ba917 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/EncryptDecryptTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/EncryptDecryptTests.cs @@ -1,102 +1,101 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class EncryptDecryptTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - [SupportedOSPlatform("windows")] - public void Decrypt_EncryptedData_ShouldReturnOriginalText( - string path, string contents) - { - Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, - "Encryption depends on the underlying device, if it is supported or not."); - - FileSystem.File.WriteAllText(path, contents); - IFileInfo sut = FileSystem.FileInfo.New(path); - - sut.Encrypt(); - sut.Decrypt(); - - string result = FileSystem.File.ReadAllText(path); - result.Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - [SupportedOSPlatform("windows")] - public void Decrypt_UnencryptedData_ShouldReturnOriginalText( - string path, string contents) - { - Skip.IfNot(Test.RunsOnWindows); - - FileSystem.File.WriteAllText(path, contents); - IFileInfo sut = FileSystem.FileInfo.New(path); - - sut.Decrypt(); - - string result = FileSystem.File.ReadAllText(path); - result.Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - [SupportedOSPlatform("windows")] - public void Encrypt_Decrypt_ShouldChangeEncryptedFileAttribute( - string path, string contents) - { - Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, - "Encryption depends on the underlying device, if it is supported or not."); - - FileSystem.File.WriteAllText(path, contents); - IFileInfo sut = FileSystem.FileInfo.New(path); - - sut.Encrypt(); - sut.Attributes.Should().HaveFlag(FileAttributes.Encrypted); - sut.Decrypt(); - sut.Attributes.Should().NotHaveFlag(FileAttributes.Encrypted); - } - - [SkippableTheory] - [AutoData] - [SupportedOSPlatform("windows")] - public void Encrypt_ShouldChangeData( - string path, byte[] bytes) - { - Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, - "Encryption depends on the underlying device, if it is supported or not."); - - FileSystem.File.WriteAllBytes(path, bytes); - IFileInfo sut = FileSystem.FileInfo.New(path); - - sut.Encrypt(); - - byte[] result = FileSystem.File.ReadAllBytes(path); - result.Should().NotBeEquivalentTo(bytes); - } - - [SkippableTheory] - [AutoData] - [SupportedOSPlatform("windows")] - public void Encrypt_Twice_ShouldIgnoreTheSecondTime( - string path, string contents) - { - Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, - "Encryption depends on the underlying device, if it is supported or not."); - - FileSystem.File.WriteAllText(path, contents); - IFileInfo sut = FileSystem.FileInfo.New(path); - - sut.Encrypt(); - sut.Encrypt(); - - sut.Decrypt(); - string result = FileSystem.File.ReadAllText(path); - result.Should().Be(contents); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class EncryptDecryptTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + [SupportedOSPlatform("windows")] + public void Decrypt_EncryptedData_ShouldReturnOriginalText( + string path, string contents) + { + Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, + "Encryption depends on the underlying device, if it is supported or not."); + + FileSystem.File.WriteAllText(path, contents); + IFileInfo sut = FileSystem.FileInfo.New(path); + + sut.Encrypt(); + sut.Decrypt(); + + string result = FileSystem.File.ReadAllText(path); + result.Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + [SupportedOSPlatform("windows")] + public void Decrypt_UnencryptedData_ShouldReturnOriginalText( + string path, string contents) + { + Skip.IfNot(Test.RunsOnWindows); + + FileSystem.File.WriteAllText(path, contents); + IFileInfo sut = FileSystem.FileInfo.New(path); + + sut.Decrypt(); + + string result = FileSystem.File.ReadAllText(path); + result.Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + [SupportedOSPlatform("windows")] + public void Encrypt_Decrypt_ShouldChangeEncryptedFileAttribute( + string path, string contents) + { + Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, + "Encryption depends on the underlying device, if it is supported or not."); + + FileSystem.File.WriteAllText(path, contents); + IFileInfo sut = FileSystem.FileInfo.New(path); + + sut.Encrypt(); + sut.Attributes.Should().HaveFlag(FileAttributes.Encrypted); + sut.Decrypt(); + sut.Attributes.Should().NotHaveFlag(FileAttributes.Encrypted); + } + + [SkippableTheory] + [AutoData] + [SupportedOSPlatform("windows")] + public void Encrypt_ShouldChangeData( + string path, byte[] bytes) + { + Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, + "Encryption depends on the underlying device, if it is supported or not."); + + FileSystem.File.WriteAllBytes(path, bytes); + IFileInfo sut = FileSystem.FileInfo.New(path); + + sut.Encrypt(); + + byte[] result = FileSystem.File.ReadAllBytes(path); + result.Should().NotBeEquivalentTo(bytes); + } + + [SkippableTheory] + [AutoData] + [SupportedOSPlatform("windows")] + public void Encrypt_Twice_ShouldIgnoreTheSecondTime( + string path, string contents) + { + Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, + "Encryption depends on the underlying device, if it is supported or not."); + + FileSystem.File.WriteAllText(path, contents); + IFileInfo sut = FileSystem.FileInfo.New(path); + + sut.Encrypt(); + sut.Encrypt(); + + sut.Decrypt(); + string result = FileSystem.File.ReadAllText(path); + result.Should().Be(contents); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ExceptionTests.cs index 074961ae4..0896cd582 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ExceptionTests.cs @@ -1,128 +1,127 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetFileInfoCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetFileInfoCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileInfoCallbacks), parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); - }); - - if (!Test.RunsOnWindows) - { - if (exception is IOException ioException) - { - ioException.HResult.Should().NotBe(-2147024809, - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - else - { - if (Test.IsNetFramework) - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should().BeException( - hResult: -2147024773, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - } - - #region Helpers - - public static IEnumerable GetFileInfoCallbacks(string? path) - => GetFileInfoCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetFileInfoCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", - fileInfo - => fileInfo.CopyTo(value)); - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", - fileInfo - => fileInfo.CopyTo(value, false)); - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", - fileInfo - => fileInfo.MoveTo(value)); -#if FEATURE_FILE_MOVETO_OVERWRITE - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", - fileInfo - => fileInfo.MoveTo(value, false)); -#endif - yield return (ExceptionTestHelper.TestTypes.All | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, - "destinationFileName", fileInfo - => fileInfo.Replace(value, "bar")); - yield return (ExceptionTestHelper.TestTypes.All | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, - "destinationFileName", fileInfo - => fileInfo.Replace(value, "bar", false)); - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetFileInfoCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetFileInfoCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileInfoCallbacks), parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); + }); + + if (!Test.RunsOnWindows) + { + if (exception is IOException ioException) + { + ioException.HResult.Should().NotBe(-2147024809, + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + else + { + if (Test.IsNetFramework) + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should().BeException( + hResult: -2147024773, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + } + + #region Helpers + + public static IEnumerable GetFileInfoCallbacks(string? path) + => GetFileInfoCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetFileInfoCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", + fileInfo + => fileInfo.CopyTo(value)); + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", + fileInfo + => fileInfo.CopyTo(value, false)); + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", + fileInfo + => fileInfo.MoveTo(value)); +#if FEATURE_FILE_MOVETO_OVERWRITE + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", + fileInfo + => fileInfo.MoveTo(value, false)); +#endif + yield return (ExceptionTestHelper.TestTypes.All | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, + "destinationFileName", fileInfo + => fileInfo.Replace(value, "bar")); + yield return (ExceptionTestHelper.TestTypes.All | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, + "destinationFileName", fileInfo + => fileInfo.Replace(value, "bar", false)); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/LengthTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/LengthTests.cs index 1a8cdf71d..f2347e2d0 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/LengthTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/LengthTests.cs @@ -1,144 +1,143 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class LengthTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Length_MissingFile_ShouldThrowFileNotFoundException(string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Length; - }); - - exception.Should().BeException( - hResult: -2147024894, - messageContains: Test.IsNetFramework - ? $"'{path}'" - : $"'{FileSystem.Path.GetFullPath(path)}'"); - } - - [SkippableTheory] - [AutoData] - public void Length_MissingDirectory_ShouldThrowFileNotFoundException( - string missingDirectory, string fileName) - { - string path = FileSystem.Path.Combine(missingDirectory, fileName); - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Length; - }); - - exception.Should().BeException( - hResult: -2147024894, - messageContains: Test.IsNetFramework - ? $"'{path}'" - : $"'{FileSystem.Path.GetFullPath(path)}'"); - } - - [SkippableTheory] - [AutoData] - public void Length_PathIsDirectory_ShouldThrowFileNotFoundException(string path) - { - FileSystem.Directory.CreateDirectory(path); - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Length; - }); - - exception.Should().BeException( - hResult: -2147024894, - messageContains: Test.IsNetFramework - ? $"'{path}'" - : $"'{FileSystem.Path.GetFullPath(path)}'"); - } - - [SkippableTheory] - [AutoData] - public void Length_WhenFileExists_ShouldBeSetCorrectly(string path, byte[] bytes) - { - FileSystem.File.WriteAllBytes(path, bytes); - IFileInfo sut = FileSystem.FileInfo.New(path); - - long result = sut.Length; - - result.Should().Be(bytes.Length); - } - - [SkippableTheory] - [AutoData] - public void Length_WhenFileIsCreated_ShouldBeSetCorrectly(string path, byte[] bytes) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - FileSystem.File.WriteAllBytes(path, bytes); - - long result = sut.Length; - - result.Should().Be(bytes.Length); - } - - [SkippableTheory] - [AutoData] - public void Length_WhenFileIsCreatedAfterAccessed_ShouldBeSetCorrectly( - string path, byte[] bytes) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.OpenRead(); - }); - - FileSystem.File.WriteAllBytes(path, bytes); - - long result = sut.Length; - - exception.Should().NotBeNull(); - result.Should().Be(bytes.Length); - } - - [SkippableTheory] - [AutoData] - public void - Length_WhenFileIsCreatedAfterLengthAccessed_ShouldThrowFileNotFoundExceptionAgain( - string path, byte[] bytes) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Length; - }); - - FileSystem.File.WriteAllBytes(path, bytes); - - Exception? exception2 = Record.Exception(() => - { - _ = sut.Length; - }); - - exception.Should().BeException( - messageContains: Test.IsNetFramework - ? $"'{path}'" - : $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - exception2.Should().BeException( - messageContains: Test.IsNetFramework - ? $"'{path}'" - : $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class LengthTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Length_MissingFile_ShouldThrowFileNotFoundException(string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Length; + }); + + exception.Should().BeException( + hResult: -2147024894, + messageContains: Test.IsNetFramework + ? $"'{path}'" + : $"'{FileSystem.Path.GetFullPath(path)}'"); + } + + [SkippableTheory] + [AutoData] + public void Length_MissingDirectory_ShouldThrowFileNotFoundException( + string missingDirectory, string fileName) + { + string path = FileSystem.Path.Combine(missingDirectory, fileName); + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Length; + }); + + exception.Should().BeException( + hResult: -2147024894, + messageContains: Test.IsNetFramework + ? $"'{path}'" + : $"'{FileSystem.Path.GetFullPath(path)}'"); + } + + [SkippableTheory] + [AutoData] + public void Length_PathIsDirectory_ShouldThrowFileNotFoundException(string path) + { + FileSystem.Directory.CreateDirectory(path); + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Length; + }); + + exception.Should().BeException( + hResult: -2147024894, + messageContains: Test.IsNetFramework + ? $"'{path}'" + : $"'{FileSystem.Path.GetFullPath(path)}'"); + } + + [SkippableTheory] + [AutoData] + public void Length_WhenFileExists_ShouldBeSetCorrectly(string path, byte[] bytes) + { + FileSystem.File.WriteAllBytes(path, bytes); + IFileInfo sut = FileSystem.FileInfo.New(path); + + long result = sut.Length; + + result.Should().Be(bytes.Length); + } + + [SkippableTheory] + [AutoData] + public void Length_WhenFileIsCreated_ShouldBeSetCorrectly(string path, byte[] bytes) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + FileSystem.File.WriteAllBytes(path, bytes); + + long result = sut.Length; + + result.Should().Be(bytes.Length); + } + + [SkippableTheory] + [AutoData] + public void Length_WhenFileIsCreatedAfterAccessed_ShouldBeSetCorrectly( + string path, byte[] bytes) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.OpenRead(); + }); + + FileSystem.File.WriteAllBytes(path, bytes); + + long result = sut.Length; + + exception.Should().NotBeNull(); + result.Should().Be(bytes.Length); + } + + [SkippableTheory] + [AutoData] + public void + Length_WhenFileIsCreatedAfterLengthAccessed_ShouldThrowFileNotFoundExceptionAgain( + string path, byte[] bytes) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Length; + }); + + FileSystem.File.WriteAllBytes(path, bytes); + + Exception? exception2 = Record.Exception(() => + { + _ = sut.Length; + }); + + exception.Should().BeException( + messageContains: Test.IsNetFramework + ? $"'{path}'" + : $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + exception2.Should().BeException( + messageContains: Test.IsNetFramework + ? $"'{path}'" + : $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/MoveToTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/MoveToTests.cs index be4e6180b..c19354241 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/MoveToTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/MoveToTests.cs @@ -1,287 +1,286 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class MoveToTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void MoveTo_DestinationExists_ShouldThrowIOException_AndNotMoveFile( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(destinationName); - }); - - exception.Should().BeException( - hResult: Test.RunsOnWindows ? -2147024713 : 17); - - sut.Exists.Should().BeTrue(); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - } - -#if FEATURE_FILE_MOVETO_OVERWRITE - [SkippableTheory] - [AutoData] - public void MoveTo_DestinationExists_WithOverwrite_ShouldOverwriteDestination( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - sut.MoveTo(destinationName, true); - - sut.Exists.Should().BeTrue(); - sut.ToString().Should().Be(destinationName); - sut.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - } -#endif - - [SkippableTheory] - [AutoData] - public void MoveTo_Itself_ShouldDoNothing( - string sourceName, - string contents) - { - FileSystem.File.WriteAllText(sourceName, contents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(sourceName); - }); - - exception.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_Itself_SourceMissing_ShouldThrowFileNotFoundException( - string sourceName) - { - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(sourceName); - }); - - exception.Should().BeException( - hResult: -2147024894, - messageContains: Test.IsNetFramework - ? null - : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void - MoveTo_MissingDestinationDirectory_ShouldThrowDirectoryNotFoundException_AndNotMoveFile( - string sourceName, - string missingDirectory, - string destinationName, - string sourceContents) - { - string destinationPath = - FileSystem.Path.Combine(missingDirectory, destinationName); - FileSystem.File.WriteAllText(sourceName, sourceContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(destinationPath); - }); - - exception.Should().BeException(hResult: -2147024893); - - sut.Exists.Should().BeTrue(); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_ReadOnly_ShouldMoveFile( - string sourceName, string destinationName, string contents) - { - FileSystem.File.WriteAllText(sourceName, contents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - sut.IsReadOnly = true; - - sut.MoveTo(destinationName); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.GetAttributes(destinationName) - .Should().HaveFlag(FileAttributes.ReadOnly); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_ShouldAddArchiveAttribute_OnWindows( - string sourceName, - string destinationName, - string contents, - FileAttributes fileAttributes) - { - FileSystem.File.WriteAllText(sourceName, contents); - FileSystem.File.SetAttributes(sourceName, fileAttributes); - FileAttributes expectedAttributes = FileSystem.File.GetAttributes(sourceName); - if (Test.RunsOnWindows) - { - expectedAttributes |= FileAttributes.Archive; - } - - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - sut.MoveTo(destinationName); - - FileSystem.File.GetAttributes(destinationName) - .Should().Be(expectedAttributes); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_ShouldKeepMetadata( - string sourceName, - string destinationName, - string contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText(sourceName, contents); - DateTime sourceCreationTime = FileSystem.File.GetCreationTime(sourceName); - DateTime sourceLastAccessTime = FileSystem.File.GetLastAccessTime(sourceName); - DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTime(sourceName); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - TimeSystem.Thread.Sleep(1000); - - sut.MoveTo(destinationName); - - FileSystem.File.GetCreationTime(destinationName) - .Should().Be(sourceCreationTime); - FileSystem.File.GetLastAccessTime(destinationName) - .Should().Be(sourceLastAccessTime); - FileSystem.File.GetLastWriteTime(destinationName) - .Should().Be(sourceLastWriteTime); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_ShouldMoveFileWithContent( - string sourceName, string destinationName, string contents) - { - FileSystem.File.WriteAllText(sourceName, contents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - sut.MoveTo(destinationName); - - sut.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - sut.Exists.Should().BeTrue(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_ShouldNotAdjustTimes(string source, string destination) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText(source, "foo"); - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - IFileInfo sut = FileSystem.FileInfo.New(source); - DateTime expectedCreationTime = sut.CreationTime; - DateTime expectedLastAccessTime = sut.LastAccessTime; - DateTime expectedLastWriteTime = sut.LastWriteTime; - - sut.MoveTo(destination); - - DateTime creationTime = FileSystem.File.GetCreationTime(destination); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTime(destination); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTime(destination); - - sut.CreationTime.Should().Be(expectedCreationTime); - sut.LastAccessTime.Should().Be(expectedLastAccessTime); - sut.LastWriteTime.Should().Be(expectedLastWriteTime); - creationTime.Should().Be(expectedCreationTime); - lastAccessTime.Should().Be(expectedLastAccessTime); - lastWriteTime.Should().Be(expectedLastWriteTime); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_SourceLocked_ShouldThrowIOException( - string sourceName, - string destinationName) - { - FileSystem.File.WriteAllText(sourceName, null); - using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, - FileAccess.Read, FileShare.Read); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(destinationName); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024864); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - else - { - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - } - } - - [SkippableTheory] - [AutoData] - public void MoveTo_SourceMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName) - { - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(destinationName); - }); - - exception.Should().BeException( - messageContains: Test.IsNetFramework - ? null - : $"'{FileSystem.Path.GetFullPath(sourceName)}'", - hResult: -2147024894); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class MoveToTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void MoveTo_DestinationExists_ShouldThrowIOException_AndNotMoveFile( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(destinationName); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147024713 : 17); + + sut.Exists.Should().BeTrue(); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + } + +#if FEATURE_FILE_MOVETO_OVERWRITE + [SkippableTheory] + [AutoData] + public void MoveTo_DestinationExists_WithOverwrite_ShouldOverwriteDestination( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + sut.MoveTo(destinationName, true); + + sut.Exists.Should().BeTrue(); + sut.ToString().Should().Be(destinationName); + sut.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + } +#endif + + [SkippableTheory] + [AutoData] + public void MoveTo_Itself_ShouldDoNothing( + string sourceName, + string contents) + { + FileSystem.File.WriteAllText(sourceName, contents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(sourceName); + }); + + exception.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_Itself_SourceMissing_ShouldThrowFileNotFoundException( + string sourceName) + { + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(sourceName); + }); + + exception.Should().BeException( + hResult: -2147024894, + messageContains: Test.IsNetFramework + ? null + : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void + MoveTo_MissingDestinationDirectory_ShouldThrowDirectoryNotFoundException_AndNotMoveFile( + string sourceName, + string missingDirectory, + string destinationName, + string sourceContents) + { + string destinationPath = + FileSystem.Path.Combine(missingDirectory, destinationName); + FileSystem.File.WriteAllText(sourceName, sourceContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(destinationPath); + }); + + exception.Should().BeException(hResult: -2147024893); + + sut.Exists.Should().BeTrue(); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_ReadOnly_ShouldMoveFile( + string sourceName, string destinationName, string contents) + { + FileSystem.File.WriteAllText(sourceName, contents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + sut.IsReadOnly = true; + + sut.MoveTo(destinationName); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.GetAttributes(destinationName) + .Should().HaveFlag(FileAttributes.ReadOnly); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_ShouldAddArchiveAttribute_OnWindows( + string sourceName, + string destinationName, + string contents, + FileAttributes fileAttributes) + { + FileSystem.File.WriteAllText(sourceName, contents); + FileSystem.File.SetAttributes(sourceName, fileAttributes); + FileAttributes expectedAttributes = FileSystem.File.GetAttributes(sourceName); + if (Test.RunsOnWindows) + { + expectedAttributes |= FileAttributes.Archive; + } + + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + sut.MoveTo(destinationName); + + FileSystem.File.GetAttributes(destinationName) + .Should().Be(expectedAttributes); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_ShouldKeepMetadata( + string sourceName, + string destinationName, + string contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText(sourceName, contents); + DateTime sourceCreationTime = FileSystem.File.GetCreationTime(sourceName); + DateTime sourceLastAccessTime = FileSystem.File.GetLastAccessTime(sourceName); + DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTime(sourceName); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + TimeSystem.Thread.Sleep(1000); + + sut.MoveTo(destinationName); + + FileSystem.File.GetCreationTime(destinationName) + .Should().Be(sourceCreationTime); + FileSystem.File.GetLastAccessTime(destinationName) + .Should().Be(sourceLastAccessTime); + FileSystem.File.GetLastWriteTime(destinationName) + .Should().Be(sourceLastWriteTime); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_ShouldMoveFileWithContent( + string sourceName, string destinationName, string contents) + { + FileSystem.File.WriteAllText(sourceName, contents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + sut.MoveTo(destinationName); + + sut.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + sut.Exists.Should().BeTrue(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_ShouldNotAdjustTimes(string source, string destination) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText(source, "foo"); + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + IFileInfo sut = FileSystem.FileInfo.New(source); + DateTime expectedCreationTime = sut.CreationTime; + DateTime expectedLastAccessTime = sut.LastAccessTime; + DateTime expectedLastWriteTime = sut.LastWriteTime; + + sut.MoveTo(destination); + + DateTime creationTime = FileSystem.File.GetCreationTime(destination); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTime(destination); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTime(destination); + + sut.CreationTime.Should().Be(expectedCreationTime); + sut.LastAccessTime.Should().Be(expectedLastAccessTime); + sut.LastWriteTime.Should().Be(expectedLastWriteTime); + creationTime.Should().Be(expectedCreationTime); + lastAccessTime.Should().Be(expectedLastAccessTime); + lastWriteTime.Should().Be(expectedLastWriteTime); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_SourceLocked_ShouldThrowIOException( + string sourceName, + string destinationName) + { + FileSystem.File.WriteAllText(sourceName, null); + using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, + FileAccess.Read, FileShare.Read); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(destinationName); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024864); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + else + { + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + } + } + + [SkippableTheory] + [AutoData] + public void MoveTo_SourceMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName) + { + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(destinationName); + }); + + exception.Should().BeException( + messageContains: Test.IsNetFramework + ? null + : $"'{FileSystem.Path.GetFullPath(sourceName)}'", + hResult: -2147024894); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenReadTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenReadTests.cs index 76e333549..618580209 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenReadTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenReadTests.cs @@ -1,40 +1,39 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OpenReadTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void OpenRead_MissingFile_ShouldThrowFileNotFoundException(string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.OpenRead(); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - - [SkippableTheory] - [AutoData] - public void OpenRead_ShouldUseReadAccessAndReadShare(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.OpenRead(); - - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Read); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be( - Test.RunsOnWindows ? FileShare.Read : FileShare.ReadWrite); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OpenReadTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void OpenRead_MissingFile_ShouldThrowFileNotFoundException(string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.OpenRead(); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + + [SkippableTheory] + [AutoData] + public void OpenRead_ShouldUseReadAccessAndReadShare(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.OpenRead(); + + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Read); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be( + Test.RunsOnWindows ? FileShare.Read : FileShare.ReadWrite); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTests.cs index 667c82cff..a910bf570 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTests.cs @@ -1,254 +1,253 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OpenTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ -#if NETFRAMEWORK - [SkippableTheory] - [AutoData] - public void Open_AppendMode_ShouldThrowArgumentException(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Open(FileMode.Append); - }); - - exception.Should().BeException(hResult: -2147024809); - } -#else - [SkippableTheory] - [AutoData] - public void Open_AppendMode_ShouldOpenExistingFile( - string path, byte[] bytes) - { - FileSystem.File.WriteAllBytes(path, bytes); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(FileMode.Append); - - stream.Position.Should().Be(bytes.Length); - stream.Length.Should().Be(bytes.Length); - } - - [SkippableTheory] - [AutoData] - public void Open_AppendMode_SeekShouldThrowIOException_WhenReadingBackward( - string path, byte[] bytes) - { - FileSystem.File.WriteAllBytes(path, bytes); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(FileMode.Append); - - stream.CanRead.Should().BeFalse(); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.Seek(0, SeekOrigin.Begin); - }); - - exception.Should().BeException(hResult: -2146232800); - } - - [SkippableTheory] - [AutoData] - public void Open_AppendMode_SeekShouldWorkWhenKeepingInNewArea( - string path, byte[] bytes) - { - FileSystem.File.WriteAllBytes(path, bytes); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(FileMode.Append); - - stream.WriteByte(0); - stream.WriteByte(1); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.Seek(bytes.Length, SeekOrigin.Begin); - }); - - exception.Should().BeNull(); - stream.Position.Should().Be(bytes.Length); - } - - [SkippableTheory] - [AutoData] - public void Open_AppendMode_ReadShouldThrowNotSupportedException( - string path, byte[] bytes) - { - FileSystem.File.WriteAllBytes(path, bytes); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(FileMode.Append); - - stream.WriteByte(0); - stream.WriteByte(1); - - stream.Seek(bytes.Length, SeekOrigin.Begin); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.ReadByte(); - }); - - exception.Should().BeException(hResult: -2146233067); - } -#endif - - [SkippableTheory] - [AutoData] - public void Open_ExistingFileWithCreateNewMode_ShouldThrowIOException( - string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Open(FileMode.CreateNew); - }); - - exception.Should().BeException($"'{FileSystem.Path.GetFullPath(path)}'", - hResult: Test.RunsOnWindows ? -2147024816 : 17); - } - - [SkippableTheory] - [InlineAutoData(FileMode.Open)] - [InlineAutoData(FileMode.Truncate)] - public void Open_MissingFileAndIncorrectMode_ShouldThrowFileNotFoundException( - FileMode mode, string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Open(mode); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - - [SkippableTheory] -#if !NETFRAMEWORK - [InlineAutoData(FileMode.Append)] -#endif - [InlineAutoData(FileMode.Create)] - [InlineAutoData(FileMode.CreateNew)] - [InlineAutoData(FileMode.OpenOrCreate)] - public void Open_MissingFileAndCorrectMode_ShouldCreateFile( - FileMode mode, string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Open(mode); - }); - - exception.Should().BeNull(); - sut.Exists.Should().BeTrue(); - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void Open_ShouldOpenWithReadAndWriteAccess( - string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(FileMode.OpenOrCreate); - - stream.CanRead.Should().BeTrue(); - stream.CanWrite.Should().BeTrue(); - } - - [SkippableTheory] -#if !NETFRAMEWORK - [InlineAutoData(FileMode.Append, FileAccess.Write)] -#endif - [InlineAutoData(FileMode.Open, FileAccess.ReadWrite)] - [InlineAutoData(FileMode.Create, FileAccess.ReadWrite)] - public void Open_ShouldUseExpectedAccessDependingOnMode( - FileMode mode, - FileAccess expectedAccess, - string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(mode); - - FileTestHelper.CheckFileAccess(stream).Should().Be(expectedAccess); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } - - [SkippableTheory] - [InlineAutoData(FileAccess.Read, FileShare.Write)] - [InlineAutoData(FileAccess.Write, FileShare.Read)] - public void Open_ShouldUseGivenAccessAndShare(string path, - FileAccess access, - FileShare share) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(FileMode.Open, access, share); - - FileTestHelper.CheckFileAccess(stream).Should().Be(access); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); - } - - [SkippableTheory] - [AutoData] - public void Open_ShouldUseNoneShareAsDefault(string path, - FileAccess access) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(FileMode.Open, access); - - FileTestHelper.CheckFileAccess(stream).Should().Be(access); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } - -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - [SkippableTheory] - [InlineAutoData(FileAccess.Read, FileShare.Write)] - [InlineAutoData(FileAccess.Write, FileShare.Read)] - public void Open_WithFileStreamOptions_ShouldUseGivenAccessAndShare( - string path, - FileAccess access, - FileShare share) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - FileStreamOptions options = new() - { - Mode = FileMode.Open, - Access = access, - Share = share - }; - - using FileSystemStream stream = sut.Open(options); - - FileTestHelper.CheckFileAccess(stream).Should().Be(access); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); - } -#endif -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OpenTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ +#if NETFRAMEWORK + [SkippableTheory] + [AutoData] + public void Open_AppendMode_ShouldThrowArgumentException(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Open(FileMode.Append); + }); + + exception.Should().BeException(hResult: -2147024809); + } +#else + [SkippableTheory] + [AutoData] + public void Open_AppendMode_ShouldOpenExistingFile( + string path, byte[] bytes) + { + FileSystem.File.WriteAllBytes(path, bytes); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(FileMode.Append); + + stream.Position.Should().Be(bytes.Length); + stream.Length.Should().Be(bytes.Length); + } + + [SkippableTheory] + [AutoData] + public void Open_AppendMode_SeekShouldThrowIOException_WhenReadingBackward( + string path, byte[] bytes) + { + FileSystem.File.WriteAllBytes(path, bytes); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(FileMode.Append); + + stream.CanRead.Should().BeFalse(); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.Seek(0, SeekOrigin.Begin); + }); + + exception.Should().BeException(hResult: -2146232800); + } + + [SkippableTheory] + [AutoData] + public void Open_AppendMode_SeekShouldWorkWhenKeepingInNewArea( + string path, byte[] bytes) + { + FileSystem.File.WriteAllBytes(path, bytes); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(FileMode.Append); + + stream.WriteByte(0); + stream.WriteByte(1); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.Seek(bytes.Length, SeekOrigin.Begin); + }); + + exception.Should().BeNull(); + stream.Position.Should().Be(bytes.Length); + } + + [SkippableTheory] + [AutoData] + public void Open_AppendMode_ReadShouldThrowNotSupportedException( + string path, byte[] bytes) + { + FileSystem.File.WriteAllBytes(path, bytes); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(FileMode.Append); + + stream.WriteByte(0); + stream.WriteByte(1); + + stream.Seek(bytes.Length, SeekOrigin.Begin); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.ReadByte(); + }); + + exception.Should().BeException(hResult: -2146233067); + } +#endif + + [SkippableTheory] + [AutoData] + public void Open_ExistingFileWithCreateNewMode_ShouldThrowIOException( + string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Open(FileMode.CreateNew); + }); + + exception.Should().BeException($"'{FileSystem.Path.GetFullPath(path)}'", + hResult: Test.RunsOnWindows ? -2147024816 : 17); + } + + [SkippableTheory] + [InlineAutoData(FileMode.Open)] + [InlineAutoData(FileMode.Truncate)] + public void Open_MissingFileAndIncorrectMode_ShouldThrowFileNotFoundException( + FileMode mode, string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Open(mode); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + + [SkippableTheory] +#if !NETFRAMEWORK + [InlineAutoData(FileMode.Append)] +#endif + [InlineAutoData(FileMode.Create)] + [InlineAutoData(FileMode.CreateNew)] + [InlineAutoData(FileMode.OpenOrCreate)] + public void Open_MissingFileAndCorrectMode_ShouldCreateFile( + FileMode mode, string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Open(mode); + }); + + exception.Should().BeNull(); + sut.Exists.Should().BeTrue(); + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void Open_ShouldOpenWithReadAndWriteAccess( + string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(FileMode.OpenOrCreate); + + stream.CanRead.Should().BeTrue(); + stream.CanWrite.Should().BeTrue(); + } + + [SkippableTheory] +#if !NETFRAMEWORK + [InlineAutoData(FileMode.Append, FileAccess.Write)] +#endif + [InlineAutoData(FileMode.Open, FileAccess.ReadWrite)] + [InlineAutoData(FileMode.Create, FileAccess.ReadWrite)] + public void Open_ShouldUseExpectedAccessDependingOnMode( + FileMode mode, + FileAccess expectedAccess, + string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(mode); + + FileTestHelper.CheckFileAccess(stream).Should().Be(expectedAccess); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } + + [SkippableTheory] + [InlineAutoData(FileAccess.Read, FileShare.Write)] + [InlineAutoData(FileAccess.Write, FileShare.Read)] + public void Open_ShouldUseGivenAccessAndShare(string path, + FileAccess access, + FileShare share) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(FileMode.Open, access, share); + + FileTestHelper.CheckFileAccess(stream).Should().Be(access); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); + } + + [SkippableTheory] + [AutoData] + public void Open_ShouldUseNoneShareAsDefault(string path, + FileAccess access) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(FileMode.Open, access); + + FileTestHelper.CheckFileAccess(stream).Should().Be(access); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } + +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + [SkippableTheory] + [InlineAutoData(FileAccess.Read, FileShare.Write)] + [InlineAutoData(FileAccess.Write, FileShare.Read)] + public void Open_WithFileStreamOptions_ShouldUseGivenAccessAndShare( + string path, + FileAccess access, + FileShare share) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + FileStreamOptions options = new() + { + Mode = FileMode.Open, + Access = access, + Share = share + }; + + using FileSystemStream stream = sut.Open(options); + + FileTestHelper.CheckFileAccess(stream).Should().Be(access); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTextTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTextTests.cs index 041b39c3a..799a2665f 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTextTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTextTests.cs @@ -1,41 +1,40 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OpenTextTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void OpenText_MissingFile_ShouldThrowFileNotFoundException( - string path) - { - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - using StreamReader stream = fileInfo.OpenText(); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - - [SkippableTheory] - [AutoData] - public void OpenText_ShouldReturnFileContent( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - using StreamReader stream = fileInfo.OpenText(); - - string result = stream.ReadToEnd(); - result.Should().Be(contents); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OpenTextTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void OpenText_MissingFile_ShouldThrowFileNotFoundException( + string path) + { + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + using StreamReader stream = fileInfo.OpenText(); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + + [SkippableTheory] + [AutoData] + public void OpenText_ShouldReturnFileContent( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + using StreamReader stream = fileInfo.OpenText(); + + string result = stream.ReadToEnd(); + result.Should().Be(contents); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenWriteTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenWriteTests.cs index 8bcb41867..04b8f5d64 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenWriteTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenWriteTests.cs @@ -1,34 +1,33 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OpenWriteTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void OpenWrite_MissingFile_ShouldCreateFile(string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.OpenWrite(); - - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void OpenWrite_ShouldUseWriteAccessAndNoneShare(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.OpenWrite(); - - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Write); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OpenWriteTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void OpenWrite_MissingFile_ShouldCreateFile(string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.OpenWrite(); + + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void OpenWrite_ShouldUseWriteAccessAndNoneShare(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.OpenWrite(); + + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Write); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ReplaceTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ReplaceTests.cs index cff56d194..c56e28b46 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ReplaceTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ReplaceTests.cs @@ -1,418 +1,417 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ReplaceTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Replace_BackupDirectoryMissing_ShouldThrowCorrectException( - string sourceName, - string destinationName, - string missingDirectory, - string backupName) - { - FileSystem.File.WriteAllText(sourceName, "some content"); - string backupPath = FileSystem.Path.Combine(missingDirectory, backupName); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupPath); - }); - - exception.Should().BeFileOrDirectoryNotFoundException(); - } - - [SkippableTheory] - [AutoData] - public void Replace_DestinationDirectoryMissing_ShouldThrowDirectoryNotFoundException( - string sourceName, - string missingDirectory, - string destinationName, - string backupName) - { - FileSystem.File.WriteAllText(sourceName, "some content"); - string destinationPath = - FileSystem.Path.Combine(missingDirectory, destinationName); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationPath, backupName); - }); - - exception.Should().BeFileOrDirectoryNotFoundException(); - } - - [SkippableTheory] - [AutoData] - public void Replace_DestinationIsDirectory_ShouldThrowUnauthorizedAccessException( - string sourceName, - string destinationName, - string backupName) - { - FileSystem.File.WriteAllText(sourceName, null); - FileSystem.Directory.CreateDirectory(destinationName); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024891); - } - - [SkippableTheory] - [AutoData] - public void Replace_DestinationMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName, - string backupName) - { - FileSystem.File.WriteAllText(sourceName, null); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024894); - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Replace_ReadOnly_WithIgnoreMetadataError_ShouldReplaceFile( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - sut.IsReadOnly = true; - - IFileInfo result = sut.Replace(destinationName, backupName, true); - - sut.Exists.Should().BeFalse(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.GetAttributes(destinationName) - .Should().HaveFlag(FileAttributes.ReadOnly); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - - [SkippableTheory] - [AutoData] - public void - Replace_ReadOnly_WithoutIgnoreMetadataError_ShouldThrowUnauthorizedAccessException_OnWindows( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - sut.IsReadOnly = true; - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupName); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024891); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - FileSystem.File.GetAttributes(destinationName) - .Should().NotHaveFlag(FileAttributes.ReadOnly); - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - else - { - exception.Should().BeNull(); - sut.Exists.Should().BeFalse(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - } - - [SkippableTheory] - [AutoData] - public void Replace_ShouldAddArchiveAttribute_OnWindows( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents, - FileAttributes sourceFileAttributes, - FileAttributes destinationFileAttributes) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.SetAttributes(sourceName, sourceFileAttributes); - FileAttributes expectedSourceAttributes = - FileSystem.File.GetAttributes(sourceName); - if (Test.RunsOnWindows) - { - expectedSourceAttributes |= FileAttributes.Archive; - } - - FileSystem.File.WriteAllText(destinationName, destinationContents); - FileSystem.File.SetAttributes(destinationName, destinationFileAttributes); - FileAttributes expectedDestinationAttributes = - FileSystem.File.GetAttributes(destinationName); - if (Test.RunsOnWindows) - { - expectedDestinationAttributes |= FileAttributes.Archive; - } - - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - sut.Replace(destinationName, backupName, true); - - FileSystem.File.GetAttributes(destinationName) - .Should().Be(expectedSourceAttributes); - FileSystem.File.GetAttributes(backupName) - .Should().Be(expectedDestinationAttributes); - } - - [SkippableTheory] - [AutoData] - public void Replace_ShouldKeepMetadata( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText(sourceName, sourceContents); - DateTime sourceLastAccessTime = FileSystem.File.GetLastAccessTime(sourceName); - DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTime(sourceName); - TimeSystem.Thread.Sleep(1000); - - FileSystem.File.WriteAllText(destinationName, destinationContents); - DateTime destinationCreationTime = - FileSystem.File.GetCreationTime(destinationName); - DateTime destinationLastAccessTime = - FileSystem.File.GetLastAccessTime(destinationName); - DateTime destinationLastWriteTime = - FileSystem.File.GetLastWriteTime(destinationName); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - TimeSystem.Thread.Sleep(1000); - - sut.Replace(destinationName, backupName); - - FileSystem.File.GetCreationTime(destinationName) - .Should().Be(destinationCreationTime); - FileSystem.File.GetLastAccessTime(destinationName) - .Should().Be(sourceLastAccessTime); - FileSystem.File.GetLastWriteTime(destinationName) - .Should().Be(sourceLastWriteTime); - FileSystem.File.GetCreationTime(backupName) - .Should().Be(destinationCreationTime); - FileSystem.File.GetLastAccessTime(backupName) - .Should().Be(destinationLastAccessTime); - FileSystem.File.GetLastWriteTime(backupName) - .Should().Be(destinationLastWriteTime); - } - - [SkippableTheory] - [AutoData] - public void Replace_ShouldReplaceFile( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - IFileInfo result = sut.Replace(destinationName, backupName); - - sut.Exists.Should().BeFalse(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - - [SkippableTheory] - [AutoData] - public void Replace_SourceDirectoryMissing_ShouldThrowFileNotFoundException( - string missingDirectory, - string sourceName, - string destinationName, - string backupName) - { - string sourcePath = FileSystem.Path.Combine(missingDirectory, sourceName); - IFileInfo sut = FileSystem.FileInfo.New(sourcePath); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupName); - }); - - exception.Should().BeFileOrDirectoryNotFoundException(); - } - - [SkippableTheory] - [AutoData] - public void Replace_SourceIsDirectory_ShouldThrowUnauthorizedAccessException( - string sourceName, - string destinationName, - string backupName) - { - Skip.IfNot(Test.RunsOnWindows, "Tests sometimes throw IOException on Linux"); - - FileSystem.Directory.CreateDirectory(sourceName); - FileSystem.File.WriteAllText(destinationName, null); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024891); - } - - [SkippableTheory] - [AutoData] - public void Replace_SourceLocked_ShouldThrowIOException_OnWindows( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, - FileAccess.Read, FileShare.Read); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupName); - }); - - stream.Dispose(); - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024864); - sut.Exists.Should().BeTrue(); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - FileSystem.File.GetAttributes(destinationName) - .Should().NotHaveFlag(FileAttributes.ReadOnly); - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - else - { - sut.Exists.Should().BeFalse(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - } - - [SkippableTheory] - [AutoData] - public void Replace_SourceMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName, - string backupName) - { - FileSystem.File.WriteAllText(destinationName, null); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024894); - if (Test.RunsOnWindows) - { - // Behaviour on Linux/MacOS is uncertain - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void Replace_WithExistingBackupFile_ShouldIgnoreBackup( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents, - string backupContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - FileSystem.File.WriteAllText(backupName, backupContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - IFileInfo result = sut.Replace(destinationName, null); - - sut.Exists.Should().BeFalse(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(backupContents); - } - - [SkippableTheory] - [AutoData] - public void Replace_WithoutBackup_ShouldReplaceFile( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - IFileInfo result = sut.Replace(destinationName, null); - - sut.Exists.Should().BeFalse(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ReplaceTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Replace_BackupDirectoryMissing_ShouldThrowCorrectException( + string sourceName, + string destinationName, + string missingDirectory, + string backupName) + { + FileSystem.File.WriteAllText(sourceName, "some content"); + string backupPath = FileSystem.Path.Combine(missingDirectory, backupName); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupPath); + }); + + exception.Should().BeFileOrDirectoryNotFoundException(); + } + + [SkippableTheory] + [AutoData] + public void Replace_DestinationDirectoryMissing_ShouldThrowDirectoryNotFoundException( + string sourceName, + string missingDirectory, + string destinationName, + string backupName) + { + FileSystem.File.WriteAllText(sourceName, "some content"); + string destinationPath = + FileSystem.Path.Combine(missingDirectory, destinationName); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationPath, backupName); + }); + + exception.Should().BeFileOrDirectoryNotFoundException(); + } + + [SkippableTheory] + [AutoData] + public void Replace_DestinationIsDirectory_ShouldThrowUnauthorizedAccessException( + string sourceName, + string destinationName, + string backupName) + { + FileSystem.File.WriteAllText(sourceName, null); + FileSystem.Directory.CreateDirectory(destinationName); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024891); + } + + [SkippableTheory] + [AutoData] + public void Replace_DestinationMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName, + string backupName) + { + FileSystem.File.WriteAllText(sourceName, null); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024894); + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Replace_ReadOnly_WithIgnoreMetadataError_ShouldReplaceFile( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + sut.IsReadOnly = true; + + IFileInfo result = sut.Replace(destinationName, backupName, true); + + sut.Exists.Should().BeFalse(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.GetAttributes(destinationName) + .Should().HaveFlag(FileAttributes.ReadOnly); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + + [SkippableTheory] + [AutoData] + public void + Replace_ReadOnly_WithoutIgnoreMetadataError_ShouldThrowUnauthorizedAccessException_OnWindows( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + sut.IsReadOnly = true; + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024891); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + FileSystem.File.GetAttributes(destinationName) + .Should().NotHaveFlag(FileAttributes.ReadOnly); + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + else + { + exception.Should().BeNull(); + sut.Exists.Should().BeFalse(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + } + + [SkippableTheory] + [AutoData] + public void Replace_ShouldAddArchiveAttribute_OnWindows( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents, + FileAttributes sourceFileAttributes, + FileAttributes destinationFileAttributes) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.SetAttributes(sourceName, sourceFileAttributes); + FileAttributes expectedSourceAttributes = + FileSystem.File.GetAttributes(sourceName); + if (Test.RunsOnWindows) + { + expectedSourceAttributes |= FileAttributes.Archive; + } + + FileSystem.File.WriteAllText(destinationName, destinationContents); + FileSystem.File.SetAttributes(destinationName, destinationFileAttributes); + FileAttributes expectedDestinationAttributes = + FileSystem.File.GetAttributes(destinationName); + if (Test.RunsOnWindows) + { + expectedDestinationAttributes |= FileAttributes.Archive; + } + + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + sut.Replace(destinationName, backupName, true); + + FileSystem.File.GetAttributes(destinationName) + .Should().Be(expectedSourceAttributes); + FileSystem.File.GetAttributes(backupName) + .Should().Be(expectedDestinationAttributes); + } + + [SkippableTheory] + [AutoData] + public void Replace_ShouldKeepMetadata( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText(sourceName, sourceContents); + DateTime sourceLastAccessTime = FileSystem.File.GetLastAccessTime(sourceName); + DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTime(sourceName); + TimeSystem.Thread.Sleep(1000); + + FileSystem.File.WriteAllText(destinationName, destinationContents); + DateTime destinationCreationTime = + FileSystem.File.GetCreationTime(destinationName); + DateTime destinationLastAccessTime = + FileSystem.File.GetLastAccessTime(destinationName); + DateTime destinationLastWriteTime = + FileSystem.File.GetLastWriteTime(destinationName); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + TimeSystem.Thread.Sleep(1000); + + sut.Replace(destinationName, backupName); + + FileSystem.File.GetCreationTime(destinationName) + .Should().Be(destinationCreationTime); + FileSystem.File.GetLastAccessTime(destinationName) + .Should().Be(sourceLastAccessTime); + FileSystem.File.GetLastWriteTime(destinationName) + .Should().Be(sourceLastWriteTime); + FileSystem.File.GetCreationTime(backupName) + .Should().Be(destinationCreationTime); + FileSystem.File.GetLastAccessTime(backupName) + .Should().Be(destinationLastAccessTime); + FileSystem.File.GetLastWriteTime(backupName) + .Should().Be(destinationLastWriteTime); + } + + [SkippableTheory] + [AutoData] + public void Replace_ShouldReplaceFile( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + IFileInfo result = sut.Replace(destinationName, backupName); + + sut.Exists.Should().BeFalse(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + + [SkippableTheory] + [AutoData] + public void Replace_SourceDirectoryMissing_ShouldThrowFileNotFoundException( + string missingDirectory, + string sourceName, + string destinationName, + string backupName) + { + string sourcePath = FileSystem.Path.Combine(missingDirectory, sourceName); + IFileInfo sut = FileSystem.FileInfo.New(sourcePath); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + exception.Should().BeFileOrDirectoryNotFoundException(); + } + + [SkippableTheory] + [AutoData] + public void Replace_SourceIsDirectory_ShouldThrowUnauthorizedAccessException( + string sourceName, + string destinationName, + string backupName) + { + Skip.IfNot(Test.RunsOnWindows, "Tests sometimes throw IOException on Linux"); + + FileSystem.Directory.CreateDirectory(sourceName); + FileSystem.File.WriteAllText(destinationName, null); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024891); + } + + [SkippableTheory] + [AutoData] + public void Replace_SourceLocked_ShouldThrowIOException_OnWindows( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, + FileAccess.Read, FileShare.Read); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + stream.Dispose(); + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024864); + sut.Exists.Should().BeTrue(); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + FileSystem.File.GetAttributes(destinationName) + .Should().NotHaveFlag(FileAttributes.ReadOnly); + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + else + { + sut.Exists.Should().BeFalse(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + } + + [SkippableTheory] + [AutoData] + public void Replace_SourceMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName, + string backupName) + { + FileSystem.File.WriteAllText(destinationName, null); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024894); + if (Test.RunsOnWindows) + { + // Behaviour on Linux/MacOS is uncertain + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void Replace_WithExistingBackupFile_ShouldIgnoreBackup( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents, + string backupContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + FileSystem.File.WriteAllText(backupName, backupContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + IFileInfo result = sut.Replace(destinationName, null); + + sut.Exists.Should().BeFalse(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(backupContents); + } + + [SkippableTheory] + [AutoData] + public void Replace_WithoutBackup_ShouldReplaceFile( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + IFileInfo result = sut.Replace(destinationName, null); + + sut.Exists.Should().BeFalse(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/Tests.cs index fb9daf62c..e0efea092 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/Tests.cs @@ -1,162 +1,161 @@ -using System.IO; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Attributes_WhenFileIsMissing_ShouldReturnMinusOne(string path) - { - FileAttributes expected = (FileAttributes)(-1); - IFileInfo sut = FileSystem.FileInfo.New(path); - - sut.Attributes.Should().Be(expected); - } - - [SkippableTheory] - [AutoData] - public void Attributes_WhenFileIsMissing_SetterShouldThrowFileNotFoundException( - string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - sut.Attributes = FileAttributes.ReadOnly; - }); - - exception.Should().BeException(hResult: -2147024894); - } - - [SkippableFact] - public void Directory_ShouldReturnParentDirectory() - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithAFile()); - IFileInfo? file = initialized[1] as IFileInfo; - - file?.Directory.Should().NotBeNull(); - file!.Directory!.FullName.Should().Be(initialized[0].FullName); - } - - [SkippableFact] - public void DirectoryName_ShouldReturnNameOfParentDirectory() - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithAFile()); - IFileInfo? file = initialized[1] as IFileInfo; - - file?.Should().NotBeNull(); - file!.DirectoryName.Should().Be(initialized[0].FullName); - } - - [SkippableTheory] - [InlineData("foo", "")] - [InlineData("foo.txt", ".txt")] - [InlineData("foo.bar.txt", ".txt")] - public void Extension_ShouldReturnExpectedValue(string fileName, string expectedValue) - { - IFileInfo sut = FileSystem.FileInfo.New(fileName); - - sut.Extension.Should().Be(expectedValue); - } - - [SkippableFact] - public void Extension_WithTrailingDot_ShouldReturnExpectedValue() - { - IFileInfo sut = FileSystem.FileInfo.New("foo."); - - if (Test.RunsOnWindows) - { - sut.Extension.Should().Be(""); - } - else - { - sut.Extension.Should().Be("."); - } - } - - [SkippableTheory] - [AutoData] - public void IsReadOnly_SetToFalse_ShouldRemoveReadOnlyAttribute(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - fileInfo.Attributes = FileAttributes.ReadOnly; - - fileInfo.IsReadOnly = false; - - fileInfo.IsReadOnly.Should().BeFalse(); - fileInfo.Attributes.Should().Be(FileAttributes.Normal); - } - - [SkippableTheory] - [AutoData] - public void IsReadOnly_SetToTrue_ShouldAddReadOnlyAttribute(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - fileInfo.IsReadOnly = true; - - fileInfo.IsReadOnly.Should().BeTrue(); - fileInfo.Attributes.Should().HaveFlag(FileAttributes.ReadOnly); - - fileInfo.IsReadOnly = true; - - fileInfo.IsReadOnly.Should().BeTrue(); - fileInfo.Attributes.Should().HaveFlag(FileAttributes.ReadOnly); - - fileInfo.IsReadOnly = false; - - fileInfo.IsReadOnly.Should().BeFalse(); - fileInfo.Attributes.Should().NotHaveFlag(FileAttributes.ReadOnly); - } - - [SkippableTheory] - [AutoData] - public void IsReadOnly_ShouldChangeWhenSettingReadOnlyAttribute(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - fileInfo.Attributes = FileAttributes.ReadOnly | FileAttributes.Encrypted; - - fileInfo.IsReadOnly.Should().BeTrue(); - fileInfo.Attributes.Should().HaveFlag(FileAttributes.ReadOnly); - } - - [SkippableTheory] - [AutoData] - public void IsReadOnly_ShouldInitializeToReadOnlyAttribute(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - fileInfo.IsReadOnly.Should().BeFalse(); - fileInfo.Attributes.Should().NotHaveFlag(FileAttributes.ReadOnly); - } - - [SkippableTheory] - [InlineData("/foo")] - [InlineData("./foo")] - [InlineData("foo")] - public void ToString_ShouldReturnProvidedPath(string path) - { - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - string? result = fileInfo.ToString(); - - result.Should().Be(path); - } -} +using System.IO; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Attributes_WhenFileIsMissing_ShouldReturnMinusOne(string path) + { + FileAttributes expected = (FileAttributes)(-1); + IFileInfo sut = FileSystem.FileInfo.New(path); + + sut.Attributes.Should().Be(expected); + } + + [SkippableTheory] + [AutoData] + public void Attributes_WhenFileIsMissing_SetterShouldThrowFileNotFoundException( + string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + sut.Attributes = FileAttributes.ReadOnly; + }); + + exception.Should().BeException(hResult: -2147024894); + } + + [SkippableFact] + public void Directory_ShouldReturnParentDirectory() + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithAFile()); + IFileInfo? file = initialized[1] as IFileInfo; + + file?.Directory.Should().NotBeNull(); + file!.Directory!.FullName.Should().Be(initialized[0].FullName); + } + + [SkippableFact] + public void DirectoryName_ShouldReturnNameOfParentDirectory() + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithAFile()); + IFileInfo? file = initialized[1] as IFileInfo; + + file?.Should().NotBeNull(); + file!.DirectoryName.Should().Be(initialized[0].FullName); + } + + [SkippableTheory] + [InlineData("foo", "")] + [InlineData("foo.txt", ".txt")] + [InlineData("foo.bar.txt", ".txt")] + public void Extension_ShouldReturnExpectedValue(string fileName, string expectedValue) + { + IFileInfo sut = FileSystem.FileInfo.New(fileName); + + sut.Extension.Should().Be(expectedValue); + } + + [SkippableFact] + public void Extension_WithTrailingDot_ShouldReturnExpectedValue() + { + IFileInfo sut = FileSystem.FileInfo.New("foo."); + + if (Test.RunsOnWindows) + { + sut.Extension.Should().Be(""); + } + else + { + sut.Extension.Should().Be("."); + } + } + + [SkippableTheory] + [AutoData] + public void IsReadOnly_SetToFalse_ShouldRemoveReadOnlyAttribute(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + fileInfo.Attributes = FileAttributes.ReadOnly; + + fileInfo.IsReadOnly = false; + + fileInfo.IsReadOnly.Should().BeFalse(); + fileInfo.Attributes.Should().Be(FileAttributes.Normal); + } + + [SkippableTheory] + [AutoData] + public void IsReadOnly_SetToTrue_ShouldAddReadOnlyAttribute(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + fileInfo.IsReadOnly = true; + + fileInfo.IsReadOnly.Should().BeTrue(); + fileInfo.Attributes.Should().HaveFlag(FileAttributes.ReadOnly); + + fileInfo.IsReadOnly = true; + + fileInfo.IsReadOnly.Should().BeTrue(); + fileInfo.Attributes.Should().HaveFlag(FileAttributes.ReadOnly); + + fileInfo.IsReadOnly = false; + + fileInfo.IsReadOnly.Should().BeFalse(); + fileInfo.Attributes.Should().NotHaveFlag(FileAttributes.ReadOnly); + } + + [SkippableTheory] + [AutoData] + public void IsReadOnly_ShouldChangeWhenSettingReadOnlyAttribute(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + fileInfo.Attributes = FileAttributes.ReadOnly | FileAttributes.Encrypted; + + fileInfo.IsReadOnly.Should().BeTrue(); + fileInfo.Attributes.Should().HaveFlag(FileAttributes.ReadOnly); + } + + [SkippableTheory] + [AutoData] + public void IsReadOnly_ShouldInitializeToReadOnlyAttribute(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + fileInfo.IsReadOnly.Should().BeFalse(); + fileInfo.Attributes.Should().NotHaveFlag(FileAttributes.ReadOnly); + } + + [SkippableTheory] + [InlineData("/foo")] + [InlineData("./foo")] + [InlineData("foo")] + public void ToString_ShouldReturnProvidedPath(string path) + { + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + string? result = fileInfo.ToString(); + + result.Should().Be(path); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/ExceptionTests.cs index 0f9fc153f..345da92e7 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/ExceptionTests.cs @@ -1,128 +1,127 @@ -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfoFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetFileInfoFactoryCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileInfoFactoryCallbacks), parameters: " ")] - public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetFileInfoFactoryCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileInfoFactoryCallbacks), - parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowArgumentException_OnNetFramework( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo); - }); - - if (Test.IsNetFramework) - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should() - .BeNull( - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - - #region Helpers - - public static IEnumerable GetFileInfoFactoryCallbacks(string? path) - => GetFileInfoFactoryCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetFileInfoFactoryCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.AllExceptNull, "path", fileInfoFactory - => fileInfoFactory.New(value)); -#if NET7_0_OR_GREATER - // https://github.com/dotnet/runtime/issues/78224 - yield return ( - ExceptionTestHelper.TestTypes.Null | ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, - "fileName", fileInfoFactory - => fileInfoFactory.New(value)); -#else - yield return (ExceptionTestHelper.TestTypes.Null, "fileName", fileInfoFactory - => fileInfoFactory.New(value)); -#endif - } - - #endregion -} +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfoFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetFileInfoFactoryCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileInfoFactoryCallbacks), parameters: " ")] + public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetFileInfoFactoryCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileInfoFactoryCallbacks), + parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowArgumentException_OnNetFramework( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo); + }); + + if (Test.IsNetFramework) + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should() + .BeNull( + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + + #region Helpers + + public static IEnumerable GetFileInfoFactoryCallbacks(string? path) + => GetFileInfoFactoryCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetFileInfoFactoryCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.AllExceptNull, "path", fileInfoFactory + => fileInfoFactory.New(value)); +#if NET7_0_OR_GREATER + // https://github.com/dotnet/runtime/issues/78224 + yield return ( + ExceptionTestHelper.TestTypes.Null | ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, + "fileName", fileInfoFactory + => fileInfoFactory.New(value)); +#else + yield return (ExceptionTestHelper.TestTypes.Null, "fileName", fileInfoFactory + => fileInfoFactory.New(value)); +#endif + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/Tests.cs index 20665b662..7ae16cebc 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/Tests.cs @@ -1,93 +1,92 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfoFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [InlineData(259)] - [InlineData(260)] - public void New_PathTooLong_ShouldThrowPathTooLongException_OnNetFramework( - int maxLength) - { - string rootDrive = FileTestHelper.RootDrive(); - string path = new('a', maxLength - rootDrive.Length); - Exception? exception = Record.Exception(() => - { - _ = FileSystem.FileInfo.New(rootDrive + path); - }); - - if (Test.IsNetFramework) - { - exception.Should().BeException(hResult: -2147024690); - } - else - { - exception.Should().BeNull(); - } - } - - [SkippableTheory] - [AutoData] - public void New_ShouldCreateNewFileInfoFromPath(string path) - { - IFileInfo result = FileSystem.FileInfo.New(path); - - result.ToString().Should().Be(path); - result.Exists.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void New_ShouldOpenWithExistingContent(string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - IFileInfo sut = FileSystem.FileInfo.New(path); - - using StreamReader streamReader = new(sut.OpenRead()); - string result = streamReader.ReadToEnd(); - result.Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void New_ShouldSetLength(string path, byte[] bytes) - { - FileSystem.File.WriteAllBytes(path, bytes); - - FileSystemStream stream = FileSystem.File.Open(path, FileMode.Open, - FileAccess.Read, FileShare.Write); - IFileInfo sut = FileSystem.FileInfo.New(path); - - long result = sut.Length; - - stream.Dispose(); - - result.Should().Be(bytes.Length); - } - - [SkippableFact] - public void Wrap_Null_ShouldReturnNull() - { - IFileInfo? result = FileSystem.FileInfo.Wrap(null); - - result.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void Wrap_ShouldWrapFromFileInfo(string path) - { - System.IO.FileInfo fileInfo = new(path); - - IFileInfo result = FileSystem.FileInfo.Wrap(fileInfo); - - result.FullName.Should().Be(fileInfo.FullName); - result.Exists.Should().Be(fileInfo.Exists); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfoFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [InlineData(259)] + [InlineData(260)] + public void New_PathTooLong_ShouldThrowPathTooLongException_OnNetFramework( + int maxLength) + { + string rootDrive = FileTestHelper.RootDrive(); + string path = new('a', maxLength - rootDrive.Length); + Exception? exception = Record.Exception(() => + { + _ = FileSystem.FileInfo.New(rootDrive + path); + }); + + if (Test.IsNetFramework) + { + exception.Should().BeException(hResult: -2147024690); + } + else + { + exception.Should().BeNull(); + } + } + + [SkippableTheory] + [AutoData] + public void New_ShouldCreateNewFileInfoFromPath(string path) + { + IFileInfo result = FileSystem.FileInfo.New(path); + + result.ToString().Should().Be(path); + result.Exists.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void New_ShouldOpenWithExistingContent(string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + IFileInfo sut = FileSystem.FileInfo.New(path); + + using StreamReader streamReader = new(sut.OpenRead()); + string result = streamReader.ReadToEnd(); + result.Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void New_ShouldSetLength(string path, byte[] bytes) + { + FileSystem.File.WriteAllBytes(path, bytes); + + FileSystemStream stream = FileSystem.File.Open(path, FileMode.Open, + FileAccess.Read, FileShare.Write); + IFileInfo sut = FileSystem.FileInfo.New(path); + + long result = sut.Length; + + stream.Dispose(); + + result.Should().Be(bytes.Length); + } + + [SkippableFact] + public void Wrap_Null_ShouldReturnNull() + { + IFileInfo? result = FileSystem.FileInfo.Wrap(null); + + result.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void Wrap_ShouldWrapFromFileInfo(string path) + { + System.IO.FileInfo fileInfo = new(path); + + IFileInfo result = FileSystem.FileInfo.Wrap(fileInfo); + + result.FullName.Should().Be(fileInfo.FullName); + result.Exists.Should().Be(fileInfo.Exists); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/AdjustTimesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/AdjustTimesTests.cs index 72dc93c2b..a2dbd2282 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/AdjustTimesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/AdjustTimesTests.cs @@ -1,510 +1,509 @@ -using System.IO; -using Testably.Abstractions.FileSystem; -#if FEATURE_SPAN -using System.Threading.Tasks; -#endif - -namespace Testably.Abstractions.Tests.FileSystem.FileStream; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class AdjustTimesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void CopyTo_ShouldAdjustTimes(string path, byte[] contents) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - byte[] buffer = new byte[contents.Length]; - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - using MemoryStream destination = new(buffer); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - - stream.CopyTo(destination); - destination.Flush(); - - DateTime lastAccessTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public void Read_AsSpan_ShouldAdjustTimes(string path, byte[] contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - byte[] buffer = new byte[2]; - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - _ = stream.Read(buffer.AsSpan()); - - DateTime lastAccessTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } -#endif - - [SkippableTheory] - [AutoData] - public void Read_ShouldAdjustTimes(string path, byte[] contents) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - byte[] buffer = new byte[2]; - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - _ = stream.Read(buffer, 0, 2); - - DateTime lastAccessTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public async Task ReadAsync_AsMemory_ShouldAdjustTimes(string path, byte[] contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - byte[] buffer = new byte[2]; - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - await FileSystem.File.WriteAllBytesAsync(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - await using FileSystemStream stream = FileSystem.File.OpenRead(path); - - _ = await stream.ReadAsync(buffer.AsMemory()); - - DateTime lastAccessTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } -#endif - -#if FEATURE_FILESYSTEM_ASYNC - [SkippableTheory] - [AutoData] - public async Task ReadAsync_ShouldAdjustTimes(string path, byte[] contents) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - byte[] buffer = new byte[2]; - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - await FileSystem.File.WriteAllBytesAsync(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - await using FileSystemStream stream = FileSystem.File.OpenRead(path); - - #pragma warning disable CA1835 - _ = await stream.ReadAsync(buffer, 0, 2); - #pragma warning restore CA1835 - - DateTime lastAccessTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } -#endif - - [SkippableTheory] - [AutoData] - public void ReadByte_ShouldAdjustTimes(string path, byte[] contents) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - stream.ReadByte(); - - DateTime lastAccessTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - [SkippableTheory] - [AutoData] - public void Seek_ShouldNotAdjustTimes(string path, byte[] contents) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - stream.Seek(2, SeekOrigin.Current); - - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public void Write_AsSpan_ShouldAdjustTimes(string path, byte[] contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, Array.Empty()); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - stream.Write(contents.AsSpan()); - stream.Dispose(); - - DateTime lastWriteTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime); - } -#endif - - [SkippableTheory] - [AutoData] - public void Write_ShouldAdjustTimes(string path, byte[] contents) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, Array.Empty()); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - stream.Write(contents, 0, 2); - stream.Dispose(); - - DateTime lastWriteTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public async Task WriteAsync_AsMemory_ShouldAdjustTimes(string path, byte[] contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - await FileSystem.File.WriteAllBytesAsync(path, Array.Empty()); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - await using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - await stream.WriteAsync(contents.AsMemory()); - await stream.DisposeAsync(); - - DateTime lastWriteTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime); - } -#endif - -#if FEATURE_FILESYSTEM_ASYNC - [SkippableTheory] - [AutoData] - public async Task WriteAsync_ShouldAdjustTimes(string path, byte[] contents) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - await FileSystem.File.WriteAllBytesAsync(path, Array.Empty()); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - await using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - #pragma warning disable CA1835 - await stream.WriteAsync(contents, 0, 2); - #pragma warning restore CA1835 - await stream.DisposeAsync(); - - DateTime lastWriteTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime); - } -#endif - - [SkippableTheory] - [AutoData] - public void WriteByte_ShouldAdjustTimes(string path, byte[] contents, byte content) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - stream.WriteByte(content); - stream.Dispose(); - - DateTime lastWriteTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime); - } - - #region Helpers - - private DateTime WaitToBeUpdatedToAfter(Func callback, - DateTime expectedAfter) - { - for (int i = 0; i < 100; i++) - { - DateTime time = callback(); - if (time >= expectedAfter) - { - return time; - } - - TimeSystem.Thread.Sleep(100); - } - - return callback(); - } - - #endregion -} +using System.IO; +#if FEATURE_SPAN +using System.Threading.Tasks; +#endif + +namespace Testably.Abstractions.Tests.FileSystem.FileStream; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class AdjustTimesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void CopyTo_ShouldAdjustTimes(string path, byte[] contents) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + byte[] buffer = new byte[contents.Length]; + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + using MemoryStream destination = new(buffer); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + + stream.CopyTo(destination); + destination.Flush(); + + DateTime lastAccessTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public void Read_AsSpan_ShouldAdjustTimes(string path, byte[] contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + byte[] buffer = new byte[2]; + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + _ = stream.Read(buffer.AsSpan()); + + DateTime lastAccessTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } +#endif + + [SkippableTheory] + [AutoData] + public void Read_ShouldAdjustTimes(string path, byte[] contents) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + byte[] buffer = new byte[2]; + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + _ = stream.Read(buffer, 0, 2); + + DateTime lastAccessTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public async Task ReadAsync_AsMemory_ShouldAdjustTimes(string path, byte[] contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + byte[] buffer = new byte[2]; + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + await FileSystem.File.WriteAllBytesAsync(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + await using FileSystemStream stream = FileSystem.File.OpenRead(path); + + _ = await stream.ReadAsync(buffer.AsMemory()); + + DateTime lastAccessTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } +#endif + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableTheory] + [AutoData] + public async Task ReadAsync_ShouldAdjustTimes(string path, byte[] contents) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + byte[] buffer = new byte[2]; + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + await FileSystem.File.WriteAllBytesAsync(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + await using FileSystemStream stream = FileSystem.File.OpenRead(path); + + #pragma warning disable CA1835 + _ = await stream.ReadAsync(buffer, 0, 2); + #pragma warning restore CA1835 + + DateTime lastAccessTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } +#endif + + [SkippableTheory] + [AutoData] + public void ReadByte_ShouldAdjustTimes(string path, byte[] contents) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + stream.ReadByte(); + + DateTime lastAccessTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + [SkippableTheory] + [AutoData] + public void Seek_ShouldNotAdjustTimes(string path, byte[] contents) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + stream.Seek(2, SeekOrigin.Current); + + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public void Write_AsSpan_ShouldAdjustTimes(string path, byte[] contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, Array.Empty()); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + stream.Write(contents.AsSpan()); + stream.Dispose(); + + DateTime lastWriteTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime); + } +#endif + + [SkippableTheory] + [AutoData] + public void Write_ShouldAdjustTimes(string path, byte[] contents) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, Array.Empty()); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + stream.Write(contents, 0, 2); + stream.Dispose(); + + DateTime lastWriteTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public async Task WriteAsync_AsMemory_ShouldAdjustTimes(string path, byte[] contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + await FileSystem.File.WriteAllBytesAsync(path, Array.Empty()); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + await using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + await stream.WriteAsync(contents.AsMemory()); + await stream.DisposeAsync(); + + DateTime lastWriteTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime); + } +#endif + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableTheory] + [AutoData] + public async Task WriteAsync_ShouldAdjustTimes(string path, byte[] contents) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + await FileSystem.File.WriteAllBytesAsync(path, Array.Empty()); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + await using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + #pragma warning disable CA1835 + await stream.WriteAsync(contents, 0, 2); + #pragma warning restore CA1835 + await stream.DisposeAsync(); + + DateTime lastWriteTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime); + } +#endif + + [SkippableTheory] + [AutoData] + public void WriteByte_ShouldAdjustTimes(string path, byte[] contents, byte content) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + stream.WriteByte(content); + stream.Dispose(); + + DateTime lastWriteTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime); + } + + #region Helpers + + private DateTime WaitToBeUpdatedToAfter(Func callback, + DateTime expectedAfter) + { + for (int i = 0; i < 100; i++) + { + DateTime time = callback(); + if (time >= expectedAfter) + { + return time; + } + + TimeSystem.Thread.Sleep(100); + } + + return callback(); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/DisposeTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/DisposeTests.cs index 90bfd9c6e..fd0de5f09 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/DisposeTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/DisposeTests.cs @@ -1,116 +1,115 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Threading; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileStream; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class DisposeTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Dispose_CalledTwiceShouldDoNothing( - string path, byte[] contents) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - FileSystem.File.WriteAllBytes(path, contents); - - using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, - FileAccess.ReadWrite, FileShare.ReadWrite, 10, FileOptions.DeleteOnClose); - - stream.Dispose(); - FileSystem.File.Exists(path).Should().BeFalse(); - FileSystem.File.WriteAllText(path, "foo"); - - stream.Dispose(); - - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void Dispose_ShouldNotResurrectFile(string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - FileSystemStream stream = FileSystem.File.Open(path, - FileMode.Open, - FileAccess.ReadWrite, - FileShare.Delete); - - int fileCount1 = FileSystem.Directory.GetFiles(".", "*").Length; - FileSystem.File.Delete(path); - int fileCount2 = FileSystem.Directory.GetFiles(".", "*").Length; - stream.Dispose(); - int fileCount3 = FileSystem.Directory.GetFiles(".", "*").Length; - - fileCount1.Should().Be(1, "File should have existed"); - fileCount2.Should().Be(0, "File should have been deleted"); - fileCount3.Should().Be(0, "Dispose should not have resurrected the file"); - } - - [Theory] - [MemberData(nameof(GetFileStreamCallbacks))] - public void Operations_ShouldThrowAfterStreamIsDisposed( - Expression> callback) - { - FileSystem.File.WriteAllText("foo", "some content"); - Exception? exception = Record.Exception(() => - { - FileSystemStream stream = - FileSystem.FileStream.New("foo", FileMode.Open, FileAccess.ReadWrite); - stream.Dispose(); - callback.Compile().Invoke(stream); - }); - - exception.Should() - .BeOfType( - $"\n{callback}\n executed after Dispose() was called.") - .Which.ObjectName.Should() - .BeEmpty($"\n{callback}\n executed after Dispose() was called."); - } - - #region Helpers - - public static IEnumerable GetFileStreamCallbacks() - => GetFileStreamCallbackTestParameters() - .Select(item => new object?[] - { - item - }); - - private static IEnumerable>> - GetFileStreamCallbackTestParameters() - { - yield return fileStream => fileStream.BeginRead(Array.Empty(), 0, 0, null, null); - yield return fileStream => fileStream.BeginWrite(Array.Empty(), 0, 0, null, null); - yield return fileStream => fileStream.CopyTo(new MemoryStream(), 1); - yield return fileStream - => fileStream.CopyToAsync(new MemoryStream(), 1, CancellationToken.None) - .GetAwaiter().GetResult(); - yield return fileStream => fileStream.Flush(); - yield return fileStream => fileStream.FlushAsync(CancellationToken.None) - .GetAwaiter().GetResult(); - // ReSharper disable once MustUseReturnValue - yield return fileStream => fileStream.Read(Array.Empty(), 0, 0); -#if FEATURE_SPAN - //yield return fileStream => fileStream.Read(Array.Empty().AsSpan()); -#endif - yield return fileStream => fileStream.ReadAsync(Array.Empty(), 0, 0) - .GetAwaiter().GetResult(); - yield return fileStream => fileStream.ReadByte(); - yield return fileStream => fileStream.Seek(0, SeekOrigin.Begin); - yield return fileStream => fileStream.SetLength(0); - yield return fileStream => fileStream.Write(Array.Empty(), 0, 0); - yield return fileStream => fileStream.WriteAsync(Array.Empty(), 0, 0) - .GetAwaiter().GetResult(); - yield return fileStream => fileStream.WriteByte(0x42); - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; + +namespace Testably.Abstractions.Tests.FileSystem.FileStream; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class DisposeTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Dispose_CalledTwiceShouldDoNothing( + string path, byte[] contents) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + FileSystem.File.WriteAllBytes(path, contents); + + using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, + FileAccess.ReadWrite, FileShare.ReadWrite, 10, FileOptions.DeleteOnClose); + + stream.Dispose(); + FileSystem.File.Exists(path).Should().BeFalse(); + FileSystem.File.WriteAllText(path, "foo"); + + stream.Dispose(); + + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void Dispose_ShouldNotResurrectFile(string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + FileSystemStream stream = FileSystem.File.Open(path, + FileMode.Open, + FileAccess.ReadWrite, + FileShare.Delete); + + int fileCount1 = FileSystem.Directory.GetFiles(".", "*").Length; + FileSystem.File.Delete(path); + int fileCount2 = FileSystem.Directory.GetFiles(".", "*").Length; + stream.Dispose(); + int fileCount3 = FileSystem.Directory.GetFiles(".", "*").Length; + + fileCount1.Should().Be(1, "File should have existed"); + fileCount2.Should().Be(0, "File should have been deleted"); + fileCount3.Should().Be(0, "Dispose should not have resurrected the file"); + } + + [Theory] + [MemberData(nameof(GetFileStreamCallbacks))] + public void Operations_ShouldThrowAfterStreamIsDisposed( + Expression> callback) + { + FileSystem.File.WriteAllText("foo", "some content"); + Exception? exception = Record.Exception(() => + { + FileSystemStream stream = + FileSystem.FileStream.New("foo", FileMode.Open, FileAccess.ReadWrite); + stream.Dispose(); + callback.Compile().Invoke(stream); + }); + + exception.Should() + .BeOfType( + $"\n{callback}\n executed after Dispose() was called.") + .Which.ObjectName.Should() + .BeEmpty($"\n{callback}\n executed after Dispose() was called."); + } + + #region Helpers + + public static IEnumerable GetFileStreamCallbacks() + => GetFileStreamCallbackTestParameters() + .Select(item => new object?[] + { + item + }); + + private static IEnumerable>> + GetFileStreamCallbackTestParameters() + { + yield return fileStream => fileStream.BeginRead(Array.Empty(), 0, 0, null, null); + yield return fileStream => fileStream.BeginWrite(Array.Empty(), 0, 0, null, null); + yield return fileStream => fileStream.CopyTo(new MemoryStream(), 1); + yield return fileStream + => fileStream.CopyToAsync(new MemoryStream(), 1, CancellationToken.None) + .GetAwaiter().GetResult(); + yield return fileStream => fileStream.Flush(); + yield return fileStream => fileStream.FlushAsync(CancellationToken.None) + .GetAwaiter().GetResult(); + // ReSharper disable once MustUseReturnValue + yield return fileStream => fileStream.Read(Array.Empty(), 0, 0); +#if FEATURE_SPAN + //yield return fileStream => fileStream.Read(Array.Empty().AsSpan()); +#endif + yield return fileStream => fileStream.ReadAsync(Array.Empty(), 0, 0) + .GetAwaiter().GetResult(); + yield return fileStream => fileStream.ReadByte(); + yield return fileStream => fileStream.Seek(0, SeekOrigin.Begin); + yield return fileStream => fileStream.SetLength(0); + yield return fileStream => fileStream.Write(Array.Empty(), 0, 0); + yield return fileStream => fileStream.WriteAsync(Array.Empty(), 0, 0) + .GetAwaiter().GetResult(); + yield return fileStream => fileStream.WriteByte(0x42); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/FileAccessTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/FileAccessTests.cs index 8de6f4f7d..70e255e35 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/FileAccessTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/FileAccessTests.cs @@ -1,243 +1,242 @@ -using System.Collections.Concurrent; -using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileStream; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class FileAccessTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [InlineAutoData(FileAccess.Read, FileShare.Read, - FileAccess.ReadWrite, FileShare.Read)] - [InlineAutoData(FileAccess.ReadWrite, FileShare.Read, - FileAccess.Read, FileShare.Read)] - [InlineAutoData(FileAccess.ReadWrite, FileShare.ReadWrite, - FileAccess.ReadWrite, FileShare.Read)] - [InlineAutoData(FileAccess.ReadWrite, FileShare.ReadWrite, - FileAccess.ReadWrite, FileShare.Write)] - [InlineAutoData(FileAccess.Read, FileShare.Read, - FileAccess.ReadWrite, FileShare.ReadWrite)] - public void FileAccess_ConcurrentAccessWithInvalidScenarios_ShouldThrowIOException( - FileAccess access1, FileShare share1, - FileAccess access2, FileShare share2, - string path, string contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText(path, contents); - - Exception? exception = Record.Exception(() => - { - _ = FileSystem.FileStream.New(path, FileMode.Open, - access1, share1); - _ = FileSystem.FileStream.New(path, FileMode.Open, - access2, share2); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024864, - because: - $"Access {access1}, Share {share1} of file 1 is incompatible with Access {access2}, Share {share2} of file 2"); - } - else - { - exception.Should().BeNull(); - } - } - - [SkippableTheory] - [InlineAutoData(FileAccess.Read, FileShare.Read, FileAccess.Read, FileShare.Read)] - [InlineAutoData(FileAccess.Read, FileShare.ReadWrite, FileAccess.ReadWrite, - FileShare.Read)] - public void FileAccess_ConcurrentReadAccessWithValidScenarios_ShouldNotThrowException( - FileAccess access1, FileShare share1, - FileAccess access2, FileShare share2, - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Open, - access1, share1); - FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Open, - access2, share2); - - using StreamReader sr1 = new(stream1, Encoding.UTF8); - using StreamReader sr2 = new(stream2, Encoding.UTF8); - string result1 = sr1.ReadToEnd(); - string result2 = sr2.ReadToEnd(); - - result1.Should().Be(contents); - result2.Should().Be(contents); - } - - [SkippableTheory] - [InlineAutoData(FileAccess.Write, FileShare.Write, FileAccess.Write, FileShare.Write)] - [InlineAutoData(FileAccess.ReadWrite, FileShare.ReadWrite, FileAccess.ReadWrite, - FileShare.ReadWrite)] - public void - FileAccess_ConcurrentWriteAccessWithValidScenarios_ShouldNotThrowException( - FileAccess access1, FileShare share1, - FileAccess access2, FileShare share2, - string path, string contents1, string contents2) - { - FileSystem.File.WriteAllText(path, null); - - FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Open, - access1, share1); - FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Open, - access2, share2); - - byte[] bytes1 = Encoding.UTF8.GetBytes(contents1); - stream1.Write(bytes1, 0, bytes1.Length); - stream1.Flush(); - byte[] bytes2 = Encoding.UTF8.GetBytes(contents2); - stream2.Write(bytes2, 0, bytes2.Length); - stream2.Flush(); - - stream1.Dispose(); - stream2.Dispose(); - string result = FileSystem.File.ReadAllText(path); - - result.Should().Be(contents2); - } - - [SkippableTheory] - [AutoData] - public void FileAccess_ReadAfterFirstAppend_ShouldContainBothContents( - string path, string contents1, string contents2) - { - FileSystem.File.WriteAllText(path, null); - - FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Append, - FileAccess.Write, FileShare.Write); - - byte[] bytes1 = Encoding.UTF8.GetBytes(contents1); - stream1.Write(bytes1, 0, bytes1.Length); - stream1.Flush(); - - FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Append, - FileAccess.Write, FileShare.Write); - byte[] bytes2 = Encoding.UTF8.GetBytes(contents2); - stream2.Write(bytes2, 0, bytes2.Length); - stream2.Flush(); - - stream1.Dispose(); - stream2.Dispose(); - string result = FileSystem.File.ReadAllText(path); - result.Should().Be(contents1 + contents2); - } - - [SkippableTheory] - [AutoData] - public void FileAccess_ReadBeforeFirstAppend_ShouldOnlyContainSecondContent( - string path, string contents1, string contents2) - { - FileSystem.File.WriteAllText(path, null); - - FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Append, - FileAccess.Write, FileShare.Write); - FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Append, - FileAccess.Write, FileShare.Write); - - byte[] bytes1 = Encoding.UTF8.GetBytes(contents1); - stream1.Write(bytes1, 0, bytes1.Length); - stream1.Flush(); - byte[] bytes2 = Encoding.UTF8.GetBytes(contents2); - stream2.Write(bytes2, 0, bytes2.Length); - stream2.Flush(); - - stream1.Dispose(); - stream2.Dispose(); - string result = FileSystem.File.ReadAllText(path); - - result.Should().Be(contents2); - } - - [SkippableTheory] - [AutoData] - public void FileAccess_ReadWhileWriteLockActive_ShouldThrowIOException( - string path, string contents) - { - using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Create); - - byte[] bytes = Encoding.UTF8.GetBytes(contents); - stream.Write(bytes, 0, bytes.Length); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.ReadAllText(path); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024864); - } - else - { - exception.Should().BeNull(); - } - } - - [SkippableTheory] - [AutoData] - public void MultipleParallelReads_ShouldBeAllowed(string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - ConcurrentBag results = new(); - - ParallelLoopResult wait = Parallel.For(0, 100, _ => - { - FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, - FileAccess.Read, FileShare.Read); - using StreamReader sr = new(stream, Encoding.UTF8); - results.Add(sr.ReadToEnd()); - }); - - while (!wait.IsCompleted) - { - Thread.Sleep(10); - } - - results.Should().HaveCount(100); - results.Should().AllBeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void Read_ShouldCreateValidFileStream(string path, string contents) - { - FileSystem.File.WriteAllText(path, contents, Encoding.UTF8); - FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open); - using StreamReader sr = new(stream, Encoding.UTF8); - string result = sr.ReadToEnd(); - - result.Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void Write_ShouldCreateValidFileStream(string path, string contents) - { - FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.CreateNew); - - byte[] bytes = Encoding.UTF8.GetBytes(contents); - stream.Write(bytes, 0, bytes.Length); - - stream.Dispose(); - - string result = FileSystem.File.ReadAllText(path); - - result.Should().Be(contents); - } -} +using System.Collections.Concurrent; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Testably.Abstractions.Tests.FileSystem.FileStream; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class FileAccessTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [InlineAutoData(FileAccess.Read, FileShare.Read, + FileAccess.ReadWrite, FileShare.Read)] + [InlineAutoData(FileAccess.ReadWrite, FileShare.Read, + FileAccess.Read, FileShare.Read)] + [InlineAutoData(FileAccess.ReadWrite, FileShare.ReadWrite, + FileAccess.ReadWrite, FileShare.Read)] + [InlineAutoData(FileAccess.ReadWrite, FileShare.ReadWrite, + FileAccess.ReadWrite, FileShare.Write)] + [InlineAutoData(FileAccess.Read, FileShare.Read, + FileAccess.ReadWrite, FileShare.ReadWrite)] + public void FileAccess_ConcurrentAccessWithInvalidScenarios_ShouldThrowIOException( + FileAccess access1, FileShare share1, + FileAccess access2, FileShare share2, + string path, string contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText(path, contents); + + Exception? exception = Record.Exception(() => + { + _ = FileSystem.FileStream.New(path, FileMode.Open, + access1, share1); + _ = FileSystem.FileStream.New(path, FileMode.Open, + access2, share2); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024864, + because: + $"Access {access1}, Share {share1} of file 1 is incompatible with Access {access2}, Share {share2} of file 2"); + } + else + { + exception.Should().BeNull(); + } + } + + [SkippableTheory] + [InlineAutoData(FileAccess.Read, FileShare.Read, FileAccess.Read, FileShare.Read)] + [InlineAutoData(FileAccess.Read, FileShare.ReadWrite, FileAccess.ReadWrite, + FileShare.Read)] + public void FileAccess_ConcurrentReadAccessWithValidScenarios_ShouldNotThrowException( + FileAccess access1, FileShare share1, + FileAccess access2, FileShare share2, + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Open, + access1, share1); + FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Open, + access2, share2); + + using StreamReader sr1 = new(stream1, Encoding.UTF8); + using StreamReader sr2 = new(stream2, Encoding.UTF8); + string result1 = sr1.ReadToEnd(); + string result2 = sr2.ReadToEnd(); + + result1.Should().Be(contents); + result2.Should().Be(contents); + } + + [SkippableTheory] + [InlineAutoData(FileAccess.Write, FileShare.Write, FileAccess.Write, FileShare.Write)] + [InlineAutoData(FileAccess.ReadWrite, FileShare.ReadWrite, FileAccess.ReadWrite, + FileShare.ReadWrite)] + public void + FileAccess_ConcurrentWriteAccessWithValidScenarios_ShouldNotThrowException( + FileAccess access1, FileShare share1, + FileAccess access2, FileShare share2, + string path, string contents1, string contents2) + { + FileSystem.File.WriteAllText(path, null); + + FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Open, + access1, share1); + FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Open, + access2, share2); + + byte[] bytes1 = Encoding.UTF8.GetBytes(contents1); + stream1.Write(bytes1, 0, bytes1.Length); + stream1.Flush(); + byte[] bytes2 = Encoding.UTF8.GetBytes(contents2); + stream2.Write(bytes2, 0, bytes2.Length); + stream2.Flush(); + + stream1.Dispose(); + stream2.Dispose(); + string result = FileSystem.File.ReadAllText(path); + + result.Should().Be(contents2); + } + + [SkippableTheory] + [AutoData] + public void FileAccess_ReadAfterFirstAppend_ShouldContainBothContents( + string path, string contents1, string contents2) + { + FileSystem.File.WriteAllText(path, null); + + FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Append, + FileAccess.Write, FileShare.Write); + + byte[] bytes1 = Encoding.UTF8.GetBytes(contents1); + stream1.Write(bytes1, 0, bytes1.Length); + stream1.Flush(); + + FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Append, + FileAccess.Write, FileShare.Write); + byte[] bytes2 = Encoding.UTF8.GetBytes(contents2); + stream2.Write(bytes2, 0, bytes2.Length); + stream2.Flush(); + + stream1.Dispose(); + stream2.Dispose(); + string result = FileSystem.File.ReadAllText(path); + result.Should().Be(contents1 + contents2); + } + + [SkippableTheory] + [AutoData] + public void FileAccess_ReadBeforeFirstAppend_ShouldOnlyContainSecondContent( + string path, string contents1, string contents2) + { + FileSystem.File.WriteAllText(path, null); + + FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Append, + FileAccess.Write, FileShare.Write); + FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Append, + FileAccess.Write, FileShare.Write); + + byte[] bytes1 = Encoding.UTF8.GetBytes(contents1); + stream1.Write(bytes1, 0, bytes1.Length); + stream1.Flush(); + byte[] bytes2 = Encoding.UTF8.GetBytes(contents2); + stream2.Write(bytes2, 0, bytes2.Length); + stream2.Flush(); + + stream1.Dispose(); + stream2.Dispose(); + string result = FileSystem.File.ReadAllText(path); + + result.Should().Be(contents2); + } + + [SkippableTheory] + [AutoData] + public void FileAccess_ReadWhileWriteLockActive_ShouldThrowIOException( + string path, string contents) + { + using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Create); + + byte[] bytes = Encoding.UTF8.GetBytes(contents); + stream.Write(bytes, 0, bytes.Length); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.ReadAllText(path); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024864); + } + else + { + exception.Should().BeNull(); + } + } + + [SkippableTheory] + [AutoData] + public void MultipleParallelReads_ShouldBeAllowed(string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + ConcurrentBag results = new(); + + ParallelLoopResult wait = Parallel.For(0, 100, _ => + { + FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, + FileAccess.Read, FileShare.Read); + using StreamReader sr = new(stream, Encoding.UTF8); + results.Add(sr.ReadToEnd()); + }); + + while (!wait.IsCompleted) + { + Thread.Sleep(10); + } + + results.Should().HaveCount(100); + results.Should().AllBeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void Read_ShouldCreateValidFileStream(string path, string contents) + { + FileSystem.File.WriteAllText(path, contents, Encoding.UTF8); + FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open); + using StreamReader sr = new(stream, Encoding.UTF8); + string result = sr.ReadToEnd(); + + result.Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void Write_ShouldCreateValidFileStream(string path, string contents) + { + FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.CreateNew); + + byte[] bytes = Encoding.UTF8.GetBytes(contents); + stream.Write(bytes, 0, bytes.Length); + + stream.Dispose(); + + string result = FileSystem.File.ReadAllText(path); + + result.Should().Be(contents); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/OptionsTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/OptionsTests.cs index 767cc8234..4870de81d 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/OptionsTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/OptionsTests.cs @@ -1,109 +1,108 @@ -using System.IO; -using System.Text; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileStream; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OptionsTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Options_DeleteOnClose_ShouldDeleteFileOnClose( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, - FileAccess.ReadWrite, FileShare.None, 10, FileOptions.DeleteOnClose); - FileSystem.File.Exists(path).Should().BeTrue(); - - stream.Close(); - - FileSystem.File.Exists(path).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Options_DeleteOnClose_ShouldDeleteFileOnDispose( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, - FileAccess.ReadWrite, FileShare.None, 10, FileOptions.DeleteOnClose); - - stream.Dispose(); - - FileSystem.File.Exists(path).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - [SupportedOSPlatform("windows")] - public void Options_Encrypt_ShouldKeepEncryptionFlag( - string path, string contents1, string contents2) - { - Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, - "Encryption depends on the underlying device, if it is supported or not."); - - FileSystem.File.WriteAllText(path, contents1); - FileSystem.File.Encrypt(path); - - using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, - FileAccess.ReadWrite, FileShare.None, 10, FileOptions.Encrypted); - byte[] bytes = Encoding.Default.GetBytes(contents2); - - stream.Write(bytes, 0, bytes.Length); - stream.SetLength(bytes.Length); - stream.Dispose(); - - FileSystem.File.GetAttributes(path).Should().HaveFlag(FileAttributes.Encrypted); - FileSystem.File.ReadAllText(path).Should().Be(contents2); - } - - [SkippableTheory] - [AutoData] - public void Options_Encrypt_Unencrypted_ShouldBeIgnored( - string path, string contents1, string contents2) - { - FileSystem.File.WriteAllText(path, contents1); - - using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, - FileAccess.ReadWrite, FileShare.None, 10, FileOptions.Encrypted); - byte[] bytes = Encoding.Default.GetBytes(contents2); - - stream.Write(bytes, 0, bytes.Length); - stream.Dispose(); - - FileSystem.File.GetAttributes(path).Should() - .NotHaveFlag(FileAttributes.Encrypted); - FileSystem.File.ReadAllText(path).Should().Be(contents2); - } - - [SkippableTheory] - [AutoData] - [SupportedOSPlatform("windows")] - public void Options_EncryptedWithoutEncryptionOption_ShouldKeepEncryptionFlag( - string path, string contents1, string contents2) - { - Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, - "Encryption depends on the underlying device, if it is supported or not."); - - FileSystem.File.WriteAllText(path, contents1); - FileSystem.File.Encrypt(path); - - using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, - FileAccess.ReadWrite, FileShare.None, 10); - byte[] bytes = Encoding.Default.GetBytes(contents2); - - stream.Write(bytes, 0, bytes.Length); - stream.SetLength(bytes.Length); - stream.Dispose(); - - FileSystem.File.GetAttributes(path).Should().HaveFlag(FileAttributes.Encrypted); - FileSystem.File.ReadAllText(path).Should().Be(contents2); - } -} +using System.IO; +using System.Text; + +namespace Testably.Abstractions.Tests.FileSystem.FileStream; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OptionsTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Options_DeleteOnClose_ShouldDeleteFileOnClose( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, + FileAccess.ReadWrite, FileShare.None, 10, FileOptions.DeleteOnClose); + FileSystem.File.Exists(path).Should().BeTrue(); + + stream.Close(); + + FileSystem.File.Exists(path).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Options_DeleteOnClose_ShouldDeleteFileOnDispose( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, + FileAccess.ReadWrite, FileShare.None, 10, FileOptions.DeleteOnClose); + + stream.Dispose(); + + FileSystem.File.Exists(path).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + [SupportedOSPlatform("windows")] + public void Options_Encrypt_ShouldKeepEncryptionFlag( + string path, string contents1, string contents2) + { + Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, + "Encryption depends on the underlying device, if it is supported or not."); + + FileSystem.File.WriteAllText(path, contents1); + FileSystem.File.Encrypt(path); + + using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, + FileAccess.ReadWrite, FileShare.None, 10, FileOptions.Encrypted); + byte[] bytes = Encoding.Default.GetBytes(contents2); + + stream.Write(bytes, 0, bytes.Length); + stream.SetLength(bytes.Length); + stream.Dispose(); + + FileSystem.File.GetAttributes(path).Should().HaveFlag(FileAttributes.Encrypted); + FileSystem.File.ReadAllText(path).Should().Be(contents2); + } + + [SkippableTheory] + [AutoData] + public void Options_Encrypt_Unencrypted_ShouldBeIgnored( + string path, string contents1, string contents2) + { + FileSystem.File.WriteAllText(path, contents1); + + using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, + FileAccess.ReadWrite, FileShare.None, 10, FileOptions.Encrypted); + byte[] bytes = Encoding.Default.GetBytes(contents2); + + stream.Write(bytes, 0, bytes.Length); + stream.Dispose(); + + FileSystem.File.GetAttributes(path).Should() + .NotHaveFlag(FileAttributes.Encrypted); + FileSystem.File.ReadAllText(path).Should().Be(contents2); + } + + [SkippableTheory] + [AutoData] + [SupportedOSPlatform("windows")] + public void Options_EncryptedWithoutEncryptionOption_ShouldKeepEncryptionFlag( + string path, string contents1, string contents2) + { + Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, + "Encryption depends on the underlying device, if it is supported or not."); + + FileSystem.File.WriteAllText(path, contents1); + FileSystem.File.Encrypt(path); + + using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, + FileAccess.ReadWrite, FileShare.None, 10); + byte[] bytes = Encoding.Default.GetBytes(contents2); + + stream.Write(bytes, 0, bytes.Length); + stream.SetLength(bytes.Length); + stream.Dispose(); + + FileSystem.File.GetAttributes(path).Should().HaveFlag(FileAttributes.Encrypted); + FileSystem.File.ReadAllText(path).Should().Be(contents2); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/ReadTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/ReadTests.cs index c7b8bfe50..b51e2185b 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/ReadTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/ReadTests.cs @@ -1,276 +1,275 @@ -using System.Threading; -using Testably.Abstractions.FileSystem; -#if FEATURE_FILESYSTEM_ASYNC -using System.Threading.Tasks; -#endif - -namespace Testably.Abstractions.Tests.FileSystem.FileStream; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ReadTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void BeginRead_ShouldCopyContentsToBuffer( - string path, byte[] contents) - { - ManualResetEventSlim ms = new(); - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - byte[] buffer = new byte[contents.Length]; - stream.BeginRead(buffer, 0, buffer.Length, ar => - { - // ReSharper disable once AccessToDisposedClosure - stream.EndRead(ar); - ms.Set(); - }, null); - - ms.Wait(1000); - buffer.Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void BeginRead_CanReadFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - FileSystem.File.WriteAllBytes(path, contents); - FileSystemStream stream = FileSystem.FileInfo.New(path).OpenWrite(); - - byte[] buffer = new byte[contents.Length]; - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.BeginRead(buffer, 0, buffer.Length, _ => { }, null); - }); - - stream.Dispose(); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [AutoData] - public void EndRead_Null_ShouldThrowArgumentNullException( - string path, byte[] contents) - { - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.EndRead(null!); - }); - - exception.Should().BeException(hResult: -2147467261); - } - - [SkippableTheory] - [AutoData] - public void EndRead_ShouldNotAdjustTimes(string path, byte[] contents) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - ManualResetEventSlim ms = new(); - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.OpenRead(path); - DateTime updateTime = DateTime.MinValue; - - byte[] buffer = new byte[contents.Length]; - stream.BeginRead(buffer, 0, buffer.Length, ar => - { - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - updateTime = TimeSystem.DateTime.UtcNow; - // ReSharper disable once AccessToDisposedClosure - stream.EndRead(ar); - ms.Set(); - }, null); - - ms.Wait(10000); - stream.Dispose(); - - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - [SkippableTheory] - [AutoData] - public void Read_CanReadFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - _ = stream.Read(buffer, 0, contents.Length); - }); - - stream.Dispose(); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [AutoData] - public void Read_ShouldFillBuffer(string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - int result = stream.Read(buffer, 0, contents.Length); - - result.Should().Be(contents.Length); - buffer.Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void ReadByte_CanReadFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - FileSystem.File.WriteAllBytes(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - _ = stream.ReadByte(); - }); - - stream.Dispose(); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [AutoData] - public void ReadByte_ShouldReadSingleByteAndAdvancePosition( - string path, byte[] contents) - { - FileSystem.File.WriteAllBytes(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - int result1 = stream.ReadByte(); - int result2 = stream.ReadByte(); - - stream.Position.Should().Be(2); - result1.Should().Be(contents[0]); - result2.Should().Be(contents[1]); - } - - [SkippableTheory] - [AutoData] - public void ReadTimeout_ShouldThrowInvalidOperationException( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - _ = stream.ReadTimeout; - }); - - exception.Should().BeException(hResult: -2146233079); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public void Read_AsSpan_ShouldFillBuffer(string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - int result = stream.Read(buffer.AsSpan()); - - result.Should().Be(contents.Length); - buffer.Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void Read_AsSpan_CanReadFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - _ = stream.Read(buffer.AsSpan()); - }); - - stream.Dispose(); - - exception.Should().BeException(hResult: -2146233067); - } -#endif - -#if FEATURE_FILESYSTEM_ASYNC - [SkippableTheory] - [AutoData] - public async Task ReadAsync_ShouldFillBuffer(string path, byte[] contents) - { - CancellationTokenSource cts = new(10000); - byte[] buffer = new byte[contents.Length]; - await FileSystem.File.WriteAllBytesAsync(path, contents, cts.Token); - await using FileSystemStream stream = FileSystem.File.OpenRead(path); - - #pragma warning disable CA1835 - int result = await stream.ReadAsync(buffer, 0, contents.Length, cts.Token); - #pragma warning restore CA1835 - - result.Should().Be(contents.Length); - buffer.Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public async Task ReadAsync_CanReadFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - CancellationTokenSource cts = new(10000); - byte[] buffer = new byte[contents.Length]; - await FileSystem.File.WriteAllBytesAsync(path, contents, cts.Token); - await using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - Exception? exception = await Record.ExceptionAsync(async () => - { - // ReSharper disable once AccessToDisposedClosure - #pragma warning disable CA1835 - _ = await stream.ReadAsync(buffer, 0, contents.Length, cts.Token); - #pragma warning restore CA1835 - }); - - await stream.DisposeAsync(); - - exception.Should().BeException(hResult: -2146233067); - } -#endif -} +using System.Threading; +#if FEATURE_FILESYSTEM_ASYNC +using System.Threading.Tasks; +#endif + +namespace Testably.Abstractions.Tests.FileSystem.FileStream; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ReadTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void BeginRead_ShouldCopyContentsToBuffer( + string path, byte[] contents) + { + ManualResetEventSlim ms = new(); + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + byte[] buffer = new byte[contents.Length]; + stream.BeginRead(buffer, 0, buffer.Length, ar => + { + // ReSharper disable once AccessToDisposedClosure + stream.EndRead(ar); + ms.Set(); + }, null); + + ms.Wait(1000); + buffer.Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void BeginRead_CanReadFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + FileSystem.File.WriteAllBytes(path, contents); + FileSystemStream stream = FileSystem.FileInfo.New(path).OpenWrite(); + + byte[] buffer = new byte[contents.Length]; + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.BeginRead(buffer, 0, buffer.Length, _ => { }, null); + }); + + stream.Dispose(); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [AutoData] + public void EndRead_Null_ShouldThrowArgumentNullException( + string path, byte[] contents) + { + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.EndRead(null!); + }); + + exception.Should().BeException(hResult: -2147467261); + } + + [SkippableTheory] + [AutoData] + public void EndRead_ShouldNotAdjustTimes(string path, byte[] contents) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + ManualResetEventSlim ms = new(); + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.OpenRead(path); + DateTime updateTime = DateTime.MinValue; + + byte[] buffer = new byte[contents.Length]; + stream.BeginRead(buffer, 0, buffer.Length, ar => + { + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + updateTime = TimeSystem.DateTime.UtcNow; + // ReSharper disable once AccessToDisposedClosure + stream.EndRead(ar); + ms.Set(); + }, null); + + ms.Wait(10000); + stream.Dispose(); + + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + [SkippableTheory] + [AutoData] + public void Read_CanReadFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + _ = stream.Read(buffer, 0, contents.Length); + }); + + stream.Dispose(); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [AutoData] + public void Read_ShouldFillBuffer(string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + int result = stream.Read(buffer, 0, contents.Length); + + result.Should().Be(contents.Length); + buffer.Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void ReadByte_CanReadFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + FileSystem.File.WriteAllBytes(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + _ = stream.ReadByte(); + }); + + stream.Dispose(); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [AutoData] + public void ReadByte_ShouldReadSingleByteAndAdvancePosition( + string path, byte[] contents) + { + FileSystem.File.WriteAllBytes(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + int result1 = stream.ReadByte(); + int result2 = stream.ReadByte(); + + stream.Position.Should().Be(2); + result1.Should().Be(contents[0]); + result2.Should().Be(contents[1]); + } + + [SkippableTheory] + [AutoData] + public void ReadTimeout_ShouldThrowInvalidOperationException( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + _ = stream.ReadTimeout; + }); + + exception.Should().BeException(hResult: -2146233079); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public void Read_AsSpan_ShouldFillBuffer(string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + int result = stream.Read(buffer.AsSpan()); + + result.Should().Be(contents.Length); + buffer.Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void Read_AsSpan_CanReadFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + _ = stream.Read(buffer.AsSpan()); + }); + + stream.Dispose(); + + exception.Should().BeException(hResult: -2146233067); + } +#endif + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableTheory] + [AutoData] + public async Task ReadAsync_ShouldFillBuffer(string path, byte[] contents) + { + CancellationTokenSource cts = new(10000); + byte[] buffer = new byte[contents.Length]; + await FileSystem.File.WriteAllBytesAsync(path, contents, cts.Token); + await using FileSystemStream stream = FileSystem.File.OpenRead(path); + + #pragma warning disable CA1835 + int result = await stream.ReadAsync(buffer, 0, contents.Length, cts.Token); + #pragma warning restore CA1835 + + result.Should().Be(contents.Length); + buffer.Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public async Task ReadAsync_CanReadFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + CancellationTokenSource cts = new(10000); + byte[] buffer = new byte[contents.Length]; + await FileSystem.File.WriteAllBytesAsync(path, contents, cts.Token); + await using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + Exception? exception = await Record.ExceptionAsync(async () => + { + // ReSharper disable once AccessToDisposedClosure + #pragma warning disable CA1835 + _ = await stream.ReadAsync(buffer, 0, contents.Length, cts.Token); + #pragma warning restore CA1835 + }); + + await stream.DisposeAsync(); + + exception.Should().BeException(hResult: -2146233067); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/Tests.cs index 318ae556e..12225baaa 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/Tests.cs @@ -1,300 +1,299 @@ -using System.IO; -using System.Threading.Tasks; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileStream; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void CanSeek_ShouldReturnTrue( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - stream.CanSeek.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void CanTimeout_ShouldReturnFalse( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - stream.CanTimeout.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Close_CalledMultipleTimes_ShouldNotThrow( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - stream.Close(); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.Close(); - }); - - exception.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void CopyTo_ShouldCopyBytes( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - using MemoryStream destination = new(buffer); - - stream.CopyTo(destination); - - destination.Flush(); - buffer.Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void CopyTo_BufferSizeZero_ShouldThrowArgumentOutOfRangeException( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - using MemoryStream destination = new(buffer); - stream.CopyTo(destination, 0); - }); - - exception.Should().BeException( - paramName: "bufferSize"); - } - -#if FEATURE_FILESYSTEM_ASYNC - [SkippableTheory] - [AutoData] - public async Task CopyToAsync_ShouldCopyBytes( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - await FileSystem.File.WriteAllBytesAsync(path, contents); - await using FileSystemStream stream = FileSystem.File.OpenRead(path); - using MemoryStream destination = new(buffer); - - await stream.CopyToAsync(destination); - - await destination.FlushAsync(); - buffer.Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public async Task CopyToAsync_BufferSizeZero_ShouldThrowArgumentOutOfRangeException( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - await FileSystem.File.WriteAllBytesAsync(path, contents); - - Exception? exception = await Record.ExceptionAsync(async () => - { - await using FileSystemStream stream = FileSystem.File.OpenRead(path); - using MemoryStream destination = new(buffer); - await stream.CopyToAsync(destination, 0); - }); - - exception.Should().BeException( - paramName: "bufferSize"); - } -#endif - - [SkippableTheory] - [AutoData] - public void Extensibility_ShouldWrapFileStreamOnRealFileSystem( - string path) - { - FileSystem.File.WriteAllText(path, null); - using FileSystemStream readStream = FileSystem.File.OpenRead(path); - bool result = readStream.Extensibility - .TryGetWrappedInstance(out System.IO.FileStream? fileStream); - - if (FileSystem is RealFileSystem) - { - result.Should().BeTrue(); - fileStream!.Name.Should().Be(readStream.Name); - } - else - { - result.Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void Flush_ShouldNotChangePosition( - string path, byte[] bytes) - { - using FileSystemStream stream = FileSystem.File.Create(path); - stream.Write(bytes, 0, bytes.Length); - stream.Seek(2, SeekOrigin.Begin); - stream.Position.Should().Be(2); - - stream.Flush(); - - stream.Position.Should().Be(2); - } - - [SkippableTheory] - [AutoData] - public void Flush_ShouldNotUpdateFileContentWhenAlreadyFlushed( - string path, byte[] bytes1, byte[] bytes2) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - using FileSystemStream stream1 = FileSystem.File.Open( - path, - FileMode.OpenOrCreate, - FileAccess.ReadWrite, - FileShare.ReadWrite); - using FileSystemStream stream2 = FileSystem.File.Open( - path, - FileMode.OpenOrCreate, - FileAccess.ReadWrite, - FileShare.ReadWrite); - stream1.Write(bytes1, 0, bytes1.Length); - stream1.Flush(); - stream2.Write(bytes2, 0, bytes2.Length); - stream2.Flush(); - - stream1.Flush(); - - stream2.Dispose(); - stream1.Dispose(); - FileSystem.File.ReadAllBytes(path).Should().BeEquivalentTo(bytes2); - } - - [SkippableTheory] - [AutoData] - public async Task FlushAsync_ShouldNotChangePosition( - string path, byte[] bytes) - { - using FileSystemStream stream = FileSystem.File.Create(path); - stream.Write(bytes, 0, bytes.Length); - stream.Seek(2, SeekOrigin.Begin); - stream.Position.Should().Be(2); - - await stream.FlushAsync(); - - stream.Position.Should().Be(2); - } - - [SkippableTheory] - [AutoData] - public void Name_ShouldReturnFullPath(string path) - { - string expectedName = FileSystem.Path.GetFullPath(path); - using FileSystemStream stream = FileSystem.File.Create(path); - - stream.Name.Should().Be(expectedName); - } - - [SkippableTheory] - [AutoData] - public void Position_ShouldChangeWhenReading( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - stream.Position.Should().Be(0); - stream.ReadByte(); - stream.Position.Should().Be(1); - } - - [SkippableTheory] - [AutoData] - public void Seek_Begin_ShouldSetAbsolutePositionFromBegin( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - stream.Position.Should().Be(0); - stream.Seek(4, SeekOrigin.Begin); - stream.Position.Should().Be(4); - } - - [SkippableTheory] - [AutoData] - public void Seek_Current_ShouldSetRelativePosition(string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - stream.Position.Should().Be(0); - stream.Seek(4, SeekOrigin.Current); - stream.Position.Should().Be(4); - stream.Seek(3, SeekOrigin.Current); - stream.Position.Should().Be(7); - stream.Seek(-1, SeekOrigin.Current); - stream.Position.Should().Be(6); - } - - [SkippableTheory] - [AutoData] - public void Seek_End_ShouldSetAbsolutePositionFromEnd(string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - stream.Position.Should().Be(0); - stream.Seek(-4, SeekOrigin.End); - stream.Position.Should().Be(contents.Length - 4); - } - - [SkippableTheory] - [AutoData] - public void SetLength(string path, int length) - { - using FileSystemStream stream = FileSystem.File.Create(path); - - stream.SetLength(length); - - stream.Length.Should().Be(length); - } - - [SkippableTheory] - [AutoData] - public void SetLength_ReadOnlyStream_ShouldThrowNotSupportedException( - string path, int length) - { - FileSystem.File.WriteAllText(path, null); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - stream.SetLength(length); - }); - - exception.Should().BeException(hResult: -2146233067); - } -} +using System.IO; +using System.Threading.Tasks; + +namespace Testably.Abstractions.Tests.FileSystem.FileStream; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void CanSeek_ShouldReturnTrue( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + stream.CanSeek.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void CanTimeout_ShouldReturnFalse( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + stream.CanTimeout.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Close_CalledMultipleTimes_ShouldNotThrow( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + stream.Close(); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.Close(); + }); + + exception.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void CopyTo_ShouldCopyBytes( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + using MemoryStream destination = new(buffer); + + stream.CopyTo(destination); + + destination.Flush(); + buffer.Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void CopyTo_BufferSizeZero_ShouldThrowArgumentOutOfRangeException( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + using MemoryStream destination = new(buffer); + stream.CopyTo(destination, 0); + }); + + exception.Should().BeException( + paramName: "bufferSize"); + } + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableTheory] + [AutoData] + public async Task CopyToAsync_ShouldCopyBytes( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + await FileSystem.File.WriteAllBytesAsync(path, contents); + await using FileSystemStream stream = FileSystem.File.OpenRead(path); + using MemoryStream destination = new(buffer); + + await stream.CopyToAsync(destination); + + await destination.FlushAsync(); + buffer.Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public async Task CopyToAsync_BufferSizeZero_ShouldThrowArgumentOutOfRangeException( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + await FileSystem.File.WriteAllBytesAsync(path, contents); + + Exception? exception = await Record.ExceptionAsync(async () => + { + await using FileSystemStream stream = FileSystem.File.OpenRead(path); + using MemoryStream destination = new(buffer); + await stream.CopyToAsync(destination, 0); + }); + + exception.Should().BeException( + paramName: "bufferSize"); + } +#endif + + [SkippableTheory] + [AutoData] + public void Extensibility_ShouldWrapFileStreamOnRealFileSystem( + string path) + { + FileSystem.File.WriteAllText(path, null); + using FileSystemStream readStream = FileSystem.File.OpenRead(path); + bool result = readStream.Extensibility + .TryGetWrappedInstance(out System.IO.FileStream? fileStream); + + if (FileSystem is RealFileSystem) + { + result.Should().BeTrue(); + fileStream!.Name.Should().Be(readStream.Name); + } + else + { + result.Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void Flush_ShouldNotChangePosition( + string path, byte[] bytes) + { + using FileSystemStream stream = FileSystem.File.Create(path); + stream.Write(bytes, 0, bytes.Length); + stream.Seek(2, SeekOrigin.Begin); + stream.Position.Should().Be(2); + + stream.Flush(); + + stream.Position.Should().Be(2); + } + + [SkippableTheory] + [AutoData] + public void Flush_ShouldNotUpdateFileContentWhenAlreadyFlushed( + string path, byte[] bytes1, byte[] bytes2) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + using FileSystemStream stream1 = FileSystem.File.Open( + path, + FileMode.OpenOrCreate, + FileAccess.ReadWrite, + FileShare.ReadWrite); + using FileSystemStream stream2 = FileSystem.File.Open( + path, + FileMode.OpenOrCreate, + FileAccess.ReadWrite, + FileShare.ReadWrite); + stream1.Write(bytes1, 0, bytes1.Length); + stream1.Flush(); + stream2.Write(bytes2, 0, bytes2.Length); + stream2.Flush(); + + stream1.Flush(); + + stream2.Dispose(); + stream1.Dispose(); + FileSystem.File.ReadAllBytes(path).Should().BeEquivalentTo(bytes2); + } + + [SkippableTheory] + [AutoData] + public async Task FlushAsync_ShouldNotChangePosition( + string path, byte[] bytes) + { + using FileSystemStream stream = FileSystem.File.Create(path); + stream.Write(bytes, 0, bytes.Length); + stream.Seek(2, SeekOrigin.Begin); + stream.Position.Should().Be(2); + + await stream.FlushAsync(); + + stream.Position.Should().Be(2); + } + + [SkippableTheory] + [AutoData] + public void Name_ShouldReturnFullPath(string path) + { + string expectedName = FileSystem.Path.GetFullPath(path); + using FileSystemStream stream = FileSystem.File.Create(path); + + stream.Name.Should().Be(expectedName); + } + + [SkippableTheory] + [AutoData] + public void Position_ShouldChangeWhenReading( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + stream.Position.Should().Be(0); + stream.ReadByte(); + stream.Position.Should().Be(1); + } + + [SkippableTheory] + [AutoData] + public void Seek_Begin_ShouldSetAbsolutePositionFromBegin( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + stream.Position.Should().Be(0); + stream.Seek(4, SeekOrigin.Begin); + stream.Position.Should().Be(4); + } + + [SkippableTheory] + [AutoData] + public void Seek_Current_ShouldSetRelativePosition(string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + stream.Position.Should().Be(0); + stream.Seek(4, SeekOrigin.Current); + stream.Position.Should().Be(4); + stream.Seek(3, SeekOrigin.Current); + stream.Position.Should().Be(7); + stream.Seek(-1, SeekOrigin.Current); + stream.Position.Should().Be(6); + } + + [SkippableTheory] + [AutoData] + public void Seek_End_ShouldSetAbsolutePositionFromEnd(string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + stream.Position.Should().Be(0); + stream.Seek(-4, SeekOrigin.End); + stream.Position.Should().Be(contents.Length - 4); + } + + [SkippableTheory] + [AutoData] + public void SetLength(string path, int length) + { + using FileSystemStream stream = FileSystem.File.Create(path); + + stream.SetLength(length); + + stream.Length.Should().Be(length); + } + + [SkippableTheory] + [AutoData] + public void SetLength_ReadOnlyStream_ShouldThrowNotSupportedException( + string path, int length) + { + FileSystem.File.WriteAllText(path, null); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + stream.SetLength(length); + }); + + exception.Should().BeException(hResult: -2146233067); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/WriteTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/WriteTests.cs index 01d708332..880c325df 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/WriteTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/WriteTests.cs @@ -1,285 +1,284 @@ -using System.IO; -using System.Threading; -using Testably.Abstractions.FileSystem; -#if FEATURE_FILESYSTEM_ASYNC -using System.Threading.Tasks; -#endif - -namespace Testably.Abstractions.Tests.FileSystem.FileStream; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class WriteTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void BeginWrite_CanWriteFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - FileSystem.File.WriteAllBytes(path, contents); - FileSystemStream stream = FileSystem.FileInfo.New(path).OpenRead(); - - byte[] buffer = new byte[contents.Length]; - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.BeginWrite(buffer, 0, buffer.Length, _ => { }, null); - }); - - stream.Dispose(); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [AutoData] - public void BeginWrite_ShouldCopyContentsToFile( - string path, byte[] contents) - { - ManualResetEventSlim ms = new(); - using FileSystemStream stream = FileSystem.File.Create(path); - stream.Flush(); - - stream.BeginWrite(contents, 0, contents.Length, ar => - { - // ReSharper disable once AccessToDisposedClosure - stream.EndWrite(ar); - ms.Set(); - }, null); - - ms.Wait(1000); - stream.Dispose(); - FileSystem.File.ReadAllBytes(path).Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void EndWrite_Null_ShouldThrowArgumentNullException(string path) - { - using FileSystemStream stream = FileSystem.File.Create(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.EndWrite(null!); - }); - - exception.Should().BeException(hResult: -2147467261); - } - - [SkippableTheory] - [AutoData] - public void EndWrite_ShouldAdjustTimes(string path, byte[] contents) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - ManualResetEventSlim ms = new(); - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.Create(path); - DateTime updateTime = DateTime.MinValue; - - stream.BeginWrite(contents, 0, contents.Length, ar => - { - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - updateTime = TimeSystem.DateTime.UtcNow; - // ReSharper disable once AccessToDisposedClosure - stream.EndWrite(ar); - ms.Set(); - }, null); - - ms.Wait(10000); - stream.Dispose(); - - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - } - - [SkippableTheory] - [AutoData] - public void Write_CanWriteFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.Write(buffer, 0, contents.Length); - }); - - stream.Dispose(); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [AutoData] - public void Write_ShouldFillBuffer(string path, byte[] contents) - { - using FileSystemStream stream = FileSystem.File.Create(path); - - stream.Write(contents, 0, contents.Length); - - stream.Dispose(); - FileSystem.File.ReadAllBytes(path) - .Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void WriteByte_HiddenFile_ShouldNotThrow( - string path, byte[] contents) - { - FileSystem.File.WriteAllBytes(path, contents); - FileSystem.File.SetAttributes(path, FileAttributes.Hidden); - - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.WriteByte(0); - }); - - stream.Dispose(); - - exception.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void WriteByte_ShouldWriteSingleByteAndAdvancePosition( - string path, byte byte1, byte byte2) - { - using FileSystemStream stream = FileSystem.File.Create(path); - - stream.WriteByte(byte1); - stream.WriteByte(byte2); - - stream.Position.Should().Be(2); - stream.Dispose(); - FileSystem.File.ReadAllBytes(path) - .Should().BeEquivalentTo(new[] - { - byte1, byte2 - }); - } - - [SkippableTheory] - [AutoData] - public void WriteTimeout_ShouldThrowInvalidOperationException( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - _ = stream.WriteTimeout; - }); - - exception.Should().BeException( - hResult: -2146233079); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public void Write_AsSpan_ShouldFillBuffer(string path, byte[] contents) - { - using FileSystemStream stream = FileSystem.File.Create(path); - - stream.Write(contents.AsSpan()); - - stream.Dispose(); - FileSystem.File.ReadAllBytes(path) - .Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void Write_AsSpan_CanWriteFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.Write(buffer.AsSpan()); - }); - - stream.Dispose(); - - exception.Should().BeException(hResult: -2146233067); - } -#endif - -#if FEATURE_FILESYSTEM_ASYNC - [SkippableTheory] - [AutoData] - public async Task WriteAsync_ShouldFillBuffer(string path, byte[] contents) - { - CancellationTokenSource cts = new(10000); - await using FileSystemStream stream = FileSystem.File.Create(path); - - #pragma warning disable CA1835 - await stream.WriteAsync(contents, 0, contents.Length, cts.Token); - #pragma warning restore CA1835 - - await stream.DisposeAsync(); - (await FileSystem.File.ReadAllBytesAsync(path, cts.Token)) - .Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public async Task WriteAsync_CanWriteFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - CancellationTokenSource cts = new(10000); - byte[] buffer = new byte[contents.Length]; - await FileSystem.File.WriteAllBytesAsync(path, contents, cts.Token); - await using FileSystemStream stream = FileSystem.File.OpenRead(path); - - Exception? exception = await Record.ExceptionAsync(async () => - { - // ReSharper disable once AccessToDisposedClosure - #pragma warning disable CA1835 - await stream.WriteAsync(buffer, 0, contents.Length, cts.Token); - #pragma warning restore CA1835 - }); - - await stream.DisposeAsync(); - - exception.Should().BeException( - hResult: -2146233067); - } -#endif -} +using System.IO; +using System.Threading; +#if FEATURE_FILESYSTEM_ASYNC +using System.Threading.Tasks; +#endif + +namespace Testably.Abstractions.Tests.FileSystem.FileStream; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class WriteTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void BeginWrite_CanWriteFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + FileSystem.File.WriteAllBytes(path, contents); + FileSystemStream stream = FileSystem.FileInfo.New(path).OpenRead(); + + byte[] buffer = new byte[contents.Length]; + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.BeginWrite(buffer, 0, buffer.Length, _ => { }, null); + }); + + stream.Dispose(); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [AutoData] + public void BeginWrite_ShouldCopyContentsToFile( + string path, byte[] contents) + { + ManualResetEventSlim ms = new(); + using FileSystemStream stream = FileSystem.File.Create(path); + stream.Flush(); + + stream.BeginWrite(contents, 0, contents.Length, ar => + { + // ReSharper disable once AccessToDisposedClosure + stream.EndWrite(ar); + ms.Set(); + }, null); + + ms.Wait(1000); + stream.Dispose(); + FileSystem.File.ReadAllBytes(path).Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void EndWrite_Null_ShouldThrowArgumentNullException(string path) + { + using FileSystemStream stream = FileSystem.File.Create(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.EndWrite(null!); + }); + + exception.Should().BeException(hResult: -2147467261); + } + + [SkippableTheory] + [AutoData] + public void EndWrite_ShouldAdjustTimes(string path, byte[] contents) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + ManualResetEventSlim ms = new(); + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.Create(path); + DateTime updateTime = DateTime.MinValue; + + stream.BeginWrite(contents, 0, contents.Length, ar => + { + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + updateTime = TimeSystem.DateTime.UtcNow; + // ReSharper disable once AccessToDisposedClosure + stream.EndWrite(ar); + ms.Set(); + }, null); + + ms.Wait(10000); + stream.Dispose(); + + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + } + + [SkippableTheory] + [AutoData] + public void Write_CanWriteFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.Write(buffer, 0, contents.Length); + }); + + stream.Dispose(); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [AutoData] + public void Write_ShouldFillBuffer(string path, byte[] contents) + { + using FileSystemStream stream = FileSystem.File.Create(path); + + stream.Write(contents, 0, contents.Length); + + stream.Dispose(); + FileSystem.File.ReadAllBytes(path) + .Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void WriteByte_HiddenFile_ShouldNotThrow( + string path, byte[] contents) + { + FileSystem.File.WriteAllBytes(path, contents); + FileSystem.File.SetAttributes(path, FileAttributes.Hidden); + + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.WriteByte(0); + }); + + stream.Dispose(); + + exception.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void WriteByte_ShouldWriteSingleByteAndAdvancePosition( + string path, byte byte1, byte byte2) + { + using FileSystemStream stream = FileSystem.File.Create(path); + + stream.WriteByte(byte1); + stream.WriteByte(byte2); + + stream.Position.Should().Be(2); + stream.Dispose(); + FileSystem.File.ReadAllBytes(path) + .Should().BeEquivalentTo(new[] + { + byte1, byte2 + }); + } + + [SkippableTheory] + [AutoData] + public void WriteTimeout_ShouldThrowInvalidOperationException( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + _ = stream.WriteTimeout; + }); + + exception.Should().BeException( + hResult: -2146233079); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public void Write_AsSpan_ShouldFillBuffer(string path, byte[] contents) + { + using FileSystemStream stream = FileSystem.File.Create(path); + + stream.Write(contents.AsSpan()); + + stream.Dispose(); + FileSystem.File.ReadAllBytes(path) + .Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void Write_AsSpan_CanWriteFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.Write(buffer.AsSpan()); + }); + + stream.Dispose(); + + exception.Should().BeException(hResult: -2146233067); + } +#endif + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableTheory] + [AutoData] + public async Task WriteAsync_ShouldFillBuffer(string path, byte[] contents) + { + CancellationTokenSource cts = new(10000); + await using FileSystemStream stream = FileSystem.File.Create(path); + + #pragma warning disable CA1835 + await stream.WriteAsync(contents, 0, contents.Length, cts.Token); + #pragma warning restore CA1835 + + await stream.DisposeAsync(); + (await FileSystem.File.ReadAllBytesAsync(path, cts.Token)) + .Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public async Task WriteAsync_CanWriteFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + CancellationTokenSource cts = new(10000); + byte[] buffer = new byte[contents.Length]; + await FileSystem.File.WriteAllBytesAsync(path, contents, cts.Token); + await using FileSystemStream stream = FileSystem.File.OpenRead(path); + + Exception? exception = await Record.ExceptionAsync(async () => + { + // ReSharper disable once AccessToDisposedClosure + #pragma warning disable CA1835 + await stream.WriteAsync(buffer, 0, contents.Length, cts.Token); + #pragma warning restore CA1835 + }); + + await stream.DisposeAsync(); + + exception.Should().BeException( + hResult: -2146233067); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/ExceptionTests.cs index be9679e9f..a7aaa4fc8 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/ExceptionTests.cs @@ -1,146 +1,145 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileStreamFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetFileStreamFactoryCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileStream); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileStreamFactoryCallbacks), parameters: " ")] - public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileStream); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetFileStreamFactoryCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileStream); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileStreamFactoryCallbacks), - parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileStream); - }); - - if (!Test.RunsOnWindows) - { - if (exception is IOException ioException) - { - ioException.HResult.Should().NotBe(-2147024809, - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - else - { - if (Test.IsNetFramework) - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should().BeException( - hResult: -2147024773, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - } - - #region Helpers - - public static IEnumerable GetFileStreamFactoryCallbacks(string? path) - => GetFileStreamFactoryCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetFileStreamFactoryCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory - => fileStreamFactory.New(value, FileMode.Open)); - yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory - => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite)); - yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory - => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite, - FileShare.None)); - yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory - => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite, - FileShare.None, 1024)); - yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory - => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite, - FileShare.None, 1024, FileOptions.None)); -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory - => fileStreamFactory.New(value, new FileStreamOptions())); -#endif - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.FileStreamFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetFileStreamFactoryCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileStream); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileStreamFactoryCallbacks), parameters: " ")] + public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileStream); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetFileStreamFactoryCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileStream); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileStreamFactoryCallbacks), + parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileStream); + }); + + if (!Test.RunsOnWindows) + { + if (exception is IOException ioException) + { + ioException.HResult.Should().NotBe(-2147024809, + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + else + { + if (Test.IsNetFramework) + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should().BeException( + hResult: -2147024773, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + } + + #region Helpers + + public static IEnumerable GetFileStreamFactoryCallbacks(string? path) + => GetFileStreamFactoryCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetFileStreamFactoryCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory + => fileStreamFactory.New(value, FileMode.Open)); + yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory + => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite)); + yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory + => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite, + FileShare.None)); + yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory + => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite, + FileShare.None, 1024)); + yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory + => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite, + FileShare.None, 1024, FileOptions.None)); +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory + => fileStreamFactory.New(value, new FileStreamOptions())); +#endif + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/SafeFileHandleTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/SafeFileHandleTests.cs index 69402e6d9..7d6041bc4 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/SafeFileHandleTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/SafeFileHandleTests.cs @@ -1,157 +1,156 @@ -#if EXECUTE_SAFEFILEHANDLE_TESTS -using Microsoft.Win32.SafeHandles; -using System.IO; -using System.Text; -using Testably.Abstractions.FileSystem; -using Testably.Abstractions.Testing.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileStreamFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class SafeFileHandleTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void New_SafeFileHandle_InvalidHandle_ShouldThrowArgumentException( - string filename) - { - string path = FileSystem.Path.GetFullPath(filename); - SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); - - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(handle, FileAccess.ReadWrite); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: "handle"); - } - - [SkippableTheory] - [AutoData] - public void New_SafeFileHandle_InvalidHandle_WithBufferSize_ShouldThrowArgumentException( - string filename) - { - string path = FileSystem.Path.GetFullPath(filename); - SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); - - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: "handle"); - } - - [SkippableTheory] - [AutoData] - public void - New_SafeFileHandle_InvalidHandle_WithBufferSizeAndAsync_ShouldThrowArgumentException( - string filename) - { - string path = FileSystem.Path.GetFullPath(filename); - SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); - - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024, true); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: "handle"); - } - - [SkippableTheory] - [AutoData] - public void New_SafeFileHandle_Valid_ShouldCreateWritableStream( - string filename, string contents) - { - IDisposable? cleanup = null; - if (FileSystem is not RealFileSystem realFileSystem) - { - realFileSystem = new RealFileSystem(); - cleanup = realFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); - FileSystem.InitializeIn(realFileSystem.Directory.GetCurrentDirectory()); - } - - string path = realFileSystem.Path.GetFullPath(filename); - realFileSystem.File.WriteAllText(path, contents); - FileSystem.File.WriteAllText(path, contents); - SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); - (FileSystem as MockFileSystem)?.WithSafeFileHandleStrategy( - new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); - - FileSystemStream stream = FileSystem.FileStream.New(handle, FileAccess.ReadWrite); - stream.Write(Encoding.UTF8.GetBytes("foo"), 0, 3); - stream.Dispose(); - - FileSystem.File.ReadAllText(path).Should().StartWith("foo"); - cleanup?.Dispose(); - } - - [SkippableTheory] - [AutoData] - public void New_SafeFileHandle_Valid_WithBufferSize_ShouldCreateWritableStream( - string filename, string contents) - { - IDisposable? cleanup = null; - if (FileSystem is not RealFileSystem realFileSystem) - { - realFileSystem = new RealFileSystem(); - cleanup = realFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); - FileSystem.InitializeIn(realFileSystem.Directory.GetCurrentDirectory()); - } - - string path = realFileSystem.Path.GetFullPath(filename); - realFileSystem.File.WriteAllText(path, contents); - FileSystem.File.WriteAllText(path, contents); - SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); - (FileSystem as MockFileSystem)?.WithSafeFileHandleStrategy( - new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); - - FileSystemStream stream = - FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024); - stream.Write(Encoding.UTF8.GetBytes("foo"), 0, 3); - stream.Dispose(); - - FileSystem.File.ReadAllText(path).Should().StartWith("foo"); - cleanup?.Dispose(); - } - - [SkippableTheory] - [AutoData] - public void - New_SafeFileHandle_Valid_WithBufferSizeAndAsync_ShouldCreateWritableStream( - string filename, string contents) - { - IDisposable? cleanup = null; - if (FileSystem is not RealFileSystem realFileSystem) - { - realFileSystem = new RealFileSystem(); - cleanup = realFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); - FileSystem.InitializeIn(realFileSystem.Directory.GetCurrentDirectory()); - } - - string path = realFileSystem.Path.GetFullPath(filename); - realFileSystem.File.WriteAllText(path, contents); - FileSystem.File.WriteAllText(path, contents); - SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); - (FileSystem as MockFileSystem)?.WithSafeFileHandleStrategy( - new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); - - FileSystemStream stream = - FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024, false); - stream.Write(Encoding.UTF8.GetBytes("foo"), 0, 3); - stream.Dispose(); - - FileSystem.File.ReadAllText(path).Should().StartWith("foo"); - cleanup?.Dispose(); - } -} -#endif +#if EXECUTE_SAFEFILEHANDLE_TESTS +using Microsoft.Win32.SafeHandles; +using System.IO; +using System.Text; +using Testably.Abstractions.Testing.FileSystem; + +namespace Testably.Abstractions.Tests.FileSystem.FileStreamFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class SafeFileHandleTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void New_SafeFileHandle_InvalidHandle_ShouldThrowArgumentException( + string filename) + { + string path = FileSystem.Path.GetFullPath(filename); + SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(handle, FileAccess.ReadWrite); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: "handle"); + } + + [SkippableTheory] + [AutoData] + public void New_SafeFileHandle_InvalidHandle_WithBufferSize_ShouldThrowArgumentException( + string filename) + { + string path = FileSystem.Path.GetFullPath(filename); + SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: "handle"); + } + + [SkippableTheory] + [AutoData] + public void + New_SafeFileHandle_InvalidHandle_WithBufferSizeAndAsync_ShouldThrowArgumentException( + string filename) + { + string path = FileSystem.Path.GetFullPath(filename); + SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024, true); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: "handle"); + } + + [SkippableTheory] + [AutoData] + public void New_SafeFileHandle_Valid_ShouldCreateWritableStream( + string filename, string contents) + { + IDisposable? cleanup = null; + if (FileSystem is not RealFileSystem realFileSystem) + { + realFileSystem = new RealFileSystem(); + cleanup = realFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); + FileSystem.InitializeIn(realFileSystem.Directory.GetCurrentDirectory()); + } + + string path = realFileSystem.Path.GetFullPath(filename); + realFileSystem.File.WriteAllText(path, contents); + FileSystem.File.WriteAllText(path, contents); + SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); + (FileSystem as MockFileSystem)?.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); + + FileSystemStream stream = FileSystem.FileStream.New(handle, FileAccess.ReadWrite); + stream.Write(Encoding.UTF8.GetBytes("foo"), 0, 3); + stream.Dispose(); + + FileSystem.File.ReadAllText(path).Should().StartWith("foo"); + cleanup?.Dispose(); + } + + [SkippableTheory] + [AutoData] + public void New_SafeFileHandle_Valid_WithBufferSize_ShouldCreateWritableStream( + string filename, string contents) + { + IDisposable? cleanup = null; + if (FileSystem is not RealFileSystem realFileSystem) + { + realFileSystem = new RealFileSystem(); + cleanup = realFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); + FileSystem.InitializeIn(realFileSystem.Directory.GetCurrentDirectory()); + } + + string path = realFileSystem.Path.GetFullPath(filename); + realFileSystem.File.WriteAllText(path, contents); + FileSystem.File.WriteAllText(path, contents); + SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); + (FileSystem as MockFileSystem)?.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); + + FileSystemStream stream = + FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024); + stream.Write(Encoding.UTF8.GetBytes("foo"), 0, 3); + stream.Dispose(); + + FileSystem.File.ReadAllText(path).Should().StartWith("foo"); + cleanup?.Dispose(); + } + + [SkippableTheory] + [AutoData] + public void + New_SafeFileHandle_Valid_WithBufferSizeAndAsync_ShouldCreateWritableStream( + string filename, string contents) + { + IDisposable? cleanup = null; + if (FileSystem is not RealFileSystem realFileSystem) + { + realFileSystem = new RealFileSystem(); + cleanup = realFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); + FileSystem.InitializeIn(realFileSystem.Directory.GetCurrentDirectory()); + } + + string path = realFileSystem.Path.GetFullPath(filename); + realFileSystem.File.WriteAllText(path, contents); + FileSystem.File.WriteAllText(path, contents); + SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); + (FileSystem as MockFileSystem)?.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); + + FileSystemStream stream = + FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024, false); + stream.Write(Encoding.UTF8.GetBytes("foo"), 0, 3); + stream.Dispose(); + + FileSystem.File.ReadAllText(path).Should().StartWith("foo"); + cleanup?.Dispose(); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/Tests.cs index 98c4d8140..78ed87dd5 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/Tests.cs @@ -1,183 +1,182 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileStreamFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void New_AppendAccessWithReadWriteMode_ShouldThrowArgumentException( - string path) - { - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(path, FileMode.Append, FileAccess.ReadWrite); - }); - - exception.Should().BeException( - messageContains: FileMode.Append.ToString(), - hResult: -2147024809, - paramName: Test.IsNetFramework ? null : "access"); - } - - [SkippableTheory] - [AutoData] - public void New_ExistingFileWithCreateMode_ShouldIgnoreContent( - string path) - { - FileSystem.File.WriteAllText(path, "foo"); - FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Create); - stream.Dispose(); - - FileSystem.File.ReadAllText(path).Should().BeEmpty(); - } - - [SkippableTheory] - [AutoData] - public void New_ExistingFileWithCreateNewMode_ShouldThrowIOException( - string path) - { - FileSystem.File.WriteAllText(path, "foo"); - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(path, FileMode.CreateNew); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: Test.RunsOnWindows ? -2147024816 : 17); - } - - [SkippableTheory] - [AutoData] - public void New_ExistingFileWithTruncateMode_ShouldIgnoreContent( - string path) - { - FileSystem.File.WriteAllText(path, "foo"); - FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Truncate); - stream.Dispose(); - - FileSystem.File.ReadAllText(path).Should().BeEmpty(); - } - - [SkippableTheory] - [InlineAutoData(FileMode.Append)] - [InlineAutoData(FileMode.Truncate)] - [InlineAutoData(FileMode.Create)] - [InlineAutoData(FileMode.CreateNew)] - [InlineAutoData(FileMode.Append)] - public void New_InvalidModeForReadAccess_ShouldThrowArgumentException( - FileMode mode, string path) - { - FileAccess access = FileAccess.Read; - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(path, mode, access); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: Test.IsNetFramework ? null : "access"); - exception!.Message.Should() - .Contain(mode.ToString()).And - .Contain(access.ToString()); - } - - [SkippableTheory] - [InlineAutoData(FileMode.Open)] - [InlineAutoData(FileMode.Truncate)] - public void New_MissingFileWithIncorrectMode_ShouldThrowFileNotFoundException( - FileMode mode, string path) - { - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(path, mode); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - - [SkippableTheory] - [AutoData] - public void New_MissingFileWithTruncateMode_ShouldThrowFileNotFoundException( - string path) - { - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(path, FileMode.Truncate); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - - [SkippableTheory] - [InlineAutoData(FileAccess.Read)] - [InlineAutoData(FileAccess.ReadWrite)] - [InlineAutoData(FileAccess.Write)] - public void - New_ReadOnlyFlag_ShouldThrowUnauthorizedAccessException_WhenAccessContainsWrite( - FileAccess access, - string path) - { - FileSystem.File.WriteAllText(path, "some content"); - FileSystem.File.SetAttributes(path, FileAttributes.ReadOnly); - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(path, FileMode.Open, access); - }); - - if (access.HasFlag(FileAccess.Write)) - { - exception.Should().BeException(hResult: -2147024891); - } - else - { - exception.Should().BeNull(); - } - } - - [SkippableTheory] - [AutoData] - public void New_SamePathAsExistingDirectory_ShouldThrowCorrectException( - string path) - { - FileSystem.Directory.CreateDirectory(path); - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(path, FileMode.CreateNew); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024891); - } - else - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: 17); - } - } - - [SkippableTheory] - [InlineAutoData(false)] - [InlineAutoData(true)] - public void New_WithUseAsyncSet_ShouldSetProperty(bool useAsync, string path) - { - using FileSystemStream stream = FileSystem.FileStream.New( - path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 1, - useAsync); - - stream.IsAsync.Should().Be(useAsync); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileStreamFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void New_AppendAccessWithReadWriteMode_ShouldThrowArgumentException( + string path) + { + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(path, FileMode.Append, FileAccess.ReadWrite); + }); + + exception.Should().BeException( + messageContains: FileMode.Append.ToString(), + hResult: -2147024809, + paramName: Test.IsNetFramework ? null : "access"); + } + + [SkippableTheory] + [AutoData] + public void New_ExistingFileWithCreateMode_ShouldIgnoreContent( + string path) + { + FileSystem.File.WriteAllText(path, "foo"); + FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Create); + stream.Dispose(); + + FileSystem.File.ReadAllText(path).Should().BeEmpty(); + } + + [SkippableTheory] + [AutoData] + public void New_ExistingFileWithCreateNewMode_ShouldThrowIOException( + string path) + { + FileSystem.File.WriteAllText(path, "foo"); + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(path, FileMode.CreateNew); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: Test.RunsOnWindows ? -2147024816 : 17); + } + + [SkippableTheory] + [AutoData] + public void New_ExistingFileWithTruncateMode_ShouldIgnoreContent( + string path) + { + FileSystem.File.WriteAllText(path, "foo"); + FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Truncate); + stream.Dispose(); + + FileSystem.File.ReadAllText(path).Should().BeEmpty(); + } + + [SkippableTheory] + [InlineAutoData(FileMode.Append)] + [InlineAutoData(FileMode.Truncate)] + [InlineAutoData(FileMode.Create)] + [InlineAutoData(FileMode.CreateNew)] + [InlineAutoData(FileMode.Append)] + public void New_InvalidModeForReadAccess_ShouldThrowArgumentException( + FileMode mode, string path) + { + FileAccess access = FileAccess.Read; + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(path, mode, access); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: Test.IsNetFramework ? null : "access"); + exception!.Message.Should() + .Contain(mode.ToString()).And + .Contain(access.ToString()); + } + + [SkippableTheory] + [InlineAutoData(FileMode.Open)] + [InlineAutoData(FileMode.Truncate)] + public void New_MissingFileWithIncorrectMode_ShouldThrowFileNotFoundException( + FileMode mode, string path) + { + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(path, mode); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + + [SkippableTheory] + [AutoData] + public void New_MissingFileWithTruncateMode_ShouldThrowFileNotFoundException( + string path) + { + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(path, FileMode.Truncate); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + + [SkippableTheory] + [InlineAutoData(FileAccess.Read)] + [InlineAutoData(FileAccess.ReadWrite)] + [InlineAutoData(FileAccess.Write)] + public void + New_ReadOnlyFlag_ShouldThrowUnauthorizedAccessException_WhenAccessContainsWrite( + FileAccess access, + string path) + { + FileSystem.File.WriteAllText(path, "some content"); + FileSystem.File.SetAttributes(path, FileAttributes.ReadOnly); + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(path, FileMode.Open, access); + }); + + if (access.HasFlag(FileAccess.Write)) + { + exception.Should().BeException(hResult: -2147024891); + } + else + { + exception.Should().BeNull(); + } + } + + [SkippableTheory] + [AutoData] + public void New_SamePathAsExistingDirectory_ShouldThrowCorrectException( + string path) + { + FileSystem.Directory.CreateDirectory(path); + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(path, FileMode.CreateNew); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024891); + } + else + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: 17); + } + } + + [SkippableTheory] + [InlineAutoData(false)] + [InlineAutoData(true)] + public void New_WithUseAsyncSet_ShouldSetProperty(bool useAsync, string path) + { + using FileSystemStream stream = FileSystem.FileStream.New( + path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 1, + useAsync); + + stream.IsAsync.Should().Be(useAsync); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/CreateAsSymbolicLinkTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/CreateAsSymbolicLinkTests.cs index d8b1ea0cf..70ef3bdc0 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/CreateAsSymbolicLinkTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/CreateAsSymbolicLinkTests.cs @@ -1,45 +1,44 @@ -#if FEATURE_FILESYSTEM_LINK -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class CreateAsSymbolicLinkTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void CreateAsSymbolicLink_ShouldCreateSymbolicLink( - string path, string pathToTarget) - { - FileSystem.File.WriteAllText(pathToTarget, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - fileInfo.CreateAsSymbolicLink(pathToTarget); - - FileSystem.File.GetAttributes(path) - .HasFlag(FileAttributes.ReparsePoint) - .Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void CreateAsSymbolicLink_SourceFileAlreadyExists_ShouldThrowIOException( - string path, string pathToTarget) - { - FileSystem.File.WriteAllText(pathToTarget, null); - FileSystem.File.WriteAllText(path, "foo"); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - fileInfo.CreateAsSymbolicLink(pathToTarget); - }); - - exception.Should().BeException($"'{path}'", - hResult: Test.RunsOnWindows ? -2147024713 : 17); - } -} -#endif +#if FEATURE_FILESYSTEM_LINK +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class CreateAsSymbolicLinkTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void CreateAsSymbolicLink_ShouldCreateSymbolicLink( + string path, string pathToTarget) + { + FileSystem.File.WriteAllText(pathToTarget, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + fileInfo.CreateAsSymbolicLink(pathToTarget); + + FileSystem.File.GetAttributes(path) + .HasFlag(FileAttributes.ReparsePoint) + .Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void CreateAsSymbolicLink_SourceFileAlreadyExists_ShouldThrowIOException( + string path, string pathToTarget) + { + FileSystem.File.WriteAllText(pathToTarget, null); + FileSystem.File.WriteAllText(path, "foo"); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + fileInfo.CreateAsSymbolicLink(pathToTarget); + }); + + exception.Should().BeException($"'{path}'", + hResult: Test.RunsOnWindows ? -2147024713 : 17); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ExceptionTests.cs index ce22b7539..8a6e2a658 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ExceptionTests.cs @@ -1,75 +1,74 @@ -#if FEATURE_FILESYSTEM_LINK -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetFileSystemInfoCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetFileSystemInfoCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - #region Helpers - - public static IEnumerable GetFileSystemInfoCallbacks(string? path) - => GetFileSystemInfoCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetFileSystemInfoCallbackTestParameters(string value) - { -#if FEATURE_FILESYSTEM_LINK - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "pathToTarget", - fileSystemInfo - => fileSystemInfo.CreateAsSymbolicLink(value)); -#endif - } - - #endregion -} -#endif +#if FEATURE_FILESYSTEM_LINK +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetFileSystemInfoCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetFileSystemInfoCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + #region Helpers + + public static IEnumerable GetFileSystemInfoCallbacks(string? path) + => GetFileSystemInfoCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetFileSystemInfoCallbackTestParameters(string value) + { +#if FEATURE_FILESYSTEM_LINK + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "pathToTarget", + fileSystemInfo + => fileSystemInfo.CreateAsSymbolicLink(value)); +#endif + } + + #endregion +} +#endif diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ResolveLinkTargetTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ResolveLinkTargetTests.cs index 15e289114..5da2ed94d 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ResolveLinkTargetTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ResolveLinkTargetTests.cs @@ -1,177 +1,176 @@ -#if FEATURE_FILESYSTEM_LINK -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ResolveLinkTargetTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - #region Test Setup - - /// - /// The maximum number of symbolic links that are followed.
- /// - ///
- private static int MaxResolveLinks => - Test.RunsOnWindows ? 63 : 40; - - #endregion - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FileWithDifferentCase_ShouldReturnPathToMissingFile( - string path, string pathToTarget, string contents) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget, "foo"); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - fileInfo.CreateAsSymbolicLink(pathToTarget); - FileSystem.File.Delete(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget.ToUpper(), contents); - - IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); - - target!.FullName.Should().Be(targetFullPath); - if (!Test.RunsOnLinux) - { - target.Exists.Should().BeTrue(); - FileSystem.File.ReadAllText(target.FullName).Should().Be(contents); - } - else - { - target.Exists.Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FinalTarget_ShouldFollowSymbolicLinkToFinalTarget( - string path, string pathToFinalTarget) - { - int maxLinks = MaxResolveLinks; - - FileSystem.File.WriteAllText(pathToFinalTarget, null); - string previousPath = pathToFinalTarget; - for (int i = 0; i < maxLinks; i++) - { - string newPath = $"{path}-{i}"; - IFileInfo linkFileInfo = FileSystem.FileInfo.New(newPath); - linkFileInfo.CreateAsSymbolicLink(previousPath); - previousPath = newPath; - } - - IFileInfo fileInfo = FileSystem.FileInfo.New(previousPath); - - IFileSystemInfo? target = fileInfo.ResolveLinkTarget(true); - - target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToFinalTarget)); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FinalTargetWithTooManyLevels_ShouldThrowIOException( - string path, string pathToFinalTarget) - { - int maxLinks = MaxResolveLinks + 1; - FileSystem.File.WriteAllText(pathToFinalTarget, null); - string previousPath = pathToFinalTarget; - for (int i = 0; i < maxLinks; i++) - { - string newPath = $"{path}-{i}"; - IFileInfo linkFileInfo = FileSystem.FileInfo.New(newPath); - linkFileInfo.CreateAsSymbolicLink(previousPath); - previousPath = newPath; - } - - IFileInfo fileInfo = FileSystem.FileInfo.New(previousPath); - - Exception? exception = Record.Exception(() => - { - _ = fileInfo.ResolveLinkTarget(true); - }); - - exception.Should().BeException( - hResult: Test.RunsOnWindows ? -2147022975 : -2146232800, - messageContains: $"'{fileInfo.FullName}'"); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_MissingFileInLinkChain_ShouldReturnPathToMissingFile( - string path, string pathToFinalTarget, string pathToMissingFile) - { - FileSystem.File.WriteAllText(pathToFinalTarget, null); - IFileInfo linkFileInfo1 = FileSystem.FileInfo.New(pathToMissingFile); - linkFileInfo1.CreateAsSymbolicLink(pathToFinalTarget); - IFileInfo linkFileInfo2 = FileSystem.FileInfo.New(path); - linkFileInfo2.CreateAsSymbolicLink(pathToMissingFile); - linkFileInfo1.Delete(); - - IFileSystemInfo? target = linkFileInfo2.ResolveLinkTarget(true); - - target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToMissingFile)); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_NormalDirectory_ShouldReturnNull( - string path) - { - FileSystem.Directory.CreateDirectory(path); - IDirectoryInfo fileInfo = FileSystem.DirectoryInfo.New(path); - - IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); - - target.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_NormalFile_ShouldReturnNull( - string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); - - target.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_ShouldFollowSymbolicLink( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - fileInfo.CreateAsSymbolicLink(pathToTarget); - - IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); - - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_TargetDeletedAfterLinkCreation_ShouldReturnNull( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - fileInfo.CreateAsSymbolicLink(pathToTarget); - FileSystem.File.Delete(pathToTarget); - - IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); - - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeFalse(); - } -} -#endif +#if FEATURE_FILESYSTEM_LINK +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ResolveLinkTargetTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + #region Test Setup + + /// + /// The maximum number of symbolic links that are followed.
+ /// + ///
+ private static int MaxResolveLinks => + Test.RunsOnWindows ? 63 : 40; + + #endregion + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FileWithDifferentCase_ShouldReturnPathToMissingFile( + string path, string pathToTarget, string contents) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget, "foo"); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + fileInfo.CreateAsSymbolicLink(pathToTarget); + FileSystem.File.Delete(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget.ToUpper(), contents); + + IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); + + target!.FullName.Should().Be(targetFullPath); + if (!Test.RunsOnLinux) + { + target.Exists.Should().BeTrue(); + FileSystem.File.ReadAllText(target.FullName).Should().Be(contents); + } + else + { + target.Exists.Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FinalTarget_ShouldFollowSymbolicLinkToFinalTarget( + string path, string pathToFinalTarget) + { + int maxLinks = MaxResolveLinks; + + FileSystem.File.WriteAllText(pathToFinalTarget, null); + string previousPath = pathToFinalTarget; + for (int i = 0; i < maxLinks; i++) + { + string newPath = $"{path}-{i}"; + IFileInfo linkFileInfo = FileSystem.FileInfo.New(newPath); + linkFileInfo.CreateAsSymbolicLink(previousPath); + previousPath = newPath; + } + + IFileInfo fileInfo = FileSystem.FileInfo.New(previousPath); + + IFileSystemInfo? target = fileInfo.ResolveLinkTarget(true); + + target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToFinalTarget)); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FinalTargetWithTooManyLevels_ShouldThrowIOException( + string path, string pathToFinalTarget) + { + int maxLinks = MaxResolveLinks + 1; + FileSystem.File.WriteAllText(pathToFinalTarget, null); + string previousPath = pathToFinalTarget; + for (int i = 0; i < maxLinks; i++) + { + string newPath = $"{path}-{i}"; + IFileInfo linkFileInfo = FileSystem.FileInfo.New(newPath); + linkFileInfo.CreateAsSymbolicLink(previousPath); + previousPath = newPath; + } + + IFileInfo fileInfo = FileSystem.FileInfo.New(previousPath); + + Exception? exception = Record.Exception(() => + { + _ = fileInfo.ResolveLinkTarget(true); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147022975 : -2146232800, + messageContains: $"'{fileInfo.FullName}'"); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_MissingFileInLinkChain_ShouldReturnPathToMissingFile( + string path, string pathToFinalTarget, string pathToMissingFile) + { + FileSystem.File.WriteAllText(pathToFinalTarget, null); + IFileInfo linkFileInfo1 = FileSystem.FileInfo.New(pathToMissingFile); + linkFileInfo1.CreateAsSymbolicLink(pathToFinalTarget); + IFileInfo linkFileInfo2 = FileSystem.FileInfo.New(path); + linkFileInfo2.CreateAsSymbolicLink(pathToMissingFile); + linkFileInfo1.Delete(); + + IFileSystemInfo? target = linkFileInfo2.ResolveLinkTarget(true); + + target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToMissingFile)); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_NormalDirectory_ShouldReturnNull( + string path) + { + FileSystem.Directory.CreateDirectory(path); + IDirectoryInfo fileInfo = FileSystem.DirectoryInfo.New(path); + + IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); + + target.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_NormalFile_ShouldReturnNull( + string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); + + target.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_ShouldFollowSymbolicLink( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + fileInfo.CreateAsSymbolicLink(pathToTarget); + + IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); + + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_TargetDeletedAfterLinkCreation_ShouldReturnNull( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + fileInfo.CreateAsSymbolicLink(pathToTarget); + FileSystem.File.Delete(pathToTarget); + + IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); + + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeFalse(); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/Tests.cs index 30e39ae62..b9e108729 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/Tests.cs @@ -1,157 +1,156 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Extensibility_ShouldWrapFileSystemInfoOnRealFileSystem( - string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - bool result = fileInfo.Extensibility - .TryGetWrappedInstance(out System.IO.FileSystemInfo? fileSystemInfo); - - if (FileSystem is RealFileSystem) - { - result.Should().BeTrue(); - fileSystemInfo!.Name.Should().Be(fileInfo.Name); - } - else - { - result.Should().BeFalse(); - } - } - -#if FEATURE_FILESYSTEM_LINK - [SkippableTheory] - [AutoData] - public void LinkTarget_ShouldBeSetByCreateAsSymbolicLink( - string path, string pathToTarget) - { - FileSystem.File.WriteAllText(pathToTarget, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - sut.LinkTarget.Should().BeNull(); - - sut.CreateAsSymbolicLink(pathToTarget); - - sut.LinkTarget.Should().Be(pathToTarget); - } -#endif - - [SkippableTheory] - [AutoData] - public void SetAttributes_Hidden_OnFileStartingWithDot_ShouldBeSet(string path) - { - Skip.IfNot(Test.RunsOnLinux); - - path = $".{path}"; - FileSystem.File.WriteAllText(path, null); - - FileAttributes result1 = FileSystem.File.GetAttributes(path); - FileSystem.File.SetAttributes(path, FileAttributes.Normal); - FileAttributes result2 = FileSystem.File.GetAttributes(path); - - result1.Should().Be(FileAttributes.Hidden); - result2.Should().Be(FileAttributes.Hidden); - } - - [SkippableTheory] - [AutoData] - public void SetAttributes_Hidden_OnNormalFile_ShouldBeIgnored(string path) - { - Skip.IfNot(Test.RunsOnLinux); - - FileSystem.File.WriteAllText(path, null); - - FileAttributes result1 = FileSystem.File.GetAttributes(path); - FileSystem.File.SetAttributes(path, FileAttributes.Hidden); - FileAttributes result2 = FileSystem.File.GetAttributes(path); - - result1.Should().Be(FileAttributes.Normal); - result2.Should().Be(FileAttributes.Normal); - } - - [SkippableTheory] - [InlineAutoData(FileAttributes.Compressed)] - [InlineAutoData(FileAttributes.Device)] - [InlineAutoData(FileAttributes.Encrypted)] - [InlineAutoData(FileAttributes.IntegrityStream)] - [InlineAutoData(FileAttributes.SparseFile)] - [InlineAutoData(FileAttributes.ReparsePoint)] - public void SetAttributes_ShouldBeIgnoredOnAllPlatforms(FileAttributes attributes, - string path) - { - FileSystem.File.WriteAllText(path, null); - FileSystem.File.SetAttributes(path, attributes); - - FileAttributes result = FileSystem.File.GetAttributes(path); - - result.Should().Be(FileAttributes.Normal); - } - - [SkippableTheory] - [InlineAutoData(FileAttributes.Hidden)] - public void SetAttributes_ShouldBeIgnoredOnLinux(FileAttributes attributes, - string path) - { - FileSystem.File.WriteAllText(path, null); - FileSystem.File.SetAttributes(path, attributes); - - FileAttributes result = FileSystem.File.GetAttributes(path); - - if (Test.RunsOnLinux) - { - result.Should().Be(FileAttributes.Normal); - } - else - { - result.Should().Be(attributes); - } - } - - [SkippableTheory] - [InlineAutoData(FileAttributes.ReadOnly)] - public void SetAttributes_ShouldBeSupportedOnAllPlatforms( - FileAttributes attributes, - string path) - { - FileSystem.File.WriteAllText(path, null); - FileSystem.File.SetAttributes(path, attributes); - - FileAttributes result = FileSystem.File.GetAttributes(path); - - result.Should().Be(attributes); - } - - [SkippableTheory] - [InlineAutoData(FileAttributes.Archive)] - [InlineAutoData(FileAttributes.NoScrubData)] - [InlineAutoData(FileAttributes.NotContentIndexed)] - [InlineAutoData(FileAttributes.Offline)] - [InlineAutoData(FileAttributes.System)] - [InlineAutoData(FileAttributes.Temporary)] - public void SetAttributes_ShouldOnlyWork_OnWindows(FileAttributes attributes, - string path) - { - FileSystem.File.WriteAllText(path, null); - FileSystem.File.SetAttributes(path, attributes); - - FileAttributes result = FileSystem.File.GetAttributes(path); - - if (Test.RunsOnWindows) - { - result.Should().Be(attributes); - } - else - { - result.Should().Be(FileAttributes.Normal); - } - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Extensibility_ShouldWrapFileSystemInfoOnRealFileSystem( + string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + bool result = fileInfo.Extensibility + .TryGetWrappedInstance(out System.IO.FileSystemInfo? fileSystemInfo); + + if (FileSystem is RealFileSystem) + { + result.Should().BeTrue(); + fileSystemInfo!.Name.Should().Be(fileInfo.Name); + } + else + { + result.Should().BeFalse(); + } + } + +#if FEATURE_FILESYSTEM_LINK + [SkippableTheory] + [AutoData] + public void LinkTarget_ShouldBeSetByCreateAsSymbolicLink( + string path, string pathToTarget) + { + FileSystem.File.WriteAllText(pathToTarget, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + sut.LinkTarget.Should().BeNull(); + + sut.CreateAsSymbolicLink(pathToTarget); + + sut.LinkTarget.Should().Be(pathToTarget); + } +#endif + + [SkippableTheory] + [AutoData] + public void SetAttributes_Hidden_OnFileStartingWithDot_ShouldBeSet(string path) + { + Skip.IfNot(Test.RunsOnLinux); + + path = $".{path}"; + FileSystem.File.WriteAllText(path, null); + + FileAttributes result1 = FileSystem.File.GetAttributes(path); + FileSystem.File.SetAttributes(path, FileAttributes.Normal); + FileAttributes result2 = FileSystem.File.GetAttributes(path); + + result1.Should().Be(FileAttributes.Hidden); + result2.Should().Be(FileAttributes.Hidden); + } + + [SkippableTheory] + [AutoData] + public void SetAttributes_Hidden_OnNormalFile_ShouldBeIgnored(string path) + { + Skip.IfNot(Test.RunsOnLinux); + + FileSystem.File.WriteAllText(path, null); + + FileAttributes result1 = FileSystem.File.GetAttributes(path); + FileSystem.File.SetAttributes(path, FileAttributes.Hidden); + FileAttributes result2 = FileSystem.File.GetAttributes(path); + + result1.Should().Be(FileAttributes.Normal); + result2.Should().Be(FileAttributes.Normal); + } + + [SkippableTheory] + [InlineAutoData(FileAttributes.Compressed)] + [InlineAutoData(FileAttributes.Device)] + [InlineAutoData(FileAttributes.Encrypted)] + [InlineAutoData(FileAttributes.IntegrityStream)] + [InlineAutoData(FileAttributes.SparseFile)] + [InlineAutoData(FileAttributes.ReparsePoint)] + public void SetAttributes_ShouldBeIgnoredOnAllPlatforms(FileAttributes attributes, + string path) + { + FileSystem.File.WriteAllText(path, null); + FileSystem.File.SetAttributes(path, attributes); + + FileAttributes result = FileSystem.File.GetAttributes(path); + + result.Should().Be(FileAttributes.Normal); + } + + [SkippableTheory] + [InlineAutoData(FileAttributes.Hidden)] + public void SetAttributes_ShouldBeIgnoredOnLinux(FileAttributes attributes, + string path) + { + FileSystem.File.WriteAllText(path, null); + FileSystem.File.SetAttributes(path, attributes); + + FileAttributes result = FileSystem.File.GetAttributes(path); + + if (Test.RunsOnLinux) + { + result.Should().Be(FileAttributes.Normal); + } + else + { + result.Should().Be(attributes); + } + } + + [SkippableTheory] + [InlineAutoData(FileAttributes.ReadOnly)] + public void SetAttributes_ShouldBeSupportedOnAllPlatforms( + FileAttributes attributes, + string path) + { + FileSystem.File.WriteAllText(path, null); + FileSystem.File.SetAttributes(path, attributes); + + FileAttributes result = FileSystem.File.GetAttributes(path); + + result.Should().Be(attributes); + } + + [SkippableTheory] + [InlineAutoData(FileAttributes.Archive)] + [InlineAutoData(FileAttributes.NoScrubData)] + [InlineAutoData(FileAttributes.NotContentIndexed)] + [InlineAutoData(FileAttributes.Offline)] + [InlineAutoData(FileAttributes.System)] + [InlineAutoData(FileAttributes.Temporary)] + public void SetAttributes_ShouldOnlyWork_OnWindows(FileAttributes attributes, + string path) + { + FileSystem.File.WriteAllText(path, null); + FileSystem.File.SetAttributes(path, attributes); + + FileAttributes result = FileSystem.File.GetAttributes(path); + + if (Test.RunsOnWindows) + { + result.Should().Be(attributes); + } + else + { + result.Should().Be(FileAttributes.Normal); + } + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/UnixFileModeTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/UnixFileModeTests.cs index 7e43391a0..af5135cbd 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/UnixFileModeTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/UnixFileModeTests.cs @@ -1,73 +1,72 @@ -#if FEATURE_FILESYSTEM_UNIXFILEMODE -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class UnixFileModeTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void UnixFileMode_MissingFile_ShouldBeInitializedToMinusOne( - string path) - { - UnixFileMode expected = (UnixFileMode)(-1); - IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); - - fileSystemInfo.UnixFileMode.Should().Be(expected); - } - - [SkippableTheory] - [AutoData] - public void UnixFileMode_ShouldBeInitializedToMinusOne( - string path) - { - Skip.IfNot(Test.RunsOnWindows); - - UnixFileMode expected = (UnixFileMode)(-1); - FileSystem.File.WriteAllText(path, "some content"); - IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); - - fileSystemInfo.UnixFileMode.Should().Be(expected); - } - - [SkippableTheory] - [AutoData] - public void UnixFileMode_ShouldBeSettableOnLinux( - string path, UnixFileMode unixFileMode) - { - Skip.If(Test.RunsOnWindows); - - FileSystem.File.WriteAllText(path, "some content"); - IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); - - #pragma warning disable CA1416 - fileSystemInfo.UnixFileMode = unixFileMode; - #pragma warning restore CA1416 - - fileSystemInfo.UnixFileMode.Should().Be(unixFileMode); - } - - [SkippableTheory] - [AutoData] - public void UnixFileMode_SetterShouldThrowPlatformNotSupportedException_OnWindows( - string path, UnixFileMode unixFileMode) - { - Skip.IfNot(Test.RunsOnWindows); - - IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - #pragma warning disable CA1416 - fileSystemInfo.UnixFileMode = unixFileMode; - #pragma warning restore CA1416 - }); - - exception.Should().BeException(hResult: -2146233031); - } -} -#endif +#if FEATURE_FILESYSTEM_UNIXFILEMODE +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class UnixFileModeTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void UnixFileMode_MissingFile_ShouldBeInitializedToMinusOne( + string path) + { + UnixFileMode expected = (UnixFileMode)(-1); + IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); + + fileSystemInfo.UnixFileMode.Should().Be(expected); + } + + [SkippableTheory] + [AutoData] + public void UnixFileMode_ShouldBeInitializedToMinusOne( + string path) + { + Skip.IfNot(Test.RunsOnWindows); + + UnixFileMode expected = (UnixFileMode)(-1); + FileSystem.File.WriteAllText(path, "some content"); + IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); + + fileSystemInfo.UnixFileMode.Should().Be(expected); + } + + [SkippableTheory] + [AutoData] + public void UnixFileMode_ShouldBeSettableOnLinux( + string path, UnixFileMode unixFileMode) + { + Skip.If(Test.RunsOnWindows); + + FileSystem.File.WriteAllText(path, "some content"); + IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); + + #pragma warning disable CA1416 + fileSystemInfo.UnixFileMode = unixFileMode; + #pragma warning restore CA1416 + + fileSystemInfo.UnixFileMode.Should().Be(unixFileMode); + } + + [SkippableTheory] + [AutoData] + public void UnixFileMode_SetterShouldThrowPlatformNotSupportedException_OnWindows( + string path, UnixFileMode unixFileMode) + { + Skip.IfNot(Test.RunsOnWindows); + + IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + #pragma warning disable CA1416 + fileSystemInfo.UnixFileMode = unixFileMode; + #pragma warning restore CA1416 + }); + + exception.Should().BeException(hResult: -2146233031); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/EnableRaisingEventsTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/EnableRaisingEventsTests.cs index 82e815b3c..8cdedc980 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/EnableRaisingEventsTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/EnableRaisingEventsTests.cs @@ -1,51 +1,50 @@ -using System.Threading; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class EnableRaisingEventsTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void EnableRaisingEvents_SetToFalse_ShouldStop(string path1, string path2) - { - FileSystem.Initialize().WithSubdirectory(path1).WithSubdirectory(path2); - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, _) => - { - ms.Set(); - }; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.Delete(path1); - ms.Wait(10000).Should().BeTrue(); - ms.Reset(); - - fileSystemWatcher.EnableRaisingEvents = false; - - FileSystem.Directory.Delete(path2); - ms.Wait(30).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void EnableRaisingEvents_ShouldBeInitializedAsFalse(string path) - { - FileSystem.Initialize().WithSubdirectory(path); - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, _) => - { - ms.Set(); - }; - - FileSystem.Directory.Delete(path); - - ms.Wait(30).Should().BeFalse(); - } -} +using System.Threading; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class EnableRaisingEventsTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void EnableRaisingEvents_SetToFalse_ShouldStop(string path1, string path2) + { + FileSystem.Initialize().WithSubdirectory(path1).WithSubdirectory(path2); + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, _) => + { + ms.Set(); + }; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.Delete(path1); + ms.Wait(10000).Should().BeTrue(); + ms.Reset(); + + fileSystemWatcher.EnableRaisingEvents = false; + + FileSystem.Directory.Delete(path2); + ms.Wait(30).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void EnableRaisingEvents_ShouldBeInitializedAsFalse(string path) + { + FileSystem.Initialize().WithSubdirectory(path); + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, _) => + { + ms.Set(); + }; + + FileSystem.Directory.Delete(path); + + ms.Wait(30).Should().BeFalse(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/FilterTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/FilterTests.cs index 0a6967ece..0b75d5ab0 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/FilterTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/FilterTests.cs @@ -1,112 +1,111 @@ -using System.IO; -using System.Threading; -using Testably.Abstractions.FileSystem; -#if FEATURE_FILESYSTEMWATCHER_ADVANCED -using System.Collections.Generic; -using System.Linq; -#endif - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class FilterTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Filter_Matching_ShouldTriggerNotification(string path) - { - FileSystem.Initialize().WithSubdirectory(path); - ManualResetEventSlim ms = new(); - FileSystemEventArgs? result = null; - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.Filter = path; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.Delete(path); - ms.Wait(10000).Should().BeTrue(); - - result.Should().NotBeNull(); - result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); - result.ChangeType.Should().Be(WatcherChangeTypes.Deleted); - result.Name.Should().Be(FileSystem.Path.GetFileName(path)); - } - - [SkippableTheory] - [AutoData] - public void Filter_NotMatching_ShouldNotTriggerNotification( - string path, string filter) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize().WithSubdirectory(path); - ManualResetEventSlim ms = new(); - FileSystemEventArgs? result = null; - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.Filter = filter; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.Delete(path); - ms.Wait(30).Should().BeFalse(); - - result.Should().BeNull(); - } - -#if FEATURE_FILESYSTEMWATCHER_ADVANCED - [SkippableTheory] - [AutoData] - public void Filters_ShouldMatchAnyOfTheSpecifiedFilters( - string[] filteredPaths, string[] otherPaths) - { - foreach (string path in otherPaths.Concat(filteredPaths)) - { - FileSystem.Directory.CreateDirectory(path); - } - - CountdownEvent ms = new(filteredPaths.Length); - List results = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - results.Add(eventArgs); - ms.Signal(); - }; - foreach (string filter in filteredPaths) - { - fileSystemWatcher.Filters.Add(filter); - } - - fileSystemWatcher.EnableRaisingEvents = true; - foreach (string path in otherPaths.Concat(filteredPaths)) - { - FileSystem.Directory.Delete(path); - } - - ms.Wait(10000).Should().BeTrue(); - - foreach (string path in otherPaths) - { - results.Should() - .NotContain(f => f.FullPath == FileSystem.Path.GetFullPath(path)); - } - - foreach (string path in filteredPaths) - { - results.Should() - .Contain(f => f.FullPath == FileSystem.Path.GetFullPath(path)); - } - } -#endif -} +using System.IO; +using System.Threading; +#if FEATURE_FILESYSTEMWATCHER_ADVANCED +using System.Collections.Generic; +using System.Linq; +#endif + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class FilterTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Filter_Matching_ShouldTriggerNotification(string path) + { + FileSystem.Initialize().WithSubdirectory(path); + ManualResetEventSlim ms = new(); + FileSystemEventArgs? result = null; + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.Filter = path; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.Delete(path); + ms.Wait(10000).Should().BeTrue(); + + result.Should().NotBeNull(); + result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); + result.ChangeType.Should().Be(WatcherChangeTypes.Deleted); + result.Name.Should().Be(FileSystem.Path.GetFileName(path)); + } + + [SkippableTheory] + [AutoData] + public void Filter_NotMatching_ShouldNotTriggerNotification( + string path, string filter) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize().WithSubdirectory(path); + ManualResetEventSlim ms = new(); + FileSystemEventArgs? result = null; + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.Filter = filter; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.Delete(path); + ms.Wait(30).Should().BeFalse(); + + result.Should().BeNull(); + } + +#if FEATURE_FILESYSTEMWATCHER_ADVANCED + [SkippableTheory] + [AutoData] + public void Filters_ShouldMatchAnyOfTheSpecifiedFilters( + string[] filteredPaths, string[] otherPaths) + { + foreach (string path in otherPaths.Concat(filteredPaths)) + { + FileSystem.Directory.CreateDirectory(path); + } + + CountdownEvent ms = new(filteredPaths.Length); + List results = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + results.Add(eventArgs); + ms.Signal(); + }; + foreach (string filter in filteredPaths) + { + fileSystemWatcher.Filters.Add(filter); + } + + fileSystemWatcher.EnableRaisingEvents = true; + foreach (string path in otherPaths.Concat(filteredPaths)) + { + FileSystem.Directory.Delete(path); + } + + ms.Wait(10000).Should().BeTrue(); + + foreach (string path in otherPaths) + { + results.Should() + .NotContain(f => f.FullPath == FileSystem.Path.GetFullPath(path)); + } + + foreach (string path in filteredPaths) + { + results.Should() + .Contain(f => f.FullPath == FileSystem.Path.GetFullPath(path)); + } + } +#endif +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/IncludeSubdirectoriesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/IncludeSubdirectoriesTests.cs index 21b8b39bf..8fc770e2e 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/IncludeSubdirectoriesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/IncludeSubdirectoriesTests.cs @@ -1,95 +1,94 @@ -using System.IO; -using System.Threading; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class IncludeSubdirectoriesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void IncludeSubdirectories_SetToFalse_ShouldNotTriggerNotification( - string baseDirectory, string path) - { - FileSystem.Initialize() - .WithSubdirectory(baseDirectory).Initialized(s => s - .WithSubdirectory(path)); - ManualResetEventSlim ms = new(); - FileSystemEventArgs? result = null; - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.IncludeSubdirectories = false; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.Delete(FileSystem.Path.Combine(baseDirectory, path)); - ms.Wait(30).Should().BeFalse(); - - result.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void - IncludeSubdirectories_SetToTrue_ShouldOnlyTriggerNotificationOnSubdirectories( - string baseDirectory, string subdirectoryName, string otherDirectory) - { - FileSystem.Initialize() - .WithSubdirectory(baseDirectory).Initialized(s => s - .WithSubdirectory(subdirectoryName)) - .WithSubdirectory(otherDirectory); - ManualResetEventSlim ms = new(); - FileSystemEventArgs? result = null; - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(baseDirectory); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.IncludeSubdirectories = true; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.Delete(otherDirectory); - ms.Wait(30).Should().BeFalse(); - - result.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void IncludeSubdirectories_SetToTrue_ShouldTriggerNotificationOnSubdirectories( - string baseDirectory, string subdirectoryName) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem, !Test.RunsOnWindows); - - FileSystem.Initialize() - .WithSubdirectory(baseDirectory).Initialized(s => s - .WithSubdirectory(subdirectoryName)); - string subdirectoryPath = - FileSystem.Path.Combine(baseDirectory, subdirectoryName); - ManualResetEventSlim ms = new(); - FileSystemEventArgs? result = null; - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.IncludeSubdirectories = true; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.Delete(subdirectoryPath); - ms.Wait(10000).Should().BeTrue(); - - result.Should().NotBeNull(); - result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(subdirectoryPath)); - result.Name.Should().Be(subdirectoryPath); - result!.ChangeType.Should().Be(WatcherChangeTypes.Deleted); - } -} +using System.IO; +using System.Threading; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class IncludeSubdirectoriesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void IncludeSubdirectories_SetToFalse_ShouldNotTriggerNotification( + string baseDirectory, string path) + { + FileSystem.Initialize() + .WithSubdirectory(baseDirectory).Initialized(s => s + .WithSubdirectory(path)); + ManualResetEventSlim ms = new(); + FileSystemEventArgs? result = null; + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.IncludeSubdirectories = false; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.Delete(FileSystem.Path.Combine(baseDirectory, path)); + ms.Wait(30).Should().BeFalse(); + + result.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void + IncludeSubdirectories_SetToTrue_ShouldOnlyTriggerNotificationOnSubdirectories( + string baseDirectory, string subdirectoryName, string otherDirectory) + { + FileSystem.Initialize() + .WithSubdirectory(baseDirectory).Initialized(s => s + .WithSubdirectory(subdirectoryName)) + .WithSubdirectory(otherDirectory); + ManualResetEventSlim ms = new(); + FileSystemEventArgs? result = null; + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(baseDirectory); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.IncludeSubdirectories = true; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.Delete(otherDirectory); + ms.Wait(30).Should().BeFalse(); + + result.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void IncludeSubdirectories_SetToTrue_ShouldTriggerNotificationOnSubdirectories( + string baseDirectory, string subdirectoryName) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem, !Test.RunsOnWindows); + + FileSystem.Initialize() + .WithSubdirectory(baseDirectory).Initialized(s => s + .WithSubdirectory(subdirectoryName)); + string subdirectoryPath = + FileSystem.Path.Combine(baseDirectory, subdirectoryName); + ManualResetEventSlim ms = new(); + FileSystemEventArgs? result = null; + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.IncludeSubdirectories = true; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.Delete(subdirectoryPath); + ms.Wait(10000).Should().BeTrue(); + + result.Should().NotBeNull(); + result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(subdirectoryPath)); + result.Name.Should().Be(subdirectoryPath); + result!.ChangeType.Should().Be(WatcherChangeTypes.Deleted); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/NotifyFiltersTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/NotifyFiltersTests.cs index 272bfe2de..5ab06a203 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/NotifyFiltersTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/NotifyFiltersTests.cs @@ -1,534 +1,533 @@ -using System.IO; -using System.Threading; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class NotifyFiltersTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - #region Test Setup - - /// - /// The delay in milliseconds when expecting a success in the test. - /// - private const int ExpectSuccess = 3000; - - /// - /// The delay in milliseconds when expecting a timeout in the test. - /// - private const int ExpectTimeout = 500; - - #endregion - - [SkippableTheory] - [AutoData] - public void NotifyFilter_AppendFile_ShouldNotNotifyOnOtherFilters(string fileName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize(); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Changed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = NotifyFilters.DirectoryName | - NotifyFilters.FileName; - if (!Test.RunsOnMac) - { - fileSystemWatcher.NotifyFilter |= NotifyFilters.CreationTime; - } - - if (!Test.RunsOnLinux) - { - fileSystemWatcher.NotifyFilter |= NotifyFilters.Security; - } - else - { - fileSystemWatcher.NotifyFilter |= NotifyFilters.Attributes; - } - - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.AppendAllText(fileName, "foo"); - - ms.Wait(ExpectTimeout).Should().BeFalse(); - result.Should().BeNull(); - } - - [SkippableTheory] - [InlineAutoData(NotifyFilters.CreationTime)] - [InlineAutoData(NotifyFilters.LastAccess)] - [InlineAutoData(NotifyFilters.LastWrite)] - [InlineAutoData(NotifyFilters.Security)] - [InlineAutoData(NotifyFilters.Size)] - public void NotifyFilter_AppendFile_ShouldTriggerChangedEventOnNotifyFilters( - NotifyFilters notifyFilter, string fileName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - if (!Test.RunsOnLinux) - { - Skip.If(notifyFilter == NotifyFilters.Security, - "`Security` is only set on Linux"); - } - - if (!Test.RunsOnMac) - { - Skip.If(notifyFilter == NotifyFilters.CreationTime, - "`CreationTime` is only set on MAC"); - } - - if (Test.RunsOnWindows) - { - Skip.If(notifyFilter == NotifyFilters.LastAccess, - "`LastAccess` is not consistently set on the real file system."); - } - - FileSystem.Initialize(); - FileSystem.File.WriteAllText(fileName, null); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Changed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = notifyFilter; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.AppendAllText(fileName, "foo"); - - ms.Wait(ExpectSuccess).Should().BeTrue(); - result.Should().NotBeNull(); - result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(fileName)); - result.ChangeType.Should().Be(WatcherChangeTypes.Changed); - result.Name.Should().Be(FileSystem.Path.GetFileName(fileName)); - } - - [SkippableTheory] - [AutoData] - public void NotifyFilter_CreateDirectory_ShouldNotNotifyOnOtherFilters(string path) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize(); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Created += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | - NotifyFilters.CreationTime | - NotifyFilters.FileName | - NotifyFilters.LastAccess | - NotifyFilters.LastWrite | - NotifyFilters.Security | - NotifyFilters.Size; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.Directory.CreateDirectory(path); - - ms.Wait(ExpectTimeout).Should().BeFalse(); - result.Should().BeNull(); - } - - [SkippableTheory] - [InlineAutoData(NotifyFilters.DirectoryName)] - public void NotifyFilter_CreateDirectory_ShouldTriggerCreatedEventOnNotifyFilters( - NotifyFilters notifyFilter, string path) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize(); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Created += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = notifyFilter; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.Directory.CreateDirectory(path); - - ms.Wait(ExpectSuccess).Should().BeTrue(); - result.Should().NotBeNull(); - result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); - result.ChangeType.Should().Be(WatcherChangeTypes.Created); - result.Name.Should().Be(FileSystem.Path.GetFileName(path)); - } - - [SkippableTheory] - [AutoData] - public void NotifyFilter_DeleteDirectory_ShouldNotNotifyOnOtherFilters(string path) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize().WithSubdirectory(path); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | - NotifyFilters.CreationTime | - NotifyFilters.FileName | - NotifyFilters.LastAccess | - NotifyFilters.LastWrite | - NotifyFilters.Security | - NotifyFilters.Size; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.Directory.Delete(path); - - ms.Wait(ExpectTimeout).Should().BeFalse(); - result.Should().BeNull(); - } - - [SkippableTheory] - [InlineAutoData(NotifyFilters.DirectoryName)] - public void NotifyFilter_DeleteDirectory_ShouldTriggerDeletedEventOnNotifyFilters( - NotifyFilters notifyFilter, string path) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize().WithSubdirectory(path); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = notifyFilter; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.Directory.Delete(path); - - ms.Wait(ExpectSuccess).Should().BeTrue(); - result.Should().NotBeNull(); - result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); - result.ChangeType.Should().Be(WatcherChangeTypes.Deleted); - result.Name.Should().Be(FileSystem.Path.GetFileName(path)); - } - - [SkippableTheory] - [AutoData] - public void NotifyFilter_DeleteFile_ShouldNotNotifyOnOtherFilters(string path) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize().WithFile(path); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | - NotifyFilters.CreationTime | - NotifyFilters.DirectoryName | - NotifyFilters.LastAccess | - NotifyFilters.LastWrite | - NotifyFilters.Security | - NotifyFilters.Size; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.Delete(path); - - ms.Wait(ExpectTimeout).Should().BeFalse(); - result.Should().BeNull(); - } - - [SkippableTheory] - [InlineAutoData(NotifyFilters.FileName)] - public void NotifyFilter_DeleteFile_ShouldTriggerDeletedEventOnNotifyFilters( - NotifyFilters notifyFilter, string path) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize().WithFile(path); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = notifyFilter; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.Delete(path); - - ms.Wait(ExpectSuccess).Should().BeTrue(); - result.Should().NotBeNull(); - result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); - result.ChangeType.Should().Be(WatcherChangeTypes.Deleted); - result.Name.Should().Be(FileSystem.Path.GetFileName(path)); - } - - [SkippableTheory] - [AutoData] - public void - NotifyFilter_MoveFile_DifferentDirectories_ShouldNotifyOnLinuxOrMac( - string sourcePath, string sourceName, - string destinationPath, string destinationName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - Skip.If(Test.RunsOnWindows); - - FileSystem.Initialize() - .WithSubdirectory(sourcePath).Initialized(s => s - .WithFile(sourceName)) - .WithSubdirectory(destinationPath); - RenamedEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Renamed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - - fileSystemWatcher.IncludeSubdirectories = true; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.Move( - FileSystem.Path.Combine(sourcePath, sourceName), - FileSystem.Path.Combine(destinationPath, destinationName)); - - ms.Wait(ExpectSuccess).Should().BeTrue(); - result.Should().NotBeNull(); - result!.ChangeType.Should().Be(WatcherChangeTypes.Renamed); - result.FullPath.Should() - .Be(FileSystem.Path.Combine(BasePath, destinationPath, destinationName)); - result.Name.Should() - .Be(FileSystem.Path.Combine(destinationPath, destinationName)); - result.OldFullPath.Should() - .Be(FileSystem.Path.Combine(BasePath, sourcePath, sourceName)); - result.OldName.Should().Be(FileSystem.Path.Combine(sourcePath, sourceName)); - } - - [SkippableTheory] - [AutoData] - public void - NotifyFilter_MoveFile_DifferentDirectories_ShouldNotNotify_OnWindows( - string sourcePath, string sourceName, - string destinationPath, string destinationName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - Skip.IfNot(Test.RunsOnWindows); - - FileSystem.Initialize() - .WithSubdirectory(sourcePath).Initialized(s => s - .WithFile(sourceName)) - .WithSubdirectory(destinationPath); - RenamedEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Renamed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - - fileSystemWatcher.IncludeSubdirectories = true; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.Move( - FileSystem.Path.Combine(sourcePath, sourceName), - FileSystem.Path.Combine(destinationPath, destinationName)); - - ms.Wait(ExpectTimeout).Should().BeFalse(); - result.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void NotifyFilter_MoveFile_ShouldNotNotifyOnOtherFilters( - string sourceName, string destinationName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize(); - FileSystem.File.WriteAllText(sourceName, null); - RenamedEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Renamed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | - NotifyFilters.CreationTime | - NotifyFilters.DirectoryName | - NotifyFilters.LastAccess | - NotifyFilters.LastWrite | - NotifyFilters.Security | - NotifyFilters.Size; - - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.Move(sourceName, destinationName); - - ms.Wait(ExpectTimeout).Should().BeFalse(); - result.Should().BeNull(); - } - - [SkippableTheory] - [InlineAutoData(NotifyFilters.FileName)] - public void NotifyFilter_MoveFile_ShouldTriggerChangedEventOnNotifyFilters( - NotifyFilters notifyFilter, string sourceName, string destinationName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize(); - FileSystem.File.WriteAllText(sourceName, "foo"); - RenamedEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Renamed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - - fileSystemWatcher.NotifyFilter = notifyFilter; - fileSystemWatcher.IncludeSubdirectories = true; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.Move(sourceName, destinationName); - - ms.Wait(ExpectSuccess).Should().BeTrue(); - result.Should().NotBeNull(); - result!.ChangeType.Should().Be(WatcherChangeTypes.Renamed); - result.FullPath.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - result.Name.Should().Be(FileSystem.Path.GetFileName(destinationName)); - result.OldFullPath.Should().Be(FileSystem.Path.GetFullPath(sourceName)); - result.OldName.Should().Be(FileSystem.Path.GetFileName(sourceName)); - } - - [SkippableTheory] - [AutoData] - public void NotifyFilter_WriteFile_ShouldNotNotifyOnOtherFilters(string fileName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize(); - FileSystem.File.WriteAllText(fileName, null); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Changed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = NotifyFilters.DirectoryName | - NotifyFilters.FileName; - if (!Test.RunsOnMac) - { - fileSystemWatcher.NotifyFilter |= NotifyFilters.CreationTime; - } - - if (!Test.RunsOnLinux) - { - fileSystemWatcher.NotifyFilter |= NotifyFilters.Security; - } - else - { - fileSystemWatcher.NotifyFilter |= NotifyFilters.Attributes; - } - - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.WriteAllText(fileName, "foo"); - - ms.Wait(ExpectTimeout).Should().BeFalse(); - result.Should().BeNull(); - } - - [SkippableTheory] - [InlineAutoData(NotifyFilters.CreationTime)] - [InlineAutoData(NotifyFilters.LastAccess)] - [InlineAutoData(NotifyFilters.LastWrite)] - [InlineAutoData(NotifyFilters.Security)] - [InlineAutoData(NotifyFilters.Size)] - public void NotifyFilter_WriteFile_ShouldTriggerChangedEventOnNotifyFilters( - NotifyFilters notifyFilter, string fileName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - if (!Test.RunsOnLinux) - { - Skip.If(notifyFilter == NotifyFilters.Security, - "`Security` is only set on Linux"); - } - - if (!Test.RunsOnMac) - { - Skip.If(notifyFilter == NotifyFilters.CreationTime, - "`CreationTime` is only set on MAC"); - } - - if (Test.RunsOnWindows) - { - Skip.If(notifyFilter == NotifyFilters.LastAccess, - "`LastAccess` is not consistently set on the real file system."); - } - - FileSystem.Initialize(); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Changed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = notifyFilter; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.WriteAllText(fileName, "foo"); - - ms.Wait(ExpectSuccess).Should().BeTrue(); - result.Should().NotBeNull(); - result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(fileName)); - result.ChangeType.Should().Be(WatcherChangeTypes.Changed); - result.Name.Should().Be(FileSystem.Path.GetFileName(fileName)); - } -} +using System.IO; +using System.Threading; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class NotifyFiltersTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + #region Test Setup + + /// + /// The delay in milliseconds when expecting a success in the test. + /// + private const int ExpectSuccess = 3000; + + /// + /// The delay in milliseconds when expecting a timeout in the test. + /// + private const int ExpectTimeout = 500; + + #endregion + + [SkippableTheory] + [AutoData] + public void NotifyFilter_AppendFile_ShouldNotNotifyOnOtherFilters(string fileName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize(); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Changed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = NotifyFilters.DirectoryName | + NotifyFilters.FileName; + if (!Test.RunsOnMac) + { + fileSystemWatcher.NotifyFilter |= NotifyFilters.CreationTime; + } + + if (!Test.RunsOnLinux) + { + fileSystemWatcher.NotifyFilter |= NotifyFilters.Security; + } + else + { + fileSystemWatcher.NotifyFilter |= NotifyFilters.Attributes; + } + + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.AppendAllText(fileName, "foo"); + + ms.Wait(ExpectTimeout).Should().BeFalse(); + result.Should().BeNull(); + } + + [SkippableTheory] + [InlineAutoData(NotifyFilters.CreationTime)] + [InlineAutoData(NotifyFilters.LastAccess)] + [InlineAutoData(NotifyFilters.LastWrite)] + [InlineAutoData(NotifyFilters.Security)] + [InlineAutoData(NotifyFilters.Size)] + public void NotifyFilter_AppendFile_ShouldTriggerChangedEventOnNotifyFilters( + NotifyFilters notifyFilter, string fileName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + if (!Test.RunsOnLinux) + { + Skip.If(notifyFilter == NotifyFilters.Security, + "`Security` is only set on Linux"); + } + + if (!Test.RunsOnMac) + { + Skip.If(notifyFilter == NotifyFilters.CreationTime, + "`CreationTime` is only set on MAC"); + } + + if (Test.RunsOnWindows) + { + Skip.If(notifyFilter == NotifyFilters.LastAccess, + "`LastAccess` is not consistently set on the real file system."); + } + + FileSystem.Initialize(); + FileSystem.File.WriteAllText(fileName, null); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Changed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.AppendAllText(fileName, "foo"); + + ms.Wait(ExpectSuccess).Should().BeTrue(); + result.Should().NotBeNull(); + result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(fileName)); + result.ChangeType.Should().Be(WatcherChangeTypes.Changed); + result.Name.Should().Be(FileSystem.Path.GetFileName(fileName)); + } + + [SkippableTheory] + [AutoData] + public void NotifyFilter_CreateDirectory_ShouldNotNotifyOnOtherFilters(string path) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize(); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Created += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | + NotifyFilters.CreationTime | + NotifyFilters.FileName | + NotifyFilters.LastAccess | + NotifyFilters.LastWrite | + NotifyFilters.Security | + NotifyFilters.Size; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.Directory.CreateDirectory(path); + + ms.Wait(ExpectTimeout).Should().BeFalse(); + result.Should().BeNull(); + } + + [SkippableTheory] + [InlineAutoData(NotifyFilters.DirectoryName)] + public void NotifyFilter_CreateDirectory_ShouldTriggerCreatedEventOnNotifyFilters( + NotifyFilters notifyFilter, string path) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize(); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Created += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.Directory.CreateDirectory(path); + + ms.Wait(ExpectSuccess).Should().BeTrue(); + result.Should().NotBeNull(); + result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); + result.ChangeType.Should().Be(WatcherChangeTypes.Created); + result.Name.Should().Be(FileSystem.Path.GetFileName(path)); + } + + [SkippableTheory] + [AutoData] + public void NotifyFilter_DeleteDirectory_ShouldNotNotifyOnOtherFilters(string path) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize().WithSubdirectory(path); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | + NotifyFilters.CreationTime | + NotifyFilters.FileName | + NotifyFilters.LastAccess | + NotifyFilters.LastWrite | + NotifyFilters.Security | + NotifyFilters.Size; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.Directory.Delete(path); + + ms.Wait(ExpectTimeout).Should().BeFalse(); + result.Should().BeNull(); + } + + [SkippableTheory] + [InlineAutoData(NotifyFilters.DirectoryName)] + public void NotifyFilter_DeleteDirectory_ShouldTriggerDeletedEventOnNotifyFilters( + NotifyFilters notifyFilter, string path) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize().WithSubdirectory(path); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.Directory.Delete(path); + + ms.Wait(ExpectSuccess).Should().BeTrue(); + result.Should().NotBeNull(); + result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); + result.ChangeType.Should().Be(WatcherChangeTypes.Deleted); + result.Name.Should().Be(FileSystem.Path.GetFileName(path)); + } + + [SkippableTheory] + [AutoData] + public void NotifyFilter_DeleteFile_ShouldNotNotifyOnOtherFilters(string path) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize().WithFile(path); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | + NotifyFilters.CreationTime | + NotifyFilters.DirectoryName | + NotifyFilters.LastAccess | + NotifyFilters.LastWrite | + NotifyFilters.Security | + NotifyFilters.Size; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.Delete(path); + + ms.Wait(ExpectTimeout).Should().BeFalse(); + result.Should().BeNull(); + } + + [SkippableTheory] + [InlineAutoData(NotifyFilters.FileName)] + public void NotifyFilter_DeleteFile_ShouldTriggerDeletedEventOnNotifyFilters( + NotifyFilters notifyFilter, string path) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize().WithFile(path); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.Delete(path); + + ms.Wait(ExpectSuccess).Should().BeTrue(); + result.Should().NotBeNull(); + result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); + result.ChangeType.Should().Be(WatcherChangeTypes.Deleted); + result.Name.Should().Be(FileSystem.Path.GetFileName(path)); + } + + [SkippableTheory] + [AutoData] + public void + NotifyFilter_MoveFile_DifferentDirectories_ShouldNotifyOnLinuxOrMac( + string sourcePath, string sourceName, + string destinationPath, string destinationName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + Skip.If(Test.RunsOnWindows); + + FileSystem.Initialize() + .WithSubdirectory(sourcePath).Initialized(s => s + .WithFile(sourceName)) + .WithSubdirectory(destinationPath); + RenamedEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Renamed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + + fileSystemWatcher.IncludeSubdirectories = true; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.Move( + FileSystem.Path.Combine(sourcePath, sourceName), + FileSystem.Path.Combine(destinationPath, destinationName)); + + ms.Wait(ExpectSuccess).Should().BeTrue(); + result.Should().NotBeNull(); + result!.ChangeType.Should().Be(WatcherChangeTypes.Renamed); + result.FullPath.Should() + .Be(FileSystem.Path.Combine(BasePath, destinationPath, destinationName)); + result.Name.Should() + .Be(FileSystem.Path.Combine(destinationPath, destinationName)); + result.OldFullPath.Should() + .Be(FileSystem.Path.Combine(BasePath, sourcePath, sourceName)); + result.OldName.Should().Be(FileSystem.Path.Combine(sourcePath, sourceName)); + } + + [SkippableTheory] + [AutoData] + public void + NotifyFilter_MoveFile_DifferentDirectories_ShouldNotNotify_OnWindows( + string sourcePath, string sourceName, + string destinationPath, string destinationName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + Skip.IfNot(Test.RunsOnWindows); + + FileSystem.Initialize() + .WithSubdirectory(sourcePath).Initialized(s => s + .WithFile(sourceName)) + .WithSubdirectory(destinationPath); + RenamedEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Renamed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + + fileSystemWatcher.IncludeSubdirectories = true; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.Move( + FileSystem.Path.Combine(sourcePath, sourceName), + FileSystem.Path.Combine(destinationPath, destinationName)); + + ms.Wait(ExpectTimeout).Should().BeFalse(); + result.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void NotifyFilter_MoveFile_ShouldNotNotifyOnOtherFilters( + string sourceName, string destinationName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize(); + FileSystem.File.WriteAllText(sourceName, null); + RenamedEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Renamed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | + NotifyFilters.CreationTime | + NotifyFilters.DirectoryName | + NotifyFilters.LastAccess | + NotifyFilters.LastWrite | + NotifyFilters.Security | + NotifyFilters.Size; + + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.Move(sourceName, destinationName); + + ms.Wait(ExpectTimeout).Should().BeFalse(); + result.Should().BeNull(); + } + + [SkippableTheory] + [InlineAutoData(NotifyFilters.FileName)] + public void NotifyFilter_MoveFile_ShouldTriggerChangedEventOnNotifyFilters( + NotifyFilters notifyFilter, string sourceName, string destinationName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize(); + FileSystem.File.WriteAllText(sourceName, "foo"); + RenamedEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Renamed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.IncludeSubdirectories = true; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.Move(sourceName, destinationName); + + ms.Wait(ExpectSuccess).Should().BeTrue(); + result.Should().NotBeNull(); + result!.ChangeType.Should().Be(WatcherChangeTypes.Renamed); + result.FullPath.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + result.Name.Should().Be(FileSystem.Path.GetFileName(destinationName)); + result.OldFullPath.Should().Be(FileSystem.Path.GetFullPath(sourceName)); + result.OldName.Should().Be(FileSystem.Path.GetFileName(sourceName)); + } + + [SkippableTheory] + [AutoData] + public void NotifyFilter_WriteFile_ShouldNotNotifyOnOtherFilters(string fileName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize(); + FileSystem.File.WriteAllText(fileName, null); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Changed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = NotifyFilters.DirectoryName | + NotifyFilters.FileName; + if (!Test.RunsOnMac) + { + fileSystemWatcher.NotifyFilter |= NotifyFilters.CreationTime; + } + + if (!Test.RunsOnLinux) + { + fileSystemWatcher.NotifyFilter |= NotifyFilters.Security; + } + else + { + fileSystemWatcher.NotifyFilter |= NotifyFilters.Attributes; + } + + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.WriteAllText(fileName, "foo"); + + ms.Wait(ExpectTimeout).Should().BeFalse(); + result.Should().BeNull(); + } + + [SkippableTheory] + [InlineAutoData(NotifyFilters.CreationTime)] + [InlineAutoData(NotifyFilters.LastAccess)] + [InlineAutoData(NotifyFilters.LastWrite)] + [InlineAutoData(NotifyFilters.Security)] + [InlineAutoData(NotifyFilters.Size)] + public void NotifyFilter_WriteFile_ShouldTriggerChangedEventOnNotifyFilters( + NotifyFilters notifyFilter, string fileName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + if (!Test.RunsOnLinux) + { + Skip.If(notifyFilter == NotifyFilters.Security, + "`Security` is only set on Linux"); + } + + if (!Test.RunsOnMac) + { + Skip.If(notifyFilter == NotifyFilters.CreationTime, + "`CreationTime` is only set on MAC"); + } + + if (Test.RunsOnWindows) + { + Skip.If(notifyFilter == NotifyFilters.LastAccess, + "`LastAccess` is not consistently set on the real file system."); + } + + FileSystem.Initialize(); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Changed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.WriteAllText(fileName, "foo"); + + ms.Wait(ExpectSuccess).Should().BeTrue(); + result.Should().NotBeNull(); + result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(fileName)); + result.ChangeType.Should().Be(WatcherChangeTypes.Changed); + result.Name.Should().Be(FileSystem.Path.GetFileName(fileName)); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs index c1a685e5b..cb51e5da4 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs @@ -1,166 +1,165 @@ -using Moq; -using System.ComponentModel; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void BeginInit_ShouldStopListening(string path) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - FileSystem.Initialize(); - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.EnableRaisingEvents = true; - - fileSystemWatcher.BeginInit(); - - fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); - try - { - Task.Run(() => - { - while (!ms.IsSet) - { - Thread.Sleep(10); - FileSystem.Directory.CreateDirectory(path); - FileSystem.Directory.Delete(path); - } - }); - IFileSystemWatcher.IWaitForChangedResult result = - fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created, 1000); - - fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); - result.TimedOut.Should().BeTrue(); - result.ChangeType.Should().Be(0); - result.Name.Should().BeNull(); - result.OldName.Should().BeNull(); - } - finally - { - ms.Set(); - } - } - - [SkippableFact] - public void Container_ShouldBeInitializedWithNull() - { - FileSystem.Initialize(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - - fileSystemWatcher.Container.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void EndInit_ShouldRestartListening(string path) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - FileSystem.Initialize(); - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.EnableRaisingEvents = true; - fileSystemWatcher.BeginInit(); - - fileSystemWatcher.EndInit(); - - fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); - try - { - Task.Run(() => - { - while (!ms.IsSet) - { - Thread.Sleep(10); - FileSystem.Directory.CreateDirectory(path); - FileSystem.Directory.Delete(path); - } - }); - IFileSystemWatcher.IWaitForChangedResult result = - fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created, 100); - - fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); - result.TimedOut.Should().BeFalse(); - } - finally - { - ms.Set(); - } - } - - [SkippableTheory] - [InlineData(-1, 4096)] - [InlineData(4095, 4096)] - [InlineData(4097, 4097)] - public void InternalBufferSize_ShouldAtLeastHave4096Bytes( - int bytes, int expectedBytes) - { - FileSystem.Initialize(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - - fileSystemWatcher.InternalBufferSize = bytes; - - fileSystemWatcher.InternalBufferSize.Should().Be(expectedBytes); - } - - [SkippableFact] - public void Site_ShouldBeInitializedWithNull() - { - FileSystem.Initialize(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - - fileSystemWatcher.Site.Should().BeNull(); - } - - [SkippableFact] - public void Site_ShouldBeWritable() - { - ISite? site = new Mock().Object; - FileSystem.Initialize(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - - fileSystemWatcher.Site = site; - - fileSystemWatcher.Site.Should().Be(site); - } - - [SkippableFact] - public void SynchronizingObject_ShouldBeInitializedWithNull() - { - FileSystem.Initialize(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - - fileSystemWatcher.SynchronizingObject.Should().BeNull(); - } - - [SkippableFact] - public void SynchronizingObject_ShouldBeWritable() - { - ISynchronizeInvoke? synchronizingObject = new Mock().Object; - FileSystem.Initialize(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - - fileSystemWatcher.SynchronizingObject = synchronizingObject; - - fileSystemWatcher.SynchronizingObject.Should().Be(synchronizingObject); - } -} +using Moq; +using System.ComponentModel; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void BeginInit_ShouldStopListening(string path) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + FileSystem.Initialize(); + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.EnableRaisingEvents = true; + + fileSystemWatcher.BeginInit(); + + fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); + try + { + Task.Run(() => + { + while (!ms.IsSet) + { + Thread.Sleep(10); + FileSystem.Directory.CreateDirectory(path); + FileSystem.Directory.Delete(path); + } + }); + IFileSystemWatcher.IWaitForChangedResult result = + fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created, 1000); + + fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); + result.TimedOut.Should().BeTrue(); + result.ChangeType.Should().Be(0); + result.Name.Should().BeNull(); + result.OldName.Should().BeNull(); + } + finally + { + ms.Set(); + } + } + + [SkippableFact] + public void Container_ShouldBeInitializedWithNull() + { + FileSystem.Initialize(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + + fileSystemWatcher.Container.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void EndInit_ShouldRestartListening(string path) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + FileSystem.Initialize(); + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.EnableRaisingEvents = true; + fileSystemWatcher.BeginInit(); + + fileSystemWatcher.EndInit(); + + fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); + try + { + Task.Run(() => + { + while (!ms.IsSet) + { + Thread.Sleep(10); + FileSystem.Directory.CreateDirectory(path); + FileSystem.Directory.Delete(path); + } + }); + IFileSystemWatcher.IWaitForChangedResult result = + fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created, 100); + + fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); + result.TimedOut.Should().BeFalse(); + } + finally + { + ms.Set(); + } + } + + [SkippableTheory] + [InlineData(-1, 4096)] + [InlineData(4095, 4096)] + [InlineData(4097, 4097)] + public void InternalBufferSize_ShouldAtLeastHave4096Bytes( + int bytes, int expectedBytes) + { + FileSystem.Initialize(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + + fileSystemWatcher.InternalBufferSize = bytes; + + fileSystemWatcher.InternalBufferSize.Should().Be(expectedBytes); + } + + [SkippableFact] + public void Site_ShouldBeInitializedWithNull() + { + FileSystem.Initialize(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + + fileSystemWatcher.Site.Should().BeNull(); + } + + [SkippableFact] + public void Site_ShouldBeWritable() + { + ISite? site = new Mock().Object; + FileSystem.Initialize(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + + fileSystemWatcher.Site = site; + + fileSystemWatcher.Site.Should().Be(site); + } + + [SkippableFact] + public void SynchronizingObject_ShouldBeInitializedWithNull() + { + FileSystem.Initialize(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + + fileSystemWatcher.SynchronizingObject.Should().BeNull(); + } + + [SkippableFact] + public void SynchronizingObject_ShouldBeWritable() + { + ISynchronizeInvoke? synchronizingObject = new Mock().Object; + FileSystem.Initialize(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + + fileSystemWatcher.SynchronizingObject = synchronizingObject; + + fileSystemWatcher.SynchronizingObject.Should().Be(synchronizingObject); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/WaitForChangedTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/WaitForChangedTests.cs index 9ef05116d..caa114190 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/WaitForChangedTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/WaitForChangedTests.cs @@ -1,89 +1,88 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class WaitForChangedTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void WaitForChanged_ShouldBlockUntilEventHappens(string path) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - FileSystem.Initialize(); - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - try - { - Task.Run(() => - { - while (!ms.IsSet) - { - Thread.Sleep(10); - FileSystem.Directory.CreateDirectory(path); - FileSystem.Directory.Delete(path); - } - }); - - using (CancellationTokenSource cts = new(5000)) - { - cts.Token.Register(() => throw new TimeoutException()); - IFileSystemWatcher.IWaitForChangedResult result = - fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created); - fileSystemWatcher.EnableRaisingEvents.Should().BeFalse(); - result.TimedOut.Should().BeFalse(); - result.ChangeType.Should().Be(WatcherChangeTypes.Created); - result.Name.Should().Be(path); - result.OldName.Should().BeNull(); - } - } - finally - { - ms.Set(); - } - } - - [SkippableTheory] - [AutoData] - public void WaitForChanged_Timeout_ShouldReturnTimedOut(string path) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - FileSystem.Initialize(); - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - try - { - fileSystemWatcher.EnableRaisingEvents = true; - Task.Run(() => - { - while (!ms.IsSet) - { - Thread.Sleep(10); - FileSystem.Directory.CreateDirectory(path); - FileSystem.Directory.Delete(path); - } - }); - IFileSystemWatcher.IWaitForChangedResult result = - fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Changed, 100); - - fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); - result.TimedOut.Should().BeTrue(); - result.ChangeType.Should().Be(0); - result.Name.Should().BeNull(); - result.OldName.Should().BeNull(); - } - finally - { - ms.Set(); - } - } -} +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class WaitForChangedTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void WaitForChanged_ShouldBlockUntilEventHappens(string path) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + FileSystem.Initialize(); + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + try + { + Task.Run(() => + { + while (!ms.IsSet) + { + Thread.Sleep(10); + FileSystem.Directory.CreateDirectory(path); + FileSystem.Directory.Delete(path); + } + }); + + using (CancellationTokenSource cts = new(5000)) + { + cts.Token.Register(() => throw new TimeoutException()); + IFileSystemWatcher.IWaitForChangedResult result = + fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created); + fileSystemWatcher.EnableRaisingEvents.Should().BeFalse(); + result.TimedOut.Should().BeFalse(); + result.ChangeType.Should().Be(WatcherChangeTypes.Created); + result.Name.Should().Be(path); + result.OldName.Should().BeNull(); + } + } + finally + { + ms.Set(); + } + } + + [SkippableTheory] + [AutoData] + public void WaitForChanged_Timeout_ShouldReturnTimedOut(string path) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + FileSystem.Initialize(); + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + try + { + fileSystemWatcher.EnableRaisingEvents = true; + Task.Run(() => + { + while (!ms.IsSet) + { + Thread.Sleep(10); + FileSystem.Directory.CreateDirectory(path); + FileSystem.Directory.Delete(path); + } + }); + IFileSystemWatcher.IWaitForChangedResult result = + fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Changed, 100); + + fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); + result.TimedOut.Should().BeTrue(); + result.ChangeType.Should().Be(0); + result.Name.Should().BeNull(); + result.OldName.Should().BeNull(); + } + finally + { + ms.Set(); + } + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/ExceptionTests.cs index 1d1f21db3..b49d5bc73 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/ExceptionTests.cs @@ -1,123 +1,122 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcherFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileSystemWatcher); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), parameters: " ")] - public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileSystemWatcher); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileSystemWatcher); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), - parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileSystemWatcher); - }); - - if (!Test.RunsOnWindows) - { - if (exception is IOException ioException) - { - ioException.HResult.Should().NotBe(-2147024809, - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - else - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - - #region Helpers - - public static IEnumerable GetFileSystemWatcherFactoryCallbacks( - string? path) - => GetFileSystemWatcherFactoryCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes.IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetFileSystemWatcherFactoryCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.All, "path", fileSystemWatcherFactory - => fileSystemWatcherFactory.New(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", fileSystemWatcherFactory - => fileSystemWatcherFactory.New(value, "*")); - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcherFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileSystemWatcher); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), parameters: " ")] + public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileSystemWatcher); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileSystemWatcher); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), + parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileSystemWatcher); + }); + + if (!Test.RunsOnWindows) + { + if (exception is IOException ioException) + { + ioException.HResult.Should().NotBe(-2147024809, + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + else + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + + #region Helpers + + public static IEnumerable GetFileSystemWatcherFactoryCallbacks( + string? path) + => GetFileSystemWatcherFactoryCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes.IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetFileSystemWatcherFactoryCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.All, "path", fileSystemWatcherFactory + => fileSystemWatcherFactory.New(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", fileSystemWatcherFactory + => fileSystemWatcherFactory.New(value, "*")); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/Tests.cs index 31c4757e6..89cc2680e 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/Tests.cs @@ -1,79 +1,78 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcherFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableFact] - public void New_ShouldInitializeWithDefaultValues() - { - IFileSystemWatcher result = - FileSystem.FileSystemWatcher.New(); - - result.Path.Should().Be(""); -#if NETFRAMEWORK - result.Filter.Should().Be("*.*"); -#else - result.Filter.Should().Be("*"); -#endif - result.IncludeSubdirectories.Should().BeFalse(); - result.InternalBufferSize.Should().Be(8192); - result.NotifyFilter.Should().Be(NotifyFilters.FileName | - NotifyFilters.DirectoryName | - NotifyFilters.LastWrite); - result.EnableRaisingEvents.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void New_WithPath_ShouldInitializeWithDefaultValues(string path) - { - FileSystem.Directory.CreateDirectory(path); - IFileSystemWatcher result = - FileSystem.FileSystemWatcher.New(path); - - result.Path.Should().Be(path); -#if NETFRAMEWORK - result.Filter.Should().Be("*.*"); -#else - result.Filter.Should().Be("*"); -#endif - result.IncludeSubdirectories.Should().BeFalse(); - result.InternalBufferSize.Should().Be(8192); - result.NotifyFilter.Should().Be(NotifyFilters.FileName | - NotifyFilters.DirectoryName | - NotifyFilters.LastWrite); - result.EnableRaisingEvents.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void New_WithPathAndFilter_ShouldInitializeWithDefaultValues( - string path, string filter) - { - FileSystem.Directory.CreateDirectory(path); - IFileSystemWatcher result = - FileSystem.FileSystemWatcher.New(path, filter); - - result.Path.Should().Be(path); - result.Filter.Should().Be(filter); - result.IncludeSubdirectories.Should().BeFalse(); - result.InternalBufferSize.Should().Be(8192); - result.NotifyFilter.Should().Be(NotifyFilters.FileName | - NotifyFilters.DirectoryName | - NotifyFilters.LastWrite); - result.EnableRaisingEvents.Should().BeFalse(); - } - - [SkippableFact] - public void Wrap_Null_ShouldReturnNull() - { - IFileSystemWatcher? result = FileSystem.FileSystemWatcher.Wrap(null); - - result.Should().BeNull(); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcherFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableFact] + public void New_ShouldInitializeWithDefaultValues() + { + IFileSystemWatcher result = + FileSystem.FileSystemWatcher.New(); + + result.Path.Should().Be(""); +#if NETFRAMEWORK + result.Filter.Should().Be("*.*"); +#else + result.Filter.Should().Be("*"); +#endif + result.IncludeSubdirectories.Should().BeFalse(); + result.InternalBufferSize.Should().Be(8192); + result.NotifyFilter.Should().Be(NotifyFilters.FileName | + NotifyFilters.DirectoryName | + NotifyFilters.LastWrite); + result.EnableRaisingEvents.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void New_WithPath_ShouldInitializeWithDefaultValues(string path) + { + FileSystem.Directory.CreateDirectory(path); + IFileSystemWatcher result = + FileSystem.FileSystemWatcher.New(path); + + result.Path.Should().Be(path); +#if NETFRAMEWORK + result.Filter.Should().Be("*.*"); +#else + result.Filter.Should().Be("*"); +#endif + result.IncludeSubdirectories.Should().BeFalse(); + result.InternalBufferSize.Should().Be(8192); + result.NotifyFilter.Should().Be(NotifyFilters.FileName | + NotifyFilters.DirectoryName | + NotifyFilters.LastWrite); + result.EnableRaisingEvents.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void New_WithPathAndFilter_ShouldInitializeWithDefaultValues( + string path, string filter) + { + FileSystem.Directory.CreateDirectory(path); + IFileSystemWatcher result = + FileSystem.FileSystemWatcher.New(path, filter); + + result.Path.Should().Be(path); + result.Filter.Should().Be(filter); + result.IncludeSubdirectories.Should().BeFalse(); + result.InternalBufferSize.Should().Be(8192); + result.NotifyFilter.Should().Be(NotifyFilters.FileName | + NotifyFilters.DirectoryName | + NotifyFilters.LastWrite); + result.EnableRaisingEvents.Should().BeFalse(); + } + + [SkippableFact] + public void Wrap_Null_ShouldReturnNull() + { + IFileSystemWatcher? result = FileSystem.FileSystemWatcher.Wrap(null); + + result.Should().BeNull(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/TestHelpers/FileTestHelper.cs b/Tests/Testably.Abstractions.Tests/TestHelpers/FileTestHelper.cs index e3eec2f79..92ee7a489 100644 --- a/Tests/Testably.Abstractions.Tests/TestHelpers/FileTestHelper.cs +++ b/Tests/Testably.Abstractions.Tests/TestHelpers/FileTestHelper.cs @@ -1,81 +1,80 @@ -using System.IO; -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Tests.TestHelpers; - -public static class FileTestHelper -{ - /// - /// The minimum delay to test if times were adjusted in the real file system. - /// - internal static readonly TimeSpan AdjustTimesDelay = TimeSpan.FromMilliseconds(1500); - - /// - /// The default time returned by the file system if no time has been set. - /// : - /// A file time is a 64-bit value that represents the number of 100-nanosecond intervals that have elapsed - /// since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC). - /// - internal static readonly DateTime NullTime = new(1601, 1, 1, 0, 0, 0, - DateTimeKind.Utc); - - public static FileAccess CheckFileAccess(FileSystemStream stream) - { - FileAccess fileAccess = 0; - if (stream.CanRead) - { - fileAccess |= FileAccess.Read; - } - - if (stream.CanWrite) - { - fileAccess |= FileAccess.Write; - } - - return fileAccess; - } - - public static FileShare CheckFileShare(IFileSystem fileSystem, string path) - { - FileShare fileShare = FileShare.None; - Exception? exception = Record.Exception(() => - { - fileSystem.File.Open( - path, - FileMode.Open, - FileAccess.Read, - FileShare.ReadWrite) - .Dispose(); - }); - if (exception == null) - { - fileShare |= FileShare.Read; - } - - exception = Record.Exception(() => - { - fileSystem.File.Open( - path, - FileMode.Open, - FileAccess.Write, - FileShare.ReadWrite) - .Dispose(); - }); - if (exception == null) - { - fileShare |= FileShare.Write; - } - - return fileShare; - } - - public static string RootDrive(string path = "", char driveLetter = 'C') - { - if (Test.RunsOnWindows) - { - return $"{driveLetter}:\\{path}"; - } - - return "/" + path; - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.TestHelpers; + +public static class FileTestHelper +{ + /// + /// The minimum delay to test if times were adjusted in the real file system. + /// + internal static readonly TimeSpan AdjustTimesDelay = TimeSpan.FromMilliseconds(1500); + + /// + /// The default time returned by the file system if no time has been set. + /// : + /// A file time is a 64-bit value that represents the number of 100-nanosecond intervals that have elapsed + /// since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC). + /// + internal static readonly DateTime NullTime = new(1601, 1, 1, 0, 0, 0, + DateTimeKind.Utc); + + public static FileAccess CheckFileAccess(FileSystemStream stream) + { + FileAccess fileAccess = 0; + if (stream.CanRead) + { + fileAccess |= FileAccess.Read; + } + + if (stream.CanWrite) + { + fileAccess |= FileAccess.Write; + } + + return fileAccess; + } + + public static FileShare CheckFileShare(IFileSystem fileSystem, string path) + { + FileShare fileShare = FileShare.None; + Exception? exception = Record.Exception(() => + { + fileSystem.File.Open( + path, + FileMode.Open, + FileAccess.Read, + FileShare.ReadWrite) + .Dispose(); + }); + if (exception == null) + { + fileShare |= FileShare.Read; + } + + exception = Record.Exception(() => + { + fileSystem.File.Open( + path, + FileMode.Open, + FileAccess.Write, + FileShare.ReadWrite) + .Dispose(); + }); + if (exception == null) + { + fileShare |= FileShare.Write; + } + + return fileShare; + } + + public static string RootDrive(string path = "", char driveLetter = 'C') + { + if (Test.RunsOnWindows) + { + return $"{driveLetter}:\\{path}"; + } + + return "/" + path; + } +} diff --git a/Tests/Testably.Abstractions.Tests/TestHelpers/Usings.cs b/Tests/Testably.Abstractions.Tests/TestHelpers/Usings.cs index 6f8ea9d7c..a27ae72ea 100644 --- a/Tests/Testably.Abstractions.Tests/TestHelpers/Usings.cs +++ b/Tests/Testably.Abstractions.Tests/TestHelpers/Usings.cs @@ -1,6 +1,7 @@ global using AutoFixture.Xunit2; global using FluentAssertions; global using System; +global using Testably.Abstractions.FileSystem; global using Testably.Abstractions.Testing; global using Testably.Abstractions.TestHelpers; global using Testably.Abstractions.Tests.TestHelpers; From 785ff237911991ca1c7a9fdec8b41849fd174f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Tue, 15 Nov 2022 18:36:29 +0100 Subject: [PATCH 4/9] Add `IFileSystemEntity` to `IFileSystemInfo` --- .../FileSystem/IFileSystemInfo.cs | 2 +- .../FileSystem/DirectoryInfoMock.cs | 53 +++++++------ .../FileSystem/FileInfoMock.cs | 77 ++++++++++--------- .../FileSystem/FileSystemInfoMock.cs | 32 ++++---- .../FileSystem/FileSystemInfoWrapper.cs | 4 + 5 files changed, 91 insertions(+), 77 deletions(-) diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemInfo.cs b/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemInfo.cs index b511941b2..71c942848 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemInfo.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemInfo.cs @@ -6,7 +6,7 @@ namespace Testably.Abstractions.FileSystem; /// /// Abstractions for . /// -public interface IFileSystemInfo +public interface IFileSystemInfo : IFileSystemEntity { /// FileAttributes Attributes { get; set; } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs index ef56e09dc..a638fe8fc 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs @@ -13,10 +13,13 @@ namespace Testably.Abstractions.Testing.FileSystem; internal sealed class DirectoryInfoMock : FileSystemInfoMock, IDirectoryInfo { + private readonly MockFileSystem _fileSystem; + private DirectoryInfoMock(IStorageLocation location, MockFileSystem fileSystem) : base(fileSystem, location, FileSystemTypes.Directory) { + _fileSystem = fileSystem; } #region IDirectoryInfo Members @@ -27,19 +30,19 @@ public override bool Exists /// public IDirectoryInfo? Parent - => New(Location.GetParent(), FileSystem); + => New(Location.GetParent(), _fileSystem); /// public IDirectoryInfo Root - => New(FileSystem.Storage.GetLocation(string.Empty.PrefixRoot()), - FileSystem); + => New(_fileSystem.Storage.GetLocation(string.Empty.PrefixRoot()), + _fileSystem); /// public void Create() { - FullName.EnsureValidFormat(FileSystem); + FullName.EnsureValidFormat(_fileSystem); - Container = FileSystem.Storage.GetOrCreateContainer(Location, + Container = _fileSystem.Storage.GetOrCreateContainer(Location, InMemoryContainer.NewDirectory, Extensibility); @@ -50,11 +53,11 @@ public void Create() public IDirectoryInfo CreateSubdirectory(string path) { DirectoryInfoMock directory = New( - FileSystem.Storage.GetLocation( - FileSystem.Path.Combine(FullName, path - .EnsureValidFormat(FileSystem, nameof(path), + _fileSystem.Storage.GetLocation( + _fileSystem.Path.Combine(FullName, path + .EnsureValidFormat(_fileSystem, nameof(path), Execute.IsWindows && !Execute.IsNetFramework))), - FileSystem); + _fileSystem); directory.Create(); return directory; } @@ -62,7 +65,7 @@ public IDirectoryInfo CreateSubdirectory(string path) /// public override void Delete() { - if (!FileSystem.Storage.DeleteContainer(Location)) + if (!_fileSystem.Storage.DeleteContainer(Location)) { throw ExceptionFactory.DirectoryNotFound(Location.FullPath); } @@ -73,8 +76,8 @@ public override void Delete() /// public void Delete(bool recursive) { - if (!FileSystem.Storage.DeleteContainer( - FileSystem.Storage.GetLocation(FullName), recursive)) + if (!_fileSystem.Storage.DeleteContainer( + _fileSystem.Storage.GetLocation(FullName), recursive)) { throw ExceptionFactory.DirectoryNotFound(FullName); } @@ -100,7 +103,7 @@ public IEnumerable EnumerateDirectories( FullName, searchPattern, EnumerationOptionsHelper.FromSearchOption(searchOption)) - .Select(location => New(location, FileSystem)); + .Select(location => New(location, _fileSystem)); #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// @@ -111,7 +114,7 @@ public IEnumerable EnumerateDirectories( FullName, searchPattern, enumerationOptions) - .Select(location => New(location, FileSystem)); + .Select(location => New(location, _fileSystem)); #endif /// @@ -131,7 +134,7 @@ public IEnumerable EnumerateFiles( FullName, searchPattern, EnumerationOptionsHelper.FromSearchOption(searchOption)) - .Select(location => FileInfoMock.New(location, FileSystem)); + .Select(location => FileInfoMock.New(location, _fileSystem)); #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// @@ -141,7 +144,7 @@ public IEnumerable EnumerateFiles( FullName, searchPattern, enumerationOptions) - .Select(location => FileInfoMock.New(location, FileSystem)); + .Select(location => FileInfoMock.New(location, _fileSystem)); #endif /// @@ -162,7 +165,7 @@ public IEnumerable EnumerateFileSystemInfos( FullName, searchPattern, EnumerationOptionsHelper.FromSearchOption(searchOption)) - .Select(location => FileSystemInfoMock.New(location, FileSystem)); + .Select(location => FileSystemInfoMock.New(location, _fileSystem)); #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// @@ -173,7 +176,7 @@ public IEnumerable EnumerateFileSystemInfos( FullName, searchPattern, enumerationOptions) - .Select(location => FileSystemInfoMock.New(location, FileSystem)); + .Select(location => FileSystemInfoMock.New(location, _fileSystem)); #endif /// @@ -240,10 +243,10 @@ public IFileSystemInfo[] GetFileSystemInfos( /// public void MoveTo(string destDirName) - => Location = FileSystem.Storage.Move( - FileSystem.Storage.GetLocation(FullName), - FileSystem.Storage.GetLocation(destDirName - .EnsureValidFormat(FileSystem, nameof(destDirName))), + => Location = _fileSystem.Storage.Move( + _fileSystem.Storage.GetLocation(FullName), + _fileSystem.Storage.GetLocation(destDirName + .EnsureValidFormat(_fileSystem, nameof(destDirName))), recursive: true) ?? throw ExceptionFactory.DirectoryNotFound(FullName); @@ -267,11 +270,11 @@ private IEnumerable EnumerateInternal( string searchPattern, EnumerationOptions enumerationOptions) { - StorageExtensions.AdjustedLocation adjustedLocation = FileSystem.Storage + StorageExtensions.AdjustedLocation adjustedLocation = _fileSystem.Storage .AdjustLocationFromSearchPattern( - path.EnsureValidFormat(FileSystem), + path.EnsureValidFormat(_fileSystem), searchPattern); - return FileSystem.Storage.EnumerateLocations( + return _fileSystem.Storage.EnumerateLocations( adjustedLocation.Location, fileSystemTypes, adjustedLocation.SearchPattern, diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs index 91441c41f..123f8d748 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs @@ -11,10 +11,13 @@ namespace Testably.Abstractions.Testing.FileSystem; internal sealed class FileInfoMock : FileSystemInfoMock, IFileInfo { + private readonly MockFileSystem _fileSystem; + private FileInfoMock(IStorageLocation location, MockFileSystem fileSystem) : base(fileSystem, location, FileSystemTypes.File) { + _fileSystem = fileSystem; } #region IFileInfo Members @@ -22,7 +25,7 @@ private FileInfoMock(IStorageLocation location, /// public IDirectoryInfo? Directory => DirectoryInfoMock.New(Location.GetParent(), - FileSystem); + _fileSystem); /// public string? DirectoryName @@ -74,37 +77,37 @@ public StreamWriter AppendText() /// public IFileInfo CopyTo(string destFileName) { - destFileName.EnsureValidArgument(FileSystem, nameof(destFileName)); - IStorageLocation destinationLocation = FileSystem.Storage.GetLocation(destFileName); - Location.ThrowExceptionIfNotFound(FileSystem); - IStorageLocation location = FileSystem.Storage + destFileName.EnsureValidArgument(_fileSystem, nameof(destFileName)); + IStorageLocation destinationLocation = _fileSystem.Storage.GetLocation(destFileName); + Location.ThrowExceptionIfNotFound(_fileSystem); + IStorageLocation location = _fileSystem.Storage .Copy(Location, destinationLocation) ?? throw ExceptionFactory.FileNotFound(FullName); - return FileSystem.FileInfo.New(location.FullPath); + return _fileSystem.FileInfo.New(location.FullPath); } /// public IFileInfo CopyTo(string destFileName, bool overwrite) { - destFileName.EnsureValidArgument(FileSystem, nameof(destFileName)); - IStorageLocation location = FileSystem.Storage.Copy( + destFileName.EnsureValidArgument(_fileSystem, nameof(destFileName)); + IStorageLocation location = _fileSystem.Storage.Copy( Location, - FileSystem.Storage.GetLocation(destFileName), + _fileSystem.Storage.GetLocation(destFileName), overwrite) ?? throw ExceptionFactory.FileNotFound(FullName); - return FileSystem.FileInfo.New(location.FullPath); + return _fileSystem.FileInfo.New(location.FullPath); } /// public FileSystemStream Create() { Execute.NotOnNetFramework(Refresh); - return FileSystem.File.Create(FullName); + return _fileSystem.File.Create(FullName); } /// public StreamWriter CreateText() - => new(FileSystem.File.Create(FullName)); + => new(_fileSystem.File.Create(FullName)); /// [SupportedOSPlatform("windows")] @@ -119,10 +122,10 @@ public void Encrypt() /// public void MoveTo(string destFileName) { - Location = FileSystem.Storage.Move( + Location = _fileSystem.Storage.Move( Location, - FileSystem.Storage.GetLocation(destFileName - .EnsureValidArgument(FileSystem, nameof(destFileName)))) + _fileSystem.Storage.GetLocation(destFileName + .EnsureValidArgument(_fileSystem, nameof(destFileName)))) ?? throw ExceptionFactory.FileNotFound(FullName); } @@ -130,10 +133,10 @@ public void MoveTo(string destFileName) /// public void MoveTo(string destFileName, bool overwrite) { - Location = FileSystem.Storage.Move( + Location = _fileSystem.Storage.Move( Location, - FileSystem.Storage.GetLocation(destFileName - .EnsureValidArgument(FileSystem, nameof(destFileName))), + _fileSystem.Storage.GetLocation(destFileName + .EnsureValidArgument(_fileSystem, nameof(destFileName))), overwrite) ?? throw ExceptionFactory.FileNotFound(FullName); } @@ -146,7 +149,7 @@ public FileSystemStream Open(FileMode mode) () => throw ExceptionFactory.AppendAccessOnlyInWriteOnlyMode()); return new FileStreamMock( - FileSystem, + _fileSystem, FullName, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, @@ -156,7 +159,7 @@ public FileSystemStream Open(FileMode mode) /// public FileSystemStream Open(FileMode mode, FileAccess access) => new FileStreamMock( - FileSystem, + _fileSystem, FullName, mode, access, @@ -165,7 +168,7 @@ public FileSystemStream Open(FileMode mode, FileAccess access) /// public FileSystemStream Open(FileMode mode, FileAccess access, FileShare share) => new FileStreamMock( - FileSystem, + _fileSystem, FullName, mode, access, @@ -174,13 +177,13 @@ public FileSystemStream Open(FileMode mode, FileAccess access, FileShare share) #if FEATURE_FILESYSTEM_STREAM_OPTIONS /// public FileSystemStream Open(FileStreamOptions options) - => FileSystem.File.Open(FullName, options); + => _fileSystem.File.Open(FullName, options); #endif /// public FileSystemStream OpenRead() => new FileStreamMock( - FileSystem, + _fileSystem, FullName, FileMode.Open, FileAccess.Read); @@ -192,7 +195,7 @@ public StreamReader OpenText() /// public FileSystemStream OpenWrite() => new FileStreamMock( - FileSystem, + _fileSystem, FullName, FileMode.OpenOrCreate, FileAccess.Write, @@ -203,10 +206,10 @@ public IFileInfo Replace(string destinationFileName, string? destinationBackupFileName) { IStorageLocation location = - FileSystem + _fileSystem .Storage .Replace( - Location.ThrowIfNotFound(FileSystem, + Location.ThrowIfNotFound(_fileSystem, () => { }, () => { @@ -217,10 +220,10 @@ public IFileInfo Replace(string destinationFileName, throw ExceptionFactory.DirectoryNotFound(FullName); }), - FileSystem.Storage + _fileSystem.Storage .GetLocation(destinationFileName - .EnsureValidFormat(FileSystem, nameof(destinationFileName))) - .ThrowIfNotFound(FileSystem, + .EnsureValidFormat(_fileSystem, nameof(destinationFileName))) + .ThrowIfNotFound(_fileSystem, () => { }, () => { @@ -231,9 +234,9 @@ public IFileInfo Replace(string destinationFileName, throw ExceptionFactory.FileNotFound(FullName); }), - FileSystem.Storage + _fileSystem.Storage .GetLocation(destinationBackupFileName) - .ThrowIfNotFound(FileSystem, + .ThrowIfNotFound(_fileSystem, () => { }, () => { @@ -245,7 +248,7 @@ public IFileInfo Replace(string destinationFileName, throw ExceptionFactory.DirectoryNotFound(FullName); })) ?? throw ExceptionFactory.FileNotFound(FullName); - return FileSystem.FileInfo.New(location.FullPath); + return _fileSystem.FileInfo.New(location.FullPath); } /// @@ -253,17 +256,17 @@ public IFileInfo Replace(string destinationFileName, string? destinationBackupFileName, bool ignoreMetadataErrors) { - IStorageLocation location = FileSystem.Storage.Replace( + IStorageLocation location = _fileSystem.Storage.Replace( Location, - FileSystem.Storage.GetLocation( + _fileSystem.Storage.GetLocation( destinationFileName - .EnsureValidFormat(FileSystem, + .EnsureValidFormat(_fileSystem, nameof(destinationFileName))), - FileSystem.Storage.GetLocation( + _fileSystem.Storage.GetLocation( destinationBackupFileName), ignoreMetadataErrors) ?? throw ExceptionFactory.FileNotFound(FullName); - return FileSystem.FileInfo.New(location.FullPath); + return _fileSystem.FileInfo.New(location.FullPath); } #endregion diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs index f15281ff9..40fd76711 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs @@ -9,7 +9,7 @@ internal class FileSystemInfoMock : IFileSystemInfo { protected FileSystemTypes FileSystemType { get; } protected IStorageLocation Location; - protected readonly MockFileSystem FileSystem; + private readonly MockFileSystem _fileSystem; protected IStorageContainer Container { @@ -32,7 +32,7 @@ protected IStorageContainer Container protected FileSystemInfoMock(MockFileSystem fileSystem, IStorageLocation location, FileSystemTypes fileSystemType) { - FileSystem = fileSystem; + _fileSystem = fileSystem; Location = location; _container = fileSystem.Storage.GetContainer(location); FileSystemType = _container is not NullContainer @@ -53,9 +53,9 @@ public FileAttributes Attributes /// public void CreateAsSymbolicLink(string pathToTarget) { - FullName.EnsureValidFormat(FileSystem); - pathToTarget.ThrowCommonExceptionsIfPathToTargetIsInvalid(FileSystem); - if (FileSystem.Storage.TryAddContainer(Location, InMemoryContainer.NewFile, + FullName.EnsureValidFormat(_fileSystem); + pathToTarget.ThrowCommonExceptionsIfPathToTargetIsInvalid(_fileSystem); + if (_fileSystem.Storage.TryAddContainer(Location, InMemoryContainer.NewFile, out IStorageContainer? container)) { Container = container; @@ -86,7 +86,7 @@ public DateTime CreationTimeUtc /// public virtual void Delete() { - FileSystem.Storage.DeleteContainer(Location); + _fileSystem.Storage.DeleteContainer(Location); ResetCache(!Execute.IsNetFramework); } @@ -113,7 +113,7 @@ public string Extension return "."; } - return FileSystem.Path.GetExtension(Location.FullPath); + return _fileSystem.Path.GetExtension(Location.FullPath); } } @@ -121,6 +121,10 @@ public string Extension public IFileSystemExtensibility Extensibility => Container.Extensibility; + /// + public IFileSystem FileSystem + => _fileSystem; + /// public string FullName => Location.FullPath; @@ -160,11 +164,11 @@ public string? LinkTarget /// public string Name - => FileSystem.Path.GetPathRoot(Location.FullPath) == Location.FullPath + => _fileSystem.Path.GetPathRoot(Location.FullPath) == Location.FullPath ? Location.FullPath - : FileSystem.Path.GetFileName(Location.FullPath.TrimEnd( - FileSystem.Path.DirectorySeparatorChar, - FileSystem.Path.AltDirectorySeparatorChar)); + : _fileSystem.Path.GetFileName(Location.FullPath.TrimEnd( + _fileSystem.Path.DirectorySeparatorChar, + _fileSystem.Path.AltDirectorySeparatorChar)); #if FEATURE_FILESYSTEM_UNIXFILEMODE /// @@ -195,12 +199,12 @@ public void Refresh() try { IStorageLocation? targetLocation = - FileSystem.Storage.ResolveLinkTarget( + _fileSystem.Storage.ResolveLinkTarget( Location, returnFinalTarget); if (targetLocation != null) { - return New(targetLocation, FileSystem); + return New(targetLocation, _fileSystem); } return null; @@ -258,7 +262,7 @@ private void RefreshInternal() return; } - Container = FileSystem.Storage.GetContainer(Location); + Container = _fileSystem.Storage.GetContainer(Location); _isInitialized = true; } } diff --git a/Source/Testably.Abstractions/FileSystem/FileSystemInfoWrapper.cs b/Source/Testably.Abstractions/FileSystem/FileSystemInfoWrapper.cs index 1623c5334..81be97699 100644 --- a/Source/Testably.Abstractions/FileSystem/FileSystemInfoWrapper.cs +++ b/Source/Testably.Abstractions/FileSystem/FileSystemInfoWrapper.cs @@ -50,6 +50,10 @@ public string Extension /// public IFileSystemExtensibility Extensibility { get; } + /// + public IFileSystem FileSystem + => _fileSystem; + /// public string FullName => _instance.FullName; From 0b0ff86071cf55819e7306d80358d8df5dc9958d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Tue, 15 Nov 2022 19:31:55 +0100 Subject: [PATCH 5/9] Remove nesting from IWaitForChangedResult --- .../FileSystem/IFileSystemWatcher.cs | 18 ---------------- .../FileSystem/IWaitForChangedResult.cs | 21 +++++++++++++++++++ .../FileSystem/FileSystemWatcherMock.cs | 21 +++++++++---------- .../FileSystem/FileSystemWatcherWrapper.cs | 16 +++++++------- .../FileSystem/FileSystemWatcher/Tests.cs | 4 ++-- .../FileSystemWatcher/WaitForChangedTests.cs | 4 ++-- 6 files changed, 43 insertions(+), 41 deletions(-) create mode 100644 Source/Testably.Abstractions.Interface/FileSystem/IWaitForChangedResult.cs diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemWatcher.cs b/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemWatcher.cs index 687eb378c..bf26b8ae2 100644 --- a/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemWatcher.cs +++ b/Source/Testably.Abstractions.Interface/FileSystem/IFileSystemWatcher.cs @@ -75,22 +75,4 @@ public interface IFileSystemWatcher : IFileSystemEntity, IDisposable /// IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout); #endif - - /// - /// Abstractions for . - /// - public interface IWaitForChangedResult - { - /// - WatcherChangeTypes ChangeType { get; } - - /// - string? Name { get; } - - /// - string? OldName { get; } - - /// - bool TimedOut { get; } - } } diff --git a/Source/Testably.Abstractions.Interface/FileSystem/IWaitForChangedResult.cs b/Source/Testably.Abstractions.Interface/FileSystem/IWaitForChangedResult.cs new file mode 100644 index 000000000..d992a3d44 --- /dev/null +++ b/Source/Testably.Abstractions.Interface/FileSystem/IWaitForChangedResult.cs @@ -0,0 +1,21 @@ +using System.IO; + +namespace Testably.Abstractions.FileSystem; + +/// +/// Abstractions for . +/// +public interface IWaitForChangedResult +{ + /// + WatcherChangeTypes ChangeType { get; } + + /// + string? Name { get; } + + /// + string? OldName { get; } + + /// + bool TimedOut { get; } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs index 8e498c8e5..0c23786ba 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs @@ -165,18 +165,18 @@ public void EndInit() public event RenamedEventHandler? Renamed; /// - public IFileSystemWatcher.IWaitForChangedResult WaitForChanged( + public IWaitForChangedResult WaitForChanged( WatcherChangeTypes changeType) => WaitForChanged(changeType, Timeout.Infinite); /// - public IFileSystemWatcher.IWaitForChangedResult WaitForChanged( + public IWaitForChangedResult WaitForChanged( WatcherChangeTypes changeType, int timeout) => WaitForChangedInternal(changeType, TimeSpan.FromMilliseconds(timeout)); #if FEATURE_FILESYSTEM_NET7 /// - public IFileSystemWatcher.IWaitForChangedResult WaitForChanged( + public IWaitForChangedResult WaitForChanged( WatcherChangeTypes changeType, TimeSpan timeout) => WaitForChangedInternal(changeType, timeout); #endif @@ -436,10 +436,10 @@ private bool TryMakeRenamedEventArgs( InMemoryLocation.StringComparisonMode) ?? true; } - private IFileSystemWatcher.IWaitForChangedResult WaitForChangedInternal( + private IWaitForChangedResult WaitForChangedInternal( WatcherChangeTypes changeType, TimeSpan timeout) { - TaskCompletionSource + TaskCompletionSource tcs = new(); void EventHandler(object? _, ChangeDescription c) @@ -479,8 +479,7 @@ void EventHandler(object? _, ChangeDescription c) #endif } - private struct WaitForChangedResultMock - : IFileSystemWatcher.IWaitForChangedResult + private struct WaitForChangedResultMock : IWaitForChangedResult { public WaitForChangedResultMock( WatcherChangeTypes changeType, @@ -500,16 +499,16 @@ public WaitForChangedResultMock( public static readonly WaitForChangedResultMock TimedOutResult = new(changeType: 0, name: null, oldName: null, timedOut: true); - /// + /// public WatcherChangeTypes ChangeType { get; } - /// + /// public string? Name { get; } - /// + /// public string? OldName { get; } - /// + /// public bool TimedOut { get; } } } diff --git a/Source/Testably.Abstractions/FileSystem/FileSystemWatcherWrapper.cs b/Source/Testably.Abstractions/FileSystem/FileSystemWatcherWrapper.cs index bd61dacc2..bd0165b08 100644 --- a/Source/Testably.Abstractions/FileSystem/FileSystemWatcherWrapper.cs +++ b/Source/Testably.Abstractions/FileSystem/FileSystemWatcherWrapper.cs @@ -138,19 +138,19 @@ public void EndInit() => _instance.EndInit(); /// - public IFileSystemWatcher.IWaitForChangedResult WaitForChanged( + public IWaitForChangedResult WaitForChanged( WatcherChangeTypes changeType) => new WaitForChangedResultWrapper(_instance.WaitForChanged(changeType)); /// - public IFileSystemWatcher.IWaitForChangedResult WaitForChanged( + public IWaitForChangedResult WaitForChanged( WatcherChangeTypes changeType, int timeout) => new WaitForChangedResultWrapper( _instance.WaitForChanged(changeType, timeout)); #if FEATURE_FILESYSTEM_NET7 /// - public IFileSystemWatcher.IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, + public IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout) => new WaitForChangedResultWrapper( _instance.WaitForChanged(changeType, timeout)); @@ -171,7 +171,7 @@ public IFileSystemWatcher.IWaitForChangedResult WaitForChanged(WatcherChangeType } private readonly struct WaitForChangedResultWrapper - : IFileSystemWatcher.IWaitForChangedResult + : IWaitForChangedResult { private readonly WaitForChangedResult _instance; @@ -180,19 +180,19 @@ public WaitForChangedResultWrapper(WaitForChangedResult instance) _instance = instance; } - /// + /// public WatcherChangeTypes ChangeType => _instance.ChangeType; - /// + /// public string? Name => _instance.Name; - /// + /// public string? OldName => _instance.OldName; - /// + /// public bool TimedOut => _instance.TimedOut; } diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs index cb51e5da4..b9a57cce5 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs @@ -37,7 +37,7 @@ public void BeginInit_ShouldStopListening(string path) FileSystem.Directory.Delete(path); } }); - IFileSystemWatcher.IWaitForChangedResult result = + IWaitForChangedResult result = fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created, 1000); fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); @@ -89,7 +89,7 @@ public void EndInit_ShouldRestartListening(string path) FileSystem.Directory.Delete(path); } }); - IFileSystemWatcher.IWaitForChangedResult result = + IWaitForChangedResult result = fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created, 100); fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/WaitForChangedTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/WaitForChangedTests.cs index caa114190..8652af60d 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/WaitForChangedTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/WaitForChangedTests.cs @@ -34,7 +34,7 @@ public void WaitForChanged_ShouldBlockUntilEventHappens(string path) using (CancellationTokenSource cts = new(5000)) { cts.Token.Register(() => throw new TimeoutException()); - IFileSystemWatcher.IWaitForChangedResult result = + IWaitForChangedResult result = fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created); fileSystemWatcher.EnableRaisingEvents.Should().BeFalse(); result.TimedOut.Should().BeFalse(); @@ -71,7 +71,7 @@ public void WaitForChanged_Timeout_ShouldReturnTimedOut(string path) FileSystem.Directory.Delete(path); } }); - IFileSystemWatcher.IWaitForChangedResult result = + IWaitForChangedResult result = fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Changed, 100); fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); From aa0b8d273a5a949fd99a5507ea52755fcfef9eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Tue, 15 Nov 2022 19:42:54 +0100 Subject: [PATCH 6/9] cleanup code --- Feature.Flags.props | 5 +- .../AccessControlHelpers.cs | 101 +- .../DirectoryAclExtensions.cs | 180 +- .../DirectoryInfoAclExtensions.cs | 158 +- .../FileAclExtensions.cs | 124 +- .../FileInfoAclExtensions.cs | 114 +- .../FileStreamAclExtensions.cs | 80 +- .../IZipArchive.cs | 96 +- .../IZipArchiveEntry.cs | 120 +- .../IZipArchiveFactory.cs | 48 +- .../IZipFile.cs | 122 +- .../Internal/ZipUtilities.cs | 528 ++--- .../ZipArchiveEntryWrapper.cs | 254 +-- .../ZipArchiveFactory.cs | 80 +- .../ZipArchiveWrapper.cs | 264 +-- .../ZipFileWrapper.cs | 330 +-- .../Helpers/PathSystemBase.cs | 1 - .../FileSystem/ChangeHandler.cs | 142 +- .../DefaultAccessControlStrategy.cs | 54 +- .../FileSystem/DirectoryInfoFactoryMock.cs | 82 +- .../FileSystem/DirectoryInfoMock.cs | 566 ++--- .../FileSystem/DirectoryMock.cs | 942 ++++---- .../FileSystem/DriveInfoFactoryMock.cs | 114 +- .../FileSystem/DriveInfoMock.cs | 412 ++-- .../FileSystem/FileInfoFactoryMock.cs | 102 +- .../FileSystem/FileInfoMock.cs | 570 ++--- .../FileSystem/FileMock.cs | 1933 +++++++++-------- .../FileSystem/FileStreamFactoryMock.cs | 300 +-- .../FileSystem/FileStreamMock.cs | 894 ++++---- .../FileSystem/FileSystemInfoMock.cs | 536 ++--- .../FileSystemWatcherFactoryMock.cs | 156 +- .../FileSystem/FileSystemWatcherMock.cs | 1028 ++++----- .../FileSystem/IAccessControlStrategy.cs | 4 +- .../FileSystem/IInterceptionHandler.cs | 44 +- .../FileSystem/INotificationHandler.cs | 46 +- .../FileSystem/NullAccessControlStrategy.cs | 4 +- .../FileSystem/PathMock.cs | 92 +- .../FileSystemInitializer/DirectoryCleaner.cs | 264 +-- .../DirectoryInitializer.cs | 66 +- .../FileSystemInitializer/FileInitializer.cs | 130 +- .../FileSystemInitializer/IFileManipulator.cs | 4 +- .../IFileSystemDirectoryInitializer.cs | 44 +- .../IFileSystemFileInitializer.cs | 44 +- .../IFileSystemInitializer.cs | 4 +- .../FileSystemInitializer/Initializer.cs | 248 +-- .../FileSystemInitializerExtensions.cs | 94 +- .../Helpers/FileSystemExtensibility.cs | 92 +- .../MockFileSystem.cs | 306 +-- .../Polyfills/FileFeatureExtensionMethods.cs | 125 +- .../Storage/IStorage.cs | 406 ++-- .../Storage/IStorageContainer.cs | 218 +- .../Storage/IStorageDrive.cs | 94 +- .../Storage/IStorageLocation.cs | 76 +- .../Storage/InMemoryContainer.cs | 746 +++---- .../Storage/InMemoryStorage.cs | 1434 ++++++------ .../Storage/NullContainer.cs | 392 ++-- .../FileSystem/FileWrapper.cs | 8 +- .../FileInfoAclExtensionsTests.cs | 124 +- .../FileStreamAclExtensionsTests.cs | 82 +- .../ZipArchive/ZipArchiveTests.Extensions.cs | 568 ++--- .../ZipArchive/ZipArchiveTests.cs | 168 +- .../ZipArchiveEntryTests.Extensions.cs | 308 +-- .../ZipArchiveEntry/ZipArchiveEntryTests.cs | 496 ++--- .../ParityTests.cs | 392 ++-- .../TestHelpers/Parity.cs | 146 +- .../FileSystem/DriveInfoMockTests.cs | 568 ++--- .../FileSystemWatcherFactoryMockTests.cs | 148 +- .../FileSystem/FileSystemWatcherMockTests.cs | 372 ++-- .../Helpers/PathHelperTests.cs | 200 +- .../MockFileSystemTests.cs | 574 ++--- .../Storage/InMemoryStorageTests.cs | 408 ++-- .../TestHelpers/LockableContainer.cs | 292 +-- .../Directory/CreateDirectoryTests.cs | 612 +++--- .../FileSystem/Directory/DeleteTests.cs | 480 ++-- .../Directory/EnumerateDirectoriesTests.cs | 498 ++--- .../FileSystem/Directory/ExceptionTests.cs | 488 ++--- .../Directory/GetDirectoriesTests.cs | 428 ++-- .../FileSystem/Directory/MoveTests.cs | 582 ++--- .../Directory/ResolveLinkTargetTests.cs | 401 ++-- .../FileSystem/Directory/Tests.Times.cs | 1026 ++++----- .../FileSystem/Directory/Tests.cs | 252 +-- .../DirectoryInfo/AttributesTests.cs | 92 +- .../DirectoryInfo/CreateSubdirectoryTests.cs | 2 - .../FileSystem/DirectoryInfo/CreateTests.cs | 2 - .../FileSystem/DirectoryInfo/DeleteTests.cs | 258 +-- .../EnumerateDirectoriesTests.cs | 340 +-- .../EnumerateFileSystemInfosTests.cs | 408 ++-- .../DirectoryInfo/EnumerateFilesTests.cs | 356 +-- .../DirectoryInfo/ExceptionTests.cs | 300 +-- .../FileSystem/DirectoryInfo/ExistsTests.cs | 2 - .../DirectoryInfo/GetDirectoriesTests.cs | 340 +-- .../DirectoryInfo/GetFileSystemInfosTests.cs | 408 ++-- .../FileSystem/DirectoryInfo/GetFilesTests.cs | 356 +-- .../FileSystem/DirectoryInfo/MoveToTests.cs | 372 ++-- .../FileSystem/DirectoryInfo/Tests.cs | 752 +++---- .../DirectoryInfoFactory/ExceptionTests.cs | 234 +- .../FileSystem/DirectoryInfoFactory/Tests.cs | 2 - .../FileSystem/DriveInfo/Tests.cs | 2 - .../DriveInfoFactory/ExceptionTests.cs | 172 +- .../FileSystem/DriveInfoFactory/Tests.cs | 314 +-- .../FileSystem/File/CopyTests.cs | 648 +++--- .../FileSystem/File/CreateTests.cs | 218 +- .../FileSystem/File/DeleteTests.cs | 142 +- .../File/ExceptionMissingFileTests.cs | 793 ++++--- .../FileSystem/File/ExceptionTests.cs | 841 ++++--- .../FileSystem/File/MoveTests.cs | 492 ++--- .../FileSystem/File/OpenReadTests.cs | 282 +-- .../FileSystem/File/OpenTests.cs | 282 +-- .../FileSystem/File/OpenWriteTests.cs | 140 +- .../FileSystem/File/ReplaceTests.cs | 560 ++--- .../FileSystem/File/ResolveLinkTargetTests.cs | 383 ++-- .../FileSystem/FileInfo/AppendTextTests.cs | 90 +- .../FileSystem/FileInfo/CopyToTests.cs | 468 ++-- .../FileSystem/FileInfo/CreateTests.cs | 110 +- .../FileSystem/FileInfo/CreateTextTests.cs | 124 +- .../FileSystem/FileInfo/DeleteTests.cs | 190 +- .../FileInfo/EncryptDecryptTests.cs | 202 +- .../FileSystem/FileInfo/ExceptionTests.cs | 254 +-- .../FileSystem/FileInfo/ExistsTests.cs | 2 - .../FileSystem/FileInfo/LengthTests.cs | 286 +-- .../FileSystem/FileInfo/MoveToTests.cs | 572 ++--- .../FileSystem/FileInfo/OpenReadTests.cs | 78 +- .../FileSystem/FileInfo/OpenTests.cs | 506 ++--- .../FileSystem/FileInfo/OpenTextTests.cs | 80 +- .../FileSystem/FileInfo/OpenWriteTests.cs | 66 +- .../FileSystem/FileInfo/ReplaceTests.cs | 834 +++---- .../FileSystem/FileInfo/Tests.cs | 322 +-- .../FileInfoFactory/ExceptionTests.cs | 254 +-- .../FileSystem/FileInfoFactory/Tests.cs | 184 +- .../FileSystem/FileStream/AdjustTimesTests.cs | 1018 ++++----- .../FileSystem/FileStream/DisposeTests.cs | 230 +- .../FileSystem/FileStream/FileAccessTests.cs | 484 ++--- .../FileSystem/FileStream/OptionsTests.cs | 216 +- .../FileSystem/FileStream/ReadTests.cs | 550 ++--- .../FileSystem/FileStream/Tests.cs | 598 ++--- .../FileSystem/FileStream/WriteTests.cs | 568 ++--- .../FileStreamFactory/ExceptionTests.cs | 290 +-- .../FileStreamFactory/SafeFileHandleTests.cs | 312 +-- .../FileSystem/FileStreamFactory/Tests.cs | 364 ++-- .../CreateAsSymbolicLinkTests.cs | 89 +- .../FileSystemInfo/ExceptionTests.cs | 149 +- .../FileSystemInfo/ResolveLinkTargetTests.cs | 353 +-- .../FileSystem/FileSystemInfo/Tests.cs | 312 +-- .../FileSystemInfo/UnixFileModeTests.cs | 145 +- .../FileSystemTests.Extensibility.cs | 2 - .../EnableRaisingEventsTests.cs | 100 +- .../FileSystemWatcher/FilterTests.cs | 222 +- .../IncludeSubdirectoriesTests.cs | 188 +- .../FileSystemWatcher/NotifyFiltersTests.cs | 1066 ++++----- .../FileSystem/FileSystemWatcher/PathTests.cs | 2 - .../FileSystem/FileSystemWatcher/Tests.cs | 330 +-- .../FileSystemWatcher/WaitForChangedTests.cs | 176 +- .../ExceptionTests.cs | 244 +-- .../FileSystemWatcherFactory/Tests.cs | 156 +- .../FileSystem/Path/GetRelativePathTests.cs | 1 - .../RandomSystem/GuidTests.cs | 1 - .../TestHelpers/FileTestHelper.cs | 160 +- .../TestHelpers/Polyfills/StringExtensions.cs | 6 +- 158 files changed, 23732 insertions(+), 23749 deletions(-) diff --git a/Feature.Flags.props b/Feature.Flags.props index 3ae357710..79753d072 100644 --- a/Feature.Flags.props +++ b/Feature.Flags.props @@ -2,7 +2,10 @@ $(DefineConstants);NETFRAMEWORK - $(DefineConstants);FEATURE_FILESYSTEM_ASYNC;FEATURE_FILESYSTEM_ENUMERATION_OPTIONS;FEATURE_PATH_JOIN;FEATURE_PATH_RELATIVE;FEATURE_SPAN;FEATURE_GUID_PARSE;FEATURE_VALUETASK;FEATURE_COMPRESSION_OVERWRITE;FEATURE_COMPRESSION_ADVANCED + + $(DefineConstants);FEATURE_FILESYSTEM_ASYNC;FEATURE_FILESYSTEM_ENUMERATION_OPTIONS;FEATURE_PATH_JOIN;FEATURE_PATH_RELATIVE;FEATURE_SPAN;FEATURE_GUID_PARSE;FEATURE_VALUETASK;FEATURE_COMPRESSION_OVERWRITE;FEATURE_COMPRESSION_ADVANCED + $(DefineConstants);FEATURE_FILESYSTEM_STREAM_OPTIONS;FEATURE_FILESYSTEM_LINK;FEATURE_PATH_ADVANCED;FEATURE_FILE_MOVETO_OVERWRITE;FEATURE_RANDOM_ADVANCED;FEATURE_FILESYSTEMWATCHER_ADVANCED;FEATURE_EXCEPTION_HRESULT $(DefineConstants);FEATURE_ZIPFILE_NET7;FEATURE_FILESYSTEM_NET7;FEATURE_FILESYSTEM_SAFEFILEHANDLE;FEATURE_FILESYSTEM_UNIXFILEMODE;FEATURE_GUID_FORMATPROVIDER diff --git a/Source/Testably.Abstractions.AccessControl/AccessControlHelpers.cs b/Source/Testably.Abstractions.AccessControl/AccessControlHelpers.cs index e1ddebbc1..30e61eb9a 100644 --- a/Source/Testably.Abstractions.AccessControl/AccessControlHelpers.cs +++ b/Source/Testably.Abstractions.AccessControl/AccessControlHelpers.cs @@ -1,50 +1,51 @@ -using System; -using System.IO; - -namespace Testably.Abstractions; - -internal static class AccessControlHelpers -{ - public const string AccessControl = nameof(AccessControl); - - public static TFileSystemInfo ThrowIfMissing( - this TFileSystemInfo fileSystemInfo) - where TFileSystemInfo : IFileSystemInfo - { - if (!fileSystemInfo.Exists) - { - if (fileSystemInfo is IDirectoryInfo directoryInfo) - { - throw new DirectoryNotFoundException( - $"Could not find a part of the path '{directoryInfo.FullName}'.") - { -#if FEATURE_EXCEPTION_HRESULT - HResult = -2147024893 -#endif - }; - } - - if (fileSystemInfo is IFileInfo fileInfo) - { - throw new FileNotFoundException($"Could not find file '{fileInfo.FullName}'.") - { -#if FEATURE_EXCEPTION_HRESULT - HResult = -2147024894 -#endif - }; - } - } - - return fileSystemInfo; - } - public static IDirectoryInfo ThrowIfParentMissing( - this IDirectoryInfo fileSystemInfo) - { - if (fileSystemInfo.Parent?.Exists != true) - { - throw new UnauthorizedAccessException(); - } - - return fileSystemInfo; - } -} +using System; +using System.IO; + +namespace Testably.Abstractions; + +internal static class AccessControlHelpers +{ + public const string AccessControl = nameof(AccessControl); + + public static TFileSystemInfo ThrowIfMissing( + this TFileSystemInfo fileSystemInfo) + where TFileSystemInfo : IFileSystemInfo + { + if (!fileSystemInfo.Exists) + { + if (fileSystemInfo is IDirectoryInfo directoryInfo) + { + throw new DirectoryNotFoundException( + $"Could not find a part of the path '{directoryInfo.FullName}'.") + { +#if FEATURE_EXCEPTION_HRESULT + HResult = -2147024893 +#endif + }; + } + + if (fileSystemInfo is IFileInfo fileInfo) + { + throw new FileNotFoundException($"Could not find file '{fileInfo.FullName}'.") + { +#if FEATURE_EXCEPTION_HRESULT + HResult = -2147024894 +#endif + }; + } + } + + return fileSystemInfo; + } + + public static IDirectoryInfo ThrowIfParentMissing( + this IDirectoryInfo fileSystemInfo) + { + if (fileSystemInfo.Parent?.Exists != true) + { + throw new UnauthorizedAccessException(); + } + + return fileSystemInfo; + } +} diff --git a/Source/Testably.Abstractions.AccessControl/DirectoryAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/DirectoryAclExtensions.cs index 557ff91b2..cf5738989 100644 --- a/Source/Testably.Abstractions.AccessControl/DirectoryAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/DirectoryAclExtensions.cs @@ -1,90 +1,90 @@ -using System; -using System.IO; -using System.Security.AccessControl; - -namespace Testably.Abstractions; - -/// -/// ACL (access control list) extension methods for . -/// -public static class DirectoryAclExtensions -{ - /// - [SupportedOSPlatform("windows")] - public static void CreateDirectory(this IDirectory directory, - string path, - DirectorySecurity directorySecurity) - { - IDirectoryInfo directoryInfo = - directory.FileSystem.DirectoryInfo.New(path); - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) - { - di.Create(directorySecurity); - } - else - { - _ = directorySecurity ?? throw new ArgumentNullException(nameof(directorySecurity)); - directoryInfo.ThrowIfParentMissing(); - directoryInfo.Create(); - directoryInfo.Extensibility.StoreMetadata(AccessControlHelpers.AccessControl, - directorySecurity); - } - } - - /// - [SupportedOSPlatform("windows")] - public static DirectorySecurity GetAccessControl( - this IDirectory directory, string path) - { - IDirectoryInfo directoryInfo = - directory.FileSystem.DirectoryInfo.New(path); - directoryInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) - ? di.GetAccessControl() - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static DirectorySecurity GetAccessControl( - this IDirectory directory, - string path, - AccessControlSections includeSections) - { - IDirectoryInfo directoryInfo = - directory.FileSystem.DirectoryInfo.New(path); - directoryInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) - ? di.GetAccessControl(includeSections) - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static void SetAccessControl(this IDirectory directory, - string path, - DirectorySecurity directorySecurity) - { - IDirectoryInfo directoryInfo = - directory.FileSystem.DirectoryInfo.New(path); - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) - { - di.SetAccessControl(directorySecurity); - } - else - { - extensibility.StoreMetadata(AccessControlHelpers.AccessControl, - directorySecurity); - } - } -} +using System; +using System.IO; +using System.Security.AccessControl; + +namespace Testably.Abstractions; + +/// +/// ACL (access control list) extension methods for . +/// +public static class DirectoryAclExtensions +{ + /// + [SupportedOSPlatform("windows")] + public static void CreateDirectory(this IDirectory directory, + string path, + DirectorySecurity directorySecurity) + { + IDirectoryInfo directoryInfo = + directory.FileSystem.DirectoryInfo.New(path); + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) + { + di.Create(directorySecurity); + } + else + { + _ = directorySecurity ?? throw new ArgumentNullException(nameof(directorySecurity)); + directoryInfo.ThrowIfParentMissing(); + directoryInfo.Create(); + directoryInfo.Extensibility.StoreMetadata(AccessControlHelpers.AccessControl, + directorySecurity); + } + } + + /// + [SupportedOSPlatform("windows")] + public static DirectorySecurity GetAccessControl( + this IDirectory directory, string path) + { + IDirectoryInfo directoryInfo = + directory.FileSystem.DirectoryInfo.New(path); + directoryInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) + ? di.GetAccessControl() + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static DirectorySecurity GetAccessControl( + this IDirectory directory, + string path, + AccessControlSections includeSections) + { + IDirectoryInfo directoryInfo = + directory.FileSystem.DirectoryInfo.New(path); + directoryInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) + ? di.GetAccessControl(includeSections) + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static void SetAccessControl(this IDirectory directory, + string path, + DirectorySecurity directorySecurity) + { + IDirectoryInfo directoryInfo = + directory.FileSystem.DirectoryInfo.New(path); + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) + { + di.SetAccessControl(directorySecurity); + } + else + { + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, + directorySecurity); + } + } +} diff --git a/Source/Testably.Abstractions.AccessControl/DirectoryInfoAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/DirectoryInfoAclExtensions.cs index db5bdcee1..d9edba740 100644 --- a/Source/Testably.Abstractions.AccessControl/DirectoryInfoAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/DirectoryInfoAclExtensions.cs @@ -1,79 +1,79 @@ -using System; -using System.IO; -using System.Security.AccessControl; - -namespace Testably.Abstractions; - -/// -/// ACL (access control list) extension methods for . -/// -public static class DirectoryInfoAclExtensions -{ - /// - [SupportedOSPlatform("windows")] - public static void Create(this IDirectoryInfo directoryInfo, - DirectorySecurity directorySecurity) - { - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) - { - di.Create(directorySecurity); - } - else - { - _ = directorySecurity ?? throw new ArgumentNullException(nameof(directorySecurity)); - directoryInfo.ThrowIfParentMissing(); - directoryInfo.Create(); - directoryInfo.Extensibility.StoreMetadata(AccessControlHelpers.AccessControl, - directorySecurity); - } - } - - /// - [SupportedOSPlatform("windows")] - public static DirectorySecurity GetAccessControl( - this IDirectoryInfo directoryInfo) - { - directoryInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) - ? di.GetAccessControl() - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static DirectorySecurity GetAccessControl( - this IDirectoryInfo directoryInfo, - AccessControlSections includeSections) - { - directoryInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) - ? di.GetAccessControl(includeSections) - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static void SetAccessControl(this IDirectoryInfo directoryInfo, - DirectorySecurity directorySecurity) - { - IFileSystemExtensibility extensibility = - directoryInfo.Extensibility; - if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) - { - di.SetAccessControl(directorySecurity); - } - else - { - extensibility.StoreMetadata(AccessControlHelpers.AccessControl, - directorySecurity); - } - } -} +using System; +using System.IO; +using System.Security.AccessControl; + +namespace Testably.Abstractions; + +/// +/// ACL (access control list) extension methods for . +/// +public static class DirectoryInfoAclExtensions +{ + /// + [SupportedOSPlatform("windows")] + public static void Create(this IDirectoryInfo directoryInfo, + DirectorySecurity directorySecurity) + { + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) + { + di.Create(directorySecurity); + } + else + { + _ = directorySecurity ?? throw new ArgumentNullException(nameof(directorySecurity)); + directoryInfo.ThrowIfParentMissing(); + directoryInfo.Create(); + directoryInfo.Extensibility.StoreMetadata(AccessControlHelpers.AccessControl, + directorySecurity); + } + } + + /// + [SupportedOSPlatform("windows")] + public static DirectorySecurity GetAccessControl( + this IDirectoryInfo directoryInfo) + { + directoryInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) + ? di.GetAccessControl() + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static DirectorySecurity GetAccessControl( + this IDirectoryInfo directoryInfo, + AccessControlSections includeSections) + { + directoryInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out DirectoryInfo? di) + ? di.GetAccessControl(includeSections) + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new DirectorySecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static void SetAccessControl(this IDirectoryInfo directoryInfo, + DirectorySecurity directorySecurity) + { + IFileSystemExtensibility extensibility = + directoryInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out DirectoryInfo? di)) + { + di.SetAccessControl(directorySecurity); + } + else + { + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, + directorySecurity); + } + } +} diff --git a/Source/Testably.Abstractions.AccessControl/FileAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/FileAclExtensions.cs index 201bfef89..5b9934ef0 100644 --- a/Source/Testably.Abstractions.AccessControl/FileAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/FileAclExtensions.cs @@ -1,62 +1,62 @@ -using System.IO; -using System.Security.AccessControl; - -namespace Testably.Abstractions; - -/// -/// ACL (access control list) extension methods for . -/// -public static class FileAclExtensions -{ - /// - [SupportedOSPlatform("windows")] - public static FileSecurity GetAccessControl( - this IFile file, string path) - { - IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); - fileInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - fileInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out FileInfo? fi) - ? fi.GetAccessControl() - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new FileSecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static FileSecurity GetAccessControl( - this IFile file, - string path, - AccessControlSections includeSections) - { - IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); - fileInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - fileInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out FileInfo? fi) - ? fi.GetAccessControl(includeSections) - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new FileSecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static void SetAccessControl(this IFile file, - string path, - FileSecurity fileSecurity) - { - IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); - IFileSystemExtensibility extensibility = - fileInfo.Extensibility; - if (extensibility.TryGetWrappedInstance(out FileInfo? fi)) - { - fi.SetAccessControl(fileSecurity); - } - else - { - extensibility.StoreMetadata(AccessControlHelpers.AccessControl, - fileSecurity); - } - } -} +using System.IO; +using System.Security.AccessControl; + +namespace Testably.Abstractions; + +/// +/// ACL (access control list) extension methods for . +/// +public static class FileAclExtensions +{ + /// + [SupportedOSPlatform("windows")] + public static FileSecurity GetAccessControl( + this IFile file, string path) + { + IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); + fileInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out FileInfo? fi) + ? fi.GetAccessControl() + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new FileSecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static FileSecurity GetAccessControl( + this IFile file, + string path, + AccessControlSections includeSections) + { + IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); + fileInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out FileInfo? fi) + ? fi.GetAccessControl(includeSections) + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new FileSecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static void SetAccessControl(this IFile file, + string path, + FileSecurity fileSecurity) + { + IFileInfo fileInfo = file.FileSystem.FileInfo.New(path); + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out FileInfo? fi)) + { + fi.SetAccessControl(fileSecurity); + } + else + { + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, + fileSecurity); + } + } +} diff --git a/Source/Testably.Abstractions.AccessControl/FileInfoAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/FileInfoAclExtensions.cs index 251bb1fbc..980541213 100644 --- a/Source/Testably.Abstractions.AccessControl/FileInfoAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/FileInfoAclExtensions.cs @@ -1,57 +1,57 @@ -using System.IO; -using System.Security.AccessControl; - -namespace Testably.Abstractions; - -/// -/// ACL (access control list) extension methods for . -/// -public static class FileInfoAclExtensions -{ - /// - [SupportedOSPlatform("windows")] - public static FileSecurity GetAccessControl( - this IFileInfo fileInfo) - { - fileInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - fileInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out FileInfo? fi) - ? fi.GetAccessControl() - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new FileSecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static FileSecurity GetAccessControl( - this IFileInfo fileInfo, - AccessControlSections includeSections) - { - fileInfo.ThrowIfMissing(); - IFileSystemExtensibility extensibility = - fileInfo.Extensibility; - return extensibility.TryGetWrappedInstance(out FileInfo? fi) - ? fi.GetAccessControl(includeSections) - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new FileSecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static void SetAccessControl(this IFileInfo fileInfo, - FileSecurity fileSecurity) - { - IFileSystemExtensibility extensibility = - fileInfo.Extensibility; - if (extensibility.TryGetWrappedInstance(out FileInfo? fi)) - { - fi.SetAccessControl(fileSecurity); - } - else - { - extensibility.StoreMetadata(AccessControlHelpers.AccessControl, - fileSecurity); - } - } -} +using System.IO; +using System.Security.AccessControl; + +namespace Testably.Abstractions; + +/// +/// ACL (access control list) extension methods for . +/// +public static class FileInfoAclExtensions +{ + /// + [SupportedOSPlatform("windows")] + public static FileSecurity GetAccessControl( + this IFileInfo fileInfo) + { + fileInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out FileInfo? fi) + ? fi.GetAccessControl() + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new FileSecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static FileSecurity GetAccessControl( + this IFileInfo fileInfo, + AccessControlSections includeSections) + { + fileInfo.ThrowIfMissing(); + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + return extensibility.TryGetWrappedInstance(out FileInfo? fi) + ? fi.GetAccessControl(includeSections) + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new FileSecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static void SetAccessControl(this IFileInfo fileInfo, + FileSecurity fileSecurity) + { + IFileSystemExtensibility extensibility = + fileInfo.Extensibility; + if (extensibility.TryGetWrappedInstance(out FileInfo? fi)) + { + fi.SetAccessControl(fileSecurity); + } + else + { + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, + fileSecurity); + } + } +} diff --git a/Source/Testably.Abstractions.AccessControl/FileStreamAclExtensions.cs b/Source/Testably.Abstractions.AccessControl/FileStreamAclExtensions.cs index 54aa7b80d..8e0fa3b0a 100644 --- a/Source/Testably.Abstractions.AccessControl/FileStreamAclExtensions.cs +++ b/Source/Testably.Abstractions.AccessControl/FileStreamAclExtensions.cs @@ -1,40 +1,40 @@ -using System.IO; -using System.Security.AccessControl; - -namespace Testably.Abstractions; - -/// -/// ACL (access control list) extension methods for . -/// -public static class FileStreamAclExtensions -{ - /// - [SupportedOSPlatform("windows")] - public static FileSecurity GetAccessControl(this FileSystemStream fileStream) - { - IFileSystemExtensibility extensibility = - fileStream.Extensibility; - return extensibility.TryGetWrappedInstance(out FileStream? fs) - ? fs.GetAccessControl() - : extensibility.RetrieveMetadata( - AccessControlHelpers.AccessControl) ?? new FileSecurity(); - } - - /// - [SupportedOSPlatform("windows")] - public static void SetAccessControl(this FileSystemStream fileStream, - FileSecurity fileSecurity) - { - IFileSystemExtensibility extensibility = - fileStream.Extensibility; - if (extensibility.TryGetWrappedInstance(out FileStream? fs)) - { - fs.SetAccessControl(fileSecurity); - } - else - { - extensibility.StoreMetadata(AccessControlHelpers.AccessControl, - fileSecurity); - } - } -} +using System.IO; +using System.Security.AccessControl; + +namespace Testably.Abstractions; + +/// +/// ACL (access control list) extension methods for . +/// +public static class FileStreamAclExtensions +{ + /// + [SupportedOSPlatform("windows")] + public static FileSecurity GetAccessControl(this FileSystemStream fileStream) + { + IFileSystemExtensibility extensibility = + fileStream.Extensibility; + return extensibility.TryGetWrappedInstance(out FileStream? fs) + ? fs.GetAccessControl() + : extensibility.RetrieveMetadata( + AccessControlHelpers.AccessControl) ?? new FileSecurity(); + } + + /// + [SupportedOSPlatform("windows")] + public static void SetAccessControl(this FileSystemStream fileStream, + FileSecurity fileSecurity) + { + IFileSystemExtensibility extensibility = + fileStream.Extensibility; + if (extensibility.TryGetWrappedInstance(out FileStream? fs)) + { + fs.SetAccessControl(fileSecurity); + } + else + { + extensibility.StoreMetadata(AccessControlHelpers.AccessControl, + fileSecurity); + } + } +} diff --git a/Source/Testably.Abstractions.Compression/IZipArchive.cs b/Source/Testably.Abstractions.Compression/IZipArchive.cs index a98808f55..831f1d93a 100644 --- a/Source/Testably.Abstractions.Compression/IZipArchive.cs +++ b/Source/Testably.Abstractions.Compression/IZipArchive.cs @@ -1,48 +1,48 @@ -using System; -using System.Collections.ObjectModel; -using System.IO.Compression; - -namespace Testably.Abstractions; - -/// -public interface IZipArchive : IFileSystemEntity, IDisposable -{ -#if FEATURE_ZIPFILE_NET7 - /// - string Comment { get; set; } -#endif - - /// - ReadOnlyCollection Entries { get; } - - /// - ZipArchiveMode Mode { get; } - - /// - IZipArchiveEntry CreateEntry(string entryName); - - /// - IZipArchiveEntry CreateEntry(string entryName, CompressionLevel compressionLevel); - - /// - IZipArchiveEntry CreateEntryFromFile(string sourceFileName, - string entryName); - - /// - IZipArchiveEntry CreateEntryFromFile(string sourceFileName, - string entryName, - CompressionLevel compressionLevel); - - /// - void ExtractToDirectory(string destinationDirectoryName); - -#if FEATURE_COMPRESSION_ADVANCED - /// - void ExtractToDirectory(string destinationDirectoryName, - bool overwriteFiles); -#endif - - /// - IZipArchiveEntry? GetEntry(string entryName); -} +using System; +using System.Collections.ObjectModel; +using System.IO.Compression; + +namespace Testably.Abstractions; + +/// +public interface IZipArchive : IFileSystemEntity, IDisposable +{ +#if FEATURE_ZIPFILE_NET7 + /// + string Comment { get; set; } +#endif + + /// + ReadOnlyCollection Entries { get; } + + /// + ZipArchiveMode Mode { get; } + + /// + IZipArchiveEntry CreateEntry(string entryName); + + /// + IZipArchiveEntry CreateEntry(string entryName, CompressionLevel compressionLevel); + + /// + IZipArchiveEntry CreateEntryFromFile(string sourceFileName, + string entryName); + + /// + IZipArchiveEntry CreateEntryFromFile(string sourceFileName, + string entryName, + CompressionLevel compressionLevel); + + /// + void ExtractToDirectory(string destinationDirectoryName); + +#if FEATURE_COMPRESSION_ADVANCED + /// + void ExtractToDirectory(string destinationDirectoryName, + bool overwriteFiles); +#endif + + /// + IZipArchiveEntry? GetEntry(string entryName); +} diff --git a/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs b/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs index d062ec5bb..38497648d 100644 --- a/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs +++ b/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs @@ -1,60 +1,60 @@ -using System; -using System.IO; -using System.IO.Compression; - -namespace Testably.Abstractions; - -/// -public interface IZipArchiveEntry : IFileSystemEntity -{ - /// - IZipArchive Archive { get; } - -#if FEATURE_ZIPFILE_NET7 - /// - string Comment { get; set; } -#endif - - /// - long CompressedLength { get; } - -#if FEATURE_COMPRESSION_ADVANCED - /// - uint Crc32 { get; } -#endif - -#if FEATURE_COMPRESSION_ADVANCED - /// - int ExternalAttributes { get; set; } -#endif - - /// - string FullName { get; } - -#if FEATURE_ZIPFILE_NET7 - /// - bool IsEncrypted { get; } -#endif - - /// - DateTimeOffset LastWriteTime { get; set; } - - /// - long Length { get; } - - /// - string Name { get; } - - /// - void Delete(); - - /// - void ExtractToFile(string destinationFileName); - - /// - void ExtractToFile(string destinationFileName, - bool overwrite); - - /// - Stream Open(); -} +using System; +using System.IO; +using System.IO.Compression; + +namespace Testably.Abstractions; + +/// +public interface IZipArchiveEntry : IFileSystemEntity +{ + /// + IZipArchive Archive { get; } + +#if FEATURE_ZIPFILE_NET7 + /// + string Comment { get; set; } +#endif + + /// + long CompressedLength { get; } + +#if FEATURE_COMPRESSION_ADVANCED + /// + uint Crc32 { get; } +#endif + +#if FEATURE_COMPRESSION_ADVANCED + /// + int ExternalAttributes { get; set; } +#endif + + /// + string FullName { get; } + +#if FEATURE_ZIPFILE_NET7 + /// + bool IsEncrypted { get; } +#endif + + /// + DateTimeOffset LastWriteTime { get; set; } + + /// + long Length { get; } + + /// + string Name { get; } + + /// + void Delete(); + + /// + void ExtractToFile(string destinationFileName); + + /// + void ExtractToFile(string destinationFileName, + bool overwrite); + + /// + Stream Open(); +} diff --git a/Source/Testably.Abstractions.Compression/IZipArchiveFactory.cs b/Source/Testably.Abstractions.Compression/IZipArchiveFactory.cs index 67676b8e8..eab3663b0 100644 --- a/Source/Testably.Abstractions.Compression/IZipArchiveFactory.cs +++ b/Source/Testably.Abstractions.Compression/IZipArchiveFactory.cs @@ -1,24 +1,24 @@ -using System.IO; -using System.IO.Compression; -using System.Text; - -namespace Testably.Abstractions; - -/// -public interface IZipArchiveFactory : IFileSystemEntity -{ - /// - IZipArchive New(Stream stream); - - /// - IZipArchive New(Stream stream, ZipArchiveMode mode); - - /// - IZipArchive New(Stream stream, ZipArchiveMode mode, bool leaveOpen); - - /// - IZipArchive New(Stream stream, - ZipArchiveMode mode, - bool leaveOpen, - Encoding? entryNameEncoding); -} +using System.IO; +using System.IO.Compression; +using System.Text; + +namespace Testably.Abstractions; + +/// +public interface IZipArchiveFactory : IFileSystemEntity +{ + /// + IZipArchive New(Stream stream); + + /// + IZipArchive New(Stream stream, ZipArchiveMode mode); + + /// + IZipArchive New(Stream stream, ZipArchiveMode mode, bool leaveOpen); + + /// + IZipArchive New(Stream stream, + ZipArchiveMode mode, + bool leaveOpen, + Encoding? entryNameEncoding); +} diff --git a/Source/Testably.Abstractions.Compression/IZipFile.cs b/Source/Testably.Abstractions.Compression/IZipFile.cs index 4fc7f592a..6e6f30891 100644 --- a/Source/Testably.Abstractions.Compression/IZipFile.cs +++ b/Source/Testably.Abstractions.Compression/IZipFile.cs @@ -1,61 +1,61 @@ -using System.IO.Compression; -using System.Text; - -namespace Testably.Abstractions; - -/// -public interface IZipFile : IFileSystemEntity -{ - /// - void CreateFromDirectory(string sourceDirectoryName, - string destinationArchiveFileName); - - /// - void CreateFromDirectory(string sourceDirectoryName, - string destinationArchiveFileName, - CompressionLevel compressionLevel, - bool includeBaseDirectory); - - /// - void CreateFromDirectory(string sourceDirectoryName, - string destinationArchiveFileName, - CompressionLevel compressionLevel, - bool includeBaseDirectory, - Encoding entryNameEncoding); - - /// - void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName); - -#if FEATURE_COMPRESSION_OVERWRITE - /// - void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName, - bool overwriteFiles); -#endif - - /// - void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName, - Encoding? entryNameEncoding); - -#if FEATURE_COMPRESSION_OVERWRITE - /// - void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName, - Encoding? entryNameEncoding, - bool overwriteFiles); -#endif - - /// - IZipArchive Open(string archiveFileName, - ZipArchiveMode mode); - - /// - IZipArchive Open(string archiveFileName, - ZipArchiveMode mode, - Encoding? entryNameEncoding); - - /// - IZipArchive OpenRead(string archiveFileName); -} +using System.IO.Compression; +using System.Text; + +namespace Testably.Abstractions; + +/// +public interface IZipFile : IFileSystemEntity +{ + /// + void CreateFromDirectory(string sourceDirectoryName, + string destinationArchiveFileName); + + /// + void CreateFromDirectory(string sourceDirectoryName, + string destinationArchiveFileName, + CompressionLevel compressionLevel, + bool includeBaseDirectory); + + /// + void CreateFromDirectory(string sourceDirectoryName, + string destinationArchiveFileName, + CompressionLevel compressionLevel, + bool includeBaseDirectory, + Encoding entryNameEncoding); + + /// + void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName); + +#if FEATURE_COMPRESSION_OVERWRITE + /// + void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName, + bool overwriteFiles); +#endif + + /// + void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName, + Encoding? entryNameEncoding); + +#if FEATURE_COMPRESSION_OVERWRITE + /// + void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName, + Encoding? entryNameEncoding, + bool overwriteFiles); +#endif + + /// + IZipArchive Open(string archiveFileName, + ZipArchiveMode mode); + + /// + IZipArchive Open(string archiveFileName, + ZipArchiveMode mode, + Encoding? entryNameEncoding); + + /// + IZipArchive OpenRead(string archiveFileName); +} diff --git a/Source/Testably.Abstractions.Compression/Internal/ZipUtilities.cs b/Source/Testably.Abstractions.Compression/Internal/ZipUtilities.cs index dcb87f566..f6ba96973 100644 --- a/Source/Testably.Abstractions.Compression/Internal/ZipUtilities.cs +++ b/Source/Testably.Abstractions.Compression/Internal/ZipUtilities.cs @@ -1,264 +1,264 @@ -using System; -using System.IO; -using System.IO.Compression; -using System.Text; - -namespace Testably.Abstractions.Internal; - -internal static class ZipUtilities -{ - internal static IZipArchiveEntry CreateEntryFromFile( - IZipArchive destination, - string sourceFileName, - string entryName, - CompressionLevel? compressionLevel = null) - { - if (sourceFileName == null) - { - throw new ArgumentNullException(nameof(sourceFileName)); - } - - using (FileSystemStream fs = destination.FileSystem.FileStream.New(sourceFileName, - FileMode.Open, FileAccess.Read, FileShare.Read)) - { - IZipArchiveEntry entry = compressionLevel.HasValue - ? destination.CreateEntry(entryName, compressionLevel.Value) - : destination.CreateEntry(entryName); - - DateTime lastWrite = - destination.FileSystem.File.GetLastWriteTime(sourceFileName); - - if (lastWrite.Year < 1980 || lastWrite.Year > 2107) - { - lastWrite = new DateTime(1980, 1, 1, 0, 0, 0); - } - - entry.LastWriteTime = lastWrite; - - using (Stream es = entry.Open()) - { - fs.CopyTo(es); - } - - return entry; - } - } - - /// - /// Create a ZipArchive from the files and directories in . - /// - /// - /// - /// - internal static void CreateFromDirectory(IFileSystem fileSystem, - string sourceDirectoryName, - string destinationArchiveFileName, - CompressionLevel? compressionLevel = null, - bool includeBaseDirectory = false, - Encoding? entryNameEncoding = null) - - { - sourceDirectoryName = fileSystem.Path.GetFullPath(sourceDirectoryName); - destinationArchiveFileName = - fileSystem.Path.GetFullPath(destinationArchiveFileName); - - using (ZipArchive archive = Open(fileSystem, destinationArchiveFileName, - ZipArchiveMode.Create, entryNameEncoding)) - { - bool directoryIsEmpty = true; - - IDirectoryInfo di = - fileSystem.DirectoryInfo.New(sourceDirectoryName); - - string basePath = di.FullName; - - if (includeBaseDirectory && di.Parent != null) - { - basePath = di.Parent.FullName; - } - - foreach (IFileSystemInfo file in di - .EnumerateFileSystemInfos("*", SearchOption.AllDirectories)) - { - directoryIsEmpty = false; - - if (file is IFileInfo fileInfo) - { - string entryName = file.FullName - .Substring(basePath.Length + 1) - .Replace("\\", "/"); - ZipArchiveEntry entry = compressionLevel.HasValue - ? archive.CreateEntry(entryName, compressionLevel.Value) - : archive.CreateEntry(entryName); - using Stream stream = entry.Open(); - fileInfo.OpenRead().CopyTo(stream); - } - else if (file is IDirectoryInfo directoryInfo && - directoryInfo.GetFileSystemInfos().Length == 0) - { - #pragma warning disable CA1845 - string entryName = file.FullName.Substring(basePath.Length + 1) + "/"; - #pragma warning restore CA1845 - archive.CreateEntry(entryName); - } - } - - if (includeBaseDirectory && directoryIsEmpty) - { - string entryName = di.Name + "/"; - archive.CreateEntry(entryName); - } - } - } - - internal static void ExtractRelativeToDirectory(this IZipArchiveEntry source, - string destinationDirectoryName, - bool overwrite) - { - if (destinationDirectoryName == null) - { - throw new ArgumentNullException(nameof(destinationDirectoryName)); - } - - string fileDestinationPath = - source.FileSystem.Path.Combine(destinationDirectoryName, - source.FullName.TrimStart( - source.FileSystem.Path.DirectorySeparatorChar, - source.FileSystem.Path.AltDirectorySeparatorChar)); - string? directoryPath = - source.FileSystem.Path.GetDirectoryName(fileDestinationPath); - if (directoryPath != null && - !source.FileSystem.Directory.Exists(directoryPath)) - { - source.FileSystem.Directory.CreateDirectory(directoryPath); - } - - if (source.FullName.EndsWith("/")) - { - if (source.Length != 0) - { - throw new IOException( - "Zip entry name ends in directory separator character but contains data."); - } - - source.FileSystem.Directory.CreateDirectory(fileDestinationPath); - } - else - { - ExtractToFile(source, fileDestinationPath, overwrite); - } - } - - /// - /// Extract the archive at to the - /// . - /// - internal static void ExtractToDirectory(IFileSystem fileSystem, - string sourceArchiveFileName, - string destinationDirectoryName, - Encoding? entryNameEncoding = null, - bool overwriteFiles = false) - { - if (sourceArchiveFileName == null) - { - throw new ArgumentNullException(nameof(sourceArchiveFileName)); - } - - using (ZipArchive archive = Open(fileSystem, sourceArchiveFileName, - ZipArchiveMode.Read, - entryNameEncoding)) - { - ZipArchiveWrapper wrappedArchive = new(fileSystem, archive); - foreach (ZipArchiveEntry entry in archive.Entries) - { - IZipArchiveEntry wrappedEntry = ZipArchiveEntryWrapper.New( - fileSystem, wrappedArchive, entry); - ExtractRelativeToDirectory(wrappedEntry, destinationDirectoryName, - overwriteFiles); - } - } - } - - internal static void ExtractToFile(IZipArchiveEntry source, - string destinationFileName, bool overwrite) - { - FileMode mode = overwrite ? FileMode.Create : FileMode.CreateNew; - - using (FileSystemStream fileStream = source.FileSystem.FileStream - .New(destinationFileName, mode, FileAccess.Write, FileShare.None)) - { - using (Stream entryStream = source.Open()) - { - entryStream.CopyTo(fileStream); - } - } - - try - { - source.FileSystem.File.SetLastWriteTime(destinationFileName, - source.LastWriteTime.DateTime); - } - catch - { - // some OSes might not support setting the last write time - // the extraction should not fail because of that - } - } - - /// - /// Opens a ZipArchive on the specified in the specified - /// . - /// - /// - /// - /// - internal static ZipArchive Open(IFileSystem fileSystem, - string archiveFileName, - ZipArchiveMode mode, - Encoding? entryNameEncoding = null) - { - FileMode fileMode; - FileAccess access; - FileShare fileShare; - - switch (mode) - { - case ZipArchiveMode.Read: - fileMode = FileMode.Open; - access = FileAccess.Read; - fileShare = FileShare.Read; - break; - - case ZipArchiveMode.Create: - fileMode = FileMode.CreateNew; - access = FileAccess.Write; - fileShare = FileShare.None; - break; - - case ZipArchiveMode.Update: - fileMode = FileMode.OpenOrCreate; - access = FileAccess.ReadWrite; - fileShare = FileShare.None; - break; - - default: - throw new ArgumentOutOfRangeException(nameof(mode)); - } - - FileSystemStream fs = fileSystem.FileStream.New(archiveFileName, fileMode, - access, fileShare, bufferSize: 0x1000); - - try - { - return new ZipArchive(fs, mode, leaveOpen: false, - entryNameEncoding: entryNameEncoding); - } - catch - { - fs.Dispose(); - throw; - } - } -} +using System; +using System.IO; +using System.IO.Compression; +using System.Text; + +namespace Testably.Abstractions.Internal; + +internal static class ZipUtilities +{ + internal static IZipArchiveEntry CreateEntryFromFile( + IZipArchive destination, + string sourceFileName, + string entryName, + CompressionLevel? compressionLevel = null) + { + if (sourceFileName == null) + { + throw new ArgumentNullException(nameof(sourceFileName)); + } + + using (FileSystemStream fs = destination.FileSystem.FileStream.New(sourceFileName, + FileMode.Open, FileAccess.Read, FileShare.Read)) + { + IZipArchiveEntry entry = compressionLevel.HasValue + ? destination.CreateEntry(entryName, compressionLevel.Value) + : destination.CreateEntry(entryName); + + DateTime lastWrite = + destination.FileSystem.File.GetLastWriteTime(sourceFileName); + + if (lastWrite.Year < 1980 || lastWrite.Year > 2107) + { + lastWrite = new DateTime(1980, 1, 1, 0, 0, 0); + } + + entry.LastWriteTime = lastWrite; + + using (Stream es = entry.Open()) + { + fs.CopyTo(es); + } + + return entry; + } + } + + /// + /// Create a ZipArchive from the files and directories in . + /// + /// + /// + /// + internal static void CreateFromDirectory(IFileSystem fileSystem, + string sourceDirectoryName, + string destinationArchiveFileName, + CompressionLevel? compressionLevel = null, + bool includeBaseDirectory = false, + Encoding? entryNameEncoding = null) + + { + sourceDirectoryName = fileSystem.Path.GetFullPath(sourceDirectoryName); + destinationArchiveFileName = + fileSystem.Path.GetFullPath(destinationArchiveFileName); + + using (ZipArchive archive = Open(fileSystem, destinationArchiveFileName, + ZipArchiveMode.Create, entryNameEncoding)) + { + bool directoryIsEmpty = true; + + IDirectoryInfo di = + fileSystem.DirectoryInfo.New(sourceDirectoryName); + + string basePath = di.FullName; + + if (includeBaseDirectory && di.Parent != null) + { + basePath = di.Parent.FullName; + } + + foreach (IFileSystemInfo file in di + .EnumerateFileSystemInfos("*", SearchOption.AllDirectories)) + { + directoryIsEmpty = false; + + if (file is IFileInfo fileInfo) + { + string entryName = file.FullName + .Substring(basePath.Length + 1) + .Replace("\\", "/"); + ZipArchiveEntry entry = compressionLevel.HasValue + ? archive.CreateEntry(entryName, compressionLevel.Value) + : archive.CreateEntry(entryName); + using Stream stream = entry.Open(); + fileInfo.OpenRead().CopyTo(stream); + } + else if (file is IDirectoryInfo directoryInfo && + directoryInfo.GetFileSystemInfos().Length == 0) + { + #pragma warning disable CA1845 + string entryName = file.FullName.Substring(basePath.Length + 1) + "/"; + #pragma warning restore CA1845 + archive.CreateEntry(entryName); + } + } + + if (includeBaseDirectory && directoryIsEmpty) + { + string entryName = di.Name + "/"; + archive.CreateEntry(entryName); + } + } + } + + internal static void ExtractRelativeToDirectory(this IZipArchiveEntry source, + string destinationDirectoryName, + bool overwrite) + { + if (destinationDirectoryName == null) + { + throw new ArgumentNullException(nameof(destinationDirectoryName)); + } + + string fileDestinationPath = + source.FileSystem.Path.Combine(destinationDirectoryName, + source.FullName.TrimStart( + source.FileSystem.Path.DirectorySeparatorChar, + source.FileSystem.Path.AltDirectorySeparatorChar)); + string? directoryPath = + source.FileSystem.Path.GetDirectoryName(fileDestinationPath); + if (directoryPath != null && + !source.FileSystem.Directory.Exists(directoryPath)) + { + source.FileSystem.Directory.CreateDirectory(directoryPath); + } + + if (source.FullName.EndsWith("/")) + { + if (source.Length != 0) + { + throw new IOException( + "Zip entry name ends in directory separator character but contains data."); + } + + source.FileSystem.Directory.CreateDirectory(fileDestinationPath); + } + else + { + ExtractToFile(source, fileDestinationPath, overwrite); + } + } + + /// + /// Extract the archive at to the + /// . + /// + internal static void ExtractToDirectory(IFileSystem fileSystem, + string sourceArchiveFileName, + string destinationDirectoryName, + Encoding? entryNameEncoding = null, + bool overwriteFiles = false) + { + if (sourceArchiveFileName == null) + { + throw new ArgumentNullException(nameof(sourceArchiveFileName)); + } + + using (ZipArchive archive = Open(fileSystem, sourceArchiveFileName, + ZipArchiveMode.Read, + entryNameEncoding)) + { + ZipArchiveWrapper wrappedArchive = new(fileSystem, archive); + foreach (ZipArchiveEntry entry in archive.Entries) + { + IZipArchiveEntry wrappedEntry = ZipArchiveEntryWrapper.New( + fileSystem, wrappedArchive, entry); + ExtractRelativeToDirectory(wrappedEntry, destinationDirectoryName, + overwriteFiles); + } + } + } + + internal static void ExtractToFile(IZipArchiveEntry source, + string destinationFileName, bool overwrite) + { + FileMode mode = overwrite ? FileMode.Create : FileMode.CreateNew; + + using (FileSystemStream fileStream = source.FileSystem.FileStream + .New(destinationFileName, mode, FileAccess.Write, FileShare.None)) + { + using (Stream entryStream = source.Open()) + { + entryStream.CopyTo(fileStream); + } + } + + try + { + source.FileSystem.File.SetLastWriteTime(destinationFileName, + source.LastWriteTime.DateTime); + } + catch + { + // some OSes might not support setting the last write time + // the extraction should not fail because of that + } + } + + /// + /// Opens a ZipArchive on the specified in the specified + /// . + /// + /// + /// + /// + internal static ZipArchive Open(IFileSystem fileSystem, + string archiveFileName, + ZipArchiveMode mode, + Encoding? entryNameEncoding = null) + { + FileMode fileMode; + FileAccess access; + FileShare fileShare; + + switch (mode) + { + case ZipArchiveMode.Read: + fileMode = FileMode.Open; + access = FileAccess.Read; + fileShare = FileShare.Read; + break; + + case ZipArchiveMode.Create: + fileMode = FileMode.CreateNew; + access = FileAccess.Write; + fileShare = FileShare.None; + break; + + case ZipArchiveMode.Update: + fileMode = FileMode.OpenOrCreate; + access = FileAccess.ReadWrite; + fileShare = FileShare.None; + break; + + default: + throw new ArgumentOutOfRangeException(nameof(mode)); + } + + FileSystemStream fs = fileSystem.FileStream.New(archiveFileName, fileMode, + access, fileShare, bufferSize: 0x1000); + + try + { + return new ZipArchive(fs, mode, leaveOpen: false, + entryNameEncoding: entryNameEncoding); + } + catch + { + fs.Dispose(); + throw; + } + } +} diff --git a/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs b/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs index 384440dd0..495ef5fd5 100644 --- a/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs +++ b/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs @@ -1,127 +1,127 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.IO.Compression; -using Testably.Abstractions.Internal; - -namespace Testably.Abstractions; - -internal sealed class ZipArchiveEntryWrapper : IZipArchiveEntry -{ - private readonly ZipArchiveEntry _instance; - - public ZipArchiveEntryWrapper(IFileSystem fileSystem, - IZipArchive archive, - ZipArchiveEntry instance) - { - _instance = instance; - FileSystem = fileSystem; - Archive = archive; - } - - #region IZipArchiveEntry Members - - /// - public IZipArchive Archive { get; } - -#if FEATURE_ZIPFILE_NET7 - /// - public string Comment - { - get => _instance.Comment; - set => _instance.Comment = value; - } -#endif - - /// - public long CompressedLength - => _instance.CompressedLength; - -#if FEATURE_COMPRESSION_ADVANCED - /// - public uint Crc32 - => _instance.Crc32; -#endif - -#if FEATURE_COMPRESSION_ADVANCED - /// - public int ExternalAttributes - { - get => _instance.ExternalAttributes; - set => _instance.ExternalAttributes = value; - } -#endif - - /// - public IFileSystem FileSystem { get; } - - /// - public string FullName - => _instance.FullName; - -#if FEATURE_ZIPFILE_NET7 - /// - public bool IsEncrypted - => _instance.IsEncrypted; -#endif - - /// - public DateTimeOffset LastWriteTime - { - get => _instance.LastWriteTime; - set => _instance.LastWriteTime = value; - } - - /// - public long Length - => _instance.Length; - - /// - public string Name - => _instance.Name; - - /// - public void Delete() - => _instance.Delete(); - - /// - public void ExtractToFile(string destinationFileName) - => Execute.WhenRealFileSystem(FileSystem, - () => _instance.ExtractToFile(destinationFileName), - () => ExtractToFile(destinationFileName, false)); - - /// - public void ExtractToFile(string destinationFileName, bool overwrite) - { - if (destinationFileName == null) - { - throw new ArgumentNullException(nameof(destinationFileName)); - } - - Execute.WhenRealFileSystem(FileSystem, - () => _instance.ExtractToFile(destinationFileName, overwrite), - () => ZipUtilities.ExtractToFile(this, destinationFileName, overwrite)); - } - - /// - public Stream Open() - => _instance.Open(); - - #endregion - - /// - public override string ToString() - => _instance.ToString(); - - [return: NotNullIfNotNull("instance")] - internal static IZipArchiveEntry? New(IFileSystem fileSystem, IZipArchive archive, - ZipArchiveEntry? instance) - { - if (instance == null) - { - return null; - } - - return new ZipArchiveEntryWrapper(fileSystem, archive, instance); - } -} +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.IO.Compression; +using Testably.Abstractions.Internal; + +namespace Testably.Abstractions; + +internal sealed class ZipArchiveEntryWrapper : IZipArchiveEntry +{ + private readonly ZipArchiveEntry _instance; + + public ZipArchiveEntryWrapper(IFileSystem fileSystem, + IZipArchive archive, + ZipArchiveEntry instance) + { + _instance = instance; + FileSystem = fileSystem; + Archive = archive; + } + + #region IZipArchiveEntry Members + + /// + public IZipArchive Archive { get; } + +#if FEATURE_ZIPFILE_NET7 + /// + public string Comment + { + get => _instance.Comment; + set => _instance.Comment = value; + } +#endif + + /// + public long CompressedLength + => _instance.CompressedLength; + +#if FEATURE_COMPRESSION_ADVANCED + /// + public uint Crc32 + => _instance.Crc32; +#endif + +#if FEATURE_COMPRESSION_ADVANCED + /// + public int ExternalAttributes + { + get => _instance.ExternalAttributes; + set => _instance.ExternalAttributes = value; + } +#endif + + /// + public IFileSystem FileSystem { get; } + + /// + public string FullName + => _instance.FullName; + +#if FEATURE_ZIPFILE_NET7 + /// + public bool IsEncrypted + => _instance.IsEncrypted; +#endif + + /// + public DateTimeOffset LastWriteTime + { + get => _instance.LastWriteTime; + set => _instance.LastWriteTime = value; + } + + /// + public long Length + => _instance.Length; + + /// + public string Name + => _instance.Name; + + /// + public void Delete() + => _instance.Delete(); + + /// + public void ExtractToFile(string destinationFileName) + => Execute.WhenRealFileSystem(FileSystem, + () => _instance.ExtractToFile(destinationFileName), + () => ExtractToFile(destinationFileName, false)); + + /// + public void ExtractToFile(string destinationFileName, bool overwrite) + { + if (destinationFileName == null) + { + throw new ArgumentNullException(nameof(destinationFileName)); + } + + Execute.WhenRealFileSystem(FileSystem, + () => _instance.ExtractToFile(destinationFileName, overwrite), + () => ZipUtilities.ExtractToFile(this, destinationFileName, overwrite)); + } + + /// + public Stream Open() + => _instance.Open(); + + #endregion + + /// + public override string ToString() + => _instance.ToString(); + + [return: NotNullIfNotNull("instance")] + internal static IZipArchiveEntry? New(IFileSystem fileSystem, IZipArchive archive, + ZipArchiveEntry? instance) + { + if (instance == null) + { + return null; + } + + return new ZipArchiveEntryWrapper(fileSystem, archive, instance); + } +} diff --git a/Source/Testably.Abstractions.Compression/ZipArchiveFactory.cs b/Source/Testably.Abstractions.Compression/ZipArchiveFactory.cs index 673116f56..47166ee9e 100644 --- a/Source/Testably.Abstractions.Compression/ZipArchiveFactory.cs +++ b/Source/Testably.Abstractions.Compression/ZipArchiveFactory.cs @@ -1,40 +1,40 @@ -using System.IO; -using System.IO.Compression; -using System.Text; - -namespace Testably.Abstractions; - -internal sealed class ZipArchiveFactory : IZipArchiveFactory -{ - public ZipArchiveFactory(IFileSystem fileSystem) - { - FileSystem = fileSystem; - } - - #region IZipArchiveFactory Members - - /// - public IFileSystem FileSystem { get; } - - /// - public IZipArchive New(Stream stream) - => new ZipArchiveWrapper(FileSystem, new ZipArchive(stream)); - - /// - public IZipArchive New(Stream stream, ZipArchiveMode mode) - => new ZipArchiveWrapper(FileSystem, new ZipArchive(stream, mode)); - - /// - public IZipArchive New(Stream stream, ZipArchiveMode mode, bool leaveOpen) - => new ZipArchiveWrapper(FileSystem, new ZipArchive(stream, mode, leaveOpen)); - - /// - public IZipArchive New(Stream stream, - ZipArchiveMode mode, - bool leaveOpen, - Encoding? entryNameEncoding) - => new ZipArchiveWrapper(FileSystem, - new ZipArchive(stream, mode, leaveOpen, entryNameEncoding)); - - #endregion -} +using System.IO; +using System.IO.Compression; +using System.Text; + +namespace Testably.Abstractions; + +internal sealed class ZipArchiveFactory : IZipArchiveFactory +{ + public ZipArchiveFactory(IFileSystem fileSystem) + { + FileSystem = fileSystem; + } + + #region IZipArchiveFactory Members + + /// + public IFileSystem FileSystem { get; } + + /// + public IZipArchive New(Stream stream) + => new ZipArchiveWrapper(FileSystem, new ZipArchive(stream)); + + /// + public IZipArchive New(Stream stream, ZipArchiveMode mode) + => new ZipArchiveWrapper(FileSystem, new ZipArchive(stream, mode)); + + /// + public IZipArchive New(Stream stream, ZipArchiveMode mode, bool leaveOpen) + => new ZipArchiveWrapper(FileSystem, new ZipArchive(stream, mode, leaveOpen)); + + /// + public IZipArchive New(Stream stream, + ZipArchiveMode mode, + bool leaveOpen, + Encoding? entryNameEncoding) + => new ZipArchiveWrapper(FileSystem, + new ZipArchive(stream, mode, leaveOpen, entryNameEncoding)); + + #endregion +} diff --git a/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs b/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs index c2343815a..fb2ee99c0 100644 --- a/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs +++ b/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs @@ -1,132 +1,132 @@ -using System; -using System.Collections.ObjectModel; -using System.IO.Compression; -using System.Linq; -using Testably.Abstractions.Internal; - -namespace Testably.Abstractions; - -internal sealed class ZipArchiveWrapper : IZipArchive -{ - private readonly ZipArchive _instance; - - public ZipArchiveWrapper(IFileSystem fileSystem, ZipArchive instance) - { - _instance = instance; - FileSystem = fileSystem; - } - - #region IZipArchive Members - -#if FEATURE_ZIPFILE_NET7 - /// - public string Comment - { - get => _instance.Comment; - set => _instance.Comment = value; - } -#endif - - /// - public ReadOnlyCollection Entries - => MapToZipArchiveEntries(_instance.Entries); - - /// - public IFileSystem FileSystem { get; } - - /// - public ZipArchiveMode Mode - => _instance.Mode; - - /// - public void Dispose() - => _instance.Dispose(); - - /// - public IZipArchiveEntry CreateEntry(string entryName) - => ZipArchiveEntryWrapper.New(FileSystem, this, _instance.CreateEntry(entryName)); - - /// - public IZipArchiveEntry CreateEntry(string entryName, - CompressionLevel compressionLevel) - => ZipArchiveEntryWrapper.New(FileSystem, this, - _instance.CreateEntry(entryName, compressionLevel)); - - /// - public IZipArchiveEntry CreateEntryFromFile(string sourceFileName, string entryName) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipArchiveEntryWrapper.New(FileSystem, this, - _instance.CreateEntryFromFile( - sourceFileName, - entryName)), - () => ZipUtilities.CreateEntryFromFile(this, - sourceFileName, - entryName)); - - /// - public IZipArchiveEntry CreateEntryFromFile(string sourceFileName, string entryName, - CompressionLevel compressionLevel) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipArchiveEntryWrapper.New(FileSystem, this, - _instance.CreateEntryFromFile( - sourceFileName, - entryName, - compressionLevel)), - () => ZipUtilities.CreateEntryFromFile(this, - sourceFileName, - entryName, - compressionLevel)); - - /// - public void ExtractToDirectory(string destinationDirectoryName) - { - if (destinationDirectoryName == null) - { - throw new ArgumentNullException(nameof(destinationDirectoryName)); - } - - Execute.WhenRealFileSystem(FileSystem, - () => _instance.ExtractToDirectory(destinationDirectoryName), - () => - { - foreach (IZipArchiveEntry entry in Entries) - { - entry.ExtractRelativeToDirectory(destinationDirectoryName, false); - } - }); - } - -#if FEATURE_COMPRESSION_ADVANCED - /// - public void ExtractToDirectory(string destinationDirectoryName, bool overwriteFiles) - { - if (destinationDirectoryName == null) - { - throw new ArgumentNullException(nameof(destinationDirectoryName)); - } - - Execute.WhenRealFileSystem(FileSystem, - () => _instance.ExtractToDirectory(destinationDirectoryName, overwriteFiles), - () => - { - foreach (IZipArchiveEntry entry in Entries) - { - entry.ExtractRelativeToDirectory(destinationDirectoryName, - overwriteFiles); - } - }); - } -#endif - - /// - public IZipArchiveEntry? GetEntry(string entryName) - => ZipArchiveEntryWrapper.New(FileSystem, this, _instance.GetEntry(entryName)); - - #endregion - - private ReadOnlyCollection MapToZipArchiveEntries( - ReadOnlyCollection zipArchiveEntries) - => new(zipArchiveEntries - .Select(e => ZipArchiveEntryWrapper.New(FileSystem, this, e)) - .ToList()); -} +using System; +using System.Collections.ObjectModel; +using System.IO.Compression; +using System.Linq; +using Testably.Abstractions.Internal; + +namespace Testably.Abstractions; + +internal sealed class ZipArchiveWrapper : IZipArchive +{ + private readonly ZipArchive _instance; + + public ZipArchiveWrapper(IFileSystem fileSystem, ZipArchive instance) + { + _instance = instance; + FileSystem = fileSystem; + } + + #region IZipArchive Members + +#if FEATURE_ZIPFILE_NET7 + /// + public string Comment + { + get => _instance.Comment; + set => _instance.Comment = value; + } +#endif + + /// + public ReadOnlyCollection Entries + => MapToZipArchiveEntries(_instance.Entries); + + /// + public IFileSystem FileSystem { get; } + + /// + public ZipArchiveMode Mode + => _instance.Mode; + + /// + public void Dispose() + => _instance.Dispose(); + + /// + public IZipArchiveEntry CreateEntry(string entryName) + => ZipArchiveEntryWrapper.New(FileSystem, this, _instance.CreateEntry(entryName)); + + /// + public IZipArchiveEntry CreateEntry(string entryName, + CompressionLevel compressionLevel) + => ZipArchiveEntryWrapper.New(FileSystem, this, + _instance.CreateEntry(entryName, compressionLevel)); + + /// + public IZipArchiveEntry CreateEntryFromFile(string sourceFileName, string entryName) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipArchiveEntryWrapper.New(FileSystem, this, + _instance.CreateEntryFromFile( + sourceFileName, + entryName)), + () => ZipUtilities.CreateEntryFromFile(this, + sourceFileName, + entryName)); + + /// + public IZipArchiveEntry CreateEntryFromFile(string sourceFileName, string entryName, + CompressionLevel compressionLevel) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipArchiveEntryWrapper.New(FileSystem, this, + _instance.CreateEntryFromFile( + sourceFileName, + entryName, + compressionLevel)), + () => ZipUtilities.CreateEntryFromFile(this, + sourceFileName, + entryName, + compressionLevel)); + + /// + public void ExtractToDirectory(string destinationDirectoryName) + { + if (destinationDirectoryName == null) + { + throw new ArgumentNullException(nameof(destinationDirectoryName)); + } + + Execute.WhenRealFileSystem(FileSystem, + () => _instance.ExtractToDirectory(destinationDirectoryName), + () => + { + foreach (IZipArchiveEntry entry in Entries) + { + entry.ExtractRelativeToDirectory(destinationDirectoryName, false); + } + }); + } + +#if FEATURE_COMPRESSION_ADVANCED + /// + public void ExtractToDirectory(string destinationDirectoryName, bool overwriteFiles) + { + if (destinationDirectoryName == null) + { + throw new ArgumentNullException(nameof(destinationDirectoryName)); + } + + Execute.WhenRealFileSystem(FileSystem, + () => _instance.ExtractToDirectory(destinationDirectoryName, overwriteFiles), + () => + { + foreach (IZipArchiveEntry entry in Entries) + { + entry.ExtractRelativeToDirectory(destinationDirectoryName, + overwriteFiles); + } + }); + } +#endif + + /// + public IZipArchiveEntry? GetEntry(string entryName) + => ZipArchiveEntryWrapper.New(FileSystem, this, _instance.GetEntry(entryName)); + + #endregion + + private ReadOnlyCollection MapToZipArchiveEntries( + ReadOnlyCollection zipArchiveEntries) + => new(zipArchiveEntries + .Select(e => ZipArchiveEntryWrapper.New(FileSystem, this, e)) + .ToList()); +} diff --git a/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs b/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs index 5ef07098a..21b0c4bea 100644 --- a/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs +++ b/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs @@ -1,165 +1,165 @@ -using System.IO.Compression; -using System.Text; -using Testably.Abstractions.Internal; - -namespace Testably.Abstractions; - -internal sealed class ZipFileWrapper : IZipFile -{ - public ZipFileWrapper(IFileSystem fileSystem) - { - FileSystem = fileSystem; - } - - #region IZipFile Members - - /// - public IFileSystem FileSystem { get; } - - /// - public void CreateFromDirectory(string sourceDirectoryName, - string destinationArchiveFileName) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.CreateFromDirectory( - sourceDirectoryName, - destinationArchiveFileName), - () => ZipUtilities.CreateFromDirectory( - FileSystem, - sourceDirectoryName, - destinationArchiveFileName)); - - /// - public void CreateFromDirectory(string sourceDirectoryName, - string destinationArchiveFileName, - CompressionLevel compressionLevel, - bool includeBaseDirectory) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.CreateFromDirectory( - sourceDirectoryName, - destinationArchiveFileName, - compressionLevel, - includeBaseDirectory), - () => ZipUtilities.CreateFromDirectory( - FileSystem, - sourceDirectoryName, - destinationArchiveFileName, - compressionLevel, - includeBaseDirectory)); - - /// - public void CreateFromDirectory(string sourceDirectoryName, - string destinationArchiveFileName, - CompressionLevel compressionLevel, - bool includeBaseDirectory, - Encoding entryNameEncoding) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.CreateFromDirectory( - sourceDirectoryName, - destinationArchiveFileName, - compressionLevel, - includeBaseDirectory, - entryNameEncoding), - () => ZipUtilities.CreateFromDirectory( - FileSystem, - sourceDirectoryName, - destinationArchiveFileName, - compressionLevel, - includeBaseDirectory, - entryNameEncoding)); - - /// - public void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.ExtractToDirectory( - sourceArchiveFileName, - destinationDirectoryName), - () => ZipUtilities.ExtractToDirectory( - FileSystem, - sourceArchiveFileName, - destinationDirectoryName)); - -#if FEATURE_COMPRESSION_OVERWRITE - /// - public void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName, - bool overwriteFiles) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.ExtractToDirectory( - sourceArchiveFileName, - destinationDirectoryName, - overwriteFiles), - () => ZipUtilities.ExtractToDirectory( - FileSystem, - sourceArchiveFileName, - destinationDirectoryName, - overwriteFiles: overwriteFiles)); -#endif - - /// - public void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName, - Encoding? entryNameEncoding) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.ExtractToDirectory( - sourceArchiveFileName, - destinationDirectoryName, - entryNameEncoding), - () => ZipUtilities.ExtractToDirectory( - FileSystem, - sourceArchiveFileName, - destinationDirectoryName, - entryNameEncoding: entryNameEncoding)); - -#if FEATURE_COMPRESSION_OVERWRITE - /// - public void ExtractToDirectory(string sourceArchiveFileName, - string destinationDirectoryName, - Encoding? entryNameEncoding, - bool overwriteFiles) - => Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.ExtractToDirectory( - sourceArchiveFileName, - destinationDirectoryName, - entryNameEncoding, - overwriteFiles), - () => ZipUtilities.ExtractToDirectory( - FileSystem, - sourceArchiveFileName, - destinationDirectoryName, - entryNameEncoding, - overwriteFiles)); -#endif - - /// - public IZipArchive Open(string archiveFileName, ZipArchiveMode mode) - => new ZipArchiveWrapper(FileSystem, - Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.Open(archiveFileName, mode), - () => ZipUtilities.Open(FileSystem, - archiveFileName, - mode))); - - /// - public IZipArchive Open(string archiveFileName, - ZipArchiveMode mode, - Encoding? entryNameEncoding) - => new ZipArchiveWrapper(FileSystem, - Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.Open(archiveFileName, mode, entryNameEncoding), - () => ZipUtilities.Open(FileSystem, - archiveFileName, - mode, - entryNameEncoding))); - - /// - public IZipArchive OpenRead(string archiveFileName) - => new ZipArchiveWrapper(FileSystem, - Execute.WhenRealFileSystem(FileSystem, - () => ZipFile.OpenRead(archiveFileName), - () => ZipUtilities.Open(FileSystem, - archiveFileName, - ZipArchiveMode.Read))); - - #endregion -} +using System.IO.Compression; +using System.Text; +using Testably.Abstractions.Internal; + +namespace Testably.Abstractions; + +internal sealed class ZipFileWrapper : IZipFile +{ + public ZipFileWrapper(IFileSystem fileSystem) + { + FileSystem = fileSystem; + } + + #region IZipFile Members + + /// + public IFileSystem FileSystem { get; } + + /// + public void CreateFromDirectory(string sourceDirectoryName, + string destinationArchiveFileName) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.CreateFromDirectory( + sourceDirectoryName, + destinationArchiveFileName), + () => ZipUtilities.CreateFromDirectory( + FileSystem, + sourceDirectoryName, + destinationArchiveFileName)); + + /// + public void CreateFromDirectory(string sourceDirectoryName, + string destinationArchiveFileName, + CompressionLevel compressionLevel, + bool includeBaseDirectory) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.CreateFromDirectory( + sourceDirectoryName, + destinationArchiveFileName, + compressionLevel, + includeBaseDirectory), + () => ZipUtilities.CreateFromDirectory( + FileSystem, + sourceDirectoryName, + destinationArchiveFileName, + compressionLevel, + includeBaseDirectory)); + + /// + public void CreateFromDirectory(string sourceDirectoryName, + string destinationArchiveFileName, + CompressionLevel compressionLevel, + bool includeBaseDirectory, + Encoding entryNameEncoding) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.CreateFromDirectory( + sourceDirectoryName, + destinationArchiveFileName, + compressionLevel, + includeBaseDirectory, + entryNameEncoding), + () => ZipUtilities.CreateFromDirectory( + FileSystem, + sourceDirectoryName, + destinationArchiveFileName, + compressionLevel, + includeBaseDirectory, + entryNameEncoding)); + + /// + public void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.ExtractToDirectory( + sourceArchiveFileName, + destinationDirectoryName), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + sourceArchiveFileName, + destinationDirectoryName)); + +#if FEATURE_COMPRESSION_OVERWRITE + /// + public void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName, + bool overwriteFiles) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.ExtractToDirectory( + sourceArchiveFileName, + destinationDirectoryName, + overwriteFiles), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + sourceArchiveFileName, + destinationDirectoryName, + overwriteFiles: overwriteFiles)); +#endif + + /// + public void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName, + Encoding? entryNameEncoding) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.ExtractToDirectory( + sourceArchiveFileName, + destinationDirectoryName, + entryNameEncoding), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + sourceArchiveFileName, + destinationDirectoryName, + entryNameEncoding: entryNameEncoding)); + +#if FEATURE_COMPRESSION_OVERWRITE + /// + public void ExtractToDirectory(string sourceArchiveFileName, + string destinationDirectoryName, + Encoding? entryNameEncoding, + bool overwriteFiles) + => Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.ExtractToDirectory( + sourceArchiveFileName, + destinationDirectoryName, + entryNameEncoding, + overwriteFiles), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + sourceArchiveFileName, + destinationDirectoryName, + entryNameEncoding, + overwriteFiles)); +#endif + + /// + public IZipArchive Open(string archiveFileName, ZipArchiveMode mode) + => new ZipArchiveWrapper(FileSystem, + Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.Open(archiveFileName, mode), + () => ZipUtilities.Open(FileSystem, + archiveFileName, + mode))); + + /// + public IZipArchive Open(string archiveFileName, + ZipArchiveMode mode, + Encoding? entryNameEncoding) + => new ZipArchiveWrapper(FileSystem, + Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.Open(archiveFileName, mode, entryNameEncoding), + () => ZipUtilities.Open(FileSystem, + archiveFileName, + mode, + entryNameEncoding))); + + /// + public IZipArchive OpenRead(string archiveFileName) + => new ZipArchiveWrapper(FileSystem, + Execute.WhenRealFileSystem(FileSystem, + () => ZipFile.OpenRead(archiveFileName), + () => ZipUtilities.Open(FileSystem, + archiveFileName, + ZipArchiveMode.Read))); + + #endregion +} diff --git a/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs b/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs index c69b74cdb..d28b6f7b1 100644 --- a/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs +++ b/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs @@ -1,7 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using Testably.Abstractions.FileSystem; - #if !NETSTANDARD2_0 using System; #endif diff --git a/Source/Testably.Abstractions.Testing/FileSystem/ChangeHandler.cs b/Source/Testably.Abstractions.Testing/FileSystem/ChangeHandler.cs index 1f28756aa..356b69789 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/ChangeHandler.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/ChangeHandler.cs @@ -1,71 +1,71 @@ -using System; -using System.IO; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class ChangeHandler : IInterceptionHandler, - INotificationHandler -{ - private readonly Notification.INotificationFactory - _changeOccurredCallbacks = Notification.CreateFactory(); - - private readonly Notification.INotificationFactory - _changeOccurringCallbacks = Notification.CreateFactory(); - - private readonly MockFileSystem _mockFileSystem; - - public ChangeHandler(MockFileSystem mockFileSystem) - { - _mockFileSystem = mockFileSystem; - } - - #region IInterceptionHandler Members - - /// - public IFileSystem FileSystem => _mockFileSystem; - - /// - public MockFileSystem Event( - Action interceptionCallback, - Func? predicate = null) - { - _changeOccurringCallbacks.RegisterCallback(interceptionCallback, predicate); - return _mockFileSystem; - } - - #endregion - - #region INotificationHandler Members - - /// - public Notification.IAwaitableCallback OnEvent( - Action? notificationCallback = null, - Func? predicate = null) - => _changeOccurredCallbacks.RegisterCallback(notificationCallback, predicate); - - #endregion - - internal void NotifyCompletedChange(ChangeDescription? fileSystemChange) - { - if (fileSystemChange != null) - { - _changeOccurredCallbacks.InvokeCallbacks(fileSystemChange); - } - } - - internal ChangeDescription NotifyPendingChange(WatcherChangeTypes changeType, - FileSystemTypes fileSystemType, - NotifyFilters notifyFilters, - IStorageLocation location, - IStorageLocation? oldLocation = - null) - { - ChangeDescription fileSystemChange = - new(changeType, fileSystemType, notifyFilters, location, oldLocation); - _changeOccurringCallbacks.InvokeCallbacks(fileSystemChange); - return fileSystemChange; - } -} +using System; +using System.IO; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class ChangeHandler : IInterceptionHandler, + INotificationHandler +{ + private readonly Notification.INotificationFactory + _changeOccurredCallbacks = Notification.CreateFactory(); + + private readonly Notification.INotificationFactory + _changeOccurringCallbacks = Notification.CreateFactory(); + + private readonly MockFileSystem _mockFileSystem; + + public ChangeHandler(MockFileSystem mockFileSystem) + { + _mockFileSystem = mockFileSystem; + } + + #region IInterceptionHandler Members + + /// + public IFileSystem FileSystem => _mockFileSystem; + + /// + public MockFileSystem Event( + Action interceptionCallback, + Func? predicate = null) + { + _changeOccurringCallbacks.RegisterCallback(interceptionCallback, predicate); + return _mockFileSystem; + } + + #endregion + + #region INotificationHandler Members + + /// + public Notification.IAwaitableCallback OnEvent( + Action? notificationCallback = null, + Func? predicate = null) + => _changeOccurredCallbacks.RegisterCallback(notificationCallback, predicate); + + #endregion + + internal void NotifyCompletedChange(ChangeDescription? fileSystemChange) + { + if (fileSystemChange != null) + { + _changeOccurredCallbacks.InvokeCallbacks(fileSystemChange); + } + } + + internal ChangeDescription NotifyPendingChange(WatcherChangeTypes changeType, + FileSystemTypes fileSystemType, + NotifyFilters notifyFilters, + IStorageLocation location, + IStorageLocation? oldLocation = + null) + { + ChangeDescription fileSystemChange = + new(changeType, fileSystemType, notifyFilters, location, oldLocation); + _changeOccurringCallbacks.InvokeCallbacks(fileSystemChange); + return fileSystemChange; + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DefaultAccessControlStrategy.cs b/Source/Testably.Abstractions.Testing/FileSystem/DefaultAccessControlStrategy.cs index 6c0b81b85..e1cd38d57 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DefaultAccessControlStrategy.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DefaultAccessControlStrategy.cs @@ -1,27 +1,27 @@ -using System; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// Default implementation of an which uses a callback to determine if access -/// should be granted. -/// -public class DefaultAccessControlStrategy : IAccessControlStrategy -{ - private readonly Func _callback; - - /// - /// Initializes a new instance of which takes a callback to determine if - /// access should be granted. - /// - public DefaultAccessControlStrategy( - Func callback) - { - _callback = callback ?? throw new ArgumentNullException(nameof(callback)); - } - - /// - public bool IsAccessGranted(string fullPath, - IFileSystemExtensibility extensibility) - => _callback(fullPath, extensibility); -} +using System; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// Default implementation of an which uses a callback to determine if access +/// should be granted. +/// +public class DefaultAccessControlStrategy : IAccessControlStrategy +{ + private readonly Func _callback; + + /// + /// Initializes a new instance of which takes a callback to determine if + /// access should be granted. + /// + public DefaultAccessControlStrategy( + Func callback) + { + _callback = callback ?? throw new ArgumentNullException(nameof(callback)); + } + + /// + public bool IsAccessGranted(string fullPath, + IFileSystemExtensibility extensibility) + => _callback(fullPath, extensibility); +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoFactoryMock.cs index 322f3d5d9..845256553 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoFactoryMock.cs @@ -1,41 +1,41 @@ -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Testably.Abstractions.Testing.Helpers; - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class DirectoryInfoFactoryMock : IDirectoryInfoFactory -{ - private readonly MockFileSystem _fileSystem; - - internal DirectoryInfoFactoryMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - #region IDirectoryInfoFactory Members - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public IDirectoryInfo New(string path) - { - return DirectoryInfoMock.New( - _fileSystem.Storage.GetLocation(path - .EnsureValidArgument(_fileSystem, nameof(path))), - _fileSystem); - } - - /// - [return: NotNullIfNotNull("directoryInfo")] - public IDirectoryInfo? Wrap(DirectoryInfo? directoryInfo) - => DirectoryInfoMock.New( - _fileSystem.Storage.GetLocation( - directoryInfo?.FullName, - directoryInfo?.ToString()), - _fileSystem); - - #endregion -} +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class DirectoryInfoFactoryMock : IDirectoryInfoFactory +{ + private readonly MockFileSystem _fileSystem; + + internal DirectoryInfoFactoryMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + #region IDirectoryInfoFactory Members + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public IDirectoryInfo New(string path) + { + return DirectoryInfoMock.New( + _fileSystem.Storage.GetLocation(path + .EnsureValidArgument(_fileSystem, nameof(path))), + _fileSystem); + } + + /// + [return: NotNullIfNotNull("directoryInfo")] + public IDirectoryInfo? Wrap(DirectoryInfo? directoryInfo) + => DirectoryInfoMock.New( + _fileSystem.Storage.GetLocation( + directoryInfo?.FullName, + directoryInfo?.ToString()), + _fileSystem); + + #endregion +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs index a638fe8fc..b24fb5f29 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs @@ -1,283 +1,283 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// A mocked directory in the . -/// -internal sealed class DirectoryInfoMock - : FileSystemInfoMock, IDirectoryInfo -{ - private readonly MockFileSystem _fileSystem; - - private DirectoryInfoMock(IStorageLocation location, - MockFileSystem fileSystem) - : base(fileSystem, location, FileSystemTypes.Directory) - { - _fileSystem = fileSystem; - } - - #region IDirectoryInfo Members - - /// - public override bool Exists - => base.Exists && FileSystemType == FileSystemTypes.Directory; - - /// - public IDirectoryInfo? Parent - => New(Location.GetParent(), _fileSystem); - - /// - public IDirectoryInfo Root - => New(_fileSystem.Storage.GetLocation(string.Empty.PrefixRoot()), - _fileSystem); - - /// - public void Create() - { - FullName.EnsureValidFormat(_fileSystem); - - Container = _fileSystem.Storage.GetOrCreateContainer(Location, - InMemoryContainer.NewDirectory, - Extensibility); - - ResetCache(!Execute.IsNetFramework); - } - - /// - public IDirectoryInfo CreateSubdirectory(string path) - { - DirectoryInfoMock directory = New( - _fileSystem.Storage.GetLocation( - _fileSystem.Path.Combine(FullName, path - .EnsureValidFormat(_fileSystem, nameof(path), - Execute.IsWindows && !Execute.IsNetFramework))), - _fileSystem); - directory.Create(); - return directory; - } - - /// - public override void Delete() - { - if (!_fileSystem.Storage.DeleteContainer(Location)) - { - throw ExceptionFactory.DirectoryNotFound(Location.FullPath); - } - - ResetCache(!Execute.IsNetFramework); - } - - /// - public void Delete(bool recursive) - { - if (!_fileSystem.Storage.DeleteContainer( - _fileSystem.Storage.GetLocation(FullName), recursive)) - { - throw ExceptionFactory.DirectoryNotFound(FullName); - } - - ResetCache(!Execute.IsNetFramework); - } - - /// - public IEnumerable EnumerateDirectories() - => EnumerateDirectories( - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly); - - /// - public IEnumerable - EnumerateDirectories(string searchPattern) - => EnumerateDirectories(searchPattern, SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateDirectories( - string searchPattern, SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.Directory, - FullName, - searchPattern, - EnumerationOptionsHelper.FromSearchOption(searchOption)) - .Select(location => New(location, _fileSystem)); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IEnumerable EnumerateDirectories( - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateInternal(FileSystemTypes.Directory, - FullName, - searchPattern, - enumerationOptions) - .Select(location => New(location, _fileSystem)); -#endif - - /// - public IEnumerable EnumerateFiles() - => EnumerateFiles( - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateFiles(string searchPattern) - => EnumerateFiles(searchPattern, SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateFiles( - string searchPattern, SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.File, - FullName, - searchPattern, - EnumerationOptionsHelper.FromSearchOption(searchOption)) - .Select(location => FileInfoMock.New(location, _fileSystem)); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IEnumerable EnumerateFiles( - string searchPattern, EnumerationOptions enumerationOptions) - => EnumerateInternal(FileSystemTypes.File, - FullName, - searchPattern, - enumerationOptions) - .Select(location => FileInfoMock.New(location, _fileSystem)); -#endif - - /// - public IEnumerable EnumerateFileSystemInfos() - => EnumerateFileSystemInfos( - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly); - - /// - public IEnumerable - EnumerateFileSystemInfos(string searchPattern) - => EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateFileSystemInfos( - string searchPattern, SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.DirectoryOrFile, - FullName, - searchPattern, - EnumerationOptionsHelper.FromSearchOption(searchOption)) - .Select(location => FileSystemInfoMock.New(location, _fileSystem)); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IEnumerable EnumerateFileSystemInfos( - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateInternal(FileSystemTypes.DirectoryOrFile, - FullName, - searchPattern, - enumerationOptions) - .Select(location => FileSystemInfoMock.New(location, _fileSystem)); -#endif - - /// - public IDirectoryInfo[] GetDirectories() - => EnumerateDirectories().ToArray(); - - /// - public IDirectoryInfo[] GetDirectories(string searchPattern) - => EnumerateDirectories(searchPattern).ToArray(); - - /// - public IDirectoryInfo[] GetDirectories( - string searchPattern, SearchOption searchOption) - => EnumerateDirectories(searchPattern, searchOption).ToArray(); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IDirectoryInfo[] GetDirectories( - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateDirectories(searchPattern, enumerationOptions).ToArray(); -#endif - - /// - public IFileInfo[] GetFiles() - => EnumerateFiles().ToArray(); - - /// - public IFileInfo[] GetFiles(string searchPattern) - => EnumerateFiles(searchPattern).ToArray(); - - /// - public IFileInfo[] GetFiles(string searchPattern, - SearchOption searchOption) - => EnumerateFiles(searchPattern, searchOption).ToArray(); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IFileInfo[] GetFiles(string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateFiles(searchPattern, enumerationOptions).ToArray(); -#endif - - /// - public IFileSystemInfo[] GetFileSystemInfos() - => EnumerateFileSystemInfos().ToArray(); - - /// - public IFileSystemInfo[] GetFileSystemInfos(string searchPattern) - => EnumerateFileSystemInfos(searchPattern).ToArray(); - - /// - public IFileSystemInfo[] GetFileSystemInfos( - string searchPattern, SearchOption searchOption) - => EnumerateFileSystemInfos(searchPattern, searchOption).ToArray(); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IFileSystemInfo[] GetFileSystemInfos( - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateFileSystemInfos(searchPattern, enumerationOptions).ToArray(); -#endif - - /// - public void MoveTo(string destDirName) - => Location = _fileSystem.Storage.Move( - _fileSystem.Storage.GetLocation(FullName), - _fileSystem.Storage.GetLocation(destDirName - .EnsureValidFormat(_fileSystem, nameof(destDirName))), - recursive: true) - ?? throw ExceptionFactory.DirectoryNotFound(FullName); - - #endregion - - [return: NotNullIfNotNull("location")] - internal static new DirectoryInfoMock? New(IStorageLocation? location, - MockFileSystem fileSystem) - { - if (location == null) - { - return null; - } - - return new DirectoryInfoMock(location, fileSystem); - } - - private IEnumerable EnumerateInternal( - FileSystemTypes fileSystemTypes, - string path, - string searchPattern, - EnumerationOptions enumerationOptions) - { - StorageExtensions.AdjustedLocation adjustedLocation = _fileSystem.Storage - .AdjustLocationFromSearchPattern( - path.EnsureValidFormat(_fileSystem), - searchPattern); - return _fileSystem.Storage.EnumerateLocations( - adjustedLocation.Location, - fileSystemTypes, - adjustedLocation.SearchPattern, - enumerationOptions); - } -} +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// A mocked directory in the . +/// +internal sealed class DirectoryInfoMock + : FileSystemInfoMock, IDirectoryInfo +{ + private readonly MockFileSystem _fileSystem; + + private DirectoryInfoMock(IStorageLocation location, + MockFileSystem fileSystem) + : base(fileSystem, location, FileSystemTypes.Directory) + { + _fileSystem = fileSystem; + } + + #region IDirectoryInfo Members + + /// + public override bool Exists + => base.Exists && FileSystemType == FileSystemTypes.Directory; + + /// + public IDirectoryInfo? Parent + => New(Location.GetParent(), _fileSystem); + + /// + public IDirectoryInfo Root + => New(_fileSystem.Storage.GetLocation(string.Empty.PrefixRoot()), + _fileSystem); + + /// + public void Create() + { + FullName.EnsureValidFormat(_fileSystem); + + Container = _fileSystem.Storage.GetOrCreateContainer(Location, + InMemoryContainer.NewDirectory, + Extensibility); + + ResetCache(!Execute.IsNetFramework); + } + + /// + public IDirectoryInfo CreateSubdirectory(string path) + { + DirectoryInfoMock directory = New( + _fileSystem.Storage.GetLocation( + _fileSystem.Path.Combine(FullName, path + .EnsureValidFormat(_fileSystem, nameof(path), + Execute.IsWindows && !Execute.IsNetFramework))), + _fileSystem); + directory.Create(); + return directory; + } + + /// + public override void Delete() + { + if (!_fileSystem.Storage.DeleteContainer(Location)) + { + throw ExceptionFactory.DirectoryNotFound(Location.FullPath); + } + + ResetCache(!Execute.IsNetFramework); + } + + /// + public void Delete(bool recursive) + { + if (!_fileSystem.Storage.DeleteContainer( + _fileSystem.Storage.GetLocation(FullName), recursive)) + { + throw ExceptionFactory.DirectoryNotFound(FullName); + } + + ResetCache(!Execute.IsNetFramework); + } + + /// + public IEnumerable EnumerateDirectories() + => EnumerateDirectories( + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly); + + /// + public IEnumerable + EnumerateDirectories(string searchPattern) + => EnumerateDirectories(searchPattern, SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateDirectories( + string searchPattern, SearchOption searchOption) + => EnumerateInternal(FileSystemTypes.Directory, + FullName, + searchPattern, + EnumerationOptionsHelper.FromSearchOption(searchOption)) + .Select(location => New(location, _fileSystem)); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IEnumerable EnumerateDirectories( + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateInternal(FileSystemTypes.Directory, + FullName, + searchPattern, + enumerationOptions) + .Select(location => New(location, _fileSystem)); +#endif + + /// + public IEnumerable EnumerateFiles() + => EnumerateFiles( + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateFiles(string searchPattern) + => EnumerateFiles(searchPattern, SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateFiles( + string searchPattern, SearchOption searchOption) + => EnumerateInternal(FileSystemTypes.File, + FullName, + searchPattern, + EnumerationOptionsHelper.FromSearchOption(searchOption)) + .Select(location => FileInfoMock.New(location, _fileSystem)); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IEnumerable EnumerateFiles( + string searchPattern, EnumerationOptions enumerationOptions) + => EnumerateInternal(FileSystemTypes.File, + FullName, + searchPattern, + enumerationOptions) + .Select(location => FileInfoMock.New(location, _fileSystem)); +#endif + + /// + public IEnumerable EnumerateFileSystemInfos() + => EnumerateFileSystemInfos( + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly); + + /// + public IEnumerable + EnumerateFileSystemInfos(string searchPattern) + => EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateFileSystemInfos( + string searchPattern, SearchOption searchOption) + => EnumerateInternal(FileSystemTypes.DirectoryOrFile, + FullName, + searchPattern, + EnumerationOptionsHelper.FromSearchOption(searchOption)) + .Select(location => FileSystemInfoMock.New(location, _fileSystem)); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IEnumerable EnumerateFileSystemInfos( + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateInternal(FileSystemTypes.DirectoryOrFile, + FullName, + searchPattern, + enumerationOptions) + .Select(location => FileSystemInfoMock.New(location, _fileSystem)); +#endif + + /// + public IDirectoryInfo[] GetDirectories() + => EnumerateDirectories().ToArray(); + + /// + public IDirectoryInfo[] GetDirectories(string searchPattern) + => EnumerateDirectories(searchPattern).ToArray(); + + /// + public IDirectoryInfo[] GetDirectories( + string searchPattern, SearchOption searchOption) + => EnumerateDirectories(searchPattern, searchOption).ToArray(); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IDirectoryInfo[] GetDirectories( + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateDirectories(searchPattern, enumerationOptions).ToArray(); +#endif + + /// + public IFileInfo[] GetFiles() + => EnumerateFiles().ToArray(); + + /// + public IFileInfo[] GetFiles(string searchPattern) + => EnumerateFiles(searchPattern).ToArray(); + + /// + public IFileInfo[] GetFiles(string searchPattern, + SearchOption searchOption) + => EnumerateFiles(searchPattern, searchOption).ToArray(); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IFileInfo[] GetFiles(string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateFiles(searchPattern, enumerationOptions).ToArray(); +#endif + + /// + public IFileSystemInfo[] GetFileSystemInfos() + => EnumerateFileSystemInfos().ToArray(); + + /// + public IFileSystemInfo[] GetFileSystemInfos(string searchPattern) + => EnumerateFileSystemInfos(searchPattern).ToArray(); + + /// + public IFileSystemInfo[] GetFileSystemInfos( + string searchPattern, SearchOption searchOption) + => EnumerateFileSystemInfos(searchPattern, searchOption).ToArray(); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IFileSystemInfo[] GetFileSystemInfos( + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateFileSystemInfos(searchPattern, enumerationOptions).ToArray(); +#endif + + /// + public void MoveTo(string destDirName) + => Location = _fileSystem.Storage.Move( + _fileSystem.Storage.GetLocation(FullName), + _fileSystem.Storage.GetLocation(destDirName + .EnsureValidFormat(_fileSystem, nameof(destDirName))), + recursive: true) + ?? throw ExceptionFactory.DirectoryNotFound(FullName); + + #endregion + + [return: NotNullIfNotNull("location")] + internal static new DirectoryInfoMock? New(IStorageLocation? location, + MockFileSystem fileSystem) + { + if (location == null) + { + return null; + } + + return new DirectoryInfoMock(location, fileSystem); + } + + private IEnumerable EnumerateInternal( + FileSystemTypes fileSystemTypes, + string path, + string searchPattern, + EnumerationOptions enumerationOptions) + { + StorageExtensions.AdjustedLocation adjustedLocation = _fileSystem.Storage + .AdjustLocationFromSearchPattern( + path.EnsureValidFormat(_fileSystem), + searchPattern); + return _fileSystem.Storage.EnumerateLocations( + adjustedLocation.Location, + fileSystemTypes, + adjustedLocation.SearchPattern, + enumerationOptions); + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs index 2f9d3358d..e600453c5 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs @@ -1,471 +1,471 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class DirectoryMock : IDirectory -{ - private readonly MockFileSystem _fileSystem; - - internal DirectoryMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - #region IDirectory Members - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public IDirectoryInfo CreateDirectory(string path) - { - path.EnsureValidFormat(_fileSystem); - - DirectoryInfoMock directory = DirectoryInfoMock.New( - _fileSystem.Storage.GetLocation(path), - _fileSystem); - directory.Create(); - - return directory; - } - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - public IDirectoryInfo CreateDirectory(string path, UnixFileMode unixCreateMode) - { - IDirectoryInfo directoryInfo = CreateDirectory(path); - #pragma warning disable CA1416 - directoryInfo.UnixFileMode = unixCreateMode; - #pragma warning restore CA1416 - return directoryInfo; - } -#endif - -#if FEATURE_FILESYSTEM_LINK - /// - public IFileSystemInfo CreateSymbolicLink( - string path, string pathToTarget) - { - path.EnsureValidFormat(_fileSystem); - IDirectoryInfo fileSystemInfo = - _fileSystem.DirectoryInfo.New(path); - fileSystemInfo.CreateAsSymbolicLink(pathToTarget); - return fileSystemInfo; - } -#endif - -#if FEATURE_FILESYSTEM_NET7 - /// - public IDirectoryInfo CreateTempSubdirectory(string? prefix = null) - { - string basePath; - - do - { - string localBasePath = _fileSystem.Path.Combine( - _fileSystem.Path.GetTempPath(), - (prefix ?? "") + _fileSystem.Path.GetFileNameWithoutExtension( - _fileSystem.Path.GetRandomFileName())); - Execute.OnMac(() => localBasePath = "/private" + localBasePath); - basePath = localBasePath; - } while (_fileSystem.Directory.Exists(basePath)); - - _fileSystem.Directory.CreateDirectory(basePath); - return _fileSystem.DirectoryInfo.New(basePath); - } -#endif - - /// - public void Delete(string path) - => _fileSystem.DirectoryInfo - .New(path.EnsureValidFormat(FileSystem)) - .Delete(); - - /// - public void Delete(string path, bool recursive) - => _fileSystem.DirectoryInfo - .New(path.EnsureValidFormat(FileSystem)) - .Delete(recursive); - - /// - public IEnumerable EnumerateDirectories(string path) - => EnumerateDirectories(path, - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateDirectories(string path, string searchPattern) - => EnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateDirectories(string path, - string searchPattern, - SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.Directory, - path, - searchPattern, - EnumerationOptionsHelper.FromSearchOption(searchOption)); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IEnumerable EnumerateDirectories(string path, - string searchPattern, - EnumerationOptions - enumerationOptions) - => EnumerateInternal(FileSystemTypes.Directory, - path, - searchPattern, - enumerationOptions); -#endif - - /// - public IEnumerable EnumerateFiles(string path) - => EnumerateFiles(path, - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateFiles(string path, string searchPattern) - => EnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateFiles(string path, - string searchPattern, - SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.File, - path, - searchPattern, - EnumerationOptionsHelper.FromSearchOption(searchOption)); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IEnumerable EnumerateFiles(string path, - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateInternal(FileSystemTypes.File, - path, - searchPattern, - enumerationOptions); -#endif - - /// - public IEnumerable EnumerateFileSystemEntries(string path) - => EnumerateFileSystemEntries(path, - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateFileSystemEntries( - string path, string searchPattern) - => EnumerateFileSystemEntries(path, - searchPattern, - SearchOption.TopDirectoryOnly); - - /// - public IEnumerable EnumerateFileSystemEntries(string path, - string searchPattern, - SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.DirectoryOrFile, - path, - searchPattern, - EnumerationOptionsHelper.FromSearchOption(searchOption)); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public IEnumerable EnumerateFileSystemEntries(string path, - string searchPattern, - EnumerationOptions - enumerationOptions) - => EnumerateInternal(FileSystemTypes.DirectoryOrFile, - path, - searchPattern, - enumerationOptions); -#endif - - /// - public bool Exists([NotNullWhen(true)] string? path) - => DirectoryInfoMock.New( - _fileSystem.Storage.GetLocation(path), - _fileSystem)?.Exists ?? false; - - /// - public DateTime GetCreationTime(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .CreationTime.Get(DateTimeKind.Local); - - /// - public DateTime GetCreationTimeUtc(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .CreationTime.Get(DateTimeKind.Utc); - - /// - public string GetCurrentDirectory() - => _fileSystem.Storage.CurrentDirectory; - - /// - public string[] GetDirectories(string path) - => EnumerateDirectories(path).ToArray(); - - /// - public string[] GetDirectories(string path, string searchPattern) - => EnumerateDirectories(path, searchPattern).ToArray(); - - /// - public string[] GetDirectories(string path, - string searchPattern, - SearchOption searchOption) - => EnumerateDirectories(path, searchPattern, searchOption).ToArray(); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public string[] GetDirectories(string path, - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateDirectories(path, searchPattern, enumerationOptions).ToArray(); -#endif - - /// - public string GetDirectoryRoot(string path) - => _fileSystem.Path.GetPathRoot( - _fileSystem.Path.GetFullPath(path)) ?? - throw ExceptionFactory.PathIsEmpty(nameof(path)); - - /// - public string[] GetFiles(string path) - => EnumerateFiles(path).ToArray(); - - /// - public string[] GetFiles(string path, string searchPattern) - => EnumerateFiles(path, searchPattern).ToArray(); - - /// - public string[] GetFiles(string path, - string searchPattern, - SearchOption searchOption) - => EnumerateFiles(path, searchPattern, searchOption).ToArray(); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public string[] GetFiles(string path, - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateFiles(path, searchPattern, enumerationOptions).ToArray(); -#endif - - /// - public string[] GetFileSystemEntries(string path) - => EnumerateFileSystemEntries(path).ToArray(); - - /// - public string[] GetFileSystemEntries(string path, string searchPattern) - => EnumerateFileSystemEntries(path, searchPattern).ToArray(); - - /// - public string[] GetFileSystemEntries(string path, - string searchPattern, - SearchOption searchOption) - => EnumerateFileSystemEntries(path, searchPattern, searchOption).ToArray(); - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - /// - public string[] GetFileSystemEntries(string path, - string searchPattern, - EnumerationOptions enumerationOptions) - => EnumerateFileSystemEntries(path, searchPattern, enumerationOptions) - .ToArray(); -#endif - - /// - public DateTime GetLastAccessTime(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastAccessTime.Get(DateTimeKind.Local); - - /// - public DateTime GetLastAccessTimeUtc(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastAccessTime.Get(DateTimeKind.Utc); - - /// - public DateTime GetLastWriteTime(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastWriteTime.Get(DateTimeKind.Local); - - /// - public DateTime GetLastWriteTimeUtc(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastWriteTime.Get(DateTimeKind.Utc); - - /// - public string[] GetLogicalDrives() - => _fileSystem.DriveInfo.GetDrives().Select(x => x.Name).ToArray(); - - /// - public IDirectoryInfo? GetParent(string path) - => _fileSystem.DirectoryInfo - .New(path.EnsureValidArgument(_fileSystem, nameof(path))) - .Parent; - - /// - public void Move(string sourceDirName, string destDirName) - => _fileSystem.DirectoryInfo.New(sourceDirName - .EnsureValidFormat(_fileSystem, nameof(sourceDirName))) - .MoveTo(destDirName - .EnsureValidFormat(_fileSystem, nameof(destDirName))); - -#if FEATURE_FILESYSTEM_LINK - /// - public IFileSystemInfo? ResolveLinkTarget( - string linkPath, bool returnFinalTarget) - { - try - { - return _fileSystem.DirectoryInfo.New(linkPath - .EnsureValidFormat(_fileSystem, nameof(linkPath))) - .ResolveLinkTarget(returnFinalTarget); - } - catch (IOException ex) - when (ex.HResult != -2147024773) - { - throw ExceptionFactory.FileNameCannotBeResolved(linkPath); - } - } -#endif - - /// - public void SetCreationTime(string path, DateTime creationTime) - => LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileCreatedTimeException) - .CreationTime = creationTime; - - /// - public void SetCreationTimeUtc(string path, DateTime creationTimeUtc) - => LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileCreatedTimeException) - .CreationTimeUtc = creationTimeUtc; - - /// - public void SetCurrentDirectory(string path) - { - IDirectoryInfo directoryInfo = - _fileSystem.DirectoryInfo.New(path); - if (!directoryInfo.Exists) - { - throw ExceptionFactory.DirectoryNotFound( - FileSystem.Path.GetFullPath(path)); - } - - _fileSystem.Storage.CurrentDirectory = directoryInfo.FullName; - } - - /// - public void SetLastAccessTime(string path, DateTime lastAccessTime) - => LoadDirectoryInfoOrThrowNotFoundException(path, - ThrowMissingFileLastAccessOrLastWriteTimeException) - .LastAccessTime = lastAccessTime; - - /// - public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) - => LoadDirectoryInfoOrThrowNotFoundException(path, - ThrowMissingFileLastAccessOrLastWriteTimeException) - .LastAccessTimeUtc = lastAccessTimeUtc; - - /// - public void SetLastWriteTime(string path, DateTime lastWriteTime) - => LoadDirectoryInfoOrThrowNotFoundException(path, - ThrowMissingFileLastAccessOrLastWriteTimeException) - .LastWriteTime = lastWriteTime; - - /// - public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) - => LoadDirectoryInfoOrThrowNotFoundException(path, - ThrowMissingFileLastAccessOrLastWriteTimeException) - .LastWriteTimeUtc = lastWriteTimeUtc; - - #endregion - - private IDirectoryInfo LoadDirectoryInfoOrThrowNotFoundException( - string path, Action onMissingCallback) - { - IDirectoryInfo directoryInfo = - _fileSystem.DirectoryInfo.New(path.EnsureValidFormat(FileSystem)); - if (!directoryInfo.Exists) - { - onMissingCallback.Invoke(FileSystem, path); - } - - return directoryInfo; - } - - private static void ThrowMissingFileCreatedTimeException(IFileSystem fileSystem, string path) - { -#if NET7_0_OR_GREATER - Execute.OnMac( - () => - throw ExceptionFactory.DirectoryNotFound( - fileSystem.Path.GetFullPath(path)), - () => - throw ExceptionFactory.FileNotFound( - fileSystem.Path.GetFullPath(path))); -#else - Execute.OnWindows( - () => - throw ExceptionFactory.FileNotFound( - fileSystem.Path.GetFullPath(path)), - () => - throw ExceptionFactory.DirectoryNotFound( - fileSystem.Path.GetFullPath(path))); -#endif - } - - private static void ThrowMissingFileLastAccessOrLastWriteTimeException(IFileSystem fileSystem, - string path) - { -#if NET7_0_OR_GREATER - throw ExceptionFactory.FileNotFound( - fileSystem.Path.GetFullPath(path)); -#else - Execute.OnWindows( - () => - throw ExceptionFactory.FileNotFound( - fileSystem.Path.GetFullPath(path)), - () => - throw ExceptionFactory.DirectoryNotFound( - fileSystem.Path.GetFullPath(path))); -#endif - } - - private IEnumerable EnumerateInternal(FileSystemTypes fileSystemTypes, - string path, - string searchPattern, - EnumerationOptions enumerationOptions) - { - StorageExtensions.AdjustedLocation adjustedLocation = _fileSystem.Storage - .AdjustLocationFromSearchPattern( - path.EnsureValidFormat(FileSystem), - searchPattern); - return _fileSystem.Storage.EnumerateLocations( - adjustedLocation.Location, - fileSystemTypes, - adjustedLocation.SearchPattern, - enumerationOptions) - .Select(x => _fileSystem - .GetSubdirectoryPath(x.FullPath, adjustedLocation.GivenPath)); - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class DirectoryMock : IDirectory +{ + private readonly MockFileSystem _fileSystem; + + internal DirectoryMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + #region IDirectory Members + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public IDirectoryInfo CreateDirectory(string path) + { + path.EnsureValidFormat(_fileSystem); + + DirectoryInfoMock directory = DirectoryInfoMock.New( + _fileSystem.Storage.GetLocation(path), + _fileSystem); + directory.Create(); + + return directory; + } + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + public IDirectoryInfo CreateDirectory(string path, UnixFileMode unixCreateMode) + { + IDirectoryInfo directoryInfo = CreateDirectory(path); + #pragma warning disable CA1416 + directoryInfo.UnixFileMode = unixCreateMode; + #pragma warning restore CA1416 + return directoryInfo; + } +#endif + +#if FEATURE_FILESYSTEM_LINK + /// + public IFileSystemInfo CreateSymbolicLink( + string path, string pathToTarget) + { + path.EnsureValidFormat(_fileSystem); + IDirectoryInfo fileSystemInfo = + _fileSystem.DirectoryInfo.New(path); + fileSystemInfo.CreateAsSymbolicLink(pathToTarget); + return fileSystemInfo; + } +#endif + +#if FEATURE_FILESYSTEM_NET7 + /// + public IDirectoryInfo CreateTempSubdirectory(string? prefix = null) + { + string basePath; + + do + { + string localBasePath = _fileSystem.Path.Combine( + _fileSystem.Path.GetTempPath(), + (prefix ?? "") + _fileSystem.Path.GetFileNameWithoutExtension( + _fileSystem.Path.GetRandomFileName())); + Execute.OnMac(() => localBasePath = "/private" + localBasePath); + basePath = localBasePath; + } while (_fileSystem.Directory.Exists(basePath)); + + _fileSystem.Directory.CreateDirectory(basePath); + return _fileSystem.DirectoryInfo.New(basePath); + } +#endif + + /// + public void Delete(string path) + => _fileSystem.DirectoryInfo + .New(path.EnsureValidFormat(FileSystem)) + .Delete(); + + /// + public void Delete(string path, bool recursive) + => _fileSystem.DirectoryInfo + .New(path.EnsureValidFormat(FileSystem)) + .Delete(recursive); + + /// + public IEnumerable EnumerateDirectories(string path) + => EnumerateDirectories(path, + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateDirectories(string path, string searchPattern) + => EnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateDirectories(string path, + string searchPattern, + SearchOption searchOption) + => EnumerateInternal(FileSystemTypes.Directory, + path, + searchPattern, + EnumerationOptionsHelper.FromSearchOption(searchOption)); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IEnumerable EnumerateDirectories(string path, + string searchPattern, + EnumerationOptions + enumerationOptions) + => EnumerateInternal(FileSystemTypes.Directory, + path, + searchPattern, + enumerationOptions); +#endif + + /// + public IEnumerable EnumerateFiles(string path) + => EnumerateFiles(path, + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateFiles(string path, string searchPattern) + => EnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateFiles(string path, + string searchPattern, + SearchOption searchOption) + => EnumerateInternal(FileSystemTypes.File, + path, + searchPattern, + EnumerationOptionsHelper.FromSearchOption(searchOption)); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IEnumerable EnumerateFiles(string path, + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateInternal(FileSystemTypes.File, + path, + searchPattern, + enumerationOptions); +#endif + + /// + public IEnumerable EnumerateFileSystemEntries(string path) + => EnumerateFileSystemEntries(path, + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateFileSystemEntries( + string path, string searchPattern) + => EnumerateFileSystemEntries(path, + searchPattern, + SearchOption.TopDirectoryOnly); + + /// + public IEnumerable EnumerateFileSystemEntries(string path, + string searchPattern, + SearchOption searchOption) + => EnumerateInternal(FileSystemTypes.DirectoryOrFile, + path, + searchPattern, + EnumerationOptionsHelper.FromSearchOption(searchOption)); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public IEnumerable EnumerateFileSystemEntries(string path, + string searchPattern, + EnumerationOptions + enumerationOptions) + => EnumerateInternal(FileSystemTypes.DirectoryOrFile, + path, + searchPattern, + enumerationOptions); +#endif + + /// + public bool Exists([NotNullWhen(true)] string? path) + => DirectoryInfoMock.New( + _fileSystem.Storage.GetLocation(path), + _fileSystem)?.Exists ?? false; + + /// + public DateTime GetCreationTime(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .CreationTime.Get(DateTimeKind.Local); + + /// + public DateTime GetCreationTimeUtc(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .CreationTime.Get(DateTimeKind.Utc); + + /// + public string GetCurrentDirectory() + => _fileSystem.Storage.CurrentDirectory; + + /// + public string[] GetDirectories(string path) + => EnumerateDirectories(path).ToArray(); + + /// + public string[] GetDirectories(string path, string searchPattern) + => EnumerateDirectories(path, searchPattern).ToArray(); + + /// + public string[] GetDirectories(string path, + string searchPattern, + SearchOption searchOption) + => EnumerateDirectories(path, searchPattern, searchOption).ToArray(); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public string[] GetDirectories(string path, + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateDirectories(path, searchPattern, enumerationOptions).ToArray(); +#endif + + /// + public string GetDirectoryRoot(string path) + => _fileSystem.Path.GetPathRoot( + _fileSystem.Path.GetFullPath(path)) ?? + throw ExceptionFactory.PathIsEmpty(nameof(path)); + + /// + public string[] GetFiles(string path) + => EnumerateFiles(path).ToArray(); + + /// + public string[] GetFiles(string path, string searchPattern) + => EnumerateFiles(path, searchPattern).ToArray(); + + /// + public string[] GetFiles(string path, + string searchPattern, + SearchOption searchOption) + => EnumerateFiles(path, searchPattern, searchOption).ToArray(); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public string[] GetFiles(string path, + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateFiles(path, searchPattern, enumerationOptions).ToArray(); +#endif + + /// + public string[] GetFileSystemEntries(string path) + => EnumerateFileSystemEntries(path).ToArray(); + + /// + public string[] GetFileSystemEntries(string path, string searchPattern) + => EnumerateFileSystemEntries(path, searchPattern).ToArray(); + + /// + public string[] GetFileSystemEntries(string path, + string searchPattern, + SearchOption searchOption) + => EnumerateFileSystemEntries(path, searchPattern, searchOption).ToArray(); + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + /// + public string[] GetFileSystemEntries(string path, + string searchPattern, + EnumerationOptions enumerationOptions) + => EnumerateFileSystemEntries(path, searchPattern, enumerationOptions) + .ToArray(); +#endif + + /// + public DateTime GetLastAccessTime(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastAccessTime.Get(DateTimeKind.Local); + + /// + public DateTime GetLastAccessTimeUtc(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastAccessTime.Get(DateTimeKind.Utc); + + /// + public DateTime GetLastWriteTime(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastWriteTime.Get(DateTimeKind.Local); + + /// + public DateTime GetLastWriteTimeUtc(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastWriteTime.Get(DateTimeKind.Utc); + + /// + public string[] GetLogicalDrives() + => _fileSystem.DriveInfo.GetDrives().Select(x => x.Name).ToArray(); + + /// + public IDirectoryInfo? GetParent(string path) + => _fileSystem.DirectoryInfo + .New(path.EnsureValidArgument(_fileSystem, nameof(path))) + .Parent; + + /// + public void Move(string sourceDirName, string destDirName) + => _fileSystem.DirectoryInfo.New(sourceDirName + .EnsureValidFormat(_fileSystem, nameof(sourceDirName))) + .MoveTo(destDirName + .EnsureValidFormat(_fileSystem, nameof(destDirName))); + +#if FEATURE_FILESYSTEM_LINK + /// + public IFileSystemInfo? ResolveLinkTarget( + string linkPath, bool returnFinalTarget) + { + try + { + return _fileSystem.DirectoryInfo.New(linkPath + .EnsureValidFormat(_fileSystem, nameof(linkPath))) + .ResolveLinkTarget(returnFinalTarget); + } + catch (IOException ex) + when (ex.HResult != -2147024773) + { + throw ExceptionFactory.FileNameCannotBeResolved(linkPath); + } + } +#endif + + /// + public void SetCreationTime(string path, DateTime creationTime) + => LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileCreatedTimeException) + .CreationTime = creationTime; + + /// + public void SetCreationTimeUtc(string path, DateTime creationTimeUtc) + => LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileCreatedTimeException) + .CreationTimeUtc = creationTimeUtc; + + /// + public void SetCurrentDirectory(string path) + { + IDirectoryInfo directoryInfo = + _fileSystem.DirectoryInfo.New(path); + if (!directoryInfo.Exists) + { + throw ExceptionFactory.DirectoryNotFound( + FileSystem.Path.GetFullPath(path)); + } + + _fileSystem.Storage.CurrentDirectory = directoryInfo.FullName; + } + + /// + public void SetLastAccessTime(string path, DateTime lastAccessTime) + => LoadDirectoryInfoOrThrowNotFoundException(path, + ThrowMissingFileLastAccessOrLastWriteTimeException) + .LastAccessTime = lastAccessTime; + + /// + public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) + => LoadDirectoryInfoOrThrowNotFoundException(path, + ThrowMissingFileLastAccessOrLastWriteTimeException) + .LastAccessTimeUtc = lastAccessTimeUtc; + + /// + public void SetLastWriteTime(string path, DateTime lastWriteTime) + => LoadDirectoryInfoOrThrowNotFoundException(path, + ThrowMissingFileLastAccessOrLastWriteTimeException) + .LastWriteTime = lastWriteTime; + + /// + public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) + => LoadDirectoryInfoOrThrowNotFoundException(path, + ThrowMissingFileLastAccessOrLastWriteTimeException) + .LastWriteTimeUtc = lastWriteTimeUtc; + + #endregion + + private IDirectoryInfo LoadDirectoryInfoOrThrowNotFoundException( + string path, Action onMissingCallback) + { + IDirectoryInfo directoryInfo = + _fileSystem.DirectoryInfo.New(path.EnsureValidFormat(FileSystem)); + if (!directoryInfo.Exists) + { + onMissingCallback.Invoke(FileSystem, path); + } + + return directoryInfo; + } + + private static void ThrowMissingFileCreatedTimeException(IFileSystem fileSystem, string path) + { +#if NET7_0_OR_GREATER + Execute.OnMac( + () => + throw ExceptionFactory.DirectoryNotFound( + fileSystem.Path.GetFullPath(path)), + () => + throw ExceptionFactory.FileNotFound( + fileSystem.Path.GetFullPath(path))); +#else + Execute.OnWindows( + () => + throw ExceptionFactory.FileNotFound( + fileSystem.Path.GetFullPath(path)), + () => + throw ExceptionFactory.DirectoryNotFound( + fileSystem.Path.GetFullPath(path))); +#endif + } + + private static void ThrowMissingFileLastAccessOrLastWriteTimeException(IFileSystem fileSystem, + string path) + { +#if NET7_0_OR_GREATER + throw ExceptionFactory.FileNotFound( + fileSystem.Path.GetFullPath(path)); +#else + Execute.OnWindows( + () => + throw ExceptionFactory.FileNotFound( + fileSystem.Path.GetFullPath(path)), + () => + throw ExceptionFactory.DirectoryNotFound( + fileSystem.Path.GetFullPath(path))); +#endif + } + + private IEnumerable EnumerateInternal(FileSystemTypes fileSystemTypes, + string path, + string searchPattern, + EnumerationOptions enumerationOptions) + { + StorageExtensions.AdjustedLocation adjustedLocation = _fileSystem.Storage + .AdjustLocationFromSearchPattern( + path.EnsureValidFormat(FileSystem), + searchPattern); + return _fileSystem.Storage.EnumerateLocations( + adjustedLocation.Location, + fileSystemTypes, + adjustedLocation.SearchPattern, + enumerationOptions) + .Select(x => _fileSystem + .GetSubdirectoryPath(x.FullPath, adjustedLocation.GivenPath)); + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs index be7a64130..b445806f6 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs @@ -1,57 +1,57 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class DriveInfoFactoryMock : IDriveInfoFactory -{ - private readonly MockFileSystem _fileSystem; - - internal DriveInfoFactoryMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - #region IDriveInfoFactory Members - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public IDriveInfo[] GetDrives() - => _fileSystem.Storage.GetDrives() - .Where(x => !x.IsUncPath) - .Cast() - .ToArray(); - - /// - public IDriveInfo New(string driveName) - { - if (driveName == null) - { - throw new ArgumentNullException(nameof(driveName)); - } - - DriveInfoMock driveInfo = DriveInfoMock.New(driveName, _fileSystem); - IStorageDrive? existingDrive = _fileSystem.Storage.GetDrive(driveInfo.Name); - return existingDrive ?? driveInfo; - } - - /// - [return: NotNullIfNotNull("driveInfo")] - public IDriveInfo? Wrap(DriveInfo? driveInfo) - { - if (driveInfo?.Name == null) - { - return null; - } - - return New(driveInfo.Name); - } - - #endregion -} +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class DriveInfoFactoryMock : IDriveInfoFactory +{ + private readonly MockFileSystem _fileSystem; + + internal DriveInfoFactoryMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + #region IDriveInfoFactory Members + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public IDriveInfo[] GetDrives() + => _fileSystem.Storage.GetDrives() + .Where(x => !x.IsUncPath) + .Cast() + .ToArray(); + + /// + public IDriveInfo New(string driveName) + { + if (driveName == null) + { + throw new ArgumentNullException(nameof(driveName)); + } + + DriveInfoMock driveInfo = DriveInfoMock.New(driveName, _fileSystem); + IStorageDrive? existingDrive = _fileSystem.Storage.GetDrive(driveInfo.Name); + return existingDrive ?? driveInfo; + } + + /// + [return: NotNullIfNotNull("driveInfo")] + public IDriveInfo? Wrap(DriveInfo? driveInfo) + { + if (driveInfo?.Name == null) + { + return null; + } + + return New(driveInfo.Name); + } + + #endregion +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoMock.cs index 3058abb20..6c41d390e 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoMock.cs @@ -1,206 +1,206 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// Mocked instance of a -/// -internal sealed class DriveInfoMock : IStorageDrive -{ - /// - /// The default . - /// - public const string DefaultDriveFormat = "NTFS"; - - /// - /// The default . - /// - public const DriveType DefaultDriveType = DriveType.Fixed; - - /// - /// The default total size of a mocked . - /// - /// The number is equal to 1GB (1 Gigabyte). - /// - public const long DefaultTotalSize = 1024 * 1024 * 1024; - - private readonly MockFileSystem _fileSystem; - - private long _usedBytes; - private string _volumeLabel = nameof(MockFileSystem); - - private DriveInfoMock(string driveName, MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - - if (driveName.IsUncPath()) - { - IsUncPath = true; - driveName = PathHelper.UncPrefix + - GetTopmostParentDirectory(driveName.Substring(2)); - } - else - { - driveName = ValidateDriveLetter(driveName, fileSystem); - } - - Name = driveName; - TotalSize = DefaultTotalSize; - DriveFormat = DefaultDriveFormat; - DriveType = DefaultDriveType; - IsReady = true; - } - - #region IStorageDrive Members - - /// - public long AvailableFreeSpace - => TotalFreeSpace; - - /// - public string DriveFormat { get; private set; } - - /// - public DriveType DriveType { get; private set; } - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public bool IsReady { get; private set; } - - /// - /// Flag indicating if the drive is a UNC drive - /// - public bool IsUncPath { get; } - - /// - public string Name { get; } - - /// - public IDirectoryInfo RootDirectory - => DirectoryInfoMock.New(_fileSystem.Storage.GetLocation(Name), _fileSystem); - - /// - public long TotalFreeSpace - => TotalSize - _usedBytes; - - /// - public long TotalSize { get; private set; } - - /// - [AllowNull] - public string VolumeLabel - { - get => _volumeLabel; - [SupportedOSPlatform("windows")] - set - { - _volumeLabel = value ?? _volumeLabel; - Execute.NotOnWindows( - () => throw ExceptionFactory.OperationNotSupportedOnThisPlatform()); - } - } - - /// - public IStorageDrive ChangeUsedBytes(long usedBytesDelta) - { - long newUsedBytes = Math.Max(0, _usedBytes + usedBytesDelta); - - if (newUsedBytes > TotalSize) - { - throw ExceptionFactory.NotEnoughDiskSpace(Name); - } - - _usedBytes = newUsedBytes; - - return this; - } - - /// - public IStorageDrive SetDriveFormat( - string driveFormat = DefaultDriveFormat) - { - DriveFormat = driveFormat; - return this; - } - - /// - public IStorageDrive SetDriveType( - DriveType driveType = DefaultDriveType) - { - DriveType = driveType; - return this; - } - - /// - public IStorageDrive SetIsReady(bool isReady = true) - { - IsReady = isReady; - return this; - } - - /// - public IStorageDrive SetTotalSize( - long totalSize = DefaultTotalSize) - { - TotalSize = totalSize; - return this; - } - - #endregion - - /// - public override string ToString() - => Name; - - private string GetTopmostParentDirectory(string path) - { - while (true) - { - string? child = FileSystem.Path.GetDirectoryName(path); - if (string.IsNullOrEmpty(child)) - { - break; - } - - path = child; - } - - return path; - } - - private static string ValidateDriveLetter(string driveName, - IFileSystem fileSystem) - { - if (driveName.Length == 1 && - char.IsLetter(driveName, 0)) - { - return $"{driveName.ToUpperInvariant()}:\\"; - } - - if (fileSystem.Path.IsPathRooted(driveName)) - { - return fileSystem.Path.GetPathRoot(driveName)!; - } - - throw ExceptionFactory.InvalidDriveName(); - } - - [return: NotNullIfNotNull("driveName")] - internal static DriveInfoMock? New(string? driveName, - MockFileSystem fileSystem) - { - if (driveName == null) - { - return null; - } - - return new DriveInfoMock(driveName, fileSystem); - } -} +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// Mocked instance of a +/// +internal sealed class DriveInfoMock : IStorageDrive +{ + /// + /// The default . + /// + public const string DefaultDriveFormat = "NTFS"; + + /// + /// The default . + /// + public const DriveType DefaultDriveType = DriveType.Fixed; + + /// + /// The default total size of a mocked . + /// + /// The number is equal to 1GB (1 Gigabyte). + /// + public const long DefaultTotalSize = 1024 * 1024 * 1024; + + private readonly MockFileSystem _fileSystem; + + private long _usedBytes; + private string _volumeLabel = nameof(MockFileSystem); + + private DriveInfoMock(string driveName, MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + + if (driveName.IsUncPath()) + { + IsUncPath = true; + driveName = PathHelper.UncPrefix + + GetTopmostParentDirectory(driveName.Substring(2)); + } + else + { + driveName = ValidateDriveLetter(driveName, fileSystem); + } + + Name = driveName; + TotalSize = DefaultTotalSize; + DriveFormat = DefaultDriveFormat; + DriveType = DefaultDriveType; + IsReady = true; + } + + #region IStorageDrive Members + + /// + public long AvailableFreeSpace + => TotalFreeSpace; + + /// + public string DriveFormat { get; private set; } + + /// + public DriveType DriveType { get; private set; } + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public bool IsReady { get; private set; } + + /// + /// Flag indicating if the drive is a UNC drive + /// + public bool IsUncPath { get; } + + /// + public string Name { get; } + + /// + public IDirectoryInfo RootDirectory + => DirectoryInfoMock.New(_fileSystem.Storage.GetLocation(Name), _fileSystem); + + /// + public long TotalFreeSpace + => TotalSize - _usedBytes; + + /// + public long TotalSize { get; private set; } + + /// + [AllowNull] + public string VolumeLabel + { + get => _volumeLabel; + [SupportedOSPlatform("windows")] + set + { + _volumeLabel = value ?? _volumeLabel; + Execute.NotOnWindows( + () => throw ExceptionFactory.OperationNotSupportedOnThisPlatform()); + } + } + + /// + public IStorageDrive ChangeUsedBytes(long usedBytesDelta) + { + long newUsedBytes = Math.Max(0, _usedBytes + usedBytesDelta); + + if (newUsedBytes > TotalSize) + { + throw ExceptionFactory.NotEnoughDiskSpace(Name); + } + + _usedBytes = newUsedBytes; + + return this; + } + + /// + public IStorageDrive SetDriveFormat( + string driveFormat = DefaultDriveFormat) + { + DriveFormat = driveFormat; + return this; + } + + /// + public IStorageDrive SetDriveType( + DriveType driveType = DefaultDriveType) + { + DriveType = driveType; + return this; + } + + /// + public IStorageDrive SetIsReady(bool isReady = true) + { + IsReady = isReady; + return this; + } + + /// + public IStorageDrive SetTotalSize( + long totalSize = DefaultTotalSize) + { + TotalSize = totalSize; + return this; + } + + #endregion + + /// + public override string ToString() + => Name; + + private string GetTopmostParentDirectory(string path) + { + while (true) + { + string? child = FileSystem.Path.GetDirectoryName(path); + if (string.IsNullOrEmpty(child)) + { + break; + } + + path = child; + } + + return path; + } + + private static string ValidateDriveLetter(string driveName, + IFileSystem fileSystem) + { + if (driveName.Length == 1 && + char.IsLetter(driveName, 0)) + { + return $"{driveName.ToUpperInvariant()}:\\"; + } + + if (fileSystem.Path.IsPathRooted(driveName)) + { + return fileSystem.Path.GetPathRoot(driveName)!; + } + + throw ExceptionFactory.InvalidDriveName(); + } + + [return: NotNullIfNotNull("driveName")] + internal static DriveInfoMock? New(string? driveName, + MockFileSystem fileSystem) + { + if (driveName == null) + { + return null; + } + + return new DriveInfoMock(driveName, fileSystem); + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs index ec6235ed2..86b35d86f 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs @@ -1,51 +1,51 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Testably.Abstractions.Testing.Helpers; - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class FileInfoFactoryMock : IFileInfoFactory -{ - private readonly MockFileSystem _fileSystem; - - internal FileInfoFactoryMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - #region IFileInfoFactory Members - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public IFileInfo New(string fileName) - { - if (fileName == null) - { - throw new ArgumentNullException(nameof(fileName)); - } - - if (fileName.Trim() == "" && Execute.IsWindows) - { - throw ExceptionFactory.PathIsEmpty("path"); - } - - return FileInfoMock.New( - _fileSystem.Storage.GetLocation(fileName), - _fileSystem); - } - - /// - [return: NotNullIfNotNull("fileInfo")] - public IFileInfo? Wrap(FileInfo? fileInfo) - => FileInfoMock.New( - _fileSystem.Storage.GetLocation( - fileInfo?.FullName, - fileInfo?.ToString()), - _fileSystem); - - #endregion -} +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class FileInfoFactoryMock : IFileInfoFactory +{ + private readonly MockFileSystem _fileSystem; + + internal FileInfoFactoryMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + #region IFileInfoFactory Members + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public IFileInfo New(string fileName) + { + if (fileName == null) + { + throw new ArgumentNullException(nameof(fileName)); + } + + if (fileName.Trim() == "" && Execute.IsWindows) + { + throw ExceptionFactory.PathIsEmpty("path"); + } + + return FileInfoMock.New( + _fileSystem.Storage.GetLocation(fileName), + _fileSystem); + } + + /// + [return: NotNullIfNotNull("fileInfo")] + public IFileInfo? Wrap(FileInfo? fileInfo) + => FileInfoMock.New( + _fileSystem.Storage.GetLocation( + fileInfo?.FullName, + fileInfo?.ToString()), + _fileSystem); + + #endregion +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs index 123f8d748..bcf7e9246 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs @@ -1,285 +1,285 @@ -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// A mocked file in the . -/// -internal sealed class FileInfoMock - : FileSystemInfoMock, IFileInfo -{ - private readonly MockFileSystem _fileSystem; - - private FileInfoMock(IStorageLocation location, - MockFileSystem fileSystem) - : base(fileSystem, location, FileSystemTypes.File) - { - _fileSystem = fileSystem; - } - - #region IFileInfo Members - - /// - public IDirectoryInfo? Directory - => DirectoryInfoMock.New(Location.GetParent(), - _fileSystem); - - /// - public string? DirectoryName - => Directory?.FullName; - - /// - public override bool Exists - => base.Exists && FileSystemType == FileSystemTypes.File; - - /// - public bool IsReadOnly - { - get => (Attributes & FileAttributes.ReadOnly) != 0; - set - { - if (value) - { - Attributes |= FileAttributes.ReadOnly; - } - else - { - Attributes &= ~FileAttributes.ReadOnly; - } - } - } - - /// - public long Length - { - get - { - if (Container is NullContainer || - Container.Type != FileSystemTypes.File) - { - throw ExceptionFactory.FileNotFound( - Execute.OnNetFramework( - () => Location.FriendlyName, - () => Location.FullPath)); - } - - return Container.GetBytes().Length; - } - } - - /// - public StreamWriter AppendText() - => new(Open(FileMode.Append, FileAccess.Write)); - - /// - public IFileInfo CopyTo(string destFileName) - { - destFileName.EnsureValidArgument(_fileSystem, nameof(destFileName)); - IStorageLocation destinationLocation = _fileSystem.Storage.GetLocation(destFileName); - Location.ThrowExceptionIfNotFound(_fileSystem); - IStorageLocation location = _fileSystem.Storage - .Copy(Location, destinationLocation) - ?? throw ExceptionFactory.FileNotFound(FullName); - return _fileSystem.FileInfo.New(location.FullPath); - } - - /// - public IFileInfo CopyTo(string destFileName, bool overwrite) - { - destFileName.EnsureValidArgument(_fileSystem, nameof(destFileName)); - IStorageLocation location = _fileSystem.Storage.Copy( - Location, - _fileSystem.Storage.GetLocation(destFileName), - overwrite) - ?? throw ExceptionFactory.FileNotFound(FullName); - return _fileSystem.FileInfo.New(location.FullPath); - } - - /// - public FileSystemStream Create() - { - Execute.NotOnNetFramework(Refresh); - return _fileSystem.File.Create(FullName); - } - - /// - public StreamWriter CreateText() - => new(_fileSystem.File.Create(FullName)); - - /// - [SupportedOSPlatform("windows")] - public void Decrypt() - => Container.Decrypt(); - - /// - [SupportedOSPlatform("windows")] - public void Encrypt() - => Container.Encrypt(); - - /// - public void MoveTo(string destFileName) - { - Location = _fileSystem.Storage.Move( - Location, - _fileSystem.Storage.GetLocation(destFileName - .EnsureValidArgument(_fileSystem, nameof(destFileName)))) - ?? throw ExceptionFactory.FileNotFound(FullName); - } - -#if FEATURE_FILE_MOVETO_OVERWRITE - /// - public void MoveTo(string destFileName, bool overwrite) - { - Location = _fileSystem.Storage.Move( - Location, - _fileSystem.Storage.GetLocation(destFileName - .EnsureValidArgument(_fileSystem, nameof(destFileName))), - overwrite) - ?? throw ExceptionFactory.FileNotFound(FullName); - } -#endif - - /// - public FileSystemStream Open(FileMode mode) - { - Execute.OnNetFrameworkIf(mode == FileMode.Append, - () => throw ExceptionFactory.AppendAccessOnlyInWriteOnlyMode()); - - return new FileStreamMock( - _fileSystem, - FullName, - mode, - mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, - FileShare.None); - } - - /// - public FileSystemStream Open(FileMode mode, FileAccess access) - => new FileStreamMock( - _fileSystem, - FullName, - mode, - access, - FileShare.None); - - /// - public FileSystemStream Open(FileMode mode, FileAccess access, FileShare share) - => new FileStreamMock( - _fileSystem, - FullName, - mode, - access, - share); - -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - /// - public FileSystemStream Open(FileStreamOptions options) - => _fileSystem.File.Open(FullName, options); -#endif - - /// - public FileSystemStream OpenRead() - => new FileStreamMock( - _fileSystem, - FullName, - FileMode.Open, - FileAccess.Read); - - /// - public StreamReader OpenText() - => new(OpenRead()); - - /// - public FileSystemStream OpenWrite() - => new FileStreamMock( - _fileSystem, - FullName, - FileMode.OpenOrCreate, - FileAccess.Write, - FileShare.None); - - /// - public IFileInfo Replace(string destinationFileName, - string? destinationBackupFileName) - { - IStorageLocation location = - _fileSystem - .Storage - .Replace( - Location.ThrowIfNotFound(_fileSystem, - () => { }, - () => - { - if (Execute.IsWindows) - { - throw ExceptionFactory.FileNotFound(FullName); - } - - throw ExceptionFactory.DirectoryNotFound(FullName); - }), - _fileSystem.Storage - .GetLocation(destinationFileName - .EnsureValidFormat(_fileSystem, nameof(destinationFileName))) - .ThrowIfNotFound(_fileSystem, - () => { }, - () => - { - if (Execute.IsWindows) - { - throw ExceptionFactory.DirectoryNotFound(FullName); - } - - throw ExceptionFactory.FileNotFound(FullName); - }), - _fileSystem.Storage - .GetLocation(destinationBackupFileName) - .ThrowIfNotFound(_fileSystem, - () => { }, - () => - { - if (Execute.IsWindows) - { - throw ExceptionFactory.FileNotFound(FullName); - } - - throw ExceptionFactory.DirectoryNotFound(FullName); - })) - ?? throw ExceptionFactory.FileNotFound(FullName); - return _fileSystem.FileInfo.New(location.FullPath); - } - - /// - public IFileInfo Replace(string destinationFileName, - string? destinationBackupFileName, - bool ignoreMetadataErrors) - { - IStorageLocation location = _fileSystem.Storage.Replace( - Location, - _fileSystem.Storage.GetLocation( - destinationFileName - .EnsureValidFormat(_fileSystem, - nameof(destinationFileName))), - _fileSystem.Storage.GetLocation( - destinationBackupFileName), - ignoreMetadataErrors) - ?? throw ExceptionFactory.FileNotFound(FullName); - return _fileSystem.FileInfo.New(location.FullPath); - } - - #endregion - - [return: NotNullIfNotNull("location")] - internal static new FileInfoMock? New(IStorageLocation? location, - MockFileSystem fileSystem) - { - if (location == null) - { - return null; - } - - return new FileInfoMock(location, fileSystem); - } -} +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// A mocked file in the . +/// +internal sealed class FileInfoMock + : FileSystemInfoMock, IFileInfo +{ + private readonly MockFileSystem _fileSystem; + + private FileInfoMock(IStorageLocation location, + MockFileSystem fileSystem) + : base(fileSystem, location, FileSystemTypes.File) + { + _fileSystem = fileSystem; + } + + #region IFileInfo Members + + /// + public IDirectoryInfo? Directory + => DirectoryInfoMock.New(Location.GetParent(), + _fileSystem); + + /// + public string? DirectoryName + => Directory?.FullName; + + /// + public override bool Exists + => base.Exists && FileSystemType == FileSystemTypes.File; + + /// + public bool IsReadOnly + { + get => (Attributes & FileAttributes.ReadOnly) != 0; + set + { + if (value) + { + Attributes |= FileAttributes.ReadOnly; + } + else + { + Attributes &= ~FileAttributes.ReadOnly; + } + } + } + + /// + public long Length + { + get + { + if (Container is NullContainer || + Container.Type != FileSystemTypes.File) + { + throw ExceptionFactory.FileNotFound( + Execute.OnNetFramework( + () => Location.FriendlyName, + () => Location.FullPath)); + } + + return Container.GetBytes().Length; + } + } + + /// + public StreamWriter AppendText() + => new(Open(FileMode.Append, FileAccess.Write)); + + /// + public IFileInfo CopyTo(string destFileName) + { + destFileName.EnsureValidArgument(_fileSystem, nameof(destFileName)); + IStorageLocation destinationLocation = _fileSystem.Storage.GetLocation(destFileName); + Location.ThrowExceptionIfNotFound(_fileSystem); + IStorageLocation location = _fileSystem.Storage + .Copy(Location, destinationLocation) + ?? throw ExceptionFactory.FileNotFound(FullName); + return _fileSystem.FileInfo.New(location.FullPath); + } + + /// + public IFileInfo CopyTo(string destFileName, bool overwrite) + { + destFileName.EnsureValidArgument(_fileSystem, nameof(destFileName)); + IStorageLocation location = _fileSystem.Storage.Copy( + Location, + _fileSystem.Storage.GetLocation(destFileName), + overwrite) + ?? throw ExceptionFactory.FileNotFound(FullName); + return _fileSystem.FileInfo.New(location.FullPath); + } + + /// + public FileSystemStream Create() + { + Execute.NotOnNetFramework(Refresh); + return _fileSystem.File.Create(FullName); + } + + /// + public StreamWriter CreateText() + => new(_fileSystem.File.Create(FullName)); + + /// + [SupportedOSPlatform("windows")] + public void Decrypt() + => Container.Decrypt(); + + /// + [SupportedOSPlatform("windows")] + public void Encrypt() + => Container.Encrypt(); + + /// + public void MoveTo(string destFileName) + { + Location = _fileSystem.Storage.Move( + Location, + _fileSystem.Storage.GetLocation(destFileName + .EnsureValidArgument(_fileSystem, nameof(destFileName)))) + ?? throw ExceptionFactory.FileNotFound(FullName); + } + +#if FEATURE_FILE_MOVETO_OVERWRITE + /// + public void MoveTo(string destFileName, bool overwrite) + { + Location = _fileSystem.Storage.Move( + Location, + _fileSystem.Storage.GetLocation(destFileName + .EnsureValidArgument(_fileSystem, nameof(destFileName))), + overwrite) + ?? throw ExceptionFactory.FileNotFound(FullName); + } +#endif + + /// + public FileSystemStream Open(FileMode mode) + { + Execute.OnNetFrameworkIf(mode == FileMode.Append, + () => throw ExceptionFactory.AppendAccessOnlyInWriteOnlyMode()); + + return new FileStreamMock( + _fileSystem, + FullName, + mode, + mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, + FileShare.None); + } + + /// + public FileSystemStream Open(FileMode mode, FileAccess access) + => new FileStreamMock( + _fileSystem, + FullName, + mode, + access, + FileShare.None); + + /// + public FileSystemStream Open(FileMode mode, FileAccess access, FileShare share) + => new FileStreamMock( + _fileSystem, + FullName, + mode, + access, + share); + +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + /// + public FileSystemStream Open(FileStreamOptions options) + => _fileSystem.File.Open(FullName, options); +#endif + + /// + public FileSystemStream OpenRead() + => new FileStreamMock( + _fileSystem, + FullName, + FileMode.Open, + FileAccess.Read); + + /// + public StreamReader OpenText() + => new(OpenRead()); + + /// + public FileSystemStream OpenWrite() + => new FileStreamMock( + _fileSystem, + FullName, + FileMode.OpenOrCreate, + FileAccess.Write, + FileShare.None); + + /// + public IFileInfo Replace(string destinationFileName, + string? destinationBackupFileName) + { + IStorageLocation location = + _fileSystem + .Storage + .Replace( + Location.ThrowIfNotFound(_fileSystem, + () => { }, + () => + { + if (Execute.IsWindows) + { + throw ExceptionFactory.FileNotFound(FullName); + } + + throw ExceptionFactory.DirectoryNotFound(FullName); + }), + _fileSystem.Storage + .GetLocation(destinationFileName + .EnsureValidFormat(_fileSystem, nameof(destinationFileName))) + .ThrowIfNotFound(_fileSystem, + () => { }, + () => + { + if (Execute.IsWindows) + { + throw ExceptionFactory.DirectoryNotFound(FullName); + } + + throw ExceptionFactory.FileNotFound(FullName); + }), + _fileSystem.Storage + .GetLocation(destinationBackupFileName) + .ThrowIfNotFound(_fileSystem, + () => { }, + () => + { + if (Execute.IsWindows) + { + throw ExceptionFactory.FileNotFound(FullName); + } + + throw ExceptionFactory.DirectoryNotFound(FullName); + })) + ?? throw ExceptionFactory.FileNotFound(FullName); + return _fileSystem.FileInfo.New(location.FullPath); + } + + /// + public IFileInfo Replace(string destinationFileName, + string? destinationBackupFileName, + bool ignoreMetadataErrors) + { + IStorageLocation location = _fileSystem.Storage.Replace( + Location, + _fileSystem.Storage.GetLocation( + destinationFileName + .EnsureValidFormat(_fileSystem, + nameof(destinationFileName))), + _fileSystem.Storage.GetLocation( + destinationBackupFileName), + ignoreMetadataErrors) + ?? throw ExceptionFactory.FileNotFound(FullName); + return _fileSystem.FileInfo.New(location.FullPath); + } + + #endregion + + [return: NotNullIfNotNull("location")] + internal static new FileInfoMock? New(IStorageLocation? location, + MockFileSystem fileSystem) + { + if (location == null) + { + return null; + } + + return new FileInfoMock(location, fileSystem); + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs index 6d032b3d8..b9e73e136 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs @@ -1,966 +1,967 @@ -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE -using Microsoft.Win32.SafeHandles; -#endif -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Text; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; -#if FEATURE_FILESYSTEM_ASYNC -using System.Threading; -using System.Threading.Tasks; -#endif - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class FileMock : IFile -{ - private readonly MockFileSystem _fileSystem; - - internal FileMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - #region IFile Members - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public void AppendAllLines(string path, IEnumerable contents) - => AppendAllLines(path, contents, Encoding.Default); - - /// - public void AppendAllLines( - string path, - IEnumerable contents, - Encoding encoding) - { - _ = contents ?? throw new ArgumentNullException(nameof(contents)); - _ = encoding ?? throw new ArgumentNullException(nameof(encoding)); - AppendAllText( - path, - contents.Aggregate(string.Empty, (a, b) => a + b + Environment.NewLine), - encoding); - } - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task AppendAllLinesAsync(string path, IEnumerable contents, - CancellationToken cancellationToken = default) - => AppendAllLinesAsync(path, contents, Encoding.Default, cancellationToken); - - /// - public Task AppendAllLinesAsync(string path, IEnumerable contents, - Encoding encoding, - CancellationToken cancellationToken = default) - { - ThrowIfCancelled(cancellationToken); - AppendAllLines(path, contents, encoding); - return Task.CompletedTask; - } -#endif - - /// - public void AppendAllText(string path, string? contents) - => AppendAllText(path, contents, Encoding.Default); - - /// - public void AppendAllText(string path, string? contents, Encoding encoding) - { - IStorageContainer fileInfo = - _fileSystem.Storage.GetOrCreateContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem)), - InMemoryContainer.NewFile); - if (contents != null) - { - using (fileInfo.RequestAccess( - FileAccess.ReadWrite, - FileStreamFactoryMock.DefaultShare)) - { - if (fileInfo.GetBytes().Length == 0) - { - fileInfo.WriteBytes(encoding.GetPreamble()); - } - - fileInfo.AppendBytes(encoding.GetBytes(contents)); - } - } - } - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task AppendAllTextAsync(string path, string? contents, - CancellationToken cancellationToken = default) - => AppendAllTextAsync(path, contents, Encoding.Default, cancellationToken); - - /// - public Task AppendAllTextAsync(string path, string? contents, Encoding encoding, - CancellationToken cancellationToken = default) - { - ThrowIfCancelled(cancellationToken); - AppendAllText(path, contents, encoding); - return Task.CompletedTask; - } -#endif - - /// - public StreamWriter AppendText(string path) - => FileSystem.FileInfo - .New(path.EnsureValidFormat(FileSystem)) - .AppendText(); - - /// - public void Copy(string sourceFileName, string destFileName) - { - sourceFileName.EnsureValidFormat(_fileSystem, nameof(sourceFileName)); - destFileName.EnsureValidFormat(_fileSystem, nameof(destFileName)); - try - { - _fileSystem.FileInfo - .New(sourceFileName) - .CopyTo(destFileName); - } - catch (UnauthorizedAccessException) - { - Execute.OnNetFramework(() - => throw ExceptionFactory.AccessToPathDenied(sourceFileName)); - - throw; - } - } - - /// - public void Copy(string sourceFileName, string destFileName, bool overwrite) - => Execute.OnNetFramework( - () => - { - try - { - _fileSystem.FileInfo.New(sourceFileName - .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) - .CopyTo(destFileName - .EnsureValidFormat(_fileSystem, nameof(destFileName)), - overwrite); - } - catch (UnauthorizedAccessException) - { - throw ExceptionFactory.AccessToPathDenied(sourceFileName); - } - }, - () => - { - _fileSystem.FileInfo.New(sourceFileName - .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) - .CopyTo(destFileName - .EnsureValidFormat(_fileSystem, nameof(destFileName)), overwrite); - }); - - /// - public FileSystemStream Create(string path) - => new FileStreamMock( - _fileSystem, - path, - FileMode.Create, - FileAccess.ReadWrite, - FileShare.None); - - /// - public FileSystemStream Create(string path, int bufferSize) - => new FileStreamMock( - _fileSystem, - path, - FileMode.Create, - FileAccess.ReadWrite, - FileShare.None, - bufferSize); - - /// - public FileSystemStream Create(string path, int bufferSize, FileOptions options) - => new FileStreamMock( - _fileSystem, - path, - FileMode.Create, - FileAccess.ReadWrite, - FileShare.None, - bufferSize, - options); - -#if FEATURE_FILESYSTEM_LINK - /// - public IFileSystemInfo CreateSymbolicLink( - string path, string pathToTarget) - { - path.EnsureValidFormat(_fileSystem); - IFileInfo fileSystemInfo = - _fileSystem.FileInfo.New(path); - fileSystemInfo.CreateAsSymbolicLink(pathToTarget); - return fileSystemInfo; - } -#endif - - /// - public StreamWriter CreateText(string path) - => FileSystem.FileInfo - .New(path.EnsureValidFormat(FileSystem)) - .CreateText(); - - /// - [SupportedOSPlatform("windows")] - public void Decrypt(string path) - { - IStorageContainer container = GetContainerFromPath(path); - container.Decrypt(); - } - - /// - public void Delete(string path) - => _fileSystem.Storage.DeleteContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))); - - /// - [SupportedOSPlatform("windows")] - public void Encrypt(string path) - { - IStorageContainer container = GetContainerFromPath(path); - container.Encrypt(); - } - - /// - public bool Exists([NotNullWhen(true)] string? path) - { - if (string.IsNullOrEmpty(path)) - { - return false; - } - - return FileInfoMock.New( - _fileSystem.Storage.GetLocation(path), - _fileSystem) - .Exists; - } - - /// - public FileAttributes GetAttributes(string path) - { - IStorageContainer container = _fileSystem.Storage - .GetContainer(_fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem)) - .ThrowExceptionIfNotFound(_fileSystem)); - - return container.Attributes; - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public FileAttributes GetAttributes(SafeFileHandle fileHandle) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - return container.Attributes; - } -#endif - - /// - public DateTime GetCreationTime(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .CreationTime.Get(DateTimeKind.Local); - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public DateTime GetCreationTime(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) - .CreationTime.Get(DateTimeKind.Local); -#endif - - /// - public DateTime GetCreationTimeUtc(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .CreationTime.Get(DateTimeKind.Utc); - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public DateTime GetCreationTimeUtc(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) - .CreationTime.Get(DateTimeKind.Utc); -#endif - - /// - public DateTime GetLastAccessTime(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastAccessTime.Get(DateTimeKind.Local); - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public DateTime GetLastAccessTime(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) - .LastAccessTime.Get(DateTimeKind.Local); -#endif - - /// - public DateTime GetLastAccessTimeUtc(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastAccessTime.Get(DateTimeKind.Utc); - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public DateTime GetLastAccessTimeUtc(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) - .LastAccessTime.Get(DateTimeKind.Utc); -#endif - - /// - public DateTime GetLastWriteTime(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastWriteTime.Get(DateTimeKind.Local); - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public DateTime GetLastWriteTime(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) - .LastWriteTime.Get(DateTimeKind.Local); -#endif - - /// - public DateTime GetLastWriteTimeUtc(string path) - => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem))) - .LastWriteTime.Get(DateTimeKind.Utc); - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public DateTime GetLastWriteTimeUtc(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) - .LastWriteTime.Get(DateTimeKind.Utc); -#endif - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - [UnsupportedOSPlatform("windows")] - public UnixFileMode GetUnixFileMode(string path) - => Execute.OnWindows( - () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform(), - () => _fileSystem.Storage.GetContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem)) - .ThrowExceptionIfNotFound(_fileSystem)) - .UnixFileMode); -#endif - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - [UnsupportedOSPlatform("windows")] - public UnixFileMode GetUnixFileMode(SafeFileHandle fileHandle) - => Execute.OnWindows( - () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform(), - () => GetContainerFromSafeFileHandle(fileHandle) - .UnixFileMode); -#endif - - /// - public void Move(string sourceFileName, string destFileName) - => _fileSystem.FileInfo.New(sourceFileName - .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) - .MoveTo(destFileName - .EnsureValidFormat(_fileSystem, nameof(destFileName))); - -#if FEATURE_FILE_MOVETO_OVERWRITE - /// - public void Move(string sourceFileName, string destFileName, bool overwrite) - => _fileSystem.FileInfo.New(sourceFileName - .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) - .MoveTo(destFileName - .EnsureValidFormat(_fileSystem, nameof(destFileName)), overwrite); -#endif - - /// - public FileSystemStream Open(string path, FileMode mode) - => new FileStreamMock( - _fileSystem, - path, - mode, - mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, - FileShare.None); - - /// - public FileSystemStream Open(string path, FileMode mode, FileAccess access) - => new FileStreamMock( - _fileSystem, - path, - mode, - access, - FileShare.None); - - /// - public FileSystemStream Open( - string path, - FileMode mode, - FileAccess access, - FileShare share) - => new FileStreamMock( - _fileSystem, - path, - mode, - access, - share); - -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - /// - public FileSystemStream Open(string path, FileStreamOptions options) - => new FileStreamMock( - _fileSystem, - path, - options.Mode, - options.Access, - options.Share, - options.BufferSize, - options.Options); -#endif - - /// - public FileSystemStream OpenRead(string path) - => new FileStreamMock( - _fileSystem, - path, - FileMode.Open, - FileAccess.Read); - - /// - public StreamReader OpenText(string path) - => FileSystem.FileInfo - .New(path.EnsureValidFormat(FileSystem)) - .OpenText(); - - /// - public FileSystemStream OpenWrite(string path) - => new FileStreamMock( - _fileSystem, - path, - FileMode.OpenOrCreate, - FileAccess.Write, - FileShare.None); - - /// - public byte[] ReadAllBytes(string path) - { - IStorageContainer container = GetContainerFromPath(path); - using (container.RequestAccess( - FileAccess.Read, - FileStreamFactoryMock.DefaultShare)) - { - container.AdjustTimes(TimeAdjustments.LastAccessTime); - return container.GetBytes().ToArray(); - } - } - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task ReadAllBytesAsync(string path, - CancellationToken cancellationToken = - default) - { - ThrowIfCancelled(cancellationToken); - return Task.FromResult(ReadAllBytes(path)); - } -#endif - - /// - public string[] ReadAllLines(string path) - => ReadAllLines(path, Encoding.Default); - - /// - public string[] ReadAllLines(string path, Encoding encoding) - => ReadLines(path, encoding).ToArray(); - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task ReadAllLinesAsync( - string path, - CancellationToken cancellationToken = default) - => ReadAllLinesAsync(path, Encoding.Default, cancellationToken); - - /// - public Task ReadAllLinesAsync( - string path, - Encoding encoding, - CancellationToken cancellationToken = default) - { - ThrowIfCancelled(cancellationToken); - return Task.FromResult(ReadAllLines(path, encoding)); - } -#endif - - /// - public string ReadAllText(string path) - => ReadAllText(path, Encoding.Default); - - /// - public string ReadAllText(string path, Encoding encoding) - { - IStorageContainer container = GetContainerFromPath(path); - using (container.RequestAccess( - FileAccess.Read, - FileStreamFactoryMock.DefaultShare)) - { - container.AdjustTimes(TimeAdjustments.LastAccessTime); - using (MemoryStream ms = new(container.GetBytes())) - using (StreamReader sr = new(ms, encoding)) - { - return sr.ReadToEnd(); - } - } - } - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task ReadAllTextAsync( - string path, - CancellationToken cancellationToken = default) - => ReadAllTextAsync(path, Encoding.Default, cancellationToken); - - /// - public Task ReadAllTextAsync( - string path, - Encoding encoding, - CancellationToken cancellationToken = default) - { - ThrowIfCancelled(cancellationToken); - return Task.FromResult(ReadAllText(path, encoding)); - } -#endif - - /// - public IEnumerable ReadLines(string path) - => ReadLines(path, Encoding.Default); - - /// - public IEnumerable ReadLines(string path, Encoding encoding) - => EnumerateLines(ReadAllText(path, encoding)); - -#if FEATURE_FILESYSTEM_NET7 - /// - public IAsyncEnumerable ReadLinesAsync(string path, - CancellationToken cancellationToken = - default) - { - ThrowIfCancelled(cancellationToken); - return ReadAllLines(path).ToAsyncEnumerable(); - } - - /// - public IAsyncEnumerable ReadLinesAsync(string path, Encoding encoding, - CancellationToken cancellationToken = - default) - { - ThrowIfCancelled(cancellationToken); - return ReadAllLines(path, encoding).ToAsyncEnumerable(); - } -#endif - - /// - public void Replace(string sourceFileName, - string destinationFileName, - string? destinationBackupFileName) - => _fileSystem.FileInfo.New(sourceFileName - .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) - .Replace(destinationFileName - .EnsureValidFormat(_fileSystem, nameof(destinationFileName)), - destinationBackupFileName); - - /// - public void Replace(string sourceFileName, - string destinationFileName, - string? destinationBackupFileName, - bool ignoreMetadataErrors) - => _fileSystem.FileInfo.New(sourceFileName - .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) - .Replace(destinationFileName - .EnsureValidFormat(_fileSystem, nameof(destinationFileName)), - destinationBackupFileName, - ignoreMetadataErrors); - -#if FEATURE_FILESYSTEM_LINK - /// - public IFileSystemInfo? ResolveLinkTarget( - string linkPath, bool returnFinalTarget) - { - IStorageLocation location = - _fileSystem.Storage.GetLocation(linkPath - .EnsureValidFormat(_fileSystem, nameof(linkPath))); - Execute.OnWindows( - () => location.ThrowExceptionIfNotFound(_fileSystem), - () => location.ThrowExceptionIfNotFound(_fileSystem, - onDirectoryNotFound: ExceptionFactory.FileNotFound)); - try - { - IStorageLocation? targetLocation = - _fileSystem.Storage.ResolveLinkTarget(location, - returnFinalTarget); - if (targetLocation != null) - { - return FileSystemInfoMock.New(targetLocation, _fileSystem); - } - - return null; - } - catch (IOException) - { - throw ExceptionFactory.FileNameCannotBeResolved(linkPath, - Execute.IsWindows ? -2147022975 : -2146232800); - } - } -#endif - - /// - public void SetAttributes(string path, FileAttributes fileAttributes) - { - IStorageContainer container = GetContainerFromPath(path); - container.Attributes = fileAttributes; - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public void SetAttributes(SafeFileHandle fileHandle, FileAttributes fileAttributes) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.Attributes = fileAttributes; - } -#endif - - /// - public void SetCreationTime(string path, DateTime creationTime) - { - IStorageContainer container = - GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); - container.CreationTime.Set(creationTime, DateTimeKind.Local); - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public void SetCreationTime(SafeFileHandle fileHandle, DateTime creationTime) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.CreationTime.Set(creationTime, DateTimeKind.Local); - } -#endif - - /// - public void SetCreationTimeUtc(string path, DateTime creationTimeUtc) - { - IStorageContainer container = - GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); - container.CreationTime.Set(creationTimeUtc, DateTimeKind.Utc); - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public void SetCreationTimeUtc(SafeFileHandle fileHandle, DateTime creationTimeUtc) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.CreationTime.Set(creationTimeUtc, DateTimeKind.Utc); - } -#endif - - /// - public void SetLastAccessTime(string path, DateTime lastAccessTime) - { - IStorageContainer container = - GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); - container.LastAccessTime.Set(lastAccessTime, DateTimeKind.Local); - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public void SetLastAccessTime(SafeFileHandle fileHandle, DateTime lastAccessTime) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.LastAccessTime.Set(lastAccessTime, DateTimeKind.Local); - } -#endif - - /// - public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) - { - IStorageContainer container = - GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); - container.LastAccessTime.Set(lastAccessTimeUtc, DateTimeKind.Utc); - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public void SetLastAccessTimeUtc(SafeFileHandle fileHandle, - DateTime lastAccessTimeUtc) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.LastAccessTime.Set(lastAccessTimeUtc, DateTimeKind.Utc); - } -#endif - - /// - public void SetLastWriteTime(string path, DateTime lastWriteTime) - { - IStorageContainer container = - GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); - container.LastWriteTime.Set(lastWriteTime, DateTimeKind.Local); - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public void SetLastWriteTime(SafeFileHandle fileHandle, DateTime lastWriteTime) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.LastWriteTime.Set(lastWriteTime, DateTimeKind.Local); - } -#endif - - /// - public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) - { - IStorageContainer container = - GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); - container.LastWriteTime.Set(lastWriteTimeUtc, DateTimeKind.Utc); - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - public void SetLastWriteTimeUtc(SafeFileHandle fileHandle, DateTime lastWriteTimeUtc) - { - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.LastWriteTime.Set(lastWriteTimeUtc, DateTimeKind.Utc); - } -#endif - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - [UnsupportedOSPlatform("windows")] - public void SetUnixFileMode(string path, UnixFileMode mode) - { - Execute.OnWindows( - () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform()); - - IStorageContainer container = GetContainerFromPath(path); - container.UnixFileMode = mode; - } -#endif - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - /// - [UnsupportedOSPlatform("windows")] - public void SetUnixFileMode(SafeFileHandle fileHandle, UnixFileMode mode) - { - Execute.OnWindows( - () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform()); - - IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); - container.UnixFileMode = mode; - } -#endif - - /// - public void WriteAllBytes(string path, byte[] bytes) - { - _ = bytes ?? throw new ArgumentNullException(nameof(bytes)); - IStorageContainer container = - _fileSystem.Storage.GetOrCreateContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem)), - InMemoryContainer.NewFile); - if (container is not NullContainer) - { - Execute.OnWindowsIf( - container.Attributes.HasFlag(FileAttributes.Hidden), - () => throw ExceptionFactory.AccessToPathDenied()); - using (container.RequestAccess( - FileAccess.Write, - FileStreamFactoryMock.DefaultShare)) - { - container.WriteBytes(bytes); - } - } - } - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task WriteAllBytesAsync(string path, byte[] bytes, - CancellationToken cancellationToken = default) - { - ThrowIfCancelled(cancellationToken); - WriteAllBytes(path, bytes); - return Task.CompletedTask; - } -#endif - - /// - public void WriteAllLines(string path, string[] contents) - => WriteAllLines(path, contents, Encoding.Default); - - /// - public void WriteAllLines(string path, IEnumerable contents) - => WriteAllLines(path, contents, Encoding.Default); - - /// - public void WriteAllLines( - string path, - string[] contents, - Encoding encoding) - => WriteAllLines(path, contents.AsEnumerable(), encoding); - - /// - public void WriteAllLines( - string path, - IEnumerable contents, - Encoding encoding) - => WriteAllText( - path, - contents.Aggregate(string.Empty, (a, b) => a + b + Environment.NewLine), - encoding); - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task WriteAllLinesAsync( - string path, - IEnumerable contents, - CancellationToken cancellationToken = default) - => WriteAllLinesAsync(path, contents, Encoding.Default, cancellationToken); - - /// - public Task WriteAllLinesAsync( - string path, - IEnumerable contents, - Encoding encoding, - CancellationToken cancellationToken = default) - { - ThrowIfCancelled(cancellationToken); - WriteAllLines(path, contents, encoding); - return Task.CompletedTask; - } -#endif - - /// - public void WriteAllText(string path, string? contents) - => WriteAllText(path, contents, Encoding.Default); - - /// - public void WriteAllText(string path, string? contents, Encoding encoding) - { - IStorageContainer container = - _fileSystem.Storage.GetOrCreateContainer( - _fileSystem.Storage.GetLocation( - path.EnsureValidFormat(FileSystem)), - InMemoryContainer.NewFile); - if (container is not NullContainer && contents != null) - { - Execute.OnWindowsIf( - container.Attributes.HasFlag(FileAttributes.Hidden), - () => throw ExceptionFactory.AccessToPathDenied()); - using (container.RequestAccess( - FileAccess.Write, - FileStreamFactoryMock.DefaultShare)) - { - container.WriteBytes(encoding.GetPreamble()); - container.AppendBytes(encoding.GetBytes(contents)); - } - } - } - -#if FEATURE_FILESYSTEM_ASYNC - /// - public Task WriteAllTextAsync(string path, string? contents, - CancellationToken cancellationToken = default) - => WriteAllTextAsync(path, contents, Encoding.Default, cancellationToken); - - /// - public Task WriteAllTextAsync(string path, string? contents, Encoding encoding, - CancellationToken cancellationToken = default) - { - ThrowIfCancelled(cancellationToken); - WriteAllText(path, contents, encoding); - return Task.CompletedTask; - } -#endif - - #endregion - - private static IEnumerable EnumerateLines(string contents) - { - using (StringReader reader = new(contents)) - { - while (reader.ReadLine() is { } line) - { - yield return line; - } - } - } - - private enum ExceptionMode - { - Default, - FileNotFoundExceptionOnLinuxAndMac - } - - private IStorageContainer GetContainerFromPath(string path, - ExceptionMode exceptionMode = ExceptionMode.Default) - { - path.EnsureValidFormat(FileSystem); - IStorageLocation location = _fileSystem.Storage.GetLocation(path); - if (exceptionMode == ExceptionMode.FileNotFoundExceptionOnLinuxAndMac) - { - Execute.OnWindows( - () => location.ThrowExceptionIfNotFound(_fileSystem), - () => location.ThrowExceptionIfNotFound(_fileSystem, - onDirectoryNotFound: ExceptionFactory.FileNotFound)); - } - - if (exceptionMode == ExceptionMode.Default) - { - location.ThrowExceptionIfNotFound(_fileSystem); - } - - return _fileSystem.Storage.GetContainer(location); - } - -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE - private IStorageContainer GetContainerFromSafeFileHandle(SafeFileHandle fileHandle) - { - SafeFileHandleMock safeFileHandleMock = _fileSystem - .SafeFileHandleStrategy.MapSafeFileHandle(fileHandle); - IStorageContainer container = _fileSystem.Storage - .GetContainer(_fileSystem.Storage.GetLocation( - safeFileHandleMock.Path) - .ThrowExceptionIfNotFound(_fileSystem)); - if (container is NullContainer) - { - throw ExceptionFactory.FileNotFound(""); - } - - return container; - } -#endif - -#if FEATURE_FILESYSTEM_ASYNC - private static void ThrowIfCancelled(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - throw ExceptionFactory.TaskWasCanceled(); - } - } -#endif -} +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Text; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE +using Microsoft.Win32.SafeHandles; +#endif + +#if FEATURE_FILESYSTEM_ASYNC +using System.Threading; +using System.Threading.Tasks; +#endif + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class FileMock : IFile +{ + private readonly MockFileSystem _fileSystem; + + internal FileMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + #region IFile Members + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public void AppendAllLines(string path, IEnumerable contents) + => AppendAllLines(path, contents, Encoding.Default); + + /// + public void AppendAllLines( + string path, + IEnumerable contents, + Encoding encoding) + { + _ = contents ?? throw new ArgumentNullException(nameof(contents)); + _ = encoding ?? throw new ArgumentNullException(nameof(encoding)); + AppendAllText( + path, + contents.Aggregate(string.Empty, (a, b) => a + b + Environment.NewLine), + encoding); + } + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task AppendAllLinesAsync(string path, IEnumerable contents, + CancellationToken cancellationToken = default) + => AppendAllLinesAsync(path, contents, Encoding.Default, cancellationToken); + + /// + public Task AppendAllLinesAsync(string path, IEnumerable contents, + Encoding encoding, + CancellationToken cancellationToken = default) + { + ThrowIfCancelled(cancellationToken); + AppendAllLines(path, contents, encoding); + return Task.CompletedTask; + } +#endif + + /// + public void AppendAllText(string path, string? contents) + => AppendAllText(path, contents, Encoding.Default); + + /// + public void AppendAllText(string path, string? contents, Encoding encoding) + { + IStorageContainer fileInfo = + _fileSystem.Storage.GetOrCreateContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem)), + InMemoryContainer.NewFile); + if (contents != null) + { + using (fileInfo.RequestAccess( + FileAccess.ReadWrite, + FileStreamFactoryMock.DefaultShare)) + { + if (fileInfo.GetBytes().Length == 0) + { + fileInfo.WriteBytes(encoding.GetPreamble()); + } + + fileInfo.AppendBytes(encoding.GetBytes(contents)); + } + } + } + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task AppendAllTextAsync(string path, string? contents, + CancellationToken cancellationToken = default) + => AppendAllTextAsync(path, contents, Encoding.Default, cancellationToken); + + /// + public Task AppendAllTextAsync(string path, string? contents, Encoding encoding, + CancellationToken cancellationToken = default) + { + ThrowIfCancelled(cancellationToken); + AppendAllText(path, contents, encoding); + return Task.CompletedTask; + } +#endif + + /// + public StreamWriter AppendText(string path) + => FileSystem.FileInfo + .New(path.EnsureValidFormat(FileSystem)) + .AppendText(); + + /// + public void Copy(string sourceFileName, string destFileName) + { + sourceFileName.EnsureValidFormat(_fileSystem, nameof(sourceFileName)); + destFileName.EnsureValidFormat(_fileSystem, nameof(destFileName)); + try + { + _fileSystem.FileInfo + .New(sourceFileName) + .CopyTo(destFileName); + } + catch (UnauthorizedAccessException) + { + Execute.OnNetFramework(() + => throw ExceptionFactory.AccessToPathDenied(sourceFileName)); + + throw; + } + } + + /// + public void Copy(string sourceFileName, string destFileName, bool overwrite) + => Execute.OnNetFramework( + () => + { + try + { + _fileSystem.FileInfo.New(sourceFileName + .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) + .CopyTo(destFileName + .EnsureValidFormat(_fileSystem, nameof(destFileName)), + overwrite); + } + catch (UnauthorizedAccessException) + { + throw ExceptionFactory.AccessToPathDenied(sourceFileName); + } + }, + () => + { + _fileSystem.FileInfo.New(sourceFileName + .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) + .CopyTo(destFileName + .EnsureValidFormat(_fileSystem, nameof(destFileName)), overwrite); + }); + + /// + public FileSystemStream Create(string path) + => new FileStreamMock( + _fileSystem, + path, + FileMode.Create, + FileAccess.ReadWrite, + FileShare.None); + + /// + public FileSystemStream Create(string path, int bufferSize) + => new FileStreamMock( + _fileSystem, + path, + FileMode.Create, + FileAccess.ReadWrite, + FileShare.None, + bufferSize); + + /// + public FileSystemStream Create(string path, int bufferSize, FileOptions options) + => new FileStreamMock( + _fileSystem, + path, + FileMode.Create, + FileAccess.ReadWrite, + FileShare.None, + bufferSize, + options); + +#if FEATURE_FILESYSTEM_LINK + /// + public IFileSystemInfo CreateSymbolicLink( + string path, string pathToTarget) + { + path.EnsureValidFormat(_fileSystem); + IFileInfo fileSystemInfo = + _fileSystem.FileInfo.New(path); + fileSystemInfo.CreateAsSymbolicLink(pathToTarget); + return fileSystemInfo; + } +#endif + + /// + public StreamWriter CreateText(string path) + => FileSystem.FileInfo + .New(path.EnsureValidFormat(FileSystem)) + .CreateText(); + + /// + [SupportedOSPlatform("windows")] + public void Decrypt(string path) + { + IStorageContainer container = GetContainerFromPath(path); + container.Decrypt(); + } + + /// + public void Delete(string path) + => _fileSystem.Storage.DeleteContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))); + + /// + [SupportedOSPlatform("windows")] + public void Encrypt(string path) + { + IStorageContainer container = GetContainerFromPath(path); + container.Encrypt(); + } + + /// + public bool Exists([NotNullWhen(true)] string? path) + { + if (string.IsNullOrEmpty(path)) + { + return false; + } + + return FileInfoMock.New( + _fileSystem.Storage.GetLocation(path), + _fileSystem) + .Exists; + } + + /// + public FileAttributes GetAttributes(string path) + { + IStorageContainer container = _fileSystem.Storage + .GetContainer(_fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem)) + .ThrowExceptionIfNotFound(_fileSystem)); + + return container.Attributes; + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public FileAttributes GetAttributes(SafeFileHandle fileHandle) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + return container.Attributes; + } +#endif + + /// + public DateTime GetCreationTime(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .CreationTime.Get(DateTimeKind.Local); + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public DateTime GetCreationTime(SafeFileHandle fileHandle) + => GetContainerFromSafeFileHandle(fileHandle) + .CreationTime.Get(DateTimeKind.Local); +#endif + + /// + public DateTime GetCreationTimeUtc(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .CreationTime.Get(DateTimeKind.Utc); + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public DateTime GetCreationTimeUtc(SafeFileHandle fileHandle) + => GetContainerFromSafeFileHandle(fileHandle) + .CreationTime.Get(DateTimeKind.Utc); +#endif + + /// + public DateTime GetLastAccessTime(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastAccessTime.Get(DateTimeKind.Local); + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public DateTime GetLastAccessTime(SafeFileHandle fileHandle) + => GetContainerFromSafeFileHandle(fileHandle) + .LastAccessTime.Get(DateTimeKind.Local); +#endif + + /// + public DateTime GetLastAccessTimeUtc(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastAccessTime.Get(DateTimeKind.Utc); + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public DateTime GetLastAccessTimeUtc(SafeFileHandle fileHandle) + => GetContainerFromSafeFileHandle(fileHandle) + .LastAccessTime.Get(DateTimeKind.Utc); +#endif + + /// + public DateTime GetLastWriteTime(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastWriteTime.Get(DateTimeKind.Local); + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public DateTime GetLastWriteTime(SafeFileHandle fileHandle) + => GetContainerFromSafeFileHandle(fileHandle) + .LastWriteTime.Get(DateTimeKind.Local); +#endif + + /// + public DateTime GetLastWriteTimeUtc(string path) + => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem))) + .LastWriteTime.Get(DateTimeKind.Utc); + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public DateTime GetLastWriteTimeUtc(SafeFileHandle fileHandle) + => GetContainerFromSafeFileHandle(fileHandle) + .LastWriteTime.Get(DateTimeKind.Utc); +#endif + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + [UnsupportedOSPlatform("windows")] + public UnixFileMode GetUnixFileMode(string path) + => Execute.OnWindows( + () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform(), + () => _fileSystem.Storage.GetContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem)) + .ThrowExceptionIfNotFound(_fileSystem)) + .UnixFileMode); +#endif + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + [UnsupportedOSPlatform("windows")] + public UnixFileMode GetUnixFileMode(SafeFileHandle fileHandle) + => Execute.OnWindows( + () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform(), + () => GetContainerFromSafeFileHandle(fileHandle) + .UnixFileMode); +#endif + + /// + public void Move(string sourceFileName, string destFileName) + => _fileSystem.FileInfo.New(sourceFileName + .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) + .MoveTo(destFileName + .EnsureValidFormat(_fileSystem, nameof(destFileName))); + +#if FEATURE_FILE_MOVETO_OVERWRITE + /// + public void Move(string sourceFileName, string destFileName, bool overwrite) + => _fileSystem.FileInfo.New(sourceFileName + .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) + .MoveTo(destFileName + .EnsureValidFormat(_fileSystem, nameof(destFileName)), overwrite); +#endif + + /// + public FileSystemStream Open(string path, FileMode mode) + => new FileStreamMock( + _fileSystem, + path, + mode, + mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, + FileShare.None); + + /// + public FileSystemStream Open(string path, FileMode mode, FileAccess access) + => new FileStreamMock( + _fileSystem, + path, + mode, + access, + FileShare.None); + + /// + public FileSystemStream Open( + string path, + FileMode mode, + FileAccess access, + FileShare share) + => new FileStreamMock( + _fileSystem, + path, + mode, + access, + share); + +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + /// + public FileSystemStream Open(string path, FileStreamOptions options) + => new FileStreamMock( + _fileSystem, + path, + options.Mode, + options.Access, + options.Share, + options.BufferSize, + options.Options); +#endif + + /// + public FileSystemStream OpenRead(string path) + => new FileStreamMock( + _fileSystem, + path, + FileMode.Open, + FileAccess.Read); + + /// + public StreamReader OpenText(string path) + => FileSystem.FileInfo + .New(path.EnsureValidFormat(FileSystem)) + .OpenText(); + + /// + public FileSystemStream OpenWrite(string path) + => new FileStreamMock( + _fileSystem, + path, + FileMode.OpenOrCreate, + FileAccess.Write, + FileShare.None); + + /// + public byte[] ReadAllBytes(string path) + { + IStorageContainer container = GetContainerFromPath(path); + using (container.RequestAccess( + FileAccess.Read, + FileStreamFactoryMock.DefaultShare)) + { + container.AdjustTimes(TimeAdjustments.LastAccessTime); + return container.GetBytes().ToArray(); + } + } + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task ReadAllBytesAsync(string path, + CancellationToken cancellationToken = + default) + { + ThrowIfCancelled(cancellationToken); + return Task.FromResult(ReadAllBytes(path)); + } +#endif + + /// + public string[] ReadAllLines(string path) + => ReadAllLines(path, Encoding.Default); + + /// + public string[] ReadAllLines(string path, Encoding encoding) + => ReadLines(path, encoding).ToArray(); + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task ReadAllLinesAsync( + string path, + CancellationToken cancellationToken = default) + => ReadAllLinesAsync(path, Encoding.Default, cancellationToken); + + /// + public Task ReadAllLinesAsync( + string path, + Encoding encoding, + CancellationToken cancellationToken = default) + { + ThrowIfCancelled(cancellationToken); + return Task.FromResult(ReadAllLines(path, encoding)); + } +#endif + + /// + public string ReadAllText(string path) + => ReadAllText(path, Encoding.Default); + + /// + public string ReadAllText(string path, Encoding encoding) + { + IStorageContainer container = GetContainerFromPath(path); + using (container.RequestAccess( + FileAccess.Read, + FileStreamFactoryMock.DefaultShare)) + { + container.AdjustTimes(TimeAdjustments.LastAccessTime); + using (MemoryStream ms = new(container.GetBytes())) + using (StreamReader sr = new(ms, encoding)) + { + return sr.ReadToEnd(); + } + } + } + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task ReadAllTextAsync( + string path, + CancellationToken cancellationToken = default) + => ReadAllTextAsync(path, Encoding.Default, cancellationToken); + + /// + public Task ReadAllTextAsync( + string path, + Encoding encoding, + CancellationToken cancellationToken = default) + { + ThrowIfCancelled(cancellationToken); + return Task.FromResult(ReadAllText(path, encoding)); + } +#endif + + /// + public IEnumerable ReadLines(string path) + => ReadLines(path, Encoding.Default); + + /// + public IEnumerable ReadLines(string path, Encoding encoding) + => EnumerateLines(ReadAllText(path, encoding)); + +#if FEATURE_FILESYSTEM_NET7 + /// + public IAsyncEnumerable ReadLinesAsync(string path, + CancellationToken cancellationToken = + default) + { + ThrowIfCancelled(cancellationToken); + return ReadAllLines(path).ToAsyncEnumerable(); + } + + /// + public IAsyncEnumerable ReadLinesAsync(string path, Encoding encoding, + CancellationToken cancellationToken = + default) + { + ThrowIfCancelled(cancellationToken); + return ReadAllLines(path, encoding).ToAsyncEnumerable(); + } +#endif + + /// + public void Replace(string sourceFileName, + string destinationFileName, + string? destinationBackupFileName) + => _fileSystem.FileInfo.New(sourceFileName + .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) + .Replace(destinationFileName + .EnsureValidFormat(_fileSystem, nameof(destinationFileName)), + destinationBackupFileName); + + /// + public void Replace(string sourceFileName, + string destinationFileName, + string? destinationBackupFileName, + bool ignoreMetadataErrors) + => _fileSystem.FileInfo.New(sourceFileName + .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) + .Replace(destinationFileName + .EnsureValidFormat(_fileSystem, nameof(destinationFileName)), + destinationBackupFileName, + ignoreMetadataErrors); + +#if FEATURE_FILESYSTEM_LINK + /// + public IFileSystemInfo? ResolveLinkTarget( + string linkPath, bool returnFinalTarget) + { + IStorageLocation location = + _fileSystem.Storage.GetLocation(linkPath + .EnsureValidFormat(_fileSystem, nameof(linkPath))); + Execute.OnWindows( + () => location.ThrowExceptionIfNotFound(_fileSystem), + () => location.ThrowExceptionIfNotFound(_fileSystem, + onDirectoryNotFound: ExceptionFactory.FileNotFound)); + try + { + IStorageLocation? targetLocation = + _fileSystem.Storage.ResolveLinkTarget(location, + returnFinalTarget); + if (targetLocation != null) + { + return FileSystemInfoMock.New(targetLocation, _fileSystem); + } + + return null; + } + catch (IOException) + { + throw ExceptionFactory.FileNameCannotBeResolved(linkPath, + Execute.IsWindows ? -2147022975 : -2146232800); + } + } +#endif + + /// + public void SetAttributes(string path, FileAttributes fileAttributes) + { + IStorageContainer container = GetContainerFromPath(path); + container.Attributes = fileAttributes; + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public void SetAttributes(SafeFileHandle fileHandle, FileAttributes fileAttributes) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.Attributes = fileAttributes; + } +#endif + + /// + public void SetCreationTime(string path, DateTime creationTime) + { + IStorageContainer container = + GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); + container.CreationTime.Set(creationTime, DateTimeKind.Local); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public void SetCreationTime(SafeFileHandle fileHandle, DateTime creationTime) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.CreationTime.Set(creationTime, DateTimeKind.Local); + } +#endif + + /// + public void SetCreationTimeUtc(string path, DateTime creationTimeUtc) + { + IStorageContainer container = + GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); + container.CreationTime.Set(creationTimeUtc, DateTimeKind.Utc); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public void SetCreationTimeUtc(SafeFileHandle fileHandle, DateTime creationTimeUtc) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.CreationTime.Set(creationTimeUtc, DateTimeKind.Utc); + } +#endif + + /// + public void SetLastAccessTime(string path, DateTime lastAccessTime) + { + IStorageContainer container = + GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); + container.LastAccessTime.Set(lastAccessTime, DateTimeKind.Local); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public void SetLastAccessTime(SafeFileHandle fileHandle, DateTime lastAccessTime) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.LastAccessTime.Set(lastAccessTime, DateTimeKind.Local); + } +#endif + + /// + public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) + { + IStorageContainer container = + GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); + container.LastAccessTime.Set(lastAccessTimeUtc, DateTimeKind.Utc); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public void SetLastAccessTimeUtc(SafeFileHandle fileHandle, + DateTime lastAccessTimeUtc) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.LastAccessTime.Set(lastAccessTimeUtc, DateTimeKind.Utc); + } +#endif + + /// + public void SetLastWriteTime(string path, DateTime lastWriteTime) + { + IStorageContainer container = + GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); + container.LastWriteTime.Set(lastWriteTime, DateTimeKind.Local); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public void SetLastWriteTime(SafeFileHandle fileHandle, DateTime lastWriteTime) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.LastWriteTime.Set(lastWriteTime, DateTimeKind.Local); + } +#endif + + /// + public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) + { + IStorageContainer container = + GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); + container.LastWriteTime.Set(lastWriteTimeUtc, DateTimeKind.Utc); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + public void SetLastWriteTimeUtc(SafeFileHandle fileHandle, DateTime lastWriteTimeUtc) + { + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.LastWriteTime.Set(lastWriteTimeUtc, DateTimeKind.Utc); + } +#endif + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + [UnsupportedOSPlatform("windows")] + public void SetUnixFileMode(string path, UnixFileMode mode) + { + Execute.OnWindows( + () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform()); + + IStorageContainer container = GetContainerFromPath(path); + container.UnixFileMode = mode; + } +#endif + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + /// + [UnsupportedOSPlatform("windows")] + public void SetUnixFileMode(SafeFileHandle fileHandle, UnixFileMode mode) + { + Execute.OnWindows( + () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform()); + + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); + container.UnixFileMode = mode; + } +#endif + + /// + public void WriteAllBytes(string path, byte[] bytes) + { + _ = bytes ?? throw new ArgumentNullException(nameof(bytes)); + IStorageContainer container = + _fileSystem.Storage.GetOrCreateContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem)), + InMemoryContainer.NewFile); + if (container is not NullContainer) + { + Execute.OnWindowsIf( + container.Attributes.HasFlag(FileAttributes.Hidden), + () => throw ExceptionFactory.AccessToPathDenied()); + using (container.RequestAccess( + FileAccess.Write, + FileStreamFactoryMock.DefaultShare)) + { + container.WriteBytes(bytes); + } + } + } + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task WriteAllBytesAsync(string path, byte[] bytes, + CancellationToken cancellationToken = default) + { + ThrowIfCancelled(cancellationToken); + WriteAllBytes(path, bytes); + return Task.CompletedTask; + } +#endif + + /// + public void WriteAllLines(string path, string[] contents) + => WriteAllLines(path, contents, Encoding.Default); + + /// + public void WriteAllLines(string path, IEnumerable contents) + => WriteAllLines(path, contents, Encoding.Default); + + /// + public void WriteAllLines( + string path, + string[] contents, + Encoding encoding) + => WriteAllLines(path, contents.AsEnumerable(), encoding); + + /// + public void WriteAllLines( + string path, + IEnumerable contents, + Encoding encoding) + => WriteAllText( + path, + contents.Aggregate(string.Empty, (a, b) => a + b + Environment.NewLine), + encoding); + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task WriteAllLinesAsync( + string path, + IEnumerable contents, + CancellationToken cancellationToken = default) + => WriteAllLinesAsync(path, contents, Encoding.Default, cancellationToken); + + /// + public Task WriteAllLinesAsync( + string path, + IEnumerable contents, + Encoding encoding, + CancellationToken cancellationToken = default) + { + ThrowIfCancelled(cancellationToken); + WriteAllLines(path, contents, encoding); + return Task.CompletedTask; + } +#endif + + /// + public void WriteAllText(string path, string? contents) + => WriteAllText(path, contents, Encoding.Default); + + /// + public void WriteAllText(string path, string? contents, Encoding encoding) + { + IStorageContainer container = + _fileSystem.Storage.GetOrCreateContainer( + _fileSystem.Storage.GetLocation( + path.EnsureValidFormat(FileSystem)), + InMemoryContainer.NewFile); + if (container is not NullContainer && contents != null) + { + Execute.OnWindowsIf( + container.Attributes.HasFlag(FileAttributes.Hidden), + () => throw ExceptionFactory.AccessToPathDenied()); + using (container.RequestAccess( + FileAccess.Write, + FileStreamFactoryMock.DefaultShare)) + { + container.WriteBytes(encoding.GetPreamble()); + container.AppendBytes(encoding.GetBytes(contents)); + } + } + } + +#if FEATURE_FILESYSTEM_ASYNC + /// + public Task WriteAllTextAsync(string path, string? contents, + CancellationToken cancellationToken = default) + => WriteAllTextAsync(path, contents, Encoding.Default, cancellationToken); + + /// + public Task WriteAllTextAsync(string path, string? contents, Encoding encoding, + CancellationToken cancellationToken = default) + { + ThrowIfCancelled(cancellationToken); + WriteAllText(path, contents, encoding); + return Task.CompletedTask; + } +#endif + + #endregion + + private static IEnumerable EnumerateLines(string contents) + { + using (StringReader reader = new(contents)) + { + while (reader.ReadLine() is { } line) + { + yield return line; + } + } + } + + private enum ExceptionMode + { + Default, + FileNotFoundExceptionOnLinuxAndMac + } + + private IStorageContainer GetContainerFromPath(string path, + ExceptionMode exceptionMode = ExceptionMode.Default) + { + path.EnsureValidFormat(FileSystem); + IStorageLocation location = _fileSystem.Storage.GetLocation(path); + if (exceptionMode == ExceptionMode.FileNotFoundExceptionOnLinuxAndMac) + { + Execute.OnWindows( + () => location.ThrowExceptionIfNotFound(_fileSystem), + () => location.ThrowExceptionIfNotFound(_fileSystem, + onDirectoryNotFound: ExceptionFactory.FileNotFound)); + } + + if (exceptionMode == ExceptionMode.Default) + { + location.ThrowExceptionIfNotFound(_fileSystem); + } + + return _fileSystem.Storage.GetContainer(location); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + private IStorageContainer GetContainerFromSafeFileHandle(SafeFileHandle fileHandle) + { + SafeFileHandleMock safeFileHandleMock = _fileSystem + .SafeFileHandleStrategy.MapSafeFileHandle(fileHandle); + IStorageContainer container = _fileSystem.Storage + .GetContainer(_fileSystem.Storage.GetLocation( + safeFileHandleMock.Path) + .ThrowExceptionIfNotFound(_fileSystem)); + if (container is NullContainer) + { + throw ExceptionFactory.FileNotFound(""); + } + + return container; + } +#endif + +#if FEATURE_FILESYSTEM_ASYNC + private static void ThrowIfCancelled(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + throw ExceptionFactory.TaskWasCanceled(); + } + } +#endif +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs index ca16d054a..aca33ee87 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs @@ -1,150 +1,150 @@ -using Microsoft.Win32.SafeHandles; -using System.IO; -using Testably.Abstractions.Testing.Helpers; -#if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; -#endif - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class FileStreamFactoryMock : IFileStreamFactory -{ - internal const FileShare DefaultShare = FileShare.Read; - private const int DefaultBufferSize = 4096; - private const bool DefaultUseAsync = false; - private readonly MockFileSystem _fileSystem; - - internal FileStreamFactoryMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - #region IFileStreamFactory Members - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public FileSystemStream New(string path, FileMode mode) - => New(path, - mode, - mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, - DefaultShare, - DefaultBufferSize, - DefaultUseAsync); - - /// - public FileSystemStream New(string path, FileMode mode, FileAccess access) - => New(path, mode, access, DefaultShare, DefaultBufferSize, DefaultUseAsync); - - /// - public FileSystemStream New(string path, - FileMode mode, - FileAccess access, - FileShare share) - => New(path, mode, access, share, DefaultBufferSize, DefaultUseAsync); - - /// - public FileSystemStream New(string path, - FileMode mode, - FileAccess access, - FileShare share, - int bufferSize) - => New(path, mode, access, share, bufferSize, DefaultUseAsync); - - /// - public FileSystemStream New(string path, - FileMode mode, - FileAccess access, - FileShare share, - int bufferSize, - bool useAsync) - => New(path, - mode, - access, - share, - bufferSize, - useAsync ? FileOptions.Asynchronous : FileOptions.None); - - /// - public FileSystemStream New(string path, - FileMode mode, - FileAccess access, - FileShare share, - int bufferSize, - FileOptions options) - => new FileStreamMock(_fileSystem, - path, - mode, - access, - share, - bufferSize, - options); - - /// -#if NET6_0_OR_GREATER - [ExcludeFromCodeCoverage(Justification = "SafeFileHandle cannot be unit tested.")] -#endif - public FileSystemStream New(SafeFileHandle handle, FileAccess access) - { - SafeFileHandleMock safeFileHandleMock = _fileSystem - .SafeFileHandleStrategy.MapSafeFileHandle(handle); - return New( - safeFileHandleMock.Path, - safeFileHandleMock.Mode, - access, - safeFileHandleMock.Share); - } - - /// -#if NET6_0_OR_GREATER - [ExcludeFromCodeCoverage(Justification = "SafeFileHandle cannot be unit tested.")] -#endif - public FileSystemStream New(SafeFileHandle handle, FileAccess access, int bufferSize) - { - SafeFileHandleMock safeFileHandleMock = _fileSystem - .SafeFileHandleStrategy.MapSafeFileHandle(handle); - return New( - safeFileHandleMock.Path, - safeFileHandleMock.Mode, - access, - safeFileHandleMock.Share, - bufferSize); - } - - /// -#if NET6_0_OR_GREATER - [ExcludeFromCodeCoverage(Justification = "SafeFileHandle cannot be unit tested.")] -#endif - public FileSystemStream New(SafeFileHandle handle, FileAccess access, int bufferSize, - bool isAsync) - { - SafeFileHandleMock safeFileHandleMock = _fileSystem - .SafeFileHandleStrategy.MapSafeFileHandle(handle); - return New( - safeFileHandleMock.Path, - safeFileHandleMock.Mode, - access, - safeFileHandleMock.Share, - bufferSize, - isAsync); - } - -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - /// - public FileSystemStream New(string path, FileStreamOptions options) - => New(path, - options.Mode, - options.Access, - options.Share, - options.BufferSize, - options.Options); -#endif - - /// - public FileSystemStream Wrap(FileStream fileStream) - => throw ExceptionFactory.NotSupportedFileStreamWrapping(); - - #endregion -} +using Microsoft.Win32.SafeHandles; +using System.IO; +using Testably.Abstractions.Testing.Helpers; +#if NET6_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class FileStreamFactoryMock : IFileStreamFactory +{ + internal const FileShare DefaultShare = FileShare.Read; + private const int DefaultBufferSize = 4096; + private const bool DefaultUseAsync = false; + private readonly MockFileSystem _fileSystem; + + internal FileStreamFactoryMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + #region IFileStreamFactory Members + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public FileSystemStream New(string path, FileMode mode) + => New(path, + mode, + mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, + DefaultShare, + DefaultBufferSize, + DefaultUseAsync); + + /// + public FileSystemStream New(string path, FileMode mode, FileAccess access) + => New(path, mode, access, DefaultShare, DefaultBufferSize, DefaultUseAsync); + + /// + public FileSystemStream New(string path, + FileMode mode, + FileAccess access, + FileShare share) + => New(path, mode, access, share, DefaultBufferSize, DefaultUseAsync); + + /// + public FileSystemStream New(string path, + FileMode mode, + FileAccess access, + FileShare share, + int bufferSize) + => New(path, mode, access, share, bufferSize, DefaultUseAsync); + + /// + public FileSystemStream New(string path, + FileMode mode, + FileAccess access, + FileShare share, + int bufferSize, + bool useAsync) + => New(path, + mode, + access, + share, + bufferSize, + useAsync ? FileOptions.Asynchronous : FileOptions.None); + + /// + public FileSystemStream New(string path, + FileMode mode, + FileAccess access, + FileShare share, + int bufferSize, + FileOptions options) + => new FileStreamMock(_fileSystem, + path, + mode, + access, + share, + bufferSize, + options); + + /// +#if NET6_0_OR_GREATER + [ExcludeFromCodeCoverage(Justification = "SafeFileHandle cannot be unit tested.")] +#endif + public FileSystemStream New(SafeFileHandle handle, FileAccess access) + { + SafeFileHandleMock safeFileHandleMock = _fileSystem + .SafeFileHandleStrategy.MapSafeFileHandle(handle); + return New( + safeFileHandleMock.Path, + safeFileHandleMock.Mode, + access, + safeFileHandleMock.Share); + } + + /// +#if NET6_0_OR_GREATER + [ExcludeFromCodeCoverage(Justification = "SafeFileHandle cannot be unit tested.")] +#endif + public FileSystemStream New(SafeFileHandle handle, FileAccess access, int bufferSize) + { + SafeFileHandleMock safeFileHandleMock = _fileSystem + .SafeFileHandleStrategy.MapSafeFileHandle(handle); + return New( + safeFileHandleMock.Path, + safeFileHandleMock.Mode, + access, + safeFileHandleMock.Share, + bufferSize); + } + + /// +#if NET6_0_OR_GREATER + [ExcludeFromCodeCoverage(Justification = "SafeFileHandle cannot be unit tested.")] +#endif + public FileSystemStream New(SafeFileHandle handle, FileAccess access, int bufferSize, + bool isAsync) + { + SafeFileHandleMock safeFileHandleMock = _fileSystem + .SafeFileHandleStrategy.MapSafeFileHandle(handle); + return New( + safeFileHandleMock.Path, + safeFileHandleMock.Mode, + access, + safeFileHandleMock.Share, + bufferSize, + isAsync); + } + +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + /// + public FileSystemStream New(string path, FileStreamOptions options) + => New(path, + options.Mode, + options.Access, + options.Share, + options.BufferSize, + options.Options); +#endif + + /// + public FileSystemStream Wrap(FileStream fileStream) + => throw ExceptionFactory.NotSupportedFileStreamWrapping(); + + #endregion +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs index 54ac14310..094a3a452 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs @@ -1,447 +1,447 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// A mocked file stream in the . -/// -internal sealed class FileStreamMock : FileSystemStream -{ - /// - public override bool CanRead - => _access.HasFlag(FileAccess.Read); - - /// - public override bool CanWrite - => _access.HasFlag(FileAccess.Write); - - /// - public override IFileSystemExtensibility Extensibility - => _container.Extensibility; - - private readonly FileAccess _access; - private readonly IDisposable _accessLock; - private readonly IStorageContainer _container; - private readonly MockFileSystem _fileSystem; - private readonly long _initialPosition; - private bool _isContentChanged; - private bool _isDisposed; - private readonly FileMode _mode; - private readonly FileOptions _options; - private readonly MemoryStream _stream; - - internal FileStreamMock(MockFileSystem fileSystem, - string? path, - FileMode mode, - FileAccess access, - FileShare share = FileShare.Read, - int bufferSize = 4096, - FileOptions options = FileOptions.None) - : this(new MemoryStream(), - fileSystem, - path.EnsureValidFormat(fileSystem, nameof(path)), - mode, - access, - share, - bufferSize, - options) - { - } - - private FileStreamMock(MemoryStream stream, - MockFileSystem fileSystem, - string? path, - FileMode mode, - FileAccess access, - FileShare share, - int bufferSize, - FileOptions options) - : base( - stream, - path == null ? null : fileSystem.Path.GetFullPath(path), - (options & FileOptions.Asynchronous) != 0) - { - ThrowIfInvalidModeAccess(mode, access); - - _stream = stream; - _fileSystem = fileSystem; - _mode = mode; - _access = access; - _ = bufferSize; - _options = options; - _initialPosition = Position; - - IStorageLocation location = _fileSystem.Storage.GetLocation(Name); - location.ThrowExceptionIfNotFound(_fileSystem, true); - IStorageContainer file = _fileSystem.Storage.GetContainer(location); - if (file is NullContainer) - { - if (_mode.Equals(FileMode.Open) || - _mode.Equals(FileMode.Truncate)) - { - throw ExceptionFactory.FileNotFound( - _fileSystem.Path.GetFullPath(Name)); - } - - file = _fileSystem.Storage.GetOrCreateContainer(location, - InMemoryContainer.NewFile); - } - else if (file.Type == FileSystemTypes.Directory) - { - Execute.OnWindows( - () => - throw ExceptionFactory.AccessToPathDenied( - _fileSystem.Path.GetFullPath(Name)), - () => - throw ExceptionFactory.FileAlreadyExists( - _fileSystem.Path.GetFullPath(Name), 17)); - } - else if (_mode.Equals(FileMode.CreateNew)) - { - throw ExceptionFactory.FileAlreadyExists( - _fileSystem.Path.GetFullPath(Name), - Execute.IsWindows ? -2147024816 : 17); - } - - if (file.Attributes.HasFlag(FileAttributes.ReadOnly) && - access.HasFlag(FileAccess.Write)) - { - throw ExceptionFactory.AccessToPathDenied(location.FullPath); - } - - _accessLock = file.RequestAccess(access, share); - - _container = file; - - InitializeStream(); - } - - /// - public override IAsyncResult BeginRead(byte[] buffer, - int offset, - int count, - AsyncCallback? callback, - object? state) - { - ThrowIfDisposed(); - if (!CanRead) - { - throw ExceptionFactory.StreamDoesNotSupportReading(); - } - - return base.BeginRead(buffer, offset, count, callback, state); - } - - /// - public override IAsyncResult BeginWrite(byte[] buffer, - int offset, - int count, - AsyncCallback? callback, - object? state) - { - ThrowIfDisposed(); - if (!CanWrite) - { - throw ExceptionFactory.StreamDoesNotSupportWriting(); - } - - return base.BeginWrite(buffer, offset, count, callback, state); - } - - /// - public override void CopyTo(Stream destination, int bufferSize) - { - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - base.CopyTo(destination, bufferSize); - } - - /// - public override Task CopyToAsync(Stream destination, int bufferSize, - CancellationToken cancellationToken) - { - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - return base.CopyToAsync(destination, bufferSize, cancellationToken); - } - - /// - public override int EndRead(IAsyncResult asyncResult) - { - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - return base.EndRead(asyncResult); - } - - /// - public override void EndWrite(IAsyncResult asyncResult) - { - _isContentChanged = true; - base.EndWrite(asyncResult); - } - - /// - public override void Flush() - { - ThrowIfDisposed(); - InternalFlush(); - } - - /// - public override Task FlushAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - throw ExceptionFactory.TaskWasCanceled(); - } - - Flush(); - return Task.CompletedTask; - } - - /// - public override int Read(byte[] buffer, int offset, int count) - { - if (!CanRead) - { - throw ExceptionFactory.StreamDoesNotSupportReading(); - } - - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - return base.Read(buffer, offset, count); - } - -#if FEATURE_SPAN - /// - public override int Read(Span buffer) - { - if (!CanRead) - { - throw ExceptionFactory.StreamDoesNotSupportReading(); - } - - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - return base.Read(buffer); - } -#endif - - /// - public override Task ReadAsync(byte[] buffer, int offset, int count, - CancellationToken cancellationToken) - { - if (!CanRead) - { - throw ExceptionFactory.StreamDoesNotSupportReading(); - } - - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - return base.ReadAsync(buffer, offset, count, cancellationToken); - } - -#if FEATURE_SPAN - /// - public override ValueTask ReadAsync(Memory buffer, - CancellationToken cancellationToken = - new()) - { - if (!CanRead) - { - throw ExceptionFactory.StreamDoesNotSupportReading(); - } - - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - return base.ReadAsync(buffer, cancellationToken); - } -#endif - - /// - public override int ReadByte() - { - if (!CanRead) - { - throw ExceptionFactory.StreamDoesNotSupportReading(); - } - - _container.AdjustTimes(TimeAdjustments.LastAccessTime); - return base.ReadByte(); - } - - /// - public override long Seek(long offset, SeekOrigin origin) - { - if (_mode == FileMode.Append && offset <= _initialPosition) - { - throw ExceptionFactory.SeekBackwardNotPossibleInAppendMode(); - } - - return base.Seek(offset, origin); - } - - /// - public override void SetLength(long value) - { - ThrowIfDisposed(); - if (!CanWrite) - { - throw ExceptionFactory.StreamDoesNotSupportWriting(); - } - - base.SetLength(value); - } - - /// - public override void Write(byte[] buffer, int offset, int count) - { - if (!CanWrite) - { - throw ExceptionFactory.StreamDoesNotSupportWriting(); - } - - _isContentChanged = true; - base.Write(buffer, offset, count); - } - -#if FEATURE_SPAN - /// - public override void Write(ReadOnlySpan buffer) - { - if (!CanWrite) - { - throw ExceptionFactory.StreamDoesNotSupportWriting(); - } - - _isContentChanged = true; - base.Write(buffer); - } -#endif - - /// - public override Task WriteAsync(byte[] buffer, int offset, int count, - CancellationToken cancellationToken) - { - if (!CanWrite) - { - throw ExceptionFactory.StreamDoesNotSupportWriting(); - } - - _isContentChanged = true; - return base.WriteAsync(buffer, offset, count, cancellationToken); - } - -#if FEATURE_SPAN - /// - public override ValueTask WriteAsync(ReadOnlyMemory buffer, - CancellationToken cancellationToken = new()) - { - if (!CanWrite) - { - throw ExceptionFactory.StreamDoesNotSupportWriting(); - } - - _isContentChanged = true; - return base.WriteAsync(buffer, cancellationToken); - } -#endif - - /// - public override void WriteByte(byte value) - { - if (!CanWrite) - { - throw ExceptionFactory.StreamDoesNotSupportWriting(); - } - - _isContentChanged = true; - base.WriteByte(value); - } - - /// - protected override void Dispose(bool disposing) - { - if (_isDisposed) - { - return; - } - - _accessLock.Dispose(); - InternalFlush(); - base.Dispose(disposing); - OnClose(); - _isDisposed = true; - } - - private void InitializeStream() - { - if (_mode != FileMode.Create && - _mode != FileMode.Truncate) - { - byte[] existingContents = _container.GetBytes(); - _stream.Write(existingContents, 0, existingContents.Length); - _stream.Seek(0, _mode == FileMode.Append - ? SeekOrigin.End - : SeekOrigin.Begin); - } - else - { - _isContentChanged = true; - } - } - - private void ThrowIfDisposed() - { - if (_isDisposed) - { - throw new ObjectDisposedException("", "Cannot access a closed file."); - } - } - - private void InternalFlush() - { - if (!_isContentChanged) - { - return; - } - - _isContentChanged = false; - long position = _stream.Position; - _stream.Seek(0, SeekOrigin.Begin); - byte[] data = new byte[Length]; - _ = _stream.Read(data, 0, (int)Length); - _stream.Seek(position, SeekOrigin.Begin); - _container.WriteBytes(data); - } - - private void OnClose() - { - if (_options.HasFlag(FileOptions.DeleteOnClose)) - { - _fileSystem.Storage.DeleteContainer( - _fileSystem.Storage.GetLocation(Name)); - } - } - - private static void ThrowIfInvalidModeAccess(FileMode mode, FileAccess access) - { - if (mode == FileMode.Append) - { - if (access == FileAccess.Read) - { - throw ExceptionFactory.InvalidAccessCombination(mode, access); - } - - if (access != FileAccess.Write) - { - throw ExceptionFactory.AppendAccessOnlyInWriteOnlyMode(); - } - } - - if (!access.HasFlag(FileAccess.Write) && - (mode == FileMode.Truncate || mode == FileMode.CreateNew || - mode == FileMode.Create || mode == FileMode.Append)) - { - throw ExceptionFactory.InvalidAccessCombination(mode, access); - } - } -} +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// A mocked file stream in the . +/// +internal sealed class FileStreamMock : FileSystemStream +{ + /// + public override bool CanRead + => _access.HasFlag(FileAccess.Read); + + /// + public override bool CanWrite + => _access.HasFlag(FileAccess.Write); + + /// + public override IFileSystemExtensibility Extensibility + => _container.Extensibility; + + private readonly FileAccess _access; + private readonly IDisposable _accessLock; + private readonly IStorageContainer _container; + private readonly MockFileSystem _fileSystem; + private readonly long _initialPosition; + private bool _isContentChanged; + private bool _isDisposed; + private readonly FileMode _mode; + private readonly FileOptions _options; + private readonly MemoryStream _stream; + + internal FileStreamMock(MockFileSystem fileSystem, + string? path, + FileMode mode, + FileAccess access, + FileShare share = FileShare.Read, + int bufferSize = 4096, + FileOptions options = FileOptions.None) + : this(new MemoryStream(), + fileSystem, + path.EnsureValidFormat(fileSystem, nameof(path)), + mode, + access, + share, + bufferSize, + options) + { + } + + private FileStreamMock(MemoryStream stream, + MockFileSystem fileSystem, + string? path, + FileMode mode, + FileAccess access, + FileShare share, + int bufferSize, + FileOptions options) + : base( + stream, + path == null ? null : fileSystem.Path.GetFullPath(path), + (options & FileOptions.Asynchronous) != 0) + { + ThrowIfInvalidModeAccess(mode, access); + + _stream = stream; + _fileSystem = fileSystem; + _mode = mode; + _access = access; + _ = bufferSize; + _options = options; + _initialPosition = Position; + + IStorageLocation location = _fileSystem.Storage.GetLocation(Name); + location.ThrowExceptionIfNotFound(_fileSystem, true); + IStorageContainer file = _fileSystem.Storage.GetContainer(location); + if (file is NullContainer) + { + if (_mode.Equals(FileMode.Open) || + _mode.Equals(FileMode.Truncate)) + { + throw ExceptionFactory.FileNotFound( + _fileSystem.Path.GetFullPath(Name)); + } + + file = _fileSystem.Storage.GetOrCreateContainer(location, + InMemoryContainer.NewFile); + } + else if (file.Type == FileSystemTypes.Directory) + { + Execute.OnWindows( + () => + throw ExceptionFactory.AccessToPathDenied( + _fileSystem.Path.GetFullPath(Name)), + () => + throw ExceptionFactory.FileAlreadyExists( + _fileSystem.Path.GetFullPath(Name), 17)); + } + else if (_mode.Equals(FileMode.CreateNew)) + { + throw ExceptionFactory.FileAlreadyExists( + _fileSystem.Path.GetFullPath(Name), + Execute.IsWindows ? -2147024816 : 17); + } + + if (file.Attributes.HasFlag(FileAttributes.ReadOnly) && + access.HasFlag(FileAccess.Write)) + { + throw ExceptionFactory.AccessToPathDenied(location.FullPath); + } + + _accessLock = file.RequestAccess(access, share); + + _container = file; + + InitializeStream(); + } + + /// + public override IAsyncResult BeginRead(byte[] buffer, + int offset, + int count, + AsyncCallback? callback, + object? state) + { + ThrowIfDisposed(); + if (!CanRead) + { + throw ExceptionFactory.StreamDoesNotSupportReading(); + } + + return base.BeginRead(buffer, offset, count, callback, state); + } + + /// + public override IAsyncResult BeginWrite(byte[] buffer, + int offset, + int count, + AsyncCallback? callback, + object? state) + { + ThrowIfDisposed(); + if (!CanWrite) + { + throw ExceptionFactory.StreamDoesNotSupportWriting(); + } + + return base.BeginWrite(buffer, offset, count, callback, state); + } + + /// + public override void CopyTo(Stream destination, int bufferSize) + { + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + base.CopyTo(destination, bufferSize); + } + + /// + public override Task CopyToAsync(Stream destination, int bufferSize, + CancellationToken cancellationToken) + { + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + return base.CopyToAsync(destination, bufferSize, cancellationToken); + } + + /// + public override int EndRead(IAsyncResult asyncResult) + { + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + return base.EndRead(asyncResult); + } + + /// + public override void EndWrite(IAsyncResult asyncResult) + { + _isContentChanged = true; + base.EndWrite(asyncResult); + } + + /// + public override void Flush() + { + ThrowIfDisposed(); + InternalFlush(); + } + + /// + public override Task FlushAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + throw ExceptionFactory.TaskWasCanceled(); + } + + Flush(); + return Task.CompletedTask; + } + + /// + public override int Read(byte[] buffer, int offset, int count) + { + if (!CanRead) + { + throw ExceptionFactory.StreamDoesNotSupportReading(); + } + + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + return base.Read(buffer, offset, count); + } + +#if FEATURE_SPAN + /// + public override int Read(Span buffer) + { + if (!CanRead) + { + throw ExceptionFactory.StreamDoesNotSupportReading(); + } + + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + return base.Read(buffer); + } +#endif + + /// + public override Task ReadAsync(byte[] buffer, int offset, int count, + CancellationToken cancellationToken) + { + if (!CanRead) + { + throw ExceptionFactory.StreamDoesNotSupportReading(); + } + + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + return base.ReadAsync(buffer, offset, count, cancellationToken); + } + +#if FEATURE_SPAN + /// + public override ValueTask ReadAsync(Memory buffer, + CancellationToken cancellationToken = + new()) + { + if (!CanRead) + { + throw ExceptionFactory.StreamDoesNotSupportReading(); + } + + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + return base.ReadAsync(buffer, cancellationToken); + } +#endif + + /// + public override int ReadByte() + { + if (!CanRead) + { + throw ExceptionFactory.StreamDoesNotSupportReading(); + } + + _container.AdjustTimes(TimeAdjustments.LastAccessTime); + return base.ReadByte(); + } + + /// + public override long Seek(long offset, SeekOrigin origin) + { + if (_mode == FileMode.Append && offset <= _initialPosition) + { + throw ExceptionFactory.SeekBackwardNotPossibleInAppendMode(); + } + + return base.Seek(offset, origin); + } + + /// + public override void SetLength(long value) + { + ThrowIfDisposed(); + if (!CanWrite) + { + throw ExceptionFactory.StreamDoesNotSupportWriting(); + } + + base.SetLength(value); + } + + /// + public override void Write(byte[] buffer, int offset, int count) + { + if (!CanWrite) + { + throw ExceptionFactory.StreamDoesNotSupportWriting(); + } + + _isContentChanged = true; + base.Write(buffer, offset, count); + } + +#if FEATURE_SPAN + /// + public override void Write(ReadOnlySpan buffer) + { + if (!CanWrite) + { + throw ExceptionFactory.StreamDoesNotSupportWriting(); + } + + _isContentChanged = true; + base.Write(buffer); + } +#endif + + /// + public override Task WriteAsync(byte[] buffer, int offset, int count, + CancellationToken cancellationToken) + { + if (!CanWrite) + { + throw ExceptionFactory.StreamDoesNotSupportWriting(); + } + + _isContentChanged = true; + return base.WriteAsync(buffer, offset, count, cancellationToken); + } + +#if FEATURE_SPAN + /// + public override ValueTask WriteAsync(ReadOnlyMemory buffer, + CancellationToken cancellationToken = new()) + { + if (!CanWrite) + { + throw ExceptionFactory.StreamDoesNotSupportWriting(); + } + + _isContentChanged = true; + return base.WriteAsync(buffer, cancellationToken); + } +#endif + + /// + public override void WriteByte(byte value) + { + if (!CanWrite) + { + throw ExceptionFactory.StreamDoesNotSupportWriting(); + } + + _isContentChanged = true; + base.WriteByte(value); + } + + /// + protected override void Dispose(bool disposing) + { + if (_isDisposed) + { + return; + } + + _accessLock.Dispose(); + InternalFlush(); + base.Dispose(disposing); + OnClose(); + _isDisposed = true; + } + + private void InitializeStream() + { + if (_mode != FileMode.Create && + _mode != FileMode.Truncate) + { + byte[] existingContents = _container.GetBytes(); + _stream.Write(existingContents, 0, existingContents.Length); + _stream.Seek(0, _mode == FileMode.Append + ? SeekOrigin.End + : SeekOrigin.Begin); + } + else + { + _isContentChanged = true; + } + } + + private void ThrowIfDisposed() + { + if (_isDisposed) + { + throw new ObjectDisposedException("", "Cannot access a closed file."); + } + } + + private void InternalFlush() + { + if (!_isContentChanged) + { + return; + } + + _isContentChanged = false; + long position = _stream.Position; + _stream.Seek(0, SeekOrigin.Begin); + byte[] data = new byte[Length]; + _ = _stream.Read(data, 0, (int)Length); + _stream.Seek(position, SeekOrigin.Begin); + _container.WriteBytes(data); + } + + private void OnClose() + { + if (_options.HasFlag(FileOptions.DeleteOnClose)) + { + _fileSystem.Storage.DeleteContainer( + _fileSystem.Storage.GetLocation(Name)); + } + } + + private static void ThrowIfInvalidModeAccess(FileMode mode, FileAccess access) + { + if (mode == FileMode.Append) + { + if (access == FileAccess.Read) + { + throw ExceptionFactory.InvalidAccessCombination(mode, access); + } + + if (access != FileAccess.Write) + { + throw ExceptionFactory.AppendAccessOnlyInWriteOnlyMode(); + } + } + + if (!access.HasFlag(FileAccess.Write) && + (mode == FileMode.Truncate || mode == FileMode.CreateNew || + mode == FileMode.Create || mode == FileMode.Append)) + { + throw ExceptionFactory.InvalidAccessCombination(mode, access); + } + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs index 40fd76711..3d1d9d713 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs @@ -1,268 +1,268 @@ -using System; -using System.IO; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -internal class FileSystemInfoMock : IFileSystemInfo -{ - protected FileSystemTypes FileSystemType { get; } - protected IStorageLocation Location; - private readonly MockFileSystem _fileSystem; - - protected IStorageContainer Container - { - get - { - if (_container is NullContainer) - { - RefreshInternal(); - } - - return _container; - } - set => _container = value; - } - - private bool? _exists; - private bool _isInitialized; - private IStorageContainer _container; - - protected FileSystemInfoMock(MockFileSystem fileSystem, IStorageLocation location, - FileSystemTypes fileSystemType) - { - _fileSystem = fileSystem; - Location = location; - _container = fileSystem.Storage.GetContainer(location); - FileSystemType = _container is not NullContainer - ? _container.Type - : fileSystemType; - } - - #region IFileSystemInfo Members - - /// - public FileAttributes Attributes - { - get => Container.Attributes; - set => Container.Attributes = value; - } - -#if FEATURE_FILESYSTEM_LINK - /// - public void CreateAsSymbolicLink(string pathToTarget) - { - FullName.EnsureValidFormat(_fileSystem); - pathToTarget.ThrowCommonExceptionsIfPathToTargetIsInvalid(_fileSystem); - if (_fileSystem.Storage.TryAddContainer(Location, InMemoryContainer.NewFile, - out IStorageContainer? container)) - { - Container = container; - container.LinkTarget = pathToTarget; - } - else - { - throw ExceptionFactory.CannotCreateFileAsAlreadyExists(Location - .FriendlyName); - } - } -#endif - - /// - public DateTime CreationTime - { - get => Container.CreationTime.Get(DateTimeKind.Local); - set => Container.CreationTime.Set(value, DateTimeKind.Local); - } - - /// - public DateTime CreationTimeUtc - { - get => Container.CreationTime.Get(DateTimeKind.Utc); - set => Container.CreationTime.Set(value, DateTimeKind.Utc); - } - - /// - public virtual void Delete() - { - _fileSystem.Storage.DeleteContainer(Location); - ResetCache(!Execute.IsNetFramework); - } - - /// - public virtual bool Exists - { - get - { - RefreshInternal(); - _exists ??= !string.IsNullOrWhiteSpace(Location.FriendlyName) && - Container is not NullContainer; - return _exists.Value; - } - } - - /// - public string Extension - { - get - { - if (Location.FullPath.EndsWith(".") && - !Execute.IsWindows) - { - return "."; - } - - return _fileSystem.Path.GetExtension(Location.FullPath); - } - } - - /// - public IFileSystemExtensibility Extensibility - => Container.Extensibility; - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public string FullName => Location.FullPath; - - /// - public DateTime LastAccessTime - { - get => Container.LastAccessTime.Get(DateTimeKind.Local); - set => Container.LastAccessTime.Set(value, DateTimeKind.Local); - } - - /// - public DateTime LastAccessTimeUtc - { - get => Container.LastAccessTime.Get(DateTimeKind.Utc); - set => Container.LastAccessTime.Set(value, DateTimeKind.Utc); - } - - /// - public DateTime LastWriteTime - { - get => Container.LastWriteTime.Get(DateTimeKind.Local); - set => Container.LastWriteTime.Set(value, DateTimeKind.Local); - } - - /// - public DateTime LastWriteTimeUtc - { - get => Container.LastWriteTime.Get(DateTimeKind.Utc); - set => Container.LastWriteTime.Set(value, DateTimeKind.Utc); - } - -#if FEATURE_FILESYSTEM_LINK - /// - public string? LinkTarget - => Container.LinkTarget; -#endif - - /// - public string Name - => _fileSystem.Path.GetPathRoot(Location.FullPath) == Location.FullPath - ? Location.FullPath - : _fileSystem.Path.GetFileName(Location.FullPath.TrimEnd( - _fileSystem.Path.DirectorySeparatorChar, - _fileSystem.Path.AltDirectorySeparatorChar)); - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - public UnixFileMode UnixFileMode - { - get => Container.UnixFileMode; - [UnsupportedOSPlatform("windows")] - set - { - Execute.OnWindows( - () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform()); - - Container.UnixFileMode = value; - } - } -#endif - - /// - public void Refresh() - { - ResetCache(true); - } - -#if FEATURE_FILESYSTEM_LINK - /// - public IFileSystemInfo? ResolveLinkTarget(bool returnFinalTarget) - { - try - { - IStorageLocation? targetLocation = - _fileSystem.Storage.ResolveLinkTarget( - Location, - returnFinalTarget); - if (targetLocation != null) - { - return New(targetLocation, _fileSystem); - } - - return null; - } - catch (IOException ex) when (ex.HResult != -2147024773) - { - throw ExceptionFactory.FileNameCannotBeResolved(Location.FullPath, - Execute.IsWindows ? -2147022975 : -2146232800); - } - } -#endif - - #endregion - -#if NETSTANDARD2_0 - /// -#else - /// -#endif - public override string ToString() - => Location.FriendlyName; - - internal static FileSystemInfoMock New(IStorageLocation location, - MockFileSystem fileSystem) - { - IStorageContainer container = fileSystem.Storage.GetContainer(location); - if (container.Type == FileSystemTypes.File) - { - return FileInfoMock.New(location, fileSystem); - } - - if (container.Type == FileSystemTypes.Directory) - { - return DirectoryInfoMock.New(location, fileSystem); - } - - return new FileSystemInfoMock(fileSystem, location, - FileSystemTypes.DirectoryOrFile); - } - - protected void ResetCache(bool resetExistsCache) - { - if (resetExistsCache) - { - _exists = null; - } - - _isInitialized = false; - } - - private void RefreshInternal() - { - if (_isInitialized) - { - return; - } - - Container = _fileSystem.Storage.GetContainer(Location); - _isInitialized = true; - } -} +using System; +using System.IO; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +internal class FileSystemInfoMock : IFileSystemInfo +{ + protected FileSystemTypes FileSystemType { get; } + protected IStorageLocation Location; + private readonly MockFileSystem _fileSystem; + + protected IStorageContainer Container + { + get + { + if (_container is NullContainer) + { + RefreshInternal(); + } + + return _container; + } + set => _container = value; + } + + private bool? _exists; + private bool _isInitialized; + private IStorageContainer _container; + + protected FileSystemInfoMock(MockFileSystem fileSystem, IStorageLocation location, + FileSystemTypes fileSystemType) + { + _fileSystem = fileSystem; + Location = location; + _container = fileSystem.Storage.GetContainer(location); + FileSystemType = _container is not NullContainer + ? _container.Type + : fileSystemType; + } + + #region IFileSystemInfo Members + + /// + public FileAttributes Attributes + { + get => Container.Attributes; + set => Container.Attributes = value; + } + +#if FEATURE_FILESYSTEM_LINK + /// + public void CreateAsSymbolicLink(string pathToTarget) + { + FullName.EnsureValidFormat(_fileSystem); + pathToTarget.ThrowCommonExceptionsIfPathToTargetIsInvalid(_fileSystem); + if (_fileSystem.Storage.TryAddContainer(Location, InMemoryContainer.NewFile, + out IStorageContainer? container)) + { + Container = container; + container.LinkTarget = pathToTarget; + } + else + { + throw ExceptionFactory.CannotCreateFileAsAlreadyExists(Location + .FriendlyName); + } + } +#endif + + /// + public DateTime CreationTime + { + get => Container.CreationTime.Get(DateTimeKind.Local); + set => Container.CreationTime.Set(value, DateTimeKind.Local); + } + + /// + public DateTime CreationTimeUtc + { + get => Container.CreationTime.Get(DateTimeKind.Utc); + set => Container.CreationTime.Set(value, DateTimeKind.Utc); + } + + /// + public virtual void Delete() + { + _fileSystem.Storage.DeleteContainer(Location); + ResetCache(!Execute.IsNetFramework); + } + + /// + public virtual bool Exists + { + get + { + RefreshInternal(); + _exists ??= !string.IsNullOrWhiteSpace(Location.FriendlyName) && + Container is not NullContainer; + return _exists.Value; + } + } + + /// + public string Extension + { + get + { + if (Location.FullPath.EndsWith(".") && + !Execute.IsWindows) + { + return "."; + } + + return _fileSystem.Path.GetExtension(Location.FullPath); + } + } + + /// + public IFileSystemExtensibility Extensibility + => Container.Extensibility; + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public string FullName => Location.FullPath; + + /// + public DateTime LastAccessTime + { + get => Container.LastAccessTime.Get(DateTimeKind.Local); + set => Container.LastAccessTime.Set(value, DateTimeKind.Local); + } + + /// + public DateTime LastAccessTimeUtc + { + get => Container.LastAccessTime.Get(DateTimeKind.Utc); + set => Container.LastAccessTime.Set(value, DateTimeKind.Utc); + } + + /// + public DateTime LastWriteTime + { + get => Container.LastWriteTime.Get(DateTimeKind.Local); + set => Container.LastWriteTime.Set(value, DateTimeKind.Local); + } + + /// + public DateTime LastWriteTimeUtc + { + get => Container.LastWriteTime.Get(DateTimeKind.Utc); + set => Container.LastWriteTime.Set(value, DateTimeKind.Utc); + } + +#if FEATURE_FILESYSTEM_LINK + /// + public string? LinkTarget + => Container.LinkTarget; +#endif + + /// + public string Name + => _fileSystem.Path.GetPathRoot(Location.FullPath) == Location.FullPath + ? Location.FullPath + : _fileSystem.Path.GetFileName(Location.FullPath.TrimEnd( + _fileSystem.Path.DirectorySeparatorChar, + _fileSystem.Path.AltDirectorySeparatorChar)); + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + public UnixFileMode UnixFileMode + { + get => Container.UnixFileMode; + [UnsupportedOSPlatform("windows")] + set + { + Execute.OnWindows( + () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform()); + + Container.UnixFileMode = value; + } + } +#endif + + /// + public void Refresh() + { + ResetCache(true); + } + +#if FEATURE_FILESYSTEM_LINK + /// + public IFileSystemInfo? ResolveLinkTarget(bool returnFinalTarget) + { + try + { + IStorageLocation? targetLocation = + _fileSystem.Storage.ResolveLinkTarget( + Location, + returnFinalTarget); + if (targetLocation != null) + { + return New(targetLocation, _fileSystem); + } + + return null; + } + catch (IOException ex) when (ex.HResult != -2147024773) + { + throw ExceptionFactory.FileNameCannotBeResolved(Location.FullPath, + Execute.IsWindows ? -2147022975 : -2146232800); + } + } +#endif + + #endregion + +#if NETSTANDARD2_0 + /// +#else + /// +#endif + public override string ToString() + => Location.FriendlyName; + + internal static FileSystemInfoMock New(IStorageLocation location, + MockFileSystem fileSystem) + { + IStorageContainer container = fileSystem.Storage.GetContainer(location); + if (container.Type == FileSystemTypes.File) + { + return FileInfoMock.New(location, fileSystem); + } + + if (container.Type == FileSystemTypes.Directory) + { + return DirectoryInfoMock.New(location, fileSystem); + } + + return new FileSystemInfoMock(fileSystem, location, + FileSystemTypes.DirectoryOrFile); + } + + protected void ResetCache(bool resetExistsCache) + { + if (resetExistsCache) + { + _exists = null; + } + + _isInitialized = false; + } + + private void RefreshInternal() + { + if (_isInitialized) + { + return; + } + + Container = _fileSystem.Storage.GetContainer(Location); + _isInitialized = true; + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs index b4e25e702..88cf8bae4 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs @@ -1,78 +1,78 @@ -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Testably.Abstractions.Testing.Helpers; - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class - FileSystemWatcherFactoryMock : IFileSystemWatcherFactory -{ - private readonly MockFileSystem _fileSystem; - - internal FileSystemWatcherFactoryMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - #region IFileSystemWatcherFactory Members - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public IFileSystemWatcher New() - => FileSystemWatcherMock.New(_fileSystem); - - /// - public IFileSystemWatcher New(string path) - { - FileSystemWatcherMock fileSystemWatcherMock = - FileSystemWatcherMock.New(_fileSystem); - fileSystemWatcherMock.Path = path.EnsureValidArgument(_fileSystem); - return fileSystemWatcherMock; - } - - /// - public IFileSystemWatcher New(string path, string filter) - { - FileSystemWatcherMock fileSystemWatcherMock = - FileSystemWatcherMock.New(_fileSystem); - fileSystemWatcherMock.Path = path.EnsureValidArgument(_fileSystem); - fileSystemWatcherMock.Filter = filter; - return fileSystemWatcherMock; - } - - /// - [return: NotNullIfNotNull("fileSystemWatcher")] - // ReSharper disable once ReturnTypeCanBeNotNullable - public IFileSystemWatcher? Wrap(FileSystemWatcher? fileSystemWatcher) - { - if (fileSystemWatcher == null) - { - return null; - } - - FileSystemWatcherMock fileSystemWatcherMock = - FileSystemWatcherMock.New(_fileSystem); - fileSystemWatcherMock.Path = fileSystemWatcher.Path; -#if FEATURE_FILESYSTEMWATCHER_ADVANCED - foreach (string filter in fileSystemWatcher.Filters) - { - fileSystemWatcherMock.Filters.Add(filter); - } -#else - fileSystemWatcherMock.Filter = fileSystemWatcher.Filter; -#endif - fileSystemWatcherMock.NotifyFilter = fileSystemWatcher.NotifyFilter; - fileSystemWatcherMock.IncludeSubdirectories = - fileSystemWatcher.IncludeSubdirectories; - fileSystemWatcherMock.InternalBufferSize = - fileSystemWatcher.InternalBufferSize; - fileSystemWatcherMock.EnableRaisingEvents = - fileSystemWatcher.EnableRaisingEvents; - return fileSystemWatcherMock; - } - - #endregion -} +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class + FileSystemWatcherFactoryMock : IFileSystemWatcherFactory +{ + private readonly MockFileSystem _fileSystem; + + internal FileSystemWatcherFactoryMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + #region IFileSystemWatcherFactory Members + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public IFileSystemWatcher New() + => FileSystemWatcherMock.New(_fileSystem); + + /// + public IFileSystemWatcher New(string path) + { + FileSystemWatcherMock fileSystemWatcherMock = + FileSystemWatcherMock.New(_fileSystem); + fileSystemWatcherMock.Path = path.EnsureValidArgument(_fileSystem); + return fileSystemWatcherMock; + } + + /// + public IFileSystemWatcher New(string path, string filter) + { + FileSystemWatcherMock fileSystemWatcherMock = + FileSystemWatcherMock.New(_fileSystem); + fileSystemWatcherMock.Path = path.EnsureValidArgument(_fileSystem); + fileSystemWatcherMock.Filter = filter; + return fileSystemWatcherMock; + } + + /// + [return: NotNullIfNotNull("fileSystemWatcher")] + // ReSharper disable once ReturnTypeCanBeNotNullable + public IFileSystemWatcher? Wrap(FileSystemWatcher? fileSystemWatcher) + { + if (fileSystemWatcher == null) + { + return null; + } + + FileSystemWatcherMock fileSystemWatcherMock = + FileSystemWatcherMock.New(_fileSystem); + fileSystemWatcherMock.Path = fileSystemWatcher.Path; +#if FEATURE_FILESYSTEMWATCHER_ADVANCED + foreach (string filter in fileSystemWatcher.Filters) + { + fileSystemWatcherMock.Filters.Add(filter); + } +#else + fileSystemWatcherMock.Filter = fileSystemWatcher.Filter; +#endif + fileSystemWatcherMock.NotifyFilter = fileSystemWatcher.NotifyFilter; + fileSystemWatcherMock.IncludeSubdirectories = + fileSystemWatcher.IncludeSubdirectories; + fileSystemWatcherMock.InternalBufferSize = + fileSystemWatcher.InternalBufferSize; + fileSystemWatcherMock.EnableRaisingEvents = + fileSystemWatcher.EnableRaisingEvents; + return fileSystemWatcherMock; + } + + #endregion +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs index 0c23786ba..39162e045 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs @@ -1,514 +1,514 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// Mocked instance of a -/// -public sealed class FileSystemWatcherMock : Component, IFileSystemWatcher -{ - /// - /// Simulated bytes pre message to calculate the size of the blocking collection relative to the - /// . - /// - private const int BytesPerMessage = 128; - - private static string DefaultFilter - => Execute.IsNetFramework ? "*.*" : "*"; - - private CancellationTokenSource? _cancellationTokenSource; - private IDisposable? _changeHandler; - private BlockingCollection _changes; - private bool _enableRaisingEvents; - private readonly MockFileSystem _fileSystem; - private readonly Collection _filters = new(); - private int _internalBufferSize = 8192; - private bool _isInitializing; - private string _path = string.Empty; - - private FileSystemWatcherMock(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - _changes = - new BlockingCollection(InternalBufferSize / - BytesPerMessage); - } - - private event EventHandler? InternalEvent; - - #region IFileSystemWatcher Members - - /// - public bool EnableRaisingEvents - { - get => _enableRaisingEvents; - set - { - _enableRaisingEvents = value; - if (_enableRaisingEvents) - { - Start(); - } - else - { - Stop(); - } - } - } - - /// - public IFileSystem FileSystem - => _fileSystem; - - /// - public string Filter - { - get => _filters.Count == 0 - ? DefaultFilter - : _filters[0]; - set - { - _filters.Clear(); - _filters.Add(value); - } - } - -#if FEATURE_FILESYSTEMWATCHER_ADVANCED - /// - public Collection Filters - => _filters; -#endif - - /// - public bool IncludeSubdirectories - { - get; - set; - } - - /// - public int InternalBufferSize - { - get => _internalBufferSize; - set - { - _internalBufferSize = Math.Max(value, 4096); - Restart(); - } - } - - /// - public NotifyFilters NotifyFilter - { - get; - set; - } = NotifyFilters.FileName | - NotifyFilters.DirectoryName | - NotifyFilters.LastWrite; - - /// - public string Path - { - get => _path; - set - { - if (!string.IsNullOrEmpty(value) && - !_fileSystem.Directory.Exists(value)) - { - throw ExceptionFactory.DirectoryNameDoesNotExist(value, nameof(Path)); - } - - _path = value; - } - } - - /// - public ISynchronizeInvoke? SynchronizingObject { get; set; } - - /// - public void BeginInit() - { - _isInitializing = true; - Stop(); - } - - /// - public event FileSystemEventHandler? Changed; - - /// - public event FileSystemEventHandler? Created; - - /// - public event FileSystemEventHandler? Deleted; - - /// - public void EndInit() - { - _isInitializing = false; - Restart(); - } - - /// - public event ErrorEventHandler? Error; - - /// - public event RenamedEventHandler? Renamed; - - /// - public IWaitForChangedResult WaitForChanged( - WatcherChangeTypes changeType) - => WaitForChanged(changeType, Timeout.Infinite); - - /// - public IWaitForChangedResult WaitForChanged( - WatcherChangeTypes changeType, int timeout) - => WaitForChangedInternal(changeType, TimeSpan.FromMilliseconds(timeout)); - -#if FEATURE_FILESYSTEM_NET7 - /// - public IWaitForChangedResult WaitForChanged( - WatcherChangeTypes changeType, TimeSpan timeout) - => WaitForChangedInternal(changeType, timeout); -#endif - - #endregion - - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - Stop(); - } - - base.Dispose(disposing); - } - - internal static FileSystemWatcherMock New(MockFileSystem fileSystem) - => new(fileSystem); - - private bool MatchesFilter(ChangeDescription changeDescription) - { - if (IncludeSubdirectories) - { - if (!changeDescription.Path.StartsWith(Path)) - { - return false; - } - } - else if (FileSystem.Path.GetDirectoryName(changeDescription.Path) != Path) - { - return false; - } - - if ((NotifyFilter & changeDescription.NotifyFilters) == 0) - { - return false; - } - - if (_filters.Count == 0) - { - return true; - } - - return _filters.Any(filter => - EnumerationOptionsHelper.MatchesPattern( - EnumerationOptionsHelper.Compatible, - _fileSystem.Path.GetFileName(changeDescription.Path), - filter)); - } - - private void NotifyChange(ChangeDescription item) - { - InternalEvent?.Invoke(this, item); - if (MatchesFilter(item)) - { - if (item.ChangeType.HasFlag(WatcherChangeTypes.Created)) - { - Created?.Invoke(this, ToFileSystemEventArgs( - item.ChangeType, item.Path, item.Name)); - } - - if (item.ChangeType.HasFlag(WatcherChangeTypes.Deleted)) - { - Deleted?.Invoke(this, ToFileSystemEventArgs( - item.ChangeType, item.Path, item.Name)); - } - - if (item.ChangeType.HasFlag(WatcherChangeTypes.Changed)) - { - Changed?.Invoke(this, ToFileSystemEventArgs( - item.ChangeType, item.Path, item.Name)); - } - - if (item.ChangeType.HasFlag(WatcherChangeTypes.Renamed)) - { - TriggerRenameNotification(item); - } - } - } - - private void Restart() - { - if (_isInitializing) - { - return; - } - - if (EnableRaisingEvents) - { - Stop(); - BlockingCollection changes = _changes; - _changes = - new BlockingCollection(InternalBufferSize / - BytesPerMessage); - changes.Dispose(); - Start(); - } - else - { - BlockingCollection changes = _changes; - _changes = - new BlockingCollection(InternalBufferSize / - BytesPerMessage); - changes.Dispose(); - } - } - - private void Start() - { - if (_isInitializing) - { - return; - } - - Stop(); - CancellationTokenSource cancellationTokenSource = new(); - _cancellationTokenSource = cancellationTokenSource; - _changeHandler = _fileSystem.Notify.OnEvent(c => - { - if (!_changes.TryAdd(c, 100)) - { - Error?.Invoke(this, new ErrorEventArgs( - ExceptionFactory.InternalBufferOverflowException( - InternalBufferSize, _changes.BoundedCapacity))); - } - }); - CancellationToken token = cancellationTokenSource.Token; - Task.Factory.StartNew(() => - { - try - { - while (!token.IsCancellationRequested) - { - if (_changes.TryTake(out ChangeDescription? c, - Timeout.Infinite, - token)) - { - NotifyChange(c); - } - } - } - catch (Exception) - { - //Ignore any exception - } - }, - token, - TaskCreationOptions.LongRunning, - TaskScheduler.Default) - .ContinueWith(_ => - { - cancellationTokenSource.Dispose(); - }, TaskScheduler.Default); - } - - private void Stop() - { - if (_cancellationTokenSource?.IsCancellationRequested == false) - { - _cancellationTokenSource.Cancel(); - } - - _changeHandler?.Dispose(); - } - - private FileSystemEventArgs ToFileSystemEventArgs( - WatcherChangeTypes changeType, - string changePath, - string? changeName) - { - string path = TransformPathAndName( - changePath, - changeName, - out string name); - - return new FileSystemEventArgs(changeType, - path, name); - } - - private string TransformPathAndName( - string changeDescriptionPath, - string? changeDescriptionName, - out string name) - { - string? transformedName = changeDescriptionName; - string? path = changeDescriptionPath; - if (transformedName == null || - _fileSystem.Path.IsPathRooted(changeDescriptionName)) - { - transformedName = _fileSystem.Path.GetFileName(changeDescriptionPath); - path = _fileSystem.Path.GetDirectoryName(path); - } - else if (path.EndsWith(transformedName)) - { - path = path.Substring(0, path.Length - transformedName.Length); - } - - name = transformedName; - return path ?? ""; - } - - private void TriggerRenameNotification(ChangeDescription item) - => Execute.OnWindows( - () => - { - if (TryMakeRenamedEventArgs(item, - out RenamedEventArgs? eventArgs)) - { - Renamed?.Invoke(this, eventArgs); - } - else if (item.OldPath != null) - { - Deleted?.Invoke(this, ToFileSystemEventArgs( - item.ChangeType, item.OldPath, item.OldName)); - Created?.Invoke(this, ToFileSystemEventArgs( - item.ChangeType, item.Path, item.Name)); - } - }, - () => - { - TryMakeRenamedEventArgs(item, - out RenamedEventArgs? eventArgs); - if (eventArgs != null) - { - Renamed?.Invoke(this, eventArgs); - } - }); - - private bool TryMakeRenamedEventArgs( - ChangeDescription changeDescription, - [NotNullWhen(true)] out RenamedEventArgs? eventArgs) - { - if (changeDescription.OldPath == null) - { - eventArgs = null; - return false; - } - - string path = TransformPathAndName( - changeDescription.Path, - changeDescription.Name, - out string name); - - TransformPathAndName( - changeDescription.OldPath, - changeDescription.OldName, - out string oldName); - - eventArgs = new RenamedEventArgs( - changeDescription.ChangeType, - path, - name, - oldName); - return System.IO.Path.GetDirectoryName(changeDescription.Path)? - .Equals(System.IO.Path.GetDirectoryName(changeDescription.OldPath), - InMemoryLocation.StringComparisonMode) ?? true; - } - - private IWaitForChangedResult WaitForChangedInternal( - WatcherChangeTypes changeType, TimeSpan timeout) - { - TaskCompletionSource - tcs = new(); - - void EventHandler(object? _, ChangeDescription c) - { - if ((c.ChangeType & changeType) != 0) - { - tcs.TrySetResult(new WaitForChangedResultMock(c.ChangeType, c.Name, - oldName: c.OldName, timedOut: false)); - } - } - - InternalEvent += EventHandler; - try - { - bool wasEnabled = EnableRaisingEvents; - if (!wasEnabled) - { - EnableRaisingEvents = true; - } - - tcs.Task.Wait(timeout); - EnableRaisingEvents = wasEnabled; - } - finally - { - InternalEvent -= EventHandler; - } - -#if NETFRAMEWORK - return tcs.Task.IsCompleted - ? tcs.Task.Result - : WaitForChangedResultMock.TimedOutResult; -#else - return tcs.Task.IsCompletedSuccessfully - ? tcs.Task.Result - : WaitForChangedResultMock.TimedOutResult; -#endif - } - - private struct WaitForChangedResultMock : IWaitForChangedResult - { - public WaitForChangedResultMock( - WatcherChangeTypes changeType, - string? name, - string? oldName, - bool timedOut) - { - ChangeType = changeType; - Name = name; - OldName = oldName; - TimedOut = timedOut; - } - - /// - /// The instance representing a timed out . - /// - public static readonly WaitForChangedResultMock TimedOutResult = - new(changeType: 0, name: null, oldName: null, timedOut: true); - - /// - public WatcherChangeTypes ChangeType { get; } - - /// - public string? Name { get; } - - /// - public string? OldName { get; } - - /// - public bool TimedOut { get; } - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// Mocked instance of a +/// +public sealed class FileSystemWatcherMock : Component, IFileSystemWatcher +{ + /// + /// Simulated bytes pre message to calculate the size of the blocking collection relative to the + /// . + /// + private const int BytesPerMessage = 128; + + private static string DefaultFilter + => Execute.IsNetFramework ? "*.*" : "*"; + + private CancellationTokenSource? _cancellationTokenSource; + private IDisposable? _changeHandler; + private BlockingCollection _changes; + private bool _enableRaisingEvents; + private readonly MockFileSystem _fileSystem; + private readonly Collection _filters = new(); + private int _internalBufferSize = 8192; + private bool _isInitializing; + private string _path = string.Empty; + + private FileSystemWatcherMock(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + _changes = + new BlockingCollection(InternalBufferSize / + BytesPerMessage); + } + + private event EventHandler? InternalEvent; + + #region IFileSystemWatcher Members + + /// + public bool EnableRaisingEvents + { + get => _enableRaisingEvents; + set + { + _enableRaisingEvents = value; + if (_enableRaisingEvents) + { + Start(); + } + else + { + Stop(); + } + } + } + + /// + public IFileSystem FileSystem + => _fileSystem; + + /// + public string Filter + { + get => _filters.Count == 0 + ? DefaultFilter + : _filters[0]; + set + { + _filters.Clear(); + _filters.Add(value); + } + } + +#if FEATURE_FILESYSTEMWATCHER_ADVANCED + /// + public Collection Filters + => _filters; +#endif + + /// + public bool IncludeSubdirectories + { + get; + set; + } + + /// + public int InternalBufferSize + { + get => _internalBufferSize; + set + { + _internalBufferSize = Math.Max(value, 4096); + Restart(); + } + } + + /// + public NotifyFilters NotifyFilter + { + get; + set; + } = NotifyFilters.FileName | + NotifyFilters.DirectoryName | + NotifyFilters.LastWrite; + + /// + public string Path + { + get => _path; + set + { + if (!string.IsNullOrEmpty(value) && + !_fileSystem.Directory.Exists(value)) + { + throw ExceptionFactory.DirectoryNameDoesNotExist(value, nameof(Path)); + } + + _path = value; + } + } + + /// + public ISynchronizeInvoke? SynchronizingObject { get; set; } + + /// + public void BeginInit() + { + _isInitializing = true; + Stop(); + } + + /// + public event FileSystemEventHandler? Changed; + + /// + public event FileSystemEventHandler? Created; + + /// + public event FileSystemEventHandler? Deleted; + + /// + public void EndInit() + { + _isInitializing = false; + Restart(); + } + + /// + public event ErrorEventHandler? Error; + + /// + public event RenamedEventHandler? Renamed; + + /// + public IWaitForChangedResult WaitForChanged( + WatcherChangeTypes changeType) + => WaitForChanged(changeType, Timeout.Infinite); + + /// + public IWaitForChangedResult WaitForChanged( + WatcherChangeTypes changeType, int timeout) + => WaitForChangedInternal(changeType, TimeSpan.FromMilliseconds(timeout)); + +#if FEATURE_FILESYSTEM_NET7 + /// + public IWaitForChangedResult WaitForChanged( + WatcherChangeTypes changeType, TimeSpan timeout) + => WaitForChangedInternal(changeType, timeout); +#endif + + #endregion + + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + Stop(); + } + + base.Dispose(disposing); + } + + internal static FileSystemWatcherMock New(MockFileSystem fileSystem) + => new(fileSystem); + + private bool MatchesFilter(ChangeDescription changeDescription) + { + if (IncludeSubdirectories) + { + if (!changeDescription.Path.StartsWith(Path)) + { + return false; + } + } + else if (FileSystem.Path.GetDirectoryName(changeDescription.Path) != Path) + { + return false; + } + + if ((NotifyFilter & changeDescription.NotifyFilters) == 0) + { + return false; + } + + if (_filters.Count == 0) + { + return true; + } + + return _filters.Any(filter => + EnumerationOptionsHelper.MatchesPattern( + EnumerationOptionsHelper.Compatible, + _fileSystem.Path.GetFileName(changeDescription.Path), + filter)); + } + + private void NotifyChange(ChangeDescription item) + { + InternalEvent?.Invoke(this, item); + if (MatchesFilter(item)) + { + if (item.ChangeType.HasFlag(WatcherChangeTypes.Created)) + { + Created?.Invoke(this, ToFileSystemEventArgs( + item.ChangeType, item.Path, item.Name)); + } + + if (item.ChangeType.HasFlag(WatcherChangeTypes.Deleted)) + { + Deleted?.Invoke(this, ToFileSystemEventArgs( + item.ChangeType, item.Path, item.Name)); + } + + if (item.ChangeType.HasFlag(WatcherChangeTypes.Changed)) + { + Changed?.Invoke(this, ToFileSystemEventArgs( + item.ChangeType, item.Path, item.Name)); + } + + if (item.ChangeType.HasFlag(WatcherChangeTypes.Renamed)) + { + TriggerRenameNotification(item); + } + } + } + + private void Restart() + { + if (_isInitializing) + { + return; + } + + if (EnableRaisingEvents) + { + Stop(); + BlockingCollection changes = _changes; + _changes = + new BlockingCollection(InternalBufferSize / + BytesPerMessage); + changes.Dispose(); + Start(); + } + else + { + BlockingCollection changes = _changes; + _changes = + new BlockingCollection(InternalBufferSize / + BytesPerMessage); + changes.Dispose(); + } + } + + private void Start() + { + if (_isInitializing) + { + return; + } + + Stop(); + CancellationTokenSource cancellationTokenSource = new(); + _cancellationTokenSource = cancellationTokenSource; + _changeHandler = _fileSystem.Notify.OnEvent(c => + { + if (!_changes.TryAdd(c, 100)) + { + Error?.Invoke(this, new ErrorEventArgs( + ExceptionFactory.InternalBufferOverflowException( + InternalBufferSize, _changes.BoundedCapacity))); + } + }); + CancellationToken token = cancellationTokenSource.Token; + Task.Factory.StartNew(() => + { + try + { + while (!token.IsCancellationRequested) + { + if (_changes.TryTake(out ChangeDescription? c, + Timeout.Infinite, + token)) + { + NotifyChange(c); + } + } + } + catch (Exception) + { + //Ignore any exception + } + }, + token, + TaskCreationOptions.LongRunning, + TaskScheduler.Default) + .ContinueWith(_ => + { + cancellationTokenSource.Dispose(); + }, TaskScheduler.Default); + } + + private void Stop() + { + if (_cancellationTokenSource?.IsCancellationRequested == false) + { + _cancellationTokenSource.Cancel(); + } + + _changeHandler?.Dispose(); + } + + private FileSystemEventArgs ToFileSystemEventArgs( + WatcherChangeTypes changeType, + string changePath, + string? changeName) + { + string path = TransformPathAndName( + changePath, + changeName, + out string name); + + return new FileSystemEventArgs(changeType, + path, name); + } + + private string TransformPathAndName( + string changeDescriptionPath, + string? changeDescriptionName, + out string name) + { + string? transformedName = changeDescriptionName; + string? path = changeDescriptionPath; + if (transformedName == null || + _fileSystem.Path.IsPathRooted(changeDescriptionName)) + { + transformedName = _fileSystem.Path.GetFileName(changeDescriptionPath); + path = _fileSystem.Path.GetDirectoryName(path); + } + else if (path.EndsWith(transformedName)) + { + path = path.Substring(0, path.Length - transformedName.Length); + } + + name = transformedName; + return path ?? ""; + } + + private void TriggerRenameNotification(ChangeDescription item) + => Execute.OnWindows( + () => + { + if (TryMakeRenamedEventArgs(item, + out RenamedEventArgs? eventArgs)) + { + Renamed?.Invoke(this, eventArgs); + } + else if (item.OldPath != null) + { + Deleted?.Invoke(this, ToFileSystemEventArgs( + item.ChangeType, item.OldPath, item.OldName)); + Created?.Invoke(this, ToFileSystemEventArgs( + item.ChangeType, item.Path, item.Name)); + } + }, + () => + { + TryMakeRenamedEventArgs(item, + out RenamedEventArgs? eventArgs); + if (eventArgs != null) + { + Renamed?.Invoke(this, eventArgs); + } + }); + + private bool TryMakeRenamedEventArgs( + ChangeDescription changeDescription, + [NotNullWhen(true)] out RenamedEventArgs? eventArgs) + { + if (changeDescription.OldPath == null) + { + eventArgs = null; + return false; + } + + string path = TransformPathAndName( + changeDescription.Path, + changeDescription.Name, + out string name); + + TransformPathAndName( + changeDescription.OldPath, + changeDescription.OldName, + out string oldName); + + eventArgs = new RenamedEventArgs( + changeDescription.ChangeType, + path, + name, + oldName); + return System.IO.Path.GetDirectoryName(changeDescription.Path)? + .Equals(System.IO.Path.GetDirectoryName(changeDescription.OldPath), + InMemoryLocation.StringComparisonMode) ?? true; + } + + private IWaitForChangedResult WaitForChangedInternal( + WatcherChangeTypes changeType, TimeSpan timeout) + { + TaskCompletionSource + tcs = new(); + + void EventHandler(object? _, ChangeDescription c) + { + if ((c.ChangeType & changeType) != 0) + { + tcs.TrySetResult(new WaitForChangedResultMock(c.ChangeType, c.Name, + oldName: c.OldName, timedOut: false)); + } + } + + InternalEvent += EventHandler; + try + { + bool wasEnabled = EnableRaisingEvents; + if (!wasEnabled) + { + EnableRaisingEvents = true; + } + + tcs.Task.Wait(timeout); + EnableRaisingEvents = wasEnabled; + } + finally + { + InternalEvent -= EventHandler; + } + +#if NETFRAMEWORK + return tcs.Task.IsCompleted + ? tcs.Task.Result + : WaitForChangedResultMock.TimedOutResult; +#else + return tcs.Task.IsCompletedSuccessfully + ? tcs.Task.Result + : WaitForChangedResultMock.TimedOutResult; +#endif + } + + private struct WaitForChangedResultMock : IWaitForChangedResult + { + public WaitForChangedResultMock( + WatcherChangeTypes changeType, + string? name, + string? oldName, + bool timedOut) + { + ChangeType = changeType; + Name = name; + OldName = oldName; + TimedOut = timedOut; + } + + /// + /// The instance representing a timed out . + /// + public static readonly WaitForChangedResultMock TimedOutResult = + new(changeType: 0, name: null, oldName: null, timedOut: true); + + /// + public WatcherChangeTypes ChangeType { get; } + + /// + public string? Name { get; } + + /// + public string? OldName { get; } + + /// + public bool TimedOut { get; } + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/IAccessControlStrategy.cs b/Source/Testably.Abstractions.Testing/FileSystem/IAccessControlStrategy.cs index 0b741a975..ad6b66ff6 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/IAccessControlStrategy.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/IAccessControlStrategy.cs @@ -1,6 +1,4 @@ -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Testing.FileSystem; +namespace Testably.Abstractions.Testing.FileSystem; /// /// The strategy to simulate access control (ACL) in the . diff --git a/Source/Testably.Abstractions.Testing/FileSystem/IInterceptionHandler.cs b/Source/Testably.Abstractions.Testing/FileSystem/IInterceptionHandler.cs index 4a9b92c0c..8316aafd0 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/IInterceptionHandler.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/IInterceptionHandler.cs @@ -1,22 +1,22 @@ -using System; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// The interception handler for the . -/// -public interface IInterceptionHandler : IFileSystemEntity -{ - /// - /// Callback executed before any change in the matching the - /// is about to occur. - /// - /// The callback to execute before the change occurred. - /// - /// (optional) A predicate used to filter which callbacks should be intercepted.
- /// If set to (default value) all callbacks are intercepted. - /// - /// This allows e.g. to throw custom exceptions instead. - MockFileSystem Event(Action interceptionCallback, - Func? predicate = null); -} +using System; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// The interception handler for the . +/// +public interface IInterceptionHandler : IFileSystemEntity +{ + /// + /// Callback executed before any change in the matching the + /// is about to occur. + /// + /// The callback to execute before the change occurred. + /// + /// (optional) A predicate used to filter which callbacks should be intercepted.
+ /// If set to (default value) all callbacks are intercepted. + /// + /// This allows e.g. to throw custom exceptions instead. + MockFileSystem Event(Action interceptionCallback, + Func? predicate = null); +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/INotificationHandler.cs b/Source/Testably.Abstractions.Testing/FileSystem/INotificationHandler.cs index d73c8a34f..95e80e571 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/INotificationHandler.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/INotificationHandler.cs @@ -1,23 +1,23 @@ -using System; - -namespace Testably.Abstractions.Testing.FileSystem; - -/// -/// The notification handler for the . -/// -public interface INotificationHandler : IFileSystemEntity -{ - /// - /// Callback executed when any change in the matching the - /// occurred. - /// - /// The callback to execute after the change occurred. - /// - /// (optional) A predicate used to filter which callbacks should be notified.
- /// If set to (default value) all callbacks are notified. - /// - /// An to un-register the callback on dispose. - Notification.IAwaitableCallback OnEvent( - Action? notificationCallback = null, - Func? predicate = null); -} +using System; + +namespace Testably.Abstractions.Testing.FileSystem; + +/// +/// The notification handler for the . +/// +public interface INotificationHandler : IFileSystemEntity +{ + /// + /// Callback executed when any change in the matching the + /// occurred. + /// + /// The callback to execute after the change occurred. + /// + /// (optional) A predicate used to filter which callbacks should be notified.
+ /// If set to (default value) all callbacks are notified. + /// + /// An to un-register the callback on dispose. + Notification.IAwaitableCallback OnEvent( + Action? notificationCallback = null, + Func? predicate = null); +} diff --git a/Source/Testably.Abstractions.Testing/FileSystem/NullAccessControlStrategy.cs b/Source/Testably.Abstractions.Testing/FileSystem/NullAccessControlStrategy.cs index be91f8cd7..e21757429 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/NullAccessControlStrategy.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/NullAccessControlStrategy.cs @@ -1,6 +1,4 @@ -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Testing.FileSystem; +namespace Testably.Abstractions.Testing.FileSystem; /// /// Null object of an which does not restrict access in any way. diff --git a/Source/Testably.Abstractions.Testing/FileSystem/PathMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/PathMock.cs index 7e682355d..3229bfdd9 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/PathMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/PathMock.cs @@ -1,46 +1,46 @@ -using System.IO; -using Testably.Abstractions.Helpers; -#if FEATURE_FILESYSTEM_NET7 -using System.Diagnostics.CodeAnalysis; -using Testably.Abstractions.Testing.Storage; -#endif - -namespace Testably.Abstractions.Testing.FileSystem; - -internal sealed class PathMock : PathSystemBase -{ - private readonly MockFileSystem _fileSystem; - - internal PathMock(MockFileSystem fileSystem) - : base(fileSystem) - { - _fileSystem = fileSystem; - } - -#if FEATURE_FILESYSTEM_NET7 - /// - public override bool Exists([NotNullWhen(true)] string? path) - { - if (string.IsNullOrEmpty(path)) - { - return false; - } - - return _fileSystem.Storage.GetContainer(_fileSystem.Storage.GetLocation(path)) - is not NullContainer; - } -#endif - - /// - public override string GetFullPath(string path) - { - if (string.IsNullOrEmpty(path)) - { - return string.Empty; - } - - return Path.GetFullPath(Path.Combine( - _fileSystem.Storage.CurrentDirectory, - path)); - } -} +using System.IO; +using Testably.Abstractions.Helpers; +#if FEATURE_FILESYSTEM_NET7 +using System.Diagnostics.CodeAnalysis; +using Testably.Abstractions.Testing.Storage; +#endif + +namespace Testably.Abstractions.Testing.FileSystem; + +internal sealed class PathMock : PathSystemBase +{ + private readonly MockFileSystem _fileSystem; + + internal PathMock(MockFileSystem fileSystem) + : base(fileSystem) + { + _fileSystem = fileSystem; + } + +#if FEATURE_FILESYSTEM_NET7 + /// + public override bool Exists([NotNullWhen(true)] string? path) + { + if (string.IsNullOrEmpty(path)) + { + return false; + } + + return _fileSystem.Storage.GetContainer(_fileSystem.Storage.GetLocation(path)) + is not NullContainer; + } +#endif + + /// + public override string GetFullPath(string path) + { + if (string.IsNullOrEmpty(path)) + { + return string.Empty; + } + + return Path.GetFullPath(Path.Combine( + _fileSystem.Storage.CurrentDirectory, + path)); + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryCleaner.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryCleaner.cs index ce97e80dc..8bc0f9bb9 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryCleaner.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryCleaner.cs @@ -1,132 +1,132 @@ -using System; -using System.IO; -using System.Threading; -using Testably.Abstractions.Testing.Helpers; - -namespace Testably.Abstractions.Testing.FileSystemInitializer; - -internal sealed class DirectoryCleaner : IDirectoryCleaner -{ - private readonly IFileSystem _fileSystem; - private readonly Action? _logger; - - public DirectoryCleaner(IFileSystem fileSystem, Action? logger) - { - _fileSystem = fileSystem; - _logger = logger; - BasePath = InitializeBasePath(); - } - - #region IDirectoryCleaner Members - - /// - public string BasePath { get; } - - /// - public void Dispose() - { - ITimeSystem? timeSystem = (_fileSystem as MockFileSystem)?.TimeSystem; - try - { - // It is important to reset the current directory, as otherwise deleting the BasePath - // results in a IOException, because the process cannot access the file. - _fileSystem.Directory.SetCurrentDirectory(_fileSystem.Path.GetTempPath()); - - _logger?.Invoke($"Cleaning up '{BasePath}'..."); - for (int i = 10; i >= 0; i--) - { - try - { - ForceDeleteDirectory(BasePath); - break; - } - catch (Exception) - { - if (i == 0) - { - throw; - } - - _logger?.Invoke( - $" Force delete failed! Retry again {i} times in 100ms..."); - if (timeSystem == null) - { - Thread.Sleep(100); - } - else - { - timeSystem.Thread.Sleep(100); - } - } - } - - _logger?.Invoke("Cleanup was successful :-)"); - } - catch (Exception ex) - { - _logger?.Invoke($"Could not clean up '{BasePath}' because: {ex}"); - } - } - - #endregion - - /// - /// Force deletes the directory at the given .
- /// Removes the flag, if necessary. - /// - /// If is set (default ), the sub directories are force deleted as - /// well. - ///
- private void ForceDeleteDirectory(string path, bool recursive = true) - { - if (!_fileSystem.Directory.Exists(path)) - { - return; - } - - IDirectoryInfo directory = _fileSystem.DirectoryInfo.New(path); - directory.Attributes = FileAttributes.Normal; - - foreach (IFileInfo info in directory.EnumerateFiles( - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly)) - { - info.Attributes = FileAttributes.Normal; - info.Delete(); - } - - if (recursive) - { - foreach (IDirectoryInfo info in - directory.EnumerateDirectories( - EnumerationOptionsHelper.DefaultSearchPattern, - SearchOption.TopDirectoryOnly)) - { - ForceDeleteDirectory(info.FullName, recursive); - } - } - - _fileSystem.Directory.Delete(path); - } - - private string InitializeBasePath() - { - string basePath; - - do - { - string localBasePath = _fileSystem.Path.Combine( - _fileSystem.Path.GetTempPath(), - _fileSystem.Path.GetFileNameWithoutExtension(_fileSystem.Path - .GetRandomFileName())); - Execute.OnMac(() => localBasePath = "/private" + localBasePath); - basePath = localBasePath; - } while (_fileSystem.Directory.Exists(basePath)); - - _fileSystem.Directory.CreateDirectory(basePath); - - _logger?.Invoke($"Use '{basePath}' as current directory."); - _fileSystem.Directory.SetCurrentDirectory(basePath); - return basePath; - } -} +using System; +using System.IO; +using System.Threading; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.FileSystemInitializer; + +internal sealed class DirectoryCleaner : IDirectoryCleaner +{ + private readonly IFileSystem _fileSystem; + private readonly Action? _logger; + + public DirectoryCleaner(IFileSystem fileSystem, Action? logger) + { + _fileSystem = fileSystem; + _logger = logger; + BasePath = InitializeBasePath(); + } + + #region IDirectoryCleaner Members + + /// + public string BasePath { get; } + + /// + public void Dispose() + { + ITimeSystem? timeSystem = (_fileSystem as MockFileSystem)?.TimeSystem; + try + { + // It is important to reset the current directory, as otherwise deleting the BasePath + // results in a IOException, because the process cannot access the file. + _fileSystem.Directory.SetCurrentDirectory(_fileSystem.Path.GetTempPath()); + + _logger?.Invoke($"Cleaning up '{BasePath}'..."); + for (int i = 10; i >= 0; i--) + { + try + { + ForceDeleteDirectory(BasePath); + break; + } + catch (Exception) + { + if (i == 0) + { + throw; + } + + _logger?.Invoke( + $" Force delete failed! Retry again {i} times in 100ms..."); + if (timeSystem == null) + { + Thread.Sleep(100); + } + else + { + timeSystem.Thread.Sleep(100); + } + } + } + + _logger?.Invoke("Cleanup was successful :-)"); + } + catch (Exception ex) + { + _logger?.Invoke($"Could not clean up '{BasePath}' because: {ex}"); + } + } + + #endregion + + /// + /// Force deletes the directory at the given .
+ /// Removes the flag, if necessary. + /// + /// If is set (default ), the sub directories are force deleted as + /// well. + ///
+ private void ForceDeleteDirectory(string path, bool recursive = true) + { + if (!_fileSystem.Directory.Exists(path)) + { + return; + } + + IDirectoryInfo directory = _fileSystem.DirectoryInfo.New(path); + directory.Attributes = FileAttributes.Normal; + + foreach (IFileInfo info in directory.EnumerateFiles( + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly)) + { + info.Attributes = FileAttributes.Normal; + info.Delete(); + } + + if (recursive) + { + foreach (IDirectoryInfo info in + directory.EnumerateDirectories( + EnumerationOptionsHelper.DefaultSearchPattern, + SearchOption.TopDirectoryOnly)) + { + ForceDeleteDirectory(info.FullName, recursive); + } + } + + _fileSystem.Directory.Delete(path); + } + + private string InitializeBasePath() + { + string basePath; + + do + { + string localBasePath = _fileSystem.Path.Combine( + _fileSystem.Path.GetTempPath(), + _fileSystem.Path.GetFileNameWithoutExtension(_fileSystem.Path + .GetRandomFileName())); + Execute.OnMac(() => localBasePath = "/private" + localBasePath); + basePath = localBasePath; + } while (_fileSystem.Directory.Exists(basePath)); + + _fileSystem.Directory.CreateDirectory(basePath); + + _logger?.Invoke($"Use '{basePath}' as current directory."); + _fileSystem.Directory.SetCurrentDirectory(basePath); + return basePath; + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryInitializer.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryInitializer.cs index 43589ca48..f62e5c2bc 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryInitializer.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/DirectoryInitializer.cs @@ -1,33 +1,33 @@ -using System; - -namespace Testably.Abstractions.Testing.FileSystemInitializer; - -internal sealed class DirectoryInitializer - : Initializer, - IFileSystemDirectoryInitializer - where TFileSystem : IFileSystem -{ - public DirectoryInitializer(Initializer initializer, - IDirectoryInfo directory) - : base(initializer) - { - Directory = directory; - } - - #region IFileSystemDirectoryInitializer Members - - /// - public IDirectoryInfo Directory { get; } - - /// - public IFileSystemDirectoryInitializer Initialized( - Action> subdirectoryInitializer) - { - Initializer initializer = new(this, Directory); - subdirectoryInitializer.Invoke(initializer); - return this; - } - - #endregion -} +using System; + +namespace Testably.Abstractions.Testing.FileSystemInitializer; + +internal sealed class DirectoryInitializer + : Initializer, + IFileSystemDirectoryInitializer + where TFileSystem : IFileSystem +{ + public DirectoryInitializer(Initializer initializer, + IDirectoryInfo directory) + : base(initializer) + { + Directory = directory; + } + + #region IFileSystemDirectoryInitializer Members + + /// + public IDirectoryInfo Directory { get; } + + /// + public IFileSystemDirectoryInitializer Initialized( + Action> subdirectoryInitializer) + { + Initializer initializer = new(this, Directory); + subdirectoryInitializer.Invoke(initializer); + return this; + } + + #endregion +} diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs index 1fdb3cc21..90cdc6317 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs @@ -1,65 +1,65 @@ -using System; - -namespace Testably.Abstractions.Testing.FileSystemInitializer; - -internal sealed class FileInitializer - : Initializer, - IFileSystemFileInitializer - where TFileSystem : IFileSystem -{ - public FileInitializer(Initializer initializer, - IFileInfo file) - : base(initializer) - { - File = file; - } - - #region IFileSystemFileInitializer Members - - /// - public IFileInfo File { get; } - - /// - public IFileSystemFileInitializer Which( - Action fileManipulation) - { - FileManipulator fileManipulator = new(FileSystem, File); - fileManipulation.Invoke(fileManipulator); - return this; - } - - #endregion - - private sealed class FileManipulator : IFileManipulator - { - internal FileManipulator(IFileSystem fileSystem, IFileInfo file) - { - FileSystem = fileSystem; - File = file; - } - - #region IFileManipulator Members - - /// - public IFileInfo File { get; } - - /// - public IFileSystem FileSystem { get; } - - /// - public IFileManipulator HasBytesContent(byte[] bytes) - { - FileSystem.File.WriteAllBytes(File.FullName, bytes); - return this; - } - - /// - public IFileManipulator HasStringContent(string contents) - { - FileSystem.File.WriteAllText(File.FullName, contents); - return this; - } - - #endregion - } -} +using System; + +namespace Testably.Abstractions.Testing.FileSystemInitializer; + +internal sealed class FileInitializer + : Initializer, + IFileSystemFileInitializer + where TFileSystem : IFileSystem +{ + public FileInitializer(Initializer initializer, + IFileInfo file) + : base(initializer) + { + File = file; + } + + #region IFileSystemFileInitializer Members + + /// + public IFileInfo File { get; } + + /// + public IFileSystemFileInitializer Which( + Action fileManipulation) + { + FileManipulator fileManipulator = new(FileSystem, File); + fileManipulation.Invoke(fileManipulator); + return this; + } + + #endregion + + private sealed class FileManipulator : IFileManipulator + { + internal FileManipulator(IFileSystem fileSystem, IFileInfo file) + { + FileSystem = fileSystem; + File = file; + } + + #region IFileManipulator Members + + /// + public IFileInfo File { get; } + + /// + public IFileSystem FileSystem { get; } + + /// + public IFileManipulator HasBytesContent(byte[] bytes) + { + FileSystem.File.WriteAllBytes(File.FullName, bytes); + return this; + } + + /// + public IFileManipulator HasStringContent(string contents) + { + FileSystem.File.WriteAllText(File.FullName, contents); + return this; + } + + #endregion + } +} diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileManipulator.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileManipulator.cs index 8156fca22..b3d5bfb9d 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileManipulator.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileManipulator.cs @@ -1,6 +1,4 @@ -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Testing.FileSystemInitializer; +namespace Testably.Abstractions.Testing.FileSystemInitializer; /// /// Manipulates the in the with test data. diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemDirectoryInitializer.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemDirectoryInitializer.cs index 32e43b01f..fb0d11b1d 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemDirectoryInitializer.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemDirectoryInitializer.cs @@ -1,22 +1,22 @@ -using System; - -namespace Testably.Abstractions.Testing.FileSystemInitializer; - -/// -/// Initializes a directory in the with test data. -/// -public interface IFileSystemDirectoryInitializer - : IFileSystemInitializer - where TFileSystem : IFileSystem -{ - /// - /// The directory to initialize. - /// - public IDirectoryInfo Directory { get; } - - /// - /// Initializes the subdirectory in the with test data. - /// - public IFileSystemDirectoryInitializer Initialized( - Action> subdirectoryInitializer); -} +using System; + +namespace Testably.Abstractions.Testing.FileSystemInitializer; + +/// +/// Initializes a directory in the with test data. +/// +public interface IFileSystemDirectoryInitializer + : IFileSystemInitializer + where TFileSystem : IFileSystem +{ + /// + /// The directory to initialize. + /// + public IDirectoryInfo Directory { get; } + + /// + /// Initializes the subdirectory in the with test data. + /// + public IFileSystemDirectoryInitializer Initialized( + Action> subdirectoryInitializer); +} diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemFileInitializer.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemFileInitializer.cs index d7b8fccf3..e7ba47f7d 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemFileInitializer.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemFileInitializer.cs @@ -1,22 +1,22 @@ -using System; - -namespace Testably.Abstractions.Testing.FileSystemInitializer; - -/// -/// Initializes a file in the with test data. -/// -public interface IFileSystemFileInitializer - : IFileSystemInitializer - where TFileSystem : IFileSystem -{ - /// - /// The file to initialize. - /// - public IFileInfo File { get; } - - /// - /// Manipulates the in the with test data. - /// - public IFileSystemFileInitializer Which( - Action fileManipulation); -} +using System; + +namespace Testably.Abstractions.Testing.FileSystemInitializer; + +/// +/// Initializes a file in the with test data. +/// +public interface IFileSystemFileInitializer + : IFileSystemInitializer + where TFileSystem : IFileSystem +{ + /// + /// The file to initialize. + /// + public IFileInfo File { get; } + + /// + /// Manipulates the in the with test data. + /// + public IFileSystemFileInitializer Which( + Action fileManipulation); +} diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemInitializer.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemInitializer.cs index fbc5a0e7f..9b54d17b8 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemInitializer.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/IFileSystemInitializer.cs @@ -1,6 +1,4 @@ -using Testably.Abstractions.FileSystem; - -namespace Testably.Abstractions.Testing.FileSystemInitializer; +namespace Testably.Abstractions.Testing.FileSystemInitializer; /// /// Initializes the with test data. diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/Initializer.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/Initializer.cs index 116eeb85e..91fc88367 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/Initializer.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/Initializer.cs @@ -1,124 +1,124 @@ -using System.Collections.Generic; -using Testably.Abstractions.RandomSystem; -using Testably.Abstractions.Testing.Helpers; - -namespace Testably.Abstractions.Testing.FileSystemInitializer; - -internal class Initializer - : IFileSystemInitializer - where TFileSystem : IFileSystem -{ - private readonly string _basePath; - - private readonly Dictionary - _initializedFileSystemInfos = new(); - - public Initializer(TFileSystem fileSystem, string basePath) - { - _basePath = basePath; - FileSystem = fileSystem; - } - - protected Initializer(Initializer parent) - { - FileSystem = parent.FileSystem; - _initializedFileSystemInfos = parent._initializedFileSystemInfos; - _basePath = parent._basePath; - } - - internal Initializer(Initializer parent, - IDirectoryInfo subdirectory) - { - FileSystem = parent.FileSystem; - _initializedFileSystemInfos = parent._initializedFileSystemInfos; - _basePath = FileSystem.Path.Combine(parent._basePath, subdirectory.Name); - } - - #region IFileSystemInitializer Members - - /// - public IDirectoryInfo BaseDirectory - => FileSystem.DirectoryInfo.New(_basePath); - - /// - public TFileSystem FileSystem { get; } - - /// - public IFileSystemInfo this[int index] - => _initializedFileSystemInfos[index]; - - /// - public IFileSystemFileInitializer WithAFile(string? extension = null) - { - IRandom random = (FileSystem as MockFileSystem)? - .RandomSystem.Random.Shared ?? RandomFactory.Shared; - string fileName; - do - { - fileName = - $"{random.GenerateFileName()}-{random.Next(10000)}.{random.GenerateFileExtension(extension)}"; - } while (FileSystem.File.Exists( - FileSystem.Path.Combine(_basePath, fileName))); - - return WithFile(fileName); - } - - /// - public IFileSystemDirectoryInitializer WithASubdirectory() - { - IRandom random = (FileSystem as MockFileSystem)? - .RandomSystem.Random.Shared ?? RandomFactory.Shared; - string directoryName; - do - { - directoryName = - $"{random.GenerateFileName()}-{random.Next(10000)}"; - } while (FileSystem.Directory.Exists( - FileSystem.Path.Combine(_basePath, directoryName))); - - return WithSubdirectory(directoryName); - } - - /// - public IFileSystemFileInitializer WithFile(string fileName) - { - IFileInfo fileInfo = FileSystem.FileInfo.New( - FileSystem.Path.Combine(_basePath, fileName)); - if (fileInfo.Exists) - { - throw new TestingException( - $"The file '{fileInfo.FullName}' already exists!"); - } - - FileSystem.File.WriteAllText(fileInfo.FullName, null); - _initializedFileSystemInfos.Add( - _initializedFileSystemInfos.Count, - fileInfo); - - fileInfo.Refresh(); - return new FileInitializer(this, fileInfo); - } - - /// - public IFileSystemDirectoryInitializer WithSubdirectory( - string directoryName) - { - IDirectoryInfo directoryInfo = FileSystem.DirectoryInfo.New( - FileSystem.Path.Combine(_basePath, directoryName)); - if (directoryInfo.Exists) - { - throw new TestingException( - $"The directory '{directoryInfo.FullName}' already exists!"); - } - - FileSystem.Directory.CreateDirectory(directoryInfo.FullName); - _initializedFileSystemInfos.Add( - _initializedFileSystemInfos.Count, - directoryInfo); - - directoryInfo.Refresh(); - return new DirectoryInitializer(this, directoryInfo); - } - - #endregion -} +using System.Collections.Generic; +using Testably.Abstractions.RandomSystem; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.FileSystemInitializer; + +internal class Initializer + : IFileSystemInitializer + where TFileSystem : IFileSystem +{ + private readonly string _basePath; + + private readonly Dictionary + _initializedFileSystemInfos = new(); + + public Initializer(TFileSystem fileSystem, string basePath) + { + _basePath = basePath; + FileSystem = fileSystem; + } + + protected Initializer(Initializer parent) + { + FileSystem = parent.FileSystem; + _initializedFileSystemInfos = parent._initializedFileSystemInfos; + _basePath = parent._basePath; + } + + internal Initializer(Initializer parent, + IDirectoryInfo subdirectory) + { + FileSystem = parent.FileSystem; + _initializedFileSystemInfos = parent._initializedFileSystemInfos; + _basePath = FileSystem.Path.Combine(parent._basePath, subdirectory.Name); + } + + #region IFileSystemInitializer Members + + /// + public IDirectoryInfo BaseDirectory + => FileSystem.DirectoryInfo.New(_basePath); + + /// + public TFileSystem FileSystem { get; } + + /// + public IFileSystemInfo this[int index] + => _initializedFileSystemInfos[index]; + + /// + public IFileSystemFileInitializer WithAFile(string? extension = null) + { + IRandom random = (FileSystem as MockFileSystem)? + .RandomSystem.Random.Shared ?? RandomFactory.Shared; + string fileName; + do + { + fileName = + $"{random.GenerateFileName()}-{random.Next(10000)}.{random.GenerateFileExtension(extension)}"; + } while (FileSystem.File.Exists( + FileSystem.Path.Combine(_basePath, fileName))); + + return WithFile(fileName); + } + + /// + public IFileSystemDirectoryInitializer WithASubdirectory() + { + IRandom random = (FileSystem as MockFileSystem)? + .RandomSystem.Random.Shared ?? RandomFactory.Shared; + string directoryName; + do + { + directoryName = + $"{random.GenerateFileName()}-{random.Next(10000)}"; + } while (FileSystem.Directory.Exists( + FileSystem.Path.Combine(_basePath, directoryName))); + + return WithSubdirectory(directoryName); + } + + /// + public IFileSystemFileInitializer WithFile(string fileName) + { + IFileInfo fileInfo = FileSystem.FileInfo.New( + FileSystem.Path.Combine(_basePath, fileName)); + if (fileInfo.Exists) + { + throw new TestingException( + $"The file '{fileInfo.FullName}' already exists!"); + } + + FileSystem.File.WriteAllText(fileInfo.FullName, null); + _initializedFileSystemInfos.Add( + _initializedFileSystemInfos.Count, + fileInfo); + + fileInfo.Refresh(); + return new FileInitializer(this, fileInfo); + } + + /// + public IFileSystemDirectoryInitializer WithSubdirectory( + string directoryName) + { + IDirectoryInfo directoryInfo = FileSystem.DirectoryInfo.New( + FileSystem.Path.Combine(_basePath, directoryName)); + if (directoryInfo.Exists) + { + throw new TestingException( + $"The directory '{directoryInfo.FullName}' already exists!"); + } + + FileSystem.Directory.CreateDirectory(directoryInfo.FullName); + _initializedFileSystemInfos.Add( + _initializedFileSystemInfos.Count, + directoryInfo); + + directoryInfo.Refresh(); + return new DirectoryInitializer(this, directoryInfo); + } + + #endregion +} diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializerExtensions.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializerExtensions.cs index cb7dc0c35..5c952531d 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializerExtensions.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializerExtensions.cs @@ -1,47 +1,47 @@ -using System; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Testing; - -/// -/// Initializes the with test data. -/// -public static class FileSystemInitializerExtensions -{ - /// - /// Initializes the in the working directory with test data. - /// - public static IFileSystemInitializer Initialize( - this TFileSystem fileSystem) - where TFileSystem : IFileSystem - => fileSystem.InitializeIn("."); - - /// - /// Initializes the in the with test data. - /// - public static IFileSystemInitializer InitializeIn( - this TFileSystem fileSystem, - string basePath) - where TFileSystem : IFileSystem - { - fileSystem.Directory.CreateDirectory(basePath); - fileSystem.Directory.SetCurrentDirectory(basePath); - return new Initializer(fileSystem, "."); - } - - /// - /// Sets the current directory to a new temporary directory.
- /// and all relative paths will use this directory. - ///
- /// The file system. - /// (optional) A callback to log the cleanup process. - /// - /// A that will - /// force delete all content in the temporary directory on dispose. - /// - public static IDirectoryCleaner SetCurrentDirectoryToEmptyTemporaryDirectory( - this IFileSystem fileSystem, Action? logger = null) - { - return new DirectoryCleaner(fileSystem, logger); - } -} +using System; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Testing; + +/// +/// Initializes the with test data. +/// +public static class FileSystemInitializerExtensions +{ + /// + /// Initializes the in the working directory with test data. + /// + public static IFileSystemInitializer Initialize( + this TFileSystem fileSystem) + where TFileSystem : IFileSystem + => fileSystem.InitializeIn("."); + + /// + /// Initializes the in the with test data. + /// + public static IFileSystemInitializer InitializeIn( + this TFileSystem fileSystem, + string basePath) + where TFileSystem : IFileSystem + { + fileSystem.Directory.CreateDirectory(basePath); + fileSystem.Directory.SetCurrentDirectory(basePath); + return new Initializer(fileSystem, "."); + } + + /// + /// Sets the current directory to a new temporary directory.
+ /// and all relative paths will use this directory. + ///
+ /// The file system. + /// (optional) A callback to log the cleanup process. + /// + /// A that will + /// force delete all content in the temporary directory on dispose. + /// + public static IDirectoryCleaner SetCurrentDirectoryToEmptyTemporaryDirectory( + this IFileSystem fileSystem, Action? logger = null) + { + return new DirectoryCleaner(fileSystem, logger); + } +} diff --git a/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensibility.cs b/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensibility.cs index 5a027e5ee..bdfe283c3 100644 --- a/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensibility.cs +++ b/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensibility.cs @@ -1,46 +1,46 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace Testably.Abstractions.Testing.Helpers; - -internal class FileSystemExtensibility : IFileSystemExtensibility -{ - private readonly Dictionary _metadata = new(); - - /// - public bool TryGetWrappedInstance([NotNullWhen(true)] out T? wrappedInstance) - { - wrappedInstance = default; - return false; - } - - /// - public void StoreMetadata(string key, T? value) - { - _metadata[key] = value; - } - - /// - public T? RetrieveMetadata(string key) - { - if (_metadata.TryGetValue(key, out object? value) && - // ReSharper disable once MergeCastWithTypeCheck -- Not possible due to nullable - value is T?) - { - return (T?)value; - } - - return default; - } - - internal void CopyMetadataTo(IFileSystemExtensibility target) - { - if (target is FileSystemExtensibility targetContainer) - { - foreach (KeyValuePair item in _metadata) - { - targetContainer._metadata[item.Key] = item.Value; - } - } - } -} +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Testably.Abstractions.Testing.Helpers; + +internal class FileSystemExtensibility : IFileSystemExtensibility +{ + private readonly Dictionary _metadata = new(); + + /// + public bool TryGetWrappedInstance([NotNullWhen(true)] out T? wrappedInstance) + { + wrappedInstance = default; + return false; + } + + /// + public void StoreMetadata(string key, T? value) + { + _metadata[key] = value; + } + + /// + public T? RetrieveMetadata(string key) + { + if (_metadata.TryGetValue(key, out object? value) && + // ReSharper disable once MergeCastWithTypeCheck -- Not possible due to nullable + value is T?) + { + return (T?)value; + } + + return default; + } + + internal void CopyMetadataTo(IFileSystemExtensibility target) + { + if (target is FileSystemExtensibility targetContainer) + { + foreach (KeyValuePair item in _metadata) + { + targetContainer._metadata[item.Key] = item.Value; + } + } + } +} diff --git a/Source/Testably.Abstractions.Testing/MockFileSystem.cs b/Source/Testably.Abstractions.Testing/MockFileSystem.cs index 0edd1a11d..998c91f66 100644 --- a/Source/Testably.Abstractions.Testing/MockFileSystem.cs +++ b/Source/Testably.Abstractions.Testing/MockFileSystem.cs @@ -1,153 +1,153 @@ -using Microsoft.Win32.SafeHandles; -using System; -using Testably.Abstractions.Testing.FileSystem; -using Testably.Abstractions.Testing.Storage; - -namespace Testably.Abstractions.Testing; - -/// -/// A test helper for simulating the file system. Implements . -/// -public sealed class MockFileSystem : IFileSystem -{ - /// - /// Intercept events in the before they occur. - /// - public IInterceptionHandler Intercept => ChangeHandler; - - /// - /// Get notified of events in the after they occurred. - /// - public INotificationHandler Notify => ChangeHandler; - - /// - /// The used random system. - /// - public IRandomSystem RandomSystem { get; } - - /// - /// The used time system. - /// - public ITimeSystem TimeSystem { get; } - - /// - /// The change handler used to notify about events occurring in the . - /// - internal ChangeHandler ChangeHandler { get; } - - /// - /// The underlying storage of directories and files. - /// - internal IStorage Storage => _storage; - - private readonly DirectoryMock _directoryMock; - private readonly FileMock _fileMock; - private readonly PathMock _pathMock; - private readonly InMemoryStorage _storage; - - internal IAccessControlStrategy AccessControlStrategy - { - get; - private set; - } - - internal ISafeFileHandleStrategy SafeFileHandleStrategy - { - get; - private set; - } - - /// - /// Initializes the . - /// - public MockFileSystem() - { - RandomSystem = new MockRandomSystem(); - TimeSystem = new MockTimeSystem(TimeProvider.Now()); - _pathMock = new PathMock(this); - _storage = new InMemoryStorage(this); - ChangeHandler = new ChangeHandler(this); - _directoryMock = new DirectoryMock(this); - _fileMock = new FileMock(this); - DirectoryInfo = new DirectoryInfoFactoryMock(this); - DriveInfo = new DriveInfoFactoryMock(this); - FileInfo = new FileInfoFactoryMock(this); - FileStream = new FileStreamFactoryMock(this); - FileSystemWatcher = new FileSystemWatcherFactoryMock(this); - SafeFileHandleStrategy = new NullSafeFileHandleStrategy(); - AccessControlStrategy = new NullAccessControlStrategy(); - } - - #region IFileSystem Members - - /// - public IDirectory Directory - => _directoryMock; - - /// - public IDirectoryInfoFactory DirectoryInfo { get; } - - /// - public IDriveInfoFactory DriveInfo { get; } - - /// - public IFile File - => _fileMock; - - /// - public IFileInfoFactory FileInfo { get; } - - /// - public IFileStreamFactory FileStream { get; } - - /// - public IFileSystemWatcherFactory FileSystemWatcher { get; } - - /// - public IPath Path - => _pathMock; - - #endregion - - /// - /// Implements a custom access control (ACL) mechanism. - /// - /// The defines a method that receives two values and allows or denies access: - ///
- /// - The full path of the file or directory as first parameter
- /// - The as second parameter - ///
- public MockFileSystem WithAccessControlStrategy(IAccessControlStrategy accessControlStrategy) - { - AccessControlStrategy = accessControlStrategy; - return this; - } - - /// - /// Changes the parameters of the specified . - /// - /// If the does not exist, it will be created/mounted. - /// - public MockFileSystem WithDrive(string? drive, - Action? driveCallback = null) - { - IStorageDrive driveInfoMock = - drive == null - ? Storage.MainDrive - : Storage.GetOrAddDrive(drive); - driveCallback?.Invoke(driveInfoMock); - return this; - } - - /// - /// Registers the strategy how to deal with s in the . - /// - /// Defaults to , if nothing is provided. - /// - public MockFileSystem WithSafeFileHandleStrategy( - ISafeFileHandleStrategy safeFileHandleStrategy) - { - SafeFileHandleStrategy = safeFileHandleStrategy; - return this; - } -} +using Microsoft.Win32.SafeHandles; +using System; +using Testably.Abstractions.Testing.FileSystem; +using Testably.Abstractions.Testing.Storage; + +namespace Testably.Abstractions.Testing; + +/// +/// A test helper for simulating the file system. Implements . +/// +public sealed class MockFileSystem : IFileSystem +{ + /// + /// Intercept events in the before they occur. + /// + public IInterceptionHandler Intercept => ChangeHandler; + + /// + /// Get notified of events in the after they occurred. + /// + public INotificationHandler Notify => ChangeHandler; + + /// + /// The used random system. + /// + public IRandomSystem RandomSystem { get; } + + /// + /// The used time system. + /// + public ITimeSystem TimeSystem { get; } + + /// + /// The change handler used to notify about events occurring in the . + /// + internal ChangeHandler ChangeHandler { get; } + + /// + /// The underlying storage of directories and files. + /// + internal IStorage Storage => _storage; + + private readonly DirectoryMock _directoryMock; + private readonly FileMock _fileMock; + private readonly PathMock _pathMock; + private readonly InMemoryStorage _storage; + + internal IAccessControlStrategy AccessControlStrategy + { + get; + private set; + } + + internal ISafeFileHandleStrategy SafeFileHandleStrategy + { + get; + private set; + } + + /// + /// Initializes the . + /// + public MockFileSystem() + { + RandomSystem = new MockRandomSystem(); + TimeSystem = new MockTimeSystem(TimeProvider.Now()); + _pathMock = new PathMock(this); + _storage = new InMemoryStorage(this); + ChangeHandler = new ChangeHandler(this); + _directoryMock = new DirectoryMock(this); + _fileMock = new FileMock(this); + DirectoryInfo = new DirectoryInfoFactoryMock(this); + DriveInfo = new DriveInfoFactoryMock(this); + FileInfo = new FileInfoFactoryMock(this); + FileStream = new FileStreamFactoryMock(this); + FileSystemWatcher = new FileSystemWatcherFactoryMock(this); + SafeFileHandleStrategy = new NullSafeFileHandleStrategy(); + AccessControlStrategy = new NullAccessControlStrategy(); + } + + #region IFileSystem Members + + /// + public IDirectory Directory + => _directoryMock; + + /// + public IDirectoryInfoFactory DirectoryInfo { get; } + + /// + public IDriveInfoFactory DriveInfo { get; } + + /// + public IFile File + => _fileMock; + + /// + public IFileInfoFactory FileInfo { get; } + + /// + public IFileStreamFactory FileStream { get; } + + /// + public IFileSystemWatcherFactory FileSystemWatcher { get; } + + /// + public IPath Path + => _pathMock; + + #endregion + + /// + /// Implements a custom access control (ACL) mechanism. + /// + /// The defines a method that receives two values and allows or denies access: + ///
+ /// - The full path of the file or directory as first parameter
+ /// - The as second parameter + ///
+ public MockFileSystem WithAccessControlStrategy(IAccessControlStrategy accessControlStrategy) + { + AccessControlStrategy = accessControlStrategy; + return this; + } + + /// + /// Changes the parameters of the specified . + /// + /// If the does not exist, it will be created/mounted. + /// + public MockFileSystem WithDrive(string? drive, + Action? driveCallback = null) + { + IStorageDrive driveInfoMock = + drive == null + ? Storage.MainDrive + : Storage.GetOrAddDrive(drive); + driveCallback?.Invoke(driveInfoMock); + return this; + } + + /// + /// Registers the strategy how to deal with s in the . + /// + /// Defaults to , if nothing is provided. + /// + public MockFileSystem WithSafeFileHandleStrategy( + ISafeFileHandleStrategy safeFileHandleStrategy) + { + SafeFileHandleStrategy = safeFileHandleStrategy; + return this; + } +} diff --git a/Source/Testably.Abstractions.Testing/Polyfills/FileFeatureExtensionMethods.cs b/Source/Testably.Abstractions.Testing/Polyfills/FileFeatureExtensionMethods.cs index 11d7edd8c..5e656a4c9 100644 --- a/Source/Testably.Abstractions.Testing/Polyfills/FileFeatureExtensionMethods.cs +++ b/Source/Testably.Abstractions.Testing/Polyfills/FileFeatureExtensionMethods.cs @@ -1,62 +1,63 @@ -#if !FEATURE_PATH_ADVANCED -using System.Diagnostics.CodeAnalysis; -using Testably.Abstractions.Testing.Helpers; - -// ReSharper disable once CheckNamespace -namespace Testably.Abstractions.Testing; - -/// -/// Provides extension methods to simplify writing platform independent tests. -/// -[ExcludeFromCodeCoverage] -internal static class FileFeatureExtensionMethods -{ - /// - /// Trims one trailing directory separator beyond the root of the path. - /// - internal static string TrimEndingDirectorySeparator( - this IPath pathSystem, - string path) - { - return TrimEndingDirectorySeparator(path, pathSystem.DirectorySeparatorChar, - pathSystem.AltDirectorySeparatorChar); - } - - internal static string TrimEndingDirectorySeparator( - string path, char directorySeparatorChar, char altDirectorySeparatorChar) - { - if (string.IsNullOrEmpty(path)) - { - return path; - } - - string trimmed = path.TrimEnd(directorySeparatorChar, - altDirectorySeparatorChar); - - return Execute.OnWindows( - () => - { - if (trimmed.Length == 2 - && char.IsLetter(trimmed[0]) - && trimmed[1] == ':') - { - return trimmed + directorySeparatorChar; - } - - return null; - }, - () => - { - if ((path[0] == directorySeparatorChar || - path[0] == altDirectorySeparatorChar) - && trimmed == "") - { - return directorySeparatorChar.ToString(); - } - - return null; - }) - ?? trimmed; - } -} -#endif +#if !FEATURE_PATH_ADVANCED +using System.Diagnostics.CodeAnalysis; + +using Testably.Abstractions.Testing.Helpers; + +// ReSharper disable once CheckNamespace +namespace Testably.Abstractions.Testing; + +/// +/// Provides extension methods to simplify writing platform independent tests. +/// +[ExcludeFromCodeCoverage] +internal static class FileFeatureExtensionMethods +{ + /// + /// Trims one trailing directory separator beyond the root of the path. + /// + internal static string TrimEndingDirectorySeparator( + this IPath pathSystem, + string path) + { + return TrimEndingDirectorySeparator(path, pathSystem.DirectorySeparatorChar, + pathSystem.AltDirectorySeparatorChar); + } + + internal static string TrimEndingDirectorySeparator( + string path, char directorySeparatorChar, char altDirectorySeparatorChar) + { + if (string.IsNullOrEmpty(path)) + { + return path; + } + + string trimmed = path.TrimEnd(directorySeparatorChar, + altDirectorySeparatorChar); + + return Execute.OnWindows( + () => + { + if (trimmed.Length == 2 + && char.IsLetter(trimmed[0]) + && trimmed[1] == ':') + { + return trimmed + directorySeparatorChar; + } + + return null; + }, + () => + { + if ((path[0] == directorySeparatorChar || + path[0] == altDirectorySeparatorChar) + && trimmed == "") + { + return directorySeparatorChar.ToString(); + } + + return null; + }) + ?? trimmed; + } +} +#endif diff --git a/Source/Testably.Abstractions.Testing/Storage/IStorage.cs b/Source/Testably.Abstractions.Testing/Storage/IStorage.cs index c0bbe2723..700121623 100644 --- a/Source/Testably.Abstractions.Testing/Storage/IStorage.cs +++ b/Source/Testably.Abstractions.Testing/Storage/IStorage.cs @@ -1,203 +1,203 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Testably.Abstractions.Testing.Helpers; - -namespace Testably.Abstractions.Testing.Storage; - -/// -/// The container storing the current data of the in memory. -/// -internal interface IStorage -{ - /// - /// The current directory used in and - /// - /// - string CurrentDirectory { get; set; } - - /// - /// The main drive. - /// - IStorageDrive MainDrive { get; } - - /// - /// Copies a specified file to a new location.
- /// This method does work across volumes. - ///
- /// The source location. - /// The destination location. - /// - /// to overwrite the , - /// otherwise . - /// - /// - /// The new location of the file.
- /// Returns when the does not exist. - ///
- IStorageLocation? Copy(IStorageLocation source, - IStorageLocation destination, - bool overwrite = false); - - /// - /// Deletes the container stored at . - /// - /// The location at which the container is located. - /// (optional) Is set, also child-containers are deleted recursively. - /// , if the container was found and deleted, otherwise . - bool DeleteContainer(IStorageLocation location, bool recursive = false); - - /// - /// Enumerate the locations of files or directories stored under . - /// - /// The parent location in which the files or directories are searched for. - /// The type of the container (file, directory or both). - /// - /// (optional) The expression to filter the name of the locations. - /// - /// Defaults to "*" which matches any name. - /// - /// - /// (optional) The enumeration options. - /// - /// Defaults to which uses the `Compatible` enumeration options:
- /// - Search only in Top-Directory
- /// - Use Win32 expression MatchType - /// - /// - /// The location of files or directories that match the , - /// and . - /// - IEnumerable EnumerateLocations( - IStorageLocation location, - FileSystemTypes type, - string searchPattern = EnumerationOptionsHelper.DefaultSearchPattern, - EnumerationOptions? enumerationOptions = null); - - /// - /// Gets the container at . - /// - /// If the container is not found, returns a . - /// - /// The location at which to look for the container. - /// - /// , if is null. Otherwise it returns the found container or - /// . - /// - [return: NotNullIfNotNull("location")] - IStorageContainer? GetContainer(IStorageLocation? location); - - /// - /// Returns the drive if it is present.
- /// Returns , if the drive does not exist. - ///
- IStorageDrive? GetDrive(string? driveName); - - /// - /// Returns the drives that are present. - /// - IEnumerable GetDrives(); - - /// - /// Gets the location for the given . - /// - /// The path. - /// (optional) the friendly name for the . - /// The that corresponds to . - [return: NotNullIfNotNull("path")] - IStorageLocation? GetLocation(string? path, string? friendlyName = null); - - /// - /// Returns the drives that are present. - /// - IStorageDrive GetOrAddDrive(string driveName); - - /// - /// Returns an existing container at .
- /// If no container exists yet, creates a new container at this using the - /// and returns the new generated container. - ///
- /// The location at which to get or create the container. - /// The callback used to create a new container at . - /// - /// The container at . - IStorageContainer GetOrCreateContainer(IStorageLocation location, - Func containerGenerator, - IFileSystemExtensibility? fileSystemExtensibility = null); - - /// - /// Moves a specified file or directory to a new location and potentially a new file name.
- /// This method does work across volumes. - ///
- /// The source location. - /// The destination location. - /// - /// to overwrite the , - /// otherwise . - /// - /// - /// to recursively move child elements the , - /// otherwise . - /// - /// - /// The new location of the file or directory.
- /// Returns when the does not exist. - ///
- IStorageLocation? Move(IStorageLocation source, - IStorageLocation destination, - bool overwrite = false, - bool recursive = false); - - /// - /// Replaces the file with the and moving it to the - /// location.
- /// This method does work across volumes. - ///
- /// The source location. - /// The destination location. - /// The backup location. - /// - /// to ignore merge errors (such as attributes and access control lists (ACLs)) from the - /// replaced file; - /// otherwise . - /// - /// - /// The new location of the file.
- /// Returns when the or the does - /// not exist. - ///
- IStorageLocation? Replace(IStorageLocation source, - IStorageLocation destination, - IStorageLocation? backup, - bool ignoreMetadataErrors = false); - -#if FEATURE_FILESYSTEM_LINK - /// - /// Resolves the link target of the container stored at . - /// - /// The location to start looking up the link targets. - /// - /// (optional) to follow links to the final target; to return the - /// immediate next link. - /// - /// The location of the link target. - IStorageLocation? ResolveLinkTarget(IStorageLocation location, - bool returnFinalTarget = false); -#endif - - /// - /// Tries to add a new container at using the . - /// - /// The location at which to add a new container. - /// The callback used to create a new container at . - /// (out) The created container if successful, otherwise . - /// - /// if the container could be created at , - /// otherwise . - /// - bool TryAddContainer(IStorageLocation location, - Func - containerGenerator, - [NotNullWhen(true)] out IStorageContainer? container); -} +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.Storage; + +/// +/// The container storing the current data of the in memory. +/// +internal interface IStorage +{ + /// + /// The current directory used in and + /// + /// + string CurrentDirectory { get; set; } + + /// + /// The main drive. + /// + IStorageDrive MainDrive { get; } + + /// + /// Copies a specified file to a new location.
+ /// This method does work across volumes. + ///
+ /// The source location. + /// The destination location. + /// + /// to overwrite the , + /// otherwise . + /// + /// + /// The new location of the file.
+ /// Returns when the does not exist. + ///
+ IStorageLocation? Copy(IStorageLocation source, + IStorageLocation destination, + bool overwrite = false); + + /// + /// Deletes the container stored at . + /// + /// The location at which the container is located. + /// (optional) Is set, also child-containers are deleted recursively. + /// , if the container was found and deleted, otherwise . + bool DeleteContainer(IStorageLocation location, bool recursive = false); + + /// + /// Enumerate the locations of files or directories stored under . + /// + /// The parent location in which the files or directories are searched for. + /// The type of the container (file, directory or both). + /// + /// (optional) The expression to filter the name of the locations. + /// + /// Defaults to "*" which matches any name. + /// + /// + /// (optional) The enumeration options. + /// + /// Defaults to which uses the `Compatible` enumeration options:
+ /// - Search only in Top-Directory
+ /// - Use Win32 expression MatchType + /// + /// + /// The location of files or directories that match the , + /// and . + /// + IEnumerable EnumerateLocations( + IStorageLocation location, + FileSystemTypes type, + string searchPattern = EnumerationOptionsHelper.DefaultSearchPattern, + EnumerationOptions? enumerationOptions = null); + + /// + /// Gets the container at . + /// + /// If the container is not found, returns a . + /// + /// The location at which to look for the container. + /// + /// , if is null. Otherwise it returns the found container or + /// . + /// + [return: NotNullIfNotNull("location")] + IStorageContainer? GetContainer(IStorageLocation? location); + + /// + /// Returns the drive if it is present.
+ /// Returns , if the drive does not exist. + ///
+ IStorageDrive? GetDrive(string? driveName); + + /// + /// Returns the drives that are present. + /// + IEnumerable GetDrives(); + + /// + /// Gets the location for the given . + /// + /// The path. + /// (optional) the friendly name for the . + /// The that corresponds to . + [return: NotNullIfNotNull("path")] + IStorageLocation? GetLocation(string? path, string? friendlyName = null); + + /// + /// Returns the drives that are present. + /// + IStorageDrive GetOrAddDrive(string driveName); + + /// + /// Returns an existing container at .
+ /// If no container exists yet, creates a new container at this using the + /// and returns the new generated container. + ///
+ /// The location at which to get or create the container. + /// The callback used to create a new container at . + /// + /// The container at . + IStorageContainer GetOrCreateContainer(IStorageLocation location, + Func containerGenerator, + IFileSystemExtensibility? fileSystemExtensibility = null); + + /// + /// Moves a specified file or directory to a new location and potentially a new file name.
+ /// This method does work across volumes. + ///
+ /// The source location. + /// The destination location. + /// + /// to overwrite the , + /// otherwise . + /// + /// + /// to recursively move child elements the , + /// otherwise . + /// + /// + /// The new location of the file or directory.
+ /// Returns when the does not exist. + ///
+ IStorageLocation? Move(IStorageLocation source, + IStorageLocation destination, + bool overwrite = false, + bool recursive = false); + + /// + /// Replaces the file with the and moving it to the + /// location.
+ /// This method does work across volumes. + ///
+ /// The source location. + /// The destination location. + /// The backup location. + /// + /// to ignore merge errors (such as attributes and access control lists (ACLs)) from the + /// replaced file; + /// otherwise . + /// + /// + /// The new location of the file.
+ /// Returns when the or the does + /// not exist. + ///
+ IStorageLocation? Replace(IStorageLocation source, + IStorageLocation destination, + IStorageLocation? backup, + bool ignoreMetadataErrors = false); + +#if FEATURE_FILESYSTEM_LINK + /// + /// Resolves the link target of the container stored at . + /// + /// The location to start looking up the link targets. + /// + /// (optional) to follow links to the final target; to return the + /// immediate next link. + /// + /// The location of the link target. + IStorageLocation? ResolveLinkTarget(IStorageLocation location, + bool returnFinalTarget = false); +#endif + + /// + /// Tries to add a new container at using the . + /// + /// The location at which to add a new container. + /// The callback used to create a new container at . + /// (out) The created container if successful, otherwise . + /// + /// if the container could be created at , + /// otherwise . + /// + bool TryAddContainer(IStorageLocation location, + Func + containerGenerator, + [NotNullWhen(true)] out IStorageContainer? container); +} diff --git a/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs b/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs index a5c48650d..629109316 100644 --- a/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs +++ b/Source/Testably.Abstractions.Testing/Storage/IStorageContainer.cs @@ -1,109 +1,109 @@ -using System; -using System.IO; -using Testably.Abstractions.TimeSystem; - -namespace Testably.Abstractions.Testing.Storage; - -/// -/// A container for a stored file or directory in the . -/// -internal interface IStorageContainer : IFileSystemEntity, - ITimeSystemEntity -{ - /// - FileAttributes Attributes { get; set; } - - /// - ITimeContainer CreationTime { get; } - - /// - /// A container to support extensions on . - /// - IFileSystemExtensibility Extensibility { get; } - - /// - ITimeContainer LastAccessTime { get; } - - /// - ITimeContainer LastWriteTime { get; } - - /// - /// If this instance represents a link, returns the link target's path, otherwise returns . - /// - string? LinkTarget { get; set; } - - /// - /// The type of the container indicates if it is a or - /// . - /// - FileSystemTypes Type { get; } - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - /// Gets or sets the Unix file mode for the current file or directory.
- /// See also: - ///
- UnixFileMode UnixFileMode { get; set; } -#endif - - /// - /// Appends the to the . - /// - void AppendBytes(byte[] bytes); - - /// - /// Clears the content of the . - /// - /// This is used to delete the file. - /// - void ClearBytes(); - - /// - /// Decrypts the file content and removes the attribute. - /// - /// Does nothing if the file is not encrypted. - /// - void Decrypt(); - - /// - /// Encrypts the file content and adds the attribute. - /// - /// Does nothing if the file is already encrypted. - /// - void Encrypt(); - - /// - /// Gets the bytes in the . - /// - byte[] GetBytes(); - - /// - /// Requests access to this file with the given . - /// - /// An that is used to release the access lock on dispose. - IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, - bool deleteAccess = false, - bool ignoreMetadataErrors = true, - int? hResult = null); - - /// - /// Writes the to the . - /// - void WriteBytes(byte[] bytes); - - /// - /// A container to allow reading/writing s with consistent . - /// - public interface ITimeContainer - { - /// - /// Get the in the given . - /// - DateTime Get(DateTimeKind kind); - - /// - /// Set the to the in the given . - /// - void Set(DateTime time, DateTimeKind kind); - } -} +using System; +using System.IO; +using Testably.Abstractions.TimeSystem; + +namespace Testably.Abstractions.Testing.Storage; + +/// +/// A container for a stored file or directory in the . +/// +internal interface IStorageContainer : IFileSystemEntity, + ITimeSystemEntity +{ + /// + FileAttributes Attributes { get; set; } + + /// + ITimeContainer CreationTime { get; } + + /// + /// A container to support extensions on . + /// + IFileSystemExtensibility Extensibility { get; } + + /// + ITimeContainer LastAccessTime { get; } + + /// + ITimeContainer LastWriteTime { get; } + + /// + /// If this instance represents a link, returns the link target's path, otherwise returns . + /// + string? LinkTarget { get; set; } + + /// + /// The type of the container indicates if it is a or + /// . + /// + FileSystemTypes Type { get; } + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + /// Gets or sets the Unix file mode for the current file or directory.
+ /// See also: + ///
+ UnixFileMode UnixFileMode { get; set; } +#endif + + /// + /// Appends the to the . + /// + void AppendBytes(byte[] bytes); + + /// + /// Clears the content of the . + /// + /// This is used to delete the file. + /// + void ClearBytes(); + + /// + /// Decrypts the file content and removes the attribute. + /// + /// Does nothing if the file is not encrypted. + /// + void Decrypt(); + + /// + /// Encrypts the file content and adds the attribute. + /// + /// Does nothing if the file is already encrypted. + /// + void Encrypt(); + + /// + /// Gets the bytes in the . + /// + byte[] GetBytes(); + + /// + /// Requests access to this file with the given . + /// + /// An that is used to release the access lock on dispose. + IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, + bool deleteAccess = false, + bool ignoreMetadataErrors = true, + int? hResult = null); + + /// + /// Writes the to the . + /// + void WriteBytes(byte[] bytes); + + /// + /// A container to allow reading/writing s with consistent . + /// + public interface ITimeContainer + { + /// + /// Get the in the given . + /// + DateTime Get(DateTimeKind kind); + + /// + /// Set the to the in the given . + /// + void Set(DateTime time, DateTimeKind kind); + } +} diff --git a/Source/Testably.Abstractions.Testing/Storage/IStorageDrive.cs b/Source/Testably.Abstractions.Testing/Storage/IStorageDrive.cs index 917ccab4a..ed35c5d6b 100644 --- a/Source/Testably.Abstractions.Testing/Storage/IStorageDrive.cs +++ b/Source/Testably.Abstractions.Testing/Storage/IStorageDrive.cs @@ -1,47 +1,47 @@ -using System.IO; -using Testably.Abstractions.Testing.FileSystem; - -namespace Testably.Abstractions.Testing.Storage; - -/// -/// A which allows to be manipulated. -/// -public interface IStorageDrive : IDriveInfo -{ - /// - /// Flag indicating if the drive is a UNC drive - /// - bool IsUncPath { get; } - - /// - /// Changes the currently used bytes by . - /// - /// Throws an if the becomes - /// negative. - /// - IStorageDrive ChangeUsedBytes(long usedBytesDelta); - - /// - /// Changes the of the mocked . - /// - IStorageDrive SetDriveFormat( - string driveFormat = DriveInfoMock.DefaultDriveFormat); - - /// - /// Changes the of the mocked . - /// - IStorageDrive SetDriveType( - DriveType driveType = DriveInfoMock.DefaultDriveType); - - /// - /// Changes the property of the mocked - /// . - /// - IStorageDrive SetIsReady(bool isReady = true); - - /// - /// Changes the total size of the mocked . - /// - IStorageDrive SetTotalSize( - long totalSize = DriveInfoMock.DefaultTotalSize); -} +using System.IO; +using Testably.Abstractions.Testing.FileSystem; + +namespace Testably.Abstractions.Testing.Storage; + +/// +/// A which allows to be manipulated. +/// +public interface IStorageDrive : IDriveInfo +{ + /// + /// Flag indicating if the drive is a UNC drive + /// + bool IsUncPath { get; } + + /// + /// Changes the currently used bytes by . + /// + /// Throws an if the becomes + /// negative. + /// + IStorageDrive ChangeUsedBytes(long usedBytesDelta); + + /// + /// Changes the of the mocked . + /// + IStorageDrive SetDriveFormat( + string driveFormat = DriveInfoMock.DefaultDriveFormat); + + /// + /// Changes the of the mocked . + /// + IStorageDrive SetDriveType( + DriveType driveType = DriveInfoMock.DefaultDriveType); + + /// + /// Changes the property of the mocked + /// . + /// + IStorageDrive SetIsReady(bool isReady = true); + + /// + /// Changes the total size of the mocked . + /// + IStorageDrive SetTotalSize( + long totalSize = DriveInfoMock.DefaultTotalSize); +} diff --git a/Source/Testably.Abstractions.Testing/Storage/IStorageLocation.cs b/Source/Testably.Abstractions.Testing/Storage/IStorageLocation.cs index 41df415b0..0cee3efd1 100644 --- a/Source/Testably.Abstractions.Testing/Storage/IStorageLocation.cs +++ b/Source/Testably.Abstractions.Testing/Storage/IStorageLocation.cs @@ -1,38 +1,38 @@ -using System; - -namespace Testably.Abstractions.Testing.Storage; - -/// -/// The location where are located in the . -/// -internal interface IStorageLocation : IEquatable -{ - /// - /// The in which the is stored. - /// - IStorageDrive? Drive { get; } - - /// - /// The friendly name from the location of the file or directory. - /// - string FriendlyName { get; } - - /// - /// The full path of the location of the file or directory. - /// - string FullPath { get; } - - /// - /// Flag indicating if the location corresponds to a root directory. - /// - bool IsRooted { get; } - - /// - /// Get the parent location. - /// - /// - /// The parent or when the location corresponds to a root - /// directory. - /// - IStorageLocation? GetParent(); -} +using System; + +namespace Testably.Abstractions.Testing.Storage; + +/// +/// The location where are located in the . +/// +internal interface IStorageLocation : IEquatable +{ + /// + /// The in which the is stored. + /// + IStorageDrive? Drive { get; } + + /// + /// The friendly name from the location of the file or directory. + /// + string FriendlyName { get; } + + /// + /// The full path of the location of the file or directory. + /// + string FullPath { get; } + + /// + /// Flag indicating if the location corresponds to a root directory. + /// + bool IsRooted { get; } + + /// + /// Get the parent location. + /// + /// + /// The parent or when the location corresponds to a root + /// directory. + /// + IStorageLocation? GetParent(); +} diff --git a/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs b/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs index 49ac53c5e..ac95ee3fe 100644 --- a/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs +++ b/Source/Testably.Abstractions.Testing/Storage/InMemoryContainer.cs @@ -1,373 +1,373 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.Testing.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.TimeSystem; -using static Testably.Abstractions.Testing.Storage.IStorageContainer; - -namespace Testably.Abstractions.Testing.Storage; - -internal class InMemoryContainer : IStorageContainer -{ - private FileAttributes _attributes; - private byte[] _bytes = Array.Empty(); - private readonly ConcurrentDictionary _fileHandles = new(); - private readonly MockFileSystem _fileSystem; - private bool _isEncrypted; - private readonly IStorageLocation _location; - private readonly FileSystemExtensibility _extensibility = new(); - - public InMemoryContainer(FileSystemTypes type, - IStorageLocation location, - MockFileSystem fileSystem) - { - _location = location; - _fileSystem = fileSystem; - Type = type; - this.AdjustTimes(TimeAdjustments.All); - } - - #region IStorageContainer Members - - /// - public FileAttributes Attributes - { - get => AdjustAttributes(_attributes); - set - { - value &= Execute.OnWindows( - () => FileAttributes.Directory | - FileAttributes.ReadOnly | - FileAttributes.Archive | - FileAttributes.Hidden | - FileAttributes.NoScrubData | - FileAttributes.NotContentIndexed | - FileAttributes.Offline | - FileAttributes.System | - FileAttributes.Temporary, - () => Execute.OnLinux( - () => FileAttributes.Directory | - FileAttributes.ReadOnly, - () => FileAttributes.Hidden | - FileAttributes.Directory | - FileAttributes.ReadOnly)); - - _attributes = value; - } - } - - /// - public ITimeContainer CreationTime { get; } = new TimeContainer(); - - /// - public IFileSystemExtensibility Extensibility - => _extensibility; - - /// - public IFileSystem FileSystem => _fileSystem; - - /// - public ITimeContainer LastAccessTime { get; } = new TimeContainer(); - - /// - public ITimeContainer LastWriteTime { get; } = new TimeContainer(); - - /// - public string? LinkTarget { get; set; } - - /// - public ITimeSystem TimeSystem => _fileSystem.TimeSystem; - - /// - public FileSystemTypes Type { get; } - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - public UnixFileMode UnixFileMode { get; set; } = (UnixFileMode)(-1); -#endif - - /// - public void AppendBytes(byte[] bytes) - { - WriteBytes(_bytes.Concat(bytes).ToArray()); - } - - /// - public void ClearBytes() - { - _location.Drive?.ChangeUsedBytes(0 - _bytes.Length); - _bytes = Array.Empty(); - } - - /// - public void Decrypt() - { - if (!_isEncrypted) - { - return; - } - - using (RequestAccess(FileAccess.Write, FileShare.Read)) - { - _isEncrypted = false; - WriteBytes(EncryptionHelper.Decrypt(GetBytes())); - } - } - - /// - public void Encrypt() - { - if (_isEncrypted) - { - return; - } - - using (RequestAccess(FileAccess.Write, FileShare.Read)) - { - _isEncrypted = true; - WriteBytes(EncryptionHelper.Encrypt(GetBytes())); - } - } - - /// - public byte[] GetBytes() => _bytes; - - /// - public IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, - bool deleteAccess = false, - bool ignoreMetadataErrors = true, - int? hResult = null) - { - if (_location.Drive == null) - { - throw ExceptionFactory.DirectoryNotFound(_location.FullPath); - } - - if (!_location.Drive.IsReady) - { - throw ExceptionFactory.NetworkPathNotFound(_location.FullPath); - } - - Execute.OnWindowsIf( - !ignoreMetadataErrors && Attributes.HasFlag(FileAttributes.ReadOnly), - () => throw ExceptionFactory.AccessToPathDenied()); - - if (!_fileSystem.AccessControlStrategy - .IsAccessGranted(_location.FullPath, Extensibility)) - { - throw ExceptionFactory.AclAccessToPathDenied(_location.FullPath); - } - - if (CanGetAccess(access, share, deleteAccess)) - { - Guid guid = Guid.NewGuid(); - FileHandle fileHandle = new(guid, ReleaseAccess, access, share, deleteAccess); - _fileHandles.TryAdd(guid, fileHandle); - return fileHandle; - } - - throw ExceptionFactory.ProcessCannotAccessTheFile(_location.FullPath, - hResult ?? -2147024864); - } - - /// - public void WriteBytes(byte[] bytes) - { - NotifyFilters notifyFilters = NotifyFilters.LastAccess | - NotifyFilters.LastWrite | - NotifyFilters.Size; - Execute.OnLinux(() - => notifyFilters |= NotifyFilters.Security); - Execute.OnMac(() - => notifyFilters |= NotifyFilters.CreationTime); - - TimeAdjustments timeAdjustment = TimeAdjustments.LastWriteTime; - Execute.OnWindows(() - => timeAdjustment |= TimeAdjustments.LastAccessTime); - - ChangeDescription fileSystemChange = - _fileSystem.ChangeHandler.NotifyPendingChange(WatcherChangeTypes.Changed, - FileSystemTypes.File, - notifyFilters, - _location); - _location.Drive?.ChangeUsedBytes(bytes.Length - _bytes.Length); - _bytes = bytes; - this.AdjustTimes(timeAdjustment); - _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); - } - - #endregion - - /// - /// Create a new directory on the . - /// - public static IStorageContainer NewDirectory(IStorageLocation location, - MockFileSystem fileSystem) - { - return new InMemoryContainer(FileSystemTypes.Directory, location, - fileSystem); - } - - /// - /// Create a new file on the . - /// - public static IStorageContainer NewFile(IStorageLocation location, - MockFileSystem fileSystem) - { - return new InMemoryContainer(FileSystemTypes.File, location, - fileSystem); - } - - internal FileAttributes AdjustAttributes(FileAttributes attributes) - { - if (Path.GetFileName(_location.FullPath).StartsWith(".")) - { - FileAttributes attr = attributes; - attributes = Execute.OnLinux( - () => attr | FileAttributes.Hidden, - () => attr); - } - -#if FEATURE_FILESYSTEM_LINK - if (LinkTarget != null) - { - attributes |= FileAttributes.ReparsePoint; - } -#endif - - if (_isEncrypted) - { - attributes |= FileAttributes.Encrypted; - } - - if (attributes == 0) - { - return FileAttributes.Normal; - } - - return attributes; - } - - private bool CanGetAccess(FileAccess access, FileShare share, bool deleteAccess) - { - foreach (KeyValuePair fileHandle in _fileHandles) - { - if (!fileHandle.Value.GrantAccess(access, share, deleteAccess)) - { - return false; - } - } - - return true; - } - - private void ReleaseAccess(Guid guid) - { - _fileHandles.TryRemove(guid, out _); - } - - internal sealed class TimeContainer : ITimeContainer - { - private DateTime _time; - - #region ITimeContainer Members - - /// - public DateTime Get(DateTimeKind kind) - => kind switch - { - DateTimeKind.Utc => _time.ToUniversalTime(), - DateTimeKind.Local => _time.ToLocalTime(), - _ => _time - }; - - /// - public void Set(DateTime time, DateTimeKind kind) - { - if (time.Kind == DateTimeKind.Unspecified) - { - _time = DateTime.SpecifyKind(time, kind); - } - else - { - _time = time; - } - } - - #endregion - } - - private sealed class FileHandle : IStorageAccessHandle - { - private readonly Guid _key; - private readonly Action _releaseCallback; - - public FileHandle(Guid key, Action releaseCallback, FileAccess access, - FileShare share, bool deleteAccess) - { - _releaseCallback = releaseCallback; - Access = access; - DeleteAccess = deleteAccess; - Share = Execute.OnWindows( - () => share, - () => share == FileShare.None - ? FileShare.None - : FileShare.ReadWrite); - - _key = key; - } - - #region IStorageAccessHandle Members - - /// - public FileAccess Access { get; } - - /// - public bool DeleteAccess { get; } - - /// - public FileShare Share { get; } - - /// - public void Dispose() - { - _releaseCallback.Invoke(_key); - } - - #endregion - - public bool GrantAccess(FileAccess access, FileShare share, bool deleteAccess) - { - FileShare usedShare = share; - Execute.NotOnWindows(() - => usedShare = FileShare.ReadWrite); - if (deleteAccess) - { - return !Execute.IsWindows || Share == FileShare.Delete; - } - - return CheckAccessWithShare(access, Share) && - CheckAccessWithShare(Access, usedShare); - } - - /// - public override string ToString() - => $"{Access} | {Share}"; - - private static bool CheckAccessWithShare(FileAccess access, FileShare share) - { - switch (access) - { - case FileAccess.Read: - return share.HasFlag(FileShare.Read); - case FileAccess.Write: - return share.HasFlag(FileShare.Write); - default: - return share == FileShare.ReadWrite; - } - } - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystem; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.TimeSystem; +using static Testably.Abstractions.Testing.Storage.IStorageContainer; + +namespace Testably.Abstractions.Testing.Storage; + +internal class InMemoryContainer : IStorageContainer +{ + private FileAttributes _attributes; + private byte[] _bytes = Array.Empty(); + private readonly ConcurrentDictionary _fileHandles = new(); + private readonly MockFileSystem _fileSystem; + private bool _isEncrypted; + private readonly IStorageLocation _location; + private readonly FileSystemExtensibility _extensibility = new(); + + public InMemoryContainer(FileSystemTypes type, + IStorageLocation location, + MockFileSystem fileSystem) + { + _location = location; + _fileSystem = fileSystem; + Type = type; + this.AdjustTimes(TimeAdjustments.All); + } + + #region IStorageContainer Members + + /// + public FileAttributes Attributes + { + get => AdjustAttributes(_attributes); + set + { + value &= Execute.OnWindows( + () => FileAttributes.Directory | + FileAttributes.ReadOnly | + FileAttributes.Archive | + FileAttributes.Hidden | + FileAttributes.NoScrubData | + FileAttributes.NotContentIndexed | + FileAttributes.Offline | + FileAttributes.System | + FileAttributes.Temporary, + () => Execute.OnLinux( + () => FileAttributes.Directory | + FileAttributes.ReadOnly, + () => FileAttributes.Hidden | + FileAttributes.Directory | + FileAttributes.ReadOnly)); + + _attributes = value; + } + } + + /// + public ITimeContainer CreationTime { get; } = new TimeContainer(); + + /// + public IFileSystemExtensibility Extensibility + => _extensibility; + + /// + public IFileSystem FileSystem => _fileSystem; + + /// + public ITimeContainer LastAccessTime { get; } = new TimeContainer(); + + /// + public ITimeContainer LastWriteTime { get; } = new TimeContainer(); + + /// + public string? LinkTarget { get; set; } + + /// + public ITimeSystem TimeSystem => _fileSystem.TimeSystem; + + /// + public FileSystemTypes Type { get; } + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + public UnixFileMode UnixFileMode { get; set; } = (UnixFileMode)(-1); +#endif + + /// + public void AppendBytes(byte[] bytes) + { + WriteBytes(_bytes.Concat(bytes).ToArray()); + } + + /// + public void ClearBytes() + { + _location.Drive?.ChangeUsedBytes(0 - _bytes.Length); + _bytes = Array.Empty(); + } + + /// + public void Decrypt() + { + if (!_isEncrypted) + { + return; + } + + using (RequestAccess(FileAccess.Write, FileShare.Read)) + { + _isEncrypted = false; + WriteBytes(EncryptionHelper.Decrypt(GetBytes())); + } + } + + /// + public void Encrypt() + { + if (_isEncrypted) + { + return; + } + + using (RequestAccess(FileAccess.Write, FileShare.Read)) + { + _isEncrypted = true; + WriteBytes(EncryptionHelper.Encrypt(GetBytes())); + } + } + + /// + public byte[] GetBytes() => _bytes; + + /// + public IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, + bool deleteAccess = false, + bool ignoreMetadataErrors = true, + int? hResult = null) + { + if (_location.Drive == null) + { + throw ExceptionFactory.DirectoryNotFound(_location.FullPath); + } + + if (!_location.Drive.IsReady) + { + throw ExceptionFactory.NetworkPathNotFound(_location.FullPath); + } + + Execute.OnWindowsIf( + !ignoreMetadataErrors && Attributes.HasFlag(FileAttributes.ReadOnly), + () => throw ExceptionFactory.AccessToPathDenied()); + + if (!_fileSystem.AccessControlStrategy + .IsAccessGranted(_location.FullPath, Extensibility)) + { + throw ExceptionFactory.AclAccessToPathDenied(_location.FullPath); + } + + if (CanGetAccess(access, share, deleteAccess)) + { + Guid guid = Guid.NewGuid(); + FileHandle fileHandle = new(guid, ReleaseAccess, access, share, deleteAccess); + _fileHandles.TryAdd(guid, fileHandle); + return fileHandle; + } + + throw ExceptionFactory.ProcessCannotAccessTheFile(_location.FullPath, + hResult ?? -2147024864); + } + + /// + public void WriteBytes(byte[] bytes) + { + NotifyFilters notifyFilters = NotifyFilters.LastAccess | + NotifyFilters.LastWrite | + NotifyFilters.Size; + Execute.OnLinux(() + => notifyFilters |= NotifyFilters.Security); + Execute.OnMac(() + => notifyFilters |= NotifyFilters.CreationTime); + + TimeAdjustments timeAdjustment = TimeAdjustments.LastWriteTime; + Execute.OnWindows(() + => timeAdjustment |= TimeAdjustments.LastAccessTime); + + ChangeDescription fileSystemChange = + _fileSystem.ChangeHandler.NotifyPendingChange(WatcherChangeTypes.Changed, + FileSystemTypes.File, + notifyFilters, + _location); + _location.Drive?.ChangeUsedBytes(bytes.Length - _bytes.Length); + _bytes = bytes; + this.AdjustTimes(timeAdjustment); + _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); + } + + #endregion + + /// + /// Create a new directory on the . + /// + public static IStorageContainer NewDirectory(IStorageLocation location, + MockFileSystem fileSystem) + { + return new InMemoryContainer(FileSystemTypes.Directory, location, + fileSystem); + } + + /// + /// Create a new file on the . + /// + public static IStorageContainer NewFile(IStorageLocation location, + MockFileSystem fileSystem) + { + return new InMemoryContainer(FileSystemTypes.File, location, + fileSystem); + } + + internal FileAttributes AdjustAttributes(FileAttributes attributes) + { + if (Path.GetFileName(_location.FullPath).StartsWith(".")) + { + FileAttributes attr = attributes; + attributes = Execute.OnLinux( + () => attr | FileAttributes.Hidden, + () => attr); + } + +#if FEATURE_FILESYSTEM_LINK + if (LinkTarget != null) + { + attributes |= FileAttributes.ReparsePoint; + } +#endif + + if (_isEncrypted) + { + attributes |= FileAttributes.Encrypted; + } + + if (attributes == 0) + { + return FileAttributes.Normal; + } + + return attributes; + } + + private bool CanGetAccess(FileAccess access, FileShare share, bool deleteAccess) + { + foreach (KeyValuePair fileHandle in _fileHandles) + { + if (!fileHandle.Value.GrantAccess(access, share, deleteAccess)) + { + return false; + } + } + + return true; + } + + private void ReleaseAccess(Guid guid) + { + _fileHandles.TryRemove(guid, out _); + } + + internal sealed class TimeContainer : ITimeContainer + { + private DateTime _time; + + #region ITimeContainer Members + + /// + public DateTime Get(DateTimeKind kind) + => kind switch + { + DateTimeKind.Utc => _time.ToUniversalTime(), + DateTimeKind.Local => _time.ToLocalTime(), + _ => _time + }; + + /// + public void Set(DateTime time, DateTimeKind kind) + { + if (time.Kind == DateTimeKind.Unspecified) + { + _time = DateTime.SpecifyKind(time, kind); + } + else + { + _time = time; + } + } + + #endregion + } + + private sealed class FileHandle : IStorageAccessHandle + { + private readonly Guid _key; + private readonly Action _releaseCallback; + + public FileHandle(Guid key, Action releaseCallback, FileAccess access, + FileShare share, bool deleteAccess) + { + _releaseCallback = releaseCallback; + Access = access; + DeleteAccess = deleteAccess; + Share = Execute.OnWindows( + () => share, + () => share == FileShare.None + ? FileShare.None + : FileShare.ReadWrite); + + _key = key; + } + + #region IStorageAccessHandle Members + + /// + public FileAccess Access { get; } + + /// + public bool DeleteAccess { get; } + + /// + public FileShare Share { get; } + + /// + public void Dispose() + { + _releaseCallback.Invoke(_key); + } + + #endregion + + public bool GrantAccess(FileAccess access, FileShare share, bool deleteAccess) + { + FileShare usedShare = share; + Execute.NotOnWindows(() + => usedShare = FileShare.ReadWrite); + if (deleteAccess) + { + return !Execute.IsWindows || Share == FileShare.Delete; + } + + return CheckAccessWithShare(access, Share) && + CheckAccessWithShare(Access, usedShare); + } + + /// + public override string ToString() + => $"{Access} | {Share}"; + + private static bool CheckAccessWithShare(FileAccess access, FileShare share) + { + switch (access) + { + case FileAccess.Read: + return share.HasFlag(FileShare.Read); + case FileAccess.Write: + return share.HasFlag(FileShare.Write); + default: + return share == FileShare.ReadWrite; + } + } + } +} diff --git a/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs b/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs index 3b6bab25b..01c2e5821 100644 --- a/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs +++ b/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs @@ -1,717 +1,717 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using Testably.Abstractions.Testing.FileSystem; -using Testably.Abstractions.Testing.Helpers; - -namespace Testably.Abstractions.Testing.Storage; - -/// -/// The container storing the current data of the in memory. -/// -internal sealed class InMemoryStorage : IStorage -{ - private readonly ConcurrentDictionary - _containers = new(); - - private readonly ConcurrentDictionary _drives = - new(StringComparer.OrdinalIgnoreCase); - - private readonly MockFileSystem _fileSystem; - - public InMemoryStorage(MockFileSystem fileSystem) - { - _fileSystem = fileSystem; - MainDrive = DriveInfoMock.New(CurrentDirectory, _fileSystem); - _drives.TryAdd(MainDrive.Name, MainDrive); - } - - #region IStorage Members - - /// - public string CurrentDirectory { get; set; } = string.Empty.PrefixRoot(); - - /// - public IStorageDrive MainDrive { get; } - - /// - public IStorageLocation? Copy(IStorageLocation source, - IStorageLocation destination, - bool overwrite = false) - { - ThrowIfParentDoesNotExist(destination, _ => ExceptionFactory.DirectoryNotFound()); - - if (!_containers.TryGetValue(source, - out IStorageContainer? sourceContainer)) - { - return null; - } - - if (sourceContainer.Type != FileSystemTypes.File) - { - throw ExceptionFactory.AccessToPathDenied(source.FullPath); - } - - using (_ = sourceContainer.RequestAccess(FileAccess.ReadWrite, FileShare.None)) - { - if (overwrite && - _containers.TryRemove(destination, - out IStorageContainer? existingContainer)) - { - existingContainer.ClearBytes(); - } - - IStorageContainer copiedContainer = - InMemoryContainer.NewFile(destination, _fileSystem); - if (_containers.TryAdd(destination, copiedContainer)) - { - copiedContainer.WriteBytes(sourceContainer.GetBytes().ToArray()); - Execute.NotOnWindows(() - => sourceContainer.AdjustTimes(TimeAdjustments.LastAccessTime)); - - copiedContainer.Attributes = sourceContainer.Attributes; - Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, - () => copiedContainer.Attributes |= FileAttributes.Archive); - Execute.NotOnWindows( - () => copiedContainer.CreationTime.Set( - sourceContainer.CreationTime.Get(DateTimeKind.Local), - DateTimeKind.Local)); - copiedContainer.LastWriteTime.Set( - sourceContainer.LastWriteTime.Get(DateTimeKind.Local), - DateTimeKind.Local); - return destination; - } - - throw ExceptionFactory.CannotCreateFileWhenAlreadyExists(Execute.IsWindows - ? -2147024816 - : 17); - } - } - - /// - public bool DeleteContainer(IStorageLocation location, bool recursive = false) - { - if (!_containers.TryGetValue(location, out IStorageContainer? container)) - { - IStorageLocation? parentLocation = location.GetParent(); - if (parentLocation != null && - !_containers.TryGetValue(parentLocation, out _)) - { - throw ExceptionFactory.DirectoryNotFound(parentLocation.FullPath); - } - - return false; - } - - if (container.Type == FileSystemTypes.Directory) - { - IEnumerable children = - EnumerateLocations(location, FileSystemTypes.DirectoryOrFile); - if (recursive) - { - foreach (IStorageLocation key in children) - { - DeleteContainer(key); - } - } - else if (children.Any()) - { - throw ExceptionFactory.DirectoryNotEmpty(location.FullPath); - } - } - - NotifyFilters notifyFilters = - container.Type == FileSystemTypes.Directory - ? NotifyFilters.DirectoryName - : NotifyFilters.FileName; - ChangeDescription fileSystemChange = - _fileSystem.ChangeHandler.NotifyPendingChange(WatcherChangeTypes.Deleted, - container.Type, - notifyFilters, location); - - using (container.RequestAccess(FileAccess.Write, FileShare.ReadWrite, - deleteAccess: true)) - { - if (_containers.TryRemove(location, out IStorageContainer? removed)) - { - removed.ClearBytes(); - _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); - CheckAndAdjustParentDirectoryTimes(location); - return true; - } - } - - return false; - } - - /// - public IEnumerable EnumerateLocations( - IStorageLocation location, - FileSystemTypes type, - string searchPattern = EnumerationOptionsHelper.DefaultSearchPattern, - EnumerationOptions? enumerationOptions = null) - { - ValidateExpression(searchPattern); - if (!_containers.ContainsKey(location)) - { - throw ExceptionFactory.DirectoryNotFound(location.FullPath); - } - - enumerationOptions ??= EnumerationOptionsHelper.Compatible; - - string fullPath = location.FullPath; -#if NETSTANDARD2_0 - if (!fullPath.EndsWith($"{_fileSystem.Path.DirectorySeparatorChar}")) -#else - if (!fullPath.EndsWith(_fileSystem.Path.DirectorySeparatorChar)) -#endif - { - fullPath += _fileSystem.Path.DirectorySeparatorChar; - } - - foreach (KeyValuePair item in _containers - .Where(x => x.Key.FullPath.StartsWith(fullPath, - InMemoryLocation.StringComparisonMode) && - !x.Key.Equals(location))) - { - string? parentPath = - _fileSystem.Path.GetDirectoryName( - item.Key.FullPath.TrimEnd(_fileSystem.Path - .DirectorySeparatorChar)); - if (!enumerationOptions.RecurseSubdirectories && - parentPath?.Equals(location.FullPath, - InMemoryLocation.StringComparisonMode) != true) - { - continue; - } - - if (!EnumerationOptionsHelper.MatchesPattern(enumerationOptions, - _fileSystem.Path.GetFileName(item.Key.FullPath), searchPattern)) - { - continue; - } - - if (type.HasFlag(item.Value.Type)) - { - yield return item.Key; - } - } - } - - /// - [return: NotNullIfNotNull("location")] - public IStorageContainer? GetContainer(IStorageLocation? location) - { - if (location == null) - { - return null; - } - - if (_containers.TryGetValue(location, out IStorageContainer? container)) - { - return container; - } - - return NullContainer.New(_fileSystem); - } - - /// - public IStorageDrive? GetDrive(string? driveName) - { - if (string.IsNullOrWhiteSpace(driveName)) - { - return null; - } - - if (!driveName.IsUncPath()) - { - driveName = _fileSystem.Path.GetPathRoot(driveName); - - if (string.IsNullOrEmpty(driveName)) - { - return null; - } - } - - DriveInfoMock drive = DriveInfoMock.New(driveName, _fileSystem); - if (_drives.TryGetValue(drive.Name, out IStorageDrive? d)) - { - return d; - } - - return null; - } - - /// - public IEnumerable GetDrives() - => _drives.Values; - - /// - [return: NotNullIfNotNull("path")] - public IStorageLocation? GetLocation(string? path, string? friendlyName = null) - { - if (path == null) - { - return null; - } - - IStorageDrive? drive = _fileSystem.Storage.GetDrive(path); - if (drive == null && - !_fileSystem.Path.IsPathRooted(path)) - { - drive = _fileSystem.Storage.MainDrive; - } - - return InMemoryLocation.New(drive, _fileSystem.Path.GetFullPath(path), path); - } - - /// - public IStorageDrive GetOrAddDrive(string driveName) - { - DriveInfoMock drive = DriveInfoMock.New(driveName, _fileSystem); - return _drives.GetOrAdd(drive.Name, _ => drive); - } - - /// - public IStorageContainer GetOrCreateContainer( - IStorageLocation location, - Func containerGenerator, - IFileSystemExtensibility? fileSystemExtensibility = null) - { - ChangeDescription? fileSystemChange = null; - IStorageContainer container = _containers.GetOrAdd(location, - loc => - { - IStorageContainer container = - containerGenerator.Invoke(loc, _fileSystem); - (fileSystemExtensibility as FileSystemExtensibility)? - .CopyMetadataTo(container.Extensibility); - if (container.Type == FileSystemTypes.Directory) - { - CreateParents(_fileSystem, loc); - } - else - { - IStorageLocation? parentLocation = loc.GetParent(); - if (parentLocation is { IsRooted: false } && - !_containers.ContainsKey(parentLocation)) - { - throw ExceptionFactory.DirectoryNotFound(loc.FullPath); - } - } - - CheckAndAdjustParentDirectoryTimes(loc); - - using (container.RequestAccess(FileAccess.Write, FileShare.ReadWrite)) - { - fileSystemChange = _fileSystem.ChangeHandler.NotifyPendingChange( - WatcherChangeTypes.Created, - container.Type, - NotifyFilters.DirectoryName, location); - } - - return container; - }); - _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); - return container; - } - - /// - public IStorageLocation? Move(IStorageLocation source, - IStorageLocation destination, - bool overwrite = false, - bool recursive = false) - { - ThrowIfParentDoesNotExist(destination, _ => ExceptionFactory.DirectoryNotFound()); - - List rollbacks = new(); - try - { - return MoveInternal(source, destination, overwrite, recursive, null, - rollbacks); - } - catch (Exception) - { - foreach (Rollback rollback in rollbacks) - { - rollback.Execute(); - } - - throw; - } - } - - /// - public IStorageLocation? Replace(IStorageLocation source, - IStorageLocation destination, - IStorageLocation? backup, - bool ignoreMetadataErrors = false) - { - ThrowIfParentDoesNotExist(destination, location => Execute.OnWindows( - () => ExceptionFactory.DirectoryNotFound(location.FullPath), - () => ExceptionFactory.FileNotFound(location.FullPath))); - - if (!_containers.TryGetValue(source, - out IStorageContainer? sourceContainer)) - { - return null; - } - - if (!_containers.TryGetValue(destination, - out IStorageContainer? destinationContainer)) - { - return null; - } - - if (sourceContainer.Type != FileSystemTypes.File || - destinationContainer.Type != FileSystemTypes.File) - { - throw ExceptionFactory.AccessToPathDenied(source.FullPath); - } - - using (_ = sourceContainer.RequestAccess(FileAccess.ReadWrite, FileShare.None, - ignoreMetadataErrors: ignoreMetadataErrors)) - { - using (_ = destinationContainer.RequestAccess(FileAccess.ReadWrite, - FileShare.None, ignoreMetadataErrors: ignoreMetadataErrors)) - { - if (_containers.TryRemove(destination, - out IStorageContainer? existingDestinationContainer)) - { - int destinationBytesLength = - existingDestinationContainer.GetBytes().Length; - destination.Drive?.ChangeUsedBytes(-1 * destinationBytesLength); - if (backup != null && - _containers.TryAdd(backup, existingDestinationContainer)) - { - Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, - () => existingDestinationContainer.Attributes |= - FileAttributes.Archive); - backup.Drive?.ChangeUsedBytes(destinationBytesLength); - } - - if (_containers.TryRemove(source, - out IStorageContainer? existingSourceContainer)) - { - int sourceBytesLength = existingSourceContainer.GetBytes().Length; - source.Drive?.ChangeUsedBytes(-1 * sourceBytesLength); - destination.Drive?.ChangeUsedBytes(sourceBytesLength); - Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, - () => existingSourceContainer.Attributes |= - FileAttributes.Archive); - existingSourceContainer.CreationTime.Set( - existingDestinationContainer.CreationTime.Get( - DateTimeKind.Utc), - DateTimeKind.Utc); - _containers.TryAdd(destination, existingSourceContainer); - return destination; - } - } - - return null; - } - } - } - -#if FEATURE_FILESYSTEM_LINK - /// - public IStorageLocation? ResolveLinkTarget(IStorageLocation location, - bool returnFinalTarget = false) - { - if (_containers.TryGetValue(location, - out IStorageContainer? initialContainer) && - initialContainer.LinkTarget != null) - { - IStorageLocation nextLocation = - _fileSystem.Storage.GetLocation(initialContainer.LinkTarget); - if (_containers.TryGetValue(nextLocation, - out IStorageContainer? container)) - { - if (returnFinalTarget) - { - nextLocation = ResolveFinalLinkTarget(container, location) ?? - nextLocation; - } - - return nextLocation; - } - - return nextLocation; - } - - return null; - } -#endif - - /// - public bool TryAddContainer( - IStorageLocation location, - Func containerGenerator, - [NotNullWhen(true)] out IStorageContainer? container) - { - IStorageLocation? parentLocation = location.GetParent(); - if (parentLocation != null && - !parentLocation.IsRooted && - !_containers.ContainsKey(parentLocation)) - { - throw ExceptionFactory.DirectoryNotFound(location.FullPath); - } - - ChangeDescription? fileSystemChange = null; - - container = _containers.GetOrAdd( - location, - _ => - { - IStorageContainer container = - containerGenerator(location, _fileSystem); - using (container.RequestAccess(FileAccess.Write, FileShare.ReadWrite)) - { - fileSystemChange = _fileSystem.ChangeHandler.NotifyPendingChange( - WatcherChangeTypes.Created, - container.Type, - NotifyFilters.DirectoryName, location); - } - - CheckAndAdjustParentDirectoryTimes(location); - return container; - }); - - if (fileSystemChange != null) - { - _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); - return true; - } - - container = null; - return false; - } - - #endregion - - private void CheckAndAdjustParentDirectoryTimes(IStorageLocation location) - { - IStorageContainer? parentContainer = GetContainer(location.GetParent()); - if (parentContainer != null && parentContainer is not NullContainer) - { - Execute.NotOnWindowsIf(parentContainer.Attributes.HasFlag(FileAttributes.ReadOnly), - () => throw ExceptionFactory.AccessToPathDenied(location.FullPath)); - TimeAdjustments timeAdjustment = TimeAdjustments.LastWriteTime; - Execute.OnWindows(() - => timeAdjustment |= TimeAdjustments.LastAccessTime); - parentContainer.AdjustTimes(timeAdjustment); - } - } - - private void CreateParents(MockFileSystem fileSystem, IStorageLocation location) - { - List parents = new(); - string? parent = fileSystem.Path.GetDirectoryName( - location.FullPath.TrimEnd(fileSystem.Path.DirectorySeparatorChar, - fileSystem.Path.AltDirectorySeparatorChar)); - while (!string.IsNullOrEmpty(parent)) - { - parents.Add(parent); - parent = fileSystem.Path.GetDirectoryName(parent); - } - - parents.Reverse(); - - List accessHandles = new(); - try - { - foreach (string? parentPath in parents) - { - ChangeDescription? fileSystemChange = null; - IStorageLocation parentLocation = - _fileSystem.Storage.GetLocation(parentPath); - _ = _containers.AddOrUpdate( - parentLocation, - loc => - { - IStorageContainer container = - InMemoryContainer.NewDirectory(loc, _fileSystem); - - accessHandles.Add(container.RequestAccess(FileAccess.Write, - FileShare.ReadWrite)); - fileSystemChange = - fileSystem.ChangeHandler.NotifyPendingChange( - WatcherChangeTypes.Created, - container.Type, - NotifyFilters.DirectoryName, parentLocation); - return container; - }, - (_, f) => f); - fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); - } - } - finally - { - foreach (IStorageAccessHandle accessHandle in accessHandles) - { - accessHandle.Dispose(); - } - } - } - - private IStorageLocation? MoveInternal(IStorageLocation source, - IStorageLocation destination, - bool overwrite, - bool recursive, - FileSystemTypes? sourceType, - List? rollbacks = null) - { - if (!_containers.TryGetValue(source, - out IStorageContainer? container)) - { - return null; - } - - if (container.Type == FileSystemTypes.Directory && - source.FullPath.Equals(destination.FullPath, Execute.IsNetFramework - ? StringComparison.OrdinalIgnoreCase - : StringComparison.Ordinal)) - { - throw ExceptionFactory.MoveSourceMustBeDifferentThanDestination(); - } - - sourceType ??= container.Type; - - List children = - EnumerateLocations(source, FileSystemTypes.DirectoryOrFile).ToList(); - if (children.Any() && !recursive) - { - throw ExceptionFactory.DirectoryNotEmpty(source.FullPath); - } - - using (container.RequestAccess(FileAccess.Write, FileShare.None, - hResult: sourceType == FileSystemTypes.Directory ? -2147024891 : -2147024864)) - { - if (children.Any() && recursive) - { - foreach (IStorageLocation child in children) - { - IStorageLocation childDestination = _fileSystem - .GetMoveLocation(child, source, destination); - MoveInternal(child, childDestination, overwrite, recursive, - sourceType, - rollbacks: rollbacks); - } - } - - ChangeDescription fileSystemChange = - _fileSystem.ChangeHandler.NotifyPendingChange(WatcherChangeTypes.Renamed, - container.Type, - NotifyFilters.FileName, - destination, - source); - if (_containers.TryRemove(source, out IStorageContainer? sourceContainer)) - { - if (overwrite && - _containers.TryRemove(destination, - out IStorageContainer? existingContainer)) - { - existingContainer.ClearBytes(); - } - - if (_containers.TryAdd(destination, sourceContainer)) - { - int bytesLength = sourceContainer.GetBytes().Length; - source.Drive?.ChangeUsedBytes(-1 * bytesLength); - destination.Drive?.ChangeUsedBytes(bytesLength); - Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, - () => sourceContainer.Attributes |= FileAttributes.Archive); - rollbacks?.Add(new Rollback( - () => MoveInternal(destination, source, true, false, - sourceType))); - _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); - return destination; - } - - _containers.TryAdd(source, sourceContainer); - throw ExceptionFactory.CannotCreateFileWhenAlreadyExists( - sourceType == FileSystemTypes.Directory - ? -2147024891 - : Execute.IsWindows - ? -2147024713 - : 17); - } - } - - return source; - } - -#if FEATURE_FILESYSTEM_LINK - private IStorageLocation? ResolveFinalLinkTarget(IStorageContainer container, - IStorageLocation originalLocation) - { - int maxResolveLinks = Execute.IsWindows ? 63 : 40; - IStorageLocation? nextLocation = null; - for (int i = 1; i < maxResolveLinks; i++) - { - if (container.LinkTarget == null) - { - break; - } - - nextLocation = _fileSystem.Storage.GetLocation(container.LinkTarget); - if (!_containers.TryGetValue(nextLocation, - out IStorageContainer? nextContainer)) - { - return nextLocation; - } - - container = nextContainer; - } - - if (container.LinkTarget != null) - { - throw ExceptionFactory.FileNameCannotBeResolved( - originalLocation.FullPath); - } - - return nextLocation; - } -#endif - - private void ThrowIfParentDoesNotExist(IStorageLocation location, - Func - exceptionCallback) - { - IStorageLocation? parentLocation = location.GetParent(); - if (parentLocation != null && - _fileSystem.Path.GetPathRoot(parentLocation.FullPath) != - parentLocation.FullPath && - !_containers.TryGetValue(parentLocation, out _)) - { - throw exceptionCallback(parentLocation); - } - } - - private static void ValidateExpression(string expression) - { - if (expression.Contains('\0')) - { - throw ExceptionFactory.PathHasIllegalCharacters(expression); - } - } - - private sealed class Rollback - { - private readonly Action _onRollback; - - public Rollback(Action onRollback) - { - _onRollback = onRollback; - } - - public void Execute() - { - _onRollback.Invoke(); - } - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystem; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.Storage; + +/// +/// The container storing the current data of the in memory. +/// +internal sealed class InMemoryStorage : IStorage +{ + private readonly ConcurrentDictionary + _containers = new(); + + private readonly ConcurrentDictionary _drives = + new(StringComparer.OrdinalIgnoreCase); + + private readonly MockFileSystem _fileSystem; + + public InMemoryStorage(MockFileSystem fileSystem) + { + _fileSystem = fileSystem; + MainDrive = DriveInfoMock.New(CurrentDirectory, _fileSystem); + _drives.TryAdd(MainDrive.Name, MainDrive); + } + + #region IStorage Members + + /// + public string CurrentDirectory { get; set; } = string.Empty.PrefixRoot(); + + /// + public IStorageDrive MainDrive { get; } + + /// + public IStorageLocation? Copy(IStorageLocation source, + IStorageLocation destination, + bool overwrite = false) + { + ThrowIfParentDoesNotExist(destination, _ => ExceptionFactory.DirectoryNotFound()); + + if (!_containers.TryGetValue(source, + out IStorageContainer? sourceContainer)) + { + return null; + } + + if (sourceContainer.Type != FileSystemTypes.File) + { + throw ExceptionFactory.AccessToPathDenied(source.FullPath); + } + + using (_ = sourceContainer.RequestAccess(FileAccess.ReadWrite, FileShare.None)) + { + if (overwrite && + _containers.TryRemove(destination, + out IStorageContainer? existingContainer)) + { + existingContainer.ClearBytes(); + } + + IStorageContainer copiedContainer = + InMemoryContainer.NewFile(destination, _fileSystem); + if (_containers.TryAdd(destination, copiedContainer)) + { + copiedContainer.WriteBytes(sourceContainer.GetBytes().ToArray()); + Execute.NotOnWindows(() + => sourceContainer.AdjustTimes(TimeAdjustments.LastAccessTime)); + + copiedContainer.Attributes = sourceContainer.Attributes; + Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, + () => copiedContainer.Attributes |= FileAttributes.Archive); + Execute.NotOnWindows( + () => copiedContainer.CreationTime.Set( + sourceContainer.CreationTime.Get(DateTimeKind.Local), + DateTimeKind.Local)); + copiedContainer.LastWriteTime.Set( + sourceContainer.LastWriteTime.Get(DateTimeKind.Local), + DateTimeKind.Local); + return destination; + } + + throw ExceptionFactory.CannotCreateFileWhenAlreadyExists(Execute.IsWindows + ? -2147024816 + : 17); + } + } + + /// + public bool DeleteContainer(IStorageLocation location, bool recursive = false) + { + if (!_containers.TryGetValue(location, out IStorageContainer? container)) + { + IStorageLocation? parentLocation = location.GetParent(); + if (parentLocation != null && + !_containers.TryGetValue(parentLocation, out _)) + { + throw ExceptionFactory.DirectoryNotFound(parentLocation.FullPath); + } + + return false; + } + + if (container.Type == FileSystemTypes.Directory) + { + IEnumerable children = + EnumerateLocations(location, FileSystemTypes.DirectoryOrFile); + if (recursive) + { + foreach (IStorageLocation key in children) + { + DeleteContainer(key); + } + } + else if (children.Any()) + { + throw ExceptionFactory.DirectoryNotEmpty(location.FullPath); + } + } + + NotifyFilters notifyFilters = + container.Type == FileSystemTypes.Directory + ? NotifyFilters.DirectoryName + : NotifyFilters.FileName; + ChangeDescription fileSystemChange = + _fileSystem.ChangeHandler.NotifyPendingChange(WatcherChangeTypes.Deleted, + container.Type, + notifyFilters, location); + + using (container.RequestAccess(FileAccess.Write, FileShare.ReadWrite, + deleteAccess: true)) + { + if (_containers.TryRemove(location, out IStorageContainer? removed)) + { + removed.ClearBytes(); + _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); + CheckAndAdjustParentDirectoryTimes(location); + return true; + } + } + + return false; + } + + /// + public IEnumerable EnumerateLocations( + IStorageLocation location, + FileSystemTypes type, + string searchPattern = EnumerationOptionsHelper.DefaultSearchPattern, + EnumerationOptions? enumerationOptions = null) + { + ValidateExpression(searchPattern); + if (!_containers.ContainsKey(location)) + { + throw ExceptionFactory.DirectoryNotFound(location.FullPath); + } + + enumerationOptions ??= EnumerationOptionsHelper.Compatible; + + string fullPath = location.FullPath; +#if NETSTANDARD2_0 + if (!fullPath.EndsWith($"{_fileSystem.Path.DirectorySeparatorChar}")) +#else + if (!fullPath.EndsWith(_fileSystem.Path.DirectorySeparatorChar)) +#endif + { + fullPath += _fileSystem.Path.DirectorySeparatorChar; + } + + foreach (KeyValuePair item in _containers + .Where(x => x.Key.FullPath.StartsWith(fullPath, + InMemoryLocation.StringComparisonMode) && + !x.Key.Equals(location))) + { + string? parentPath = + _fileSystem.Path.GetDirectoryName( + item.Key.FullPath.TrimEnd(_fileSystem.Path + .DirectorySeparatorChar)); + if (!enumerationOptions.RecurseSubdirectories && + parentPath?.Equals(location.FullPath, + InMemoryLocation.StringComparisonMode) != true) + { + continue; + } + + if (!EnumerationOptionsHelper.MatchesPattern(enumerationOptions, + _fileSystem.Path.GetFileName(item.Key.FullPath), searchPattern)) + { + continue; + } + + if (type.HasFlag(item.Value.Type)) + { + yield return item.Key; + } + } + } + + /// + [return: NotNullIfNotNull("location")] + public IStorageContainer? GetContainer(IStorageLocation? location) + { + if (location == null) + { + return null; + } + + if (_containers.TryGetValue(location, out IStorageContainer? container)) + { + return container; + } + + return NullContainer.New(_fileSystem); + } + + /// + public IStorageDrive? GetDrive(string? driveName) + { + if (string.IsNullOrWhiteSpace(driveName)) + { + return null; + } + + if (!driveName.IsUncPath()) + { + driveName = _fileSystem.Path.GetPathRoot(driveName); + + if (string.IsNullOrEmpty(driveName)) + { + return null; + } + } + + DriveInfoMock drive = DriveInfoMock.New(driveName, _fileSystem); + if (_drives.TryGetValue(drive.Name, out IStorageDrive? d)) + { + return d; + } + + return null; + } + + /// + public IEnumerable GetDrives() + => _drives.Values; + + /// + [return: NotNullIfNotNull("path")] + public IStorageLocation? GetLocation(string? path, string? friendlyName = null) + { + if (path == null) + { + return null; + } + + IStorageDrive? drive = _fileSystem.Storage.GetDrive(path); + if (drive == null && + !_fileSystem.Path.IsPathRooted(path)) + { + drive = _fileSystem.Storage.MainDrive; + } + + return InMemoryLocation.New(drive, _fileSystem.Path.GetFullPath(path), path); + } + + /// + public IStorageDrive GetOrAddDrive(string driveName) + { + DriveInfoMock drive = DriveInfoMock.New(driveName, _fileSystem); + return _drives.GetOrAdd(drive.Name, _ => drive); + } + + /// + public IStorageContainer GetOrCreateContainer( + IStorageLocation location, + Func containerGenerator, + IFileSystemExtensibility? fileSystemExtensibility = null) + { + ChangeDescription? fileSystemChange = null; + IStorageContainer container = _containers.GetOrAdd(location, + loc => + { + IStorageContainer container = + containerGenerator.Invoke(loc, _fileSystem); + (fileSystemExtensibility as FileSystemExtensibility)? + .CopyMetadataTo(container.Extensibility); + if (container.Type == FileSystemTypes.Directory) + { + CreateParents(_fileSystem, loc); + } + else + { + IStorageLocation? parentLocation = loc.GetParent(); + if (parentLocation is { IsRooted: false } && + !_containers.ContainsKey(parentLocation)) + { + throw ExceptionFactory.DirectoryNotFound(loc.FullPath); + } + } + + CheckAndAdjustParentDirectoryTimes(loc); + + using (container.RequestAccess(FileAccess.Write, FileShare.ReadWrite)) + { + fileSystemChange = _fileSystem.ChangeHandler.NotifyPendingChange( + WatcherChangeTypes.Created, + container.Type, + NotifyFilters.DirectoryName, location); + } + + return container; + }); + _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); + return container; + } + + /// + public IStorageLocation? Move(IStorageLocation source, + IStorageLocation destination, + bool overwrite = false, + bool recursive = false) + { + ThrowIfParentDoesNotExist(destination, _ => ExceptionFactory.DirectoryNotFound()); + + List rollbacks = new(); + try + { + return MoveInternal(source, destination, overwrite, recursive, null, + rollbacks); + } + catch (Exception) + { + foreach (Rollback rollback in rollbacks) + { + rollback.Execute(); + } + + throw; + } + } + + /// + public IStorageLocation? Replace(IStorageLocation source, + IStorageLocation destination, + IStorageLocation? backup, + bool ignoreMetadataErrors = false) + { + ThrowIfParentDoesNotExist(destination, location => Execute.OnWindows( + () => ExceptionFactory.DirectoryNotFound(location.FullPath), + () => ExceptionFactory.FileNotFound(location.FullPath))); + + if (!_containers.TryGetValue(source, + out IStorageContainer? sourceContainer)) + { + return null; + } + + if (!_containers.TryGetValue(destination, + out IStorageContainer? destinationContainer)) + { + return null; + } + + if (sourceContainer.Type != FileSystemTypes.File || + destinationContainer.Type != FileSystemTypes.File) + { + throw ExceptionFactory.AccessToPathDenied(source.FullPath); + } + + using (_ = sourceContainer.RequestAccess(FileAccess.ReadWrite, FileShare.None, + ignoreMetadataErrors: ignoreMetadataErrors)) + { + using (_ = destinationContainer.RequestAccess(FileAccess.ReadWrite, + FileShare.None, ignoreMetadataErrors: ignoreMetadataErrors)) + { + if (_containers.TryRemove(destination, + out IStorageContainer? existingDestinationContainer)) + { + int destinationBytesLength = + existingDestinationContainer.GetBytes().Length; + destination.Drive?.ChangeUsedBytes(-1 * destinationBytesLength); + if (backup != null && + _containers.TryAdd(backup, existingDestinationContainer)) + { + Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, + () => existingDestinationContainer.Attributes |= + FileAttributes.Archive); + backup.Drive?.ChangeUsedBytes(destinationBytesLength); + } + + if (_containers.TryRemove(source, + out IStorageContainer? existingSourceContainer)) + { + int sourceBytesLength = existingSourceContainer.GetBytes().Length; + source.Drive?.ChangeUsedBytes(-1 * sourceBytesLength); + destination.Drive?.ChangeUsedBytes(sourceBytesLength); + Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, + () => existingSourceContainer.Attributes |= + FileAttributes.Archive); + existingSourceContainer.CreationTime.Set( + existingDestinationContainer.CreationTime.Get( + DateTimeKind.Utc), + DateTimeKind.Utc); + _containers.TryAdd(destination, existingSourceContainer); + return destination; + } + } + + return null; + } + } + } + +#if FEATURE_FILESYSTEM_LINK + /// + public IStorageLocation? ResolveLinkTarget(IStorageLocation location, + bool returnFinalTarget = false) + { + if (_containers.TryGetValue(location, + out IStorageContainer? initialContainer) && + initialContainer.LinkTarget != null) + { + IStorageLocation nextLocation = + _fileSystem.Storage.GetLocation(initialContainer.LinkTarget); + if (_containers.TryGetValue(nextLocation, + out IStorageContainer? container)) + { + if (returnFinalTarget) + { + nextLocation = ResolveFinalLinkTarget(container, location) ?? + nextLocation; + } + + return nextLocation; + } + + return nextLocation; + } + + return null; + } +#endif + + /// + public bool TryAddContainer( + IStorageLocation location, + Func containerGenerator, + [NotNullWhen(true)] out IStorageContainer? container) + { + IStorageLocation? parentLocation = location.GetParent(); + if (parentLocation != null && + !parentLocation.IsRooted && + !_containers.ContainsKey(parentLocation)) + { + throw ExceptionFactory.DirectoryNotFound(location.FullPath); + } + + ChangeDescription? fileSystemChange = null; + + container = _containers.GetOrAdd( + location, + _ => + { + IStorageContainer container = + containerGenerator(location, _fileSystem); + using (container.RequestAccess(FileAccess.Write, FileShare.ReadWrite)) + { + fileSystemChange = _fileSystem.ChangeHandler.NotifyPendingChange( + WatcherChangeTypes.Created, + container.Type, + NotifyFilters.DirectoryName, location); + } + + CheckAndAdjustParentDirectoryTimes(location); + return container; + }); + + if (fileSystemChange != null) + { + _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); + return true; + } + + container = null; + return false; + } + + #endregion + + private void CheckAndAdjustParentDirectoryTimes(IStorageLocation location) + { + IStorageContainer? parentContainer = GetContainer(location.GetParent()); + if (parentContainer != null && parentContainer is not NullContainer) + { + Execute.NotOnWindowsIf(parentContainer.Attributes.HasFlag(FileAttributes.ReadOnly), + () => throw ExceptionFactory.AccessToPathDenied(location.FullPath)); + TimeAdjustments timeAdjustment = TimeAdjustments.LastWriteTime; + Execute.OnWindows(() + => timeAdjustment |= TimeAdjustments.LastAccessTime); + parentContainer.AdjustTimes(timeAdjustment); + } + } + + private void CreateParents(MockFileSystem fileSystem, IStorageLocation location) + { + List parents = new(); + string? parent = fileSystem.Path.GetDirectoryName( + location.FullPath.TrimEnd(fileSystem.Path.DirectorySeparatorChar, + fileSystem.Path.AltDirectorySeparatorChar)); + while (!string.IsNullOrEmpty(parent)) + { + parents.Add(parent); + parent = fileSystem.Path.GetDirectoryName(parent); + } + + parents.Reverse(); + + List accessHandles = new(); + try + { + foreach (string? parentPath in parents) + { + ChangeDescription? fileSystemChange = null; + IStorageLocation parentLocation = + _fileSystem.Storage.GetLocation(parentPath); + _ = _containers.AddOrUpdate( + parentLocation, + loc => + { + IStorageContainer container = + InMemoryContainer.NewDirectory(loc, _fileSystem); + + accessHandles.Add(container.RequestAccess(FileAccess.Write, + FileShare.ReadWrite)); + fileSystemChange = + fileSystem.ChangeHandler.NotifyPendingChange( + WatcherChangeTypes.Created, + container.Type, + NotifyFilters.DirectoryName, parentLocation); + return container; + }, + (_, f) => f); + fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); + } + } + finally + { + foreach (IStorageAccessHandle accessHandle in accessHandles) + { + accessHandle.Dispose(); + } + } + } + + private IStorageLocation? MoveInternal(IStorageLocation source, + IStorageLocation destination, + bool overwrite, + bool recursive, + FileSystemTypes? sourceType, + List? rollbacks = null) + { + if (!_containers.TryGetValue(source, + out IStorageContainer? container)) + { + return null; + } + + if (container.Type == FileSystemTypes.Directory && + source.FullPath.Equals(destination.FullPath, Execute.IsNetFramework + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal)) + { + throw ExceptionFactory.MoveSourceMustBeDifferentThanDestination(); + } + + sourceType ??= container.Type; + + List children = + EnumerateLocations(source, FileSystemTypes.DirectoryOrFile).ToList(); + if (children.Any() && !recursive) + { + throw ExceptionFactory.DirectoryNotEmpty(source.FullPath); + } + + using (container.RequestAccess(FileAccess.Write, FileShare.None, + hResult: sourceType == FileSystemTypes.Directory ? -2147024891 : -2147024864)) + { + if (children.Any() && recursive) + { + foreach (IStorageLocation child in children) + { + IStorageLocation childDestination = _fileSystem + .GetMoveLocation(child, source, destination); + MoveInternal(child, childDestination, overwrite, recursive, + sourceType, + rollbacks: rollbacks); + } + } + + ChangeDescription fileSystemChange = + _fileSystem.ChangeHandler.NotifyPendingChange(WatcherChangeTypes.Renamed, + container.Type, + NotifyFilters.FileName, + destination, + source); + if (_containers.TryRemove(source, out IStorageContainer? sourceContainer)) + { + if (overwrite && + _containers.TryRemove(destination, + out IStorageContainer? existingContainer)) + { + existingContainer.ClearBytes(); + } + + if (_containers.TryAdd(destination, sourceContainer)) + { + int bytesLength = sourceContainer.GetBytes().Length; + source.Drive?.ChangeUsedBytes(-1 * bytesLength); + destination.Drive?.ChangeUsedBytes(bytesLength); + Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, + () => sourceContainer.Attributes |= FileAttributes.Archive); + rollbacks?.Add(new Rollback( + () => MoveInternal(destination, source, true, false, + sourceType))); + _fileSystem.ChangeHandler.NotifyCompletedChange(fileSystemChange); + return destination; + } + + _containers.TryAdd(source, sourceContainer); + throw ExceptionFactory.CannotCreateFileWhenAlreadyExists( + sourceType == FileSystemTypes.Directory + ? -2147024891 + : Execute.IsWindows + ? -2147024713 + : 17); + } + } + + return source; + } + +#if FEATURE_FILESYSTEM_LINK + private IStorageLocation? ResolveFinalLinkTarget(IStorageContainer container, + IStorageLocation originalLocation) + { + int maxResolveLinks = Execute.IsWindows ? 63 : 40; + IStorageLocation? nextLocation = null; + for (int i = 1; i < maxResolveLinks; i++) + { + if (container.LinkTarget == null) + { + break; + } + + nextLocation = _fileSystem.Storage.GetLocation(container.LinkTarget); + if (!_containers.TryGetValue(nextLocation, + out IStorageContainer? nextContainer)) + { + return nextLocation; + } + + container = nextContainer; + } + + if (container.LinkTarget != null) + { + throw ExceptionFactory.FileNameCannotBeResolved( + originalLocation.FullPath); + } + + return nextLocation; + } +#endif + + private void ThrowIfParentDoesNotExist(IStorageLocation location, + Func + exceptionCallback) + { + IStorageLocation? parentLocation = location.GetParent(); + if (parentLocation != null && + _fileSystem.Path.GetPathRoot(parentLocation.FullPath) != + parentLocation.FullPath && + !_containers.TryGetValue(parentLocation, out _)) + { + throw exceptionCallback(parentLocation); + } + } + + private static void ValidateExpression(string expression) + { + if (expression.Contains('\0')) + { + throw ExceptionFactory.PathHasIllegalCharacters(expression); + } + } + + private sealed class Rollback + { + private readonly Action _onRollback; + + public Rollback(Action onRollback) + { + _onRollback = onRollback; + } + + public void Execute() + { + _onRollback.Invoke(); + } + } +} diff --git a/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs b/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs index 3d18c5f8e..abf9ab117 100644 --- a/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs +++ b/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs @@ -1,196 +1,196 @@ -using System; -using System.IO; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.TimeSystem; - -namespace Testably.Abstractions.Testing.Storage; - -internal sealed class NullContainer : IStorageContainer -{ - private NullContainer(IFileSystem fileSystem, ITimeSystem timeSystem) - { - FileSystem = fileSystem; - TimeSystem = timeSystem; - Extensibility = new FileSystemExtensibility(); - } - - #region IStorageContainer Members - - /// - public FileAttributes Attributes - { - get => (FileAttributes)(-1); - set => throw ExceptionFactory.FileNotFound(string.Empty); - } - - /// - public IStorageContainer.ITimeContainer CreationTime { get; } = - new CreationNullTime(); - - /// - public IFileSystemExtensibility Extensibility { get; } - - /// - public IFileSystem FileSystem { get; } - - /// - public IStorageContainer.ITimeContainer LastAccessTime { get; } = new NullTime(); - - /// - public IStorageContainer.ITimeContainer LastWriteTime { get; } = new NullTime(); - - /// - public string? LinkTarget - { - get => null; - set => _ = value; - } - - /// - public ITimeSystem TimeSystem { get; } - - /// - public FileSystemTypes Type - => FileSystemTypes.DirectoryOrFile; - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - public UnixFileMode UnixFileMode { get; set; } = (UnixFileMode)(-1); -#endif - - /// - public void AppendBytes(byte[] bytes) - { - // Do nothing in NullContainer - } - - /// - public void ClearBytes() - { - // Do nothing in NullContainer - } - - /// - public void Decrypt() - { - // Do nothing in NullContainer - } - - /// - public void Encrypt() - { - // Do nothing in NullContainer - } - - /// - public byte[] GetBytes() - => Array.Empty(); - - /// - public IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, - bool deleteAccess = false, - bool ignoreMetadataErrors = true, - int? hResult = null) - => new NullStorageAccessHandle(access, share, deleteAccess); - - /// - public void WriteBytes(byte[] bytes) - { - // Do nothing in NullContainer - } - - #endregion - - internal static IStorageContainer New(MockFileSystem fileSystem) - => new NullContainer(fileSystem, fileSystem.TimeSystem); - - private sealed class NullStorageAccessHandle : IStorageAccessHandle - { - public NullStorageAccessHandle(FileAccess access, FileShare share, - bool deleteAccess) - { - Access = access; - Share = share; - DeleteAccess = deleteAccess; - } - - #region IStorageAccessHandle Members - - /// - public FileAccess Access { get; } - - /// - public bool DeleteAccess { get; } - - /// - public FileShare Share { get; } - - /// - public void Dispose() - { - // Nothing to do! - } - - #endregion - } - - /// - /// The default time returned by the file system if no time has been set. - /// : - /// A file time is a 64-bit value that represents the number of 100-nanosecond intervals that have elapsed - /// since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC). - /// - private class NullTime : IStorageContainer.ITimeContainer - { - private readonly DateTime _time = - new(1601, 01, 01, 00, 00, 00, DateTimeKind.Utc); - - #region ITimeContainer Members - - /// - public DateTime Get(DateTimeKind kind) - => kind switch - { - DateTimeKind.Utc => _time.ToUniversalTime(), - DateTimeKind.Local => _time.ToLocalTime(), - _ => _time - }; - - /// - public virtual void Set(DateTime time, DateTimeKind kind) - { -#if NET7_0_OR_GREATER - throw ExceptionFactory.FileNotFound(string.Empty); -#else - Execute.OnWindows(() - => throw ExceptionFactory.FileNotFound(string.Empty)); - - throw ExceptionFactory.DirectoryNotFound(string.Empty); -#endif - } - - #endregion - } - - /// - /// Overrides the setter of as different exceptions are thrown for MacOS starting with .NET 7. - /// - private sealed class CreationNullTime : NullTime - { - /// - public override void Set(DateTime time, DateTimeKind kind) - { -#if NET7_0_OR_GREATER - Execute.OnMac(() - => throw ExceptionFactory.DirectoryNotFound(string.Empty)); - - throw ExceptionFactory.FileNotFound(string.Empty); -#else - Execute.OnWindows(() - => throw ExceptionFactory.FileNotFound(string.Empty)); - - throw ExceptionFactory.DirectoryNotFound(string.Empty); -#endif - } - } -} +using System; +using System.IO; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.TimeSystem; + +namespace Testably.Abstractions.Testing.Storage; + +internal sealed class NullContainer : IStorageContainer +{ + private NullContainer(IFileSystem fileSystem, ITimeSystem timeSystem) + { + FileSystem = fileSystem; + TimeSystem = timeSystem; + Extensibility = new FileSystemExtensibility(); + } + + #region IStorageContainer Members + + /// + public FileAttributes Attributes + { + get => (FileAttributes)(-1); + set => throw ExceptionFactory.FileNotFound(string.Empty); + } + + /// + public IStorageContainer.ITimeContainer CreationTime { get; } = + new CreationNullTime(); + + /// + public IFileSystemExtensibility Extensibility { get; } + + /// + public IFileSystem FileSystem { get; } + + /// + public IStorageContainer.ITimeContainer LastAccessTime { get; } = new NullTime(); + + /// + public IStorageContainer.ITimeContainer LastWriteTime { get; } = new NullTime(); + + /// + public string? LinkTarget + { + get => null; + set => _ = value; + } + + /// + public ITimeSystem TimeSystem { get; } + + /// + public FileSystemTypes Type + => FileSystemTypes.DirectoryOrFile; + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + public UnixFileMode UnixFileMode { get; set; } = (UnixFileMode)(-1); +#endif + + /// + public void AppendBytes(byte[] bytes) + { + // Do nothing in NullContainer + } + + /// + public void ClearBytes() + { + // Do nothing in NullContainer + } + + /// + public void Decrypt() + { + // Do nothing in NullContainer + } + + /// + public void Encrypt() + { + // Do nothing in NullContainer + } + + /// + public byte[] GetBytes() + => Array.Empty(); + + /// + public IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, + bool deleteAccess = false, + bool ignoreMetadataErrors = true, + int? hResult = null) + => new NullStorageAccessHandle(access, share, deleteAccess); + + /// + public void WriteBytes(byte[] bytes) + { + // Do nothing in NullContainer + } + + #endregion + + internal static IStorageContainer New(MockFileSystem fileSystem) + => new NullContainer(fileSystem, fileSystem.TimeSystem); + + private sealed class NullStorageAccessHandle : IStorageAccessHandle + { + public NullStorageAccessHandle(FileAccess access, FileShare share, + bool deleteAccess) + { + Access = access; + Share = share; + DeleteAccess = deleteAccess; + } + + #region IStorageAccessHandle Members + + /// + public FileAccess Access { get; } + + /// + public bool DeleteAccess { get; } + + /// + public FileShare Share { get; } + + /// + public void Dispose() + { + // Nothing to do! + } + + #endregion + } + + /// + /// The default time returned by the file system if no time has been set. + /// : + /// A file time is a 64-bit value that represents the number of 100-nanosecond intervals that have elapsed + /// since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC). + /// + private class NullTime : IStorageContainer.ITimeContainer + { + private readonly DateTime _time = + new(1601, 01, 01, 00, 00, 00, DateTimeKind.Utc); + + #region ITimeContainer Members + + /// + public DateTime Get(DateTimeKind kind) + => kind switch + { + DateTimeKind.Utc => _time.ToUniversalTime(), + DateTimeKind.Local => _time.ToLocalTime(), + _ => _time + }; + + /// + public virtual void Set(DateTime time, DateTimeKind kind) + { +#if NET7_0_OR_GREATER + throw ExceptionFactory.FileNotFound(string.Empty); +#else + Execute.OnWindows(() + => throw ExceptionFactory.FileNotFound(string.Empty)); + + throw ExceptionFactory.DirectoryNotFound(string.Empty); +#endif + } + + #endregion + } + + /// + /// Overrides the setter of as different exceptions are thrown for MacOS starting with .NET 7. + /// + private sealed class CreationNullTime : NullTime + { + /// + public override void Set(DateTime time, DateTimeKind kind) + { +#if NET7_0_OR_GREATER + Execute.OnMac(() + => throw ExceptionFactory.DirectoryNotFound(string.Empty)); + + throw ExceptionFactory.FileNotFound(string.Empty); +#else + Execute.OnWindows(() + => throw ExceptionFactory.FileNotFound(string.Empty)); + + throw ExceptionFactory.DirectoryNotFound(string.Empty); +#endif + } + } +} diff --git a/Source/Testably.Abstractions/FileSystem/FileWrapper.cs b/Source/Testably.Abstractions/FileSystem/FileWrapper.cs index b98bbabe5..6257dc194 100644 --- a/Source/Testably.Abstractions/FileSystem/FileWrapper.cs +++ b/Source/Testably.Abstractions/FileSystem/FileWrapper.cs @@ -1,11 +1,11 @@ -#if FEATURE_FILESYSTEM_SAFEFILEHANDLE -using Microsoft.Win32.SafeHandles; -#endif -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE +using Microsoft.Win32.SafeHandles; +#endif #if FEATURE_FILESYSTEM_ASYNC using System.Threading; using System.Threading.Tasks; diff --git a/Tests/Testably.Abstractions.AccessControl.Tests/FileInfoAclExtensionsTests.cs b/Tests/Testably.Abstractions.AccessControl.Tests/FileInfoAclExtensionsTests.cs index bb7eda994..da55776a0 100644 --- a/Tests/Testably.Abstractions.AccessControl.Tests/FileInfoAclExtensionsTests.cs +++ b/Tests/Testably.Abstractions.AccessControl.Tests/FileInfoAclExtensionsTests.cs @@ -1,62 +1,62 @@ -using System.Security.AccessControl; -using Testably.Abstractions.AccessControl.Tests.TestHelpers; - -namespace Testably.Abstractions.AccessControl.Tests; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class FileInfoAclExtensionsTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableFact] - public void GetAccessControl_ShouldBeInitializedWithNotNullValue() - { - Skip.IfNot(Test.RunsOnWindows); - - FileSystem.File.WriteAllText("foo", null); - IFileInfo fileInfo = FileSystem.FileInfo.New("foo"); - - #pragma warning disable CA1416 - FileSecurity result = fileInfo.GetAccessControl(); - #pragma warning restore CA1416 - - result.Should().NotBeNull(); - } - - [SkippableFact] - public void GetAccessControl_WithAccessControlSections_ShouldBeInitializedWithNotNullValue() - { - Skip.IfNot(Test.RunsOnWindows); - - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText("foo", null); - IFileInfo fileInfo = FileSystem.FileInfo.New("foo"); - - #pragma warning disable CA1416 - FileSecurity result = fileInfo.GetAccessControl(AccessControlSections.All); - #pragma warning restore CA1416 - - result.Should().NotBeNull(); - } - - [SkippableFact] - public void SetAccessControl_ShouldChangeAccessControl() - { - Skip.IfNot(Test.RunsOnWindows); - - FileSystem.File.WriteAllText("foo", null); - #pragma warning disable CA1416 - FileSecurity originalAccessControl = - FileSystem.FileInfo.New("foo").GetAccessControl(); - FileSystem.FileInfo.New("foo").SetAccessControl(originalAccessControl); - - FileSecurity currentAccessControl = - FileSystem.FileInfo.New("foo") - .GetAccessControl(AccessControlSections.Access); - #pragma warning restore CA1416 - - currentAccessControl.HasSameAccessRightsAs(originalAccessControl) - .Should().BeTrue(); - } -} +using System.Security.AccessControl; +using Testably.Abstractions.AccessControl.Tests.TestHelpers; + +namespace Testably.Abstractions.AccessControl.Tests; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class FileInfoAclExtensionsTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableFact] + public void GetAccessControl_ShouldBeInitializedWithNotNullValue() + { + Skip.IfNot(Test.RunsOnWindows); + + FileSystem.File.WriteAllText("foo", null); + IFileInfo fileInfo = FileSystem.FileInfo.New("foo"); + + #pragma warning disable CA1416 + FileSecurity result = fileInfo.GetAccessControl(); + #pragma warning restore CA1416 + + result.Should().NotBeNull(); + } + + [SkippableFact] + public void GetAccessControl_WithAccessControlSections_ShouldBeInitializedWithNotNullValue() + { + Skip.IfNot(Test.RunsOnWindows); + + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText("foo", null); + IFileInfo fileInfo = FileSystem.FileInfo.New("foo"); + + #pragma warning disable CA1416 + FileSecurity result = fileInfo.GetAccessControl(AccessControlSections.All); + #pragma warning restore CA1416 + + result.Should().NotBeNull(); + } + + [SkippableFact] + public void SetAccessControl_ShouldChangeAccessControl() + { + Skip.IfNot(Test.RunsOnWindows); + + FileSystem.File.WriteAllText("foo", null); + #pragma warning disable CA1416 + FileSecurity originalAccessControl = + FileSystem.FileInfo.New("foo").GetAccessControl(); + FileSystem.FileInfo.New("foo").SetAccessControl(originalAccessControl); + + FileSecurity currentAccessControl = + FileSystem.FileInfo.New("foo") + .GetAccessControl(AccessControlSections.Access); + #pragma warning restore CA1416 + + currentAccessControl.HasSameAccessRightsAs(originalAccessControl) + .Should().BeTrue(); + } +} diff --git a/Tests/Testably.Abstractions.AccessControl.Tests/FileStreamAclExtensionsTests.cs b/Tests/Testably.Abstractions.AccessControl.Tests/FileStreamAclExtensionsTests.cs index 186350eb2..25bfe3499 100644 --- a/Tests/Testably.Abstractions.AccessControl.Tests/FileStreamAclExtensionsTests.cs +++ b/Tests/Testably.Abstractions.AccessControl.Tests/FileStreamAclExtensionsTests.cs @@ -1,41 +1,41 @@ -using System.Security.AccessControl; -using Testably.Abstractions.AccessControl.Tests.TestHelpers; - -namespace Testably.Abstractions.AccessControl.Tests; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class FileStreamAclExtensionsTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableFact] - public void GetAccessControl_ShouldBeInitializedWithNotNullValue() - { - Skip.IfNot(Test.RunsOnWindows); - - FileSystemStream fileStream = FileSystem.File.Create("foo"); - - #pragma warning disable CA1416 - FileSecurity result = fileStream.GetAccessControl(); - #pragma warning restore CA1416 - - result.Should().NotBeNull(); - } - - [SkippableFact] - public void SetAccessControl_ShouldChangeAccessControl() - { - Skip.IfNot(Test.RunsOnWindows); - - FileSystemStream fileStream = FileSystem.File.Create("foo"); - #pragma warning disable CA1416 - FileSecurity originalAccessControl = fileStream.GetAccessControl(); - fileStream.SetAccessControl(originalAccessControl); - - FileSecurity currentAccessControl = fileStream.GetAccessControl(); - #pragma warning restore CA1416 - - currentAccessControl.HasSameAccessRightsAs(originalAccessControl) - .Should().BeTrue(); - } -} +using System.Security.AccessControl; +using Testably.Abstractions.AccessControl.Tests.TestHelpers; + +namespace Testably.Abstractions.AccessControl.Tests; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class FileStreamAclExtensionsTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableFact] + public void GetAccessControl_ShouldBeInitializedWithNotNullValue() + { + Skip.IfNot(Test.RunsOnWindows); + + FileSystemStream fileStream = FileSystem.File.Create("foo"); + + #pragma warning disable CA1416 + FileSecurity result = fileStream.GetAccessControl(); + #pragma warning restore CA1416 + + result.Should().NotBeNull(); + } + + [SkippableFact] + public void SetAccessControl_ShouldChangeAccessControl() + { + Skip.IfNot(Test.RunsOnWindows); + + FileSystemStream fileStream = FileSystem.File.Create("foo"); + #pragma warning disable CA1416 + FileSecurity originalAccessControl = fileStream.GetAccessControl(); + fileStream.SetAccessControl(originalAccessControl); + + FileSecurity currentAccessControl = fileStream.GetAccessControl(); + #pragma warning restore CA1416 + + currentAccessControl.HasSameAccessRightsAs(originalAccessControl) + .Should().BeTrue(); + } +} diff --git a/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.Extensions.cs b/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.Extensions.cs index 1877b8db9..c4adf2719 100644 --- a/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.Extensions.cs +++ b/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.Extensions.cs @@ -1,284 +1,284 @@ -using System.IO; -using System.IO.Compression; -using System.Linq; - -namespace Testably.Abstractions.Compression.Tests.ZipArchive; - -public abstract partial class ZipArchiveTests - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [InlineData("2000-01-01T12:14:15")] - [InlineData("1980-01-01T00:00:00")] - [InlineData("2107-12-31T23:59:59")] - public void CreateEntryFromFile_LastWriteTime_ShouldBeCopiedFromFile( - string lastWriteTimeString) - { - DateTime lastWriteTime = DateTime.Parse(lastWriteTimeString); - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar"); - FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); - FileSystem.File.SetLastWriteTime("bar/foo.txt", lastWriteTime); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - archive.Entries.Count.Should().Be(0); - - archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt", - CompressionLevel.NoCompression); - - IZipArchiveEntry entry = archive.Entries.Single(); - entry.LastWriteTime.DateTime.Should().Be(lastWriteTime); - } - - [SkippableTheory] - [InlineData("1930-06-21T14:15:16")] - [InlineData("1979-12-31T00:00:00")] - [InlineData("2108-01-01T00:00:00")] - [InlineData("2208-01-01T00:00:00")] - public void CreateEntryFromFile_LastWriteTimeOutOfRange_ShouldBeFirstJanuary1980( - string lastWriteTimeString) - { - DateTime expectedTime = new(1980, 1, 1, 0, 0, 0); - DateTime lastWriteTime = DateTime.Parse(lastWriteTimeString).ToUniversalTime(); - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar"); - FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); - FileSystem.File.SetLastWriteTime("bar/foo.txt", lastWriteTime); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - archive.Entries.Count.Should().Be(0); - - archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt", - CompressionLevel.NoCompression); - - IZipArchiveEntry entry = archive.Entries.Single(); - entry.LastWriteTime.DateTime.Should().Be(expectedTime); - } - - [SkippableFact] - public void CreateEntryFromFile_NullEntryName_ShouldThrowArgumentNullException() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar"); - FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - archive.Entries.Count.Should().Be(0); - - Exception? exception = Record.Exception(() => - { - archive.CreateEntryFromFile("bar/foo.txt", null!); - }); - - exception.Should().BeOfType() - .Which.ParamName.Should().Be("entryName"); - } - - [SkippableFact] - public void CreateEntryFromFile_NullSourceFileName_ShouldThrowArgumentNullException() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar"); - FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - archive.Entries.Count.Should().Be(0); - - Exception? exception = Record.Exception(() => - { - archive.CreateEntryFromFile(null!, "foo/bar.txt", - CompressionLevel.NoCompression); - }); - - exception.Should().BeOfType() - .Which.ParamName.Should().Be("sourceFileName"); - } - - [SkippableFact] - public void CreateEntryFromFile_ReadOnlyArchive_ShouldThrowNotSupportedException() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar"); - FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - archive.Entries.Count.Should().Be(0); - - Exception? exception = Record.Exception(() => - { - archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt"); - }); - - exception.Should().BeOfType(); - } - - [SkippableFact] - public void CreateEntryFromFile_ShouldCreateEntryWithFileContent() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar"); - FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - archive.Entries.Count.Should().Be(0); - - archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt", - CompressionLevel.NoCompression); - - IZipArchiveEntry entry = archive.Entries.Single(); - entry.FullName.Should().Be("foo/bar.txt"); - - entry.ExtractToFile("test.txt"); - FileSystem.File.ReadAllText("test.txt").Should().Be("FooFooFoo"); - } - - [SkippableTheory] - [AutoData] - public void ExtractToDirectory_DestinationNull_ShouldThrowArgumentNullException( - CompressionLevel compressionLevel) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); - - Exception? exception = Record.Exception(() => - { - using IZipArchive archive = - FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); - - archive.ExtractToDirectory(null!); - }); - - exception.Should().BeOfType() - .Which.ParamName.Should().Be("destinationDirectoryName"); - } - -#if FEATURE_COMPRESSION_ADVANCED - [SkippableTheory] - [AutoData] - public void - ExtractToDirectory_DestinationNull_WithOverwrite_ShouldThrowArgumentNullException( - CompressionLevel compressionLevel) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); - - Exception? exception = Record.Exception(() => - { - using IZipArchive archive = - FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); - - archive.ExtractToDirectory(null!, true); - }); - - exception.Should().BeOfType(); - } -#endif - - [SkippableFact] - public void ExtractToDirectory_WithoutOverwrite_ShouldThrowIOException() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar").Initialized(s => s - .WithFile("foo.txt")); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - Exception? exception = Record.Exception(() => - { - archive.ExtractToDirectory("bar"); - }); - - exception.Should().BeOfType() - .Which.Message.Should() - .Contain($"'{FileSystem.Path.GetFullPath("bar/foo.txt")}'"); - FileSystem.File.ReadAllText("bar/foo.txt") - .Should().NotBe("FooFooFoo"); - } - - [SkippableFact] - public void ExtractToDirectory_ShouldExtractFilesAndDirectories() - { - FileSystem.Initialize() - .WithSubdirectory("foo").Initialized(s => s - .WithSubdirectory("bar") - .WithFile("bar.txt")); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - archive.ExtractToDirectory("bar"); - - FileSystem.File.ReadAllText("bar/foo.txt") - .Should().Be("FooFooFoo"); - FileSystem.Directory.Exists("bar/bar") - .Should().BeTrue(); - FileSystem.File.Exists("bar/bar.txt") - .Should().BeTrue(); - } - -#if FEATURE_COMPRESSION_ADVANCED - [SkippableFact] - public void ExtractToDirectory_WithOverwrite_ShouldOverwriteExistingFile() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar").Initialized(s => s - .WithFile("foo.txt")); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - archive.ExtractToDirectory("bar", true); - - FileSystem.File.ReadAllText("bar/foo.txt") - .Should().Be("FooFooFoo"); - } -#endif -} +using System.IO; +using System.IO.Compression; +using System.Linq; + +namespace Testably.Abstractions.Compression.Tests.ZipArchive; + +public abstract partial class ZipArchiveTests + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [InlineData("2000-01-01T12:14:15")] + [InlineData("1980-01-01T00:00:00")] + [InlineData("2107-12-31T23:59:59")] + public void CreateEntryFromFile_LastWriteTime_ShouldBeCopiedFromFile( + string lastWriteTimeString) + { + DateTime lastWriteTime = DateTime.Parse(lastWriteTimeString); + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar"); + FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); + FileSystem.File.SetLastWriteTime("bar/foo.txt", lastWriteTime); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + archive.Entries.Count.Should().Be(0); + + archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt", + CompressionLevel.NoCompression); + + IZipArchiveEntry entry = archive.Entries.Single(); + entry.LastWriteTime.DateTime.Should().Be(lastWriteTime); + } + + [SkippableTheory] + [InlineData("1930-06-21T14:15:16")] + [InlineData("1979-12-31T00:00:00")] + [InlineData("2108-01-01T00:00:00")] + [InlineData("2208-01-01T00:00:00")] + public void CreateEntryFromFile_LastWriteTimeOutOfRange_ShouldBeFirstJanuary1980( + string lastWriteTimeString) + { + DateTime expectedTime = new(1980, 1, 1, 0, 0, 0); + DateTime lastWriteTime = DateTime.Parse(lastWriteTimeString).ToUniversalTime(); + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar"); + FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); + FileSystem.File.SetLastWriteTime("bar/foo.txt", lastWriteTime); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + archive.Entries.Count.Should().Be(0); + + archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt", + CompressionLevel.NoCompression); + + IZipArchiveEntry entry = archive.Entries.Single(); + entry.LastWriteTime.DateTime.Should().Be(expectedTime); + } + + [SkippableFact] + public void CreateEntryFromFile_NullEntryName_ShouldThrowArgumentNullException() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar"); + FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + archive.Entries.Count.Should().Be(0); + + Exception? exception = Record.Exception(() => + { + archive.CreateEntryFromFile("bar/foo.txt", null!); + }); + + exception.Should().BeOfType() + .Which.ParamName.Should().Be("entryName"); + } + + [SkippableFact] + public void CreateEntryFromFile_NullSourceFileName_ShouldThrowArgumentNullException() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar"); + FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + archive.Entries.Count.Should().Be(0); + + Exception? exception = Record.Exception(() => + { + archive.CreateEntryFromFile(null!, "foo/bar.txt", + CompressionLevel.NoCompression); + }); + + exception.Should().BeOfType() + .Which.ParamName.Should().Be("sourceFileName"); + } + + [SkippableFact] + public void CreateEntryFromFile_ReadOnlyArchive_ShouldThrowNotSupportedException() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar"); + FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + archive.Entries.Count.Should().Be(0); + + Exception? exception = Record.Exception(() => + { + archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt"); + }); + + exception.Should().BeOfType(); + } + + [SkippableFact] + public void CreateEntryFromFile_ShouldCreateEntryWithFileContent() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar"); + FileSystem.File.WriteAllText("bar/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + archive.Entries.Count.Should().Be(0); + + archive.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt", + CompressionLevel.NoCompression); + + IZipArchiveEntry entry = archive.Entries.Single(); + entry.FullName.Should().Be("foo/bar.txt"); + + entry.ExtractToFile("test.txt"); + FileSystem.File.ReadAllText("test.txt").Should().Be("FooFooFoo"); + } + + [SkippableTheory] + [AutoData] + public void ExtractToDirectory_DestinationNull_ShouldThrowArgumentNullException( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); + + Exception? exception = Record.Exception(() => + { + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + archive.ExtractToDirectory(null!); + }); + + exception.Should().BeOfType() + .Which.ParamName.Should().Be("destinationDirectoryName"); + } + +#if FEATURE_COMPRESSION_ADVANCED + [SkippableTheory] + [AutoData] + public void + ExtractToDirectory_DestinationNull_WithOverwrite_ShouldThrowArgumentNullException( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); + + Exception? exception = Record.Exception(() => + { + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + archive.ExtractToDirectory(null!, true); + }); + + exception.Should().BeOfType(); + } +#endif + + [SkippableFact] + public void ExtractToDirectory_WithoutOverwrite_ShouldThrowIOException() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar").Initialized(s => s + .WithFile("foo.txt")); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + Exception? exception = Record.Exception(() => + { + archive.ExtractToDirectory("bar"); + }); + + exception.Should().BeOfType() + .Which.Message.Should() + .Contain($"'{FileSystem.Path.GetFullPath("bar/foo.txt")}'"); + FileSystem.File.ReadAllText("bar/foo.txt") + .Should().NotBe("FooFooFoo"); + } + + [SkippableFact] + public void ExtractToDirectory_ShouldExtractFilesAndDirectories() + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithSubdirectory("bar") + .WithFile("bar.txt")); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + archive.ExtractToDirectory("bar"); + + FileSystem.File.ReadAllText("bar/foo.txt") + .Should().Be("FooFooFoo"); + FileSystem.Directory.Exists("bar/bar") + .Should().BeTrue(); + FileSystem.File.Exists("bar/bar.txt") + .Should().BeTrue(); + } + +#if FEATURE_COMPRESSION_ADVANCED + [SkippableFact] + public void ExtractToDirectory_WithOverwrite_ShouldOverwriteExistingFile() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar").Initialized(s => s + .WithFile("foo.txt")); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + archive.ExtractToDirectory("bar", true); + + FileSystem.File.ReadAllText("bar/foo.txt") + .Should().Be("FooFooFoo"); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.cs b/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.cs index 318e6d052..7c5b9032e 100644 --- a/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.cs +++ b/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ZipArchiveTests.cs @@ -1,84 +1,84 @@ -using System.IO.Compression; -#if FEATURE_ZIPFILE_NET7 -#endif - -namespace Testably.Abstractions.Compression.Tests.ZipArchive; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ZipArchiveTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ -#if FEATURE_ZIPFILE_NET7 - [SkippableTheory] - [AutoData] - public void Comment_ShouldBeSettable(string comment) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - archive.Comment = comment; - - archive.Comment.Should().Be(comment); - } - - [SkippableFact] - public void Comment_ShouldBeInitializedEmpty() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - archive.Comment.Should().Be(""); - } -#endif - - [SkippableTheory] - [AutoData] - public void FileSystemExtension_ShouldBeSet( - CompressionLevel compressionLevel) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); - - using IZipArchive archive = - FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); - - archive.FileSystem.Should().Be(FileSystem); - } - - [SkippableFact] - public void GetEntry_WhenNameIsNotFound_ShouldReturnNull() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - true); - - using IZipArchive archive = - FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); - - archive.GetEntry("bar.txt").Should().BeNull(); - archive.GetEntry("foo.txt").Should().BeNull(); - archive.GetEntry("foo/foo.txt").Should().NotBeNull(); - } -} +using System.IO.Compression; +#if FEATURE_ZIPFILE_NET7 +#endif + +namespace Testably.Abstractions.Compression.Tests.ZipArchive; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ZipArchiveTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ +#if FEATURE_ZIPFILE_NET7 + [SkippableTheory] + [AutoData] + public void Comment_ShouldBeSettable(string comment) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + archive.Comment = comment; + + archive.Comment.Should().Be(comment); + } + + [SkippableFact] + public void Comment_ShouldBeInitializedEmpty() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + archive.Comment.Should().Be(""); + } +#endif + + [SkippableTheory] + [AutoData] + public void FileSystemExtension_ShouldBeSet( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); + + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + archive.FileSystem.Should().Be(FileSystem); + } + + [SkippableFact] + public void GetEntry_WhenNameIsNotFound_ShouldReturnNull() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + true); + + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + archive.GetEntry("bar.txt").Should().BeNull(); + archive.GetEntry("foo.txt").Should().BeNull(); + archive.GetEntry("foo/foo.txt").Should().NotBeNull(); + } +} diff --git a/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.Extensions.cs b/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.Extensions.cs index 4753ef5f3..374e5dbe6 100644 --- a/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.Extensions.cs +++ b/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.Extensions.cs @@ -1,154 +1,154 @@ -using System.IO; -using System.IO.Compression; -using System.Linq; - -namespace Testably.Abstractions.Compression.Tests.ZipArchiveEntry; - -public abstract partial class ZipArchiveEntryTests - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void ExtractToFile_DestinationNull_ShouldThrowArgumentNullException( - CompressionLevel compressionLevel) - { - FileSystem.Initialize() - .WithSubdirectory("foo").Initialized(s => s - .WithAFile()); - - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); - - Exception? exception = Record.Exception(() => - { - using IZipArchive archive = - FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); - - archive.Entries.Single().ExtractToFile(null!); - }); - - exception.Should().BeOfType() - .Which.ParamName.Should().Be("destinationFileName"); - } - - [SkippableTheory] - [AutoData] - public void - ExtractToFile_DestinationNull_WithOverwrite_ShouldThrowArgumentNullException( - CompressionLevel compressionLevel) - { - FileSystem.Initialize() - .WithSubdirectory("foo").Initialized(s => s - .WithAFile()); - - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); - - Exception? exception = Record.Exception(() => - { - using IZipArchive archive = - FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); - - archive.Entries.Single().ExtractToFile(null!, true); - }); - - exception.Should().BeOfType(); - } - - [SkippableFact] - public void ExtractToFile_WithoutOverwrite_ShouldThrowIOException() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar").Initialized(s => s - .WithFile("bar.txt")); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - IZipArchiveEntry entry = archive.Entries.Single(); - - Exception? exception = Record.Exception(() => - { - entry.ExtractToFile("bar/bar.txt"); - }); - - exception.Should().BeOfType() - .Which.Message.Should() - .Contain($"'{FileSystem.Path.GetFullPath("bar/bar.txt")}'"); - FileSystem.File.ReadAllText("bar/bar.txt") - .Should().NotBe("FooFooFoo"); - } - - [SkippableFact] - public void ExtractToFile_WithOverwrite_ShouldOverwriteExistingFile() - { - FileSystem.Initialize() - .WithSubdirectory("foo") - .WithSubdirectory("bar").Initialized(s => s - .WithFile("bar.txt")); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - IZipArchiveEntry entry = archive.Entries.Single(); - - entry.ExtractToFile("bar/bar.txt", true); - - FileSystem.File.ReadAllText("bar/bar.txt") - .Should().Be("FooFooFoo"); - } - - [SkippableFact] - public void ExtractToFile_IncorrectEntryType_ShouldThrowIOException() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo.txt", "some content"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - archive.CreateEntryFromFile("foo.txt", "foo/"); - archive.Dispose(); - - using FileSystemStream stream2 = FileSystem.File.OpenRead("destination.zip"); - IZipArchive archive2 = FileSystem.ZipArchive().New(stream2, ZipArchiveMode.Read); - - Exception? exception = Record.Exception(() => - { - archive2.ExtractToDirectory("bar"); - }); - - exception.Should().BeOfType(); - } - - [SkippableFact] - public void - ExtractToFile_AccessLengthOnWritableStream_ShouldThrowInvalidOperationException() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo.txt", "some content"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - archive.CreateEntryFromFile("foo.txt", "foo/"); - - Exception? exception = Record.Exception(() => - { - archive.ExtractToDirectory("bar"); - }); - - exception.Should().BeOfType(); - } -} +using System.IO; +using System.IO.Compression; +using System.Linq; + +namespace Testably.Abstractions.Compression.Tests.ZipArchiveEntry; + +public abstract partial class ZipArchiveEntryTests + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void ExtractToFile_DestinationNull_ShouldThrowArgumentNullException( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithAFile()); + + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); + + Exception? exception = Record.Exception(() => + { + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + archive.Entries.Single().ExtractToFile(null!); + }); + + exception.Should().BeOfType() + .Which.ParamName.Should().Be("destinationFileName"); + } + + [SkippableTheory] + [AutoData] + public void + ExtractToFile_DestinationNull_WithOverwrite_ShouldThrowArgumentNullException( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithAFile()); + + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); + + Exception? exception = Record.Exception(() => + { + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + archive.Entries.Single().ExtractToFile(null!, true); + }); + + exception.Should().BeOfType(); + } + + [SkippableFact] + public void ExtractToFile_WithoutOverwrite_ShouldThrowIOException() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar").Initialized(s => s + .WithFile("bar.txt")); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + IZipArchiveEntry entry = archive.Entries.Single(); + + Exception? exception = Record.Exception(() => + { + entry.ExtractToFile("bar/bar.txt"); + }); + + exception.Should().BeOfType() + .Which.Message.Should() + .Contain($"'{FileSystem.Path.GetFullPath("bar/bar.txt")}'"); + FileSystem.File.ReadAllText("bar/bar.txt") + .Should().NotBe("FooFooFoo"); + } + + [SkippableFact] + public void ExtractToFile_WithOverwrite_ShouldOverwriteExistingFile() + { + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar").Initialized(s => s + .WithFile("bar.txt")); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + IZipArchiveEntry entry = archive.Entries.Single(); + + entry.ExtractToFile("bar/bar.txt", true); + + FileSystem.File.ReadAllText("bar/bar.txt") + .Should().Be("FooFooFoo"); + } + + [SkippableFact] + public void ExtractToFile_IncorrectEntryType_ShouldThrowIOException() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo.txt", "some content"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + archive.CreateEntryFromFile("foo.txt", "foo/"); + archive.Dispose(); + + using FileSystemStream stream2 = FileSystem.File.OpenRead("destination.zip"); + IZipArchive archive2 = FileSystem.ZipArchive().New(stream2, ZipArchiveMode.Read); + + Exception? exception = Record.Exception(() => + { + archive2.ExtractToDirectory("bar"); + }); + + exception.Should().BeOfType(); + } + + [SkippableFact] + public void + ExtractToFile_AccessLengthOnWritableStream_ShouldThrowInvalidOperationException() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo.txt", "some content"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + archive.CreateEntryFromFile("foo.txt", "foo/"); + + Exception? exception = Record.Exception(() => + { + archive.ExtractToDirectory("bar"); + }); + + exception.Should().BeOfType(); + } +} diff --git a/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.cs b/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.cs index 087840d23..3ee2a869f 100644 --- a/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.cs +++ b/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ZipArchiveEntryTests.cs @@ -1,248 +1,248 @@ -using System.IO; -using System.IO.Compression; -using System.Linq; - -namespace Testably.Abstractions.Compression.Tests.ZipArchiveEntry; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ZipArchiveEntryTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableFact] - public void Archive_ShouldBeSetToArchive() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - IZipArchiveEntry entry = archive.Entries.Single(); - - entry.Archive.Should().Be(archive); - } - - [SkippableFact] - public void CompressedLength_WithNoCompression_ShouldBeFileLength() - { - Skip.If(Test.IsNetFramework, "Test is brittle on .NET Framework."); - - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - archive.Entries.Single().Length.Should().Be(9); - archive.Entries.Single().CompressedLength.Should().Be(9); - } - -#if FEATURE_ZIPFILE_NET7 - [SkippableTheory] - [AutoData] - public void Comment_ShouldBeSettable(string comment) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - IZipArchiveEntry entry = archive.Entries.Single(); - - entry.Comment = comment; - - entry.Comment.Should().Be(comment); - } - - [SkippableFact] - public void Comment_ShouldBeInitializedEmpty() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - IZipArchiveEntry entry = archive.Entries.Single(); - - entry.Comment.Should().Be(""); - } -#endif - - [SkippableFact] - public void CompressedLength_WithOptimalCompressionLevel_ShouldBeLessThanFileLength() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.Optimal, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - archive.Entries.Single().Length.Should().Be(9); - archive.Entries.Single().CompressedLength.Should().BeLessThan(9); - } - -#if FEATURE_COMPRESSION_ADVANCED - [SkippableFact] - public void Crc32_ShouldBeCalculatedFromTheFileContent() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); - FileSystem.File.WriteAllText("foo/foo2.txt", "Some other text"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - IZipArchiveEntry entry1 = archive.Entries[0]; - IZipArchiveEntry entry2 = archive.Entries[1]; - - entry1.Crc32.Should().NotBe(entry2.Crc32); - } -#endif - -#if FEATURE_COMPRESSION_ADVANCED - [SkippableTheory] - [AutoData] - public void ExternalAttributes_ShouldBeSettable(int externalAttributes) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); - FileSystem.File.WriteAllText("foo/foo2.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - IZipArchiveEntry entry1 = archive.Entries[0]; - IZipArchiveEntry entry2 = archive.Entries[1]; - - entry1.ExternalAttributes = externalAttributes; - entry1.ExternalAttributes.Should().Be(externalAttributes); - entry2.ExternalAttributes.Should().NotBe(externalAttributes); - } -#endif - - [SkippableFact] - public void FileSystemExtension_ShouldBeSet() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - IZipArchiveEntry entry = archive.Entries.Single(); - - entry.FileSystem.Should().Be(FileSystem); - } - - [SkippableFact] - public void FullName_ShouldIncludeDirectory() - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - true); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - IZipArchiveEntry entry = archive.Entries.Single(); - - entry.FullName.Should().Be("foo/foo.txt"); - entry.Name.Should().Be("foo.txt"); - } - - [SkippableTheory] - [AutoData] - public void LastWriteTime_ReadOnlyArchive_ShouldThrowNotSupportedException( - DateTime lastWriteTime) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); - FileSystem.File.WriteAllText("foo/foo2.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); - - IZipArchiveEntry entry1 = archive.Entries[0]; - Exception? exception = Record.Exception(() => - { - entry1.LastWriteTime = lastWriteTime; - }); - - exception.Should().BeOfType(); - } - - [SkippableTheory] - [AutoData] - public void LastWriteTime_ShouldBeSettable(DateTime lastWriteTime) - { - FileSystem.Initialize() - .WithSubdirectory("foo"); - FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); - FileSystem.File.WriteAllText("foo/foo2.txt", "FooFooFoo"); - FileSystem.ZipFile() - .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, - false); - - using FileSystemStream stream = FileSystem.File.Open("destination.zip", - FileMode.Open, FileAccess.ReadWrite); - - IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); - - IZipArchiveEntry entry1 = archive.Entries[0]; - IZipArchiveEntry entry2 = archive.Entries[1]; - - entry1.LastWriteTime = lastWriteTime; - entry1.LastWriteTime.Should().Be(lastWriteTime); - entry2.LastWriteTime.Should().NotBe(lastWriteTime); - } -} +using System.IO; +using System.IO.Compression; +using System.Linq; + +namespace Testably.Abstractions.Compression.Tests.ZipArchiveEntry; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ZipArchiveEntryTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableFact] + public void Archive_ShouldBeSetToArchive() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + IZipArchiveEntry entry = archive.Entries.Single(); + + entry.Archive.Should().Be(archive); + } + + [SkippableFact] + public void CompressedLength_WithNoCompression_ShouldBeFileLength() + { + Skip.If(Test.IsNetFramework, "Test is brittle on .NET Framework."); + + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + archive.Entries.Single().Length.Should().Be(9); + archive.Entries.Single().CompressedLength.Should().Be(9); + } + +#if FEATURE_ZIPFILE_NET7 + [SkippableTheory] + [AutoData] + public void Comment_ShouldBeSettable(string comment) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + IZipArchiveEntry entry = archive.Entries.Single(); + + entry.Comment = comment; + + entry.Comment.Should().Be(comment); + } + + [SkippableFact] + public void Comment_ShouldBeInitializedEmpty() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + IZipArchiveEntry entry = archive.Entries.Single(); + + entry.Comment.Should().Be(""); + } +#endif + + [SkippableFact] + public void CompressedLength_WithOptimalCompressionLevel_ShouldBeLessThanFileLength() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.Optimal, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + archive.Entries.Single().Length.Should().Be(9); + archive.Entries.Single().CompressedLength.Should().BeLessThan(9); + } + +#if FEATURE_COMPRESSION_ADVANCED + [SkippableFact] + public void Crc32_ShouldBeCalculatedFromTheFileContent() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); + FileSystem.File.WriteAllText("foo/foo2.txt", "Some other text"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + IZipArchiveEntry entry1 = archive.Entries[0]; + IZipArchiveEntry entry2 = archive.Entries[1]; + + entry1.Crc32.Should().NotBe(entry2.Crc32); + } +#endif + +#if FEATURE_COMPRESSION_ADVANCED + [SkippableTheory] + [AutoData] + public void ExternalAttributes_ShouldBeSettable(int externalAttributes) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); + FileSystem.File.WriteAllText("foo/foo2.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + IZipArchiveEntry entry1 = archive.Entries[0]; + IZipArchiveEntry entry2 = archive.Entries[1]; + + entry1.ExternalAttributes = externalAttributes; + entry1.ExternalAttributes.Should().Be(externalAttributes); + entry2.ExternalAttributes.Should().NotBe(externalAttributes); + } +#endif + + [SkippableFact] + public void FileSystemExtension_ShouldBeSet() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + IZipArchiveEntry entry = archive.Entries.Single(); + + entry.FileSystem.Should().Be(FileSystem); + } + + [SkippableFact] + public void FullName_ShouldIncludeDirectory() + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + true); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + IZipArchiveEntry entry = archive.Entries.Single(); + + entry.FullName.Should().Be("foo/foo.txt"); + entry.Name.Should().Be("foo.txt"); + } + + [SkippableTheory] + [AutoData] + public void LastWriteTime_ReadOnlyArchive_ShouldThrowNotSupportedException( + DateTime lastWriteTime) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); + FileSystem.File.WriteAllText("foo/foo2.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.OpenRead("destination.zip"); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + IZipArchiveEntry entry1 = archive.Entries[0]; + Exception? exception = Record.Exception(() => + { + entry1.LastWriteTime = lastWriteTime; + }); + + exception.Should().BeOfType(); + } + + [SkippableTheory] + [AutoData] + public void LastWriteTime_ShouldBeSettable(DateTime lastWriteTime) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + FileSystem.File.WriteAllText("foo/foo1.txt", "FooFooFoo"); + FileSystem.File.WriteAllText("foo/foo2.txt", "FooFooFoo"); + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", CompressionLevel.NoCompression, + false); + + using FileSystemStream stream = FileSystem.File.Open("destination.zip", + FileMode.Open, FileAccess.ReadWrite); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + + IZipArchiveEntry entry1 = archive.Entries[0]; + IZipArchiveEntry entry2 = archive.Entries[1]; + + entry1.LastWriteTime = lastWriteTime; + entry1.LastWriteTime.Should().Be(lastWriteTime); + entry2.LastWriteTime.Should().NotBe(lastWriteTime); + } +} diff --git a/Tests/Testably.Abstractions.Parity.Tests/ParityTests.cs b/Tests/Testably.Abstractions.Parity.Tests/ParityTests.cs index bc86f1f01..b2c922199 100644 --- a/Tests/Testably.Abstractions.Parity.Tests/ParityTests.cs +++ b/Tests/Testably.Abstractions.Parity.Tests/ParityTests.cs @@ -1,196 +1,196 @@ -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using Testably.Abstractions.RandomSystem; -using Xunit.Abstractions; - -namespace Testably.Abstractions.Parity.Tests; - -public abstract class ParityTests -{ - #region Test Setup - - public TestHelpers.Parity Parity { get; } - - private readonly ITestOutputHelper _testOutputHelper; - - protected ParityTests(TestHelpers.Parity parity, - ITestOutputHelper testOutputHelper) - { - Parity = parity; - _testOutputHelper = testOutputHelper; - } - - #endregion - - [Fact] - public void IDirectory_EnsureParityWith_Directory() - { - List parityErrors = Parity.Directory - .GetErrorsToStaticType( - typeof(Directory), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IDirectoryInfoAndIDirectoryInfoFactory_EnsureParityWith_DirectoryInfo() - { - List parityErrors = Parity.DirectoryInfo - .GetErrorsToInstanceType( - typeof(DirectoryInfo), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IDriveInfoAndIDriveInfoFactory_EnsureParityWith_DriveInfo() - { - List parityErrors = Parity.Drive - .GetErrorsToInstanceType( - typeof(DriveInfo), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IFile_EnsureParityWith_File() - { - List parityErrors = Parity.File - .GetErrorsToStaticType( - typeof(File), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IFileInfoAndIFileInfoFactory_EnsureParityWith_FileInfo() - { - List parityErrors = Parity.FileInfo - .GetErrorsToInstanceType( - typeof(FileInfo), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IFileSystemInfo_EnsureParityWith_FileSystemInfo() - { - List parityErrors = Parity.FileSystemInfo - .GetErrorsToInstanceType( - typeof(FileSystemInfo), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void - IFileSystemWatcherAndIFileSystemWatcherFactory_EnsureParityWith_FileSystemWatcher() - { - List parityErrors = Parity.FileSystemWatcher - .GetErrorsToInstanceType( - typeof(FileSystemWatcher), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IGuid_EnsureParityWith_Guid() - { - List parityErrors = Parity.Guid - .GetErrorsToStaticType( - typeof(Guid), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IPath_EnsureParityWith_Path() - { - List parityErrors = Parity.Path - .GetErrorsToStaticType( - typeof(Path), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IRandomAndIRandomFactory_EnsureParityWith_Random() - { - List parityErrors = Parity.Random - .GetErrorsToInstanceType( - typeof(Random), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IZipArchive_EnsureParityWith_ZipArchive() - { - List parityErrors = Parity.ZipArchive - .GetErrorsToInstanceType( - typeof(ZipArchive), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IZipArchive_EnsureParityWith_ZipFileExtensions() - { - List parityErrors = Parity.ZipArchive - .GetErrorsToExtensionMethods( - typeof(ZipFileExtensions), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IZipArchiveEntry_EnsureParityWith_ZipArchiveEntry() - { - List parityErrors = Parity.ZipArchiveEntry - .GetErrorsToInstanceType( - typeof(ZipArchiveEntry), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IZipArchiveEntry_EnsureParityWith_ZipFileExtensions() - { - List parityErrors = Parity.ZipArchiveEntry - .GetErrorsToExtensionMethods( - typeof(ZipFileExtensions), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } - - [Fact] - public void IZipFile_EnsureParityWith_ZipFile() - { - List parityErrors = Parity.ZipFile - .GetErrorsToStaticType( - typeof(ZipFile), - _testOutputHelper); - - parityErrors.Should().BeEmpty(); - } -} +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using Testably.Abstractions.RandomSystem; +using Xunit.Abstractions; + +namespace Testably.Abstractions.Parity.Tests; + +public abstract class ParityTests +{ + #region Test Setup + + public TestHelpers.Parity Parity { get; } + + private readonly ITestOutputHelper _testOutputHelper; + + protected ParityTests(TestHelpers.Parity parity, + ITestOutputHelper testOutputHelper) + { + Parity = parity; + _testOutputHelper = testOutputHelper; + } + + #endregion + + [Fact] + public void IDirectory_EnsureParityWith_Directory() + { + List parityErrors = Parity.Directory + .GetErrorsToStaticType( + typeof(Directory), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IDirectoryInfoAndIDirectoryInfoFactory_EnsureParityWith_DirectoryInfo() + { + List parityErrors = Parity.DirectoryInfo + .GetErrorsToInstanceType( + typeof(DirectoryInfo), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IDriveInfoAndIDriveInfoFactory_EnsureParityWith_DriveInfo() + { + List parityErrors = Parity.Drive + .GetErrorsToInstanceType( + typeof(DriveInfo), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IFile_EnsureParityWith_File() + { + List parityErrors = Parity.File + .GetErrorsToStaticType( + typeof(File), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IFileInfoAndIFileInfoFactory_EnsureParityWith_FileInfo() + { + List parityErrors = Parity.FileInfo + .GetErrorsToInstanceType( + typeof(FileInfo), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IFileSystemInfo_EnsureParityWith_FileSystemInfo() + { + List parityErrors = Parity.FileSystemInfo + .GetErrorsToInstanceType( + typeof(FileSystemInfo), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void + IFileSystemWatcherAndIFileSystemWatcherFactory_EnsureParityWith_FileSystemWatcher() + { + List parityErrors = Parity.FileSystemWatcher + .GetErrorsToInstanceType( + typeof(FileSystemWatcher), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IGuid_EnsureParityWith_Guid() + { + List parityErrors = Parity.Guid + .GetErrorsToStaticType( + typeof(Guid), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IPath_EnsureParityWith_Path() + { + List parityErrors = Parity.Path + .GetErrorsToStaticType( + typeof(Path), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IRandomAndIRandomFactory_EnsureParityWith_Random() + { + List parityErrors = Parity.Random + .GetErrorsToInstanceType( + typeof(Random), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IZipArchive_EnsureParityWith_ZipArchive() + { + List parityErrors = Parity.ZipArchive + .GetErrorsToInstanceType( + typeof(ZipArchive), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IZipArchive_EnsureParityWith_ZipFileExtensions() + { + List parityErrors = Parity.ZipArchive + .GetErrorsToExtensionMethods( + typeof(ZipFileExtensions), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IZipArchiveEntry_EnsureParityWith_ZipArchiveEntry() + { + List parityErrors = Parity.ZipArchiveEntry + .GetErrorsToInstanceType( + typeof(ZipArchiveEntry), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IZipArchiveEntry_EnsureParityWith_ZipFileExtensions() + { + List parityErrors = Parity.ZipArchiveEntry + .GetErrorsToExtensionMethods( + typeof(ZipFileExtensions), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } + + [Fact] + public void IZipFile_EnsureParityWith_ZipFile() + { + List parityErrors = Parity.ZipFile + .GetErrorsToStaticType( + typeof(ZipFile), + _testOutputHelper); + + parityErrors.Should().BeEmpty(); + } +} diff --git a/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/Parity.cs b/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/Parity.cs index a49b4b08e..014bd789e 100644 --- a/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/Parity.cs +++ b/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/Parity.cs @@ -1,73 +1,73 @@ -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.IO; -using System.IO.Compression; - -namespace Testably.Abstractions.Parity.Tests.TestHelpers; - -public class Parity -{ - public static readonly ReadOnlyDictionary AcceptedTypeMapping = new( - new Dictionary - { - { - nameof(FileStream), nameof(FileSystemStream) - } - }); - - public ParityCheck Directory { get; } = new(); - - public ParityCheck DirectoryInfo { get; } = new(excludeMethods: new[] - { - typeof(DirectoryInfo).GetMethod(nameof(System.IO.DirectoryInfo - .GetObjectData)), - typeof(DirectoryInfo).GetMethod(nameof(System.IO.DirectoryInfo.ToString)) - }); - - public ParityCheck Drive { get; } = new(excludeMethods: new[] - { - typeof(DriveInfo).GetMethod(nameof(DriveInfo.ToString)) - }); - - public ParityCheck File { get; } = new(); - - public ParityCheck FileInfo { get; } = new(excludeMethods: new[] - { - typeof(FileInfo).GetMethod(nameof(System.IO.FileInfo.GetObjectData)), - typeof(FileInfo).GetMethod(nameof(System.IO.FileInfo.ToString)) - }); - - public ParityCheck FileStream { get; } = new(); - - public ParityCheck FileSystemInfo { get; } = new(excludeMethods: new[] - { - typeof(FileSystemInfo).GetMethod( - nameof(System.IO.FileSystemInfo.GetObjectData)), - typeof(FileSystemInfo).GetMethod(nameof(ToString)) - }); - - public ParityCheck FileSystemWatcher { get; } = new(excludeMethods: new[] - { - typeof(FileSystemWatcher).GetMethod( - nameof(System.IO.FileSystemWatcher.ToString)) - }); - - public ParityCheck Guid { get; } = new(); - - public ParityCheck Path { get; } = new(excludeFields: new[] - { - #pragma warning disable CS0618 - typeof(Path).GetField(nameof(System.IO.Path.InvalidPathChars)) - #pragma warning restore CS0618 - }); - - public ParityCheck Random { get; } = new(); - public ParityCheck ZipArchive { get; } = new(); - - public ParityCheck ZipArchiveEntry { get; } = new(excludeMethods: new[] - { - typeof(ZipArchiveEntry).GetMethod(nameof(ToString)) - }); - - public ParityCheck ZipFile { get; } = new(); -} +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.IO.Compression; + +namespace Testably.Abstractions.Parity.Tests.TestHelpers; + +public class Parity +{ + public static readonly ReadOnlyDictionary AcceptedTypeMapping = new( + new Dictionary + { + { + nameof(FileStream), nameof(FileSystemStream) + } + }); + + public ParityCheck Directory { get; } = new(); + + public ParityCheck DirectoryInfo { get; } = new(excludeMethods: new[] + { + typeof(DirectoryInfo).GetMethod(nameof(System.IO.DirectoryInfo + .GetObjectData)), + typeof(DirectoryInfo).GetMethod(nameof(System.IO.DirectoryInfo.ToString)) + }); + + public ParityCheck Drive { get; } = new(excludeMethods: new[] + { + typeof(DriveInfo).GetMethod(nameof(DriveInfo.ToString)) + }); + + public ParityCheck File { get; } = new(); + + public ParityCheck FileInfo { get; } = new(excludeMethods: new[] + { + typeof(FileInfo).GetMethod(nameof(System.IO.FileInfo.GetObjectData)), + typeof(FileInfo).GetMethod(nameof(System.IO.FileInfo.ToString)) + }); + + public ParityCheck FileStream { get; } = new(); + + public ParityCheck FileSystemInfo { get; } = new(excludeMethods: new[] + { + typeof(FileSystemInfo).GetMethod( + nameof(System.IO.FileSystemInfo.GetObjectData)), + typeof(FileSystemInfo).GetMethod(nameof(ToString)) + }); + + public ParityCheck FileSystemWatcher { get; } = new(excludeMethods: new[] + { + typeof(FileSystemWatcher).GetMethod( + nameof(System.IO.FileSystemWatcher.ToString)) + }); + + public ParityCheck Guid { get; } = new(); + + public ParityCheck Path { get; } = new(excludeFields: new[] + { + #pragma warning disable CS0618 + typeof(Path).GetField(nameof(System.IO.Path.InvalidPathChars)) + #pragma warning restore CS0618 + }); + + public ParityCheck Random { get; } = new(); + public ParityCheck ZipArchive { get; } = new(); + + public ParityCheck ZipArchiveEntry { get; } = new(excludeMethods: new[] + { + typeof(ZipArchiveEntry).GetMethod(nameof(ToString)) + }); + + public ParityCheck ZipFile { get; } = new(); +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/FileSystem/DriveInfoMockTests.cs b/Tests/Testably.Abstractions.Testing.Tests/FileSystem/DriveInfoMockTests.cs index 0d000f451..ec4ebd297 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/FileSystem/DriveInfoMockTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/FileSystem/DriveInfoMockTests.cs @@ -1,284 +1,284 @@ -using System.IO; -using System.Linq; -using System.Text; -using Testably.Abstractions.Testing.FileSystem; - -namespace Testably.Abstractions.Testing.Tests.FileSystem; - -public class DriveInfoMockTests -{ - #region Test Setup - - public MockFileSystem FileSystem { get; } - - public DriveInfoMockTests() - { - FileSystem = new MockFileSystem(); - } - - #endregion - - [SkippableTheory] - [AutoData] - public void AvailableFreeSpace_CannotGetNegative(long size) - { - FileSystem.WithDrive(d => d.SetTotalSize(size)); - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - - FileSystem.WithDrive(d => d.ChangeUsedBytes(-1)); - - drive.AvailableFreeSpace.Should().Be(size); - } - - [SkippableTheory] - [AutoData] - public void AvailableFreeSpace_NotEnoughSpace_ShouldThrowIOException( - int fileSize, string path) - { - byte[] bytes = new byte[fileSize]; - FileSystem.WithDrive(d => d.SetTotalSize(fileSize - 1)); - FileSystem.RandomSystem.Random.Shared.NextBytes(bytes); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.WriteAllBytes(path, bytes); - }); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - exception.Should().BeOfType() - .Which.Message.Should().Contain($"'{drive.Name}'"); - drive.AvailableFreeSpace.Should().Be(fileSize - 1); - } - - [SkippableTheory] - [AutoData] - public void AvailableFreeSpace_ShouldBeChangedWhenAppendingToAFile( - string fileContent1, string fileContent2, int expectedRemainingBytes, - string path, Encoding encoding) - { - int fileSize1 = encoding.GetPreamble().Length + encoding.GetBytes(fileContent1).Length; - int fileSize2 = encoding.GetBytes(fileContent2).Length; - FileSystem.WithDrive(d - => d.SetTotalSize(fileSize1 + fileSize2 + expectedRemainingBytes)); - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - - FileSystem.File.WriteAllText(path, fileContent1, encoding); - drive.AvailableFreeSpace.Should().Be(expectedRemainingBytes + fileSize2); - FileSystem.File.AppendAllText(path, fileContent2, encoding); - - drive.AvailableFreeSpace.Should().Be(expectedRemainingBytes); - } - - [SkippableTheory] - [InlineAutoData(0)] - [InlineAutoData(1)] - [InlineAutoData(10)] - public void AvailableFreeSpace_ShouldBeChangedWhenWorkingWithStreams( - int reduceLength, string path, string previousContent) - { - FileSystem.File.WriteAllText(path, previousContent); - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - long previousFreeSpace = drive.AvailableFreeSpace; - - FileSystemStream stream = FileSystem.File.OpenWrite(path); - using (StreamWriter streamWriter = new(stream)) - { - streamWriter.Write("new-content"); - stream.SetLength(stream.Length - reduceLength); - } - - drive.AvailableFreeSpace.Should().Be(previousFreeSpace + reduceLength); - } - - [SkippableTheory] - [AutoData] - public void AvailableFreeSpace_ShouldBeReducedByWritingToFile( - int fileSize, string path) - { - byte[] bytes = new byte[fileSize]; - FileSystem.WithDrive(d => d.SetTotalSize(fileSize)); - FileSystem.RandomSystem.Random.Shared.NextBytes(bytes); - - FileSystem.File.WriteAllBytes(path, bytes); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - - drive.AvailableFreeSpace.Should().Be(0); - } - - [SkippableTheory] - [AutoData] - public void AvailableFreeSpace_ShouldBeReleasedWhenDeletingAFile( - int fileSize, string path) - { - byte[] bytes = new byte[fileSize]; - FileSystem.WithDrive(d => d.SetTotalSize(fileSize)); - FileSystem.RandomSystem.Random.Shared.NextBytes(bytes); - - FileSystem.File.WriteAllBytes(path, bytes); - FileSystem.File.Delete(path); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - - drive.AvailableFreeSpace.Should().Be(fileSize); - } - - [SkippableTheory] - [AutoData] - public void AvailableFreeSpace_ShouldBeSetTotalSize(long size) - { - FileSystem.WithDrive(d => d.SetTotalSize(size)); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - - drive.AvailableFreeSpace.Should().Be(size); - } - - [SkippableTheory] - [InlineData(@"//foo", @"//foo")] - [InlineData(@"//foo/bar", @"//foo")] - [InlineData(@"//foo/bar/xyz", @"//foo")] - public void New_DriveNameWithUncPath_ShouldUseTopMostDirectory( - string driveName, string expectedName) - { - expectedName = expectedName - .Replace('/', FileSystem.Path.DirectorySeparatorChar); - - IDriveInfo drive = - DriveInfoMock.New(driveName, FileSystem); - - drive.Name.Should().Be(expectedName); - } - - [SkippableTheory] - [InlineData("foo")] - public void New_InvalidDriveName_ShouldThrowArgumentException(string driveName) - { - Exception? exception = Record.Exception(() => - { - DriveInfoMock.New(driveName, FileSystem); - }); - - exception.Should().BeOfType(); - } - - [SkippableFact] - public void New_Null_ShouldReturnNull() - { - IDriveInfo? drive = - DriveInfoMock.New(null, FileSystem); - - drive.Should().BeNull(); - } - - [SkippableFact] - public void New_UncPath_ShouldSetFlag() - { - IDriveInfo drive = - DriveInfoMock.New(@"//foo", FileSystem); - - (drive as DriveInfoMock)?.IsUncPath.Should().BeTrue(); - } - - [SkippableTheory] - [InlineData("C", "C:\\")] - [InlineData("d", "D:\\")] - public void New_ValidDriveName_ShouldAppendColonAndSlash( - string driveName, string expectedDriveName) - { - DriveInfoMock result = - DriveInfoMock.New(driveName, FileSystem); - - result.Name.Should().Be(expectedDriveName); - } - - [SkippableTheory] - [AutoData] - public void NotReady_AccessDirectory_ShouldThrowIOException( - string path) - { - FileSystem.WithDrive(d => d.SetIsReady(false)); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.CreateDirectory(path); - }); - - exception.Should().BeOfType(); - } - - [SkippableTheory] - [AutoData] - public void NotReady_AccessFile_ShouldThrowIOException( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - FileSystem.WithDrive(d => d.SetIsReady(false)); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.ReadAllText(path); - }); - - exception.Should().BeOfType(); - } - - [SkippableFact] - public void SetDriveFormat_Default_ShouldBeNTFS() - { - FileSystem.WithDrive(d => d.SetDriveFormat()); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - drive.DriveFormat.Should().Be("NTFS"); - } - - [SkippableTheory] - [AutoData] - public void SetDriveFormat_ShouldChangeDriveFormat(string driveFormat) - { - FileSystem.WithDrive(d => d.SetDriveFormat(driveFormat)); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - drive.DriveFormat.Should().Be(driveFormat); - } - - [SkippableFact] - public void SetDriveType_Default_ShouldBeFixed() - { - FileSystem.WithDrive(d => d.SetDriveType()); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - drive.DriveType.Should().Be(DriveType.Fixed); - } - - [SkippableTheory] - [AutoData] - public void SetDriveType_ShouldChangeDriveType(DriveType driveType) - { - FileSystem.WithDrive(d => d.SetDriveType(driveType)); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - drive.DriveType.Should().Be(driveType); - } - - [SkippableTheory] - [InlineData(true)] - [InlineData(false)] - public void SetIsReady_ShouldChangeIsReady(bool isReady) - { - FileSystem.WithDrive(d => d.SetIsReady(isReady)); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - drive.IsReady.Should().Be(isReady); - } - - [SkippableFact] - public void SetTotalSize_Default_ShouldBe1Gigabyte() - { - FileSystem.WithDrive(d => d.SetTotalSize()); - - IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); - - drive.AvailableFreeSpace.Should().Be(1024 * 1024 * 1024); - } -} +using System.IO; +using System.Linq; +using System.Text; +using Testably.Abstractions.Testing.FileSystem; + +namespace Testably.Abstractions.Testing.Tests.FileSystem; + +public class DriveInfoMockTests +{ + #region Test Setup + + public MockFileSystem FileSystem { get; } + + public DriveInfoMockTests() + { + FileSystem = new MockFileSystem(); + } + + #endregion + + [SkippableTheory] + [AutoData] + public void AvailableFreeSpace_CannotGetNegative(long size) + { + FileSystem.WithDrive(d => d.SetTotalSize(size)); + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + + FileSystem.WithDrive(d => d.ChangeUsedBytes(-1)); + + drive.AvailableFreeSpace.Should().Be(size); + } + + [SkippableTheory] + [AutoData] + public void AvailableFreeSpace_NotEnoughSpace_ShouldThrowIOException( + int fileSize, string path) + { + byte[] bytes = new byte[fileSize]; + FileSystem.WithDrive(d => d.SetTotalSize(fileSize - 1)); + FileSystem.RandomSystem.Random.Shared.NextBytes(bytes); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.WriteAllBytes(path, bytes); + }); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + exception.Should().BeOfType() + .Which.Message.Should().Contain($"'{drive.Name}'"); + drive.AvailableFreeSpace.Should().Be(fileSize - 1); + } + + [SkippableTheory] + [AutoData] + public void AvailableFreeSpace_ShouldBeChangedWhenAppendingToAFile( + string fileContent1, string fileContent2, int expectedRemainingBytes, + string path, Encoding encoding) + { + int fileSize1 = encoding.GetPreamble().Length + encoding.GetBytes(fileContent1).Length; + int fileSize2 = encoding.GetBytes(fileContent2).Length; + FileSystem.WithDrive(d + => d.SetTotalSize(fileSize1 + fileSize2 + expectedRemainingBytes)); + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + + FileSystem.File.WriteAllText(path, fileContent1, encoding); + drive.AvailableFreeSpace.Should().Be(expectedRemainingBytes + fileSize2); + FileSystem.File.AppendAllText(path, fileContent2, encoding); + + drive.AvailableFreeSpace.Should().Be(expectedRemainingBytes); + } + + [SkippableTheory] + [InlineAutoData(0)] + [InlineAutoData(1)] + [InlineAutoData(10)] + public void AvailableFreeSpace_ShouldBeChangedWhenWorkingWithStreams( + int reduceLength, string path, string previousContent) + { + FileSystem.File.WriteAllText(path, previousContent); + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + long previousFreeSpace = drive.AvailableFreeSpace; + + FileSystemStream stream = FileSystem.File.OpenWrite(path); + using (StreamWriter streamWriter = new(stream)) + { + streamWriter.Write("new-content"); + stream.SetLength(stream.Length - reduceLength); + } + + drive.AvailableFreeSpace.Should().Be(previousFreeSpace + reduceLength); + } + + [SkippableTheory] + [AutoData] + public void AvailableFreeSpace_ShouldBeReducedByWritingToFile( + int fileSize, string path) + { + byte[] bytes = new byte[fileSize]; + FileSystem.WithDrive(d => d.SetTotalSize(fileSize)); + FileSystem.RandomSystem.Random.Shared.NextBytes(bytes); + + FileSystem.File.WriteAllBytes(path, bytes); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + + drive.AvailableFreeSpace.Should().Be(0); + } + + [SkippableTheory] + [AutoData] + public void AvailableFreeSpace_ShouldBeReleasedWhenDeletingAFile( + int fileSize, string path) + { + byte[] bytes = new byte[fileSize]; + FileSystem.WithDrive(d => d.SetTotalSize(fileSize)); + FileSystem.RandomSystem.Random.Shared.NextBytes(bytes); + + FileSystem.File.WriteAllBytes(path, bytes); + FileSystem.File.Delete(path); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + + drive.AvailableFreeSpace.Should().Be(fileSize); + } + + [SkippableTheory] + [AutoData] + public void AvailableFreeSpace_ShouldBeSetTotalSize(long size) + { + FileSystem.WithDrive(d => d.SetTotalSize(size)); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + + drive.AvailableFreeSpace.Should().Be(size); + } + + [SkippableTheory] + [InlineData(@"//foo", @"//foo")] + [InlineData(@"//foo/bar", @"//foo")] + [InlineData(@"//foo/bar/xyz", @"//foo")] + public void New_DriveNameWithUncPath_ShouldUseTopMostDirectory( + string driveName, string expectedName) + { + expectedName = expectedName + .Replace('/', FileSystem.Path.DirectorySeparatorChar); + + IDriveInfo drive = + DriveInfoMock.New(driveName, FileSystem); + + drive.Name.Should().Be(expectedName); + } + + [SkippableTheory] + [InlineData("foo")] + public void New_InvalidDriveName_ShouldThrowArgumentException(string driveName) + { + Exception? exception = Record.Exception(() => + { + DriveInfoMock.New(driveName, FileSystem); + }); + + exception.Should().BeOfType(); + } + + [SkippableFact] + public void New_Null_ShouldReturnNull() + { + IDriveInfo? drive = + DriveInfoMock.New(null, FileSystem); + + drive.Should().BeNull(); + } + + [SkippableFact] + public void New_UncPath_ShouldSetFlag() + { + IDriveInfo drive = + DriveInfoMock.New(@"//foo", FileSystem); + + (drive as DriveInfoMock)?.IsUncPath.Should().BeTrue(); + } + + [SkippableTheory] + [InlineData("C", "C:\\")] + [InlineData("d", "D:\\")] + public void New_ValidDriveName_ShouldAppendColonAndSlash( + string driveName, string expectedDriveName) + { + DriveInfoMock result = + DriveInfoMock.New(driveName, FileSystem); + + result.Name.Should().Be(expectedDriveName); + } + + [SkippableTheory] + [AutoData] + public void NotReady_AccessDirectory_ShouldThrowIOException( + string path) + { + FileSystem.WithDrive(d => d.SetIsReady(false)); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.CreateDirectory(path); + }); + + exception.Should().BeOfType(); + } + + [SkippableTheory] + [AutoData] + public void NotReady_AccessFile_ShouldThrowIOException( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + FileSystem.WithDrive(d => d.SetIsReady(false)); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.ReadAllText(path); + }); + + exception.Should().BeOfType(); + } + + [SkippableFact] + public void SetDriveFormat_Default_ShouldBeNTFS() + { + FileSystem.WithDrive(d => d.SetDriveFormat()); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + drive.DriveFormat.Should().Be("NTFS"); + } + + [SkippableTheory] + [AutoData] + public void SetDriveFormat_ShouldChangeDriveFormat(string driveFormat) + { + FileSystem.WithDrive(d => d.SetDriveFormat(driveFormat)); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + drive.DriveFormat.Should().Be(driveFormat); + } + + [SkippableFact] + public void SetDriveType_Default_ShouldBeFixed() + { + FileSystem.WithDrive(d => d.SetDriveType()); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + drive.DriveType.Should().Be(DriveType.Fixed); + } + + [SkippableTheory] + [AutoData] + public void SetDriveType_ShouldChangeDriveType(DriveType driveType) + { + FileSystem.WithDrive(d => d.SetDriveType(driveType)); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + drive.DriveType.Should().Be(driveType); + } + + [SkippableTheory] + [InlineData(true)] + [InlineData(false)] + public void SetIsReady_ShouldChangeIsReady(bool isReady) + { + FileSystem.WithDrive(d => d.SetIsReady(isReady)); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + drive.IsReady.Should().Be(isReady); + } + + [SkippableFact] + public void SetTotalSize_Default_ShouldBe1Gigabyte() + { + FileSystem.WithDrive(d => d.SetTotalSize()); + + IDriveInfo drive = FileSystem.DriveInfo.GetDrives().Single(); + + drive.AvailableFreeSpace.Should().Be(1024 * 1024 * 1024); + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherFactoryMockTests.cs b/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherFactoryMockTests.cs index bf9d84301..86ed95985 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherFactoryMockTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherFactoryMockTests.cs @@ -1,74 +1,74 @@ -using System.IO; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Testing.Tests.FileSystem; - -public sealed class FileSystemWatcherFactoryMockTests : IDisposable -{ - public string BasePath => _directoryCleaner.BasePath; - public MockFileSystem FileSystem { get; } - public RealFileSystem RealFileSystem { get; } - private readonly IDirectoryCleaner _directoryCleaner; - - public FileSystemWatcherFactoryMockTests() - { - FileSystem = new MockFileSystem(); - RealFileSystem = new RealFileSystem(); - _directoryCleaner = RealFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); - FileSystem.InitializeIn(RealFileSystem.Directory.GetCurrentDirectory()); - FileSystem.Directory.SetCurrentDirectory(RealFileSystem.Directory - .GetCurrentDirectory()); - } - - #region IDisposable Members - - /// - public void Dispose() - => _directoryCleaner.Dispose(); - - #endregion - - [SkippableTheory] - [AutoData] - public void Wrap_ShouldUsePropertiesFromFileSystemWatcher( - string path, bool includeSubdirectories, NotifyFilters notifyFilter, - int internalBufferSize, bool enableRaisingEvents, string filter) - { - FileSystem.Directory.CreateDirectory(path); - RealFileSystem.Directory.CreateDirectory(path); - FileSystemWatcher fileSystemWatcher = new(); - fileSystemWatcher.Path = path; - fileSystemWatcher.IncludeSubdirectories = includeSubdirectories; - fileSystemWatcher.NotifyFilter = notifyFilter; - fileSystemWatcher.InternalBufferSize = internalBufferSize; - fileSystemWatcher.EnableRaisingEvents = enableRaisingEvents; - fileSystemWatcher.Filter = filter; - - IFileSystemWatcher result = FileSystem.FileSystemWatcher.Wrap(fileSystemWatcher); - - result.Path.Should().Be(fileSystemWatcher.Path); - result.IncludeSubdirectories.Should().Be(fileSystemWatcher.IncludeSubdirectories); - result.NotifyFilter.Should().Be(fileSystemWatcher.NotifyFilter); - result.InternalBufferSize.Should().Be(fileSystemWatcher.InternalBufferSize); - result.EnableRaisingEvents.Should().Be(fileSystemWatcher.EnableRaisingEvents); - result.Filter.Should().Be(fileSystemWatcher.Filter); - } - -#if FEATURE_FILESYSTEMWATCHER_ADVANCED - [SkippableTheory] - [AutoData] - public void Wrap_WithFilters_ShouldUsePropertiesFromFileSystemWatcher( - string[] filters) - { - FileSystemWatcher fileSystemWatcher = new("."); - foreach (string filter in filters) - { - fileSystemWatcher.Filters.Add(filter); - } - - IFileSystemWatcher result = FileSystem.FileSystemWatcher.Wrap(fileSystemWatcher); - - result.Filters.Should().BeEquivalentTo(filters); - } -#endif -} +using System.IO; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Testing.Tests.FileSystem; + +public sealed class FileSystemWatcherFactoryMockTests : IDisposable +{ + public string BasePath => _directoryCleaner.BasePath; + public MockFileSystem FileSystem { get; } + public RealFileSystem RealFileSystem { get; } + private readonly IDirectoryCleaner _directoryCleaner; + + public FileSystemWatcherFactoryMockTests() + { + FileSystem = new MockFileSystem(); + RealFileSystem = new RealFileSystem(); + _directoryCleaner = RealFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); + FileSystem.InitializeIn(RealFileSystem.Directory.GetCurrentDirectory()); + FileSystem.Directory.SetCurrentDirectory(RealFileSystem.Directory + .GetCurrentDirectory()); + } + + #region IDisposable Members + + /// + public void Dispose() + => _directoryCleaner.Dispose(); + + #endregion + + [SkippableTheory] + [AutoData] + public void Wrap_ShouldUsePropertiesFromFileSystemWatcher( + string path, bool includeSubdirectories, NotifyFilters notifyFilter, + int internalBufferSize, bool enableRaisingEvents, string filter) + { + FileSystem.Directory.CreateDirectory(path); + RealFileSystem.Directory.CreateDirectory(path); + FileSystemWatcher fileSystemWatcher = new(); + fileSystemWatcher.Path = path; + fileSystemWatcher.IncludeSubdirectories = includeSubdirectories; + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.InternalBufferSize = internalBufferSize; + fileSystemWatcher.EnableRaisingEvents = enableRaisingEvents; + fileSystemWatcher.Filter = filter; + + IFileSystemWatcher result = FileSystem.FileSystemWatcher.Wrap(fileSystemWatcher); + + result.Path.Should().Be(fileSystemWatcher.Path); + result.IncludeSubdirectories.Should().Be(fileSystemWatcher.IncludeSubdirectories); + result.NotifyFilter.Should().Be(fileSystemWatcher.NotifyFilter); + result.InternalBufferSize.Should().Be(fileSystemWatcher.InternalBufferSize); + result.EnableRaisingEvents.Should().Be(fileSystemWatcher.EnableRaisingEvents); + result.Filter.Should().Be(fileSystemWatcher.Filter); + } + +#if FEATURE_FILESYSTEMWATCHER_ADVANCED + [SkippableTheory] + [AutoData] + public void Wrap_WithFilters_ShouldUsePropertiesFromFileSystemWatcher( + string[] filters) + { + FileSystemWatcher fileSystemWatcher = new("."); + foreach (string filter in filters) + { + fileSystemWatcher.Filters.Add(filter); + } + + IFileSystemWatcher result = FileSystem.FileSystemWatcher.Wrap(fileSystemWatcher); + + result.Filters.Should().BeEquivalentTo(filters); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherMockTests.cs b/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherMockTests.cs index eba35d186..2049185ab 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherMockTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/FileSystem/FileSystemWatcherMockTests.cs @@ -1,186 +1,186 @@ -using System.IO; -using System.Threading; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Testing.Tests.FileSystem; - -public sealed class FileSystemWatcherMockTests : IDisposable -{ - /// - /// Default number of messages before the buffer overflows is 64:
- /// internal buffer size / bytes per message = 8192 / 128 = 64 - ///
- public static readonly int DefaultMaxMessages = 64; - - public string BasePath => _directoryCleaner.BasePath; - public MockFileSystem FileSystem { get; } - private readonly IDirectoryCleaner _directoryCleaner; - - public FileSystemWatcherMockTests() - { - FileSystem = new MockFileSystem(); - _directoryCleaner = FileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); - FileSystem.Initialize(); - } - - #region IDisposable Members - - /// - public void Dispose() - => _directoryCleaner.Dispose(); - - #endregion - - [SkippableTheory] - [AutoData] - public void Error_DefaultTo64Messages_ShouldBeTriggeredWhenBufferOverflows( - string path) - { - FileSystem.Directory.CreateDirectory(path); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - ManualResetEventSlim block1 = new(); - ManualResetEventSlim block2 = new(); - ErrorEventArgs? result = null; - fileSystemWatcher.Error += (_, eventArgs) => - { - result = eventArgs; - block1.Set(); - block2.Set(); - }; - fileSystemWatcher.Deleted += (_, _) => - { - block1.Wait(10000); - }; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.Delete(path); - for (int i = 0; i <= DefaultMaxMessages; i++) - { - if (block1.IsSet) - { - break; - } - - FileSystem.Directory.CreateDirectory($"{i}_{path}"); - } - - block2.Wait(10000).Should().BeTrue(); - fileSystemWatcher.Dispose(); - result.Should().NotBeNull(); - result!.GetException().Should().BeOfType(); - } - - [SkippableTheory] - [InlineAutoData(4096)] - [InlineAutoData(8192)] - public void Error_ShouldBeTriggeredWhenBufferOverflows( - int internalBufferSize, string path) - { - int maxMessages = internalBufferSize / 128; - FileSystem.Directory.CreateDirectory(path); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - ManualResetEventSlim block1 = new(); - ManualResetEventSlim block2 = new(); - ErrorEventArgs? result = null; - fileSystemWatcher.Error += (_, eventArgs) => - { - result = eventArgs; - block1.Set(); - block2.Set(); - }; - fileSystemWatcher.Deleted += (_, _) => - { - block1.Wait(5000); - }; - fileSystemWatcher.EnableRaisingEvents = true; - fileSystemWatcher.InternalBufferSize = internalBufferSize; - FileSystem.Directory.Delete(path); - for (int i = 0; i <= maxMessages; i++) - { - if (block1.IsSet) - { - break; - } - - FileSystem.Directory.CreateDirectory($"{i}_{path}"); - } - - block2.Wait(5000).Should().BeTrue(); - fileSystemWatcher.Dispose(); - result.Should().NotBeNull(); - result!.GetException().Should().BeOfType(); - } - -#if FEATURE_FILESYSTEMWATCHER_ADVANCED - [SkippableTheory] - [AutoData] - public void Filter_ShouldResetFiltersToOnlyContainASingleValue( - string[] filters, string expectedFilter) - { - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - foreach (string filter in filters) - { - fileSystemWatcher.Filters.Add(filter); - } - - fileSystemWatcher.Filters.Count.Should().Be(filters.Length); - - fileSystemWatcher.Filter = expectedFilter; - - fileSystemWatcher.Filters.Count.Should().Be(1); - fileSystemWatcher.Filters.Should().ContainSingle(expectedFilter); - fileSystemWatcher.Filter.Should().Be(expectedFilter); - } -#endif - - [SkippableTheory] - [AutoData] - public void InternalBufferSize_ShouldResetQueue(string path1, string path2) - { - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - ManualResetEventSlim block1 = new(); - ManualResetEventSlim block2 = new(); - ErrorEventArgs? result = null; - fileSystemWatcher.Error += (_, eventArgs) => - { - result = eventArgs; - block1.Set(); - block2.Set(); - }; - fileSystemWatcher.Created += (_, _) => - { - block1.Wait(100); - }; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.CreateDirectory(path1); - for (int i = 0; i < DefaultMaxMessages; i++) - { - if (block1.IsSet) - { - break; - } - - FileSystem.Directory.CreateDirectory($"{i}_{path1}"); - } - - fileSystemWatcher.InternalBufferSize = 4196; - - FileSystem.Directory.CreateDirectory(path2); - for (int i = 0; i < 4196 / 128; i++) - { - if (block1.IsSet) - { - break; - } - - FileSystem.Directory.CreateDirectory($"{i}_{path2}"); - } - - block2.Wait(100).Should().BeFalse(); - fileSystemWatcher.Dispose(); - result.Should().BeNull(); - } -} +using System.IO; +using System.Threading; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Testing.Tests.FileSystem; + +public sealed class FileSystemWatcherMockTests : IDisposable +{ + /// + /// Default number of messages before the buffer overflows is 64:
+ /// internal buffer size / bytes per message = 8192 / 128 = 64 + ///
+ public static readonly int DefaultMaxMessages = 64; + + public string BasePath => _directoryCleaner.BasePath; + public MockFileSystem FileSystem { get; } + private readonly IDirectoryCleaner _directoryCleaner; + + public FileSystemWatcherMockTests() + { + FileSystem = new MockFileSystem(); + _directoryCleaner = FileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); + FileSystem.Initialize(); + } + + #region IDisposable Members + + /// + public void Dispose() + => _directoryCleaner.Dispose(); + + #endregion + + [SkippableTheory] + [AutoData] + public void Error_DefaultTo64Messages_ShouldBeTriggeredWhenBufferOverflows( + string path) + { + FileSystem.Directory.CreateDirectory(path); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + ManualResetEventSlim block1 = new(); + ManualResetEventSlim block2 = new(); + ErrorEventArgs? result = null; + fileSystemWatcher.Error += (_, eventArgs) => + { + result = eventArgs; + block1.Set(); + block2.Set(); + }; + fileSystemWatcher.Deleted += (_, _) => + { + block1.Wait(10000); + }; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.Delete(path); + for (int i = 0; i <= DefaultMaxMessages; i++) + { + if (block1.IsSet) + { + break; + } + + FileSystem.Directory.CreateDirectory($"{i}_{path}"); + } + + block2.Wait(10000).Should().BeTrue(); + fileSystemWatcher.Dispose(); + result.Should().NotBeNull(); + result!.GetException().Should().BeOfType(); + } + + [SkippableTheory] + [InlineAutoData(4096)] + [InlineAutoData(8192)] + public void Error_ShouldBeTriggeredWhenBufferOverflows( + int internalBufferSize, string path) + { + int maxMessages = internalBufferSize / 128; + FileSystem.Directory.CreateDirectory(path); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + ManualResetEventSlim block1 = new(); + ManualResetEventSlim block2 = new(); + ErrorEventArgs? result = null; + fileSystemWatcher.Error += (_, eventArgs) => + { + result = eventArgs; + block1.Set(); + block2.Set(); + }; + fileSystemWatcher.Deleted += (_, _) => + { + block1.Wait(5000); + }; + fileSystemWatcher.EnableRaisingEvents = true; + fileSystemWatcher.InternalBufferSize = internalBufferSize; + FileSystem.Directory.Delete(path); + for (int i = 0; i <= maxMessages; i++) + { + if (block1.IsSet) + { + break; + } + + FileSystem.Directory.CreateDirectory($"{i}_{path}"); + } + + block2.Wait(5000).Should().BeTrue(); + fileSystemWatcher.Dispose(); + result.Should().NotBeNull(); + result!.GetException().Should().BeOfType(); + } + +#if FEATURE_FILESYSTEMWATCHER_ADVANCED + [SkippableTheory] + [AutoData] + public void Filter_ShouldResetFiltersToOnlyContainASingleValue( + string[] filters, string expectedFilter) + { + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + foreach (string filter in filters) + { + fileSystemWatcher.Filters.Add(filter); + } + + fileSystemWatcher.Filters.Count.Should().Be(filters.Length); + + fileSystemWatcher.Filter = expectedFilter; + + fileSystemWatcher.Filters.Count.Should().Be(1); + fileSystemWatcher.Filters.Should().ContainSingle(expectedFilter); + fileSystemWatcher.Filter.Should().Be(expectedFilter); + } +#endif + + [SkippableTheory] + [AutoData] + public void InternalBufferSize_ShouldResetQueue(string path1, string path2) + { + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + ManualResetEventSlim block1 = new(); + ManualResetEventSlim block2 = new(); + ErrorEventArgs? result = null; + fileSystemWatcher.Error += (_, eventArgs) => + { + result = eventArgs; + block1.Set(); + block2.Set(); + }; + fileSystemWatcher.Created += (_, _) => + { + block1.Wait(100); + }; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.CreateDirectory(path1); + for (int i = 0; i < DefaultMaxMessages; i++) + { + if (block1.IsSet) + { + break; + } + + FileSystem.Directory.CreateDirectory($"{i}_{path1}"); + } + + fileSystemWatcher.InternalBufferSize = 4196; + + FileSystem.Directory.CreateDirectory(path2); + for (int i = 0; i < 4196 / 128; i++) + { + if (block1.IsSet) + { + break; + } + + FileSystem.Directory.CreateDirectory($"{i}_{path2}"); + } + + block2.Wait(100).Should().BeFalse(); + fileSystemWatcher.Dispose(); + result.Should().BeNull(); + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Helpers/PathHelperTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Helpers/PathHelperTests.cs index 2bf3cf326..070ecf281 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/Helpers/PathHelperTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/Helpers/PathHelperTests.cs @@ -1,100 +1,100 @@ -using Moq; -using System.IO; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Tests.TestHelpers; - -namespace Testably.Abstractions.Testing.Tests.Helpers; - -public class PathHelperTests -{ - [Theory] - [AutoData] - public void IsUncPath_AltDirectorySeparatorChar_ShouldReturnTrue(string path) - { - string prefix = new(Path.AltDirectorySeparatorChar, 2); - path = prefix + path; - - bool result = path.IsUncPath(); - - result.Should().BeTrue(); - } - - [Theory] - [AutoData] - public void IsUncPath_DirectorySeparatorChar_ShouldReturnTrue(string path) - { - string prefix = new(Path.DirectorySeparatorChar, 2); - path = prefix + path; - - bool result = path.IsUncPath(); - - result.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void IsUncPath_MixedDirectorySeparatorChars_ShouldReturnFalse(string path) - { - Skip.IfNot(Test.RunsOnWindows, - "Mac and Linux don't have distinctive directory separator chars."); - - path = $"{Path.AltDirectorySeparatorChar}{Path.DirectorySeparatorChar}{path}"; - - bool result = path.IsUncPath(); - - result.Should().BeFalse(); - } - - [Fact] - public void IsUncPath_Null_ShouldReturnFalse() - { - string? path = null; - - bool result = path!.IsUncPath(); - - result.Should().BeFalse(); - } - - [Fact] - public void - ThrowCommonExceptionsIfPathIsInvalid_StartWithNull_ShouldThrowArgumentException() - { - string path = "\0foo"; - - Exception? exception = Record.Exception(() => - { - path.EnsureValidFormat(new MockFileSystem()); - }); - - exception.Should().BeOfType() - .Which.Message.Should().Contain($"'{path}'"); - } - - [Theory] - [AutoData] - public void ThrowCommonExceptionsIfPathIsInvalid_WithInvalidCharacters( - char[] invalidChars) - { - Mock mockFileSystem = new(); - Mock pathSystemMock = new(); - mockFileSystem.Setup(m => m.Path).Returns(pathSystemMock.Object); - pathSystemMock.Setup(m => m.GetInvalidPathChars()).Returns(invalidChars); - pathSystemMock - .Setup(m => m.GetFullPath(It.IsAny())) - .Returns(s => s); - string path = invalidChars[0] + "foo"; - - Exception? exception = Record.Exception(() => - { - path.EnsureValidFormat(mockFileSystem.Object); - }); - -#if NETFRAMEWORK - exception.Should().BeOfType() - .Which.Message.Should().Contain($"'{path}'"); -#else - exception.Should().BeOfType() - .Which.Message.Should().Contain($"'{path}'"); -#endif - } -} +using Moq; +using System.IO; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Helpers; + +public class PathHelperTests +{ + [Theory] + [AutoData] + public void IsUncPath_AltDirectorySeparatorChar_ShouldReturnTrue(string path) + { + string prefix = new(Path.AltDirectorySeparatorChar, 2); + path = prefix + path; + + bool result = path.IsUncPath(); + + result.Should().BeTrue(); + } + + [Theory] + [AutoData] + public void IsUncPath_DirectorySeparatorChar_ShouldReturnTrue(string path) + { + string prefix = new(Path.DirectorySeparatorChar, 2); + path = prefix + path; + + bool result = path.IsUncPath(); + + result.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void IsUncPath_MixedDirectorySeparatorChars_ShouldReturnFalse(string path) + { + Skip.IfNot(Test.RunsOnWindows, + "Mac and Linux don't have distinctive directory separator chars."); + + path = $"{Path.AltDirectorySeparatorChar}{Path.DirectorySeparatorChar}{path}"; + + bool result = path.IsUncPath(); + + result.Should().BeFalse(); + } + + [Fact] + public void IsUncPath_Null_ShouldReturnFalse() + { + string? path = null; + + bool result = path!.IsUncPath(); + + result.Should().BeFalse(); + } + + [Fact] + public void + ThrowCommonExceptionsIfPathIsInvalid_StartWithNull_ShouldThrowArgumentException() + { + string path = "\0foo"; + + Exception? exception = Record.Exception(() => + { + path.EnsureValidFormat(new MockFileSystem()); + }); + + exception.Should().BeOfType() + .Which.Message.Should().Contain($"'{path}'"); + } + + [Theory] + [AutoData] + public void ThrowCommonExceptionsIfPathIsInvalid_WithInvalidCharacters( + char[] invalidChars) + { + Mock mockFileSystem = new(); + Mock pathSystemMock = new(); + mockFileSystem.Setup(m => m.Path).Returns(pathSystemMock.Object); + pathSystemMock.Setup(m => m.GetInvalidPathChars()).Returns(invalidChars); + pathSystemMock + .Setup(m => m.GetFullPath(It.IsAny())) + .Returns(s => s); + string path = invalidChars[0] + "foo"; + + Exception? exception = Record.Exception(() => + { + path.EnsureValidFormat(mockFileSystem.Object); + }); + +#if NETFRAMEWORK + exception.Should().BeOfType() + .Which.Message.Should().Contain($"'{path}'"); +#else + exception.Should().BeOfType() + .Which.Message.Should().Contain($"'{path}'"); +#endif + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/MockFileSystemTests.cs b/Tests/Testably.Abstractions.Testing.Tests/MockFileSystemTests.cs index 8e2756a20..0eb99f1c0 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/MockFileSystemTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/MockFileSystemTests.cs @@ -1,287 +1,287 @@ -using System.IO; -using System.Linq; -using Testably.Abstractions.Testing.FileSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Tests.TestHelpers; -#if NET6_0_OR_GREATER -using Microsoft.Win32.SafeHandles; -#endif - -namespace Testably.Abstractions.Testing.Tests; - -public class MockFileSystemTests -{ - [SkippableTheory] - [AutoData] - public void FileSystemMock_File_Decrypt(string path, string contents) - { - MockFileSystem sut = new(); - sut.File.WriteAllText(path, contents); - #pragma warning disable CA1416 - sut.File.Encrypt(path); - #pragma warning restore CA1416 - - #pragma warning disable CA1416 - sut.File.Decrypt(path); - #pragma warning restore CA1416 - - sut.File.ReadAllText(path).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void FileSystemMock_File_Encrypt(string path, string contents) - { - MockFileSystem sut = new(); - sut.File.WriteAllText(path, contents); - - #pragma warning disable CA1416 - sut.File.Encrypt(path); - #pragma warning restore CA1416 - - sut.File.ReadAllText(path).Should().NotBe(contents); - } - - [SkippableTheory] - [AutoData] - public void FileSystemMock_FileInfo_Decrypt(string path, string contents) - { - MockFileSystem sut = new(); - sut.File.WriteAllText(path, contents); - #pragma warning disable CA1416 - sut.FileInfo.New(path).Encrypt(); - #pragma warning restore CA1416 - - #pragma warning disable CA1416 - sut.FileInfo.New(path).Decrypt(); - #pragma warning restore CA1416 - - sut.File.ReadAllText(path).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void FileSystemMock_FileInfo_Encrypt(string path, string contents) - { - MockFileSystem sut = new(); - sut.File.WriteAllText(path, contents); - - #pragma warning disable CA1416 - sut.FileInfo.New(path).Encrypt(); - #pragma warning restore CA1416 - - sut.File.ReadAllText(path).Should().NotBe(contents); - } - - [SkippableFact] - public void FileSystemMock_ShouldBeInitializedWithASingleDefaultDrive() - { - string expectedDriveName = "".PrefixRoot(); - MockFileSystem sut = new(); - - IDriveInfo[] drives = sut.DriveInfo.GetDrives(); - IDriveInfo drive = drives.Single(); - - drive.Name.Should().Be(expectedDriveName); - drive.AvailableFreeSpace.Should().BeGreaterThan(0); - drive.DriveFormat.Should() - .Be(DriveInfoMock.DefaultDriveFormat); - drive.DriveType.Should() - .Be(DriveInfoMock.DefaultDriveType); - drive.VolumeLabel.Should().NotBeNullOrEmpty(); - } - - [SkippableTheory] - [AutoData] - public void WithAccessControl_Denied_CreateDirectoryShouldThrowIOException( - string path) - { - MockFileSystem sut = new(); - sut.Initialize(); - sut.WithAccessControlStrategy(new DefaultAccessControlStrategy((_, _) => false)); - - Exception? exception = Record.Exception(() => - { - sut.Directory.CreateDirectory(path); - }); - - exception.Should().BeOfType(); - } - - [SkippableTheory] - [AutoData] - public void WithAccessControl_ShouldConsiderPath( - string allowedPath, string deniedPath) - { - MockFileSystem sut = new(); - sut.Initialize(); - sut.WithAccessControlStrategy(new DefaultAccessControlStrategy((p, _) - => p == sut.Path.GetFullPath(allowedPath))); - - sut.Directory.CreateDirectory(allowedPath); - Exception? exception = Record.Exception(() => - { - sut.Directory.CreateDirectory(deniedPath); - }); - - exception.Should().BeOfType(); - } - - [SkippableTheory] - [InlineData("D:\\")] - public void WithDrive_Duplicate_ShouldUpdateExistingDrive(string driveName) - { - Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); - - MockFileSystem sut = new(); - sut.WithDrive(driveName, d => d.SetTotalSize(100)); - sut.DriveInfo.GetDrives().Length.Should().Be(2); - IDriveInfo drive = sut.DriveInfo.GetDrives().Single(x => x.Name == driveName); - drive.TotalSize.Should().Be(100); - - sut.WithDrive(driveName, d => d.SetTotalSize(200)); - sut.DriveInfo.GetDrives().Length.Should().Be(2); - drive.TotalSize.Should().Be(200); - } - - [SkippableFact] - public void WithDrive_ExistingName_ShouldUpdateDrive() - { - string driveName = "".PrefixRoot(); - MockFileSystem sut = new(); - sut.WithDrive(driveName); - - IDriveInfo[] drives = sut.DriveInfo.GetDrives(); - - drives.Length.Should().Be(1); - drives.Should().ContainSingle(d => d.Name == driveName); - } - - [SkippableTheory] - [InlineData("D:\\")] - public void WithDrive_NewName_ShouldCreateNewDrives(string driveName) - { - Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); - - MockFileSystem sut = new(); - sut.WithDrive(driveName); - - IDriveInfo[] drives = sut.DriveInfo.GetDrives(); - - drives.Length.Should().Be(2); - drives.Should().ContainSingle(d => d.Name == driveName); - } - - [SkippableTheory] - [AutoData] - public void WithDrive_WithCallback_ShouldUpdateDrive(long totalSize) - { - MockFileSystem sut = new(); - sut.WithDrive(d => d.SetTotalSize(totalSize)); - - IDriveInfo drive = sut.DriveInfo.GetDrives().Single(); - - drive.TotalSize.Should().Be(totalSize); - drive.TotalFreeSpace.Should().Be(totalSize); - drive.AvailableFreeSpace.Should().Be(totalSize); - } - - [SkippableTheory] - [AutoData] - public void WithUncDrive_ShouldCreateUncDrive( - string path, string contents) - { - MockFileSystem sut = new(); - sut.WithUncDrive("UNC-Path"); - string fullPath = sut.Path.Combine("//UNC-Path", path); - sut.File.WriteAllText(fullPath, contents); - - string result = sut.File.ReadAllText(fullPath); - result.Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void WithUncDrive_ShouldNotBeIncludedInGetDrives( - string server) - { - MockFileSystem sut = new(); - string uncPrefix = new(sut.Path.DirectorySeparatorChar, 2); - string uncDrive = $"{uncPrefix}{server}"; - - sut.WithUncDrive(uncDrive); - - sut.Directory.GetLogicalDrives().Length.Should().Be(1); - sut.DriveInfo.GetDrives().Length.Should().Be(1); - } - - [SkippableTheory] - [AutoData] - public void WithUncDrive_WriteBytes_ShouldReduceAvailableFreeSpace( - string server, string path, byte[] bytes) - { - MockFileSystem sut = new(); - string uncPrefix = new(sut.Path.DirectorySeparatorChar, 2); - string uncDrive = $"{uncPrefix}{server}"; - sut.WithUncDrive(uncDrive); - IDriveInfo drive = sut.DriveInfo.New(uncDrive); - long previousFreeSpace = drive.AvailableFreeSpace; - - sut.File.WriteAllBytes(Path.Combine(uncDrive, path), bytes); - - drive.AvailableFreeSpace.Should().Be(previousFreeSpace - bytes.Length); - } - - [SkippableTheory] - [AutoData] - public void WriteAllText_OnUncPath_ShouldThrowDirectoryNotFoundException( - string path, string contents) - { - MockFileSystem sut = new(); - string fullPath = sut.Path.Combine("//UNC-Path", path); - Exception? exception = Record.Exception(() => - { - sut.File.WriteAllText(fullPath, contents); - }); - - exception.Should().BeOfType(); - } - -#if NET6_0_OR_GREATER - [SkippableTheory] - [AutoData] - public void - WithSafeFileHandleStrategy_DefaultStrategy_ShouldUseMappedSafeFileHandleMock( - string path, string contents) - { - MockFileSystem sut = new(); - sut.File.WriteAllText(path, contents); - sut.WithSafeFileHandleStrategy( - new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); - - using FileSystemStream stream = - sut.FileStream.New(new SafeFileHandle(), FileAccess.Read); - using StreamReader streamReader = new(stream); - string result = streamReader.ReadToEnd(); - - result.Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void WithSafeFileHandleStrategy_NullStrategy_ShouldThrowException( - string path, string contents) - { - MockFileSystem sut = new(); - sut.File.WriteAllText(path, contents); - - Exception? exception = Record.Exception(() => - { - sut.FileStream.New(new SafeFileHandle(), FileAccess.Read); - }); - - exception.Should().BeOfType() - .Which.ParamName.Should().Be("handle"); - } -#endif -} +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystem; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Tests.TestHelpers; +#if NET6_0_OR_GREATER +using Microsoft.Win32.SafeHandles; +#endif + +namespace Testably.Abstractions.Testing.Tests; + +public class MockFileSystemTests +{ + [SkippableTheory] + [AutoData] + public void FileSystemMock_File_Decrypt(string path, string contents) + { + MockFileSystem sut = new(); + sut.File.WriteAllText(path, contents); + #pragma warning disable CA1416 + sut.File.Encrypt(path); + #pragma warning restore CA1416 + + #pragma warning disable CA1416 + sut.File.Decrypt(path); + #pragma warning restore CA1416 + + sut.File.ReadAllText(path).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void FileSystemMock_File_Encrypt(string path, string contents) + { + MockFileSystem sut = new(); + sut.File.WriteAllText(path, contents); + + #pragma warning disable CA1416 + sut.File.Encrypt(path); + #pragma warning restore CA1416 + + sut.File.ReadAllText(path).Should().NotBe(contents); + } + + [SkippableTheory] + [AutoData] + public void FileSystemMock_FileInfo_Decrypt(string path, string contents) + { + MockFileSystem sut = new(); + sut.File.WriteAllText(path, contents); + #pragma warning disable CA1416 + sut.FileInfo.New(path).Encrypt(); + #pragma warning restore CA1416 + + #pragma warning disable CA1416 + sut.FileInfo.New(path).Decrypt(); + #pragma warning restore CA1416 + + sut.File.ReadAllText(path).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void FileSystemMock_FileInfo_Encrypt(string path, string contents) + { + MockFileSystem sut = new(); + sut.File.WriteAllText(path, contents); + + #pragma warning disable CA1416 + sut.FileInfo.New(path).Encrypt(); + #pragma warning restore CA1416 + + sut.File.ReadAllText(path).Should().NotBe(contents); + } + + [SkippableFact] + public void FileSystemMock_ShouldBeInitializedWithASingleDefaultDrive() + { + string expectedDriveName = "".PrefixRoot(); + MockFileSystem sut = new(); + + IDriveInfo[] drives = sut.DriveInfo.GetDrives(); + IDriveInfo drive = drives.Single(); + + drive.Name.Should().Be(expectedDriveName); + drive.AvailableFreeSpace.Should().BeGreaterThan(0); + drive.DriveFormat.Should() + .Be(DriveInfoMock.DefaultDriveFormat); + drive.DriveType.Should() + .Be(DriveInfoMock.DefaultDriveType); + drive.VolumeLabel.Should().NotBeNullOrEmpty(); + } + + [SkippableTheory] + [AutoData] + public void WithAccessControl_Denied_CreateDirectoryShouldThrowIOException( + string path) + { + MockFileSystem sut = new(); + sut.Initialize(); + sut.WithAccessControlStrategy(new DefaultAccessControlStrategy((_, _) => false)); + + Exception? exception = Record.Exception(() => + { + sut.Directory.CreateDirectory(path); + }); + + exception.Should().BeOfType(); + } + + [SkippableTheory] + [AutoData] + public void WithAccessControl_ShouldConsiderPath( + string allowedPath, string deniedPath) + { + MockFileSystem sut = new(); + sut.Initialize(); + sut.WithAccessControlStrategy(new DefaultAccessControlStrategy((p, _) + => p == sut.Path.GetFullPath(allowedPath))); + + sut.Directory.CreateDirectory(allowedPath); + Exception? exception = Record.Exception(() => + { + sut.Directory.CreateDirectory(deniedPath); + }); + + exception.Should().BeOfType(); + } + + [SkippableTheory] + [InlineData("D:\\")] + public void WithDrive_Duplicate_ShouldUpdateExistingDrive(string driveName) + { + Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); + + MockFileSystem sut = new(); + sut.WithDrive(driveName, d => d.SetTotalSize(100)); + sut.DriveInfo.GetDrives().Length.Should().Be(2); + IDriveInfo drive = sut.DriveInfo.GetDrives().Single(x => x.Name == driveName); + drive.TotalSize.Should().Be(100); + + sut.WithDrive(driveName, d => d.SetTotalSize(200)); + sut.DriveInfo.GetDrives().Length.Should().Be(2); + drive.TotalSize.Should().Be(200); + } + + [SkippableFact] + public void WithDrive_ExistingName_ShouldUpdateDrive() + { + string driveName = "".PrefixRoot(); + MockFileSystem sut = new(); + sut.WithDrive(driveName); + + IDriveInfo[] drives = sut.DriveInfo.GetDrives(); + + drives.Length.Should().Be(1); + drives.Should().ContainSingle(d => d.Name == driveName); + } + + [SkippableTheory] + [InlineData("D:\\")] + public void WithDrive_NewName_ShouldCreateNewDrives(string driveName) + { + Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); + + MockFileSystem sut = new(); + sut.WithDrive(driveName); + + IDriveInfo[] drives = sut.DriveInfo.GetDrives(); + + drives.Length.Should().Be(2); + drives.Should().ContainSingle(d => d.Name == driveName); + } + + [SkippableTheory] + [AutoData] + public void WithDrive_WithCallback_ShouldUpdateDrive(long totalSize) + { + MockFileSystem sut = new(); + sut.WithDrive(d => d.SetTotalSize(totalSize)); + + IDriveInfo drive = sut.DriveInfo.GetDrives().Single(); + + drive.TotalSize.Should().Be(totalSize); + drive.TotalFreeSpace.Should().Be(totalSize); + drive.AvailableFreeSpace.Should().Be(totalSize); + } + + [SkippableTheory] + [AutoData] + public void WithUncDrive_ShouldCreateUncDrive( + string path, string contents) + { + MockFileSystem sut = new(); + sut.WithUncDrive("UNC-Path"); + string fullPath = sut.Path.Combine("//UNC-Path", path); + sut.File.WriteAllText(fullPath, contents); + + string result = sut.File.ReadAllText(fullPath); + result.Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void WithUncDrive_ShouldNotBeIncludedInGetDrives( + string server) + { + MockFileSystem sut = new(); + string uncPrefix = new(sut.Path.DirectorySeparatorChar, 2); + string uncDrive = $"{uncPrefix}{server}"; + + sut.WithUncDrive(uncDrive); + + sut.Directory.GetLogicalDrives().Length.Should().Be(1); + sut.DriveInfo.GetDrives().Length.Should().Be(1); + } + + [SkippableTheory] + [AutoData] + public void WithUncDrive_WriteBytes_ShouldReduceAvailableFreeSpace( + string server, string path, byte[] bytes) + { + MockFileSystem sut = new(); + string uncPrefix = new(sut.Path.DirectorySeparatorChar, 2); + string uncDrive = $"{uncPrefix}{server}"; + sut.WithUncDrive(uncDrive); + IDriveInfo drive = sut.DriveInfo.New(uncDrive); + long previousFreeSpace = drive.AvailableFreeSpace; + + sut.File.WriteAllBytes(Path.Combine(uncDrive, path), bytes); + + drive.AvailableFreeSpace.Should().Be(previousFreeSpace - bytes.Length); + } + + [SkippableTheory] + [AutoData] + public void WriteAllText_OnUncPath_ShouldThrowDirectoryNotFoundException( + string path, string contents) + { + MockFileSystem sut = new(); + string fullPath = sut.Path.Combine("//UNC-Path", path); + Exception? exception = Record.Exception(() => + { + sut.File.WriteAllText(fullPath, contents); + }); + + exception.Should().BeOfType(); + } + +#if NET6_0_OR_GREATER + [SkippableTheory] + [AutoData] + public void + WithSafeFileHandleStrategy_DefaultStrategy_ShouldUseMappedSafeFileHandleMock( + string path, string contents) + { + MockFileSystem sut = new(); + sut.File.WriteAllText(path, contents); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); + + using FileSystemStream stream = + sut.FileStream.New(new SafeFileHandle(), FileAccess.Read); + using StreamReader streamReader = new(stream); + string result = streamReader.ReadToEnd(); + + result.Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void WithSafeFileHandleStrategy_NullStrategy_ShouldThrowException( + string path, string contents) + { + MockFileSystem sut = new(); + sut.File.WriteAllText(path, contents); + + Exception? exception = Record.Exception(() => + { + sut.FileStream.New(new SafeFileHandle(), FileAccess.Read); + }); + + exception.Should().BeOfType() + .Which.ParamName.Should().Be("handle"); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Storage/InMemoryStorageTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Storage/InMemoryStorageTests.cs index ff67c680d..525bedc12 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/Storage/InMemoryStorageTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/Storage/InMemoryStorageTests.cs @@ -1,204 +1,204 @@ -using System.IO; -using Testably.Abstractions.RandomSystem; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; -using Testably.Abstractions.Testing.Tests.TestHelpers; - -namespace Testably.Abstractions.Testing.Tests.Storage; - -public class InMemoryStorageTests -{ - #region Test Setup - - internal MockFileSystem FileSystem { get; } - internal IStorage Storage { get; } - - public InMemoryStorageTests() - { - FileSystem = new MockFileSystem(); - Storage = new InMemoryStorage(FileSystem); - } - - #endregion - - [Theory] - [AutoData] - public void Copy_Overwrite_ShouldAdjustAvailableFreeSpace( - int file1Size, int file2Size) - { - MockFileSystem fileSystem = new(); - IDriveInfo mainDrive = fileSystem.DriveInfo.New("".PrefixRoot()); - IRandom random = RandomFactory.Shared; - byte[] file1Content = new byte[file1Size]; - byte[] file2Content = new byte[file2Size]; - random.NextBytes(file1Content); - random.NextBytes(file2Content); - - fileSystem.File.WriteAllBytes("foo", file1Content); - fileSystem.File.WriteAllBytes("bar", file2Content); - long availableFreeSpaceBefore = mainDrive.AvailableFreeSpace; - - fileSystem.File.Copy("foo", "bar", true); - - long availableFreeSpaceAfter = mainDrive.AvailableFreeSpace; - availableFreeSpaceAfter.Should() - .Be(availableFreeSpaceBefore + file2Size - file1Size); - } - - [Theory] - [AutoData] - public void Replace_WithoutBackup_ShouldNotChangeAvailableFreeSpace( - int file1Size, int file2Size) - { - MockFileSystem fileSystem = new(); - IDriveInfo mainDrive = fileSystem.DriveInfo.New("".PrefixRoot()); - IRandom random = RandomFactory.Shared; - byte[] file1Content = new byte[file1Size]; - byte[] file2Content = new byte[file2Size]; - random.NextBytes(file1Content); - random.NextBytes(file2Content); - - fileSystem.File.WriteAllBytes("foo", file1Content); - fileSystem.File.WriteAllBytes("bar", file2Content); - long availableFreeSpaceBefore = mainDrive.AvailableFreeSpace; - - fileSystem.File.Replace("foo", "bar", "backup"); - - long availableFreeSpaceAfter = mainDrive.AvailableFreeSpace; - availableFreeSpaceAfter.Should() - .Be(availableFreeSpaceBefore); - } - - [Theory] - [AutoData] - public void Replace_WithBackup_ShouldChangeAvailableFreeSpace( - int file1Size, int file2Size, int file3Size) - { - MockFileSystem fileSystem = new(); - IDriveInfo mainDrive = fileSystem.DriveInfo.New("".PrefixRoot()); - IRandom random = RandomFactory.Shared; - byte[] file1Content = new byte[file1Size]; - byte[] file2Content = new byte[file2Size]; - byte[] file3Content = new byte[file3Size]; - random.NextBytes(file1Content); - random.NextBytes(file2Content); - random.NextBytes(file3Content); - - fileSystem.File.WriteAllBytes("foo", file1Content); - fileSystem.File.WriteAllBytes("bar", file2Content); - fileSystem.File.WriteAllBytes("backup", file3Content); - long availableFreeSpaceBefore = mainDrive.AvailableFreeSpace; - - fileSystem.File.Replace("foo", "bar", "backup", true); - - long availableFreeSpaceAfter = mainDrive.AvailableFreeSpace; - availableFreeSpaceAfter.Should() - .Be(availableFreeSpaceBefore + file2Size); - } - - [Fact] - public void CurrentDirectory_ShouldBeInitializedToDefaultRoot() - { - string expectedRoot = string.Empty.PrefixRoot(); - Storage.CurrentDirectory.Should().Be(expectedRoot); - } - - [Fact] - public void Delete_RaceCondition_ShouldReturnFalse() - { - MockFileSystem fileSystem = new(); - fileSystem.Directory.CreateDirectory("foo"); - bool isFirstDeletion = true; - fileSystem.Intercept.Deleting(FileSystemTypes.Directory, _ => - { - if (isFirstDeletion) - { - isFirstDeletion = false; - fileSystem.Directory.Delete("foo"); - } - }); - - Exception? exception = Record.Exception(() => - { - fileSystem.Directory.Delete("foo"); - }); - - exception.Should().BeOfType(); - } - - [Theory] - [AutoData] - public void Move_RequestDeniedForChild_ShouldRollback( - string locationPath, string destinationPath) - { - IStorageLocation location = Storage.GetLocation(locationPath); - IStorageLocation destination = Storage.GetLocation(destinationPath); - IStorageLocation child1Location = - Storage.GetLocation(Path.Combine(locationPath, "foo")); - IStorageLocation child2Location = - Storage.GetLocation(Path.Combine(locationPath, "bar")); - LockableContainer lockedContainer = new(FileSystem); - Storage.TryAddContainer( - location, - InMemoryContainer.NewDirectory, - out _); - Storage.TryAddContainer( - child1Location, - InMemoryContainer.NewFile, - out _); - Storage.TryAddContainer( - child2Location, - (_, _) => lockedContainer, - out _); - - lockedContainer.IsLocked = true; - - Exception? exception = Record.Exception(() => - { - Storage.Move(location, destination, recursive: true); - }); - - Storage.GetContainer(location).Should().NotBeOfType(); - Storage.GetContainer(child1Location).Should().NotBeOfType(); - Storage.GetContainer(child2Location).Should().NotBeOfType(); - exception.Should().BeOfType(); - } - - [Theory] - [AutoData] - public void TryAddContainer_ShouldNotifyWhenAdded(string path) - { - bool receivedNotification = false; - FileSystem.Notify.OnEvent(_ => receivedNotification = true); - IStorageLocation location = Storage.GetLocation(path); - bool result = Storage.TryAddContainer( - location, - InMemoryContainer.NewDirectory, - out IStorageContainer? container); - - result.Should().BeTrue(); - receivedNotification.Should().BeTrue(); - container!.Type.Should().Be(FileSystemTypes.Directory); - } - - [Theory] - [AutoData] - public void TryAddContainer_ShouldNotNotifyWhenExistsPreviously(string path) - { - IStorageLocation location = Storage.GetLocation(path); - Storage.TryAddContainer( - location, - InMemoryContainer.NewDirectory, - out _); - bool receivedNotification = false; - FileSystem.Notify.OnEvent(_ => receivedNotification = true); - bool result = Storage.TryAddContainer( - location, - InMemoryContainer.NewDirectory, - out IStorageContainer? container); - - result.Should().BeFalse(); - receivedNotification.Should().BeFalse(); - container.Should().BeNull(); - } -} +using System.IO; +using Testably.Abstractions.RandomSystem; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Storage; + +public class InMemoryStorageTests +{ + #region Test Setup + + internal MockFileSystem FileSystem { get; } + internal IStorage Storage { get; } + + public InMemoryStorageTests() + { + FileSystem = new MockFileSystem(); + Storage = new InMemoryStorage(FileSystem); + } + + #endregion + + [Theory] + [AutoData] + public void Copy_Overwrite_ShouldAdjustAvailableFreeSpace( + int file1Size, int file2Size) + { + MockFileSystem fileSystem = new(); + IDriveInfo mainDrive = fileSystem.DriveInfo.New("".PrefixRoot()); + IRandom random = RandomFactory.Shared; + byte[] file1Content = new byte[file1Size]; + byte[] file2Content = new byte[file2Size]; + random.NextBytes(file1Content); + random.NextBytes(file2Content); + + fileSystem.File.WriteAllBytes("foo", file1Content); + fileSystem.File.WriteAllBytes("bar", file2Content); + long availableFreeSpaceBefore = mainDrive.AvailableFreeSpace; + + fileSystem.File.Copy("foo", "bar", true); + + long availableFreeSpaceAfter = mainDrive.AvailableFreeSpace; + availableFreeSpaceAfter.Should() + .Be(availableFreeSpaceBefore + file2Size - file1Size); + } + + [Theory] + [AutoData] + public void Replace_WithoutBackup_ShouldNotChangeAvailableFreeSpace( + int file1Size, int file2Size) + { + MockFileSystem fileSystem = new(); + IDriveInfo mainDrive = fileSystem.DriveInfo.New("".PrefixRoot()); + IRandom random = RandomFactory.Shared; + byte[] file1Content = new byte[file1Size]; + byte[] file2Content = new byte[file2Size]; + random.NextBytes(file1Content); + random.NextBytes(file2Content); + + fileSystem.File.WriteAllBytes("foo", file1Content); + fileSystem.File.WriteAllBytes("bar", file2Content); + long availableFreeSpaceBefore = mainDrive.AvailableFreeSpace; + + fileSystem.File.Replace("foo", "bar", "backup"); + + long availableFreeSpaceAfter = mainDrive.AvailableFreeSpace; + availableFreeSpaceAfter.Should() + .Be(availableFreeSpaceBefore); + } + + [Theory] + [AutoData] + public void Replace_WithBackup_ShouldChangeAvailableFreeSpace( + int file1Size, int file2Size, int file3Size) + { + MockFileSystem fileSystem = new(); + IDriveInfo mainDrive = fileSystem.DriveInfo.New("".PrefixRoot()); + IRandom random = RandomFactory.Shared; + byte[] file1Content = new byte[file1Size]; + byte[] file2Content = new byte[file2Size]; + byte[] file3Content = new byte[file3Size]; + random.NextBytes(file1Content); + random.NextBytes(file2Content); + random.NextBytes(file3Content); + + fileSystem.File.WriteAllBytes("foo", file1Content); + fileSystem.File.WriteAllBytes("bar", file2Content); + fileSystem.File.WriteAllBytes("backup", file3Content); + long availableFreeSpaceBefore = mainDrive.AvailableFreeSpace; + + fileSystem.File.Replace("foo", "bar", "backup", true); + + long availableFreeSpaceAfter = mainDrive.AvailableFreeSpace; + availableFreeSpaceAfter.Should() + .Be(availableFreeSpaceBefore + file2Size); + } + + [Fact] + public void CurrentDirectory_ShouldBeInitializedToDefaultRoot() + { + string expectedRoot = string.Empty.PrefixRoot(); + Storage.CurrentDirectory.Should().Be(expectedRoot); + } + + [Fact] + public void Delete_RaceCondition_ShouldReturnFalse() + { + MockFileSystem fileSystem = new(); + fileSystem.Directory.CreateDirectory("foo"); + bool isFirstDeletion = true; + fileSystem.Intercept.Deleting(FileSystemTypes.Directory, _ => + { + if (isFirstDeletion) + { + isFirstDeletion = false; + fileSystem.Directory.Delete("foo"); + } + }); + + Exception? exception = Record.Exception(() => + { + fileSystem.Directory.Delete("foo"); + }); + + exception.Should().BeOfType(); + } + + [Theory] + [AutoData] + public void Move_RequestDeniedForChild_ShouldRollback( + string locationPath, string destinationPath) + { + IStorageLocation location = Storage.GetLocation(locationPath); + IStorageLocation destination = Storage.GetLocation(destinationPath); + IStorageLocation child1Location = + Storage.GetLocation(Path.Combine(locationPath, "foo")); + IStorageLocation child2Location = + Storage.GetLocation(Path.Combine(locationPath, "bar")); + LockableContainer lockedContainer = new(FileSystem); + Storage.TryAddContainer( + location, + InMemoryContainer.NewDirectory, + out _); + Storage.TryAddContainer( + child1Location, + InMemoryContainer.NewFile, + out _); + Storage.TryAddContainer( + child2Location, + (_, _) => lockedContainer, + out _); + + lockedContainer.IsLocked = true; + + Exception? exception = Record.Exception(() => + { + Storage.Move(location, destination, recursive: true); + }); + + Storage.GetContainer(location).Should().NotBeOfType(); + Storage.GetContainer(child1Location).Should().NotBeOfType(); + Storage.GetContainer(child2Location).Should().NotBeOfType(); + exception.Should().BeOfType(); + } + + [Theory] + [AutoData] + public void TryAddContainer_ShouldNotifyWhenAdded(string path) + { + bool receivedNotification = false; + FileSystem.Notify.OnEvent(_ => receivedNotification = true); + IStorageLocation location = Storage.GetLocation(path); + bool result = Storage.TryAddContainer( + location, + InMemoryContainer.NewDirectory, + out IStorageContainer? container); + + result.Should().BeTrue(); + receivedNotification.Should().BeTrue(); + container!.Type.Should().Be(FileSystemTypes.Directory); + } + + [Theory] + [AutoData] + public void TryAddContainer_ShouldNotNotifyWhenExistsPreviously(string path) + { + IStorageLocation location = Storage.GetLocation(path); + Storage.TryAddContainer( + location, + InMemoryContainer.NewDirectory, + out _); + bool receivedNotification = false; + FileSystem.Notify.OnEvent(_ => receivedNotification = true); + bool result = Storage.TryAddContainer( + location, + InMemoryContainer.NewDirectory, + out IStorageContainer? container); + + result.Should().BeFalse(); + receivedNotification.Should().BeFalse(); + container.Should().BeNull(); + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs b/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs index 366edfeaa..932c0d604 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/LockableContainer.cs @@ -1,146 +1,146 @@ -using System.IO; -using System.Linq; -using System.Security.AccessControl; -using Testably.Abstractions.Testing.Helpers; -using Testably.Abstractions.Testing.Storage; -using Testably.Abstractions.TimeSystem; - -namespace Testably.Abstractions.Testing.Tests.TestHelpers; - -/// -/// A for testing purposes. -/// -/// Set to to simulate a locked file -/// ( throws an ). -/// -internal sealed class LockableContainer : IStorageContainer -{ - /// - /// Simulate a locked file, if set to .
- /// In this case throws an - /// , - /// otherwise it will succeed. - ///
- public bool IsLocked { get; set; } - - private byte[] _bytes = Array.Empty(); - - public LockableContainer(MockFileSystem fileSystem, - FileSystemTypes containerType = - FileSystemTypes.DirectoryOrFile) - { - FileSystem = fileSystem; - TimeSystem = fileSystem.TimeSystem; - Type = containerType; - } - - #region IStorageContainer Members - - /// - public FileSystemSecurity? AccessControl { get; set; } - - /// - public FileAttributes Attributes { get; set; } - - /// - public IStorageContainer.ITimeContainer CreationTime { get; } - = new InMemoryContainer.TimeContainer(); - - /// - public IFileSystemExtensibility Extensibility { get; } - = new FileSystemExtensibility(); - - /// - public IFileSystem FileSystem { get; } - - /// - public IStorageContainer.ITimeContainer LastAccessTime { get; } - = new InMemoryContainer.TimeContainer(); - - /// - public IStorageContainer.ITimeContainer LastWriteTime { get; } - = new InMemoryContainer.TimeContainer(); - - /// - public string? LinkTarget { get; set; } - - /// - public ITimeSystem TimeSystem { get; } - - /// - public FileSystemTypes Type { get; } - -#if FEATURE_FILESYSTEM_UNIXFILEMODE - /// - public UnixFileMode UnixFileMode { get; set; } -#endif - - /// - public void AppendBytes(byte[] bytes) - => WriteBytes(_bytes.Concat(bytes).ToArray()); - - /// - public void ClearBytes() - => _bytes = Array.Empty(); - - /// - public void Decrypt() - => throw new NotSupportedException(); - - /// - public void Encrypt() - => throw new NotSupportedException(); - - /// - public byte[] GetBytes() - => _bytes; - - /// - public IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, - bool deleteAccess = false, - bool ignoreMetadataErrors = true, - int? hResult = null) - { - if (IsLocked) - { - throw ExceptionFactory.ProcessCannotAccessTheFile("", hResult ?? -1); - } - - return new AccessHandle(access, share, deleteAccess); - } - - /// - public void WriteBytes(byte[] bytes) - => _bytes = bytes; - - #endregion - - private class AccessHandle : IStorageAccessHandle - { - public AccessHandle(FileAccess access, FileShare share, bool deleteAccess) - { - Access = access; - Share = share; - DeleteAccess = deleteAccess; - } - - #region IStorageAccessHandle Members - - /// - public FileAccess Access { get; } - - /// - public bool DeleteAccess { get; } - - /// - public FileShare Share { get; } - - /// - public void Dispose() - { - // Nothing to do - } - - #endregion - } -} +using System.IO; +using System.Linq; +using System.Security.AccessControl; +using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Storage; +using Testably.Abstractions.TimeSystem; + +namespace Testably.Abstractions.Testing.Tests.TestHelpers; + +/// +/// A for testing purposes. +/// +/// Set to to simulate a locked file +/// ( throws an ). +/// +internal sealed class LockableContainer : IStorageContainer +{ + /// + /// Simulate a locked file, if set to .
+ /// In this case throws an + /// , + /// otherwise it will succeed. + ///
+ public bool IsLocked { get; set; } + + private byte[] _bytes = Array.Empty(); + + public LockableContainer(MockFileSystem fileSystem, + FileSystemTypes containerType = + FileSystemTypes.DirectoryOrFile) + { + FileSystem = fileSystem; + TimeSystem = fileSystem.TimeSystem; + Type = containerType; + } + + #region IStorageContainer Members + + /// + public FileSystemSecurity? AccessControl { get; set; } + + /// + public FileAttributes Attributes { get; set; } + + /// + public IStorageContainer.ITimeContainer CreationTime { get; } + = new InMemoryContainer.TimeContainer(); + + /// + public IFileSystemExtensibility Extensibility { get; } + = new FileSystemExtensibility(); + + /// + public IFileSystem FileSystem { get; } + + /// + public IStorageContainer.ITimeContainer LastAccessTime { get; } + = new InMemoryContainer.TimeContainer(); + + /// + public IStorageContainer.ITimeContainer LastWriteTime { get; } + = new InMemoryContainer.TimeContainer(); + + /// + public string? LinkTarget { get; set; } + + /// + public ITimeSystem TimeSystem { get; } + + /// + public FileSystemTypes Type { get; } + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + /// + public UnixFileMode UnixFileMode { get; set; } +#endif + + /// + public void AppendBytes(byte[] bytes) + => WriteBytes(_bytes.Concat(bytes).ToArray()); + + /// + public void ClearBytes() + => _bytes = Array.Empty(); + + /// + public void Decrypt() + => throw new NotSupportedException(); + + /// + public void Encrypt() + => throw new NotSupportedException(); + + /// + public byte[] GetBytes() + => _bytes; + + /// + public IStorageAccessHandle RequestAccess(FileAccess access, FileShare share, + bool deleteAccess = false, + bool ignoreMetadataErrors = true, + int? hResult = null) + { + if (IsLocked) + { + throw ExceptionFactory.ProcessCannotAccessTheFile("", hResult ?? -1); + } + + return new AccessHandle(access, share, deleteAccess); + } + + /// + public void WriteBytes(byte[] bytes) + => _bytes = bytes; + + #endregion + + private class AccessHandle : IStorageAccessHandle + { + public AccessHandle(FileAccess access, FileShare share, bool deleteAccess) + { + Access = access; + Share = share; + DeleteAccess = deleteAccess; + } + + #region IStorageAccessHandle Members + + /// + public FileAccess Access { get; } + + /// + public bool DeleteAccess { get; } + + /// + public FileShare Share { get; } + + /// + public void Dispose() + { + // Nothing to do + } + + #endregion + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/CreateDirectoryTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/CreateDirectoryTests.cs index ac7d4df08..577088025 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/CreateDirectoryTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/CreateDirectoryTests.cs @@ -1,306 +1,306 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class CreateDirectoryTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void CreateDirectory_AlreadyExisting_ShouldDoNothing(string path) - { - FileSystem.Directory.CreateDirectory(path); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.CreateDirectory(path); - }); - - exception.Should().BeNull(); - FileSystem.Directory.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void CreateDirectory_ReadOnlyParent_ShouldStillCreateDirectory(string parent, - string subdirectory) - { - string subdirectoryPath = FileSystem.Path.Combine(parent, subdirectory); - FileSystem.Directory.CreateDirectory(parent); - FileSystem.DirectoryInfo.New(parent).Attributes = FileAttributes.ReadOnly; - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.CreateDirectory(subdirectoryPath); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeNull(); - FileSystem.Directory.Exists(subdirectoryPath).Should().BeTrue(); - FileSystem.DirectoryInfo.New(parent).Attributes - .Should().HaveFlag(FileAttributes.ReadOnly); - } - else - { - exception.Should().BeException(hResult: -2147024891); - FileSystem.Directory.Exists(subdirectoryPath).Should().BeFalse(); - } - } - - [SkippableFact] - public void CreateDirectory_Root_ShouldNotThrowException() - { - string path = FileTestHelper.RootDrive(); - FileSystem.Directory.CreateDirectory(path); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.CreateDirectory(path); - }); - - exception.Should().BeNull(); - FileSystem.Directory.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void CreateDirectory_ShouldTrimTrailingSpaces_OnWindows(string path) - { - string pathWithSpaces = path + " "; - - IDirectoryInfo result = FileSystem.Directory.CreateDirectory(pathWithSpaces); - - if (Test.RunsOnWindows) - { - result.Name.Should().Be(path); - } - else - { - result.Name.Should().Be(pathWithSpaces); - } - } - - [SkippableTheory] - [AutoData] - public void CreateDirectory_ShouldAdjustTimes(string path, string subdirectoryName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - string subdirectoryPath = FileSystem.Path.Combine(path, subdirectoryName); - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.Directory.CreateDirectory(path); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - - FileSystem.Directory.CreateDirectory(subdirectoryPath); - - DateTime creationTime = FileSystem.Directory.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.Directory.GetLastAccessTimeUtc(path); - DateTime lastWriteTime = FileSystem.Directory.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - } - - [SkippableTheory] - [AutoData] - public void CreateDirectory_ShouldAdjustTimesOnlyForDirectParentDirectory( - string rootPath) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - string subdirectoryLevel1Path = - FileSystem.Path.Combine(rootPath, "lvl1"); - string subdirectoryLevel2Path = - FileSystem.Path.Combine(subdirectoryLevel1Path, "lvl2"); - string subdirectoryLevel3Path = - FileSystem.Path.Combine(subdirectoryLevel2Path, "lvl3"); - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.Directory.CreateDirectory(subdirectoryLevel2Path); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - - FileSystem.Directory.CreateDirectory(subdirectoryLevel3Path); - - foreach (string path in new[] - { - rootPath, subdirectoryLevel1Path - }) - { - DateTime lastAccessTime = - FileSystem.Directory.GetLastAccessTimeUtc(path); - DateTime lastWriteTime = - FileSystem.Directory.GetLastWriteTimeUtc(path); - - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - } - - [SkippableTheory] - [AutoData] - public void CreateDirectory_ShouldSetCreationTime(string path) - { - DateTime start = TimeSystem.DateTime.Now; - - FileSystem.Directory.CreateDirectory(path); - - DateTime result = FileSystem.Directory.GetCreationTime(path); - result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.Should().BeOnOrBefore(TimeSystem.DateTime.Now); - result.Kind.Should().Be(DateTimeKind.Local); - } - - [SkippableTheory] - [AutoData] - public void CreateDirectory_ShouldSetCreationTimeUtc(string path) - { - DateTime start = TimeSystem.DateTime.UtcNow; - - FileSystem.Directory.CreateDirectory(path); - - DateTime result = FileSystem.Directory.GetCreationTimeUtc(path); - result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.Should().BeOnOrBefore(TimeSystem.DateTime.UtcNow); - result.Kind.Should().Be(DateTimeKind.Utc); - } - - [SkippableFact] - public void CreateDirectory_NullCharacter_ShouldThrowArgumentException() - { - string path = "foo\0bar"; - Exception? exception = - Record.Exception(() => FileSystem.Directory.CreateDirectory(path)); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableFact] - public void CreateDirectory_ShouldCreateDirectoryInBasePath() - { - IDirectoryInfo result = FileSystem.Directory.CreateDirectory("foo"); - bool exists = FileSystem.Directory.Exists("foo"); - - exists.Should().BeTrue(); - result.FullName.Should().StartWith(BasePath); - } - - [SkippableTheory] - [AutoData] - public void CreateDirectory_ShouldCreateParentDirectories( - string directoryLevel1, string directoryLevel2, string directoryLevel3) - { - string path = - FileSystem.Path.Combine(directoryLevel1, directoryLevel2, directoryLevel3); - - IDirectoryInfo result = FileSystem.Directory.CreateDirectory(path); - - result.Name.Should().Be(directoryLevel3); - result.Parent!.Name.Should().Be(directoryLevel2); - result.Parent.Parent!.Name.Should().Be(directoryLevel1); - result.Exists.Should().BeTrue(); - result.Parent.Exists.Should().BeTrue(); - result.Parent.Parent.Exists.Should().BeTrue(); - } - -#if NETFRAMEWORK - [SkippableTheory] - [InlineData("/")] - [InlineData("\\")] - public void CreateDirectory_TrailingDirectorySeparator_ShouldNotBeTrimmed( - string suffix) - { - string nameWithSuffix = "foobar" + suffix; - string expectedName = nameWithSuffix; - expectedName = expectedName.TrimEnd(' '); - - IDirectoryInfo result = - FileSystem.Directory.CreateDirectory(nameWithSuffix); - - result.Name.Should().Be(expectedName.TrimEnd( - FileSystem.Path.DirectorySeparatorChar, - FileSystem.Path.AltDirectorySeparatorChar)); - result.FullName.Should().Be(System.IO.Path.Combine(BasePath, expectedName - .Replace(FileSystem.Path.AltDirectorySeparatorChar, - FileSystem.Path.DirectorySeparatorChar))); - FileSystem.Directory.Exists(nameWithSuffix).Should().BeTrue(); - } - - [SkippableTheory] - [InlineData("")] - [InlineData(" ")] - public void CreateDirectory_EmptyOrWhitespace_ShouldReturnEmptyString( - string suffix) - { - string nameWithSuffix = "foobar" + suffix; - string expectedName = nameWithSuffix; - expectedName = expectedName.TrimEnd(' '); - - IDirectoryInfo result = - FileSystem.Directory.CreateDirectory(nameWithSuffix); - - result.Name.Should().Be(expectedName.TrimEnd( - FileSystem.Path.DirectorySeparatorChar, - FileSystem.Path.AltDirectorySeparatorChar)); - result.FullName.Should().Be(System.IO.Path.Combine(BasePath, expectedName - .Replace(FileSystem.Path.AltDirectorySeparatorChar, - FileSystem.Path.DirectorySeparatorChar))); - FileSystem.Directory.Exists(nameWithSuffix).Should().BeTrue(); - } -#else - [SkippableTheory] - [InlineData("")] - [InlineData(" ")] - [InlineData("/")] - [InlineData("\\")] - public void CreateDirectory_TrailingDirectorySeparator_ShouldNotBeTrimmed( - string suffix) - { - string nameWithSuffix = "foobar" + suffix; - string expectedName = nameWithSuffix; - if (Test.RunsOnWindows) - { - expectedName = expectedName.TrimEnd(' '); - } - else if (suffix == "\\") - { - //This case is only supported on Windows - return; - } - - IDirectoryInfo result = - FileSystem.Directory.CreateDirectory(nameWithSuffix); - - result.Name.Should().Be(expectedName.TrimEnd( - FileSystem.Path.DirectorySeparatorChar, - FileSystem.Path.AltDirectorySeparatorChar)); - result.FullName.Should().Be(System.IO.Path.Combine(BasePath, expectedName - .Replace(FileSystem.Path.AltDirectorySeparatorChar, - FileSystem.Path.DirectorySeparatorChar))); - FileSystem.Directory.Exists(nameWithSuffix).Should().BeTrue(); - } -#endif -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class CreateDirectoryTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void CreateDirectory_AlreadyExisting_ShouldDoNothing(string path) + { + FileSystem.Directory.CreateDirectory(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.CreateDirectory(path); + }); + + exception.Should().BeNull(); + FileSystem.Directory.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void CreateDirectory_ReadOnlyParent_ShouldStillCreateDirectory(string parent, + string subdirectory) + { + string subdirectoryPath = FileSystem.Path.Combine(parent, subdirectory); + FileSystem.Directory.CreateDirectory(parent); + FileSystem.DirectoryInfo.New(parent).Attributes = FileAttributes.ReadOnly; + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.CreateDirectory(subdirectoryPath); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeNull(); + FileSystem.Directory.Exists(subdirectoryPath).Should().BeTrue(); + FileSystem.DirectoryInfo.New(parent).Attributes + .Should().HaveFlag(FileAttributes.ReadOnly); + } + else + { + exception.Should().BeException(hResult: -2147024891); + FileSystem.Directory.Exists(subdirectoryPath).Should().BeFalse(); + } + } + + [SkippableFact] + public void CreateDirectory_Root_ShouldNotThrowException() + { + string path = FileTestHelper.RootDrive(); + FileSystem.Directory.CreateDirectory(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.CreateDirectory(path); + }); + + exception.Should().BeNull(); + FileSystem.Directory.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void CreateDirectory_ShouldTrimTrailingSpaces_OnWindows(string path) + { + string pathWithSpaces = path + " "; + + IDirectoryInfo result = FileSystem.Directory.CreateDirectory(pathWithSpaces); + + if (Test.RunsOnWindows) + { + result.Name.Should().Be(path); + } + else + { + result.Name.Should().Be(pathWithSpaces); + } + } + + [SkippableTheory] + [AutoData] + public void CreateDirectory_ShouldAdjustTimes(string path, string subdirectoryName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + string subdirectoryPath = FileSystem.Path.Combine(path, subdirectoryName); + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.Directory.CreateDirectory(path); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + + FileSystem.Directory.CreateDirectory(subdirectoryPath); + + DateTime creationTime = FileSystem.Directory.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.Directory.GetLastAccessTimeUtc(path); + DateTime lastWriteTime = FileSystem.Directory.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + } + + [SkippableTheory] + [AutoData] + public void CreateDirectory_ShouldAdjustTimesOnlyForDirectParentDirectory( + string rootPath) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + string subdirectoryLevel1Path = + FileSystem.Path.Combine(rootPath, "lvl1"); + string subdirectoryLevel2Path = + FileSystem.Path.Combine(subdirectoryLevel1Path, "lvl2"); + string subdirectoryLevel3Path = + FileSystem.Path.Combine(subdirectoryLevel2Path, "lvl3"); + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.Directory.CreateDirectory(subdirectoryLevel2Path); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + + FileSystem.Directory.CreateDirectory(subdirectoryLevel3Path); + + foreach (string path in new[] + { + rootPath, subdirectoryLevel1Path + }) + { + DateTime lastAccessTime = + FileSystem.Directory.GetLastAccessTimeUtc(path); + DateTime lastWriteTime = + FileSystem.Directory.GetLastWriteTimeUtc(path); + + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + } + + [SkippableTheory] + [AutoData] + public void CreateDirectory_ShouldSetCreationTime(string path) + { + DateTime start = TimeSystem.DateTime.Now; + + FileSystem.Directory.CreateDirectory(path); + + DateTime result = FileSystem.Directory.GetCreationTime(path); + result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.Should().BeOnOrBefore(TimeSystem.DateTime.Now); + result.Kind.Should().Be(DateTimeKind.Local); + } + + [SkippableTheory] + [AutoData] + public void CreateDirectory_ShouldSetCreationTimeUtc(string path) + { + DateTime start = TimeSystem.DateTime.UtcNow; + + FileSystem.Directory.CreateDirectory(path); + + DateTime result = FileSystem.Directory.GetCreationTimeUtc(path); + result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.Should().BeOnOrBefore(TimeSystem.DateTime.UtcNow); + result.Kind.Should().Be(DateTimeKind.Utc); + } + + [SkippableFact] + public void CreateDirectory_NullCharacter_ShouldThrowArgumentException() + { + string path = "foo\0bar"; + Exception? exception = + Record.Exception(() => FileSystem.Directory.CreateDirectory(path)); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableFact] + public void CreateDirectory_ShouldCreateDirectoryInBasePath() + { + IDirectoryInfo result = FileSystem.Directory.CreateDirectory("foo"); + bool exists = FileSystem.Directory.Exists("foo"); + + exists.Should().BeTrue(); + result.FullName.Should().StartWith(BasePath); + } + + [SkippableTheory] + [AutoData] + public void CreateDirectory_ShouldCreateParentDirectories( + string directoryLevel1, string directoryLevel2, string directoryLevel3) + { + string path = + FileSystem.Path.Combine(directoryLevel1, directoryLevel2, directoryLevel3); + + IDirectoryInfo result = FileSystem.Directory.CreateDirectory(path); + + result.Name.Should().Be(directoryLevel3); + result.Parent!.Name.Should().Be(directoryLevel2); + result.Parent.Parent!.Name.Should().Be(directoryLevel1); + result.Exists.Should().BeTrue(); + result.Parent.Exists.Should().BeTrue(); + result.Parent.Parent.Exists.Should().BeTrue(); + } + +#if NETFRAMEWORK + [SkippableTheory] + [InlineData("/")] + [InlineData("\\")] + public void CreateDirectory_TrailingDirectorySeparator_ShouldNotBeTrimmed( + string suffix) + { + string nameWithSuffix = "foobar" + suffix; + string expectedName = nameWithSuffix; + expectedName = expectedName.TrimEnd(' '); + + IDirectoryInfo result = + FileSystem.Directory.CreateDirectory(nameWithSuffix); + + result.Name.Should().Be(expectedName.TrimEnd( + FileSystem.Path.DirectorySeparatorChar, + FileSystem.Path.AltDirectorySeparatorChar)); + result.FullName.Should().Be(System.IO.Path.Combine(BasePath, expectedName + .Replace(FileSystem.Path.AltDirectorySeparatorChar, + FileSystem.Path.DirectorySeparatorChar))); + FileSystem.Directory.Exists(nameWithSuffix).Should().BeTrue(); + } + + [SkippableTheory] + [InlineData("")] + [InlineData(" ")] + public void CreateDirectory_EmptyOrWhitespace_ShouldReturnEmptyString( + string suffix) + { + string nameWithSuffix = "foobar" + suffix; + string expectedName = nameWithSuffix; + expectedName = expectedName.TrimEnd(' '); + + IDirectoryInfo result = + FileSystem.Directory.CreateDirectory(nameWithSuffix); + + result.Name.Should().Be(expectedName.TrimEnd( + FileSystem.Path.DirectorySeparatorChar, + FileSystem.Path.AltDirectorySeparatorChar)); + result.FullName.Should().Be(System.IO.Path.Combine(BasePath, expectedName + .Replace(FileSystem.Path.AltDirectorySeparatorChar, + FileSystem.Path.DirectorySeparatorChar))); + FileSystem.Directory.Exists(nameWithSuffix).Should().BeTrue(); + } +#else + [SkippableTheory] + [InlineData("")] + [InlineData(" ")] + [InlineData("/")] + [InlineData("\\")] + public void CreateDirectory_TrailingDirectorySeparator_ShouldNotBeTrimmed( + string suffix) + { + string nameWithSuffix = "foobar" + suffix; + string expectedName = nameWithSuffix; + if (Test.RunsOnWindows) + { + expectedName = expectedName.TrimEnd(' '); + } + else if (suffix == "\\") + { + //This case is only supported on Windows + return; + } + + IDirectoryInfo result = + FileSystem.Directory.CreateDirectory(nameWithSuffix); + + result.Name.Should().Be(expectedName.TrimEnd( + FileSystem.Path.DirectorySeparatorChar, + FileSystem.Path.AltDirectorySeparatorChar)); + result.FullName.Should().Be(System.IO.Path.Combine(BasePath, expectedName + .Replace(FileSystem.Path.AltDirectorySeparatorChar, + FileSystem.Path.DirectorySeparatorChar))); + FileSystem.Directory.Exists(nameWithSuffix).Should().BeTrue(); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/DeleteTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/DeleteTests.cs index d51057553..f7d227186 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/DeleteTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/DeleteTests.cs @@ -1,240 +1,240 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class DeleteTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - Delete_CaseDifferentPath_ShouldThrowDirectoryNotFoundException_OnLinux( - string directoryName) - { - directoryName = directoryName.ToLowerInvariant(); - FileSystem.Directory.CreateDirectory(directoryName.ToUpperInvariant()); - string expectedPath = System.IO.Path.Combine(BasePath, directoryName); - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Delete(directoryName); - }); - - if (Test.RunsOnLinux) - { - exception.Should().BeException($"'{expectedPath}'", - hResult: -2147024893); - } - else - { - exception.Should().BeNull(); - FileSystem.Directory.Exists(directoryName.ToUpperInvariant()) - .Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void Delete_FullPath_ShouldDeleteDirectory(string directoryName) - { - IDirectoryInfo result = - FileSystem.Directory.CreateDirectory(directoryName); - - FileSystem.Directory.Delete(result.FullName); - - FileSystem.Directory.Exists(directoryName).Should().BeFalse(); - result.Exists.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string directoryName) - { - string expectedPath = System.IO.Path.Combine(BasePath, directoryName); - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Delete(directoryName); - }); - - exception.Should().BeException($"'{expectedPath}'", - hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Delete_Recursive_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string directoryName) - { - string expectedPath = System.IO.Path.Combine(BasePath, directoryName); - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Delete(directoryName, true); - }); - - exception.Should().BeException($"'{expectedPath}'", - hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Delete_Recursive_WithOpenFile_ShouldThrowIOException_OnWindows( - string path, string filename) - { - FileSystem.Initialize() - .WithSubdirectory(path); - string filePath = FileSystem.Path.Combine(path, filename); - FileSystemStream openFile = FileSystem.File.OpenWrite(filePath); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Delete(path, true); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException($"{filename}'", - hResult: -2147024864); - FileSystem.File.Exists(filePath).Should().BeTrue(); - } - else - { - exception.Should().BeNull(); - FileSystem.File.Exists(filePath).Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void - Delete_Recursive_WithSimilarNamedFile_ShouldOnlyDeleteDirectoryAndItsContents( - string subdirectory) - { - string fileName = $"{subdirectory}.txt"; - FileSystem.Initialize() - .WithSubdirectory(subdirectory).Initialized(s => s - .WithAFile() - .WithASubdirectory()) - .WithFile(fileName); - - FileSystem.Directory.Delete(subdirectory, true); - - FileSystem.Directory.Exists(subdirectory).Should().BeFalse(); - FileSystem.File.Exists(fileName).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void Delete_Recursive_WithSubdirectory_ShouldDeleteDirectoryWithContent( - string path, string subdirectory) - { - string subdirectoryPath = FileSystem.Path.Combine(path, subdirectory); - FileSystem.Directory.CreateDirectory(subdirectoryPath); - FileSystem.Directory.Exists(path).Should().BeTrue(); - - FileSystem.Directory.Delete(path, true); - - FileSystem.Directory.Exists(path).Should().BeFalse(); - FileSystem.Directory.Exists(subdirectoryPath).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Delete_ShouldAdjustTimes(string path, string subdirectoryName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - string subdirectoryPath = FileSystem.Path.Combine(path, subdirectoryName); - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.Directory.CreateDirectory(subdirectoryPath); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - - FileSystem.Directory.Delete(subdirectoryPath); - - DateTime creationTime = FileSystem.Directory.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.Directory.GetLastAccessTimeUtc(path); - DateTime lastWriteTime = FileSystem.Directory.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - } - - [SkippableTheory] - [AutoData] - public void Delete_ShouldDeleteDirectory(string directoryName) - { - IDirectoryInfo result = - FileSystem.Directory.CreateDirectory(directoryName); - - FileSystem.Directory.Delete(directoryName); - - bool exists = FileSystem.Directory.Exists(directoryName); - - exists.Should().BeFalse(); - result.Exists.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Delete_WithSimilarNamedFile_ShouldOnlyDeleteDirectory( - string subdirectory) - { - string fileName = $"{subdirectory}.txt"; - FileSystem.Initialize() - .WithSubdirectory(subdirectory) - .WithFile(fileName); - - FileSystem.Directory.Delete(subdirectory); - - FileSystem.Directory.Exists(subdirectory).Should().BeFalse(); - FileSystem.File.Exists(fileName).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void Delete_WithSubdirectory_ShouldThrowIOException_AndNotDeleteDirectory( - string path, string subdirectory) - { - FileSystem.Directory.CreateDirectory(FileSystem.Path.Combine(path, subdirectory)); - FileSystem.Directory.Exists(path).Should().BeTrue(); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Delete(path); - }); - - exception.Should().BeException( - hResult: Test.RunsOnWindows ? -2147024751 : Test.RunsOnMac ? 66 : 39, - // Path information only included in exception message on Windows and not in .NET Framework - messageContains: !Test.RunsOnWindows || Test.IsNetFramework - ? null - : $"'{System.IO.Path.Combine(BasePath, path)}'"); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class DeleteTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + Delete_CaseDifferentPath_ShouldThrowDirectoryNotFoundException_OnLinux( + string directoryName) + { + directoryName = directoryName.ToLowerInvariant(); + FileSystem.Directory.CreateDirectory(directoryName.ToUpperInvariant()); + string expectedPath = System.IO.Path.Combine(BasePath, directoryName); + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Delete(directoryName); + }); + + if (Test.RunsOnLinux) + { + exception.Should().BeException($"'{expectedPath}'", + hResult: -2147024893); + } + else + { + exception.Should().BeNull(); + FileSystem.Directory.Exists(directoryName.ToUpperInvariant()) + .Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void Delete_FullPath_ShouldDeleteDirectory(string directoryName) + { + IDirectoryInfo result = + FileSystem.Directory.CreateDirectory(directoryName); + + FileSystem.Directory.Delete(result.FullName); + + FileSystem.Directory.Exists(directoryName).Should().BeFalse(); + result.Exists.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string directoryName) + { + string expectedPath = System.IO.Path.Combine(BasePath, directoryName); + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Delete(directoryName); + }); + + exception.Should().BeException($"'{expectedPath}'", + hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Delete_Recursive_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string directoryName) + { + string expectedPath = System.IO.Path.Combine(BasePath, directoryName); + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Delete(directoryName, true); + }); + + exception.Should().BeException($"'{expectedPath}'", + hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Delete_Recursive_WithOpenFile_ShouldThrowIOException_OnWindows( + string path, string filename) + { + FileSystem.Initialize() + .WithSubdirectory(path); + string filePath = FileSystem.Path.Combine(path, filename); + FileSystemStream openFile = FileSystem.File.OpenWrite(filePath); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Delete(path, true); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException($"{filename}'", + hResult: -2147024864); + FileSystem.File.Exists(filePath).Should().BeTrue(); + } + else + { + exception.Should().BeNull(); + FileSystem.File.Exists(filePath).Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void + Delete_Recursive_WithSimilarNamedFile_ShouldOnlyDeleteDirectoryAndItsContents( + string subdirectory) + { + string fileName = $"{subdirectory}.txt"; + FileSystem.Initialize() + .WithSubdirectory(subdirectory).Initialized(s => s + .WithAFile() + .WithASubdirectory()) + .WithFile(fileName); + + FileSystem.Directory.Delete(subdirectory, true); + + FileSystem.Directory.Exists(subdirectory).Should().BeFalse(); + FileSystem.File.Exists(fileName).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void Delete_Recursive_WithSubdirectory_ShouldDeleteDirectoryWithContent( + string path, string subdirectory) + { + string subdirectoryPath = FileSystem.Path.Combine(path, subdirectory); + FileSystem.Directory.CreateDirectory(subdirectoryPath); + FileSystem.Directory.Exists(path).Should().BeTrue(); + + FileSystem.Directory.Delete(path, true); + + FileSystem.Directory.Exists(path).Should().BeFalse(); + FileSystem.Directory.Exists(subdirectoryPath).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Delete_ShouldAdjustTimes(string path, string subdirectoryName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + string subdirectoryPath = FileSystem.Path.Combine(path, subdirectoryName); + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.Directory.CreateDirectory(subdirectoryPath); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + + FileSystem.Directory.Delete(subdirectoryPath); + + DateTime creationTime = FileSystem.Directory.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.Directory.GetLastAccessTimeUtc(path); + DateTime lastWriteTime = FileSystem.Directory.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + } + + [SkippableTheory] + [AutoData] + public void Delete_ShouldDeleteDirectory(string directoryName) + { + IDirectoryInfo result = + FileSystem.Directory.CreateDirectory(directoryName); + + FileSystem.Directory.Delete(directoryName); + + bool exists = FileSystem.Directory.Exists(directoryName); + + exists.Should().BeFalse(); + result.Exists.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Delete_WithSimilarNamedFile_ShouldOnlyDeleteDirectory( + string subdirectory) + { + string fileName = $"{subdirectory}.txt"; + FileSystem.Initialize() + .WithSubdirectory(subdirectory) + .WithFile(fileName); + + FileSystem.Directory.Delete(subdirectory); + + FileSystem.Directory.Exists(subdirectory).Should().BeFalse(); + FileSystem.File.Exists(fileName).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void Delete_WithSubdirectory_ShouldThrowIOException_AndNotDeleteDirectory( + string path, string subdirectory) + { + FileSystem.Directory.CreateDirectory(FileSystem.Path.Combine(path, subdirectory)); + FileSystem.Directory.Exists(path).Should().BeTrue(); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Delete(path); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147024751 : Test.RunsOnMac ? 66 : 39, + // Path information only included in exception message on Windows and not in .NET Framework + messageContains: !Test.RunsOnWindows || Test.IsNetFramework + ? null + : $"'{System.IO.Path.Combine(BasePath, path)}'"); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/EnumerateDirectoriesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/EnumerateDirectoriesTests.cs index 4e4ae0778..32f2819de 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/EnumerateDirectoriesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/EnumerateDirectoriesTests.cs @@ -1,249 +1,249 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class EnumerateDirectoriesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableFact] - public void EnumerateDirectories_AbsolutePath_ShouldNotIncludeTrailingSlash() - { - FileSystem.Directory.CreateDirectory("foo"); - FileSystem.Directory.CreateDirectory("bar"); - - List result = FileSystem.Directory - .EnumerateDirectories(BasePath) - .ToList(); - - result.Should().Contain(FileSystem.Path.Combine(BasePath, "foo")); - result.Should().Contain(FileSystem.Path.Combine(BasePath, "bar")); - } - - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string path) - { - string expectedPath = System.IO.Path.Combine(BasePath, path); - Exception? exception = - Record.Exception(() - => FileSystem.Directory.EnumerateDirectories(path).ToList()); - - exception.Should().BeException( - $"'{expectedPath}'", - hResult: -2147024893); - FileSystem.Directory.Exists(path).Should().BeFalse(); - } - - [SkippableFact] - public void EnumerateDirectories_RelativePath_ShouldNotIncludeTrailingSlash() - { - string path = "."; - FileSystem.Directory.CreateDirectory("foo"); - FileSystem.Directory.CreateDirectory("bar"); - - List result = FileSystem.Directory - .EnumerateDirectories(path) - .ToList(); - - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - result.Should().Contain(FileSystem.Path.Combine(path, "bar")); - } - - [SkippableFact] - public void - EnumerateDirectories_RelativePathToParentDirectory_ShouldNotIncludeTrailingSlash() - { - string path = "foo/.."; - FileSystem.Directory.CreateDirectory("foo"); - FileSystem.Directory.CreateDirectory("bar"); - - List result = FileSystem.Directory - .EnumerateDirectories(path) - .ToList(); - - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - result.Should().Contain(FileSystem.Path.Combine(path, "bar")); - } - - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_SearchOptionAllDirectories_FullPath_ShouldReturnAllSubdirectoriesWithFullPath( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory - .EnumerateDirectories(baseDirectory.FullName, "*", SearchOption.AllDirectories) - .ToList(); - - result.Count.Should().Be(3); - result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo")); - result.Should() - .Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo", "xyz")); - result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "bar")); - } - - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory - .EnumerateDirectories(path, "*", SearchOption.AllDirectories).ToList(); - - result.Count.Should().Be(3); - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); - result.Should().Contain(FileSystem.Path.Combine(path, "bar")); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void EnumerateDirectories_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string subdirectoryName) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory("foo"); - baseDirectory.CreateSubdirectory(subdirectoryName); - - List result = FileSystem.Directory - .EnumerateDirectories("foo", searchPattern).ToList(); - - if (expectToBeFound) - { - result.Should().ContainSingle( - FileSystem.Path.Combine("foo", subdirectoryName), - $"it should match {searchPattern}"); - } - else - { - result.Should() - .BeEmpty($"{subdirectoryName} should not match {searchPattern}"); - } - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory - .EnumerateDirectories(path, "XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }).ToList(); - - result.Count.Should().Be(1); - result.Should().NotContain(FileSystem.Path.Combine(path, "foo")); - result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); - result.Should().NotContain(FileSystem.Path.Combine(path, "bar")); - } -#endif - - [SkippableTheory] - [AutoData] - public void EnumerateDirectories_WithNewline_ShouldThrowArgumentException( - string path) - { - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = FileSystem.Directory.EnumerateDirectories(path, searchPattern) - .FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory.EnumerateDirectories(path).ToList(); - - result.Count.Should().Be(2); - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - result.Should().NotContain(FileSystem.Path.Combine(path, "foo", "xyz")); - result.Should().Contain(FileSystem.Path.Combine(path, "bar")); - } - - [SkippableTheory] - [AutoData] - public void EnumerateDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo"); - baseDirectory.CreateSubdirectory("bar"); - - IEnumerable result = - FileSystem.Directory.EnumerateDirectories(path, "foo"); - - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - } - - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar/xyz"); - - IEnumerable result = FileSystem.Directory - .EnumerateDirectories(path, "xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(2); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class EnumerateDirectoriesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableFact] + public void EnumerateDirectories_AbsolutePath_ShouldNotIncludeTrailingSlash() + { + FileSystem.Directory.CreateDirectory("foo"); + FileSystem.Directory.CreateDirectory("bar"); + + List result = FileSystem.Directory + .EnumerateDirectories(BasePath) + .ToList(); + + result.Should().Contain(FileSystem.Path.Combine(BasePath, "foo")); + result.Should().Contain(FileSystem.Path.Combine(BasePath, "bar")); + } + + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string path) + { + string expectedPath = System.IO.Path.Combine(BasePath, path); + Exception? exception = + Record.Exception(() + => FileSystem.Directory.EnumerateDirectories(path).ToList()); + + exception.Should().BeException( + $"'{expectedPath}'", + hResult: -2147024893); + FileSystem.Directory.Exists(path).Should().BeFalse(); + } + + [SkippableFact] + public void EnumerateDirectories_RelativePath_ShouldNotIncludeTrailingSlash() + { + string path = "."; + FileSystem.Directory.CreateDirectory("foo"); + FileSystem.Directory.CreateDirectory("bar"); + + List result = FileSystem.Directory + .EnumerateDirectories(path) + .ToList(); + + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + result.Should().Contain(FileSystem.Path.Combine(path, "bar")); + } + + [SkippableFact] + public void + EnumerateDirectories_RelativePathToParentDirectory_ShouldNotIncludeTrailingSlash() + { + string path = "foo/.."; + FileSystem.Directory.CreateDirectory("foo"); + FileSystem.Directory.CreateDirectory("bar"); + + List result = FileSystem.Directory + .EnumerateDirectories(path) + .ToList(); + + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + result.Should().Contain(FileSystem.Path.Combine(path, "bar")); + } + + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_SearchOptionAllDirectories_FullPath_ShouldReturnAllSubdirectoriesWithFullPath( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory + .EnumerateDirectories(baseDirectory.FullName, "*", SearchOption.AllDirectories) + .ToList(); + + result.Count.Should().Be(3); + result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo")); + result.Should() + .Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo", "xyz")); + result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "bar")); + } + + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory + .EnumerateDirectories(path, "*", SearchOption.AllDirectories).ToList(); + + result.Count.Should().Be(3); + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); + result.Should().Contain(FileSystem.Path.Combine(path, "bar")); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void EnumerateDirectories_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string subdirectoryName) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory("foo"); + baseDirectory.CreateSubdirectory(subdirectoryName); + + List result = FileSystem.Directory + .EnumerateDirectories("foo", searchPattern).ToList(); + + if (expectToBeFound) + { + result.Should().ContainSingle( + FileSystem.Path.Combine("foo", subdirectoryName), + $"it should match {searchPattern}"); + } + else + { + result.Should() + .BeEmpty($"{subdirectoryName} should not match {searchPattern}"); + } + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory + .EnumerateDirectories(path, "XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }).ToList(); + + result.Count.Should().Be(1); + result.Should().NotContain(FileSystem.Path.Combine(path, "foo")); + result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); + result.Should().NotContain(FileSystem.Path.Combine(path, "bar")); + } +#endif + + [SkippableTheory] + [AutoData] + public void EnumerateDirectories_WithNewline_ShouldThrowArgumentException( + string path) + { + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = FileSystem.Directory.EnumerateDirectories(path, searchPattern) + .FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory.EnumerateDirectories(path).ToList(); + + result.Count.Should().Be(2); + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + result.Should().NotContain(FileSystem.Path.Combine(path, "foo", "xyz")); + result.Should().Contain(FileSystem.Path.Combine(path, "bar")); + } + + [SkippableTheory] + [AutoData] + public void EnumerateDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo"); + baseDirectory.CreateSubdirectory("bar"); + + IEnumerable result = + FileSystem.Directory.EnumerateDirectories(path, "foo"); + + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + } + + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar/xyz"); + + IEnumerable result = FileSystem.Directory + .EnumerateDirectories(path, "xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(2); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ExceptionTests.cs index 6ca506b71..6ac5188bb 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ExceptionTests.cs @@ -1,244 +1,244 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetDirectoryCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.Directory); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetDirectoryCallbacks), parameters: " ")] - public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.Directory); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetDirectoryCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.Directory); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetDirectoryCallbacks), parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.Directory); - }); - - if (!Test.RunsOnWindows) - { - if (exception is IOException ioException) - { - ioException.HResult.Should().NotBe(-2147024809, - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - else - { - if (Test.IsNetFramework) - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should().BeException( - hResult: -2147024773, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - } - - #region Helpers - - public static IEnumerable GetDirectoryCallbacks(string? path) - => GetDirectoryCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string ParamName, - Expression> Callback)> - GetDirectoryCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.CreateDirectory(value)); -#if FEATURE_FILESYSTEM_UNIXFILEMODE - if (!Test.RunsOnWindows) - { - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.CreateDirectory(value, UnixFileMode.None)); - } -#endif -#if FEATURE_FILESYSTEM_LINK - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.CreateSymbolicLink(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "pathToTarget", directory - => directory.CreateSymbolicLink("foo", value)); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.Delete(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.Delete(value, true)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateDirectories(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateDirectories(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateDirectories(value, "foo", SearchOption.AllDirectories)); -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateDirectories(value, "foo", new EnumerationOptions())); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFiles(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFiles(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFiles(value, "foo", SearchOption.AllDirectories)); -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFiles(value, "foo", new EnumerationOptions())); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFileSystemEntries(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFileSystemEntries(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFileSystemEntries(value, "foo", - SearchOption.AllDirectories)); -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.EnumerateFileSystemEntries(value, "foo", - new EnumerationOptions())); -#endif - // `Directory.Exists` doesn't throw an exception on `null` - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetCreationTime(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetCreationTimeUtc(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetDirectories(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetDirectories(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetDirectories(value, "foo", SearchOption.AllDirectories)); -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetDirectories(value, "foo", new EnumerationOptions())); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFiles(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFiles(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFiles(value, "foo", SearchOption.AllDirectories)); -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFiles(value, "foo", new EnumerationOptions())); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFileSystemEntries(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFileSystemEntries(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFileSystemEntries(value, "foo", SearchOption.AllDirectories)); -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetFileSystemEntries(value, "foo", new EnumerationOptions())); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetLastAccessTime(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetLastAccessTimeUtc(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetLastWriteTime(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.GetLastWriteTimeUtc(value)); - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "path", - directory - => directory.GetParent(value)); - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "sourceDirName", - directory - => directory.Move(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destDirName", - directory - => directory.Move("foo", value)); -#if FEATURE_FILESYSTEM_LINK - yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "linkPath", - directory - => directory.ResolveLinkTarget(value, false)); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.SetCreationTime(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.SetCreationTimeUtc(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.SetLastAccessTime(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.SetLastAccessTimeUtc(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.SetLastWriteTime(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", directory - => directory.SetLastWriteTimeUtc(value, DateTime.Now)); - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetDirectoryCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.Directory); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetDirectoryCallbacks), parameters: " ")] + public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.Directory); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetDirectoryCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.Directory); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetDirectoryCallbacks), parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.Directory); + }); + + if (!Test.RunsOnWindows) + { + if (exception is IOException ioException) + { + ioException.HResult.Should().NotBe(-2147024809, + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + else + { + if (Test.IsNetFramework) + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should().BeException( + hResult: -2147024773, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + } + + #region Helpers + + public static IEnumerable GetDirectoryCallbacks(string? path) + => GetDirectoryCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string ParamName, + Expression> Callback)> + GetDirectoryCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.CreateDirectory(value)); +#if FEATURE_FILESYSTEM_UNIXFILEMODE + if (!Test.RunsOnWindows) + { + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.CreateDirectory(value, UnixFileMode.None)); + } +#endif +#if FEATURE_FILESYSTEM_LINK + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.CreateSymbolicLink(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "pathToTarget", directory + => directory.CreateSymbolicLink("foo", value)); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.Delete(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.Delete(value, true)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateDirectories(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateDirectories(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateDirectories(value, "foo", SearchOption.AllDirectories)); +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateDirectories(value, "foo", new EnumerationOptions())); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFiles(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFiles(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFiles(value, "foo", SearchOption.AllDirectories)); +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFiles(value, "foo", new EnumerationOptions())); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFileSystemEntries(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFileSystemEntries(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFileSystemEntries(value, "foo", + SearchOption.AllDirectories)); +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.EnumerateFileSystemEntries(value, "foo", + new EnumerationOptions())); +#endif + // `Directory.Exists` doesn't throw an exception on `null` + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetCreationTime(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetCreationTimeUtc(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetDirectories(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetDirectories(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetDirectories(value, "foo", SearchOption.AllDirectories)); +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetDirectories(value, "foo", new EnumerationOptions())); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFiles(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFiles(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFiles(value, "foo", SearchOption.AllDirectories)); +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFiles(value, "foo", new EnumerationOptions())); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFileSystemEntries(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFileSystemEntries(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFileSystemEntries(value, "foo", SearchOption.AllDirectories)); +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetFileSystemEntries(value, "foo", new EnumerationOptions())); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetLastAccessTime(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetLastAccessTimeUtc(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetLastWriteTime(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.GetLastWriteTimeUtc(value)); + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "path", + directory + => directory.GetParent(value)); + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "sourceDirName", + directory + => directory.Move(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destDirName", + directory + => directory.Move("foo", value)); +#if FEATURE_FILESYSTEM_LINK + yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "linkPath", + directory + => directory.ResolveLinkTarget(value, false)); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.SetCreationTime(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.SetCreationTimeUtc(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.SetLastAccessTime(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.SetLastAccessTimeUtc(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.SetLastWriteTime(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", directory + => directory.SetLastWriteTimeUtc(value, DateTime.Now)); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/GetDirectoriesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/GetDirectoriesTests.cs index c52b7e066..1a0f5fe33 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/GetDirectoriesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/GetDirectoriesTests.cs @@ -1,214 +1,214 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class GetDirectoriesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - GetDirectories_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string path) - { - string expectedPath = System.IO.Path.Combine(BasePath, path); - Exception? exception = - Record.Exception(() - => FileSystem.Directory.GetDirectories(path).ToList()); - - exception.Should().BeException($"'{expectedPath}'", - hResult: -2147024893); - FileSystem.Directory.Exists(path).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void - GetDirectories_SearchOptionAllDirectories_FullPath_ShouldReturnAllSubdirectoriesWithFullPath( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory - .GetDirectories(baseDirectory.FullName, "*", SearchOption.AllDirectories) - .ToList(); - - result.Count.Should().Be(3); - result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo")); - result.Should() - .Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo", "xyz")); - result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "bar")); - } - - [SkippableTheory] - [AutoData] - public void - GetDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory - .GetDirectories(path, "*", SearchOption.AllDirectories).ToList(); - - result.Count.Should().Be(3); - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); - result.Should().Contain(FileSystem.Path.Combine(path, "bar")); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void GetDirectories_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string subdirectoryName) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory("foo"); - baseDirectory.CreateSubdirectory(subdirectoryName); - - List result = FileSystem.Directory - .GetDirectories("foo", searchPattern).ToList(); - - if (expectToBeFound) - { - result.Should().ContainSingle( - FileSystem.Path.Combine("foo", subdirectoryName), - $"it should match {searchPattern}"); - } - else - { - result.Should() - .BeEmpty($"{subdirectoryName} should not match {searchPattern}"); - } - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableTheory] - [AutoData] - public void - GetDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory - .GetDirectories(path, "XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }).ToList(); - - result.Count.Should().Be(1); - result.Should().NotContain(FileSystem.Path.Combine(path, "foo")); - result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); - result.Should().NotContain(FileSystem.Path.Combine(path, "bar")); - } -#endif - - [SkippableTheory] - [AutoData] - public void GetDirectories_WithNewline_ShouldThrowArgumentException( - string path) - { - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = FileSystem.Directory.GetDirectories(path, searchPattern) - .FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableTheory] - [AutoData] - public void - GetDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - List result = FileSystem.Directory.GetDirectories(path).ToList(); - - result.Count.Should().Be(2); - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - result.Should().NotContain(FileSystem.Path.Combine(path, "foo", "xyz")); - result.Should().Contain(FileSystem.Path.Combine(path, "bar")); - } - - [SkippableFact] - public void GetDirectories_WithRelativePath_ShouldReturnRelativePaths() - { - string path = $"foo{FileSystem.Path.DirectorySeparatorChar}bar"; - FileSystem.Directory.CreateDirectory(path); - - string[] result = FileSystem.Directory.GetDirectories("foo"); - - result.Should().BeEquivalentTo(path); - } - - [SkippableTheory] - [AutoData] - public void GetDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo"); - baseDirectory.CreateSubdirectory("bar"); - - IEnumerable result = - FileSystem.Directory.GetDirectories(path, "foo"); - - result.Should().Contain(FileSystem.Path.Combine(path, "foo")); - } - - [SkippableTheory] - [AutoData] - public void - GetDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar/xyz"); - - IEnumerable result = FileSystem.Directory - .GetDirectories(path, "xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(2); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class GetDirectoriesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + GetDirectories_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string path) + { + string expectedPath = System.IO.Path.Combine(BasePath, path); + Exception? exception = + Record.Exception(() + => FileSystem.Directory.GetDirectories(path).ToList()); + + exception.Should().BeException($"'{expectedPath}'", + hResult: -2147024893); + FileSystem.Directory.Exists(path).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void + GetDirectories_SearchOptionAllDirectories_FullPath_ShouldReturnAllSubdirectoriesWithFullPath( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory + .GetDirectories(baseDirectory.FullName, "*", SearchOption.AllDirectories) + .ToList(); + + result.Count.Should().Be(3); + result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo")); + result.Should() + .Contain(FileSystem.Path.Combine(baseDirectory.FullName, "foo", "xyz")); + result.Should().Contain(FileSystem.Path.Combine(baseDirectory.FullName, "bar")); + } + + [SkippableTheory] + [AutoData] + public void + GetDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory + .GetDirectories(path, "*", SearchOption.AllDirectories).ToList(); + + result.Count.Should().Be(3); + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); + result.Should().Contain(FileSystem.Path.Combine(path, "bar")); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void GetDirectories_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string subdirectoryName) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory("foo"); + baseDirectory.CreateSubdirectory(subdirectoryName); + + List result = FileSystem.Directory + .GetDirectories("foo", searchPattern).ToList(); + + if (expectToBeFound) + { + result.Should().ContainSingle( + FileSystem.Path.Combine("foo", subdirectoryName), + $"it should match {searchPattern}"); + } + else + { + result.Should() + .BeEmpty($"{subdirectoryName} should not match {searchPattern}"); + } + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableTheory] + [AutoData] + public void + GetDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory + .GetDirectories(path, "XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }).ToList(); + + result.Count.Should().Be(1); + result.Should().NotContain(FileSystem.Path.Combine(path, "foo")); + result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz")); + result.Should().NotContain(FileSystem.Path.Combine(path, "bar")); + } +#endif + + [SkippableTheory] + [AutoData] + public void GetDirectories_WithNewline_ShouldThrowArgumentException( + string path) + { + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = FileSystem.Directory.GetDirectories(path, searchPattern) + .FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableTheory] + [AutoData] + public void + GetDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + List result = FileSystem.Directory.GetDirectories(path).ToList(); + + result.Count.Should().Be(2); + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + result.Should().NotContain(FileSystem.Path.Combine(path, "foo", "xyz")); + result.Should().Contain(FileSystem.Path.Combine(path, "bar")); + } + + [SkippableFact] + public void GetDirectories_WithRelativePath_ShouldReturnRelativePaths() + { + string path = $"foo{FileSystem.Path.DirectorySeparatorChar}bar"; + FileSystem.Directory.CreateDirectory(path); + + string[] result = FileSystem.Directory.GetDirectories("foo"); + + result.Should().BeEquivalentTo(path); + } + + [SkippableTheory] + [AutoData] + public void GetDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo"); + baseDirectory.CreateSubdirectory("bar"); + + IEnumerable result = + FileSystem.Directory.GetDirectories(path, "foo"); + + result.Should().Contain(FileSystem.Path.Combine(path, "foo")); + } + + [SkippableTheory] + [AutoData] + public void + GetDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar/xyz"); + + IEnumerable result = FileSystem.Directory + .GetDirectories(path, "xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(2); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/MoveTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/MoveTests.cs index 277e1fa5c..6809b4077 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/MoveTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/MoveTests.cs @@ -1,291 +1,291 @@ -using System.IO; -using Testably.Abstractions.Testing.FileSystemInitializer; -#if !NETFRAMEWORK -#endif - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class MoveTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Move_CaseOnlyChange_ShouldMoveDirectoryWithContent(string path) - { - Skip.If(Test.IsNetFramework); - - string source = path.ToLowerInvariant(); - string destination = path.ToUpperInvariant(); - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - - FileSystem.Directory.Move(source, destination); - - FileSystem.Directory.Exists(source).Should().Be(!Test.RunsOnLinux); - FileSystem.Directory.Exists(destination).Should().BeTrue(); - FileSystem.Directory.GetDirectories(".").Should() - .ContainSingle(d => d.Contains(destination)); - FileSystem.Directory.GetFiles(destination, initialized[1].Name) - .Should().ContainSingle(); - FileSystem.Directory.GetDirectories(destination, initialized[2].Name) - .Should().ContainSingle(); - FileSystem.Directory.GetFiles(destination, initialized[3].Name, - SearchOption.AllDirectories) - .Should().ContainSingle(); - FileSystem.Directory.GetDirectories(destination, initialized[4].Name, - SearchOption.AllDirectories) - .Should().ContainSingle(); - } - - [SkippableTheory] - [AutoData] - public void Move_CaseOnlyChange_ShouldThrowIOException_OnNetFramework(string path) - { - Skip.IfNot(Test.IsNetFramework); - - string source = path.ToLowerInvariant(); - string destination = path.ToUpperInvariant(); - FileSystem.Initialize() - .WithSubdirectory(source); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Move(source, destination); - }); - - exception.Should().BeException(hResult: -2146232800); - } - - [SkippableTheory] - [AutoData] - public void Move_DestinationDoesNotExist_ShouldThrowDirectoryNotFoundException( - string source) - { - FileSystem.InitializeIn(source) - .WithAFile(); - string destination = FileTestHelper.RootDrive("not-existing/path"); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Move(source, destination); - }); - - exception.Should().BeException(hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Move_ShouldMoveAttributes(string source, string destination) - { - FileSystem.Initialize() - .WithSubdirectory(source); - FileSystem.DirectoryInfo.New(source).Attributes |= FileAttributes.System; - FileAttributes expectedAttributes = - FileSystem.DirectoryInfo.New(source).Attributes; - - FileSystem.Directory.Move(source, destination); - - FileSystem.DirectoryInfo.New(destination).Attributes - .Should().Be(expectedAttributes); - } - - [SkippableTheory] - [AutoData] - public void Move_ShouldMoveDirectoryWithContent(string source, string destination) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - - FileSystem.Directory.Move(source, destination); - - FileSystem.Directory.Exists(source).Should().BeFalse(); - FileSystem.Directory.Exists(destination).Should().BeTrue(); - FileSystem.Directory.GetFiles(destination, initialized[1].Name) - .Should().ContainSingle(); - FileSystem.Directory.GetDirectories(destination, initialized[2].Name) - .Should().ContainSingle(); - FileSystem.Directory.GetFiles(destination, initialized[3].Name, - SearchOption.AllDirectories) - .Should().ContainSingle(); - FileSystem.Directory.GetDirectories(destination, initialized[4].Name, - SearchOption.AllDirectories) - .Should().ContainSingle(); - } - - [SkippableTheory] - [AutoData] - public void Move_ShouldNotAdjustTimes(string source, string destination) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - - FileSystem.Directory.Move(source, destination); - - DateTime creationTime = FileSystem.Directory.GetCreationTimeUtc(destination); - DateTime lastAccessTime = FileSystem.Directory.GetLastAccessTimeUtc(destination); - DateTime lastWriteTime = FileSystem.Directory.GetLastWriteTimeUtc(destination); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - [SkippableTheory] - [AutoData] - public void Move_SourceAndDestinationIdentical_ShouldThrowIOException(string path) - { - FileSystem.Initialize() - .WithSubdirectory(path); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Move(path, path); - }); - - exception.Should().BeException(hResult: -2146232800); - } - - [SkippableTheory] - [AutoData] - public void Move_WithLockedFile_ShouldThrowIOException_AndNotMoveDirectoryAtAll_OnWindows( - string source, string destination) - { - Skip.IfNot(Test.RunsOnWindows); - - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, - FileMode.Open, - FileAccess.Read, - FileShare.Read); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Move(source, destination); - }); - - exception.Should().BeException(hResult: -2147024891); - FileSystem.Directory.Exists(source).Should().BeTrue(); - FileSystem.Directory.Exists(destination).Should().BeFalse(); - IDirectoryInfo sourceDirectory = - FileSystem.DirectoryInfo.New(source); - sourceDirectory.GetFiles(initialized[1].Name) - .Should().ContainSingle(); - sourceDirectory.GetDirectories(initialized[2].Name) - .Should().ContainSingle(); - sourceDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - sourceDirectory - .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - } - - [SkippableTheory] - [AutoData] - public void Move_WithLockedFile_ShouldStillMoveDirectory_NotOnWindows( - string source, string destination) - { - Skip.If(Test.RunsOnWindows); - - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, - FileMode.Open, - FileAccess.Read, - FileShare.Read); - - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.Move(source, destination); - }); - - exception.Should().BeNull(); - FileSystem.Directory.Exists(source).Should().BeFalse(); - FileSystem.Directory.Exists(destination).Should().BeTrue(); - IDirectoryInfo destinationDirectory = - FileSystem.DirectoryInfo.New(destination); - destinationDirectory.GetFiles(initialized[1].Name) - .Should().ContainSingle(); - destinationDirectory.GetDirectories(initialized[2].Name) - .Should().ContainSingle(); - destinationDirectory - .GetFiles(initialized[3].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - destinationDirectory - .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - } - - [SkippableTheory] - [AutoData] - public void Move_WithReadOnlyFile_ShouldMoveDirectoryWithContent( - string source, string destination) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - initialized[3].Attributes = FileAttributes.ReadOnly; - - FileSystem.Directory.Move(source, destination); - - FileSystem.Directory.Exists(source).Should().BeFalse(); - FileSystem.Directory.Exists(destination).Should().BeTrue(); - IDirectoryInfo destinationDirectory = - FileSystem.DirectoryInfo.New(destination); - destinationDirectory.GetFiles(initialized[1].Name) - .Should().ContainSingle(); - destinationDirectory.GetDirectories(initialized[2].Name) - .Should().ContainSingle(); - destinationDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) - .Should().ContainSingle().Which.Attributes.Should() - .HaveFlag(FileAttributes.ReadOnly); - destinationDirectory - .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - } -} +using System.IO; +using Testably.Abstractions.Testing.FileSystemInitializer; +#if !NETFRAMEWORK +#endif + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class MoveTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Move_CaseOnlyChange_ShouldMoveDirectoryWithContent(string path) + { + Skip.If(Test.IsNetFramework); + + string source = path.ToLowerInvariant(); + string destination = path.ToUpperInvariant(); + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + + FileSystem.Directory.Move(source, destination); + + FileSystem.Directory.Exists(source).Should().Be(!Test.RunsOnLinux); + FileSystem.Directory.Exists(destination).Should().BeTrue(); + FileSystem.Directory.GetDirectories(".").Should() + .ContainSingle(d => d.Contains(destination)); + FileSystem.Directory.GetFiles(destination, initialized[1].Name) + .Should().ContainSingle(); + FileSystem.Directory.GetDirectories(destination, initialized[2].Name) + .Should().ContainSingle(); + FileSystem.Directory.GetFiles(destination, initialized[3].Name, + SearchOption.AllDirectories) + .Should().ContainSingle(); + FileSystem.Directory.GetDirectories(destination, initialized[4].Name, + SearchOption.AllDirectories) + .Should().ContainSingle(); + } + + [SkippableTheory] + [AutoData] + public void Move_CaseOnlyChange_ShouldThrowIOException_OnNetFramework(string path) + { + Skip.IfNot(Test.IsNetFramework); + + string source = path.ToLowerInvariant(); + string destination = path.ToUpperInvariant(); + FileSystem.Initialize() + .WithSubdirectory(source); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Move(source, destination); + }); + + exception.Should().BeException(hResult: -2146232800); + } + + [SkippableTheory] + [AutoData] + public void Move_DestinationDoesNotExist_ShouldThrowDirectoryNotFoundException( + string source) + { + FileSystem.InitializeIn(source) + .WithAFile(); + string destination = FileTestHelper.RootDrive("not-existing/path"); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Move(source, destination); + }); + + exception.Should().BeException(hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Move_ShouldMoveAttributes(string source, string destination) + { + FileSystem.Initialize() + .WithSubdirectory(source); + FileSystem.DirectoryInfo.New(source).Attributes |= FileAttributes.System; + FileAttributes expectedAttributes = + FileSystem.DirectoryInfo.New(source).Attributes; + + FileSystem.Directory.Move(source, destination); + + FileSystem.DirectoryInfo.New(destination).Attributes + .Should().Be(expectedAttributes); + } + + [SkippableTheory] + [AutoData] + public void Move_ShouldMoveDirectoryWithContent(string source, string destination) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + + FileSystem.Directory.Move(source, destination); + + FileSystem.Directory.Exists(source).Should().BeFalse(); + FileSystem.Directory.Exists(destination).Should().BeTrue(); + FileSystem.Directory.GetFiles(destination, initialized[1].Name) + .Should().ContainSingle(); + FileSystem.Directory.GetDirectories(destination, initialized[2].Name) + .Should().ContainSingle(); + FileSystem.Directory.GetFiles(destination, initialized[3].Name, + SearchOption.AllDirectories) + .Should().ContainSingle(); + FileSystem.Directory.GetDirectories(destination, initialized[4].Name, + SearchOption.AllDirectories) + .Should().ContainSingle(); + } + + [SkippableTheory] + [AutoData] + public void Move_ShouldNotAdjustTimes(string source, string destination) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + + FileSystem.Directory.Move(source, destination); + + DateTime creationTime = FileSystem.Directory.GetCreationTimeUtc(destination); + DateTime lastAccessTime = FileSystem.Directory.GetLastAccessTimeUtc(destination); + DateTime lastWriteTime = FileSystem.Directory.GetLastWriteTimeUtc(destination); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + [SkippableTheory] + [AutoData] + public void Move_SourceAndDestinationIdentical_ShouldThrowIOException(string path) + { + FileSystem.Initialize() + .WithSubdirectory(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Move(path, path); + }); + + exception.Should().BeException(hResult: -2146232800); + } + + [SkippableTheory] + [AutoData] + public void Move_WithLockedFile_ShouldThrowIOException_AndNotMoveDirectoryAtAll_OnWindows( + string source, string destination) + { + Skip.IfNot(Test.RunsOnWindows); + + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, + FileMode.Open, + FileAccess.Read, + FileShare.Read); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Move(source, destination); + }); + + exception.Should().BeException(hResult: -2147024891); + FileSystem.Directory.Exists(source).Should().BeTrue(); + FileSystem.Directory.Exists(destination).Should().BeFalse(); + IDirectoryInfo sourceDirectory = + FileSystem.DirectoryInfo.New(source); + sourceDirectory.GetFiles(initialized[1].Name) + .Should().ContainSingle(); + sourceDirectory.GetDirectories(initialized[2].Name) + .Should().ContainSingle(); + sourceDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + sourceDirectory + .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + } + + [SkippableTheory] + [AutoData] + public void Move_WithLockedFile_ShouldStillMoveDirectory_NotOnWindows( + string source, string destination) + { + Skip.If(Test.RunsOnWindows); + + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, + FileMode.Open, + FileAccess.Read, + FileShare.Read); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.Move(source, destination); + }); + + exception.Should().BeNull(); + FileSystem.Directory.Exists(source).Should().BeFalse(); + FileSystem.Directory.Exists(destination).Should().BeTrue(); + IDirectoryInfo destinationDirectory = + FileSystem.DirectoryInfo.New(destination); + destinationDirectory.GetFiles(initialized[1].Name) + .Should().ContainSingle(); + destinationDirectory.GetDirectories(initialized[2].Name) + .Should().ContainSingle(); + destinationDirectory + .GetFiles(initialized[3].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + destinationDirectory + .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + } + + [SkippableTheory] + [AutoData] + public void Move_WithReadOnlyFile_ShouldMoveDirectoryWithContent( + string source, string destination) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + initialized[3].Attributes = FileAttributes.ReadOnly; + + FileSystem.Directory.Move(source, destination); + + FileSystem.Directory.Exists(source).Should().BeFalse(); + FileSystem.Directory.Exists(destination).Should().BeTrue(); + IDirectoryInfo destinationDirectory = + FileSystem.DirectoryInfo.New(destination); + destinationDirectory.GetFiles(initialized[1].Name) + .Should().ContainSingle(); + destinationDirectory.GetDirectories(initialized[2].Name) + .Should().ContainSingle(); + destinationDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) + .Should().ContainSingle().Which.Attributes.Should() + .HaveFlag(FileAttributes.ReadOnly); + destinationDirectory + .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ResolveLinkTargetTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ResolveLinkTargetTests.cs index 6e19de0cf..99d411959 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ResolveLinkTargetTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/ResolveLinkTargetTests.cs @@ -1,200 +1,201 @@ -#if FEATURE_FILESYSTEM_LINK -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ResolveLinkTargetTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - #region Test Setup - - /// - /// The maximum number of symbolic links that are followed.
- /// - ///
- private static int MaxResolveLinks => - Test.RunsOnWindows ? 63 : 40; - - #endregion - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_AbsolutePath_ShouldFollowSymbolicLink( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.Directory.CreateDirectory(pathToTarget); - FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(path, false); - - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void - ResolveLinkTarget_FileWithDifferentCase_ShouldReturnPathToMissingDirectory( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.Directory.CreateDirectory(pathToTarget); - FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); - FileSystem.Directory.Delete(pathToTarget); - FileSystem.Directory.CreateDirectory(pathToTarget.ToUpper()); - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(path, false); - - if (!Test.RunsOnLinux) - { - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeTrue(); - } - else - { - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FinalTarget_ShouldFollowSymbolicLinkToFinalTarget( - string path, string pathToFinalTarget) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - int maxLinks = MaxResolveLinks; - - FileSystem.Directory.CreateDirectory(pathToFinalTarget); - string previousPath = pathToFinalTarget; - for (int i = 0; i < maxLinks; i++) - { - string newPath = $"{path}-{i}"; - FileSystem.Directory.CreateSymbolicLink(newPath, - System.IO.Path.Combine(BasePath, previousPath)); - previousPath = newPath; - } - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(previousPath, true); - - target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToFinalTarget)); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FinalTargetWithTooManyLevels_ShouldThrowIOException( - string path, string pathToFinalTarget) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - int maxLinks = MaxResolveLinks + 1; - FileSystem.Directory.CreateDirectory(pathToFinalTarget); - string previousPath = pathToFinalTarget; - for (int i = 0; i < maxLinks; i++) - { - string newPath = $"{path}-{i}"; - FileSystem.Directory.CreateSymbolicLink(newPath, - System.IO.Path.Combine(BasePath, previousPath)); - previousPath = newPath; - } - - Exception? exception = Record.Exception(() => - { - _ = FileSystem.Directory.ResolveLinkTarget(previousPath, true); - }); - - exception.Should().BeException($"'{previousPath}'", - hResult: -2147022975); - } - - [SkippableTheory] - [AutoData] - public void - ResolveLinkTarget_MissingDirectoryInLinkChain_ShouldReturnPathToMissingDirectory( - string path, string pathToFinalTarget, string pathToMissingDirectory) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Directory.CreateDirectory(pathToFinalTarget); - FileSystem.Directory.CreateSymbolicLink(pathToMissingDirectory, - System.IO.Path.Combine(BasePath, pathToFinalTarget)); - FileSystem.Directory.CreateSymbolicLink(path, - System.IO.Path.Combine(BasePath, pathToMissingDirectory)); - FileSystem.Directory.Delete(pathToMissingDirectory); - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(path, true); - - target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToMissingDirectory)); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_NormalDirectory_ShouldReturnNull( - string path) - { - FileSystem.Directory.CreateDirectory(path); - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(path, false); - - target.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_NormalFile_ShouldReturnNull( - string path) - { - FileSystem.File.WriteAllText(path, null); - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(path, false); - - target.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_RelativePath_ShouldFollowSymbolicLinkUnderWindows( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.Directory.CreateDirectory(pathToTarget); - FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(path, false); - - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_TargetDeletedAfterLinkCreation_ShouldReturnNull( - string path, string pathToTarget) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.Directory.CreateDirectory(pathToTarget); - FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); - FileSystem.Directory.Delete(pathToTarget); - - IFileSystemInfo? target = - FileSystem.Directory.ResolveLinkTarget(path, false); - - target!.FullName.Should().Be(targetFullPath); - - target.Exists.Should().BeFalse(); - } -} -#endif +#if FEATURE_FILESYSTEM_LINK +using System.IO; + + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ResolveLinkTargetTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + #region Test Setup + + /// + /// The maximum number of symbolic links that are followed.
+ /// + ///
+ private static int MaxResolveLinks => + Test.RunsOnWindows ? 63 : 40; + + #endregion + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_AbsolutePath_ShouldFollowSymbolicLink( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.Directory.CreateDirectory(pathToTarget); + FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(path, false); + + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void + ResolveLinkTarget_FileWithDifferentCase_ShouldReturnPathToMissingDirectory( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.Directory.CreateDirectory(pathToTarget); + FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); + FileSystem.Directory.Delete(pathToTarget); + FileSystem.Directory.CreateDirectory(pathToTarget.ToUpper()); + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(path, false); + + if (!Test.RunsOnLinux) + { + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeTrue(); + } + else + { + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FinalTarget_ShouldFollowSymbolicLinkToFinalTarget( + string path, string pathToFinalTarget) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + int maxLinks = MaxResolveLinks; + + FileSystem.Directory.CreateDirectory(pathToFinalTarget); + string previousPath = pathToFinalTarget; + for (int i = 0; i < maxLinks; i++) + { + string newPath = $"{path}-{i}"; + FileSystem.Directory.CreateSymbolicLink(newPath, + System.IO.Path.Combine(BasePath, previousPath)); + previousPath = newPath; + } + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(previousPath, true); + + target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToFinalTarget)); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FinalTargetWithTooManyLevels_ShouldThrowIOException( + string path, string pathToFinalTarget) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + int maxLinks = MaxResolveLinks + 1; + FileSystem.Directory.CreateDirectory(pathToFinalTarget); + string previousPath = pathToFinalTarget; + for (int i = 0; i < maxLinks; i++) + { + string newPath = $"{path}-{i}"; + FileSystem.Directory.CreateSymbolicLink(newPath, + System.IO.Path.Combine(BasePath, previousPath)); + previousPath = newPath; + } + + Exception? exception = Record.Exception(() => + { + _ = FileSystem.Directory.ResolveLinkTarget(previousPath, true); + }); + + exception.Should().BeException($"'{previousPath}'", + hResult: -2147022975); + } + + [SkippableTheory] + [AutoData] + public void + ResolveLinkTarget_MissingDirectoryInLinkChain_ShouldReturnPathToMissingDirectory( + string path, string pathToFinalTarget, string pathToMissingDirectory) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Directory.CreateDirectory(pathToFinalTarget); + FileSystem.Directory.CreateSymbolicLink(pathToMissingDirectory, + System.IO.Path.Combine(BasePath, pathToFinalTarget)); + FileSystem.Directory.CreateSymbolicLink(path, + System.IO.Path.Combine(BasePath, pathToMissingDirectory)); + FileSystem.Directory.Delete(pathToMissingDirectory); + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(path, true); + + target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToMissingDirectory)); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_NormalDirectory_ShouldReturnNull( + string path) + { + FileSystem.Directory.CreateDirectory(path); + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(path, false); + + target.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_NormalFile_ShouldReturnNull( + string path) + { + FileSystem.File.WriteAllText(path, null); + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(path, false); + + target.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_RelativePath_ShouldFollowSymbolicLinkUnderWindows( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.Directory.CreateDirectory(pathToTarget); + FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(path, false); + + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_TargetDeletedAfterLinkCreation_ShouldReturnNull( + string path, string pathToTarget) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.Directory.CreateDirectory(pathToTarget); + FileSystem.Directory.CreateSymbolicLink(path, targetFullPath); + FileSystem.Directory.Delete(pathToTarget); + + IFileSystemInfo? target = + FileSystem.Directory.ResolveLinkTarget(path, false); + + target!.FullName.Should().Be(targetFullPath); + + target.Exists.Should().BeFalse(); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.Times.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.Times.cs index 6617bd623..eb25a8ef3 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.Times.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.Times.cs @@ -1,513 +1,513 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -public abstract partial class Tests - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void GetCreationTime_PathNotFound_ShouldReturnNullTime(string path) - { - DateTime expectedTime = FileTestHelper.NullTime.ToLocalTime(); - - DateTime result = FileSystem.Directory.GetCreationTime(path); - - result.Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void GetCreationTimeUtc_PathNotFound_ShouldReturnNullTime(string path) - { - DateTime expectedTime = FileTestHelper.NullTime.ToUniversalTime(); - - DateTime result = FileSystem.Directory.GetCreationTimeUtc(path); - - result.Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void GetLastAccessTime_PathNotFound_ShouldReturnNullTime(string path) - { - DateTime expectedTime = FileTestHelper.NullTime.ToLocalTime(); - - DateTime result = FileSystem.Directory.GetLastAccessTime(path); - - result.Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void GetLastAccessTimeUtc_PathNotFound_ShouldReturnNullTime(string path) - { - DateTime expectedTime = FileTestHelper.NullTime.ToUniversalTime(); - - DateTime result = FileSystem.Directory.GetLastAccessTimeUtc(path); - - result.Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void GetLastWriteTime_PathNotFound_ShouldReturnNullTime(string path) - { - DateTime expectedTime = FileTestHelper.NullTime.ToLocalTime(); - - DateTime result = FileSystem.Directory.GetLastWriteTime(path); - - result.Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void GetLastWriteTimeUtc_PathNotFound_ShouldReturnNullTime(string path) - { - DateTime expectedTime = FileTestHelper.NullTime.ToUniversalTime(); - - DateTime result = FileSystem.Directory.GetLastWriteTimeUtc(path); - - result.Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void LastAccessTime_CreateSubDirectory_ShouldUpdateLastAccessAndLastWriteTime( - string path, string subPath) - { - DateTime start = TimeSystem.DateTime.Now; - IDirectoryInfo result = FileSystem.Directory.CreateDirectory(path); - TimeSystem.Thread.Sleep(100); - DateTime sleepTime = TimeSystem.DateTime.Now; - FileSystem.Directory.CreateDirectory(FileSystem.Path.Combine(path, subPath)); - - result.CreationTime.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.CreationTime.Should().BeBefore(sleepTime); - // Last Access Time is only updated on Windows - if (Test.RunsOnWindows) - { - result.LastAccessTime.Should() - .BeOnOrAfter(sleepTime.ApplySystemClockTolerance()); - result.LastAccessTime.Should().BeOnOrBefore(TimeSystem.DateTime.Now); - } - else - { - result.LastAccessTime.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.LastAccessTime.Should().BeBefore(sleepTime); - } - - result.LastWriteTime.Should().BeOnOrAfter(sleepTime.ApplySystemClockTolerance()); - result.LastWriteTime.Should().BeOnOrBefore(TimeSystem.DateTime.Now); - } - - [SkippableTheory] - [AutoData] - public void LastAccessTime_ShouldBeSet(string path) - { - DateTime start = TimeSystem.DateTime.Now; - - FileSystem.Directory.CreateDirectory(path); - - DateTime result = FileSystem.Directory.GetLastAccessTime(path); - result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.Should().BeOnOrBefore(TimeSystem.DateTime.Now); - result.Kind.Should().Be(DateTimeKind.Local); - } - - [SkippableTheory] - [AutoData] - public void LastAccessTimeUtc_ShouldBeSet(string path) - { - DateTime start = TimeSystem.DateTime.UtcNow; - - FileSystem.Directory.CreateDirectory(path); - - DateTime result = FileSystem.Directory.GetLastAccessTimeUtc(path); - result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.Should().BeOnOrBefore(TimeSystem.DateTime.UtcNow); - result.Kind.Should().Be(DateTimeKind.Utc); - } - - [SkippableTheory] - [AutoData] - public void LastWriteTime_ShouldBeSet(string path) - { - DateTime start = TimeSystem.DateTime.Now; - - FileSystem.Directory.CreateDirectory(path); - - DateTime result = FileSystem.Directory.GetLastWriteTime(path); - result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.Should().BeOnOrBefore(TimeSystem.DateTime.Now); - result.Kind.Should().Be(DateTimeKind.Local); - } - - [SkippableTheory] - [AutoData] - public void LastWriteTimeUtc_ShouldBeSet(string path) - { - DateTime start = TimeSystem.DateTime.UtcNow; - - FileSystem.Directory.CreateDirectory(path); - - DateTime result = FileSystem.Directory.GetLastWriteTimeUtc(path); - result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); - result.Should().BeOnOrBefore(TimeSystem.DateTime.UtcNow); - result.Kind.Should().Be(DateTimeKind.Utc); - } - - [SkippableTheory] - [AutoData] - public void SetCreationTime_PathNotFound_ShouldThrowCorrectException( - string path, DateTime creationTime) - { - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.SetCreationTime(path, creationTime); - }); - - if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - else - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024893); - } - } - - [SkippableTheory] - [AutoData] - public void SetCreationTime_ShouldChangeCreationTime( - string path, DateTime creationTime) - { - Skip.IfNot(Test.RunsOnWindows, - "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); - - creationTime = creationTime.ToLocalTime(); - DateTime expectedTime = creationTime.ToUniversalTime(); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetCreationTime(path, creationTime); - - FileSystem.Directory.GetCreationTimeUtc(path) - .Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void SetCreationTime_Unspecified_ShouldChangeCreationTime( - string path, DateTime creationTime) - { - Skip.IfNot(Test.RunsOnWindows, - "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); - - creationTime = DateTime.SpecifyKind(creationTime, DateTimeKind.Unspecified); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetCreationTime(path, creationTime); - - FileSystem.Directory.GetCreationTimeUtc(path) - .Should().Be(creationTime.ToUniversalTime()); - FileSystem.Directory.GetCreationTime(path) - .Should().Be(creationTime); - FileSystem.Directory.GetCreationTime(path).Kind - .Should().NotBe(DateTimeKind.Unspecified); - } - - [SkippableTheory] - [AutoData] - public void SetCreationTimeUtc_PathNotFound_ShouldThrowCorrectException( - string path, DateTime creationTime) - { - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.SetCreationTimeUtc(path, creationTime); - }); - - if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - else - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024893); - } - } - - [SkippableTheory] - [AutoData] - public void SetCreationTimeUtc_ShouldChangeCreationTime( - string path, DateTime creationTime) - { - Skip.IfNot(Test.RunsOnWindows, - "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); - - creationTime = creationTime.ToUniversalTime(); - DateTime expectedTime = creationTime.ToLocalTime(); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetCreationTimeUtc(path, creationTime); - - FileSystem.Directory.GetCreationTime(path) - .Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void SetCreationTimeUtc_Unspecified_ShouldChangeCreationTime( - string path, DateTime creationTime) - { - Skip.IfNot(Test.RunsOnWindows, - "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); - - creationTime = DateTime.SpecifyKind(creationTime, DateTimeKind.Unspecified); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetCreationTimeUtc(path, creationTime); - - FileSystem.Directory.GetCreationTimeUtc(path) - .Should().Be(creationTime); - FileSystem.Directory.GetCreationTime(path) - .Should().Be(creationTime.ToLocalTime()); - FileSystem.Directory.GetCreationTime(path).Kind - .Should().NotBe(DateTimeKind.Unspecified); - } - - [SkippableTheory] - [AutoData] - public void SetLastAccessTime_PathNotFound_ShouldThrowCorrectException( - string path, DateTime lastAccessTime) - { - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.SetLastAccessTime(path, lastAccessTime); - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - else - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024893); - } - } - - [SkippableTheory] - [AutoData] - public void SetLastAccessTime_ShouldChangeLastAccessTime( - string path, DateTime lastAccessTime) - { - lastAccessTime = lastAccessTime.ToLocalTime(); - DateTime expectedTime = lastAccessTime.ToUniversalTime(); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastAccessTime(path, lastAccessTime); - - FileSystem.Directory.GetLastAccessTimeUtc(path) - .Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void SetLastAccessTime_Unspecified_ShouldChangeLastAccessTime( - string path, DateTime lastAccessTime) - { - lastAccessTime = DateTime.SpecifyKind(lastAccessTime, DateTimeKind.Unspecified); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastAccessTime(path, lastAccessTime); - - FileSystem.Directory.GetLastAccessTimeUtc(path) - .Should().Be(lastAccessTime.ToUniversalTime()); - FileSystem.Directory.GetLastAccessTime(path) - .Should().Be(lastAccessTime); - FileSystem.Directory.GetLastAccessTime(path).Kind - .Should().NotBe(DateTimeKind.Unspecified); - } - - [SkippableTheory] - [AutoData] - public void SetLastAccessTimeUtc_PathNotFound_ShouldThrowCorrectException( - string path, DateTime lastAccessTime) - { - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.SetLastAccessTimeUtc(path, lastAccessTime); - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - else - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024893); - } - } - - [SkippableTheory] - [AutoData] - public void SetLastAccessTimeUtc_ShouldChangeLastAccessTime( - string path, DateTime lastAccessTime) - { - lastAccessTime = lastAccessTime.ToUniversalTime(); - DateTime expectedTime = lastAccessTime.ToLocalTime(); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastAccessTimeUtc(path, lastAccessTime); - - FileSystem.Directory.GetLastAccessTime(path) - .Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void SetLastAccessTimeUtc_Unspecified_ShouldChangeLastAccessTime( - string path, DateTime lastAccessTime) - { - lastAccessTime = DateTime.SpecifyKind(lastAccessTime, DateTimeKind.Unspecified); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastAccessTimeUtc(path, lastAccessTime); - - FileSystem.Directory.GetLastAccessTimeUtc(path) - .Should().Be(lastAccessTime); - FileSystem.Directory.GetLastAccessTime(path) - .Should().Be(lastAccessTime.ToLocalTime()); - FileSystem.Directory.GetLastAccessTime(path).Kind - .Should().NotBe(DateTimeKind.Unspecified); - } - - [SkippableTheory] - [AutoData] - public void SetLastWriteTime_PathNotFound_ShouldThrowCorrectException( - string path, DateTime lastWriteTime) - { - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.SetLastWriteTime(path, lastWriteTime); - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - else - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024893); - } - } - - [SkippableTheory] - [AutoData] - public void SetLastWriteTime_ShouldChangeLastWriteTime( - string path, DateTime lastWriteTime) - { - lastWriteTime = lastWriteTime.ToLocalTime(); - DateTime expectedTime = lastWriteTime.ToUniversalTime(); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastWriteTime(path, lastWriteTime); - - FileSystem.Directory.GetLastWriteTimeUtc(path) - .Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void SetLastWriteTime_Unspecified_ShouldChangeLastWriteTime( - string path, DateTime lastWriteTime) - { - lastWriteTime = DateTime.SpecifyKind(lastWriteTime, DateTimeKind.Unspecified); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastWriteTime(path, lastWriteTime); - - FileSystem.Directory.GetLastWriteTimeUtc(path) - .Should().Be(lastWriteTime.ToUniversalTime()); - FileSystem.Directory.GetLastWriteTime(path) - .Should().Be(lastWriteTime); - FileSystem.Directory.GetLastWriteTime(path).Kind - .Should().NotBe(DateTimeKind.Unspecified); - } - - [SkippableTheory] - [AutoData] - public void SetLastWriteTimeUtc_PathNotFound_ShouldThrowCorrectException( - string path, DateTime lastWriteTime) - { - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.SetLastWriteTimeUtc(path, lastWriteTime); - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - else - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024893); - } - } - - [SkippableTheory] - [AutoData] - public void SetLastWriteTimeUtc_ShouldChangeLastWriteTime( - string path, DateTime lastWriteTime) - { - lastWriteTime = lastWriteTime.ToUniversalTime(); - DateTime expectedTime = lastWriteTime.ToLocalTime(); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastWriteTimeUtc(path, lastWriteTime); - - FileSystem.Directory.GetLastWriteTime(path) - .Should().Be(expectedTime); - } - - [SkippableTheory] - [AutoData] - public void SetLastWriteTimeUtc_Unspecified_ShouldChangeLastWriteTime( - string path, DateTime lastWriteTime) - { - lastWriteTime = DateTime.SpecifyKind(lastWriteTime, DateTimeKind.Unspecified); - FileSystem.Directory.CreateDirectory(path); - - FileSystem.Directory.SetLastWriteTimeUtc(path, lastWriteTime); - - FileSystem.Directory.GetLastWriteTimeUtc(path) - .Should().Be(lastWriteTime); - FileSystem.Directory.GetLastWriteTime(path) - .Should().Be(lastWriteTime.ToLocalTime()); - FileSystem.Directory.GetLastWriteTime(path).Kind - .Should().NotBe(DateTimeKind.Unspecified); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +public abstract partial class Tests + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void GetCreationTime_PathNotFound_ShouldReturnNullTime(string path) + { + DateTime expectedTime = FileTestHelper.NullTime.ToLocalTime(); + + DateTime result = FileSystem.Directory.GetCreationTime(path); + + result.Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void GetCreationTimeUtc_PathNotFound_ShouldReturnNullTime(string path) + { + DateTime expectedTime = FileTestHelper.NullTime.ToUniversalTime(); + + DateTime result = FileSystem.Directory.GetCreationTimeUtc(path); + + result.Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void GetLastAccessTime_PathNotFound_ShouldReturnNullTime(string path) + { + DateTime expectedTime = FileTestHelper.NullTime.ToLocalTime(); + + DateTime result = FileSystem.Directory.GetLastAccessTime(path); + + result.Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void GetLastAccessTimeUtc_PathNotFound_ShouldReturnNullTime(string path) + { + DateTime expectedTime = FileTestHelper.NullTime.ToUniversalTime(); + + DateTime result = FileSystem.Directory.GetLastAccessTimeUtc(path); + + result.Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void GetLastWriteTime_PathNotFound_ShouldReturnNullTime(string path) + { + DateTime expectedTime = FileTestHelper.NullTime.ToLocalTime(); + + DateTime result = FileSystem.Directory.GetLastWriteTime(path); + + result.Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void GetLastWriteTimeUtc_PathNotFound_ShouldReturnNullTime(string path) + { + DateTime expectedTime = FileTestHelper.NullTime.ToUniversalTime(); + + DateTime result = FileSystem.Directory.GetLastWriteTimeUtc(path); + + result.Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void LastAccessTime_CreateSubDirectory_ShouldUpdateLastAccessAndLastWriteTime( + string path, string subPath) + { + DateTime start = TimeSystem.DateTime.Now; + IDirectoryInfo result = FileSystem.Directory.CreateDirectory(path); + TimeSystem.Thread.Sleep(100); + DateTime sleepTime = TimeSystem.DateTime.Now; + FileSystem.Directory.CreateDirectory(FileSystem.Path.Combine(path, subPath)); + + result.CreationTime.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.CreationTime.Should().BeBefore(sleepTime); + // Last Access Time is only updated on Windows + if (Test.RunsOnWindows) + { + result.LastAccessTime.Should() + .BeOnOrAfter(sleepTime.ApplySystemClockTolerance()); + result.LastAccessTime.Should().BeOnOrBefore(TimeSystem.DateTime.Now); + } + else + { + result.LastAccessTime.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.LastAccessTime.Should().BeBefore(sleepTime); + } + + result.LastWriteTime.Should().BeOnOrAfter(sleepTime.ApplySystemClockTolerance()); + result.LastWriteTime.Should().BeOnOrBefore(TimeSystem.DateTime.Now); + } + + [SkippableTheory] + [AutoData] + public void LastAccessTime_ShouldBeSet(string path) + { + DateTime start = TimeSystem.DateTime.Now; + + FileSystem.Directory.CreateDirectory(path); + + DateTime result = FileSystem.Directory.GetLastAccessTime(path); + result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.Should().BeOnOrBefore(TimeSystem.DateTime.Now); + result.Kind.Should().Be(DateTimeKind.Local); + } + + [SkippableTheory] + [AutoData] + public void LastAccessTimeUtc_ShouldBeSet(string path) + { + DateTime start = TimeSystem.DateTime.UtcNow; + + FileSystem.Directory.CreateDirectory(path); + + DateTime result = FileSystem.Directory.GetLastAccessTimeUtc(path); + result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.Should().BeOnOrBefore(TimeSystem.DateTime.UtcNow); + result.Kind.Should().Be(DateTimeKind.Utc); + } + + [SkippableTheory] + [AutoData] + public void LastWriteTime_ShouldBeSet(string path) + { + DateTime start = TimeSystem.DateTime.Now; + + FileSystem.Directory.CreateDirectory(path); + + DateTime result = FileSystem.Directory.GetLastWriteTime(path); + result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.Should().BeOnOrBefore(TimeSystem.DateTime.Now); + result.Kind.Should().Be(DateTimeKind.Local); + } + + [SkippableTheory] + [AutoData] + public void LastWriteTimeUtc_ShouldBeSet(string path) + { + DateTime start = TimeSystem.DateTime.UtcNow; + + FileSystem.Directory.CreateDirectory(path); + + DateTime result = FileSystem.Directory.GetLastWriteTimeUtc(path); + result.Should().BeOnOrAfter(start.ApplySystemClockTolerance()); + result.Should().BeOnOrBefore(TimeSystem.DateTime.UtcNow); + result.Kind.Should().Be(DateTimeKind.Utc); + } + + [SkippableTheory] + [AutoData] + public void SetCreationTime_PathNotFound_ShouldThrowCorrectException( + string path, DateTime creationTime) + { + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.SetCreationTime(path, creationTime); + }); + + if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + else + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024893); + } + } + + [SkippableTheory] + [AutoData] + public void SetCreationTime_ShouldChangeCreationTime( + string path, DateTime creationTime) + { + Skip.IfNot(Test.RunsOnWindows, + "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); + + creationTime = creationTime.ToLocalTime(); + DateTime expectedTime = creationTime.ToUniversalTime(); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetCreationTime(path, creationTime); + + FileSystem.Directory.GetCreationTimeUtc(path) + .Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void SetCreationTime_Unspecified_ShouldChangeCreationTime( + string path, DateTime creationTime) + { + Skip.IfNot(Test.RunsOnWindows, + "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); + + creationTime = DateTime.SpecifyKind(creationTime, DateTimeKind.Unspecified); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetCreationTime(path, creationTime); + + FileSystem.Directory.GetCreationTimeUtc(path) + .Should().Be(creationTime.ToUniversalTime()); + FileSystem.Directory.GetCreationTime(path) + .Should().Be(creationTime); + FileSystem.Directory.GetCreationTime(path).Kind + .Should().NotBe(DateTimeKind.Unspecified); + } + + [SkippableTheory] + [AutoData] + public void SetCreationTimeUtc_PathNotFound_ShouldThrowCorrectException( + string path, DateTime creationTime) + { + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.SetCreationTimeUtc(path, creationTime); + }); + + if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + else + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024893); + } + } + + [SkippableTheory] + [AutoData] + public void SetCreationTimeUtc_ShouldChangeCreationTime( + string path, DateTime creationTime) + { + Skip.IfNot(Test.RunsOnWindows, + "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); + + creationTime = creationTime.ToUniversalTime(); + DateTime expectedTime = creationTime.ToLocalTime(); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetCreationTimeUtc(path, creationTime); + + FileSystem.Directory.GetCreationTime(path) + .Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void SetCreationTimeUtc_Unspecified_ShouldChangeCreationTime( + string path, DateTime creationTime) + { + Skip.IfNot(Test.RunsOnWindows, + "Linux does not have a creation timestamp: https://unix.stackexchange.com/a/102692"); + + creationTime = DateTime.SpecifyKind(creationTime, DateTimeKind.Unspecified); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetCreationTimeUtc(path, creationTime); + + FileSystem.Directory.GetCreationTimeUtc(path) + .Should().Be(creationTime); + FileSystem.Directory.GetCreationTime(path) + .Should().Be(creationTime.ToLocalTime()); + FileSystem.Directory.GetCreationTime(path).Kind + .Should().NotBe(DateTimeKind.Unspecified); + } + + [SkippableTheory] + [AutoData] + public void SetLastAccessTime_PathNotFound_ShouldThrowCorrectException( + string path, DateTime lastAccessTime) + { + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.SetLastAccessTime(path, lastAccessTime); + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + else + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024893); + } + } + + [SkippableTheory] + [AutoData] + public void SetLastAccessTime_ShouldChangeLastAccessTime( + string path, DateTime lastAccessTime) + { + lastAccessTime = lastAccessTime.ToLocalTime(); + DateTime expectedTime = lastAccessTime.ToUniversalTime(); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastAccessTime(path, lastAccessTime); + + FileSystem.Directory.GetLastAccessTimeUtc(path) + .Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void SetLastAccessTime_Unspecified_ShouldChangeLastAccessTime( + string path, DateTime lastAccessTime) + { + lastAccessTime = DateTime.SpecifyKind(lastAccessTime, DateTimeKind.Unspecified); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastAccessTime(path, lastAccessTime); + + FileSystem.Directory.GetLastAccessTimeUtc(path) + .Should().Be(lastAccessTime.ToUniversalTime()); + FileSystem.Directory.GetLastAccessTime(path) + .Should().Be(lastAccessTime); + FileSystem.Directory.GetLastAccessTime(path).Kind + .Should().NotBe(DateTimeKind.Unspecified); + } + + [SkippableTheory] + [AutoData] + public void SetLastAccessTimeUtc_PathNotFound_ShouldThrowCorrectException( + string path, DateTime lastAccessTime) + { + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.SetLastAccessTimeUtc(path, lastAccessTime); + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + else + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024893); + } + } + + [SkippableTheory] + [AutoData] + public void SetLastAccessTimeUtc_ShouldChangeLastAccessTime( + string path, DateTime lastAccessTime) + { + lastAccessTime = lastAccessTime.ToUniversalTime(); + DateTime expectedTime = lastAccessTime.ToLocalTime(); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastAccessTimeUtc(path, lastAccessTime); + + FileSystem.Directory.GetLastAccessTime(path) + .Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void SetLastAccessTimeUtc_Unspecified_ShouldChangeLastAccessTime( + string path, DateTime lastAccessTime) + { + lastAccessTime = DateTime.SpecifyKind(lastAccessTime, DateTimeKind.Unspecified); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastAccessTimeUtc(path, lastAccessTime); + + FileSystem.Directory.GetLastAccessTimeUtc(path) + .Should().Be(lastAccessTime); + FileSystem.Directory.GetLastAccessTime(path) + .Should().Be(lastAccessTime.ToLocalTime()); + FileSystem.Directory.GetLastAccessTime(path).Kind + .Should().NotBe(DateTimeKind.Unspecified); + } + + [SkippableTheory] + [AutoData] + public void SetLastWriteTime_PathNotFound_ShouldThrowCorrectException( + string path, DateTime lastWriteTime) + { + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.SetLastWriteTime(path, lastWriteTime); + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + else + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024893); + } + } + + [SkippableTheory] + [AutoData] + public void SetLastWriteTime_ShouldChangeLastWriteTime( + string path, DateTime lastWriteTime) + { + lastWriteTime = lastWriteTime.ToLocalTime(); + DateTime expectedTime = lastWriteTime.ToUniversalTime(); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastWriteTime(path, lastWriteTime); + + FileSystem.Directory.GetLastWriteTimeUtc(path) + .Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void SetLastWriteTime_Unspecified_ShouldChangeLastWriteTime( + string path, DateTime lastWriteTime) + { + lastWriteTime = DateTime.SpecifyKind(lastWriteTime, DateTimeKind.Unspecified); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastWriteTime(path, lastWriteTime); + + FileSystem.Directory.GetLastWriteTimeUtc(path) + .Should().Be(lastWriteTime.ToUniversalTime()); + FileSystem.Directory.GetLastWriteTime(path) + .Should().Be(lastWriteTime); + FileSystem.Directory.GetLastWriteTime(path).Kind + .Should().NotBe(DateTimeKind.Unspecified); + } + + [SkippableTheory] + [AutoData] + public void SetLastWriteTimeUtc_PathNotFound_ShouldThrowCorrectException( + string path, DateTime lastWriteTime) + { + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.SetLastWriteTimeUtc(path, lastWriteTime); + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + else + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024893); + } + } + + [SkippableTheory] + [AutoData] + public void SetLastWriteTimeUtc_ShouldChangeLastWriteTime( + string path, DateTime lastWriteTime) + { + lastWriteTime = lastWriteTime.ToUniversalTime(); + DateTime expectedTime = lastWriteTime.ToLocalTime(); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastWriteTimeUtc(path, lastWriteTime); + + FileSystem.Directory.GetLastWriteTime(path) + .Should().Be(expectedTime); + } + + [SkippableTheory] + [AutoData] + public void SetLastWriteTimeUtc_Unspecified_ShouldChangeLastWriteTime( + string path, DateTime lastWriteTime) + { + lastWriteTime = DateTime.SpecifyKind(lastWriteTime, DateTimeKind.Unspecified); + FileSystem.Directory.CreateDirectory(path); + + FileSystem.Directory.SetLastWriteTimeUtc(path, lastWriteTime); + + FileSystem.Directory.GetLastWriteTimeUtc(path) + .Should().Be(lastWriteTime); + FileSystem.Directory.GetLastWriteTime(path) + .Should().Be(lastWriteTime.ToLocalTime()); + FileSystem.Directory.GetLastWriteTime(path).Kind + .Should().NotBe(DateTimeKind.Unspecified); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.cs index 1aba527a8..29b345af5 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/Tests.cs @@ -1,126 +1,126 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.Directory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ -#if FEATURE_FILESYSTEM_NET7 - [SkippableFact] - public void CreateTempSubdirectory_ShouldCreateTheTemporaryDirectory() - { - IDirectoryInfo result = FileSystem.Directory.CreateTempSubdirectory(); - - result.Exists.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void CreateTempSubdirectory_WithPrefix_ShouldStartWithPrefix(string prefix) - { - IDirectoryInfo result = FileSystem.Directory.CreateTempSubdirectory(prefix); - - result.Name.Should().StartWith(prefix); - } -#endif - [SkippableFact] - public void GetCurrentDirectory_ShouldNotBeRooted() - { - string result = FileSystem.Directory.GetCurrentDirectory(); - - result.Should().NotBe(FileTestHelper.RootDrive()); - } - - [SkippableTheory] - [AutoData] - public void GetDirectoryRoot_ShouldReturnRoot(string path) - { - string root = FileTestHelper.RootDrive(); - string rootedPath = root + path; - - string result = FileSystem.Directory.GetDirectoryRoot(rootedPath); - - result.Should().Be(root); - } - - [SkippableFact] - public void GetLogicalDrives_ShouldNotBeEmpty() - { - string[] result = FileSystem.Directory.GetLogicalDrives(); - - result.Should().NotBeEmpty(); - result.Should().Contain(FileTestHelper.RootDrive()); - } - - [SkippableTheory] - [AutoData] - public void GetParent_ArbitraryPaths_ShouldNotBeNull(string path1, - string path2, - string path3) - { - string path = FileSystem.Path.Combine(path1, path2, path3); - IDirectoryInfo expectedParent = FileSystem.DirectoryInfo.New( - FileSystem.Path.Combine(path1, path2)); - - IDirectoryInfo? result = FileSystem.Directory.GetParent(path); - - result.Should().NotBeNull(); - result!.FullName.Should().Be(expectedParent.FullName); - } - - [SkippableFact] - public void GetParent_Root_ShouldReturnNull() - { - string path = FileTestHelper.RootDrive(); - - IDirectoryInfo? result = FileSystem.Directory.GetParent(path); - - result.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void - SetCurrentDirectory_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string path) - { - string previousCurrentDirectory = FileSystem.Directory.GetCurrentDirectory(); - try - { - Exception? exception = Record.Exception(() => - { - FileSystem.Directory.SetCurrentDirectory(path); - }); - - exception.Should().BeException(hResult: -2147024893); - FileSystem.Directory.GetCurrentDirectory().Should() - .Be(previousCurrentDirectory); - } - finally - { - FileSystem.Directory.SetCurrentDirectory(previousCurrentDirectory); - } - } - - [SkippableTheory] - [AutoData] - public void SetCurrentDirectory_RelativePath_ShouldBeFullyQualified(string path) - { - string previousCurrentDirectory = FileSystem.Directory.GetCurrentDirectory(); - try - { - string expectedPath = FileSystem.Path.GetFullPath(path); - FileSystem.Directory.CreateDirectory(path); - FileSystem.Directory.SetCurrentDirectory(path); - string result = FileSystem.Directory.GetCurrentDirectory(); - - result.Should().Be(expectedPath); - } - finally - { - FileSystem.Directory.SetCurrentDirectory(previousCurrentDirectory); - } - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.Directory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ +#if FEATURE_FILESYSTEM_NET7 + [SkippableFact] + public void CreateTempSubdirectory_ShouldCreateTheTemporaryDirectory() + { + IDirectoryInfo result = FileSystem.Directory.CreateTempSubdirectory(); + + result.Exists.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void CreateTempSubdirectory_WithPrefix_ShouldStartWithPrefix(string prefix) + { + IDirectoryInfo result = FileSystem.Directory.CreateTempSubdirectory(prefix); + + result.Name.Should().StartWith(prefix); + } +#endif + [SkippableFact] + public void GetCurrentDirectory_ShouldNotBeRooted() + { + string result = FileSystem.Directory.GetCurrentDirectory(); + + result.Should().NotBe(FileTestHelper.RootDrive()); + } + + [SkippableTheory] + [AutoData] + public void GetDirectoryRoot_ShouldReturnRoot(string path) + { + string root = FileTestHelper.RootDrive(); + string rootedPath = root + path; + + string result = FileSystem.Directory.GetDirectoryRoot(rootedPath); + + result.Should().Be(root); + } + + [SkippableFact] + public void GetLogicalDrives_ShouldNotBeEmpty() + { + string[] result = FileSystem.Directory.GetLogicalDrives(); + + result.Should().NotBeEmpty(); + result.Should().Contain(FileTestHelper.RootDrive()); + } + + [SkippableTheory] + [AutoData] + public void GetParent_ArbitraryPaths_ShouldNotBeNull(string path1, + string path2, + string path3) + { + string path = FileSystem.Path.Combine(path1, path2, path3); + IDirectoryInfo expectedParent = FileSystem.DirectoryInfo.New( + FileSystem.Path.Combine(path1, path2)); + + IDirectoryInfo? result = FileSystem.Directory.GetParent(path); + + result.Should().NotBeNull(); + result!.FullName.Should().Be(expectedParent.FullName); + } + + [SkippableFact] + public void GetParent_Root_ShouldReturnNull() + { + string path = FileTestHelper.RootDrive(); + + IDirectoryInfo? result = FileSystem.Directory.GetParent(path); + + result.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void + SetCurrentDirectory_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string path) + { + string previousCurrentDirectory = FileSystem.Directory.GetCurrentDirectory(); + try + { + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.SetCurrentDirectory(path); + }); + + exception.Should().BeException(hResult: -2147024893); + FileSystem.Directory.GetCurrentDirectory().Should() + .Be(previousCurrentDirectory); + } + finally + { + FileSystem.Directory.SetCurrentDirectory(previousCurrentDirectory); + } + } + + [SkippableTheory] + [AutoData] + public void SetCurrentDirectory_RelativePath_ShouldBeFullyQualified(string path) + { + string previousCurrentDirectory = FileSystem.Directory.GetCurrentDirectory(); + try + { + string expectedPath = FileSystem.Path.GetFullPath(path); + FileSystem.Directory.CreateDirectory(path); + FileSystem.Directory.SetCurrentDirectory(path); + string result = FileSystem.Directory.GetCurrentDirectory(); + + result.Should().Be(expectedPath); + } + finally + { + FileSystem.Directory.SetCurrentDirectory(previousCurrentDirectory); + } + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/AttributesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/AttributesTests.cs index 3e1af25c0..cdf870c6f 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/AttributesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/AttributesTests.cs @@ -1,46 +1,46 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class AttributesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Attributes_WhenFileIsExisting_SetterShouldChangeAttributesOnFileSystem( - string path, FileAttributes attributes) - { - FileSystem.Directory.CreateDirectory(path); - IDirectoryInfo sut1 = FileSystem.DirectoryInfo.New(path); - IDirectoryInfo sut2 = FileSystem.DirectoryInfo.New(path); - - sut1.Attributes = attributes; - FileAttributes expectedAttributes = sut1.Attributes; - - sut2.Attributes.Should().Be(expectedAttributes); - } - - [SkippableFact] - public void Attributes_WhenFileIsMissing_SetterShouldThrowFileNotFoundException() - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("missing file"); - - Exception? exception = Record.Exception(() => - { - sut.Attributes = FileAttributes.Normal; - }); - - exception.Should().BeException(hResult: -2147024894); - } - - [SkippableFact] - public void Attributes_WhenFileIsMissing_ShouldReturnMinusOne() - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("missing file"); - FileAttributes expected = (FileAttributes)(-1); - - sut.Attributes.Should().Be(expected); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class AttributesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Attributes_WhenFileIsExisting_SetterShouldChangeAttributesOnFileSystem( + string path, FileAttributes attributes) + { + FileSystem.Directory.CreateDirectory(path); + IDirectoryInfo sut1 = FileSystem.DirectoryInfo.New(path); + IDirectoryInfo sut2 = FileSystem.DirectoryInfo.New(path); + + sut1.Attributes = attributes; + FileAttributes expectedAttributes = sut1.Attributes; + + sut2.Attributes.Should().Be(expectedAttributes); + } + + [SkippableFact] + public void Attributes_WhenFileIsMissing_SetterShouldThrowFileNotFoundException() + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("missing file"); + + Exception? exception = Record.Exception(() => + { + sut.Attributes = FileAttributes.Normal; + }); + + exception.Should().BeException(hResult: -2147024894); + } + + [SkippableFact] + public void Attributes_WhenFileIsMissing_ShouldReturnMinusOne() + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("missing file"); + FileAttributes expected = (FileAttributes)(-1); + + sut.Attributes.Should().Be(expected); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateSubdirectoryTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateSubdirectoryTests.cs index 75c41063a..37d184e4a 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateSubdirectoryTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateSubdirectoryTests.cs @@ -1,5 +1,3 @@ -using Testably.Abstractions.FileSystem; - namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; // ReSharper disable once PartialTypeWithSinglePart diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateTests.cs index 3073c254d..5c01a9a11 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateTests.cs @@ -1,5 +1,3 @@ -using Testably.Abstractions.FileSystem; - namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; // ReSharper disable once PartialTypeWithSinglePart diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/DeleteTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/DeleteTests.cs index ff4f3f64c..edb3d17dc 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/DeleteTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/DeleteTests.cs @@ -1,129 +1,129 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class DeleteTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException(string path) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - sut.Exists.Should().BeFalse(); - - Exception? exception = Record.Exception(() => - { - sut.Delete(); - }); - - exception.Should().BeException($"'{sut.FullName}'", - hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Delete_Recursive_WithOpenFile_ShouldThrowIOException( - string path, string filename) - { - FileSystem.Initialize() - .WithSubdirectory(path); - string filePath = FileSystem.Path.Combine(path, filename); - FileSystemStream openFile = FileSystem.File.OpenWrite(filePath); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - Exception? exception = Record.Exception(() => - { - sut.Delete(true); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException($"{filename}'", - hResult: -2147024864); - FileSystem.File.Exists(filePath).Should().BeTrue(); - } - else - { - exception.Should().BeNull(); - FileSystem.File.Exists(filePath).Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void Delete_Recursive_WithSubdirectory_ShouldDeleteDirectoryWithContent( - string path, string subdirectory) - { - string subdirectoryPath = FileSystem.Path.Combine(path, subdirectory); - FileSystem.Directory.CreateDirectory(subdirectoryPath); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - sut.Exists.Should().BeTrue(); - - sut.Delete(true); - -#if NETFRAMEWORK - // The DirectoryInfo is not updated in .NET Framework! - sut.Exists.Should().BeTrue(); -#else - sut.Exists.Should().BeFalse(); -#endif - FileSystem.Directory.Exists(sut.FullName).Should().BeFalse(); - FileSystem.Directory.Exists(subdirectoryPath).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Delete_ShouldDeleteDirectory(string path) - { - FileSystem.Directory.CreateDirectory(path); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - sut.Exists.Should().BeTrue(); - - sut.Delete(); - -#if NETFRAMEWORK - // The DirectoryInfo is not updated in .NET Framework! - sut.Exists.Should().BeTrue(); -#else - sut.Exists.Should().BeFalse(); -#endif - FileSystem.Directory.Exists(sut.FullName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Delete_WithSubdirectory_ShouldThrowIOException_AndNotDeleteDirectory( - string path, string subdirectory) - { - FileSystem.Directory.CreateDirectory(FileSystem.Path.Combine(path, subdirectory)); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - sut.Exists.Should().BeTrue(); - - Exception? exception = Record.Exception(() => - { - sut.Delete(); - }); - - exception.Should().BeException( - hResult: Test.RunsOnWindows ? -2147024751 : Test.RunsOnMac ? 66 : 39, - // Path information only included in exception message on Windows and not in .NET Framework - messageContains: !Test.RunsOnWindows || Test.IsNetFramework - ? null - : $"'{sut.FullName}'"); - - sut.Exists.Should().BeTrue(); - FileSystem.Directory.Exists(sut.FullName).Should().BeTrue(); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class DeleteTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException(string path) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + sut.Exists.Should().BeFalse(); + + Exception? exception = Record.Exception(() => + { + sut.Delete(); + }); + + exception.Should().BeException($"'{sut.FullName}'", + hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Delete_Recursive_WithOpenFile_ShouldThrowIOException( + string path, string filename) + { + FileSystem.Initialize() + .WithSubdirectory(path); + string filePath = FileSystem.Path.Combine(path, filename); + FileSystemStream openFile = FileSystem.File.OpenWrite(filePath); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + Exception? exception = Record.Exception(() => + { + sut.Delete(true); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException($"{filename}'", + hResult: -2147024864); + FileSystem.File.Exists(filePath).Should().BeTrue(); + } + else + { + exception.Should().BeNull(); + FileSystem.File.Exists(filePath).Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void Delete_Recursive_WithSubdirectory_ShouldDeleteDirectoryWithContent( + string path, string subdirectory) + { + string subdirectoryPath = FileSystem.Path.Combine(path, subdirectory); + FileSystem.Directory.CreateDirectory(subdirectoryPath); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + sut.Exists.Should().BeTrue(); + + sut.Delete(true); + +#if NETFRAMEWORK + // The DirectoryInfo is not updated in .NET Framework! + sut.Exists.Should().BeTrue(); +#else + sut.Exists.Should().BeFalse(); +#endif + FileSystem.Directory.Exists(sut.FullName).Should().BeFalse(); + FileSystem.Directory.Exists(subdirectoryPath).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Delete_ShouldDeleteDirectory(string path) + { + FileSystem.Directory.CreateDirectory(path); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + sut.Exists.Should().BeTrue(); + + sut.Delete(); + +#if NETFRAMEWORK + // The DirectoryInfo is not updated in .NET Framework! + sut.Exists.Should().BeTrue(); +#else + sut.Exists.Should().BeFalse(); +#endif + FileSystem.Directory.Exists(sut.FullName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Delete_WithSubdirectory_ShouldThrowIOException_AndNotDeleteDirectory( + string path, string subdirectory) + { + FileSystem.Directory.CreateDirectory(FileSystem.Path.Combine(path, subdirectory)); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + sut.Exists.Should().BeTrue(); + + Exception? exception = Record.Exception(() => + { + sut.Delete(); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147024751 : Test.RunsOnMac ? 66 : 39, + // Path information only included in exception message on Windows and not in .NET Framework + messageContains: !Test.RunsOnWindows || Test.IsNetFramework + ? null + : $"'{sut.FullName}'"); + + sut.Exists.Should().BeTrue(); + FileSystem.Directory.Exists(sut.FullName).Should().BeTrue(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateDirectoriesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateDirectoriesTests.cs index 85f89c640..3bd701681 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateDirectoriesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateDirectoriesTests.cs @@ -1,170 +1,170 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class EnumerateDirectoriesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( - string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithSubdirectory("foo/xyz") - .WithSubdirectory("bar")); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IDirectoryInfo[] result = baseDirectory - .EnumerateDirectories("*", SearchOption.AllDirectories).ToArray(); - - result.Length.Should().Be(3); - result.Should().Contain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "bar"); - result.Should().Contain(d => d.Name == "xyz"); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void EnumerateDirectories_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string subdirectoryName) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory("foo"); - baseDirectory.CreateSubdirectory(subdirectoryName); - - IDirectoryInfo[] result = baseDirectory - .EnumerateDirectories(searchPattern).ToArray(); - - if (expectToBeFound) - { - result.Should().ContainSingle(d => d.Name == subdirectoryName, - $"it should match '{searchPattern}'"); - } - else - { - result.Should() - .BeEmpty($"{subdirectoryName} should not match '{searchPattern}'"); - } - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - IDirectoryInfo[] result = baseDirectory - .EnumerateDirectories("XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }).ToArray(); - - result.Length.Should().Be(1); - result.Should().NotContain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "xyz"); - result.Should().NotContain(d => d.Name == "bar"); - } -#endif - - [SkippableTheory] - [AutoData] - public void EnumerateDirectories_WithNewline_ShouldThrowArgumentException( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.DirectoryInfo.New(path); - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = baseDirectory.EnumerateDirectories(searchPattern).FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - IDirectoryInfo[] result = baseDirectory - .EnumerateDirectories().ToArray(); - - result.Length.Should().Be(2); - result.Should().Contain(d => d.Name == "foo"); - result.Should().NotContain(d => d.Name == "xyz"); - result.Should().Contain(d => d.Name == "bar"); - } - - [SkippableTheory] - [AutoData] - public void EnumerateDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo"); - baseDirectory.CreateSubdirectory("bar"); - - IEnumerable result = baseDirectory - .EnumerateDirectories("foo"); - - result.Should().ContainSingle(d => d.Name == "foo"); - } - - [SkippableTheory] - [AutoData] - public void - EnumerateDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar/xyz"); - - IEnumerable result = baseDirectory - .EnumerateDirectories("xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(2); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class EnumerateDirectoriesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( + string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithSubdirectory("foo/xyz") + .WithSubdirectory("bar")); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IDirectoryInfo[] result = baseDirectory + .EnumerateDirectories("*", SearchOption.AllDirectories).ToArray(); + + result.Length.Should().Be(3); + result.Should().Contain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "bar"); + result.Should().Contain(d => d.Name == "xyz"); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void EnumerateDirectories_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string subdirectoryName) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory("foo"); + baseDirectory.CreateSubdirectory(subdirectoryName); + + IDirectoryInfo[] result = baseDirectory + .EnumerateDirectories(searchPattern).ToArray(); + + if (expectToBeFound) + { + result.Should().ContainSingle(d => d.Name == subdirectoryName, + $"it should match '{searchPattern}'"); + } + else + { + result.Should() + .BeEmpty($"{subdirectoryName} should not match '{searchPattern}'"); + } + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + IDirectoryInfo[] result = baseDirectory + .EnumerateDirectories("XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }).ToArray(); + + result.Length.Should().Be(1); + result.Should().NotContain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "xyz"); + result.Should().NotContain(d => d.Name == "bar"); + } +#endif + + [SkippableTheory] + [AutoData] + public void EnumerateDirectories_WithNewline_ShouldThrowArgumentException( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.DirectoryInfo.New(path); + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = baseDirectory.EnumerateDirectories(searchPattern).FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + IDirectoryInfo[] result = baseDirectory + .EnumerateDirectories().ToArray(); + + result.Length.Should().Be(2); + result.Should().Contain(d => d.Name == "foo"); + result.Should().NotContain(d => d.Name == "xyz"); + result.Should().Contain(d => d.Name == "bar"); + } + + [SkippableTheory] + [AutoData] + public void EnumerateDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo"); + baseDirectory.CreateSubdirectory("bar"); + + IEnumerable result = baseDirectory + .EnumerateDirectories("foo"); + + result.Should().ContainSingle(d => d.Name == "foo"); + } + + [SkippableTheory] + [AutoData] + public void + EnumerateDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar/xyz"); + + IEnumerable result = baseDirectory + .EnumerateDirectories("xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(2); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFileSystemInfosTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFileSystemInfosTests.cs index 333cd052a..c33a8e7e9 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFileSystemInfosTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFileSystemInfosTests.cs @@ -1,204 +1,204 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class EnumerateFileSystemInfosTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - EnumerateFileSystemInfos_SearchOptionAllFiles_ShouldReturnAllFiles( - string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithASubdirectory().Initialized(d => d - .WithAFile() - .WithAFile()) - .WithASubdirectory() - .WithAFile()); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IFileSystemInfo[] result = baseDirectory - .EnumerateFileSystemInfos("*", SearchOption.AllDirectories).ToArray(); - - result.Length.Should().Be(5); - result.Should().Contain(d => d.Name == initialized[1].Name); - result.Should().Contain(d => d.Name == initialized[2].Name); - result.Should().Contain(d => d.Name == initialized[3].Name); - result.Should().Contain(d => d.Name == initialized[4].Name); - result.Should().Contain(d => d.Name == initialized[5].Name); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void EnumerateFileSystemInfos_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string fileName) - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile(fileName) - .BaseDirectory; - - IFileSystemInfo[] result = baseDirectory - .EnumerateFileSystemInfos(searchPattern).ToArray(); - - if (expectToBeFound) - { - result.Should().ContainSingle(d => d.Name == fileName, - $"it should match '{searchPattern}'"); - } - else - { - result.Should() - .BeEmpty($"{fileName} should not match '{searchPattern}'"); - } - } - - [SkippableTheory] - [AutoData] - public void - EnumerateFileSystemInfos_ShouldMatchTypes(string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithASubdirectory() - .WithAFile()); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IFileSystemInfo[] result = baseDirectory - .EnumerateFileSystemInfos("*").ToArray(); - - result.Length.Should().Be(2); - result.Should().Contain(d - => d.Name == initialized[1].Name && d is IDirectoryInfo); - result.Should().Contain(d - => d.Name == initialized[2].Name && d is IFileInfo); - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableFact] - public void - EnumerateFileSystemInfos_WithEnumerationOptions_ShouldConsiderSetOptions() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithAFile() - .BaseDirectory; - - IFileSystemInfo[] result = baseDirectory - .EnumerateFileSystemInfos("XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }).ToArray(); - - result.Length.Should().Be(1); - result.Should().NotContain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "xyz"); - result.Should().NotContain(d => d.Name == "bar"); - } -#endif - - [SkippableTheory] - [AutoData] - public void EnumerateFileSystemInfos_WithNewline_ShouldThrowArgumentException( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.DirectoryInfo.New(path); - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = baseDirectory.EnumerateFileSystemInfos(searchPattern).FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableFact] - public void - EnumerateFileSystemInfos_WithoutSearchString_ShouldReturnAllDirectFilesAndDirectories() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithSubdirectory("muh").Initialized(s => s - .WithFile("xyz")) - .WithFile("bar") - .BaseDirectory; - - IFileSystemInfo[] result = baseDirectory - .EnumerateFileSystemInfos().ToArray(); - - result.Length.Should().Be(3); - result.Should().Contain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "muh"); - result.Should().NotContain(d => d.Name == "xyz"); - result.Should().Contain(d => d.Name == "bar"); - } - - [SkippableFact] - public void EnumerateFileSystemInfos_WithSearchPattern_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithFile("bar") - .BaseDirectory; - - IEnumerable result = baseDirectory - .EnumerateFileSystemInfos("foo").ToArray(); - - result.Should().ContainSingle(d => d.Name == "foo"); - result.Count().Should().Be(1); - } - - [SkippableFact] - public void - EnumerateFileSystemInfos_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithSubdirectory("xyz").Initialized(s => s - .WithAFile()) - .BaseDirectory; - - IEnumerable result = baseDirectory - .EnumerateFileSystemInfos("xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(3); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class EnumerateFileSystemInfosTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + EnumerateFileSystemInfos_SearchOptionAllFiles_ShouldReturnAllFiles( + string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithASubdirectory().Initialized(d => d + .WithAFile() + .WithAFile()) + .WithASubdirectory() + .WithAFile()); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IFileSystemInfo[] result = baseDirectory + .EnumerateFileSystemInfos("*", SearchOption.AllDirectories).ToArray(); + + result.Length.Should().Be(5); + result.Should().Contain(d => d.Name == initialized[1].Name); + result.Should().Contain(d => d.Name == initialized[2].Name); + result.Should().Contain(d => d.Name == initialized[3].Name); + result.Should().Contain(d => d.Name == initialized[4].Name); + result.Should().Contain(d => d.Name == initialized[5].Name); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void EnumerateFileSystemInfos_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string fileName) + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile(fileName) + .BaseDirectory; + + IFileSystemInfo[] result = baseDirectory + .EnumerateFileSystemInfos(searchPattern).ToArray(); + + if (expectToBeFound) + { + result.Should().ContainSingle(d => d.Name == fileName, + $"it should match '{searchPattern}'"); + } + else + { + result.Should() + .BeEmpty($"{fileName} should not match '{searchPattern}'"); + } + } + + [SkippableTheory] + [AutoData] + public void + EnumerateFileSystemInfos_ShouldMatchTypes(string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithASubdirectory() + .WithAFile()); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IFileSystemInfo[] result = baseDirectory + .EnumerateFileSystemInfos("*").ToArray(); + + result.Length.Should().Be(2); + result.Should().Contain(d + => d.Name == initialized[1].Name && d is IDirectoryInfo); + result.Should().Contain(d + => d.Name == initialized[2].Name && d is IFileInfo); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void + EnumerateFileSystemInfos_WithEnumerationOptions_ShouldConsiderSetOptions() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithAFile() + .BaseDirectory; + + IFileSystemInfo[] result = baseDirectory + .EnumerateFileSystemInfos("XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }).ToArray(); + + result.Length.Should().Be(1); + result.Should().NotContain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "xyz"); + result.Should().NotContain(d => d.Name == "bar"); + } +#endif + + [SkippableTheory] + [AutoData] + public void EnumerateFileSystemInfos_WithNewline_ShouldThrowArgumentException( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.DirectoryInfo.New(path); + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = baseDirectory.EnumerateFileSystemInfos(searchPattern).FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableFact] + public void + EnumerateFileSystemInfos_WithoutSearchString_ShouldReturnAllDirectFilesAndDirectories() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithSubdirectory("muh").Initialized(s => s + .WithFile("xyz")) + .WithFile("bar") + .BaseDirectory; + + IFileSystemInfo[] result = baseDirectory + .EnumerateFileSystemInfos().ToArray(); + + result.Length.Should().Be(3); + result.Should().Contain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "muh"); + result.Should().NotContain(d => d.Name == "xyz"); + result.Should().Contain(d => d.Name == "bar"); + } + + [SkippableFact] + public void EnumerateFileSystemInfos_WithSearchPattern_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithFile("bar") + .BaseDirectory; + + IEnumerable result = baseDirectory + .EnumerateFileSystemInfos("foo").ToArray(); + + result.Should().ContainSingle(d => d.Name == "foo"); + result.Count().Should().Be(1); + } + + [SkippableFact] + public void + EnumerateFileSystemInfos_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithSubdirectory("xyz").Initialized(s => s + .WithAFile()) + .BaseDirectory; + + IEnumerable result = baseDirectory + .EnumerateFileSystemInfos("xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(3); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFilesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFilesTests.cs index a9f957b67..9c458a410 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFilesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/EnumerateFilesTests.cs @@ -1,178 +1,178 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class EnumerateFilesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - EnumerateFiles_SearchOptionAllFiles_ShouldReturnAllFiles( - string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithASubdirectory().Initialized(d => d - .WithAFile() - .WithAFile()) - .WithASubdirectory() - .WithAFile()); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IFileInfo[] result = baseDirectory - .EnumerateFiles("*", SearchOption.AllDirectories).ToArray(); - - result.Length.Should().Be(3); - result.Should().Contain(d => d.Name == initialized[2].Name); - result.Should().Contain(d => d.Name == initialized[3].Name); - result.Should().Contain(d => d.Name == initialized[5].Name); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void EnumerateFiles_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string fileName) - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile(fileName) - .BaseDirectory; - - IFileInfo[] result = baseDirectory - .EnumerateFiles(searchPattern).ToArray(); - - if (expectToBeFound) - { - result.Should().ContainSingle(d => d.Name == fileName, - $"it should match '{searchPattern}'"); - } - else - { - result.Should() - .BeEmpty($"{fileName} should not match '{searchPattern}'"); - } - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableFact] - public void - EnumerateFiles_WithEnumerationOptions_ShouldConsiderSetOptions() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithAFile() - .BaseDirectory; - - IFileInfo[] result = baseDirectory - .EnumerateFiles("XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }).ToArray(); - - result.Length.Should().Be(1); - result.Should().NotContain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "xyz"); - result.Should().NotContain(d => d.Name == "bar"); - } -#endif - - [SkippableTheory] - [AutoData] - public void EnumerateFiles_WithNewline_ShouldThrowArgumentException( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.DirectoryInfo.New(path); - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = baseDirectory.EnumerateFiles(searchPattern).FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableFact] - public void - EnumerateFiles_WithoutSearchString_ShouldReturnAllDirectFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithFile("bar") - .BaseDirectory; - - IFileInfo[] result = baseDirectory - .EnumerateFiles().ToArray(); - - result.Length.Should().Be(2); - result.Should().Contain(d => d.Name == "foo"); - result.Should().NotContain(d => d.Name == "xyz"); - result.Should().Contain(d => d.Name == "bar"); - } - - [SkippableFact] - public void EnumerateFiles_WithSearchPattern_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithFile("bar") - .BaseDirectory; - - IEnumerable result = baseDirectory - .EnumerateFiles("foo").ToArray(); - - result.Should().ContainSingle(d => d.Name == "foo"); - result.Count().Should().Be(1); - } - - [SkippableFact] - public void - EnumerateFiles_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithSubdirectory("xyz").Initialized(s => s - .WithAFile()) - .BaseDirectory; - - IEnumerable result = baseDirectory - .EnumerateFiles("xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(2); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class EnumerateFilesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + EnumerateFiles_SearchOptionAllFiles_ShouldReturnAllFiles( + string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithASubdirectory().Initialized(d => d + .WithAFile() + .WithAFile()) + .WithASubdirectory() + .WithAFile()); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IFileInfo[] result = baseDirectory + .EnumerateFiles("*", SearchOption.AllDirectories).ToArray(); + + result.Length.Should().Be(3); + result.Should().Contain(d => d.Name == initialized[2].Name); + result.Should().Contain(d => d.Name == initialized[3].Name); + result.Should().Contain(d => d.Name == initialized[5].Name); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void EnumerateFiles_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string fileName) + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile(fileName) + .BaseDirectory; + + IFileInfo[] result = baseDirectory + .EnumerateFiles(searchPattern).ToArray(); + + if (expectToBeFound) + { + result.Should().ContainSingle(d => d.Name == fileName, + $"it should match '{searchPattern}'"); + } + else + { + result.Should() + .BeEmpty($"{fileName} should not match '{searchPattern}'"); + } + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void + EnumerateFiles_WithEnumerationOptions_ShouldConsiderSetOptions() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithAFile() + .BaseDirectory; + + IFileInfo[] result = baseDirectory + .EnumerateFiles("XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }).ToArray(); + + result.Length.Should().Be(1); + result.Should().NotContain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "xyz"); + result.Should().NotContain(d => d.Name == "bar"); + } +#endif + + [SkippableTheory] + [AutoData] + public void EnumerateFiles_WithNewline_ShouldThrowArgumentException( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.DirectoryInfo.New(path); + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = baseDirectory.EnumerateFiles(searchPattern).FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableFact] + public void + EnumerateFiles_WithoutSearchString_ShouldReturnAllDirectFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithFile("bar") + .BaseDirectory; + + IFileInfo[] result = baseDirectory + .EnumerateFiles().ToArray(); + + result.Length.Should().Be(2); + result.Should().Contain(d => d.Name == "foo"); + result.Should().NotContain(d => d.Name == "xyz"); + result.Should().Contain(d => d.Name == "bar"); + } + + [SkippableFact] + public void EnumerateFiles_WithSearchPattern_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithFile("bar") + .BaseDirectory; + + IEnumerable result = baseDirectory + .EnumerateFiles("foo").ToArray(); + + result.Should().ContainSingle(d => d.Name == "foo"); + result.Count().Should().Be(1); + } + + [SkippableFact] + public void + EnumerateFiles_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithSubdirectory("xyz").Initialized(s => s + .WithAFile()) + .BaseDirectory; + + IEnumerable result = baseDirectory + .EnumerateFiles("xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(2); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/ExceptionTests.cs index 962b49095..ebd736e07 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/ExceptionTests.cs @@ -1,150 +1,150 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetDirectoryInfoCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetDirectoryInfoCallbacks), parameters: " ")] - public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); - }); - - if (Test.IsNetFramework) - { - exception.Should() - .BeNull( - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should().BeException( - hResult: -2147024809, - paramName: paramName, - because: - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - - [Theory] - [MemberData(nameof(GetDirectoryInfoCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetDirectoryInfoCallbacks), - parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); - }); - - if (!Test.RunsOnWindows) - { - if (exception is IOException ioException) - { - ioException.HResult.Should().NotBe(-2147024809, - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - else - { - if (Test.IsNetFramework) - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should().BeException( - hResult: -2147024773, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - } - - #region Helpers - - public static IEnumerable GetDirectoryInfoCallbacks(string? path) - => GetDirectoryInfoCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetDirectoryInfoCallbackTestParameters(string value) - { - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.InvalidPath, "path", - directoryInfo - => directoryInfo.CreateSubdirectory(value)); -#if FEATURE_FILESYSTEM_LINK - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "pathToTarget", - directoryInfo - => directoryInfo.CreateAsSymbolicLink(value)); -#endif - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destDirName", - directoryInfo - => directoryInfo.MoveTo(value)); - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetDirectoryInfoCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetDirectoryInfoCallbacks), parameters: " ")] + public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); + }); + + if (Test.IsNetFramework) + { + exception.Should() + .BeNull( + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should().BeException( + hResult: -2147024809, + paramName: paramName, + because: + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + + [Theory] + [MemberData(nameof(GetDirectoryInfoCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetDirectoryInfoCallbacks), + parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo.New("foo")); + }); + + if (!Test.RunsOnWindows) + { + if (exception is IOException ioException) + { + ioException.HResult.Should().NotBe(-2147024809, + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + else + { + if (Test.IsNetFramework) + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should().BeException( + hResult: -2147024773, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + } + + #region Helpers + + public static IEnumerable GetDirectoryInfoCallbacks(string? path) + => GetDirectoryInfoCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetDirectoryInfoCallbackTestParameters(string value) + { + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.InvalidPath, "path", + directoryInfo + => directoryInfo.CreateSubdirectory(value)); +#if FEATURE_FILESYSTEM_LINK + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "pathToTarget", + directoryInfo + => directoryInfo.CreateAsSymbolicLink(value)); +#endif + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destDirName", + directoryInfo + => directoryInfo.MoveTo(value)); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/ExistsTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/ExistsTests.cs index c27638c08..2731c4a73 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/ExistsTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/ExistsTests.cs @@ -1,5 +1,3 @@ -using Testably.Abstractions.FileSystem; - namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; // ReSharper disable once PartialTypeWithSinglePart diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetDirectoriesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetDirectoriesTests.cs index 6849c81f9..a9cc91a7b 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetDirectoriesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetDirectoriesTests.cs @@ -1,170 +1,170 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class GetDirectoriesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - GetDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( - string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithSubdirectory("foo/xyz") - .WithSubdirectory("bar")); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IDirectoryInfo[] result = baseDirectory - .GetDirectories("*", SearchOption.AllDirectories); - - result.Length.Should().Be(3); - result.Should().Contain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "bar"); - result.Should().Contain(d => d.Name == "xyz"); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void GetDirectories_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string subdirectoryName) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory("foo"); - baseDirectory.CreateSubdirectory(subdirectoryName); - - IDirectoryInfo[] result = baseDirectory - .GetDirectories(searchPattern); - - if (expectToBeFound) - { - result.Should().ContainSingle(d => d.Name == subdirectoryName, - $"it should match '{searchPattern}'"); - } - else - { - result.Should() - .BeEmpty($"{subdirectoryName} should not match '{searchPattern}'"); - } - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableTheory] - [AutoData] - public void - GetDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - IDirectoryInfo[] result = baseDirectory - .GetDirectories("XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }); - - result.Length.Should().Be(1); - result.Should().NotContain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "xyz"); - result.Should().NotContain(d => d.Name == "bar"); - } -#endif - - [SkippableTheory] - [AutoData] - public void GetDirectories_WithNewline_ShouldThrowArgumentException( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.DirectoryInfo.New(path); - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = baseDirectory.GetDirectories(searchPattern).FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableTheory] - [AutoData] - public void - GetDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar"); - - IDirectoryInfo[] result = baseDirectory - .GetDirectories(); - - result.Length.Should().Be(2); - result.Should().Contain(d => d.Name == "foo"); - result.Should().NotContain(d => d.Name == "xyz"); - result.Should().Contain(d => d.Name == "bar"); - } - - [SkippableTheory] - [AutoData] - public void GetDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo"); - baseDirectory.CreateSubdirectory("bar"); - - IEnumerable result = baseDirectory - .GetDirectories("foo"); - - result.Should().ContainSingle(d => d.Name == "foo"); - } - - [SkippableTheory] - [AutoData] - public void - GetDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.Directory.CreateDirectory(path); - baseDirectory.CreateSubdirectory("foo/xyz"); - baseDirectory.CreateSubdirectory("bar/xyz"); - - IEnumerable result = baseDirectory - .GetDirectories("xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(2); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class GetDirectoriesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + GetDirectories_SearchOptionAllDirectories_ShouldReturnAllSubdirectories( + string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithSubdirectory("foo/xyz") + .WithSubdirectory("bar")); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IDirectoryInfo[] result = baseDirectory + .GetDirectories("*", SearchOption.AllDirectories); + + result.Length.Should().Be(3); + result.Should().Contain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "bar"); + result.Should().Contain(d => d.Name == "xyz"); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void GetDirectories_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string subdirectoryName) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory("foo"); + baseDirectory.CreateSubdirectory(subdirectoryName); + + IDirectoryInfo[] result = baseDirectory + .GetDirectories(searchPattern); + + if (expectToBeFound) + { + result.Should().ContainSingle(d => d.Name == subdirectoryName, + $"it should match '{searchPattern}'"); + } + else + { + result.Should() + .BeEmpty($"{subdirectoryName} should not match '{searchPattern}'"); + } + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableTheory] + [AutoData] + public void + GetDirectories_WithEnumerationOptions_ShouldConsiderSetOptions( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + IDirectoryInfo[] result = baseDirectory + .GetDirectories("XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }); + + result.Length.Should().Be(1); + result.Should().NotContain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "xyz"); + result.Should().NotContain(d => d.Name == "bar"); + } +#endif + + [SkippableTheory] + [AutoData] + public void GetDirectories_WithNewline_ShouldThrowArgumentException( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.DirectoryInfo.New(path); + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = baseDirectory.GetDirectories(searchPattern).FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableTheory] + [AutoData] + public void + GetDirectories_WithoutSearchString_ShouldReturnAllDirectSubdirectories( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar"); + + IDirectoryInfo[] result = baseDirectory + .GetDirectories(); + + result.Length.Should().Be(2); + result.Should().Contain(d => d.Name == "foo"); + result.Should().NotContain(d => d.Name == "xyz"); + result.Should().Contain(d => d.Name == "bar"); + } + + [SkippableTheory] + [AutoData] + public void GetDirectories_WithSearchPattern_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo"); + baseDirectory.CreateSubdirectory("bar"); + + IEnumerable result = baseDirectory + .GetDirectories("foo"); + + result.Should().ContainSingle(d => d.Name == "foo"); + } + + [SkippableTheory] + [AutoData] + public void + GetDirectories_WithSearchPatternInSubdirectory_ShouldReturnMatchingSubdirectory( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.Directory.CreateDirectory(path); + baseDirectory.CreateSubdirectory("foo/xyz"); + baseDirectory.CreateSubdirectory("bar/xyz"); + + IEnumerable result = baseDirectory + .GetDirectories("xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(2); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFileSystemInfosTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFileSystemInfosTests.cs index fd491eaff..4716424cb 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFileSystemInfosTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFileSystemInfosTests.cs @@ -1,204 +1,204 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class GetFileSystemInfosTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - GetFileSystemInfos_SearchOptionAllFiles_ShouldReturnAllFiles( - string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithASubdirectory().Initialized(d => d - .WithAFile() - .WithAFile()) - .WithASubdirectory() - .WithAFile()); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IFileSystemInfo[] result = baseDirectory - .GetFileSystemInfos("*", SearchOption.AllDirectories); - - result.Length.Should().Be(5); - result.Should().Contain(d => d.Name == initialized[1].Name); - result.Should().Contain(d => d.Name == initialized[2].Name); - result.Should().Contain(d => d.Name == initialized[3].Name); - result.Should().Contain(d => d.Name == initialized[4].Name); - result.Should().Contain(d => d.Name == initialized[5].Name); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void GetFileSystemInfos_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string fileName) - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile(fileName) - .BaseDirectory; - - IFileSystemInfo[] result = baseDirectory - .GetFileSystemInfos(searchPattern); - - if (expectToBeFound) - { - result.Should().ContainSingle(d => d.Name == fileName, - $"it should match '{searchPattern}'"); - } - else - { - result.Should() - .BeEmpty($"{fileName} should not match '{searchPattern}'"); - } - } - - [SkippableTheory] - [AutoData] - public void - GetFileSystemInfos_ShouldMatchTypes(string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithASubdirectory() - .WithAFile()); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IFileSystemInfo[] result = baseDirectory - .GetFileSystemInfos("*"); - - result.Length.Should().Be(2); - result.Should().Contain(d - => d.Name == initialized[1].Name && d is IDirectoryInfo); - result.Should().Contain(d - => d.Name == initialized[2].Name && d is IFileInfo); - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableFact] - public void - GetFileSystemInfos_WithEnumerationOptions_ShouldConsiderSetOptions() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithAFile() - .BaseDirectory; - - IFileSystemInfo[] result = baseDirectory - .GetFileSystemInfos("XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }); - - result.Length.Should().Be(1); - result.Should().NotContain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "xyz"); - result.Should().NotContain(d => d.Name == "bar"); - } -#endif - - [SkippableTheory] - [AutoData] - public void GetFileSystemInfos_WithNewline_ShouldThrowArgumentException( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.DirectoryInfo.New(path); - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = baseDirectory.GetFileSystemInfos(searchPattern).FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableFact] - public void - GetFileSystemInfos_WithoutSearchString_ShouldReturnAllDirectFilesAndDirectories() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithSubdirectory("muh").Initialized(s => s - .WithFile("xyz")) - .WithFile("bar") - .BaseDirectory; - - IFileSystemInfo[] result = baseDirectory - .GetFileSystemInfos(); - - result.Length.Should().Be(3); - result.Should().Contain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "muh"); - result.Should().NotContain(d => d.Name == "xyz"); - result.Should().Contain(d => d.Name == "bar"); - } - - [SkippableFact] - public void GetFileSystemInfos_WithSearchPattern_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithFile("bar") - .BaseDirectory; - - IEnumerable result = baseDirectory - .GetFileSystemInfos("foo"); - - result.Should().ContainSingle(d => d.Name == "foo"); - result.Count().Should().Be(1); - } - - [SkippableFact] - public void - GetFileSystemInfos_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithSubdirectory("xyz").Initialized(s => s - .WithAFile()) - .BaseDirectory; - - IEnumerable result = baseDirectory - .GetFileSystemInfos("xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(3); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class GetFileSystemInfosTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + GetFileSystemInfos_SearchOptionAllFiles_ShouldReturnAllFiles( + string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithASubdirectory().Initialized(d => d + .WithAFile() + .WithAFile()) + .WithASubdirectory() + .WithAFile()); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IFileSystemInfo[] result = baseDirectory + .GetFileSystemInfos("*", SearchOption.AllDirectories); + + result.Length.Should().Be(5); + result.Should().Contain(d => d.Name == initialized[1].Name); + result.Should().Contain(d => d.Name == initialized[2].Name); + result.Should().Contain(d => d.Name == initialized[3].Name); + result.Should().Contain(d => d.Name == initialized[4].Name); + result.Should().Contain(d => d.Name == initialized[5].Name); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void GetFileSystemInfos_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string fileName) + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile(fileName) + .BaseDirectory; + + IFileSystemInfo[] result = baseDirectory + .GetFileSystemInfos(searchPattern); + + if (expectToBeFound) + { + result.Should().ContainSingle(d => d.Name == fileName, + $"it should match '{searchPattern}'"); + } + else + { + result.Should() + .BeEmpty($"{fileName} should not match '{searchPattern}'"); + } + } + + [SkippableTheory] + [AutoData] + public void + GetFileSystemInfos_ShouldMatchTypes(string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithASubdirectory() + .WithAFile()); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IFileSystemInfo[] result = baseDirectory + .GetFileSystemInfos("*"); + + result.Length.Should().Be(2); + result.Should().Contain(d + => d.Name == initialized[1].Name && d is IDirectoryInfo); + result.Should().Contain(d + => d.Name == initialized[2].Name && d is IFileInfo); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void + GetFileSystemInfos_WithEnumerationOptions_ShouldConsiderSetOptions() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithAFile() + .BaseDirectory; + + IFileSystemInfo[] result = baseDirectory + .GetFileSystemInfos("XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }); + + result.Length.Should().Be(1); + result.Should().NotContain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "xyz"); + result.Should().NotContain(d => d.Name == "bar"); + } +#endif + + [SkippableTheory] + [AutoData] + public void GetFileSystemInfos_WithNewline_ShouldThrowArgumentException( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.DirectoryInfo.New(path); + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = baseDirectory.GetFileSystemInfos(searchPattern).FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableFact] + public void + GetFileSystemInfos_WithoutSearchString_ShouldReturnAllDirectFilesAndDirectories() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithSubdirectory("muh").Initialized(s => s + .WithFile("xyz")) + .WithFile("bar") + .BaseDirectory; + + IFileSystemInfo[] result = baseDirectory + .GetFileSystemInfos(); + + result.Length.Should().Be(3); + result.Should().Contain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "muh"); + result.Should().NotContain(d => d.Name == "xyz"); + result.Should().Contain(d => d.Name == "bar"); + } + + [SkippableFact] + public void GetFileSystemInfos_WithSearchPattern_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithFile("bar") + .BaseDirectory; + + IEnumerable result = baseDirectory + .GetFileSystemInfos("foo"); + + result.Should().ContainSingle(d => d.Name == "foo"); + result.Count().Should().Be(1); + } + + [SkippableFact] + public void + GetFileSystemInfos_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithSubdirectory("xyz").Initialized(s => s + .WithAFile()) + .BaseDirectory; + + IEnumerable result = baseDirectory + .GetFileSystemInfos("xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(3); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFilesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFilesTests.cs index 37d71e9e0..4436a6bbe 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFilesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/GetFilesTests.cs @@ -1,178 +1,178 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class GetFilesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - GetFiles_SearchOptionAllFiles_ShouldReturnAllFiles( - string path) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(path).Initialized(s => s - .WithASubdirectory().Initialized(d => d - .WithAFile() - .WithAFile()) - .WithASubdirectory() - .WithAFile()); - IDirectoryInfo baseDirectory = - (IDirectoryInfo)initialized[0]; - - IFileInfo[] result = baseDirectory - .GetFiles("*", SearchOption.AllDirectories); - - result.Length.Should().Be(3); - result.Should().Contain(d => d.Name == initialized[2].Name); - result.Should().Contain(d => d.Name == initialized[3].Name); - result.Should().Contain(d => d.Name == initialized[5].Name); - } - - [SkippableTheory] -#if NETFRAMEWORK - [InlineAutoData(false, "")] -#else - [InlineAutoData(true, "")] -#endif - [InlineAutoData(true, "*")] - [InlineAutoData(true, ".")] - [InlineAutoData(true, "*.*")] - [InlineData(true, "a*c", "abc")] - [InlineData(true, "ab*c", "abc")] - [InlineData(true, "abc?", "abc")] - [InlineData(false, "ab?c", "abc")] - [InlineData(false, "ac", "abc")] - public void GetFiles_SearchPattern_ShouldReturnExpectedValue( - bool expectToBeFound, string searchPattern, string fileName) - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile(fileName) - .BaseDirectory; - - IFileInfo[] result = baseDirectory - .GetFiles(searchPattern); - - if (expectToBeFound) - { - result.Should().ContainSingle(d => d.Name == fileName, - $"it should match '{searchPattern}'"); - } - else - { - result.Should() - .BeEmpty($"{fileName} should not match '{searchPattern}'"); - } - } - -#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS - [SkippableFact] - public void - GetFiles_WithEnumerationOptions_ShouldConsiderSetOptions() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithAFile() - .BaseDirectory; - - IFileInfo[] result = baseDirectory - .GetFiles("XYZ", - new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - RecurseSubdirectories = true, - // Filename could start with a leading '.' indicating it as Hidden in Linux - AttributesToSkip = FileAttributes.System - }); - - result.Length.Should().Be(1); - result.Should().NotContain(d => d.Name == "foo"); - result.Should().Contain(d => d.Name == "xyz"); - result.Should().NotContain(d => d.Name == "bar"); - } -#endif - - [SkippableTheory] - [AutoData] - public void GetFiles_WithNewline_ShouldThrowArgumentException( - string path) - { - IDirectoryInfo baseDirectory = - FileSystem.DirectoryInfo.New(path); - string searchPattern = "foo\0bar"; - - Exception? exception = Record.Exception(() => - { - _ = baseDirectory.GetFiles(searchPattern).FirstOrDefault(); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableFact] - public void - GetFiles_WithoutSearchString_ShouldReturnAllDirectFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithFile("bar") - .BaseDirectory; - - IFileInfo[] result = baseDirectory - .GetFiles(); - - result.Length.Should().Be(2); - result.Should().Contain(d => d.Name == "foo"); - result.Should().NotContain(d => d.Name == "xyz"); - result.Should().Contain(d => d.Name == "bar"); - } - - [SkippableFact] - public void GetFiles_WithSearchPattern_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithFile("foo") - .WithFile("bar") - .BaseDirectory; - - IEnumerable result = baseDirectory - .GetFiles("foo"); - - result.Should().ContainSingle(d => d.Name == "foo"); - result.Count().Should().Be(1); - } - - [SkippableFact] - public void - GetFiles_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() - { - IDirectoryInfo baseDirectory = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithASubdirectory().Initialized(s => s - .WithFile("xyz")) - .WithSubdirectory("xyz").Initialized(s => s - .WithAFile()) - .BaseDirectory; - - IEnumerable result = baseDirectory - .GetFiles("xyz", SearchOption.AllDirectories); - - result.Count().Should().Be(2); - } -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class GetFilesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + GetFiles_SearchOptionAllFiles_ShouldReturnAllFiles( + string path) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(path).Initialized(s => s + .WithASubdirectory().Initialized(d => d + .WithAFile() + .WithAFile()) + .WithASubdirectory() + .WithAFile()); + IDirectoryInfo baseDirectory = + (IDirectoryInfo)initialized[0]; + + IFileInfo[] result = baseDirectory + .GetFiles("*", SearchOption.AllDirectories); + + result.Length.Should().Be(3); + result.Should().Contain(d => d.Name == initialized[2].Name); + result.Should().Contain(d => d.Name == initialized[3].Name); + result.Should().Contain(d => d.Name == initialized[5].Name); + } + + [SkippableTheory] +#if NETFRAMEWORK + [InlineAutoData(false, "")] +#else + [InlineAutoData(true, "")] +#endif + [InlineAutoData(true, "*")] + [InlineAutoData(true, ".")] + [InlineAutoData(true, "*.*")] + [InlineData(true, "a*c", "abc")] + [InlineData(true, "ab*c", "abc")] + [InlineData(true, "abc?", "abc")] + [InlineData(false, "ab?c", "abc")] + [InlineData(false, "ac", "abc")] + public void GetFiles_SearchPattern_ShouldReturnExpectedValue( + bool expectToBeFound, string searchPattern, string fileName) + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile(fileName) + .BaseDirectory; + + IFileInfo[] result = baseDirectory + .GetFiles(searchPattern); + + if (expectToBeFound) + { + result.Should().ContainSingle(d => d.Name == fileName, + $"it should match '{searchPattern}'"); + } + else + { + result.Should() + .BeEmpty($"{fileName} should not match '{searchPattern}'"); + } + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void + GetFiles_WithEnumerationOptions_ShouldConsiderSetOptions() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithAFile() + .BaseDirectory; + + IFileInfo[] result = baseDirectory + .GetFiles("XYZ", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + // Filename could start with a leading '.' indicating it as Hidden in Linux + AttributesToSkip = FileAttributes.System + }); + + result.Length.Should().Be(1); + result.Should().NotContain(d => d.Name == "foo"); + result.Should().Contain(d => d.Name == "xyz"); + result.Should().NotContain(d => d.Name == "bar"); + } +#endif + + [SkippableTheory] + [AutoData] + public void GetFiles_WithNewline_ShouldThrowArgumentException( + string path) + { + IDirectoryInfo baseDirectory = + FileSystem.DirectoryInfo.New(path); + string searchPattern = "foo\0bar"; + + Exception? exception = Record.Exception(() => + { + _ = baseDirectory.GetFiles(searchPattern).FirstOrDefault(); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableFact] + public void + GetFiles_WithoutSearchString_ShouldReturnAllDirectFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithFile("bar") + .BaseDirectory; + + IFileInfo[] result = baseDirectory + .GetFiles(); + + result.Length.Should().Be(2); + result.Should().Contain(d => d.Name == "foo"); + result.Should().NotContain(d => d.Name == "xyz"); + result.Should().Contain(d => d.Name == "bar"); + } + + [SkippableFact] + public void GetFiles_WithSearchPattern_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithFile("foo") + .WithFile("bar") + .BaseDirectory; + + IEnumerable result = baseDirectory + .GetFiles("foo"); + + result.Should().ContainSingle(d => d.Name == "foo"); + result.Count().Should().Be(1); + } + + [SkippableFact] + public void + GetFiles_WithSearchPatternInSubdirectory_ShouldReturnMatchingFiles() + { + IDirectoryInfo baseDirectory = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithASubdirectory().Initialized(s => s + .WithFile("xyz")) + .WithSubdirectory("xyz").Initialized(s => s + .WithAFile()) + .BaseDirectory; + + IEnumerable result = baseDirectory + .GetFiles("xyz", SearchOption.AllDirectories); + + result.Count().Should().Be(2); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/MoveToTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/MoveToTests.cs index 816d2f7b0..3db790fd6 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/MoveToTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/MoveToTests.cs @@ -1,186 +1,186 @@ -using System.IO; -using Testably.Abstractions.Testing.FileSystemInitializer; -#if !NETFRAMEWORK -#endif - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class MoveToTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void MoveTo_ShouldMoveDirectoryWithContent(string source, string destination) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); - - sut.MoveTo(destination); - - FileSystem.Directory.Exists(source).Should().BeFalse(); - FileSystem.Directory.Exists(destination).Should().BeTrue(); - FileSystem.Directory.GetFiles(destination, initialized[1].Name) - .Should().ContainSingle(); - FileSystem.Directory.GetDirectories(destination, initialized[2].Name) - .Should().ContainSingle(); - FileSystem.Directory.GetFiles(destination, initialized[3].Name, - SearchOption.AllDirectories) - .Should().ContainSingle(); - FileSystem.Directory.GetDirectories(destination, initialized[4].Name, - SearchOption.AllDirectories) - .Should().ContainSingle(); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_ShouldUpdatePropertiesOfDirectoryInfo( - string source, string destination) - { - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); - - sut.MoveTo(destination); - - sut.FullName.TrimEnd(FileSystem.Path.DirectorySeparatorChar) - .Should().Be(FileSystem.Path.GetFullPath(destination)); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_WithLockedFile_ShouldThrowIOException_AndNotMoveDirectory_OnWindows( - string source, string destination) - { - Skip.IfNot(Test.RunsOnWindows); - - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); - using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, - FileMode.Open, - FileAccess.Read, - FileShare.Read); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(destination); - }); - - if (Test.IsNetFramework) - { - // On .NET Framework the HResult is "-2146232800", but only in `DirectoryInfo.MoveTo` (not in `Directory.Move`) - // This peculiar deviation is not supported by the FileSystemMock. - exception.Should().BeException(); - } - else - { - exception.Should().BeException(hResult: -2147024891); - } - - FileSystem.Directory.Exists(source).Should().BeTrue(); - FileSystem.Directory.Exists(destination).Should().BeFalse(); - IDirectoryInfo sourceDirectory = - FileSystem.DirectoryInfo.New(source); - sourceDirectory.GetFiles(initialized[1].Name) - .Should().ContainSingle(); - sourceDirectory.GetDirectories(initialized[2].Name) - .Should().ContainSingle(); - sourceDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - sourceDirectory - .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_WithLockedFile_ShouldMoveDirectory_NotOnWindows( - string source, string destination) - { - Skip.If(Test.RunsOnWindows); - - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); - using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, - FileMode.Open, - FileAccess.Read, - FileShare.Read); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(destination); - }); - - exception.Should().BeNull(); - FileSystem.Directory.Exists(source).Should().BeFalse(); - FileSystem.Directory.Exists(destination).Should().BeTrue(); - IDirectoryInfo destinationDirectory = - FileSystem.DirectoryInfo.New(destination); - destinationDirectory.GetFiles(initialized[1].Name) - .Should().ContainSingle(); - destinationDirectory.GetDirectories(initialized[2].Name) - .Should().ContainSingle(); - destinationDirectory - .GetFiles(initialized[3].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - destinationDirectory - .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_WithReadOnlyFile_ShouldMoveDirectoryWithContent( - string source, string destination) - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithSubdirectory(source).Initialized(s => s - .WithAFile() - .WithASubdirectory().Initialized(t => t - .WithAFile() - .WithASubdirectory())); - initialized[3].Attributes = FileAttributes.ReadOnly; - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); - - sut.MoveTo(destination); - - FileSystem.Directory.Exists(source).Should().BeFalse(); - FileSystem.Directory.Exists(destination).Should().BeTrue(); - IDirectoryInfo destinationDirectory = - FileSystem.DirectoryInfo.New(destination); - destinationDirectory.GetFiles(initialized[1].Name) - .Should().ContainSingle(); - destinationDirectory.GetDirectories(initialized[2].Name) - .Should().ContainSingle(); - destinationDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) - .Should().ContainSingle().Which.Attributes.Should() - .HaveFlag(FileAttributes.ReadOnly); - destinationDirectory - .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) - .Should().ContainSingle(); - } -} +using System.IO; +using Testably.Abstractions.Testing.FileSystemInitializer; +#if !NETFRAMEWORK +#endif + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class MoveToTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void MoveTo_ShouldMoveDirectoryWithContent(string source, string destination) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); + + sut.MoveTo(destination); + + FileSystem.Directory.Exists(source).Should().BeFalse(); + FileSystem.Directory.Exists(destination).Should().BeTrue(); + FileSystem.Directory.GetFiles(destination, initialized[1].Name) + .Should().ContainSingle(); + FileSystem.Directory.GetDirectories(destination, initialized[2].Name) + .Should().ContainSingle(); + FileSystem.Directory.GetFiles(destination, initialized[3].Name, + SearchOption.AllDirectories) + .Should().ContainSingle(); + FileSystem.Directory.GetDirectories(destination, initialized[4].Name, + SearchOption.AllDirectories) + .Should().ContainSingle(); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_ShouldUpdatePropertiesOfDirectoryInfo( + string source, string destination) + { + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); + + sut.MoveTo(destination); + + sut.FullName.TrimEnd(FileSystem.Path.DirectorySeparatorChar) + .Should().Be(FileSystem.Path.GetFullPath(destination)); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_WithLockedFile_ShouldThrowIOException_AndNotMoveDirectory_OnWindows( + string source, string destination) + { + Skip.IfNot(Test.RunsOnWindows); + + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); + using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, + FileMode.Open, + FileAccess.Read, + FileShare.Read); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(destination); + }); + + if (Test.IsNetFramework) + { + // On .NET Framework the HResult is "-2146232800", but only in `DirectoryInfo.MoveTo` (not in `Directory.Move`) + // This peculiar deviation is not supported by the FileSystemMock. + exception.Should().BeException(); + } + else + { + exception.Should().BeException(hResult: -2147024891); + } + + FileSystem.Directory.Exists(source).Should().BeTrue(); + FileSystem.Directory.Exists(destination).Should().BeFalse(); + IDirectoryInfo sourceDirectory = + FileSystem.DirectoryInfo.New(source); + sourceDirectory.GetFiles(initialized[1].Name) + .Should().ContainSingle(); + sourceDirectory.GetDirectories(initialized[2].Name) + .Should().ContainSingle(); + sourceDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + sourceDirectory + .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_WithLockedFile_ShouldMoveDirectory_NotOnWindows( + string source, string destination) + { + Skip.If(Test.RunsOnWindows); + + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); + using FileSystemStream stream = FileSystem.File.Open(initialized[3].FullName, + FileMode.Open, + FileAccess.Read, + FileShare.Read); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(destination); + }); + + exception.Should().BeNull(); + FileSystem.Directory.Exists(source).Should().BeFalse(); + FileSystem.Directory.Exists(destination).Should().BeTrue(); + IDirectoryInfo destinationDirectory = + FileSystem.DirectoryInfo.New(destination); + destinationDirectory.GetFiles(initialized[1].Name) + .Should().ContainSingle(); + destinationDirectory.GetDirectories(initialized[2].Name) + .Should().ContainSingle(); + destinationDirectory + .GetFiles(initialized[3].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + destinationDirectory + .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_WithReadOnlyFile_ShouldMoveDirectoryWithContent( + string source, string destination) + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithSubdirectory(source).Initialized(s => s + .WithAFile() + .WithASubdirectory().Initialized(t => t + .WithAFile() + .WithASubdirectory())); + initialized[3].Attributes = FileAttributes.ReadOnly; + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(source); + + sut.MoveTo(destination); + + FileSystem.Directory.Exists(source).Should().BeFalse(); + FileSystem.Directory.Exists(destination).Should().BeTrue(); + IDirectoryInfo destinationDirectory = + FileSystem.DirectoryInfo.New(destination); + destinationDirectory.GetFiles(initialized[1].Name) + .Should().ContainSingle(); + destinationDirectory.GetDirectories(initialized[2].Name) + .Should().ContainSingle(); + destinationDirectory.GetFiles(initialized[3].Name, SearchOption.AllDirectories) + .Should().ContainSingle().Which.Attributes.Should() + .HaveFlag(FileAttributes.ReadOnly); + destinationDirectory + .GetDirectories(initialized[4].Name, SearchOption.AllDirectories) + .Should().ContainSingle(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/Tests.cs index f20703a31..79f716d42 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/Tests.cs @@ -1,376 +1,376 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [InlineData("foo")] - [InlineData("foo/")] - public void Extension_ShouldReturnEmptyString(string path) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - - string result = sut.Extension; - - result.Should().BeEmpty(); - } - - [SkippableTheory] - [InlineData(@"/temp\\folder")] - [InlineData(@"/temp/folder")] - [InlineData(@"/temp/\\/folder")] - public void FullName_ShouldNotNormalizePathOnLinux(string path) - { - Skip.If(Test.RunsOnWindows); - - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - - sut.FullName.Should().Be(path); - } - - [SkippableTheory] - [InlineData("foo")] - [InlineData("foo/")] - public void FullName_ShouldReturnFullPath(string path) - { - string expectedPath = FileSystem.Path.GetFullPath(path); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - - sut.FullName.Should().Be(expectedPath); - } - - [SkippableTheory] - [InlineData(@"\\unc\folder", @"\\unc\folder")] - [InlineData(@"\\unc/folder\\foo", @"\\unc\folder\foo")] - [InlineData(@"c:\temp\\folder", @"c:\temp\folder")] - [InlineData(@"c:\temp//folder", @"c:\temp\folder")] - [InlineData(@"c:\temp//\\///folder", @"c:\temp\folder")] - public void FullName_ShouldReturnNormalizedPath_OnWindows( - string path, string expectedPath) - { - Skip.IfNot(Test.RunsOnWindows); - - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - - sut.FullName.Should().Be(expectedPath); - } - - [SkippableTheory] - [AutoData] - public void FullName_ShouldTrimTrailingSpaces_OnWindows(string path) - { - path = FileSystem.Path.GetFullPath(path); - string pathWithSpaces = path + " "; - - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(pathWithSpaces); - - if (Test.RunsOnWindows) - { - sut.FullName.Should().Be(path); - } - else - { - sut.FullName.Should().Be(pathWithSpaces); - } - } - - [SkippableTheory] - [AutoData] - public void - MissingFile_Attributes_ShouldAlwaysBeNegativeOne_AndSetterShouldThrowFileNotFoundException( - FileAttributes fileAttributes) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); - sut.Attributes.Should().Be((FileAttributes)(-1)); - Exception? exception = Record.Exception(() => - { - sut.Attributes = fileAttributes; - }); - exception.Should().BeException(hResult: -2147024894); - sut.Attributes.Should().Be((FileAttributes)(-1)); - } - - [SkippableTheory] - [AutoData] - public void - MissingFile_CreationTime_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( - DateTime creationTime) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); - sut.CreationTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); - Exception? exception = Record.Exception(() => - { - sut.CreationTime = creationTime; - }); - - if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) - { - exception.Should().BeException(hResult: -2147024894); - } - else - { - exception.Should().BeException(hResult: -2147024893); - } - - sut.CreationTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); - } - - [SkippableTheory] - [AutoData] - public void - MissingFile_CreationTimeUtc_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( - DateTime creationTimeUtc) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); - sut.CreationTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); - Exception? exception = Record.Exception(() => - { - sut.CreationTimeUtc = creationTimeUtc; - }); - - if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) - { - exception.Should().BeException(hResult: -2147024894); - } - else - { - exception.Should().BeException(hResult: -2147024893); - } - - sut.CreationTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); - } - - [SkippableTheory] - [AutoData] - public void - MissingFile_LastAccessTime_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( - DateTime lastAccessTime) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); - sut.LastAccessTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); - Exception? exception = Record.Exception(() => - { - sut.LastAccessTime = lastAccessTime; - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException(hResult: -2147024894); - } - else - { - exception.Should().BeException(hResult: -2147024893); - } - - sut.LastAccessTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); - } - - [SkippableTheory] - [AutoData] - public void - MissingFile_LastAccessTimeUtc_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( - DateTime lastAccessTimeUtc) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); - sut.LastAccessTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); - Exception? exception = Record.Exception(() => - { - sut.LastAccessTimeUtc = lastAccessTimeUtc; - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException(hResult: -2147024894); - } - else - { - exception.Should().BeException(hResult: -2147024893); - } - - sut.LastAccessTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); - } - - [SkippableTheory] - [AutoData] - public void - MissingFile_LastWriteTime_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( - DateTime lastWriteTime) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); - sut.LastWriteTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); - Exception? exception = Record.Exception(() => - { - sut.LastWriteTime = lastWriteTime; - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException(hResult: -2147024894); - } - else - { - exception.Should().BeException(hResult: -2147024893); - } - - sut.LastWriteTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); - } - - [SkippableTheory] - [AutoData] - public void - MissingFile_LastWriteTimeUtc_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( - DateTime lastWriteTimeUtc) - { - IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); - sut.LastWriteTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); - Exception? exception = Record.Exception(() => - { - sut.LastWriteTimeUtc = lastWriteTimeUtc; - }); - - if (Test.RunsOnWindows || Test.IsNet7OrGreater) - { - exception.Should().BeException(hResult: -2147024894); - } - else - { - exception.Should().BeException(hResult: -2147024893); - } - - sut.LastWriteTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); - } - - [SkippableTheory] - [AutoData] - public void Name_ShouldTrimTrailingSpaces_OnWindows(string path) - { - string pathWithSpaces = path + " "; - - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(pathWithSpaces); - - if (Test.RunsOnWindows) - { - sut.Name.Should().Be(path); - } - else - { - sut.Name.Should().Be(pathWithSpaces); - } - } - - [SkippableTheory] - [AutoData] - public void Parent_ArbitraryPaths_ShouldNotBeNull(string path1, - string path2, - string path3) - { - string path = FileSystem.Path.Combine(path1, path2, path3); - - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - - sut.Parent.Should().NotBeNull(); - sut.Parent!.Exists.Should().BeFalse(); - sut.Parent.Parent.Should().NotBeNull(); - sut.Parent.Parent!.Exists.Should().BeFalse(); - } - - [SkippableFact] - [AutoData] - public void Parent_Root_ShouldBeNull() - { - IDirectoryInfo sut = - FileSystem.DirectoryInfo.New(FileTestHelper.RootDrive()); - - sut.Parent.Should().BeNull(); - } - - [SkippableTheory] - [InlineAutoData("./foo/bar", "foo")] - [InlineAutoData("./foo", ".")] - public void Parent_ToString_ShouldBeAbsolutePathOnNetCore( - string path, string expectedParent) - { - Skip.If(Test.IsNetFramework); - - FileSystem.Initialize(); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - sut.ToString().Should().Be(path); - - IDirectoryInfo? parent = sut.Parent; - - parent.Should().NotBeNull(); - if (Test.IsNetFramework) - { - parent!.ToString().Should().Be(expectedParent); - } - else - { - parent!.ToString().Should().Be(FileSystem.Path.GetFullPath(expectedParent)); - } - } - - [SkippableTheory] - [InlineAutoData("./foo/bar", "foo")] - [InlineAutoData("./foo", "bar", "bar")] - public void Parent_ToString_ShouldBeDirectoryNameOnNetFramework( - string path, string expectedParent, string directory) - { - Skip.IfNot(Test.IsNetFramework); - - FileSystem.InitializeIn(directory); - IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); - sut.ToString().Should().Be(path); - - IDirectoryInfo? parent = sut.Parent; - - parent.Should().NotBeNull(); - if (Test.IsNetFramework) - { - parent!.ToString().Should().Be(expectedParent); - } - else - { - parent!.ToString().Should().Be(FileSystem.Path.GetFullPath(expectedParent)); - } - } - - [SkippableFact] - [AutoData] - public void Root_Name_ShouldBeCorrect() - { - string rootName = FileTestHelper.RootDrive(); - IDirectoryInfo sut = - FileSystem.DirectoryInfo.New(rootName); - - sut.FullName.Should().Be(rootName); - sut.Name.Should().Be(rootName); - } - - [SkippableTheory] - [AutoData] - public void Root_ShouldExist(string path) - { - string expectedRoot = FileTestHelper.RootDrive(); - IDirectoryInfo result = FileSystem.DirectoryInfo.New(path); - - result.Root.Exists.Should().BeTrue(); - result.Root.FullName.Should().Be(expectedRoot); - } - - [SkippableTheory] - [InlineData("/foo")] - [InlineData("./foo")] - [InlineData("foo")] - public void ToString_ShouldReturnProvidedPath(string path) - { - IDirectoryInfo directoryInfo = FileSystem.DirectoryInfo.New(path); - - string? result = directoryInfo.ToString(); - - result.Should().Be(path); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [InlineData("foo")] + [InlineData("foo/")] + public void Extension_ShouldReturnEmptyString(string path) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + + string result = sut.Extension; + + result.Should().BeEmpty(); + } + + [SkippableTheory] + [InlineData(@"/temp\\folder")] + [InlineData(@"/temp/folder")] + [InlineData(@"/temp/\\/folder")] + public void FullName_ShouldNotNormalizePathOnLinux(string path) + { + Skip.If(Test.RunsOnWindows); + + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + + sut.FullName.Should().Be(path); + } + + [SkippableTheory] + [InlineData("foo")] + [InlineData("foo/")] + public void FullName_ShouldReturnFullPath(string path) + { + string expectedPath = FileSystem.Path.GetFullPath(path); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + + sut.FullName.Should().Be(expectedPath); + } + + [SkippableTheory] + [InlineData(@"\\unc\folder", @"\\unc\folder")] + [InlineData(@"\\unc/folder\\foo", @"\\unc\folder\foo")] + [InlineData(@"c:\temp\\folder", @"c:\temp\folder")] + [InlineData(@"c:\temp//folder", @"c:\temp\folder")] + [InlineData(@"c:\temp//\\///folder", @"c:\temp\folder")] + public void FullName_ShouldReturnNormalizedPath_OnWindows( + string path, string expectedPath) + { + Skip.IfNot(Test.RunsOnWindows); + + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + + sut.FullName.Should().Be(expectedPath); + } + + [SkippableTheory] + [AutoData] + public void FullName_ShouldTrimTrailingSpaces_OnWindows(string path) + { + path = FileSystem.Path.GetFullPath(path); + string pathWithSpaces = path + " "; + + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(pathWithSpaces); + + if (Test.RunsOnWindows) + { + sut.FullName.Should().Be(path); + } + else + { + sut.FullName.Should().Be(pathWithSpaces); + } + } + + [SkippableTheory] + [AutoData] + public void + MissingFile_Attributes_ShouldAlwaysBeNegativeOne_AndSetterShouldThrowFileNotFoundException( + FileAttributes fileAttributes) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); + sut.Attributes.Should().Be((FileAttributes)(-1)); + Exception? exception = Record.Exception(() => + { + sut.Attributes = fileAttributes; + }); + exception.Should().BeException(hResult: -2147024894); + sut.Attributes.Should().Be((FileAttributes)(-1)); + } + + [SkippableTheory] + [AutoData] + public void + MissingFile_CreationTime_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( + DateTime creationTime) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); + sut.CreationTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); + Exception? exception = Record.Exception(() => + { + sut.CreationTime = creationTime; + }); + + if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) + { + exception.Should().BeException(hResult: -2147024894); + } + else + { + exception.Should().BeException(hResult: -2147024893); + } + + sut.CreationTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); + } + + [SkippableTheory] + [AutoData] + public void + MissingFile_CreationTimeUtc_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( + DateTime creationTimeUtc) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); + sut.CreationTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); + Exception? exception = Record.Exception(() => + { + sut.CreationTimeUtc = creationTimeUtc; + }); + + if (Test.RunsOnWindows || (Test.IsNet7OrGreater && !Test.RunsOnMac)) + { + exception.Should().BeException(hResult: -2147024894); + } + else + { + exception.Should().BeException(hResult: -2147024893); + } + + sut.CreationTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); + } + + [SkippableTheory] + [AutoData] + public void + MissingFile_LastAccessTime_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( + DateTime lastAccessTime) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); + sut.LastAccessTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); + Exception? exception = Record.Exception(() => + { + sut.LastAccessTime = lastAccessTime; + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException(hResult: -2147024894); + } + else + { + exception.Should().BeException(hResult: -2147024893); + } + + sut.LastAccessTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); + } + + [SkippableTheory] + [AutoData] + public void + MissingFile_LastAccessTimeUtc_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( + DateTime lastAccessTimeUtc) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); + sut.LastAccessTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); + Exception? exception = Record.Exception(() => + { + sut.LastAccessTimeUtc = lastAccessTimeUtc; + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException(hResult: -2147024894); + } + else + { + exception.Should().BeException(hResult: -2147024893); + } + + sut.LastAccessTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); + } + + [SkippableTheory] + [AutoData] + public void + MissingFile_LastWriteTime_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( + DateTime lastWriteTime) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); + sut.LastWriteTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); + Exception? exception = Record.Exception(() => + { + sut.LastWriteTime = lastWriteTime; + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException(hResult: -2147024894); + } + else + { + exception.Should().BeException(hResult: -2147024893); + } + + sut.LastWriteTime.Should().Be(FileTestHelper.NullTime.ToLocalTime()); + } + + [SkippableTheory] + [AutoData] + public void + MissingFile_LastWriteTimeUtc_ShouldAlwaysBeNullTime_AndSetterShouldThrowCorrectException( + DateTime lastWriteTimeUtc) + { + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("Missing File"); + sut.LastWriteTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); + Exception? exception = Record.Exception(() => + { + sut.LastWriteTimeUtc = lastWriteTimeUtc; + }); + + if (Test.RunsOnWindows || Test.IsNet7OrGreater) + { + exception.Should().BeException(hResult: -2147024894); + } + else + { + exception.Should().BeException(hResult: -2147024893); + } + + sut.LastWriteTimeUtc.Should().Be(FileTestHelper.NullTime.ToUniversalTime()); + } + + [SkippableTheory] + [AutoData] + public void Name_ShouldTrimTrailingSpaces_OnWindows(string path) + { + string pathWithSpaces = path + " "; + + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(pathWithSpaces); + + if (Test.RunsOnWindows) + { + sut.Name.Should().Be(path); + } + else + { + sut.Name.Should().Be(pathWithSpaces); + } + } + + [SkippableTheory] + [AutoData] + public void Parent_ArbitraryPaths_ShouldNotBeNull(string path1, + string path2, + string path3) + { + string path = FileSystem.Path.Combine(path1, path2, path3); + + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + + sut.Parent.Should().NotBeNull(); + sut.Parent!.Exists.Should().BeFalse(); + sut.Parent.Parent.Should().NotBeNull(); + sut.Parent.Parent!.Exists.Should().BeFalse(); + } + + [SkippableFact] + [AutoData] + public void Parent_Root_ShouldBeNull() + { + IDirectoryInfo sut = + FileSystem.DirectoryInfo.New(FileTestHelper.RootDrive()); + + sut.Parent.Should().BeNull(); + } + + [SkippableTheory] + [InlineAutoData("./foo/bar", "foo")] + [InlineAutoData("./foo", ".")] + public void Parent_ToString_ShouldBeAbsolutePathOnNetCore( + string path, string expectedParent) + { + Skip.If(Test.IsNetFramework); + + FileSystem.Initialize(); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + sut.ToString().Should().Be(path); + + IDirectoryInfo? parent = sut.Parent; + + parent.Should().NotBeNull(); + if (Test.IsNetFramework) + { + parent!.ToString().Should().Be(expectedParent); + } + else + { + parent!.ToString().Should().Be(FileSystem.Path.GetFullPath(expectedParent)); + } + } + + [SkippableTheory] + [InlineAutoData("./foo/bar", "foo")] + [InlineAutoData("./foo", "bar", "bar")] + public void Parent_ToString_ShouldBeDirectoryNameOnNetFramework( + string path, string expectedParent, string directory) + { + Skip.IfNot(Test.IsNetFramework); + + FileSystem.InitializeIn(directory); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(path); + sut.ToString().Should().Be(path); + + IDirectoryInfo? parent = sut.Parent; + + parent.Should().NotBeNull(); + if (Test.IsNetFramework) + { + parent!.ToString().Should().Be(expectedParent); + } + else + { + parent!.ToString().Should().Be(FileSystem.Path.GetFullPath(expectedParent)); + } + } + + [SkippableFact] + [AutoData] + public void Root_Name_ShouldBeCorrect() + { + string rootName = FileTestHelper.RootDrive(); + IDirectoryInfo sut = + FileSystem.DirectoryInfo.New(rootName); + + sut.FullName.Should().Be(rootName); + sut.Name.Should().Be(rootName); + } + + [SkippableTheory] + [AutoData] + public void Root_ShouldExist(string path) + { + string expectedRoot = FileTestHelper.RootDrive(); + IDirectoryInfo result = FileSystem.DirectoryInfo.New(path); + + result.Root.Exists.Should().BeTrue(); + result.Root.FullName.Should().Be(expectedRoot); + } + + [SkippableTheory] + [InlineData("/foo")] + [InlineData("./foo")] + [InlineData("foo")] + public void ToString_ShouldReturnProvidedPath(string path) + { + IDirectoryInfo directoryInfo = FileSystem.DirectoryInfo.New(path); + + string? result = directoryInfo.ToString(); + + result.Should().Be(path); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfoFactory/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfoFactory/ExceptionTests.cs index 7ab85d3a6..4f64bd79b 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfoFactory/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfoFactory/ExceptionTests.cs @@ -1,117 +1,117 @@ -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfoFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), parameters: " ")] - public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), - parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowArgumentException_OnNetFramework( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DirectoryInfo); - }); - - if (Test.IsNetFramework) - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should() - .BeNull( - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - - #region Helpers - - public static IEnumerable GetDirectoryInfoFactoryCallbacks(string? path) - => GetDirectoryInfoFactoryCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetDirectoryInfoFactoryCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.All, "path", directoryInfoFactory - => directoryInfoFactory.New(value)); - } - - #endregion -} +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfoFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), parameters: " ")] + public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetDirectoryInfoFactoryCallbacks), + parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowArgumentException_OnNetFramework( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DirectoryInfo); + }); + + if (Test.IsNetFramework) + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should() + .BeNull( + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + + #region Helpers + + public static IEnumerable GetDirectoryInfoFactoryCallbacks(string? path) + => GetDirectoryInfoFactoryCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetDirectoryInfoFactoryCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.All, "path", directoryInfoFactory + => directoryInfoFactory.New(value)); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfoFactory/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfoFactory/Tests.cs index 6b85dcd0c..acdadc0c2 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfoFactory/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfoFactory/Tests.cs @@ -1,5 +1,3 @@ -using Testably.Abstractions.FileSystem; - namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfoFactory; // ReSharper disable once PartialTypeWithSinglePart diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfo/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfo/Tests.cs index f7373d6ec..0255c7a33 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfo/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfo/Tests.cs @@ -1,5 +1,3 @@ -using Testably.Abstractions.FileSystem; - namespace Testably.Abstractions.Tests.FileSystem.DriveInfo; // ReSharper disable once PartialTypeWithSinglePart diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/ExceptionTests.cs index 3681beffa..30bc06ab1 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/ExceptionTests.cs @@ -1,86 +1,86 @@ -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -namespace Testably.Abstractions.Tests.FileSystem.DriveInfoFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [InlineData("?invalid-drive-name")] - [InlineData("invalid")] - [InlineData(" ")] - public void New_WhenDriveNameIsInvalid_ShouldThrowArgumentException( - string driveName) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - FileSystem.DriveInfo.New(driveName); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [Theory] - [MemberData(nameof(GetDriveInfoFactoryCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DriveInfo); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetDriveInfoFactoryCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.DriveInfo); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - #region Helpers - - public static IEnumerable GetDriveInfoFactoryCallbacks(string? path) - => GetDriveInfoFactoryCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string ParamName, - Expression> Callback)> - GetDriveInfoFactoryCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.All, "driveName", driveInfoFactory - => driveInfoFactory.New(value)); - } - - #endregion -} +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.DriveInfoFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [InlineData("?invalid-drive-name")] + [InlineData("invalid")] + [InlineData(" ")] + public void New_WhenDriveNameIsInvalid_ShouldThrowArgumentException( + string driveName) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + FileSystem.DriveInfo.New(driveName); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [Theory] + [MemberData(nameof(GetDriveInfoFactoryCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DriveInfo); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetDriveInfoFactoryCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.DriveInfo); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + #region Helpers + + public static IEnumerable GetDriveInfoFactoryCallbacks(string? path) + => GetDriveInfoFactoryCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string ParamName, + Expression> Callback)> + GetDriveInfoFactoryCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.All, "driveName", driveInfoFactory + => driveInfoFactory.New(value)); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/Tests.cs index 93e5ca047..63b5ed213 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DriveInfoFactory/Tests.cs @@ -1,157 +1,157 @@ -using System.IO; -using System.Linq; - -namespace Testably.Abstractions.Tests.FileSystem.DriveInfoFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableFact] - public void GetDrives_ShouldNotBeEmpty() - { - IDriveInfo[] result = FileSystem.DriveInfo.GetDrives(); - - result.Should().NotBeEmpty(); - } - - [SkippableTheory] - [AutoData] - public void MissingDrive_CreateDirectoryInfo_ShouldOnlyThrowWhenAccessingData( - string path, string subPath) - { - Skip.IfNot(Test.RunsOnWindows); - - IDriveInfo driveInfo = GetUnmappedDrive(); - - path = $"{driveInfo.Name}{path}"; - IDirectoryInfo directoryInfo = - FileSystem.DirectoryInfo.New(FileSystem.Path.Combine(path, subPath)); - IDirectoryInfo? parent = directoryInfo.Parent; - - Exception? exception = Record.Exception(() => - { - _ = parent!.EnumerateDirectories().ToArray(); - }); - - exception.Should().BeException($"'{path}'", - hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void MissingDrive_WriteAllBytes_ShouldThrowDirectoryNotFoundException( - string path, byte[] contents) - { - Skip.IfNot(Test.RunsOnWindows); - - IDriveInfo driveInfo = GetUnmappedDrive(); - - path = $"{driveInfo.Name}{path}"; - - Exception? exception = Record.Exception(() => - { - FileSystem.File.WriteAllBytes(path, contents); - }); - - exception.Should().BeException($"'{path}'", - hResult: -2147024893); - } - - [SkippableFact] - public void New_DefaultDrive_ShouldBeFixed() - { - IDriveInfo result = - FileSystem.DriveInfo.New(FileTestHelper.RootDrive()); - - result.AvailableFreeSpace.Should().BeGreaterThan(0); - result.DriveFormat.Should().NotBeNull(); - result.DriveType.Should().Be(DriveType.Fixed); - result.IsReady.Should().BeTrue(); - result.RootDirectory.FullName.Should().Be(FileTestHelper.RootDrive()); - result.TotalFreeSpace.Should().BeGreaterThan(0); - result.TotalSize.Should().BeGreaterThan(0); - result.VolumeLabel.Should().NotBeEmpty(); - } - - [SkippableTheory] - [AutoData] - public void New_InvalidDriveName_ShouldThrowArgumentException( - string invalidDriveName) - { - Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); - - Exception? exception = Record.Exception(() => - { - _ = FileSystem.DriveInfo.New(invalidDriveName); - }); - - exception.Should().BeException(hResult: -2147024809); - } - - [SkippableTheory] - [InlineData('A')] - [InlineData('C')] - [InlineData('X')] - public void New_WithDriveLetter_ShouldReturnDriveInfo(char driveLetter) - { - Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); - - IDriveInfo result = FileSystem.DriveInfo.New($"{driveLetter}"); - - result.Name.Should().Be($"{driveLetter}:\\"); - } - - [SkippableTheory] - [InlineAutoData('A')] - [InlineAutoData('C')] - [InlineAutoData('Y')] - public void New_WithRootedPath_ShouldReturnDriveInfo(char driveLetter, string path) - { - Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); - - string rootedPath = FileTestHelper.RootDrive(path, driveLetter); - - IDriveInfo result = FileSystem.DriveInfo.New(rootedPath); - - result.Name.Should().Be($"{driveLetter}:\\"); - } - - [SkippableFact] - public void Wrap_Null_ShouldReturnNull() - { - IDriveInfo? result = FileSystem.DriveInfo.Wrap(null); - - result.Should().BeNull(); - } - - [SkippableFact] - public void Wrap_ShouldReturnDriveInfoWithSameName() - { - System.IO.DriveInfo driveInfo = System.IO.DriveInfo.GetDrives().First(); - - IDriveInfo result = FileSystem.DriveInfo.Wrap(driveInfo); - - result.Name.Should().Be(driveInfo.Name); - } - - #region Helpers - - private IDriveInfo GetUnmappedDrive() - { - IDriveInfo? driveInfo = null; - for (char c = 'A'; c <= 'Z'; c++) - { - driveInfo = FileSystem.DriveInfo.New($"{c}"); - if (FileSystem.DriveInfo.GetDrives().All(d => d.Name != driveInfo.Name)) - { - break; - } - } - - return driveInfo ?? throw new NotSupportedException("No unmapped drive found!"); - } - - #endregion -} +using System.IO; +using System.Linq; + +namespace Testably.Abstractions.Tests.FileSystem.DriveInfoFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableFact] + public void GetDrives_ShouldNotBeEmpty() + { + IDriveInfo[] result = FileSystem.DriveInfo.GetDrives(); + + result.Should().NotBeEmpty(); + } + + [SkippableTheory] + [AutoData] + public void MissingDrive_CreateDirectoryInfo_ShouldOnlyThrowWhenAccessingData( + string path, string subPath) + { + Skip.IfNot(Test.RunsOnWindows); + + IDriveInfo driveInfo = GetUnmappedDrive(); + + path = $"{driveInfo.Name}{path}"; + IDirectoryInfo directoryInfo = + FileSystem.DirectoryInfo.New(FileSystem.Path.Combine(path, subPath)); + IDirectoryInfo? parent = directoryInfo.Parent; + + Exception? exception = Record.Exception(() => + { + _ = parent!.EnumerateDirectories().ToArray(); + }); + + exception.Should().BeException($"'{path}'", + hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void MissingDrive_WriteAllBytes_ShouldThrowDirectoryNotFoundException( + string path, byte[] contents) + { + Skip.IfNot(Test.RunsOnWindows); + + IDriveInfo driveInfo = GetUnmappedDrive(); + + path = $"{driveInfo.Name}{path}"; + + Exception? exception = Record.Exception(() => + { + FileSystem.File.WriteAllBytes(path, contents); + }); + + exception.Should().BeException($"'{path}'", + hResult: -2147024893); + } + + [SkippableFact] + public void New_DefaultDrive_ShouldBeFixed() + { + IDriveInfo result = + FileSystem.DriveInfo.New(FileTestHelper.RootDrive()); + + result.AvailableFreeSpace.Should().BeGreaterThan(0); + result.DriveFormat.Should().NotBeNull(); + result.DriveType.Should().Be(DriveType.Fixed); + result.IsReady.Should().BeTrue(); + result.RootDirectory.FullName.Should().Be(FileTestHelper.RootDrive()); + result.TotalFreeSpace.Should().BeGreaterThan(0); + result.TotalSize.Should().BeGreaterThan(0); + result.VolumeLabel.Should().NotBeEmpty(); + } + + [SkippableTheory] + [AutoData] + public void New_InvalidDriveName_ShouldThrowArgumentException( + string invalidDriveName) + { + Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); + + Exception? exception = Record.Exception(() => + { + _ = FileSystem.DriveInfo.New(invalidDriveName); + }); + + exception.Should().BeException(hResult: -2147024809); + } + + [SkippableTheory] + [InlineData('A')] + [InlineData('C')] + [InlineData('X')] + public void New_WithDriveLetter_ShouldReturnDriveInfo(char driveLetter) + { + Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); + + IDriveInfo result = FileSystem.DriveInfo.New($"{driveLetter}"); + + result.Name.Should().Be($"{driveLetter}:\\"); + } + + [SkippableTheory] + [InlineAutoData('A')] + [InlineAutoData('C')] + [InlineAutoData('Y')] + public void New_WithRootedPath_ShouldReturnDriveInfo(char driveLetter, string path) + { + Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives."); + + string rootedPath = FileTestHelper.RootDrive(path, driveLetter); + + IDriveInfo result = FileSystem.DriveInfo.New(rootedPath); + + result.Name.Should().Be($"{driveLetter}:\\"); + } + + [SkippableFact] + public void Wrap_Null_ShouldReturnNull() + { + IDriveInfo? result = FileSystem.DriveInfo.Wrap(null); + + result.Should().BeNull(); + } + + [SkippableFact] + public void Wrap_ShouldReturnDriveInfoWithSameName() + { + System.IO.DriveInfo driveInfo = System.IO.DriveInfo.GetDrives().First(); + + IDriveInfo result = FileSystem.DriveInfo.Wrap(driveInfo); + + result.Name.Should().Be(driveInfo.Name); + } + + #region Helpers + + private IDriveInfo GetUnmappedDrive() + { + IDriveInfo? driveInfo = null; + for (char c = 'A'; c <= 'Z'; c++) + { + driveInfo = FileSystem.DriveInfo.New($"{c}"); + if (FileSystem.DriveInfo.GetDrives().All(d => d.Name != driveInfo.Name)) + { + break; + } + } + + return driveInfo ?? throw new NotSupportedException("No unmapped drive found!"); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/CopyTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/CopyTests.cs index 5ec714cd0..86e185030 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/CopyTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/CopyTests.cs @@ -1,324 +1,324 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class CopyTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - Copy_DestinationDirectoryDoesNotExist_ShouldThrowDirectoryNotFoundException( - string source) - { - FileSystem.Initialize() - .WithFile(source); - string destination = FileTestHelper.RootDrive("not-existing/path/foo.txt"); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Copy(source, destination); - }); - - exception.Should().BeException(hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Copy_DestinationExists_ShouldThrowIOException_AndNotCopyFile( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Copy(sourceName, destinationName); - }); - - exception.Should().BeException(hResult: Test.RunsOnWindows ? -2147024816 : 17); - - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - } - -#if FEATURE_FILE_MOVETO_OVERWRITE - [SkippableTheory] - [AutoData] - public void Copy_DestinationExists_WithOverwrite_ShouldOverwriteDestination( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - - FileSystem.File.Copy(sourceName, destinationName, true); - - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - } -#endif - - [SkippableTheory] - [InlineData(@"0:\something\demo.txt", @"C:\elsewhere\demo.txt")] - [InlineData(@"C:\something\demo.txt", @"^:\elsewhere\demo.txt")] - [InlineData(@"C:\something\demo.txt", @"C:\elsewhere:\demo.txt")] - public void - Copy_InvalidDriveName_ShouldThrowNotSupportedException( - string source, string destination) - { - Skip.IfNot(Test.IsNetFramework); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Copy(source, destination); - }); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [InlineData(@"C::\something\demo.txt", @"C:\elsewhere\demo.txt")] - public void - Copy_InvalidPath_ShouldThrowCorrectException( - string source, string destination) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Copy(source, destination); - }); - - if (Test.IsNetFramework) - { - exception.Should().BeException(hResult: -2146233067); - } - else - { - exception.Should().BeException(hResult: -2147024773); - } - } - - [SkippableTheory] - [AutoData] - public void Copy_ReadOnly_ShouldCopyFile( - string sourceName, string destinationName, string contents) - { - FileSystem.File.WriteAllText(sourceName, contents); - FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); - - FileSystem.File.Copy(sourceName, destinationName); - - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.GetAttributes(destinationName) - .Should().HaveFlag(FileAttributes.ReadOnly); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void Copy_ShouldAdjustTimes( - string source, string destination) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllText(source, "foo"); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - - FileSystem.File.Copy(source, destination); - - DateTime sourceCreationTime = FileSystem.File.GetCreationTimeUtc(source); - DateTime sourceLastAccessTime = FileSystem.File.GetLastAccessTimeUtc(source); - DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTimeUtc(source); - DateTime destinationCreationTime = - FileSystem.File.GetCreationTimeUtc(destination); - DateTime destinationLastAccessTime = - FileSystem.File.GetLastAccessTimeUtc(destination); - DateTime destinationLastWriteTime = - FileSystem.File.GetLastWriteTimeUtc(destination); - - sourceCreationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - if (Test.RunsOnWindows) - { - sourceLastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - else - { - sourceLastAccessTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - } - - sourceLastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - if (Test.RunsOnWindows) - { - destinationCreationTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - } - else - { - destinationCreationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - destinationLastAccessTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - destinationLastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - [SkippableTheory] - [AutoData] - public void Copy_ShouldCloneBinaryContent( - string source, string destination, byte[] original) - { - FileSystem.File.WriteAllBytes(source, original); - - FileSystem.File.Copy(source, destination); - - using (FileSystemStream stream = - FileSystem.File.Open(source, FileMode.Open, FileAccess.ReadWrite)) - { - BinaryWriter binaryWriter = new(stream); - - binaryWriter.Seek(0, SeekOrigin.Begin); - binaryWriter.Write("Some text"); - } - - FileSystem.File.ReadAllBytes(destination).Should() - .BeEquivalentTo(original); - FileSystem.File.ReadAllBytes(destination).Should() - .NotBeEquivalentTo(FileSystem.File.ReadAllBytes(source)); - } - - [SkippableTheory] - [AutoData] - public void Copy_ShouldCloneTextContent( - string source, string destination, string contents) - { - FileSystem.File.WriteAllText(source, contents); - - FileSystem.File.Copy(source, destination); - - using (FileSystemStream stream = - FileSystem.File.Open(source, FileMode.Open, FileAccess.ReadWrite)) - { - BinaryWriter binaryWriter = new(stream); - - binaryWriter.Seek(0, SeekOrigin.Begin); - binaryWriter.Write("Some text"); - } - - FileSystem.File.ReadAllText(source).Should() - .NotBe(FileSystem.File.ReadAllText(destination)); - } - - [SkippableTheory] - [AutoData] - public void Copy_ShouldCopyFileWithContent( - string sourceName, string destinationName, string contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText(sourceName, contents); - - TimeSystem.Thread.Sleep(1000); - - FileSystem.File.Copy(sourceName, destinationName); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(contents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void Copy_SourceIsDirectory_ShouldThrowUnauthorizedAccessException_AndNotCopyFile( - string sourceName, - string destinationName) - { - FileSystem.Directory.CreateDirectory(sourceName); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Copy(sourceName, destinationName); - }); - - exception.Should().BeException( - hResult: -2147024891, - messageContains: Test.IsNetFramework - ? $"'{sourceName}'" - : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); - FileSystem.Directory.Exists(sourceName).Should().BeTrue(); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Copy_SourceLocked_ShouldThrowIOException( - string sourceName, - string destinationName) - { - FileSystem.File.WriteAllText(sourceName, null); - using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, - FileAccess.Read, FileShare.None); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Copy(sourceName, destinationName); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024864); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - else - { - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void Copy_SourceMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName) - { - Exception? exception = Record.Exception(() => - { - FileSystem.File.Copy(sourceName, destinationName); - }); - - exception.Should().BeException( - hResult: -2147024894, - messageContains: Test.IsNetFramework - ? null - : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class CopyTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + Copy_DestinationDirectoryDoesNotExist_ShouldThrowDirectoryNotFoundException( + string source) + { + FileSystem.Initialize() + .WithFile(source); + string destination = FileTestHelper.RootDrive("not-existing/path/foo.txt"); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Copy(source, destination); + }); + + exception.Should().BeException(hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Copy_DestinationExists_ShouldThrowIOException_AndNotCopyFile( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Copy(sourceName, destinationName); + }); + + exception.Should().BeException(hResult: Test.RunsOnWindows ? -2147024816 : 17); + + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + } + +#if FEATURE_FILE_MOVETO_OVERWRITE + [SkippableTheory] + [AutoData] + public void Copy_DestinationExists_WithOverwrite_ShouldOverwriteDestination( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + + FileSystem.File.Copy(sourceName, destinationName, true); + + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + } +#endif + + [SkippableTheory] + [InlineData(@"0:\something\demo.txt", @"C:\elsewhere\demo.txt")] + [InlineData(@"C:\something\demo.txt", @"^:\elsewhere\demo.txt")] + [InlineData(@"C:\something\demo.txt", @"C:\elsewhere:\demo.txt")] + public void + Copy_InvalidDriveName_ShouldThrowNotSupportedException( + string source, string destination) + { + Skip.IfNot(Test.IsNetFramework); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Copy(source, destination); + }); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [InlineData(@"C::\something\demo.txt", @"C:\elsewhere\demo.txt")] + public void + Copy_InvalidPath_ShouldThrowCorrectException( + string source, string destination) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Copy(source, destination); + }); + + if (Test.IsNetFramework) + { + exception.Should().BeException(hResult: -2146233067); + } + else + { + exception.Should().BeException(hResult: -2147024773); + } + } + + [SkippableTheory] + [AutoData] + public void Copy_ReadOnly_ShouldCopyFile( + string sourceName, string destinationName, string contents) + { + FileSystem.File.WriteAllText(sourceName, contents); + FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); + + FileSystem.File.Copy(sourceName, destinationName); + + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.GetAttributes(destinationName) + .Should().HaveFlag(FileAttributes.ReadOnly); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void Copy_ShouldAdjustTimes( + string source, string destination) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllText(source, "foo"); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + + FileSystem.File.Copy(source, destination); + + DateTime sourceCreationTime = FileSystem.File.GetCreationTimeUtc(source); + DateTime sourceLastAccessTime = FileSystem.File.GetLastAccessTimeUtc(source); + DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTimeUtc(source); + DateTime destinationCreationTime = + FileSystem.File.GetCreationTimeUtc(destination); + DateTime destinationLastAccessTime = + FileSystem.File.GetLastAccessTimeUtc(destination); + DateTime destinationLastWriteTime = + FileSystem.File.GetLastWriteTimeUtc(destination); + + sourceCreationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + if (Test.RunsOnWindows) + { + sourceLastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + else + { + sourceLastAccessTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + } + + sourceLastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + if (Test.RunsOnWindows) + { + destinationCreationTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + } + else + { + destinationCreationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + destinationLastAccessTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + destinationLastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + [SkippableTheory] + [AutoData] + public void Copy_ShouldCloneBinaryContent( + string source, string destination, byte[] original) + { + FileSystem.File.WriteAllBytes(source, original); + + FileSystem.File.Copy(source, destination); + + using (FileSystemStream stream = + FileSystem.File.Open(source, FileMode.Open, FileAccess.ReadWrite)) + { + BinaryWriter binaryWriter = new(stream); + + binaryWriter.Seek(0, SeekOrigin.Begin); + binaryWriter.Write("Some text"); + } + + FileSystem.File.ReadAllBytes(destination).Should() + .BeEquivalentTo(original); + FileSystem.File.ReadAllBytes(destination).Should() + .NotBeEquivalentTo(FileSystem.File.ReadAllBytes(source)); + } + + [SkippableTheory] + [AutoData] + public void Copy_ShouldCloneTextContent( + string source, string destination, string contents) + { + FileSystem.File.WriteAllText(source, contents); + + FileSystem.File.Copy(source, destination); + + using (FileSystemStream stream = + FileSystem.File.Open(source, FileMode.Open, FileAccess.ReadWrite)) + { + BinaryWriter binaryWriter = new(stream); + + binaryWriter.Seek(0, SeekOrigin.Begin); + binaryWriter.Write("Some text"); + } + + FileSystem.File.ReadAllText(source).Should() + .NotBe(FileSystem.File.ReadAllText(destination)); + } + + [SkippableTheory] + [AutoData] + public void Copy_ShouldCopyFileWithContent( + string sourceName, string destinationName, string contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText(sourceName, contents); + + TimeSystem.Thread.Sleep(1000); + + FileSystem.File.Copy(sourceName, destinationName); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(contents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void Copy_SourceIsDirectory_ShouldThrowUnauthorizedAccessException_AndNotCopyFile( + string sourceName, + string destinationName) + { + FileSystem.Directory.CreateDirectory(sourceName); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Copy(sourceName, destinationName); + }); + + exception.Should().BeException( + hResult: -2147024891, + messageContains: Test.IsNetFramework + ? $"'{sourceName}'" + : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); + FileSystem.Directory.Exists(sourceName).Should().BeTrue(); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Copy_SourceLocked_ShouldThrowIOException( + string sourceName, + string destinationName) + { + FileSystem.File.WriteAllText(sourceName, null); + using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, + FileAccess.Read, FileShare.None); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Copy(sourceName, destinationName); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024864); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + else + { + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void Copy_SourceMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName) + { + Exception? exception = Record.Exception(() => + { + FileSystem.File.Copy(sourceName, destinationName); + }); + + exception.Should().BeException( + hResult: -2147024894, + messageContains: Test.IsNetFramework + ? null + : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/CreateTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/CreateTests.cs index fb052c091..45f567f2f 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/CreateTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/CreateTests.cs @@ -1,109 +1,109 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class CreateTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Create_ExistingFile_ShouldBeOverwritten( - string path, string originalContent, string newContent) - { - FileSystem.File.WriteAllText(path, originalContent); - - using FileSystemStream stream = FileSystem.File.Create(path); - - using StreamWriter streamWriter = new(stream); - streamWriter.Write(newContent); - - streamWriter.Dispose(); - stream.Dispose(); - FileSystem.File.ReadAllText(path).Should().Be(newContent); - } - - [SkippableTheory] - [AutoData] - public void Create_ReadOnlyFile_ShouldThrowUnauthorizedAccessException( - string path, string content) - { - FileSystem.File.WriteAllText(path, content); - FileSystem.File.SetAttributes(path, FileAttributes.ReadOnly); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Create(path); - }); - - exception.Should().BeException(hResult: -2147024891); - } - - [SkippableTheory] - [AutoData] - public void Create_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string missingDirectory, string fileName) - { - string filePath = FileSystem.Path.Combine(missingDirectory, fileName); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Create(filePath); - }); - - exception.Should().BeException(hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Create_MissingFile_ShouldCreateFile(string path) - { - using FileSystemStream stream = FileSystem.File.Create(path); - - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void Create_ShouldUseReadWriteAccessAndNoneShare(string path) - { - using FileSystemStream stream = FileSystem.File.Create(path); - - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - stream.CanRead.Should().BeTrue(); - stream.CanWrite.Should().BeTrue(); - stream.CanSeek.Should().BeTrue(); - stream.CanTimeout.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Create_WithBufferSize_ShouldUseReadWriteAccessAndNoneShare( - string path, int bufferSize) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = FileSystem.File.Create(path, bufferSize); - - stream.IsAsync.Should().BeFalse(); - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } - - [SkippableTheory] - [AutoData] - public void Create_WithBufferSizeAndFileOptions_ShouldUseReadWriteAccessAndNoneShare( - string path, int bufferSize) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = - FileSystem.File.Create(path, bufferSize, FileOptions.Asynchronous); - - stream.IsAsync.Should().BeTrue(); - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class CreateTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Create_ExistingFile_ShouldBeOverwritten( + string path, string originalContent, string newContent) + { + FileSystem.File.WriteAllText(path, originalContent); + + using FileSystemStream stream = FileSystem.File.Create(path); + + using StreamWriter streamWriter = new(stream); + streamWriter.Write(newContent); + + streamWriter.Dispose(); + stream.Dispose(); + FileSystem.File.ReadAllText(path).Should().Be(newContent); + } + + [SkippableTheory] + [AutoData] + public void Create_ReadOnlyFile_ShouldThrowUnauthorizedAccessException( + string path, string content) + { + FileSystem.File.WriteAllText(path, content); + FileSystem.File.SetAttributes(path, FileAttributes.ReadOnly); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Create(path); + }); + + exception.Should().BeException(hResult: -2147024891); + } + + [SkippableTheory] + [AutoData] + public void Create_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string missingDirectory, string fileName) + { + string filePath = FileSystem.Path.Combine(missingDirectory, fileName); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Create(filePath); + }); + + exception.Should().BeException(hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Create_MissingFile_ShouldCreateFile(string path) + { + using FileSystemStream stream = FileSystem.File.Create(path); + + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void Create_ShouldUseReadWriteAccessAndNoneShare(string path) + { + using FileSystemStream stream = FileSystem.File.Create(path); + + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + stream.CanRead.Should().BeTrue(); + stream.CanWrite.Should().BeTrue(); + stream.CanSeek.Should().BeTrue(); + stream.CanTimeout.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Create_WithBufferSize_ShouldUseReadWriteAccessAndNoneShare( + string path, int bufferSize) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = FileSystem.File.Create(path, bufferSize); + + stream.IsAsync.Should().BeFalse(); + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } + + [SkippableTheory] + [AutoData] + public void Create_WithBufferSizeAndFileOptions_ShouldUseReadWriteAccessAndNoneShare( + string path, int bufferSize) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = + FileSystem.File.Create(path, bufferSize, FileOptions.Asynchronous); + + stream.IsAsync.Should().BeTrue(); + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/DeleteTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/DeleteTests.cs index 592b4116d..260c89e32 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/DeleteTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/DeleteTests.cs @@ -1,71 +1,71 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class DeleteTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string missingDirectory, string fileName) - { - string filePath = FileSystem.Path.Combine(missingDirectory, fileName); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Delete(filePath); - }); - - exception.Should().BeException(hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Delete_MissingFile_ShouldDoNothing( - string fileName) - { - Exception? exception = Record.Exception(() => - { - FileSystem.File.Delete(fileName); - }); - - exception.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void Delete_WithOpenFile_ShouldThrowIOException_OnWindows(string filename) - { - FileSystem.Initialize(); - FileSystemStream openFile = FileSystem.File.OpenWrite(filename); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - Exception? exception = Record.Exception(() => - { - FileSystem.File.Delete(filename); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException($"{filename}'", - hResult: -2147024864); - FileSystem.File.Exists(filename).Should().BeTrue(); - } - else - { - exception.Should().BeNull(); - FileSystem.File.Exists(filename).Should().BeFalse(); - } - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class DeleteTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string missingDirectory, string fileName) + { + string filePath = FileSystem.Path.Combine(missingDirectory, fileName); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Delete(filePath); + }); + + exception.Should().BeException(hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Delete_MissingFile_ShouldDoNothing( + string fileName) + { + Exception? exception = Record.Exception(() => + { + FileSystem.File.Delete(fileName); + }); + + exception.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void Delete_WithOpenFile_ShouldThrowIOException_OnWindows(string filename) + { + FileSystem.Initialize(); + FileSystemStream openFile = FileSystem.File.OpenWrite(filename); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + Exception? exception = Record.Exception(() => + { + FileSystem.File.Delete(filename); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException($"{filename}'", + hResult: -2147024864); + FileSystem.File.Exists(filename).Should().BeTrue(); + } + else + { + exception.Should().BeNull(); + FileSystem.File.Exists(filename).Should().BeFalse(); + } + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionMissingFileTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionMissingFileTests.cs index cf8731c48..d26a2a66b 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionMissingFileTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionMissingFileTests.cs @@ -1,397 +1,396 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Threading; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionMissingFileTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetFileCallbacks), parameters: (int)MissingFileTestCase.FileMissing)] - public void Operations_WhenFileIsMissing_ShouldThrowFileNotFoundException( - Expression> callback) - { - string path = "missing-file.txt"; - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.File, path); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException( - hResult: -2147024894, - because: $"\n{callback}\n was called with a missing file"); - } - else - { - exception.Should() - .BeFileOrDirectoryNotFoundException( - $"\n{callback}\n was called with a missing file"); - } - } - - [Theory] - [MemberData(nameof(GetFileCallbacks), parameters: (int)MissingFileTestCase.DirectoryMissing)] - public void Operations_WhenDirectoryIsMissing_ShouldThrowDirectoryNotFoundException( - Expression> callback) - { - string path = FileSystem.Path.Combine("missing-directory", "file.txt"); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.File, path); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException( - hResult: -2147024893, - because: $"\n{callback}\n was called with a missing directory"); - } - else - { - exception.Should() - .BeFileOrDirectoryNotFoundException( - $"\n{callback}\n was called with a missing directory"); - } - } - - #region Helpers - - [Flags] - public enum MissingFileTestCase - { - FileMissing = 1, - DirectoryMissing = 2, - All = FileMissing | DirectoryMissing - } - - public enum ExpectedExceptionType - { - Default, - } - - public static IEnumerable GetFileCallbacks(int testCases) - => GetFileCallbackTestParameters() - .Where(item => (item.TestCase & (MissingFileTestCase)testCases) != 0) - .Select(item => new object?[] - { - item.Callback - }); - - private static IEnumerable<(MissingFileTestCase TestCase, ExpectedExceptionType ExceptionType, - Expression> Callback)> - GetFileCallbackTestParameters() - { - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.GetAttributes(path)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.SetAttributes(path, FileAttributes.ReadOnly)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.Copy(path, "destination.txt")); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllLines(path, new[] - { - "foo" - })); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllLines(path, new[] - { - "foo" - })); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllLines(path, new[] - { - "foo" - }, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllLinesAsync(path, new[] - { - "foo" - }, CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllLinesAsync(path, new[] - { - "foo" - }, Encoding.UTF8, - CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllText(path, "foo")); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllText(path, "foo", Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllTextAsync(path, "foo", CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendAllTextAsync(path, "foo", Encoding.UTF8, - CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.AppendText(path)); - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Copy(path, "foo")); - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Copy("foo", path)); - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Copy(path, "foo", false)); - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Copy("foo", path, false)); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.Create(path)); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.Create(path, 1023)); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.Create(path, 1023, FileOptions.None)); -#if FEATURE_FILESYSTEM_LINK - if (Test.RunsOnWindows) - { - yield return (MissingFileTestCase.DirectoryMissing, - ExpectedExceptionType.Default, - (file, path) - => file.CreateSymbolicLink(path, "foo")); - } -#endif - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.CreateText(path)); - - if (Test.RunsOnWindows) - { - #pragma warning disable CA1416 - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.Decrypt(path)); - #pragma warning restore CA1416 - } - - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.Delete(path)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.GetAttributes(path)); - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Move(path, "foo")); - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Move("foo", path)); -#if FEATURE_FILE_MOVETO_OVERWRITE - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Move(path, "foo", false)); - yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, - (file, path) - => file.Move("foo", path, false)); -#endif - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.Open(path, FileMode.Open)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.Open(path, FileMode.Open, FileAccess.ReadWrite)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None)); -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.Open(path, new FileStreamOptions())); -#endif - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.OpenRead(path)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.OpenText(path)); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.OpenWrite(path)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllBytes(path)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllBytesAsync(path, CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllLines(path)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllLines(path, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllLinesAsync(path, CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllLinesAsync(path, Encoding.UTF8, CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllText(path)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllText(path, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllTextAsync(path, CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadAllTextAsync(path, Encoding.UTF8, CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadLines(path)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadLines(path, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_NET7 - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadLinesAsync(path, CancellationToken.None)); - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.ReadLinesAsync(path, Encoding.UTF8, CancellationToken.None)); -#endif -#if FEATURE_FILESYSTEM_LINK - yield return (MissingFileTestCase.All, - ExpectedExceptionType.Default, - (file, path) - => file.ResolveLinkTarget(path, false)); -#endif - yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, - (file, path) - => file.SetAttributes(path, FileAttributes.ReadOnly)); - yield return (MissingFileTestCase.All, - ExpectedExceptionType.Default, - (file, path) - => file.SetCreationTime(path, DateTime.Now)); - yield return (MissingFileTestCase.All, - ExpectedExceptionType.Default, - (file, path) - => file.SetCreationTimeUtc(path, DateTime.Now)); - yield return (MissingFileTestCase.All, - ExpectedExceptionType.Default, - (file, path) - => file.SetLastAccessTime(path, DateTime.Now)); - yield return (MissingFileTestCase.All, - ExpectedExceptionType.Default, - (file, path) - => file.SetLastAccessTimeUtc(path, DateTime.Now)); - yield return (MissingFileTestCase.All, - ExpectedExceptionType.Default, - (file, path) - => file.SetLastWriteTime(path, DateTime.Now)); - yield return (MissingFileTestCase.All, - ExpectedExceptionType.Default, - (file, path) - => file.SetLastWriteTimeUtc(path, DateTime.Now)); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllBytes(path, new byte[] - { - 0, 1 - })); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllBytesAsync(path, new byte[] - { - 0, 1 - }, - CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllLines(path, new[] - { - "foo" - })); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllLines(path, new[] - { - "foo" - }, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllLinesAsync(path, new[] - { - "foo" - }, CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllLinesAsync(path, new[] - { - "foo" - }, Encoding.UTF8, - CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllText(path, "foo")); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllText(path, "foo", Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllTextAsync(path, "foo", CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, - (file, path) - => file.WriteAllTextAsync(path, "foo", Encoding.UTF8, - CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionMissingFileTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetFileCallbacks), parameters: (int)MissingFileTestCase.FileMissing)] + public void Operations_WhenFileIsMissing_ShouldThrowFileNotFoundException( + Expression> callback) + { + string path = "missing-file.txt"; + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.File, path); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException( + hResult: -2147024894, + because: $"\n{callback}\n was called with a missing file"); + } + else + { + exception.Should() + .BeFileOrDirectoryNotFoundException( + $"\n{callback}\n was called with a missing file"); + } + } + + [Theory] + [MemberData(nameof(GetFileCallbacks), parameters: (int)MissingFileTestCase.DirectoryMissing)] + public void Operations_WhenDirectoryIsMissing_ShouldThrowDirectoryNotFoundException( + Expression> callback) + { + string path = FileSystem.Path.Combine("missing-directory", "file.txt"); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.File, path); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException( + hResult: -2147024893, + because: $"\n{callback}\n was called with a missing directory"); + } + else + { + exception.Should() + .BeFileOrDirectoryNotFoundException( + $"\n{callback}\n was called with a missing directory"); + } + } + + #region Helpers + + [Flags] + public enum MissingFileTestCase + { + FileMissing = 1, + DirectoryMissing = 2, + All = FileMissing | DirectoryMissing + } + + public enum ExpectedExceptionType + { + Default, + } + + public static IEnumerable GetFileCallbacks(int testCases) + => GetFileCallbackTestParameters() + .Where(item => (item.TestCase & (MissingFileTestCase)testCases) != 0) + .Select(item => new object?[] + { + item.Callback + }); + + private static IEnumerable<(MissingFileTestCase TestCase, ExpectedExceptionType ExceptionType, + Expression> Callback)> + GetFileCallbackTestParameters() + { + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.GetAttributes(path)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.SetAttributes(path, FileAttributes.ReadOnly)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.Copy(path, "destination.txt")); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllLines(path, new[] + { + "foo" + })); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllLines(path, new[] + { + "foo" + })); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllLines(path, new[] + { + "foo" + }, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllLinesAsync(path, new[] + { + "foo" + }, CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllLinesAsync(path, new[] + { + "foo" + }, Encoding.UTF8, + CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllText(path, "foo")); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllText(path, "foo", Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllTextAsync(path, "foo", CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendAllTextAsync(path, "foo", Encoding.UTF8, + CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.AppendText(path)); + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Copy(path, "foo")); + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Copy("foo", path)); + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Copy(path, "foo", false)); + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Copy("foo", path, false)); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.Create(path)); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.Create(path, 1023)); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.Create(path, 1023, FileOptions.None)); +#if FEATURE_FILESYSTEM_LINK + if (Test.RunsOnWindows) + { + yield return (MissingFileTestCase.DirectoryMissing, + ExpectedExceptionType.Default, + (file, path) + => file.CreateSymbolicLink(path, "foo")); + } +#endif + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.CreateText(path)); + + if (Test.RunsOnWindows) + { + #pragma warning disable CA1416 + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.Decrypt(path)); + #pragma warning restore CA1416 + } + + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.Delete(path)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.GetAttributes(path)); + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Move(path, "foo")); + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Move("foo", path)); +#if FEATURE_FILE_MOVETO_OVERWRITE + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Move(path, "foo", false)); + yield return (MissingFileTestCase.FileMissing, ExpectedExceptionType.Default, + (file, path) + => file.Move("foo", path, false)); +#endif + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.Open(path, FileMode.Open)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.Open(path, FileMode.Open, FileAccess.ReadWrite)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None)); +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.Open(path, new FileStreamOptions())); +#endif + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.OpenRead(path)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.OpenText(path)); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.OpenWrite(path)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllBytes(path)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllBytesAsync(path, CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllLines(path)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllLines(path, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllLinesAsync(path, CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllLinesAsync(path, Encoding.UTF8, CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllText(path)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllText(path, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllTextAsync(path, CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadAllTextAsync(path, Encoding.UTF8, CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadLines(path)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadLines(path, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_NET7 + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadLinesAsync(path, CancellationToken.None)); + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.ReadLinesAsync(path, Encoding.UTF8, CancellationToken.None)); +#endif +#if FEATURE_FILESYSTEM_LINK + yield return (MissingFileTestCase.All, + ExpectedExceptionType.Default, + (file, path) + => file.ResolveLinkTarget(path, false)); +#endif + yield return (MissingFileTestCase.All, ExpectedExceptionType.Default, + (file, path) + => file.SetAttributes(path, FileAttributes.ReadOnly)); + yield return (MissingFileTestCase.All, + ExpectedExceptionType.Default, + (file, path) + => file.SetCreationTime(path, DateTime.Now)); + yield return (MissingFileTestCase.All, + ExpectedExceptionType.Default, + (file, path) + => file.SetCreationTimeUtc(path, DateTime.Now)); + yield return (MissingFileTestCase.All, + ExpectedExceptionType.Default, + (file, path) + => file.SetLastAccessTime(path, DateTime.Now)); + yield return (MissingFileTestCase.All, + ExpectedExceptionType.Default, + (file, path) + => file.SetLastAccessTimeUtc(path, DateTime.Now)); + yield return (MissingFileTestCase.All, + ExpectedExceptionType.Default, + (file, path) + => file.SetLastWriteTime(path, DateTime.Now)); + yield return (MissingFileTestCase.All, + ExpectedExceptionType.Default, + (file, path) + => file.SetLastWriteTimeUtc(path, DateTime.Now)); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllBytes(path, new byte[] + { + 0, 1 + })); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllBytesAsync(path, new byte[] + { + 0, 1 + }, + CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllLines(path, new[] + { + "foo" + })); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllLines(path, new[] + { + "foo" + }, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllLinesAsync(path, new[] + { + "foo" + }, CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllLinesAsync(path, new[] + { + "foo" + }, Encoding.UTF8, + CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllText(path, "foo")); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllText(path, "foo", Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllTextAsync(path, "foo", CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (MissingFileTestCase.DirectoryMissing, ExpectedExceptionType.Default, + (file, path) + => file.WriteAllTextAsync(path, "foo", Encoding.UTF8, + CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionTests.cs index 261217eb5..95387735c 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionTests.cs @@ -1,421 +1,420 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Threading; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetFileCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.File); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileCallbacks), parameters: " ")] - public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.File); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetFileCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.File); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileCallbacks), parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.File); - }); - - if (!Test.RunsOnWindows) - { - if (exception is IOException ioException) - { - ioException.HResult.Should().NotBe(-2147024809, - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - else - { - if (Test.IsNetFramework) - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should().BeException( - hResult: -2147024773, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - } - - #region Helpers - - public static IEnumerable GetFileCallbacks(string? path) - => GetFileCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetFileCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllLines(value, new[] - { - "foo" - })); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllLines(value, new[] - { - "foo" - }, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllLinesAsync(value, new[] - { - "foo" - }, CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllLinesAsync(value, new[] - { - "foo" - }, Encoding.UTF8, - CancellationToken.None).GetAwaiter().GetResult()); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllText(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllText(value, "foo", Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllTextAsync(value, "foo", CancellationToken.None).GetAwaiter() - .GetResult()); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.AppendAllTextAsync(value, "foo", Encoding.UTF8, - CancellationToken.None).GetAwaiter().GetResult()); -#endif - yield return (ExceptionTestHelper.TestTypes.NullOrInvalidPath, "path", file - => file.AppendText(value)); - yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "sourceFileName", - file - => file.Copy(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file - => file.Copy("foo", value)); - yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "sourceFileName", - file - => file.Copy(value, "foo", false)); - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file - => file.Copy("foo", value, false)); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file - => file.Copy(value, "foo")); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file - => file.Copy("foo", value)); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file - => file.Copy(value, "foo", false)); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file - => file.Copy("foo", value, false)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Create(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Create(value, 1023)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Create(value, 1023, FileOptions.None)); -#if FEATURE_FILESYSTEM_LINK - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.CreateSymbolicLink(value, "foo")); -#endif - yield return (ExceptionTestHelper.TestTypes.NullOrInvalidPath, "path", file - => file.CreateText(value)); - - if (Test.RunsOnWindows) - { - #pragma warning disable CA1416 - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "path", file - => file.Decrypt(value)); - #pragma warning restore CA1416 - } - - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Delete(value)); - if (Test.RunsOnWindows) - { - #pragma warning disable CA1416 - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "path", file - => file.Encrypt(value)); - #pragma warning restore CA1416 - } - - // `File.Exists` doesn't throw an exception on `null` - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetAttributes(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetCreationTime(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetCreationTimeUtc(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetLastAccessTime(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetLastAccessTimeUtc(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetLastWriteTime(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetLastWriteTimeUtc(value)); -#if FEATURE_FILESYSTEM_UNIXFILEMODE - if (!Test.RunsOnWindows) - { - #pragma warning disable CA1416 - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.GetUnixFileMode(value)); - #pragma warning restore CA1416 - } -#endif - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "sourceFileName", file - => file.Move(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file - => file.Move("foo", value)); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file - => file.Move(value, "foo")); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file - => file.Move("foo", value)); -#if FEATURE_FILE_MOVETO_OVERWRITE - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "sourceFileName", file - => file.Move(value, "foo", false)); - yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file - => file.Move("foo", value, false)); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file - => file.Move(value, "foo", false)); - yield return ( - ExceptionTestHelper.TestTypes.Whitespace | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file - => file.Move("foo", value, false)); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Open(value, FileMode.Open)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Open(value, FileMode.Open, FileAccess.ReadWrite)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Open(value, FileMode.Open, FileAccess.ReadWrite, FileShare.None)); -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.Open(value, new FileStreamOptions())); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.OpenRead(value)); - yield return (ExceptionTestHelper.TestTypes.NullOrInvalidPath, "path", file - => file.OpenText(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.OpenWrite(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllBytes(value)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllBytesAsync(value, CancellationToken.None).GetAwaiter() - .GetResult()); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllLines(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllLines(value, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllLinesAsync(value, CancellationToken.None).GetAwaiter() - .GetResult()); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllLinesAsync(value, Encoding.UTF8, CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllText(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllText(value, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllTextAsync(value, CancellationToken.None).GetAwaiter() - .GetResult()); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadAllTextAsync(value, Encoding.UTF8, CancellationToken.None) - .GetAwaiter().GetResult()); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadLines(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadLines(value, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_NET7 - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadLinesAsync(value, CancellationToken.None)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.ReadLinesAsync(value, Encoding.UTF8, CancellationToken.None)); -#endif - yield return ( - ExceptionTestHelper.TestTypes.AllExceptInvalidPath | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file - => file.Replace(value, "foo", "bar")); - yield return ( - ExceptionTestHelper.TestTypes.All | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destinationFileName", - file - => file.Replace("foo", value, "bar")); - yield return ( - ExceptionTestHelper.TestTypes.AllExceptInvalidPath | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file - => file.Replace(value, "foo", "bar", false)); - yield return ( - ExceptionTestHelper.TestTypes.All | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destinationFileName", - file - => file.Replace("foo", value, "bar", false)); -#if FEATURE_FILESYSTEM_LINK - yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "linkPath", file - => file.ResolveLinkTarget(value, false)); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetAttributes(value, FileAttributes.ReadOnly)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetCreationTime(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetCreationTimeUtc(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetLastAccessTime(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetLastAccessTimeUtc(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetLastWriteTime(value, DateTime.Now)); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetLastWriteTimeUtc(value, DateTime.Now)); -#if FEATURE_FILESYSTEM_UNIXFILEMODE - if (!Test.RunsOnWindows) - { - #pragma warning disable CA1416 - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.SetUnixFileMode(value, UnixFileMode.None)); - #pragma warning restore CA1416 - } -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllBytes(value, new byte[] - { - 0, 1 - })); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllBytesAsync(value, new byte[] - { - 0, 1 - }, - CancellationToken.None).GetAwaiter().GetResult()); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllLines(value, new[] - { - "foo" - })); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllLines(value, new[] - { - "foo" - }, Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllLinesAsync(value, new[] - { - "foo" - }, CancellationToken.None) - .GetAwaiter().GetResult()); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllLinesAsync(value, new[] - { - "foo" - }, Encoding.UTF8, - CancellationToken.None).GetAwaiter().GetResult()); -#endif - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllText(value, "foo")); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllText(value, "foo", Encoding.UTF8)); -#if FEATURE_FILESYSTEM_ASYNC - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllTextAsync(value, "foo", CancellationToken.None).GetAwaiter() - .GetResult()); - yield return (ExceptionTestHelper.TestTypes.All, "path", file - => file.WriteAllTextAsync(value, "foo", Encoding.UTF8, - CancellationToken.None).GetAwaiter().GetResult()); -#endif - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetFileCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.File); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileCallbacks), parameters: " ")] + public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.File); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetFileCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.File); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileCallbacks), parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.File); + }); + + if (!Test.RunsOnWindows) + { + if (exception is IOException ioException) + { + ioException.HResult.Should().NotBe(-2147024809, + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + else + { + if (Test.IsNetFramework) + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should().BeException( + hResult: -2147024773, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + } + + #region Helpers + + public static IEnumerable GetFileCallbacks(string? path) + => GetFileCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetFileCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllLines(value, new[] + { + "foo" + })); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllLines(value, new[] + { + "foo" + }, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllLinesAsync(value, new[] + { + "foo" + }, CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllLinesAsync(value, new[] + { + "foo" + }, Encoding.UTF8, + CancellationToken.None).GetAwaiter().GetResult()); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllText(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllText(value, "foo", Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllTextAsync(value, "foo", CancellationToken.None).GetAwaiter() + .GetResult()); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.AppendAllTextAsync(value, "foo", Encoding.UTF8, + CancellationToken.None).GetAwaiter().GetResult()); +#endif + yield return (ExceptionTestHelper.TestTypes.NullOrInvalidPath, "path", file + => file.AppendText(value)); + yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "sourceFileName", + file + => file.Copy(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file + => file.Copy("foo", value)); + yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "sourceFileName", + file + => file.Copy(value, "foo", false)); + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file + => file.Copy("foo", value, false)); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file + => file.Copy(value, "foo")); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file + => file.Copy("foo", value)); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file + => file.Copy(value, "foo", false)); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file + => file.Copy("foo", value, false)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Create(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Create(value, 1023)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Create(value, 1023, FileOptions.None)); +#if FEATURE_FILESYSTEM_LINK + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.CreateSymbolicLink(value, "foo")); +#endif + yield return (ExceptionTestHelper.TestTypes.NullOrInvalidPath, "path", file + => file.CreateText(value)); + + if (Test.RunsOnWindows) + { + #pragma warning disable CA1416 + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "path", file + => file.Decrypt(value)); + #pragma warning restore CA1416 + } + + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Delete(value)); + if (Test.RunsOnWindows) + { + #pragma warning disable CA1416 + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "path", file + => file.Encrypt(value)); + #pragma warning restore CA1416 + } + + // `File.Exists` doesn't throw an exception on `null` + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetAttributes(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetCreationTime(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetCreationTimeUtc(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetLastAccessTime(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetLastAccessTimeUtc(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetLastWriteTime(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetLastWriteTimeUtc(value)); +#if FEATURE_FILESYSTEM_UNIXFILEMODE + if (!Test.RunsOnWindows) + { + #pragma warning disable CA1416 + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.GetUnixFileMode(value)); + #pragma warning restore CA1416 + } +#endif + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "sourceFileName", file + => file.Move(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file + => file.Move("foo", value)); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file + => file.Move(value, "foo")); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file + => file.Move("foo", value)); +#if FEATURE_FILE_MOVETO_OVERWRITE + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "sourceFileName", file + => file.Move(value, "foo", false)); + yield return (ExceptionTestHelper.TestTypes.NullOrEmpty, "destFileName", file + => file.Move("foo", value, false)); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file + => file.Move(value, "foo", false)); + yield return ( + ExceptionTestHelper.TestTypes.Whitespace | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destFileName", file + => file.Move("foo", value, false)); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Open(value, FileMode.Open)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Open(value, FileMode.Open, FileAccess.ReadWrite)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Open(value, FileMode.Open, FileAccess.ReadWrite, FileShare.None)); +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.Open(value, new FileStreamOptions())); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.OpenRead(value)); + yield return (ExceptionTestHelper.TestTypes.NullOrInvalidPath, "path", file + => file.OpenText(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.OpenWrite(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllBytes(value)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllBytesAsync(value, CancellationToken.None).GetAwaiter() + .GetResult()); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllLines(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllLines(value, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllLinesAsync(value, CancellationToken.None).GetAwaiter() + .GetResult()); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllLinesAsync(value, Encoding.UTF8, CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllText(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllText(value, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllTextAsync(value, CancellationToken.None).GetAwaiter() + .GetResult()); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadAllTextAsync(value, Encoding.UTF8, CancellationToken.None) + .GetAwaiter().GetResult()); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadLines(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadLines(value, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_NET7 + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadLinesAsync(value, CancellationToken.None)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.ReadLinesAsync(value, Encoding.UTF8, CancellationToken.None)); +#endif + yield return ( + ExceptionTestHelper.TestTypes.AllExceptInvalidPath | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file + => file.Replace(value, "foo", "bar")); + yield return ( + ExceptionTestHelper.TestTypes.All | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destinationFileName", + file + => file.Replace("foo", value, "bar")); + yield return ( + ExceptionTestHelper.TestTypes.AllExceptInvalidPath | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "sourceFileName", file + => file.Replace(value, "foo", "bar", false)); + yield return ( + ExceptionTestHelper.TestTypes.All | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, "destinationFileName", + file + => file.Replace("foo", value, "bar", false)); +#if FEATURE_FILESYSTEM_LINK + yield return (ExceptionTestHelper.TestTypes.AllExceptWhitespace, "linkPath", file + => file.ResolveLinkTarget(value, false)); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetAttributes(value, FileAttributes.ReadOnly)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetCreationTime(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetCreationTimeUtc(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetLastAccessTime(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetLastAccessTimeUtc(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetLastWriteTime(value, DateTime.Now)); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetLastWriteTimeUtc(value, DateTime.Now)); +#if FEATURE_FILESYSTEM_UNIXFILEMODE + if (!Test.RunsOnWindows) + { + #pragma warning disable CA1416 + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.SetUnixFileMode(value, UnixFileMode.None)); + #pragma warning restore CA1416 + } +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllBytes(value, new byte[] + { + 0, 1 + })); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllBytesAsync(value, new byte[] + { + 0, 1 + }, + CancellationToken.None).GetAwaiter().GetResult()); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllLines(value, new[] + { + "foo" + })); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllLines(value, new[] + { + "foo" + }, Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllLinesAsync(value, new[] + { + "foo" + }, CancellationToken.None) + .GetAwaiter().GetResult()); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllLinesAsync(value, new[] + { + "foo" + }, Encoding.UTF8, + CancellationToken.None).GetAwaiter().GetResult()); +#endif + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllText(value, "foo")); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllText(value, "foo", Encoding.UTF8)); +#if FEATURE_FILESYSTEM_ASYNC + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllTextAsync(value, "foo", CancellationToken.None).GetAwaiter() + .GetResult()); + yield return (ExceptionTestHelper.TestTypes.All, "path", file + => file.WriteAllTextAsync(value, "foo", Encoding.UTF8, + CancellationToken.None).GetAwaiter().GetResult()); +#endif + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/MoveTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/MoveTests.cs index fa725a24b..641552228 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/MoveTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/MoveTests.cs @@ -1,246 +1,246 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class MoveTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Move_CaseOnlyChange_ShouldMoveFileWithContent( - string name, string contents) - { - string sourceName = name.ToLowerInvariant(); - string destinationName = name.ToUpperInvariant(); - FileSystem.File.WriteAllText(sourceName, contents); - - FileSystem.File.Move(sourceName, destinationName); - - if (Test.RunsOnLinux) - { - // sourceName and destinationName are considered different only on Linux - FileSystem.File.Exists(sourceName).Should().BeFalse(); - } - - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void - Move_DestinationDirectoryDoesNotExist_ShouldThrowDirectoryNotFoundException( - string source) - { - FileSystem.Initialize() - .WithFile(source); - string destination = FileTestHelper.RootDrive("not-existing/path"); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Move(source, destination); - }); - - exception.Should().BeException(hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Move_DestinationExists_ShouldThrowIOException_AndNotMoveFile( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Move(sourceName, destinationName); - }); - - exception.Should().BeException( - hResult: Test.RunsOnWindows ? -2147024713 : 17); - - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - } - -#if FEATURE_FILE_MOVETO_OVERWRITE - [SkippableTheory] - [AutoData] - public void Move_DestinationExists_WithOverwrite_ShouldOverwriteDestination( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - - FileSystem.File.Move(sourceName, destinationName, true); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - } -#endif - - [SkippableTheory] - [AutoData] - public void Move_ReadOnly_ShouldMoveFile( - string sourceName, string destinationName, string contents) - { - FileSystem.File.WriteAllText(sourceName, contents); - FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); - - FileSystem.File.Move(sourceName, destinationName); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.GetAttributes(destinationName) - .Should().HaveFlag(FileAttributes.ReadOnly); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void Move_ShouldMoveFileWithContent( - string sourceName, string destinationName, string contents) - { - FileSystem.File.WriteAllText(sourceName, contents); - - FileSystem.File.Move(sourceName, destinationName); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void Move_ShouldNotAdjustTimes(string source, string destination) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllText(source, "foo"); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - - FileSystem.File.Move(source, destination); - - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(destination); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(destination); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(destination); - - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - [SkippableTheory] - [AutoData] - public void Move_SourceAndDestinationIdentical_ShouldNotThrowException(string path) - { - FileSystem.Initialize() - .WithFile(path); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Move(path, path); - }); - - exception.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void Move_SourceDirectoryMissing_ShouldThrowFileNotFoundException( - string missingDirectory, - string sourceName, - string destinationName) - { - string sourcePath = FileSystem.Path.Combine(missingDirectory, sourceName); - Exception? exception = Record.Exception(() => - { - FileSystem.File.Move(sourcePath, destinationName); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(sourcePath)}'", - hResult: -2147024894); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Move_SourceLocked_ShouldThrowIOException_OnWindows( - string sourceName, - string destinationName) - { - FileSystem.File.WriteAllText(sourceName, null); - using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, - FileAccess.Read, FileShare.Read); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Move(sourceName, destinationName); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException( - hResult: -2147024864); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - else - { - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - } - } - - [SkippableTheory] - [AutoData] - public void Move_SourceMissing_CopyToItself_ShouldThrowFileNotFoundException( - string sourceName) - { - Exception? exception = Record.Exception(() => - { - FileSystem.File.Move(sourceName, sourceName); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(sourceName)}'", - hResult: -2147024894); - } - - [SkippableTheory] - [AutoData] - public void Move_SourceMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName) - { - Exception? exception = Record.Exception(() => - { - FileSystem.File.Move(sourceName, destinationName); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(sourceName)}'", - hResult: -2147024894); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class MoveTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Move_CaseOnlyChange_ShouldMoveFileWithContent( + string name, string contents) + { + string sourceName = name.ToLowerInvariant(); + string destinationName = name.ToUpperInvariant(); + FileSystem.File.WriteAllText(sourceName, contents); + + FileSystem.File.Move(sourceName, destinationName); + + if (Test.RunsOnLinux) + { + // sourceName and destinationName are considered different only on Linux + FileSystem.File.Exists(sourceName).Should().BeFalse(); + } + + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void + Move_DestinationDirectoryDoesNotExist_ShouldThrowDirectoryNotFoundException( + string source) + { + FileSystem.Initialize() + .WithFile(source); + string destination = FileTestHelper.RootDrive("not-existing/path"); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Move(source, destination); + }); + + exception.Should().BeException(hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Move_DestinationExists_ShouldThrowIOException_AndNotMoveFile( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Move(sourceName, destinationName); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147024713 : 17); + + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + } + +#if FEATURE_FILE_MOVETO_OVERWRITE + [SkippableTheory] + [AutoData] + public void Move_DestinationExists_WithOverwrite_ShouldOverwriteDestination( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + + FileSystem.File.Move(sourceName, destinationName, true); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + } +#endif + + [SkippableTheory] + [AutoData] + public void Move_ReadOnly_ShouldMoveFile( + string sourceName, string destinationName, string contents) + { + FileSystem.File.WriteAllText(sourceName, contents); + FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); + + FileSystem.File.Move(sourceName, destinationName); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.GetAttributes(destinationName) + .Should().HaveFlag(FileAttributes.ReadOnly); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void Move_ShouldMoveFileWithContent( + string sourceName, string destinationName, string contents) + { + FileSystem.File.WriteAllText(sourceName, contents); + + FileSystem.File.Move(sourceName, destinationName); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void Move_ShouldNotAdjustTimes(string source, string destination) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllText(source, "foo"); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + + FileSystem.File.Move(source, destination); + + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(destination); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(destination); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(destination); + + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + [SkippableTheory] + [AutoData] + public void Move_SourceAndDestinationIdentical_ShouldNotThrowException(string path) + { + FileSystem.Initialize() + .WithFile(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Move(path, path); + }); + + exception.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void Move_SourceDirectoryMissing_ShouldThrowFileNotFoundException( + string missingDirectory, + string sourceName, + string destinationName) + { + string sourcePath = FileSystem.Path.Combine(missingDirectory, sourceName); + Exception? exception = Record.Exception(() => + { + FileSystem.File.Move(sourcePath, destinationName); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(sourcePath)}'", + hResult: -2147024894); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Move_SourceLocked_ShouldThrowIOException_OnWindows( + string sourceName, + string destinationName) + { + FileSystem.File.WriteAllText(sourceName, null); + using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, + FileAccess.Read, FileShare.Read); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Move(sourceName, destinationName); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException( + hResult: -2147024864); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + else + { + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + } + } + + [SkippableTheory] + [AutoData] + public void Move_SourceMissing_CopyToItself_ShouldThrowFileNotFoundException( + string sourceName) + { + Exception? exception = Record.Exception(() => + { + FileSystem.File.Move(sourceName, sourceName); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(sourceName)}'", + hResult: -2147024894); + } + + [SkippableTheory] + [AutoData] + public void Move_SourceMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName) + { + Exception? exception = Record.Exception(() => + { + FileSystem.File.Move(sourceName, destinationName); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(sourceName)}'", + hResult: -2147024894); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenReadTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenReadTests.cs index 2ba2939f5..ad7246d77 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenReadTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenReadTests.cs @@ -1,141 +1,141 @@ -using System.IO; -using System.Threading.Tasks; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OpenReadTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void OpenRead_MissingFile_ShouldThrowFileNotFoundException(string path) - { - Exception? exception = Record.Exception(() => - { - _ = FileSystem.File.OpenRead(path); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - - [SkippableTheory] - [AutoData] - public void OpenRead_ShouldUseReadAccessAndReadShare(string path) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Read); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be( - Test.RunsOnWindows ? FileShare.Read : FileShare.ReadWrite); - stream.CanRead.Should().BeTrue(); - stream.CanWrite.Should().BeFalse(); - stream.CanSeek.Should().BeTrue(); - stream.CanTimeout.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void OpenRead_SetLength_ShouldThrowNotSupportedException(string path) - { - FileSystem.File.WriteAllText(path, null); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - stream.SetLength(3); - }); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [AutoData] - public void OpenRead_Write_ShouldThrowNotSupportedException(string path, byte[] bytes) - { - FileSystem.File.WriteAllText(path, null); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - stream.Write(bytes, 0, bytes.Length); - }); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [AutoData] - public async Task OpenRead_WriteAsync_ShouldThrowNotSupportedException( - string path, byte[] bytes) - { - // ReSharper disable once MethodHasAsyncOverload - FileSystem.File.WriteAllText(path, null); - - Exception? exception = await Record.ExceptionAsync(async () => - { - // ReSharper disable once UseAwaitUsing - using FileSystemStream stream = FileSystem.File.OpenRead(path); - #pragma warning disable CA1835 - await stream.WriteAsync(bytes, 0, bytes.Length); - #pragma warning restore CA1835 - }); - - exception.Should().BeException(hResult: -2146233067); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public async Task OpenRead_WriteAsyncWithMemory_ShouldThrowNotSupportedException( - string path, byte[] bytes) - { - await FileSystem.File.WriteAllTextAsync(path, null); - - Exception? exception = await Record.ExceptionAsync(async () => - { - await using FileSystemStream stream = FileSystem.File.OpenRead(path); - await stream.WriteAsync(bytes.AsMemory()); - }); - - exception.Should().BeException(hResult: -2146233067); - } -#endif - - [SkippableTheory] - [AutoData] - public void OpenRead_WriteByte_ShouldThrowNotSupportedException(string path) - { - FileSystem.File.WriteAllText(path, null); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - stream.WriteByte(0); - }); - - exception.Should().BeException(hResult: -2146233067); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public void OpenRead_WriteWithSpan_ShouldThrowNotSupportedException(string path, byte[] bytes) - { - FileSystem.File.WriteAllText(path, null); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - stream.Write(bytes.AsSpan()); - }); - - exception.Should().BeException(hResult: -2146233067); - } -#endif -} +using System.IO; +using System.Threading.Tasks; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OpenReadTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void OpenRead_MissingFile_ShouldThrowFileNotFoundException(string path) + { + Exception? exception = Record.Exception(() => + { + _ = FileSystem.File.OpenRead(path); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + + [SkippableTheory] + [AutoData] + public void OpenRead_ShouldUseReadAccessAndReadShare(string path) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Read); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be( + Test.RunsOnWindows ? FileShare.Read : FileShare.ReadWrite); + stream.CanRead.Should().BeTrue(); + stream.CanWrite.Should().BeFalse(); + stream.CanSeek.Should().BeTrue(); + stream.CanTimeout.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void OpenRead_SetLength_ShouldThrowNotSupportedException(string path) + { + FileSystem.File.WriteAllText(path, null); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + stream.SetLength(3); + }); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [AutoData] + public void OpenRead_Write_ShouldThrowNotSupportedException(string path, byte[] bytes) + { + FileSystem.File.WriteAllText(path, null); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + stream.Write(bytes, 0, bytes.Length); + }); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [AutoData] + public async Task OpenRead_WriteAsync_ShouldThrowNotSupportedException( + string path, byte[] bytes) + { + // ReSharper disable once MethodHasAsyncOverload + FileSystem.File.WriteAllText(path, null); + + Exception? exception = await Record.ExceptionAsync(async () => + { + // ReSharper disable once UseAwaitUsing + using FileSystemStream stream = FileSystem.File.OpenRead(path); + #pragma warning disable CA1835 + await stream.WriteAsync(bytes, 0, bytes.Length); + #pragma warning restore CA1835 + }); + + exception.Should().BeException(hResult: -2146233067); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public async Task OpenRead_WriteAsyncWithMemory_ShouldThrowNotSupportedException( + string path, byte[] bytes) + { + await FileSystem.File.WriteAllTextAsync(path, null); + + Exception? exception = await Record.ExceptionAsync(async () => + { + await using FileSystemStream stream = FileSystem.File.OpenRead(path); + await stream.WriteAsync(bytes.AsMemory()); + }); + + exception.Should().BeException(hResult: -2146233067); + } +#endif + + [SkippableTheory] + [AutoData] + public void OpenRead_WriteByte_ShouldThrowNotSupportedException(string path) + { + FileSystem.File.WriteAllText(path, null); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + stream.WriteByte(0); + }); + + exception.Should().BeException(hResult: -2146233067); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public void OpenRead_WriteWithSpan_ShouldThrowNotSupportedException(string path, byte[] bytes) + { + FileSystem.File.WriteAllText(path, null); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + stream.Write(bytes.AsSpan()); + }); + + exception.Should().BeException(hResult: -2146233067); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenTests.cs index 0d2e8c24a..d469f0fff 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenTests.cs @@ -1,141 +1,141 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OpenTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Open_ExistingFileWithCreateNewMode_ShouldThrowIOException( - string path) - { - FileSystem.File.WriteAllText(path, null); - - Exception? exception = Record.Exception(() => - { - _ = FileSystem.File.Open(path, FileMode.CreateNew); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: Test.RunsOnWindows ? -2147024816 : 17); - } - - [SkippableTheory] - [AutoData] - public void Open_MissingFileAndIncorrectMode_ShouldThrowFileNotFoundException( - string path) - { - Exception? exception = Record.Exception(() => - { - _ = FileSystem.File.Open(path, FileMode.Open); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - - [SkippableTheory] - [InlineAutoData(FileMode.Open, FileAccess.Read)] - [InlineAutoData(FileMode.OpenOrCreate, FileAccess.ReadWrite)] - public void Open_ShouldNotAdjustTimes(FileMode mode, FileAccess access, string path) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllText(path, "foo"); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - - FileSystemStream stream = FileSystem.File.Open(path, mode, access); - stream.Dispose(); - - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - [SkippableTheory] - [InlineAutoData(FileMode.Append, FileAccess.Write)] - [InlineAutoData(FileMode.Open, FileAccess.ReadWrite)] - [InlineAutoData(FileMode.Create, FileAccess.ReadWrite)] - public void Open_ShouldUseExpectedAccessDependingOnMode( - FileMode mode, - FileAccess expectedAccess, - string path) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = FileSystem.File.Open(path, mode); - - FileTestHelper.CheckFileAccess(stream).Should().Be(expectedAccess); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } - - [SkippableTheory] - [InlineAutoData(FileAccess.Read, FileShare.Write)] - [InlineAutoData(FileAccess.Write, FileShare.Read)] - public void Open_ShouldUseGivenAccessAndShare(string path, - FileAccess access, - FileShare share) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = - FileSystem.File.Open(path, FileMode.Open, access, share); - - FileTestHelper.CheckFileAccess(stream).Should().Be(access); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); - } - - [SkippableTheory] - [AutoData] - public void Open_ShouldUseNoneShareAsDefault(string path, - FileAccess access) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = FileSystem.File.Open(path, FileMode.Open, access); - - FileTestHelper.CheckFileAccess(stream).Should().Be(access); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } - -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - [SkippableTheory] - [InlineAutoData(FileAccess.Read, FileShare.Write)] - [InlineAutoData(FileAccess.Write, FileShare.Read)] - public void Open_WithFileStreamOptions_ShouldUseGivenAccessAndShare( - string path, - FileAccess access, - FileShare share) - { - FileSystem.File.WriteAllText(path, null); - FileStreamOptions options = new() - { - Mode = FileMode.Open, - Access = access, - Share = share - }; - - using FileSystemStream stream = FileSystem.File.Open(path, options); - - FileTestHelper.CheckFileAccess(stream).Should().Be(access); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); - } -#endif -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OpenTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Open_ExistingFileWithCreateNewMode_ShouldThrowIOException( + string path) + { + FileSystem.File.WriteAllText(path, null); + + Exception? exception = Record.Exception(() => + { + _ = FileSystem.File.Open(path, FileMode.CreateNew); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: Test.RunsOnWindows ? -2147024816 : 17); + } + + [SkippableTheory] + [AutoData] + public void Open_MissingFileAndIncorrectMode_ShouldThrowFileNotFoundException( + string path) + { + Exception? exception = Record.Exception(() => + { + _ = FileSystem.File.Open(path, FileMode.Open); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + + [SkippableTheory] + [InlineAutoData(FileMode.Open, FileAccess.Read)] + [InlineAutoData(FileMode.OpenOrCreate, FileAccess.ReadWrite)] + public void Open_ShouldNotAdjustTimes(FileMode mode, FileAccess access, string path) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllText(path, "foo"); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + + FileSystemStream stream = FileSystem.File.Open(path, mode, access); + stream.Dispose(); + + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + [SkippableTheory] + [InlineAutoData(FileMode.Append, FileAccess.Write)] + [InlineAutoData(FileMode.Open, FileAccess.ReadWrite)] + [InlineAutoData(FileMode.Create, FileAccess.ReadWrite)] + public void Open_ShouldUseExpectedAccessDependingOnMode( + FileMode mode, + FileAccess expectedAccess, + string path) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = FileSystem.File.Open(path, mode); + + FileTestHelper.CheckFileAccess(stream).Should().Be(expectedAccess); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } + + [SkippableTheory] + [InlineAutoData(FileAccess.Read, FileShare.Write)] + [InlineAutoData(FileAccess.Write, FileShare.Read)] + public void Open_ShouldUseGivenAccessAndShare(string path, + FileAccess access, + FileShare share) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = + FileSystem.File.Open(path, FileMode.Open, access, share); + + FileTestHelper.CheckFileAccess(stream).Should().Be(access); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); + } + + [SkippableTheory] + [AutoData] + public void Open_ShouldUseNoneShareAsDefault(string path, + FileAccess access) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = FileSystem.File.Open(path, FileMode.Open, access); + + FileTestHelper.CheckFileAccess(stream).Should().Be(access); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } + +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + [SkippableTheory] + [InlineAutoData(FileAccess.Read, FileShare.Write)] + [InlineAutoData(FileAccess.Write, FileShare.Read)] + public void Open_WithFileStreamOptions_ShouldUseGivenAccessAndShare( + string path, + FileAccess access, + FileShare share) + { + FileSystem.File.WriteAllText(path, null); + FileStreamOptions options = new() + { + Mode = FileMode.Open, + Access = access, + Share = share + }; + + using FileSystemStream stream = FileSystem.File.Open(path, options); + + FileTestHelper.CheckFileAccess(stream).Should().Be(access); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenWriteTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenWriteTests.cs index 9bc204cf3..f3275b56d 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenWriteTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/OpenWriteTests.cs @@ -1,70 +1,70 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OpenWriteTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void OpenWrite_MissingFile_ShouldCreateFile(string path) - { - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void OpenWrite_ShouldOverwriteExistingFile(string path, string previousContent) - { - FileSystem.File.WriteAllText(path, previousContent); - - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - using (StreamWriter streamWriter = new(stream)) - { - streamWriter.Write("new-content"); - } - - stream.Dispose(); - string result = FileSystem.File.ReadAllText(path); - - result.Should().StartWith("new-content"); - result.Length.Should().Be(previousContent.Length); - } - - [SkippableTheory] - [AutoData] - public void OpenWrite_ShouldUseWriteAccessAndNoneShare(string path) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Write); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - stream.CanRead.Should().BeFalse(); - stream.CanWrite.Should().BeTrue(); - stream.CanSeek.Should().BeTrue(); - stream.CanTimeout.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void OpenWrite_StreamShouldNotThrowExceptionWhenReading(string path) - { - FileSystem.File.WriteAllText(path, null); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - _ = stream.ReadByte(); - }); - - exception.Should().BeNull(); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OpenWriteTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void OpenWrite_MissingFile_ShouldCreateFile(string path) + { + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void OpenWrite_ShouldOverwriteExistingFile(string path, string previousContent) + { + FileSystem.File.WriteAllText(path, previousContent); + + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + using (StreamWriter streamWriter = new(stream)) + { + streamWriter.Write("new-content"); + } + + stream.Dispose(); + string result = FileSystem.File.ReadAllText(path); + + result.Should().StartWith("new-content"); + result.Length.Should().Be(previousContent.Length); + } + + [SkippableTheory] + [AutoData] + public void OpenWrite_ShouldUseWriteAccessAndNoneShare(string path) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Write); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + stream.CanRead.Should().BeFalse(); + stream.CanWrite.Should().BeTrue(); + stream.CanSeek.Should().BeTrue(); + stream.CanTimeout.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void OpenWrite_StreamShouldNotThrowExceptionWhenReading(string path) + { + FileSystem.File.WriteAllText(path, null); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + _ = stream.ReadByte(); + }); + + exception.Should().BeNull(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/ReplaceTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/ReplaceTests.cs index b0b9f9350..8992dd750 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/ReplaceTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/ReplaceTests.cs @@ -1,280 +1,280 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ReplaceTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void - Replace_DestinationDirectoryDoesNotExist_ShouldThrowCorrectException( - string source) - { - FileSystem.Initialize() - .WithFile(source); - string destination = FileTestHelper.RootDrive("not-existing/path/foo.txt"); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Replace(source, destination, null); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024893); - } - else - { - exception.Should().BeException(hResult: -2147024894); - } - } - - [SkippableTheory] - [AutoData] - public void Replace_DestinationIsDirectory_ShouldThrowUnauthorizedAccessException( - string sourceName, - string destinationName, - string backupName) - { - FileSystem.File.WriteAllText(sourceName, null); - FileSystem.Directory.CreateDirectory(destinationName); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Replace(sourceName, destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024891); - } - - [SkippableTheory] - [AutoData] - public void Replace_DestinationMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName, - string backupName) - { - FileSystem.File.WriteAllText(sourceName, null); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Replace(sourceName, destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024894); - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Replace_ReadOnly_WithIgnoreMetadataError_ShouldReplaceFile( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); - - FileSystem.File.Replace(sourceName, destinationName, backupName, true); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.GetAttributes(destinationName) - .Should().HaveFlag(FileAttributes.ReadOnly); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - - [SkippableTheory] - [AutoData] - public void - Replace_ReadOnly_WithoutIgnoreMetadataError_ShouldThrowUnauthorizedAccessException_OnWindows( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Replace(sourceName, destinationName, backupName); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024891); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - FileSystem.File.GetAttributes(destinationName) - .Should().NotHaveFlag(FileAttributes.ReadOnly); - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - else - { - exception.Should().BeNull(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - } - - [SkippableTheory] - [AutoData] - public void Replace_ShouldReplaceFile( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - - FileSystem.File.Replace(sourceName, destinationName, backupName); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - - [SkippableTheory] - [AutoData] - public void Replace_SourceIsDirectory_ShouldThrowUnauthorizedAccessException( - string sourceName, - string destinationName, - string backupName) - { - Skip.IfNot(Test.RunsOnWindows, "Tests sometimes throw IOException on Linux"); - - FileSystem.Directory.CreateDirectory(sourceName); - FileSystem.File.WriteAllText(destinationName, null); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Replace(sourceName, destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024891); - } - - [SkippableTheory] - [AutoData] - public void Replace_SourceLocked_ShouldThrowIOException_OnWindows( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, - FileAccess.Read, FileShare.Read); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Replace(sourceName, destinationName, backupName); - }); - - stream.Dispose(); - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024864); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - FileSystem.File.GetAttributes(destinationName) - .Should().NotHaveFlag(FileAttributes.ReadOnly); - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - else - { - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - } - - [SkippableTheory] - [AutoData] - public void Replace_SourceMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName, - string backupName) - { - FileSystem.File.WriteAllText(destinationName, null); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.Replace(sourceName, destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024894); - if (Test.RunsOnWindows) - { - // Behaviour on Linux/MacOS is uncertain - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void Replace_WithExistingBackupFile_ShouldIgnoreBackup( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents, - string backupContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - FileSystem.File.WriteAllText(backupName, backupContents); - - FileSystem.File.Replace(sourceName, destinationName, null); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(backupContents); - } - - [SkippableTheory] - [AutoData] - public void Replace_WithoutBackup_ShouldReplaceFile( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - - FileSystem.File.Replace(sourceName, destinationName, null); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ReplaceTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void + Replace_DestinationDirectoryDoesNotExist_ShouldThrowCorrectException( + string source) + { + FileSystem.Initialize() + .WithFile(source); + string destination = FileTestHelper.RootDrive("not-existing/path/foo.txt"); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Replace(source, destination, null); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024893); + } + else + { + exception.Should().BeException(hResult: -2147024894); + } + } + + [SkippableTheory] + [AutoData] + public void Replace_DestinationIsDirectory_ShouldThrowUnauthorizedAccessException( + string sourceName, + string destinationName, + string backupName) + { + FileSystem.File.WriteAllText(sourceName, null); + FileSystem.Directory.CreateDirectory(destinationName); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Replace(sourceName, destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024891); + } + + [SkippableTheory] + [AutoData] + public void Replace_DestinationMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName, + string backupName) + { + FileSystem.File.WriteAllText(sourceName, null); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Replace(sourceName, destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024894); + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Replace_ReadOnly_WithIgnoreMetadataError_ShouldReplaceFile( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); + + FileSystem.File.Replace(sourceName, destinationName, backupName, true); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.GetAttributes(destinationName) + .Should().HaveFlag(FileAttributes.ReadOnly); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + + [SkippableTheory] + [AutoData] + public void + Replace_ReadOnly_WithoutIgnoreMetadataError_ShouldThrowUnauthorizedAccessException_OnWindows( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + FileSystem.File.SetAttributes(sourceName, FileAttributes.ReadOnly); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Replace(sourceName, destinationName, backupName); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024891); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + FileSystem.File.GetAttributes(destinationName) + .Should().NotHaveFlag(FileAttributes.ReadOnly); + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + else + { + exception.Should().BeNull(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + } + + [SkippableTheory] + [AutoData] + public void Replace_ShouldReplaceFile( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + + FileSystem.File.Replace(sourceName, destinationName, backupName); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + + [SkippableTheory] + [AutoData] + public void Replace_SourceIsDirectory_ShouldThrowUnauthorizedAccessException( + string sourceName, + string destinationName, + string backupName) + { + Skip.IfNot(Test.RunsOnWindows, "Tests sometimes throw IOException on Linux"); + + FileSystem.Directory.CreateDirectory(sourceName); + FileSystem.File.WriteAllText(destinationName, null); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Replace(sourceName, destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024891); + } + + [SkippableTheory] + [AutoData] + public void Replace_SourceLocked_ShouldThrowIOException_OnWindows( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, + FileAccess.Read, FileShare.Read); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Replace(sourceName, destinationName, backupName); + }); + + stream.Dispose(); + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024864); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + FileSystem.File.GetAttributes(destinationName) + .Should().NotHaveFlag(FileAttributes.ReadOnly); + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + else + { + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + } + + [SkippableTheory] + [AutoData] + public void Replace_SourceMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName, + string backupName) + { + FileSystem.File.WriteAllText(destinationName, null); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.Replace(sourceName, destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024894); + if (Test.RunsOnWindows) + { + // Behaviour on Linux/MacOS is uncertain + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void Replace_WithExistingBackupFile_ShouldIgnoreBackup( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents, + string backupContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + FileSystem.File.WriteAllText(backupName, backupContents); + + FileSystem.File.Replace(sourceName, destinationName, null); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(backupContents); + } + + [SkippableTheory] + [AutoData] + public void Replace_WithoutBackup_ShouldReplaceFile( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + + FileSystem.File.Replace(sourceName, destinationName, null); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/ResolveLinkTargetTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/ResolveLinkTargetTests.cs index 9833c379d..fc7a488d4 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/ResolveLinkTargetTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/ResolveLinkTargetTests.cs @@ -1,191 +1,192 @@ -#if FEATURE_FILESYSTEM_LINK -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.File; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ResolveLinkTargetTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - #region Test Setup - - /// - /// The maximum number of symbolic links that are followed.
- /// - ///
- private static int MaxResolveLinks => - Test.RunsOnWindows ? 63 : 40; - - #endregion - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_AbsolutePath_ShouldFollowSymbolicLink( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget, null); - FileSystem.File.CreateSymbolicLink(path, targetFullPath); - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(path, false); - - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FileWithDifferentCase_ShouldReturnPathToMissingFile( - string path, string pathToTarget, string contents) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget, "foo"); - FileSystem.File.CreateSymbolicLink(path, targetFullPath); - FileSystem.File.Delete(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget.ToUpper(), contents); - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(path, false); - - if (!Test.RunsOnLinux) - { - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeTrue(); - FileSystem.File.ReadAllText(target.FullName).Should().Be(contents); - } - else - { - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FinalTarget_ShouldFollowSymbolicLinkToFinalTarget( - string path, string pathToFinalTarget) - { - int maxLinks = MaxResolveLinks; - - FileSystem.File.WriteAllText(pathToFinalTarget, null); - string previousPath = pathToFinalTarget; - for (int i = 0; i < maxLinks; i++) - { - string newPath = $"{path}-{i}"; - FileSystem.File.CreateSymbolicLink(newPath, - System.IO.Path.Combine(BasePath, previousPath)); - previousPath = newPath; - } - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(previousPath, true); - - target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToFinalTarget)); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FinalTargetWithTooManyLevels_ShouldThrowIOException( - string path, string pathToFinalTarget) - { - int maxLinks = MaxResolveLinks + 1; - FileSystem.File.WriteAllText(pathToFinalTarget, null); - string previousPath = pathToFinalTarget; - for (int i = 0; i < maxLinks; i++) - { - string newPath = $"{path}-{i}"; - FileSystem.File.CreateSymbolicLink(newPath, - System.IO.Path.Combine(BasePath, previousPath)); - previousPath = newPath; - } - - Exception? exception = Record.Exception(() => - { - _ = FileSystem.File.ResolveLinkTarget(previousPath, true); - }); - - exception.Should().BeException($"'{previousPath}'", - hResult: Test.RunsOnWindows ? -2147022975 : -2146232800); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_MissingFileInLinkChain_ShouldReturnPathToMissingFile( - string path, string pathToFinalTarget, string pathToMissingFile) - { - FileSystem.File.WriteAllText(pathToFinalTarget, null); - FileSystem.File.CreateSymbolicLink(pathToMissingFile, - System.IO.Path.Combine(BasePath, pathToFinalTarget)); - FileSystem.File.CreateSymbolicLink(path, - System.IO.Path.Combine(BasePath, pathToMissingFile)); - FileSystem.File.Delete(pathToMissingFile); - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(path, true); - - target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToMissingFile)); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_NormalDirectory_ShouldReturnNull( - string path) - { - FileSystem.Directory.CreateDirectory(path); - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(path, false); - - target.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_NormalFile_ShouldReturnNull( - string path) - { - FileSystem.File.WriteAllText(path, null); - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(path, false); - - target.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_RelativePath_ShouldFollowSymbolicLinkUnderWindows( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget, null); - FileSystem.File.CreateSymbolicLink(path, targetFullPath); - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(path, false); - - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_TargetDeletedAfterLinkCreation_ShouldReturnNull( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget, null); - FileSystem.File.CreateSymbolicLink(path, targetFullPath); - FileSystem.File.Delete(pathToTarget); - - IFileSystemInfo? target = - FileSystem.File.ResolveLinkTarget(path, false); - - target!.FullName.Should().Be(targetFullPath); - - target.Exists.Should().BeFalse(); - } -} -#endif +#if FEATURE_FILESYSTEM_LINK +using System.IO; + + +namespace Testably.Abstractions.Tests.FileSystem.File; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ResolveLinkTargetTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + #region Test Setup + + /// + /// The maximum number of symbolic links that are followed.
+ /// + ///
+ private static int MaxResolveLinks => + Test.RunsOnWindows ? 63 : 40; + + #endregion + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_AbsolutePath_ShouldFollowSymbolicLink( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget, null); + FileSystem.File.CreateSymbolicLink(path, targetFullPath); + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(path, false); + + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FileWithDifferentCase_ShouldReturnPathToMissingFile( + string path, string pathToTarget, string contents) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget, "foo"); + FileSystem.File.CreateSymbolicLink(path, targetFullPath); + FileSystem.File.Delete(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget.ToUpper(), contents); + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(path, false); + + if (!Test.RunsOnLinux) + { + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeTrue(); + FileSystem.File.ReadAllText(target.FullName).Should().Be(contents); + } + else + { + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FinalTarget_ShouldFollowSymbolicLinkToFinalTarget( + string path, string pathToFinalTarget) + { + int maxLinks = MaxResolveLinks; + + FileSystem.File.WriteAllText(pathToFinalTarget, null); + string previousPath = pathToFinalTarget; + for (int i = 0; i < maxLinks; i++) + { + string newPath = $"{path}-{i}"; + FileSystem.File.CreateSymbolicLink(newPath, + System.IO.Path.Combine(BasePath, previousPath)); + previousPath = newPath; + } + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(previousPath, true); + + target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToFinalTarget)); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FinalTargetWithTooManyLevels_ShouldThrowIOException( + string path, string pathToFinalTarget) + { + int maxLinks = MaxResolveLinks + 1; + FileSystem.File.WriteAllText(pathToFinalTarget, null); + string previousPath = pathToFinalTarget; + for (int i = 0; i < maxLinks; i++) + { + string newPath = $"{path}-{i}"; + FileSystem.File.CreateSymbolicLink(newPath, + System.IO.Path.Combine(BasePath, previousPath)); + previousPath = newPath; + } + + Exception? exception = Record.Exception(() => + { + _ = FileSystem.File.ResolveLinkTarget(previousPath, true); + }); + + exception.Should().BeException($"'{previousPath}'", + hResult: Test.RunsOnWindows ? -2147022975 : -2146232800); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_MissingFileInLinkChain_ShouldReturnPathToMissingFile( + string path, string pathToFinalTarget, string pathToMissingFile) + { + FileSystem.File.WriteAllText(pathToFinalTarget, null); + FileSystem.File.CreateSymbolicLink(pathToMissingFile, + System.IO.Path.Combine(BasePath, pathToFinalTarget)); + FileSystem.File.CreateSymbolicLink(path, + System.IO.Path.Combine(BasePath, pathToMissingFile)); + FileSystem.File.Delete(pathToMissingFile); + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(path, true); + + target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToMissingFile)); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_NormalDirectory_ShouldReturnNull( + string path) + { + FileSystem.Directory.CreateDirectory(path); + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(path, false); + + target.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_NormalFile_ShouldReturnNull( + string path) + { + FileSystem.File.WriteAllText(path, null); + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(path, false); + + target.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_RelativePath_ShouldFollowSymbolicLinkUnderWindows( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget, null); + FileSystem.File.CreateSymbolicLink(path, targetFullPath); + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(path, false); + + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_TargetDeletedAfterLinkCreation_ShouldReturnNull( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget, null); + FileSystem.File.CreateSymbolicLink(path, targetFullPath); + FileSystem.File.Delete(pathToTarget); + + IFileSystemInfo? target = + FileSystem.File.ResolveLinkTarget(path, false); + + target!.FullName.Should().Be(targetFullPath); + + target.Exists.Should().BeFalse(); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/AppendTextTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/AppendTextTests.cs index 142c3f610..9a2ece140 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/AppendTextTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/AppendTextTests.cs @@ -1,45 +1,45 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class AppendTextTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void AppendText_MissingFile_ShouldCreateFile( - string path, string appendText) - { - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - using (StreamWriter stream = fileInfo.AppendText()) - { - stream.Write(appendText); - } - - string result = FileSystem.File.ReadAllText(path); - - result.Should().Be(appendText); - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void AppendText_ShouldAddTextToExistingFile( - string path, string contents, string appendText) - { - FileSystem.File.WriteAllText(path, contents); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - using (StreamWriter stream = fileInfo.AppendText()) - { - stream.Write(appendText); - } - - string result = FileSystem.File.ReadAllText(path); - - result.Should().Be(contents + appendText); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class AppendTextTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void AppendText_MissingFile_ShouldCreateFile( + string path, string appendText) + { + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + using (StreamWriter stream = fileInfo.AppendText()) + { + stream.Write(appendText); + } + + string result = FileSystem.File.ReadAllText(path); + + result.Should().Be(appendText); + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void AppendText_ShouldAddTextToExistingFile( + string path, string contents, string appendText) + { + FileSystem.File.WriteAllText(path, contents); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + using (StreamWriter stream = fileInfo.AppendText()) + { + stream.Write(appendText); + } + + string result = FileSystem.File.ReadAllText(path); + + result.Should().Be(contents + appendText); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CopyToTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CopyToTests.cs index 549b05d3f..b5ea400ac 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CopyToTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CopyToTests.cs @@ -1,234 +1,234 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class CopyToTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void CopyTo_DestinationExists_ShouldThrowIOException_AndNotCopyFile( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.CopyTo(destinationName); - }); - - exception.Should().BeException( - hResult: Test.RunsOnWindows ? -2147024816 : 17); - sut.Exists.Should().BeTrue(); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - } - -#if FEATURE_FILE_MOVETO_OVERWRITE - [SkippableTheory] - [AutoData] - public void CopyTo_DestinationExists_WithOverwrite_ShouldOverwriteDestination( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - IFileInfo result = sut.CopyTo(destinationName, true); - - sut.Exists.Should().BeTrue(); - sut.FullName.Should().Be(FileSystem.Path.GetFullPath(sourceName)); - result.Exists.Should().BeTrue(); - result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - } -#endif - - [SkippableTheory] - [AutoData] - public void CopyTo_ReadOnly_ShouldCopyFile( - string sourceName, string destinationName, string contents) - { - FileSystem.File.WriteAllText(sourceName, contents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - sut.IsReadOnly = true; - - sut.CopyTo(destinationName); - - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.GetAttributes(destinationName) - .Should().HaveFlag(FileAttributes.ReadOnly); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void CopyTo_ShouldAddArchiveAttribute_OnWindows( - string sourceName, - string destinationName, - string contents, - FileAttributes fileAttributes) - { - FileSystem.File.WriteAllText(sourceName, contents); - FileSystem.File.SetAttributes(sourceName, fileAttributes); - FileAttributes expectedAttributes = FileSystem.File.GetAttributes(sourceName); - if (Test.RunsOnWindows) - { - expectedAttributes |= FileAttributes.Archive; - } - - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - IFileInfo result = sut.CopyTo(destinationName); - - result.Attributes.Should().Be(expectedAttributes); - FileSystem.File.GetAttributes(destinationName) - .Should().Be(expectedAttributes); - } - - [SkippableTheory] - [AutoData] - public void CopyTo_ShouldCopyFileWithContent( - string sourceName, string destinationName, string contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText(sourceName, contents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - TimeSystem.Thread.Sleep(1000); - - IFileInfo result = sut.CopyTo(destinationName); - - sut.FullName.Should().Be(FileSystem.Path.GetFullPath(sourceName)); - sut.Exists.Should().BeTrue(); - result.Exists.Should().BeTrue(); - result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(contents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void CopyTo_ShouldKeepMetadata( - string sourceName, - string destinationName, - string contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText(sourceName, contents); - DateTime sourceCreationTime = FileSystem.File.GetCreationTime(sourceName); - DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTime(sourceName); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - TimeSystem.Thread.Sleep(1000); - - DateTime updatedTime = TimeSystem.DateTime.Now; - sut.CopyTo(destinationName); - - if (Test.RunsOnWindows) - { - FileSystem.File.GetCreationTime(destinationName) - .Should().BeOnOrAfter(updatedTime.ApplySystemClockTolerance()); - } - else - { - FileSystem.File.GetCreationTime(destinationName) - .Should().Be(sourceCreationTime); - } - - FileSystem.File.GetLastAccessTime(destinationName) - .Should().BeOnOrAfter(updatedTime.ApplySystemClockTolerance()); - FileSystem.File.GetLastWriteTime(destinationName) - .Should().Be(sourceLastWriteTime); - } - - [SkippableTheory] - [AutoData] - public void CopyTo_SourceIsDirectory_ShouldThrowUnauthorizedAccessException_AndNotCopyFile( - string sourceName, - string destinationName) - { - FileSystem.Directory.CreateDirectory(sourceName); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.CopyTo(destinationName); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(sourceName)}'", - hResult: -2147024891); - FileSystem.Directory.Exists(sourceName).Should().BeTrue(); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void CopyTo_SourceLocked_ShouldThrowIOException( - string sourceName, - string destinationName) - { - FileSystem.File.WriteAllText(sourceName, null); - using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, - FileAccess.Read, FileShare.None); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.CopyTo(destinationName); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024864); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - else - { - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void CopyTo_SourceMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName) - { - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.CopyTo(destinationName); - }); - - exception.Should().BeException( - hResult: -2147024894, - messageContains: Test.IsNetFramework - ? null - : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class CopyToTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void CopyTo_DestinationExists_ShouldThrowIOException_AndNotCopyFile( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.CopyTo(destinationName); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147024816 : 17); + sut.Exists.Should().BeTrue(); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + } + +#if FEATURE_FILE_MOVETO_OVERWRITE + [SkippableTheory] + [AutoData] + public void CopyTo_DestinationExists_WithOverwrite_ShouldOverwriteDestination( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + IFileInfo result = sut.CopyTo(destinationName, true); + + sut.Exists.Should().BeTrue(); + sut.FullName.Should().Be(FileSystem.Path.GetFullPath(sourceName)); + result.Exists.Should().BeTrue(); + result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + } +#endif + + [SkippableTheory] + [AutoData] + public void CopyTo_ReadOnly_ShouldCopyFile( + string sourceName, string destinationName, string contents) + { + FileSystem.File.WriteAllText(sourceName, contents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + sut.IsReadOnly = true; + + sut.CopyTo(destinationName); + + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.GetAttributes(destinationName) + .Should().HaveFlag(FileAttributes.ReadOnly); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void CopyTo_ShouldAddArchiveAttribute_OnWindows( + string sourceName, + string destinationName, + string contents, + FileAttributes fileAttributes) + { + FileSystem.File.WriteAllText(sourceName, contents); + FileSystem.File.SetAttributes(sourceName, fileAttributes); + FileAttributes expectedAttributes = FileSystem.File.GetAttributes(sourceName); + if (Test.RunsOnWindows) + { + expectedAttributes |= FileAttributes.Archive; + } + + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + IFileInfo result = sut.CopyTo(destinationName); + + result.Attributes.Should().Be(expectedAttributes); + FileSystem.File.GetAttributes(destinationName) + .Should().Be(expectedAttributes); + } + + [SkippableTheory] + [AutoData] + public void CopyTo_ShouldCopyFileWithContent( + string sourceName, string destinationName, string contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText(sourceName, contents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + TimeSystem.Thread.Sleep(1000); + + IFileInfo result = sut.CopyTo(destinationName); + + sut.FullName.Should().Be(FileSystem.Path.GetFullPath(sourceName)); + sut.Exists.Should().BeTrue(); + result.Exists.Should().BeTrue(); + result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(contents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void CopyTo_ShouldKeepMetadata( + string sourceName, + string destinationName, + string contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText(sourceName, contents); + DateTime sourceCreationTime = FileSystem.File.GetCreationTime(sourceName); + DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTime(sourceName); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + TimeSystem.Thread.Sleep(1000); + + DateTime updatedTime = TimeSystem.DateTime.Now; + sut.CopyTo(destinationName); + + if (Test.RunsOnWindows) + { + FileSystem.File.GetCreationTime(destinationName) + .Should().BeOnOrAfter(updatedTime.ApplySystemClockTolerance()); + } + else + { + FileSystem.File.GetCreationTime(destinationName) + .Should().Be(sourceCreationTime); + } + + FileSystem.File.GetLastAccessTime(destinationName) + .Should().BeOnOrAfter(updatedTime.ApplySystemClockTolerance()); + FileSystem.File.GetLastWriteTime(destinationName) + .Should().Be(sourceLastWriteTime); + } + + [SkippableTheory] + [AutoData] + public void CopyTo_SourceIsDirectory_ShouldThrowUnauthorizedAccessException_AndNotCopyFile( + string sourceName, + string destinationName) + { + FileSystem.Directory.CreateDirectory(sourceName); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.CopyTo(destinationName); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(sourceName)}'", + hResult: -2147024891); + FileSystem.Directory.Exists(sourceName).Should().BeTrue(); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void CopyTo_SourceLocked_ShouldThrowIOException( + string sourceName, + string destinationName) + { + FileSystem.File.WriteAllText(sourceName, null); + using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, + FileAccess.Read, FileShare.None); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.CopyTo(destinationName); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024864); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + else + { + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void CopyTo_SourceMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName) + { + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.CopyTo(destinationName); + }); + + exception.Should().BeException( + hResult: -2147024894, + messageContains: Test.IsNetFramework + ? null + : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTests.cs index 1fac81244..21b38fc3d 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTests.cs @@ -1,55 +1,55 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class CreateTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Create_MissingFile_ShouldCreateFile(string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - FileSystem.File.Exists(path).Should().BeFalse(); - - using FileSystemStream stream = sut.Create(); - - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void Create_ShouldRefreshExistsCache_ExceptOnNetFramework(string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - sut.Exists.Should().BeFalse(); - - using FileSystemStream stream = sut.Create(); - - if (Test.IsNetFramework) - { - sut.Exists.Should().BeFalse(); - } - else - { - sut.Exists.Should().BeTrue(); - } - - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void Create_ShouldUseReadWriteAccessAndNoneShare(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Create(); - - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class CreateTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Create_MissingFile_ShouldCreateFile(string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + FileSystem.File.Exists(path).Should().BeFalse(); + + using FileSystemStream stream = sut.Create(); + + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void Create_ShouldRefreshExistsCache_ExceptOnNetFramework(string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + sut.Exists.Should().BeFalse(); + + using FileSystemStream stream = sut.Create(); + + if (Test.IsNetFramework) + { + sut.Exists.Should().BeFalse(); + } + else + { + sut.Exists.Should().BeTrue(); + } + + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void Create_ShouldUseReadWriteAccessAndNoneShare(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Create(); + + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.ReadWrite); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTextTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTextTests.cs index 6c0dae4ec..7968364c9 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTextTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/CreateTextTests.cs @@ -1,62 +1,62 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class CreateTextTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void CreateText_MissingFile_ShouldCreateFile( - string path, string appendText) - { - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - using (StreamWriter stream = fileInfo.CreateText()) - { - stream.Write(appendText); - } - - string result = FileSystem.File.ReadAllText(path); - - result.Should().Be(appendText); - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void CreateText_ShouldNotRefreshExistsCache( - string path, string appendText) - { - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - fileInfo.Exists.Should().BeFalse(); - - using (StreamWriter stream = fileInfo.CreateText()) - { - stream.Write(appendText); - } - - fileInfo.Exists.Should().BeFalse(); - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void CreateText_ShouldReplaceTextInExistingFile( - string path, string contents, string appendText) - { - FileSystem.File.WriteAllText(path, contents); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - using (StreamWriter stream = fileInfo.CreateText()) - { - stream.Write(appendText); - } - - string result = FileSystem.File.ReadAllText(path); - - result.Should().Be(appendText); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class CreateTextTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void CreateText_MissingFile_ShouldCreateFile( + string path, string appendText) + { + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + using (StreamWriter stream = fileInfo.CreateText()) + { + stream.Write(appendText); + } + + string result = FileSystem.File.ReadAllText(path); + + result.Should().Be(appendText); + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void CreateText_ShouldNotRefreshExistsCache( + string path, string appendText) + { + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + fileInfo.Exists.Should().BeFalse(); + + using (StreamWriter stream = fileInfo.CreateText()) + { + stream.Write(appendText); + } + + fileInfo.Exists.Should().BeFalse(); + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void CreateText_ShouldReplaceTextInExistingFile( + string path, string contents, string appendText) + { + FileSystem.File.WriteAllText(path, contents); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + using (StreamWriter stream = fileInfo.CreateText()) + { + stream.Write(appendText); + } + + string result = FileSystem.File.ReadAllText(path); + + result.Should().Be(appendText); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/DeleteTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/DeleteTests.cs index d8de2f6a3..c87c63b55 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/DeleteTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/DeleteTests.cs @@ -1,95 +1,95 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class DeleteTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException( - string missingDirectory, string fileName) - { - string filePath = FileSystem.Path.Combine(missingDirectory, fileName); - - Exception? exception = Record.Exception(() => - { - FileSystem.FileInfo.New(filePath).Delete(); - }); - - exception.Should().BeException(hResult: -2147024893); - } - - [SkippableTheory] - [AutoData] - public void Delete_MissingFile_ShouldDoNothing( - string fileName) - { - Exception? exception = Record.Exception(() => - { - FileSystem.FileInfo.New(fileName).Delete(); - }); - - exception.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void Delete_ShouldRefreshExistsCache_ExceptOnNetFramework(string path) - { - FileSystem.File.WriteAllText(path, "some content"); - IFileInfo sut = FileSystem.FileInfo.New(path); - sut.Exists.Should().BeTrue(); - - sut.Delete(); - - if (Test.IsNetFramework) - { - sut.Exists.Should().BeTrue(); - } - else - { - sut.Exists.Should().BeFalse(); - } - - FileSystem.File.Exists(path).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Delete_WithOpenFile_ShouldThrowIOException_OnWindows(string filename) - { - FileSystem.Initialize(); - FileSystemStream openFile = FileSystem.File.OpenWrite(filename); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - IFileInfo sut = FileSystem.FileInfo.New(filename); - Exception? exception = Record.Exception(() => - { - sut.Delete(); - openFile.Write(new byte[] - { - 0 - }, 0, 1); - openFile.Flush(); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException( - messageContains: $"{filename}'", - hResult: -2147024864); - FileSystem.File.Exists(filename).Should().BeTrue(); - } - else - { - exception.Should().BeNull(); - FileSystem.File.Exists(filename).Should().BeFalse(); - } - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class DeleteTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Delete_MissingDirectory_ShouldThrowDirectoryNotFoundException( + string missingDirectory, string fileName) + { + string filePath = FileSystem.Path.Combine(missingDirectory, fileName); + + Exception? exception = Record.Exception(() => + { + FileSystem.FileInfo.New(filePath).Delete(); + }); + + exception.Should().BeException(hResult: -2147024893); + } + + [SkippableTheory] + [AutoData] + public void Delete_MissingFile_ShouldDoNothing( + string fileName) + { + Exception? exception = Record.Exception(() => + { + FileSystem.FileInfo.New(fileName).Delete(); + }); + + exception.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void Delete_ShouldRefreshExistsCache_ExceptOnNetFramework(string path) + { + FileSystem.File.WriteAllText(path, "some content"); + IFileInfo sut = FileSystem.FileInfo.New(path); + sut.Exists.Should().BeTrue(); + + sut.Delete(); + + if (Test.IsNetFramework) + { + sut.Exists.Should().BeTrue(); + } + else + { + sut.Exists.Should().BeFalse(); + } + + FileSystem.File.Exists(path).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Delete_WithOpenFile_ShouldThrowIOException_OnWindows(string filename) + { + FileSystem.Initialize(); + FileSystemStream openFile = FileSystem.File.OpenWrite(filename); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + IFileInfo sut = FileSystem.FileInfo.New(filename); + Exception? exception = Record.Exception(() => + { + sut.Delete(); + openFile.Write(new byte[] + { + 0 + }, 0, 1); + openFile.Flush(); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException( + messageContains: $"{filename}'", + hResult: -2147024864); + FileSystem.File.Exists(filename).Should().BeTrue(); + } + else + { + exception.Should().BeNull(); + FileSystem.File.Exists(filename).Should().BeFalse(); + } + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/EncryptDecryptTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/EncryptDecryptTests.cs index 1ed9ba917..ff707823b 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/EncryptDecryptTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/EncryptDecryptTests.cs @@ -1,101 +1,101 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class EncryptDecryptTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - [SupportedOSPlatform("windows")] - public void Decrypt_EncryptedData_ShouldReturnOriginalText( - string path, string contents) - { - Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, - "Encryption depends on the underlying device, if it is supported or not."); - - FileSystem.File.WriteAllText(path, contents); - IFileInfo sut = FileSystem.FileInfo.New(path); - - sut.Encrypt(); - sut.Decrypt(); - - string result = FileSystem.File.ReadAllText(path); - result.Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - [SupportedOSPlatform("windows")] - public void Decrypt_UnencryptedData_ShouldReturnOriginalText( - string path, string contents) - { - Skip.IfNot(Test.RunsOnWindows); - - FileSystem.File.WriteAllText(path, contents); - IFileInfo sut = FileSystem.FileInfo.New(path); - - sut.Decrypt(); - - string result = FileSystem.File.ReadAllText(path); - result.Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - [SupportedOSPlatform("windows")] - public void Encrypt_Decrypt_ShouldChangeEncryptedFileAttribute( - string path, string contents) - { - Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, - "Encryption depends on the underlying device, if it is supported or not."); - - FileSystem.File.WriteAllText(path, contents); - IFileInfo sut = FileSystem.FileInfo.New(path); - - sut.Encrypt(); - sut.Attributes.Should().HaveFlag(FileAttributes.Encrypted); - sut.Decrypt(); - sut.Attributes.Should().NotHaveFlag(FileAttributes.Encrypted); - } - - [SkippableTheory] - [AutoData] - [SupportedOSPlatform("windows")] - public void Encrypt_ShouldChangeData( - string path, byte[] bytes) - { - Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, - "Encryption depends on the underlying device, if it is supported or not."); - - FileSystem.File.WriteAllBytes(path, bytes); - IFileInfo sut = FileSystem.FileInfo.New(path); - - sut.Encrypt(); - - byte[] result = FileSystem.File.ReadAllBytes(path); - result.Should().NotBeEquivalentTo(bytes); - } - - [SkippableTheory] - [AutoData] - [SupportedOSPlatform("windows")] - public void Encrypt_Twice_ShouldIgnoreTheSecondTime( - string path, string contents) - { - Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, - "Encryption depends on the underlying device, if it is supported or not."); - - FileSystem.File.WriteAllText(path, contents); - IFileInfo sut = FileSystem.FileInfo.New(path); - - sut.Encrypt(); - sut.Encrypt(); - - sut.Decrypt(); - string result = FileSystem.File.ReadAllText(path); - result.Should().Be(contents); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class EncryptDecryptTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + [SupportedOSPlatform("windows")] + public void Decrypt_EncryptedData_ShouldReturnOriginalText( + string path, string contents) + { + Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, + "Encryption depends on the underlying device, if it is supported or not."); + + FileSystem.File.WriteAllText(path, contents); + IFileInfo sut = FileSystem.FileInfo.New(path); + + sut.Encrypt(); + sut.Decrypt(); + + string result = FileSystem.File.ReadAllText(path); + result.Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + [SupportedOSPlatform("windows")] + public void Decrypt_UnencryptedData_ShouldReturnOriginalText( + string path, string contents) + { + Skip.IfNot(Test.RunsOnWindows); + + FileSystem.File.WriteAllText(path, contents); + IFileInfo sut = FileSystem.FileInfo.New(path); + + sut.Decrypt(); + + string result = FileSystem.File.ReadAllText(path); + result.Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + [SupportedOSPlatform("windows")] + public void Encrypt_Decrypt_ShouldChangeEncryptedFileAttribute( + string path, string contents) + { + Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, + "Encryption depends on the underlying device, if it is supported or not."); + + FileSystem.File.WriteAllText(path, contents); + IFileInfo sut = FileSystem.FileInfo.New(path); + + sut.Encrypt(); + sut.Attributes.Should().HaveFlag(FileAttributes.Encrypted); + sut.Decrypt(); + sut.Attributes.Should().NotHaveFlag(FileAttributes.Encrypted); + } + + [SkippableTheory] + [AutoData] + [SupportedOSPlatform("windows")] + public void Encrypt_ShouldChangeData( + string path, byte[] bytes) + { + Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, + "Encryption depends on the underlying device, if it is supported or not."); + + FileSystem.File.WriteAllBytes(path, bytes); + IFileInfo sut = FileSystem.FileInfo.New(path); + + sut.Encrypt(); + + byte[] result = FileSystem.File.ReadAllBytes(path); + result.Should().NotBeEquivalentTo(bytes); + } + + [SkippableTheory] + [AutoData] + [SupportedOSPlatform("windows")] + public void Encrypt_Twice_ShouldIgnoreTheSecondTime( + string path, string contents) + { + Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, + "Encryption depends on the underlying device, if it is supported or not."); + + FileSystem.File.WriteAllText(path, contents); + IFileInfo sut = FileSystem.FileInfo.New(path); + + sut.Encrypt(); + sut.Encrypt(); + + sut.Decrypt(); + string result = FileSystem.File.ReadAllText(path); + result.Should().Be(contents); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ExceptionTests.cs index 0896cd582..91b11e114 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ExceptionTests.cs @@ -1,127 +1,127 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetFileInfoCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetFileInfoCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileInfoCallbacks), parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); - }); - - if (!Test.RunsOnWindows) - { - if (exception is IOException ioException) - { - ioException.HResult.Should().NotBe(-2147024809, - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - else - { - if (Test.IsNetFramework) - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should().BeException( - hResult: -2147024773, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - } - - #region Helpers - - public static IEnumerable GetFileInfoCallbacks(string? path) - => GetFileInfoCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetFileInfoCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", - fileInfo - => fileInfo.CopyTo(value)); - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", - fileInfo - => fileInfo.CopyTo(value, false)); - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", - fileInfo - => fileInfo.MoveTo(value)); -#if FEATURE_FILE_MOVETO_OVERWRITE - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", - fileInfo - => fileInfo.MoveTo(value, false)); -#endif - yield return (ExceptionTestHelper.TestTypes.All | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, - "destinationFileName", fileInfo - => fileInfo.Replace(value, "bar")); - yield return (ExceptionTestHelper.TestTypes.All | - ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, - "destinationFileName", fileInfo - => fileInfo.Replace(value, "bar", false)); - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetFileInfoCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetFileInfoCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileInfoCallbacks), parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); + }); + + if (!Test.RunsOnWindows) + { + if (exception is IOException ioException) + { + ioException.HResult.Should().NotBe(-2147024809, + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + else + { + if (Test.IsNetFramework) + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should().BeException( + hResult: -2147024773, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + } + + #region Helpers + + public static IEnumerable GetFileInfoCallbacks(string? path) + => GetFileInfoCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetFileInfoCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", + fileInfo + => fileInfo.CopyTo(value)); + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", + fileInfo + => fileInfo.CopyTo(value, false)); + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", + fileInfo + => fileInfo.MoveTo(value)); +#if FEATURE_FILE_MOVETO_OVERWRITE + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "destFileName", + fileInfo + => fileInfo.MoveTo(value, false)); +#endif + yield return (ExceptionTestHelper.TestTypes.All | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, + "destinationFileName", fileInfo + => fileInfo.Replace(value, "bar")); + yield return (ExceptionTestHelper.TestTypes.All | + ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, + "destinationFileName", fileInfo + => fileInfo.Replace(value, "bar", false)); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ExistsTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ExistsTests.cs index 1f5407a3b..90bd0d08c 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ExistsTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ExistsTests.cs @@ -1,5 +1,3 @@ -using Testably.Abstractions.FileSystem; - namespace Testably.Abstractions.Tests.FileSystem.FileInfo; // ReSharper disable once PartialTypeWithSinglePart diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/LengthTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/LengthTests.cs index f2347e2d0..7a6fdbc66 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/LengthTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/LengthTests.cs @@ -1,143 +1,143 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class LengthTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Length_MissingFile_ShouldThrowFileNotFoundException(string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Length; - }); - - exception.Should().BeException( - hResult: -2147024894, - messageContains: Test.IsNetFramework - ? $"'{path}'" - : $"'{FileSystem.Path.GetFullPath(path)}'"); - } - - [SkippableTheory] - [AutoData] - public void Length_MissingDirectory_ShouldThrowFileNotFoundException( - string missingDirectory, string fileName) - { - string path = FileSystem.Path.Combine(missingDirectory, fileName); - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Length; - }); - - exception.Should().BeException( - hResult: -2147024894, - messageContains: Test.IsNetFramework - ? $"'{path}'" - : $"'{FileSystem.Path.GetFullPath(path)}'"); - } - - [SkippableTheory] - [AutoData] - public void Length_PathIsDirectory_ShouldThrowFileNotFoundException(string path) - { - FileSystem.Directory.CreateDirectory(path); - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Length; - }); - - exception.Should().BeException( - hResult: -2147024894, - messageContains: Test.IsNetFramework - ? $"'{path}'" - : $"'{FileSystem.Path.GetFullPath(path)}'"); - } - - [SkippableTheory] - [AutoData] - public void Length_WhenFileExists_ShouldBeSetCorrectly(string path, byte[] bytes) - { - FileSystem.File.WriteAllBytes(path, bytes); - IFileInfo sut = FileSystem.FileInfo.New(path); - - long result = sut.Length; - - result.Should().Be(bytes.Length); - } - - [SkippableTheory] - [AutoData] - public void Length_WhenFileIsCreated_ShouldBeSetCorrectly(string path, byte[] bytes) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - FileSystem.File.WriteAllBytes(path, bytes); - - long result = sut.Length; - - result.Should().Be(bytes.Length); - } - - [SkippableTheory] - [AutoData] - public void Length_WhenFileIsCreatedAfterAccessed_ShouldBeSetCorrectly( - string path, byte[] bytes) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.OpenRead(); - }); - - FileSystem.File.WriteAllBytes(path, bytes); - - long result = sut.Length; - - exception.Should().NotBeNull(); - result.Should().Be(bytes.Length); - } - - [SkippableTheory] - [AutoData] - public void - Length_WhenFileIsCreatedAfterLengthAccessed_ShouldThrowFileNotFoundExceptionAgain( - string path, byte[] bytes) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Length; - }); - - FileSystem.File.WriteAllBytes(path, bytes); - - Exception? exception2 = Record.Exception(() => - { - _ = sut.Length; - }); - - exception.Should().BeException( - messageContains: Test.IsNetFramework - ? $"'{path}'" - : $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - exception2.Should().BeException( - messageContains: Test.IsNetFramework - ? $"'{path}'" - : $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class LengthTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Length_MissingFile_ShouldThrowFileNotFoundException(string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Length; + }); + + exception.Should().BeException( + hResult: -2147024894, + messageContains: Test.IsNetFramework + ? $"'{path}'" + : $"'{FileSystem.Path.GetFullPath(path)}'"); + } + + [SkippableTheory] + [AutoData] + public void Length_MissingDirectory_ShouldThrowFileNotFoundException( + string missingDirectory, string fileName) + { + string path = FileSystem.Path.Combine(missingDirectory, fileName); + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Length; + }); + + exception.Should().BeException( + hResult: -2147024894, + messageContains: Test.IsNetFramework + ? $"'{path}'" + : $"'{FileSystem.Path.GetFullPath(path)}'"); + } + + [SkippableTheory] + [AutoData] + public void Length_PathIsDirectory_ShouldThrowFileNotFoundException(string path) + { + FileSystem.Directory.CreateDirectory(path); + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Length; + }); + + exception.Should().BeException( + hResult: -2147024894, + messageContains: Test.IsNetFramework + ? $"'{path}'" + : $"'{FileSystem.Path.GetFullPath(path)}'"); + } + + [SkippableTheory] + [AutoData] + public void Length_WhenFileExists_ShouldBeSetCorrectly(string path, byte[] bytes) + { + FileSystem.File.WriteAllBytes(path, bytes); + IFileInfo sut = FileSystem.FileInfo.New(path); + + long result = sut.Length; + + result.Should().Be(bytes.Length); + } + + [SkippableTheory] + [AutoData] + public void Length_WhenFileIsCreated_ShouldBeSetCorrectly(string path, byte[] bytes) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + FileSystem.File.WriteAllBytes(path, bytes); + + long result = sut.Length; + + result.Should().Be(bytes.Length); + } + + [SkippableTheory] + [AutoData] + public void Length_WhenFileIsCreatedAfterAccessed_ShouldBeSetCorrectly( + string path, byte[] bytes) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.OpenRead(); + }); + + FileSystem.File.WriteAllBytes(path, bytes); + + long result = sut.Length; + + exception.Should().NotBeNull(); + result.Should().Be(bytes.Length); + } + + [SkippableTheory] + [AutoData] + public void + Length_WhenFileIsCreatedAfterLengthAccessed_ShouldThrowFileNotFoundExceptionAgain( + string path, byte[] bytes) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Length; + }); + + FileSystem.File.WriteAllBytes(path, bytes); + + Exception? exception2 = Record.Exception(() => + { + _ = sut.Length; + }); + + exception.Should().BeException( + messageContains: Test.IsNetFramework + ? $"'{path}'" + : $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + exception2.Should().BeException( + messageContains: Test.IsNetFramework + ? $"'{path}'" + : $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/MoveToTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/MoveToTests.cs index c19354241..38c2e0920 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/MoveToTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/MoveToTests.cs @@ -1,286 +1,286 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class MoveToTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void MoveTo_DestinationExists_ShouldThrowIOException_AndNotMoveFile( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(destinationName); - }); - - exception.Should().BeException( - hResult: Test.RunsOnWindows ? -2147024713 : 17); - - sut.Exists.Should().BeTrue(); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - } - -#if FEATURE_FILE_MOVETO_OVERWRITE - [SkippableTheory] - [AutoData] - public void MoveTo_DestinationExists_WithOverwrite_ShouldOverwriteDestination( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - sut.MoveTo(destinationName, true); - - sut.Exists.Should().BeTrue(); - sut.ToString().Should().Be(destinationName); - sut.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - } -#endif - - [SkippableTheory] - [AutoData] - public void MoveTo_Itself_ShouldDoNothing( - string sourceName, - string contents) - { - FileSystem.File.WriteAllText(sourceName, contents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(sourceName); - }); - - exception.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_Itself_SourceMissing_ShouldThrowFileNotFoundException( - string sourceName) - { - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(sourceName); - }); - - exception.Should().BeException( - hResult: -2147024894, - messageContains: Test.IsNetFramework - ? null - : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void - MoveTo_MissingDestinationDirectory_ShouldThrowDirectoryNotFoundException_AndNotMoveFile( - string sourceName, - string missingDirectory, - string destinationName, - string sourceContents) - { - string destinationPath = - FileSystem.Path.Combine(missingDirectory, destinationName); - FileSystem.File.WriteAllText(sourceName, sourceContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(destinationPath); - }); - - exception.Should().BeException(hResult: -2147024893); - - sut.Exists.Should().BeTrue(); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_ReadOnly_ShouldMoveFile( - string sourceName, string destinationName, string contents) - { - FileSystem.File.WriteAllText(sourceName, contents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - sut.IsReadOnly = true; - - sut.MoveTo(destinationName); - - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.GetAttributes(destinationName) - .Should().HaveFlag(FileAttributes.ReadOnly); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_ShouldAddArchiveAttribute_OnWindows( - string sourceName, - string destinationName, - string contents, - FileAttributes fileAttributes) - { - FileSystem.File.WriteAllText(sourceName, contents); - FileSystem.File.SetAttributes(sourceName, fileAttributes); - FileAttributes expectedAttributes = FileSystem.File.GetAttributes(sourceName); - if (Test.RunsOnWindows) - { - expectedAttributes |= FileAttributes.Archive; - } - - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - sut.MoveTo(destinationName); - - FileSystem.File.GetAttributes(destinationName) - .Should().Be(expectedAttributes); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_ShouldKeepMetadata( - string sourceName, - string destinationName, - string contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText(sourceName, contents); - DateTime sourceCreationTime = FileSystem.File.GetCreationTime(sourceName); - DateTime sourceLastAccessTime = FileSystem.File.GetLastAccessTime(sourceName); - DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTime(sourceName); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - TimeSystem.Thread.Sleep(1000); - - sut.MoveTo(destinationName); - - FileSystem.File.GetCreationTime(destinationName) - .Should().Be(sourceCreationTime); - FileSystem.File.GetLastAccessTime(destinationName) - .Should().Be(sourceLastAccessTime); - FileSystem.File.GetLastWriteTime(destinationName) - .Should().Be(sourceLastWriteTime); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_ShouldMoveFileWithContent( - string sourceName, string destinationName, string contents) - { - FileSystem.File.WriteAllText(sourceName, contents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - sut.MoveTo(destinationName); - - sut.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - sut.Exists.Should().BeTrue(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_ShouldNotAdjustTimes(string source, string destination) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText(source, "foo"); - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - IFileInfo sut = FileSystem.FileInfo.New(source); - DateTime expectedCreationTime = sut.CreationTime; - DateTime expectedLastAccessTime = sut.LastAccessTime; - DateTime expectedLastWriteTime = sut.LastWriteTime; - - sut.MoveTo(destination); - - DateTime creationTime = FileSystem.File.GetCreationTime(destination); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTime(destination); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTime(destination); - - sut.CreationTime.Should().Be(expectedCreationTime); - sut.LastAccessTime.Should().Be(expectedLastAccessTime); - sut.LastWriteTime.Should().Be(expectedLastWriteTime); - creationTime.Should().Be(expectedCreationTime); - lastAccessTime.Should().Be(expectedLastAccessTime); - lastWriteTime.Should().Be(expectedLastWriteTime); - } - - [SkippableTheory] - [AutoData] - public void MoveTo_SourceLocked_ShouldThrowIOException( - string sourceName, - string destinationName) - { - FileSystem.File.WriteAllText(sourceName, null); - using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, - FileAccess.Read, FileShare.Read); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(destinationName); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024864); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } - else - { - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - } - } - - [SkippableTheory] - [AutoData] - public void MoveTo_SourceMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName) - { - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.MoveTo(destinationName); - }); - - exception.Should().BeException( - messageContains: Test.IsNetFramework - ? null - : $"'{FileSystem.Path.GetFullPath(sourceName)}'", - hResult: -2147024894); - FileSystem.File.Exists(destinationName).Should().BeFalse(); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class MoveToTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void MoveTo_DestinationExists_ShouldThrowIOException_AndNotMoveFile( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(destinationName); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147024713 : 17); + + sut.Exists.Should().BeTrue(); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + } + +#if FEATURE_FILE_MOVETO_OVERWRITE + [SkippableTheory] + [AutoData] + public void MoveTo_DestinationExists_WithOverwrite_ShouldOverwriteDestination( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + sut.MoveTo(destinationName, true); + + sut.Exists.Should().BeTrue(); + sut.ToString().Should().Be(destinationName); + sut.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + } +#endif + + [SkippableTheory] + [AutoData] + public void MoveTo_Itself_ShouldDoNothing( + string sourceName, + string contents) + { + FileSystem.File.WriteAllText(sourceName, contents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(sourceName); + }); + + exception.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_Itself_SourceMissing_ShouldThrowFileNotFoundException( + string sourceName) + { + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(sourceName); + }); + + exception.Should().BeException( + hResult: -2147024894, + messageContains: Test.IsNetFramework + ? null + : $"'{FileSystem.Path.GetFullPath(sourceName)}'"); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void + MoveTo_MissingDestinationDirectory_ShouldThrowDirectoryNotFoundException_AndNotMoveFile( + string sourceName, + string missingDirectory, + string destinationName, + string sourceContents) + { + string destinationPath = + FileSystem.Path.Combine(missingDirectory, destinationName); + FileSystem.File.WriteAllText(sourceName, sourceContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(destinationPath); + }); + + exception.Should().BeException(hResult: -2147024893); + + sut.Exists.Should().BeTrue(); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_ReadOnly_ShouldMoveFile( + string sourceName, string destinationName, string contents) + { + FileSystem.File.WriteAllText(sourceName, contents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + sut.IsReadOnly = true; + + sut.MoveTo(destinationName); + + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.GetAttributes(destinationName) + .Should().HaveFlag(FileAttributes.ReadOnly); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_ShouldAddArchiveAttribute_OnWindows( + string sourceName, + string destinationName, + string contents, + FileAttributes fileAttributes) + { + FileSystem.File.WriteAllText(sourceName, contents); + FileSystem.File.SetAttributes(sourceName, fileAttributes); + FileAttributes expectedAttributes = FileSystem.File.GetAttributes(sourceName); + if (Test.RunsOnWindows) + { + expectedAttributes |= FileAttributes.Archive; + } + + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + sut.MoveTo(destinationName); + + FileSystem.File.GetAttributes(destinationName) + .Should().Be(expectedAttributes); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_ShouldKeepMetadata( + string sourceName, + string destinationName, + string contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText(sourceName, contents); + DateTime sourceCreationTime = FileSystem.File.GetCreationTime(sourceName); + DateTime sourceLastAccessTime = FileSystem.File.GetLastAccessTime(sourceName); + DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTime(sourceName); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + TimeSystem.Thread.Sleep(1000); + + sut.MoveTo(destinationName); + + FileSystem.File.GetCreationTime(destinationName) + .Should().Be(sourceCreationTime); + FileSystem.File.GetLastAccessTime(destinationName) + .Should().Be(sourceLastAccessTime); + FileSystem.File.GetLastWriteTime(destinationName) + .Should().Be(sourceLastWriteTime); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_ShouldMoveFileWithContent( + string sourceName, string destinationName, string contents) + { + FileSystem.File.WriteAllText(sourceName, contents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + sut.MoveTo(destinationName); + + sut.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + sut.Exists.Should().BeTrue(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_ShouldNotAdjustTimes(string source, string destination) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText(source, "foo"); + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + IFileInfo sut = FileSystem.FileInfo.New(source); + DateTime expectedCreationTime = sut.CreationTime; + DateTime expectedLastAccessTime = sut.LastAccessTime; + DateTime expectedLastWriteTime = sut.LastWriteTime; + + sut.MoveTo(destination); + + DateTime creationTime = FileSystem.File.GetCreationTime(destination); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTime(destination); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTime(destination); + + sut.CreationTime.Should().Be(expectedCreationTime); + sut.LastAccessTime.Should().Be(expectedLastAccessTime); + sut.LastWriteTime.Should().Be(expectedLastWriteTime); + creationTime.Should().Be(expectedCreationTime); + lastAccessTime.Should().Be(expectedLastAccessTime); + lastWriteTime.Should().Be(expectedLastWriteTime); + } + + [SkippableTheory] + [AutoData] + public void MoveTo_SourceLocked_ShouldThrowIOException( + string sourceName, + string destinationName) + { + FileSystem.File.WriteAllText(sourceName, null); + using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, + FileAccess.Read, FileShare.Read); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(destinationName); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024864); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } + else + { + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + } + } + + [SkippableTheory] + [AutoData] + public void MoveTo_SourceMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName) + { + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.MoveTo(destinationName); + }); + + exception.Should().BeException( + messageContains: Test.IsNetFramework + ? null + : $"'{FileSystem.Path.GetFullPath(sourceName)}'", + hResult: -2147024894); + FileSystem.File.Exists(destinationName).Should().BeFalse(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenReadTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenReadTests.cs index 618580209..a2b17e86a 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenReadTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenReadTests.cs @@ -1,39 +1,39 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OpenReadTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void OpenRead_MissingFile_ShouldThrowFileNotFoundException(string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.OpenRead(); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - - [SkippableTheory] - [AutoData] - public void OpenRead_ShouldUseReadAccessAndReadShare(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.OpenRead(); - - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Read); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be( - Test.RunsOnWindows ? FileShare.Read : FileShare.ReadWrite); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OpenReadTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void OpenRead_MissingFile_ShouldThrowFileNotFoundException(string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.OpenRead(); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + + [SkippableTheory] + [AutoData] + public void OpenRead_ShouldUseReadAccessAndReadShare(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.OpenRead(); + + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Read); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be( + Test.RunsOnWindows ? FileShare.Read : FileShare.ReadWrite); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTests.cs index a910bf570..9d52a8811 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTests.cs @@ -1,253 +1,253 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OpenTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ -#if NETFRAMEWORK - [SkippableTheory] - [AutoData] - public void Open_AppendMode_ShouldThrowArgumentException(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Open(FileMode.Append); - }); - - exception.Should().BeException(hResult: -2147024809); - } -#else - [SkippableTheory] - [AutoData] - public void Open_AppendMode_ShouldOpenExistingFile( - string path, byte[] bytes) - { - FileSystem.File.WriteAllBytes(path, bytes); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(FileMode.Append); - - stream.Position.Should().Be(bytes.Length); - stream.Length.Should().Be(bytes.Length); - } - - [SkippableTheory] - [AutoData] - public void Open_AppendMode_SeekShouldThrowIOException_WhenReadingBackward( - string path, byte[] bytes) - { - FileSystem.File.WriteAllBytes(path, bytes); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(FileMode.Append); - - stream.CanRead.Should().BeFalse(); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.Seek(0, SeekOrigin.Begin); - }); - - exception.Should().BeException(hResult: -2146232800); - } - - [SkippableTheory] - [AutoData] - public void Open_AppendMode_SeekShouldWorkWhenKeepingInNewArea( - string path, byte[] bytes) - { - FileSystem.File.WriteAllBytes(path, bytes); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(FileMode.Append); - - stream.WriteByte(0); - stream.WriteByte(1); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.Seek(bytes.Length, SeekOrigin.Begin); - }); - - exception.Should().BeNull(); - stream.Position.Should().Be(bytes.Length); - } - - [SkippableTheory] - [AutoData] - public void Open_AppendMode_ReadShouldThrowNotSupportedException( - string path, byte[] bytes) - { - FileSystem.File.WriteAllBytes(path, bytes); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(FileMode.Append); - - stream.WriteByte(0); - stream.WriteByte(1); - - stream.Seek(bytes.Length, SeekOrigin.Begin); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.ReadByte(); - }); - - exception.Should().BeException(hResult: -2146233067); - } -#endif - - [SkippableTheory] - [AutoData] - public void Open_ExistingFileWithCreateNewMode_ShouldThrowIOException( - string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Open(FileMode.CreateNew); - }); - - exception.Should().BeException($"'{FileSystem.Path.GetFullPath(path)}'", - hResult: Test.RunsOnWindows ? -2147024816 : 17); - } - - [SkippableTheory] - [InlineAutoData(FileMode.Open)] - [InlineAutoData(FileMode.Truncate)] - public void Open_MissingFileAndIncorrectMode_ShouldThrowFileNotFoundException( - FileMode mode, string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Open(mode); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - - [SkippableTheory] -#if !NETFRAMEWORK - [InlineAutoData(FileMode.Append)] -#endif - [InlineAutoData(FileMode.Create)] - [InlineAutoData(FileMode.CreateNew)] - [InlineAutoData(FileMode.OpenOrCreate)] - public void Open_MissingFileAndCorrectMode_ShouldCreateFile( - FileMode mode, string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - _ = sut.Open(mode); - }); - - exception.Should().BeNull(); - sut.Exists.Should().BeTrue(); - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void Open_ShouldOpenWithReadAndWriteAccess( - string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(FileMode.OpenOrCreate); - - stream.CanRead.Should().BeTrue(); - stream.CanWrite.Should().BeTrue(); - } - - [SkippableTheory] -#if !NETFRAMEWORK - [InlineAutoData(FileMode.Append, FileAccess.Write)] -#endif - [InlineAutoData(FileMode.Open, FileAccess.ReadWrite)] - [InlineAutoData(FileMode.Create, FileAccess.ReadWrite)] - public void Open_ShouldUseExpectedAccessDependingOnMode( - FileMode mode, - FileAccess expectedAccess, - string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(mode); - - FileTestHelper.CheckFileAccess(stream).Should().Be(expectedAccess); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } - - [SkippableTheory] - [InlineAutoData(FileAccess.Read, FileShare.Write)] - [InlineAutoData(FileAccess.Write, FileShare.Read)] - public void Open_ShouldUseGivenAccessAndShare(string path, - FileAccess access, - FileShare share) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(FileMode.Open, access, share); - - FileTestHelper.CheckFileAccess(stream).Should().Be(access); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); - } - - [SkippableTheory] - [AutoData] - public void Open_ShouldUseNoneShareAsDefault(string path, - FileAccess access) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.Open(FileMode.Open, access); - - FileTestHelper.CheckFileAccess(stream).Should().Be(access); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } - -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - [SkippableTheory] - [InlineAutoData(FileAccess.Read, FileShare.Write)] - [InlineAutoData(FileAccess.Write, FileShare.Read)] - public void Open_WithFileStreamOptions_ShouldUseGivenAccessAndShare( - string path, - FileAccess access, - FileShare share) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - FileStreamOptions options = new() - { - Mode = FileMode.Open, - Access = access, - Share = share - }; - - using FileSystemStream stream = sut.Open(options); - - FileTestHelper.CheckFileAccess(stream).Should().Be(access); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); - } -#endif -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OpenTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ +#if NETFRAMEWORK + [SkippableTheory] + [AutoData] + public void Open_AppendMode_ShouldThrowArgumentException(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Open(FileMode.Append); + }); + + exception.Should().BeException(hResult: -2147024809); + } +#else + [SkippableTheory] + [AutoData] + public void Open_AppendMode_ShouldOpenExistingFile( + string path, byte[] bytes) + { + FileSystem.File.WriteAllBytes(path, bytes); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(FileMode.Append); + + stream.Position.Should().Be(bytes.Length); + stream.Length.Should().Be(bytes.Length); + } + + [SkippableTheory] + [AutoData] + public void Open_AppendMode_SeekShouldThrowIOException_WhenReadingBackward( + string path, byte[] bytes) + { + FileSystem.File.WriteAllBytes(path, bytes); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(FileMode.Append); + + stream.CanRead.Should().BeFalse(); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.Seek(0, SeekOrigin.Begin); + }); + + exception.Should().BeException(hResult: -2146232800); + } + + [SkippableTheory] + [AutoData] + public void Open_AppendMode_SeekShouldWorkWhenKeepingInNewArea( + string path, byte[] bytes) + { + FileSystem.File.WriteAllBytes(path, bytes); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(FileMode.Append); + + stream.WriteByte(0); + stream.WriteByte(1); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.Seek(bytes.Length, SeekOrigin.Begin); + }); + + exception.Should().BeNull(); + stream.Position.Should().Be(bytes.Length); + } + + [SkippableTheory] + [AutoData] + public void Open_AppendMode_ReadShouldThrowNotSupportedException( + string path, byte[] bytes) + { + FileSystem.File.WriteAllBytes(path, bytes); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(FileMode.Append); + + stream.WriteByte(0); + stream.WriteByte(1); + + stream.Seek(bytes.Length, SeekOrigin.Begin); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.ReadByte(); + }); + + exception.Should().BeException(hResult: -2146233067); + } +#endif + + [SkippableTheory] + [AutoData] + public void Open_ExistingFileWithCreateNewMode_ShouldThrowIOException( + string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Open(FileMode.CreateNew); + }); + + exception.Should().BeException($"'{FileSystem.Path.GetFullPath(path)}'", + hResult: Test.RunsOnWindows ? -2147024816 : 17); + } + + [SkippableTheory] + [InlineAutoData(FileMode.Open)] + [InlineAutoData(FileMode.Truncate)] + public void Open_MissingFileAndIncorrectMode_ShouldThrowFileNotFoundException( + FileMode mode, string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Open(mode); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + + [SkippableTheory] +#if !NETFRAMEWORK + [InlineAutoData(FileMode.Append)] +#endif + [InlineAutoData(FileMode.Create)] + [InlineAutoData(FileMode.CreateNew)] + [InlineAutoData(FileMode.OpenOrCreate)] + public void Open_MissingFileAndCorrectMode_ShouldCreateFile( + FileMode mode, string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + _ = sut.Open(mode); + }); + + exception.Should().BeNull(); + sut.Exists.Should().BeTrue(); + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void Open_ShouldOpenWithReadAndWriteAccess( + string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(FileMode.OpenOrCreate); + + stream.CanRead.Should().BeTrue(); + stream.CanWrite.Should().BeTrue(); + } + + [SkippableTheory] +#if !NETFRAMEWORK + [InlineAutoData(FileMode.Append, FileAccess.Write)] +#endif + [InlineAutoData(FileMode.Open, FileAccess.ReadWrite)] + [InlineAutoData(FileMode.Create, FileAccess.ReadWrite)] + public void Open_ShouldUseExpectedAccessDependingOnMode( + FileMode mode, + FileAccess expectedAccess, + string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(mode); + + FileTestHelper.CheckFileAccess(stream).Should().Be(expectedAccess); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } + + [SkippableTheory] + [InlineAutoData(FileAccess.Read, FileShare.Write)] + [InlineAutoData(FileAccess.Write, FileShare.Read)] + public void Open_ShouldUseGivenAccessAndShare(string path, + FileAccess access, + FileShare share) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(FileMode.Open, access, share); + + FileTestHelper.CheckFileAccess(stream).Should().Be(access); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); + } + + [SkippableTheory] + [AutoData] + public void Open_ShouldUseNoneShareAsDefault(string path, + FileAccess access) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.Open(FileMode.Open, access); + + FileTestHelper.CheckFileAccess(stream).Should().Be(access); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } + +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + [SkippableTheory] + [InlineAutoData(FileAccess.Read, FileShare.Write)] + [InlineAutoData(FileAccess.Write, FileShare.Read)] + public void Open_WithFileStreamOptions_ShouldUseGivenAccessAndShare( + string path, + FileAccess access, + FileShare share) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + FileStreamOptions options = new() + { + Mode = FileMode.Open, + Access = access, + Share = share + }; + + using FileSystemStream stream = sut.Open(options); + + FileTestHelper.CheckFileAccess(stream).Should().Be(access); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(share); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTextTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTextTests.cs index 799a2665f..c5bdd63a6 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTextTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenTextTests.cs @@ -1,40 +1,40 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OpenTextTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void OpenText_MissingFile_ShouldThrowFileNotFoundException( - string path) - { - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - using StreamReader stream = fileInfo.OpenText(); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - - [SkippableTheory] - [AutoData] - public void OpenText_ShouldReturnFileContent( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - using StreamReader stream = fileInfo.OpenText(); - - string result = stream.ReadToEnd(); - result.Should().Be(contents); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OpenTextTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void OpenText_MissingFile_ShouldThrowFileNotFoundException( + string path) + { + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + using StreamReader stream = fileInfo.OpenText(); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + + [SkippableTheory] + [AutoData] + public void OpenText_ShouldReturnFileContent( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + using StreamReader stream = fileInfo.OpenText(); + + string result = stream.ReadToEnd(); + result.Should().Be(contents); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenWriteTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenWriteTests.cs index 04b8f5d64..43e860a8e 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenWriteTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/OpenWriteTests.cs @@ -1,33 +1,33 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OpenWriteTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void OpenWrite_MissingFile_ShouldCreateFile(string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.OpenWrite(); - - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void OpenWrite_ShouldUseWriteAccessAndNoneShare(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - - using FileSystemStream stream = sut.OpenWrite(); - - FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Write); - FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OpenWriteTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void OpenWrite_MissingFile_ShouldCreateFile(string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.OpenWrite(); + + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void OpenWrite_ShouldUseWriteAccessAndNoneShare(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + + using FileSystemStream stream = sut.OpenWrite(); + + FileTestHelper.CheckFileAccess(stream).Should().Be(FileAccess.Write); + FileTestHelper.CheckFileShare(FileSystem, path).Should().Be(FileShare.None); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ReplaceTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ReplaceTests.cs index c56e28b46..162b02730 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ReplaceTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ReplaceTests.cs @@ -1,417 +1,417 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ReplaceTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Replace_BackupDirectoryMissing_ShouldThrowCorrectException( - string sourceName, - string destinationName, - string missingDirectory, - string backupName) - { - FileSystem.File.WriteAllText(sourceName, "some content"); - string backupPath = FileSystem.Path.Combine(missingDirectory, backupName); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupPath); - }); - - exception.Should().BeFileOrDirectoryNotFoundException(); - } - - [SkippableTheory] - [AutoData] - public void Replace_DestinationDirectoryMissing_ShouldThrowDirectoryNotFoundException( - string sourceName, - string missingDirectory, - string destinationName, - string backupName) - { - FileSystem.File.WriteAllText(sourceName, "some content"); - string destinationPath = - FileSystem.Path.Combine(missingDirectory, destinationName); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationPath, backupName); - }); - - exception.Should().BeFileOrDirectoryNotFoundException(); - } - - [SkippableTheory] - [AutoData] - public void Replace_DestinationIsDirectory_ShouldThrowUnauthorizedAccessException( - string sourceName, - string destinationName, - string backupName) - { - FileSystem.File.WriteAllText(sourceName, null); - FileSystem.Directory.CreateDirectory(destinationName); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024891); - } - - [SkippableTheory] - [AutoData] - public void Replace_DestinationMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName, - string backupName) - { - FileSystem.File.WriteAllText(sourceName, null); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024894); - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Replace_ReadOnly_WithIgnoreMetadataError_ShouldReplaceFile( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - sut.IsReadOnly = true; - - IFileInfo result = sut.Replace(destinationName, backupName, true); - - sut.Exists.Should().BeFalse(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.GetAttributes(destinationName) - .Should().HaveFlag(FileAttributes.ReadOnly); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - - [SkippableTheory] - [AutoData] - public void - Replace_ReadOnly_WithoutIgnoreMetadataError_ShouldThrowUnauthorizedAccessException_OnWindows( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - sut.IsReadOnly = true; - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupName); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024891); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - FileSystem.File.GetAttributes(destinationName) - .Should().NotHaveFlag(FileAttributes.ReadOnly); - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - else - { - exception.Should().BeNull(); - sut.Exists.Should().BeFalse(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - } - - [SkippableTheory] - [AutoData] - public void Replace_ShouldAddArchiveAttribute_OnWindows( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents, - FileAttributes sourceFileAttributes, - FileAttributes destinationFileAttributes) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.SetAttributes(sourceName, sourceFileAttributes); - FileAttributes expectedSourceAttributes = - FileSystem.File.GetAttributes(sourceName); - if (Test.RunsOnWindows) - { - expectedSourceAttributes |= FileAttributes.Archive; - } - - FileSystem.File.WriteAllText(destinationName, destinationContents); - FileSystem.File.SetAttributes(destinationName, destinationFileAttributes); - FileAttributes expectedDestinationAttributes = - FileSystem.File.GetAttributes(destinationName); - if (Test.RunsOnWindows) - { - expectedDestinationAttributes |= FileAttributes.Archive; - } - - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - sut.Replace(destinationName, backupName, true); - - FileSystem.File.GetAttributes(destinationName) - .Should().Be(expectedSourceAttributes); - FileSystem.File.GetAttributes(backupName) - .Should().Be(expectedDestinationAttributes); - } - - [SkippableTheory] - [AutoData] - public void Replace_ShouldKeepMetadata( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText(sourceName, sourceContents); - DateTime sourceLastAccessTime = FileSystem.File.GetLastAccessTime(sourceName); - DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTime(sourceName); - TimeSystem.Thread.Sleep(1000); - - FileSystem.File.WriteAllText(destinationName, destinationContents); - DateTime destinationCreationTime = - FileSystem.File.GetCreationTime(destinationName); - DateTime destinationLastAccessTime = - FileSystem.File.GetLastAccessTime(destinationName); - DateTime destinationLastWriteTime = - FileSystem.File.GetLastWriteTime(destinationName); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - TimeSystem.Thread.Sleep(1000); - - sut.Replace(destinationName, backupName); - - FileSystem.File.GetCreationTime(destinationName) - .Should().Be(destinationCreationTime); - FileSystem.File.GetLastAccessTime(destinationName) - .Should().Be(sourceLastAccessTime); - FileSystem.File.GetLastWriteTime(destinationName) - .Should().Be(sourceLastWriteTime); - FileSystem.File.GetCreationTime(backupName) - .Should().Be(destinationCreationTime); - FileSystem.File.GetLastAccessTime(backupName) - .Should().Be(destinationLastAccessTime); - FileSystem.File.GetLastWriteTime(backupName) - .Should().Be(destinationLastWriteTime); - } - - [SkippableTheory] - [AutoData] - public void Replace_ShouldReplaceFile( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - IFileInfo result = sut.Replace(destinationName, backupName); - - sut.Exists.Should().BeFalse(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - - [SkippableTheory] - [AutoData] - public void Replace_SourceDirectoryMissing_ShouldThrowFileNotFoundException( - string missingDirectory, - string sourceName, - string destinationName, - string backupName) - { - string sourcePath = FileSystem.Path.Combine(missingDirectory, sourceName); - IFileInfo sut = FileSystem.FileInfo.New(sourcePath); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupName); - }); - - exception.Should().BeFileOrDirectoryNotFoundException(); - } - - [SkippableTheory] - [AutoData] - public void Replace_SourceIsDirectory_ShouldThrowUnauthorizedAccessException( - string sourceName, - string destinationName, - string backupName) - { - Skip.IfNot(Test.RunsOnWindows, "Tests sometimes throw IOException on Linux"); - - FileSystem.Directory.CreateDirectory(sourceName); - FileSystem.File.WriteAllText(destinationName, null); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024891); - } - - [SkippableTheory] - [AutoData] - public void Replace_SourceLocked_ShouldThrowIOException_OnWindows( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, - FileAccess.Read, FileShare.Read); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupName); - }); - - stream.Dispose(); - if (Test.RunsOnWindows) - { - exception.Should().BeException(hResult: -2147024864); - sut.Exists.Should().BeTrue(); - FileSystem.File.Exists(sourceName).Should().BeTrue(); - FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); - FileSystem.File.GetAttributes(destinationName) - .Should().NotHaveFlag(FileAttributes.ReadOnly); - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - else - { - sut.Exists.Should().BeFalse(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); - } - } - - [SkippableTheory] - [AutoData] - public void Replace_SourceMissing_ShouldThrowFileNotFoundException( - string sourceName, - string destinationName, - string backupName) - { - FileSystem.File.WriteAllText(destinationName, null); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - Exception? exception = Record.Exception(() => - { - sut.Replace(destinationName, backupName); - }); - - exception.Should().BeException(hResult: -2147024894); - if (Test.RunsOnWindows) - { - // Behaviour on Linux/MacOS is uncertain - FileSystem.File.Exists(backupName).Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void Replace_WithExistingBackupFile_ShouldIgnoreBackup( - string sourceName, - string destinationName, - string backupName, - string sourceContents, - string destinationContents, - string backupContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - FileSystem.File.WriteAllText(backupName, backupContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - IFileInfo result = sut.Replace(destinationName, null); - - sut.Exists.Should().BeFalse(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - FileSystem.File.Exists(backupName).Should().BeTrue(); - FileSystem.File.ReadAllText(backupName).Should().Be(backupContents); - } - - [SkippableTheory] - [AutoData] - public void Replace_WithoutBackup_ShouldReplaceFile( - string sourceName, - string destinationName, - string sourceContents, - string destinationContents) - { - FileSystem.File.WriteAllText(sourceName, sourceContents); - FileSystem.File.WriteAllText(destinationName, destinationContents); - IFileInfo sut = FileSystem.FileInfo.New(sourceName); - - IFileInfo result = sut.Replace(destinationName, null); - - sut.Exists.Should().BeFalse(); - FileSystem.File.Exists(sourceName).Should().BeFalse(); - result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - FileSystem.File.Exists(destinationName).Should().BeTrue(); - FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ReplaceTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Replace_BackupDirectoryMissing_ShouldThrowCorrectException( + string sourceName, + string destinationName, + string missingDirectory, + string backupName) + { + FileSystem.File.WriteAllText(sourceName, "some content"); + string backupPath = FileSystem.Path.Combine(missingDirectory, backupName); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupPath); + }); + + exception.Should().BeFileOrDirectoryNotFoundException(); + } + + [SkippableTheory] + [AutoData] + public void Replace_DestinationDirectoryMissing_ShouldThrowDirectoryNotFoundException( + string sourceName, + string missingDirectory, + string destinationName, + string backupName) + { + FileSystem.File.WriteAllText(sourceName, "some content"); + string destinationPath = + FileSystem.Path.Combine(missingDirectory, destinationName); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationPath, backupName); + }); + + exception.Should().BeFileOrDirectoryNotFoundException(); + } + + [SkippableTheory] + [AutoData] + public void Replace_DestinationIsDirectory_ShouldThrowUnauthorizedAccessException( + string sourceName, + string destinationName, + string backupName) + { + FileSystem.File.WriteAllText(sourceName, null); + FileSystem.Directory.CreateDirectory(destinationName); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024891); + } + + [SkippableTheory] + [AutoData] + public void Replace_DestinationMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName, + string backupName) + { + FileSystem.File.WriteAllText(sourceName, null); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024894); + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Replace_ReadOnly_WithIgnoreMetadataError_ShouldReplaceFile( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + sut.IsReadOnly = true; + + IFileInfo result = sut.Replace(destinationName, backupName, true); + + sut.Exists.Should().BeFalse(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.GetAttributes(destinationName) + .Should().HaveFlag(FileAttributes.ReadOnly); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + + [SkippableTheory] + [AutoData] + public void + Replace_ReadOnly_WithoutIgnoreMetadataError_ShouldThrowUnauthorizedAccessException_OnWindows( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + sut.IsReadOnly = true; + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024891); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + FileSystem.File.GetAttributes(destinationName) + .Should().NotHaveFlag(FileAttributes.ReadOnly); + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + else + { + exception.Should().BeNull(); + sut.Exists.Should().BeFalse(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + } + + [SkippableTheory] + [AutoData] + public void Replace_ShouldAddArchiveAttribute_OnWindows( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents, + FileAttributes sourceFileAttributes, + FileAttributes destinationFileAttributes) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.SetAttributes(sourceName, sourceFileAttributes); + FileAttributes expectedSourceAttributes = + FileSystem.File.GetAttributes(sourceName); + if (Test.RunsOnWindows) + { + expectedSourceAttributes |= FileAttributes.Archive; + } + + FileSystem.File.WriteAllText(destinationName, destinationContents); + FileSystem.File.SetAttributes(destinationName, destinationFileAttributes); + FileAttributes expectedDestinationAttributes = + FileSystem.File.GetAttributes(destinationName); + if (Test.RunsOnWindows) + { + expectedDestinationAttributes |= FileAttributes.Archive; + } + + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + sut.Replace(destinationName, backupName, true); + + FileSystem.File.GetAttributes(destinationName) + .Should().Be(expectedSourceAttributes); + FileSystem.File.GetAttributes(backupName) + .Should().Be(expectedDestinationAttributes); + } + + [SkippableTheory] + [AutoData] + public void Replace_ShouldKeepMetadata( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText(sourceName, sourceContents); + DateTime sourceLastAccessTime = FileSystem.File.GetLastAccessTime(sourceName); + DateTime sourceLastWriteTime = FileSystem.File.GetLastWriteTime(sourceName); + TimeSystem.Thread.Sleep(1000); + + FileSystem.File.WriteAllText(destinationName, destinationContents); + DateTime destinationCreationTime = + FileSystem.File.GetCreationTime(destinationName); + DateTime destinationLastAccessTime = + FileSystem.File.GetLastAccessTime(destinationName); + DateTime destinationLastWriteTime = + FileSystem.File.GetLastWriteTime(destinationName); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + TimeSystem.Thread.Sleep(1000); + + sut.Replace(destinationName, backupName); + + FileSystem.File.GetCreationTime(destinationName) + .Should().Be(destinationCreationTime); + FileSystem.File.GetLastAccessTime(destinationName) + .Should().Be(sourceLastAccessTime); + FileSystem.File.GetLastWriteTime(destinationName) + .Should().Be(sourceLastWriteTime); + FileSystem.File.GetCreationTime(backupName) + .Should().Be(destinationCreationTime); + FileSystem.File.GetLastAccessTime(backupName) + .Should().Be(destinationLastAccessTime); + FileSystem.File.GetLastWriteTime(backupName) + .Should().Be(destinationLastWriteTime); + } + + [SkippableTheory] + [AutoData] + public void Replace_ShouldReplaceFile( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + IFileInfo result = sut.Replace(destinationName, backupName); + + sut.Exists.Should().BeFalse(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + + [SkippableTheory] + [AutoData] + public void Replace_SourceDirectoryMissing_ShouldThrowFileNotFoundException( + string missingDirectory, + string sourceName, + string destinationName, + string backupName) + { + string sourcePath = FileSystem.Path.Combine(missingDirectory, sourceName); + IFileInfo sut = FileSystem.FileInfo.New(sourcePath); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + exception.Should().BeFileOrDirectoryNotFoundException(); + } + + [SkippableTheory] + [AutoData] + public void Replace_SourceIsDirectory_ShouldThrowUnauthorizedAccessException( + string sourceName, + string destinationName, + string backupName) + { + Skip.IfNot(Test.RunsOnWindows, "Tests sometimes throw IOException on Linux"); + + FileSystem.Directory.CreateDirectory(sourceName); + FileSystem.File.WriteAllText(destinationName, null); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024891); + } + + [SkippableTheory] + [AutoData] + public void Replace_SourceLocked_ShouldThrowIOException_OnWindows( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + using FileSystemStream stream = FileSystem.File.Open(sourceName, FileMode.Open, + FileAccess.Read, FileShare.Read); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + stream.Dispose(); + if (Test.RunsOnWindows) + { + exception.Should().BeException(hResult: -2147024864); + sut.Exists.Should().BeTrue(); + FileSystem.File.Exists(sourceName).Should().BeTrue(); + FileSystem.File.ReadAllText(sourceName).Should().Be(sourceContents); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(destinationContents); + FileSystem.File.GetAttributes(destinationName) + .Should().NotHaveFlag(FileAttributes.ReadOnly); + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + else + { + sut.Exists.Should().BeFalse(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(destinationContents); + } + } + + [SkippableTheory] + [AutoData] + public void Replace_SourceMissing_ShouldThrowFileNotFoundException( + string sourceName, + string destinationName, + string backupName) + { + FileSystem.File.WriteAllText(destinationName, null); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024894); + if (Test.RunsOnWindows) + { + // Behaviour on Linux/MacOS is uncertain + FileSystem.File.Exists(backupName).Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void Replace_WithExistingBackupFile_ShouldIgnoreBackup( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents, + string backupContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + FileSystem.File.WriteAllText(backupName, backupContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + IFileInfo result = sut.Replace(destinationName, null); + + sut.Exists.Should().BeFalse(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + FileSystem.File.Exists(backupName).Should().BeTrue(); + FileSystem.File.ReadAllText(backupName).Should().Be(backupContents); + } + + [SkippableTheory] + [AutoData] + public void Replace_WithoutBackup_ShouldReplaceFile( + string sourceName, + string destinationName, + string sourceContents, + string destinationContents) + { + FileSystem.File.WriteAllText(sourceName, sourceContents); + FileSystem.File.WriteAllText(destinationName, destinationContents); + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + IFileInfo result = sut.Replace(destinationName, null); + + sut.Exists.Should().BeFalse(); + FileSystem.File.Exists(sourceName).Should().BeFalse(); + result.FullName.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + FileSystem.File.Exists(destinationName).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationName).Should().Be(sourceContents); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/Tests.cs index e0efea092..6ae15157c 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/Tests.cs @@ -1,161 +1,161 @@ -using System.IO; -using Testably.Abstractions.Testing.FileSystemInitializer; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Attributes_WhenFileIsMissing_ShouldReturnMinusOne(string path) - { - FileAttributes expected = (FileAttributes)(-1); - IFileInfo sut = FileSystem.FileInfo.New(path); - - sut.Attributes.Should().Be(expected); - } - - [SkippableTheory] - [AutoData] - public void Attributes_WhenFileIsMissing_SetterShouldThrowFileNotFoundException( - string path) - { - IFileInfo sut = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - sut.Attributes = FileAttributes.ReadOnly; - }); - - exception.Should().BeException(hResult: -2147024894); - } - - [SkippableFact] - public void Directory_ShouldReturnParentDirectory() - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithAFile()); - IFileInfo? file = initialized[1] as IFileInfo; - - file?.Directory.Should().NotBeNull(); - file!.Directory!.FullName.Should().Be(initialized[0].FullName); - } - - [SkippableFact] - public void DirectoryName_ShouldReturnNameOfParentDirectory() - { - IFileSystemDirectoryInitializer initialized = - FileSystem.Initialize() - .WithASubdirectory().Initialized(s => s - .WithAFile()); - IFileInfo? file = initialized[1] as IFileInfo; - - file?.Should().NotBeNull(); - file!.DirectoryName.Should().Be(initialized[0].FullName); - } - - [SkippableTheory] - [InlineData("foo", "")] - [InlineData("foo.txt", ".txt")] - [InlineData("foo.bar.txt", ".txt")] - public void Extension_ShouldReturnExpectedValue(string fileName, string expectedValue) - { - IFileInfo sut = FileSystem.FileInfo.New(fileName); - - sut.Extension.Should().Be(expectedValue); - } - - [SkippableFact] - public void Extension_WithTrailingDot_ShouldReturnExpectedValue() - { - IFileInfo sut = FileSystem.FileInfo.New("foo."); - - if (Test.RunsOnWindows) - { - sut.Extension.Should().Be(""); - } - else - { - sut.Extension.Should().Be("."); - } - } - - [SkippableTheory] - [AutoData] - public void IsReadOnly_SetToFalse_ShouldRemoveReadOnlyAttribute(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - fileInfo.Attributes = FileAttributes.ReadOnly; - - fileInfo.IsReadOnly = false; - - fileInfo.IsReadOnly.Should().BeFalse(); - fileInfo.Attributes.Should().Be(FileAttributes.Normal); - } - - [SkippableTheory] - [AutoData] - public void IsReadOnly_SetToTrue_ShouldAddReadOnlyAttribute(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - fileInfo.IsReadOnly = true; - - fileInfo.IsReadOnly.Should().BeTrue(); - fileInfo.Attributes.Should().HaveFlag(FileAttributes.ReadOnly); - - fileInfo.IsReadOnly = true; - - fileInfo.IsReadOnly.Should().BeTrue(); - fileInfo.Attributes.Should().HaveFlag(FileAttributes.ReadOnly); - - fileInfo.IsReadOnly = false; - - fileInfo.IsReadOnly.Should().BeFalse(); - fileInfo.Attributes.Should().NotHaveFlag(FileAttributes.ReadOnly); - } - - [SkippableTheory] - [AutoData] - public void IsReadOnly_ShouldChangeWhenSettingReadOnlyAttribute(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - fileInfo.Attributes = FileAttributes.ReadOnly | FileAttributes.Encrypted; - - fileInfo.IsReadOnly.Should().BeTrue(); - fileInfo.Attributes.Should().HaveFlag(FileAttributes.ReadOnly); - } - - [SkippableTheory] - [AutoData] - public void IsReadOnly_ShouldInitializeToReadOnlyAttribute(string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - fileInfo.IsReadOnly.Should().BeFalse(); - fileInfo.Attributes.Should().NotHaveFlag(FileAttributes.ReadOnly); - } - - [SkippableTheory] - [InlineData("/foo")] - [InlineData("./foo")] - [InlineData("foo")] - public void ToString_ShouldReturnProvidedPath(string path) - { - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - string? result = fileInfo.ToString(); - - result.Should().Be(path); - } -} +using System.IO; +using Testably.Abstractions.Testing.FileSystemInitializer; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Attributes_WhenFileIsMissing_ShouldReturnMinusOne(string path) + { + FileAttributes expected = (FileAttributes)(-1); + IFileInfo sut = FileSystem.FileInfo.New(path); + + sut.Attributes.Should().Be(expected); + } + + [SkippableTheory] + [AutoData] + public void Attributes_WhenFileIsMissing_SetterShouldThrowFileNotFoundException( + string path) + { + IFileInfo sut = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + sut.Attributes = FileAttributes.ReadOnly; + }); + + exception.Should().BeException(hResult: -2147024894); + } + + [SkippableFact] + public void Directory_ShouldReturnParentDirectory() + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithAFile()); + IFileInfo? file = initialized[1] as IFileInfo; + + file?.Directory.Should().NotBeNull(); + file!.Directory!.FullName.Should().Be(initialized[0].FullName); + } + + [SkippableFact] + public void DirectoryName_ShouldReturnNameOfParentDirectory() + { + IFileSystemDirectoryInitializer initialized = + FileSystem.Initialize() + .WithASubdirectory().Initialized(s => s + .WithAFile()); + IFileInfo? file = initialized[1] as IFileInfo; + + file?.Should().NotBeNull(); + file!.DirectoryName.Should().Be(initialized[0].FullName); + } + + [SkippableTheory] + [InlineData("foo", "")] + [InlineData("foo.txt", ".txt")] + [InlineData("foo.bar.txt", ".txt")] + public void Extension_ShouldReturnExpectedValue(string fileName, string expectedValue) + { + IFileInfo sut = FileSystem.FileInfo.New(fileName); + + sut.Extension.Should().Be(expectedValue); + } + + [SkippableFact] + public void Extension_WithTrailingDot_ShouldReturnExpectedValue() + { + IFileInfo sut = FileSystem.FileInfo.New("foo."); + + if (Test.RunsOnWindows) + { + sut.Extension.Should().Be(""); + } + else + { + sut.Extension.Should().Be("."); + } + } + + [SkippableTheory] + [AutoData] + public void IsReadOnly_SetToFalse_ShouldRemoveReadOnlyAttribute(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + fileInfo.Attributes = FileAttributes.ReadOnly; + + fileInfo.IsReadOnly = false; + + fileInfo.IsReadOnly.Should().BeFalse(); + fileInfo.Attributes.Should().Be(FileAttributes.Normal); + } + + [SkippableTheory] + [AutoData] + public void IsReadOnly_SetToTrue_ShouldAddReadOnlyAttribute(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + fileInfo.IsReadOnly = true; + + fileInfo.IsReadOnly.Should().BeTrue(); + fileInfo.Attributes.Should().HaveFlag(FileAttributes.ReadOnly); + + fileInfo.IsReadOnly = true; + + fileInfo.IsReadOnly.Should().BeTrue(); + fileInfo.Attributes.Should().HaveFlag(FileAttributes.ReadOnly); + + fileInfo.IsReadOnly = false; + + fileInfo.IsReadOnly.Should().BeFalse(); + fileInfo.Attributes.Should().NotHaveFlag(FileAttributes.ReadOnly); + } + + [SkippableTheory] + [AutoData] + public void IsReadOnly_ShouldChangeWhenSettingReadOnlyAttribute(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + fileInfo.Attributes = FileAttributes.ReadOnly | FileAttributes.Encrypted; + + fileInfo.IsReadOnly.Should().BeTrue(); + fileInfo.Attributes.Should().HaveFlag(FileAttributes.ReadOnly); + } + + [SkippableTheory] + [AutoData] + public void IsReadOnly_ShouldInitializeToReadOnlyAttribute(string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + fileInfo.IsReadOnly.Should().BeFalse(); + fileInfo.Attributes.Should().NotHaveFlag(FileAttributes.ReadOnly); + } + + [SkippableTheory] + [InlineData("/foo")] + [InlineData("./foo")] + [InlineData("foo")] + public void ToString_ShouldReturnProvidedPath(string path) + { + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + string? result = fileInfo.ToString(); + + result.Should().Be(path); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/ExceptionTests.cs index 345da92e7..eaa1301aa 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/ExceptionTests.cs @@ -1,127 +1,127 @@ -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfoFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetFileInfoFactoryCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileInfoFactoryCallbacks), parameters: " ")] - public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetFileInfoFactoryCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileInfoFactoryCallbacks), - parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowArgumentException_OnNetFramework( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo); - }); - - if (Test.IsNetFramework) - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should() - .BeNull( - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - - #region Helpers - - public static IEnumerable GetFileInfoFactoryCallbacks(string? path) - => GetFileInfoFactoryCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetFileInfoFactoryCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.AllExceptNull, "path", fileInfoFactory - => fileInfoFactory.New(value)); -#if NET7_0_OR_GREATER - // https://github.com/dotnet/runtime/issues/78224 - yield return ( - ExceptionTestHelper.TestTypes.Null | ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, - "fileName", fileInfoFactory - => fileInfoFactory.New(value)); -#else - yield return (ExceptionTestHelper.TestTypes.Null, "fileName", fileInfoFactory - => fileInfoFactory.New(value)); -#endif - } - - #endregion -} +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfoFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetFileInfoFactoryCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileInfoFactoryCallbacks), parameters: " ")] + public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetFileInfoFactoryCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileInfoFactoryCallbacks), + parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowArgumentException_OnNetFramework( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo); + }); + + if (Test.IsNetFramework) + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should() + .BeNull( + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + + #region Helpers + + public static IEnumerable GetFileInfoFactoryCallbacks(string? path) + => GetFileInfoFactoryCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetFileInfoFactoryCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.AllExceptNull, "path", fileInfoFactory + => fileInfoFactory.New(value)); +#if NET7_0_OR_GREATER + // https://github.com/dotnet/runtime/issues/78224 + yield return ( + ExceptionTestHelper.TestTypes.Null | ExceptionTestHelper.TestTypes.IgnoreParamNameCheck, + "fileName", fileInfoFactory + => fileInfoFactory.New(value)); +#else + yield return (ExceptionTestHelper.TestTypes.Null, "fileName", fileInfoFactory + => fileInfoFactory.New(value)); +#endif + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/Tests.cs index 7ae16cebc..22b085c51 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfoFactory/Tests.cs @@ -1,92 +1,92 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileInfoFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [InlineData(259)] - [InlineData(260)] - public void New_PathTooLong_ShouldThrowPathTooLongException_OnNetFramework( - int maxLength) - { - string rootDrive = FileTestHelper.RootDrive(); - string path = new('a', maxLength - rootDrive.Length); - Exception? exception = Record.Exception(() => - { - _ = FileSystem.FileInfo.New(rootDrive + path); - }); - - if (Test.IsNetFramework) - { - exception.Should().BeException(hResult: -2147024690); - } - else - { - exception.Should().BeNull(); - } - } - - [SkippableTheory] - [AutoData] - public void New_ShouldCreateNewFileInfoFromPath(string path) - { - IFileInfo result = FileSystem.FileInfo.New(path); - - result.ToString().Should().Be(path); - result.Exists.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void New_ShouldOpenWithExistingContent(string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - IFileInfo sut = FileSystem.FileInfo.New(path); - - using StreamReader streamReader = new(sut.OpenRead()); - string result = streamReader.ReadToEnd(); - result.Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void New_ShouldSetLength(string path, byte[] bytes) - { - FileSystem.File.WriteAllBytes(path, bytes); - - FileSystemStream stream = FileSystem.File.Open(path, FileMode.Open, - FileAccess.Read, FileShare.Write); - IFileInfo sut = FileSystem.FileInfo.New(path); - - long result = sut.Length; - - stream.Dispose(); - - result.Should().Be(bytes.Length); - } - - [SkippableFact] - public void Wrap_Null_ShouldReturnNull() - { - IFileInfo? result = FileSystem.FileInfo.Wrap(null); - - result.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void Wrap_ShouldWrapFromFileInfo(string path) - { - System.IO.FileInfo fileInfo = new(path); - - IFileInfo result = FileSystem.FileInfo.Wrap(fileInfo); - - result.FullName.Should().Be(fileInfo.FullName); - result.Exists.Should().Be(fileInfo.Exists); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileInfoFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [InlineData(259)] + [InlineData(260)] + public void New_PathTooLong_ShouldThrowPathTooLongException_OnNetFramework( + int maxLength) + { + string rootDrive = FileTestHelper.RootDrive(); + string path = new('a', maxLength - rootDrive.Length); + Exception? exception = Record.Exception(() => + { + _ = FileSystem.FileInfo.New(rootDrive + path); + }); + + if (Test.IsNetFramework) + { + exception.Should().BeException(hResult: -2147024690); + } + else + { + exception.Should().BeNull(); + } + } + + [SkippableTheory] + [AutoData] + public void New_ShouldCreateNewFileInfoFromPath(string path) + { + IFileInfo result = FileSystem.FileInfo.New(path); + + result.ToString().Should().Be(path); + result.Exists.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void New_ShouldOpenWithExistingContent(string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + IFileInfo sut = FileSystem.FileInfo.New(path); + + using StreamReader streamReader = new(sut.OpenRead()); + string result = streamReader.ReadToEnd(); + result.Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void New_ShouldSetLength(string path, byte[] bytes) + { + FileSystem.File.WriteAllBytes(path, bytes); + + FileSystemStream stream = FileSystem.File.Open(path, FileMode.Open, + FileAccess.Read, FileShare.Write); + IFileInfo sut = FileSystem.FileInfo.New(path); + + long result = sut.Length; + + stream.Dispose(); + + result.Should().Be(bytes.Length); + } + + [SkippableFact] + public void Wrap_Null_ShouldReturnNull() + { + IFileInfo? result = FileSystem.FileInfo.Wrap(null); + + result.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void Wrap_ShouldWrapFromFileInfo(string path) + { + System.IO.FileInfo fileInfo = new(path); + + IFileInfo result = FileSystem.FileInfo.Wrap(fileInfo); + + result.FullName.Should().Be(fileInfo.FullName); + result.Exists.Should().Be(fileInfo.Exists); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/AdjustTimesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/AdjustTimesTests.cs index a2dbd2282..b3d114639 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/AdjustTimesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/AdjustTimesTests.cs @@ -1,509 +1,509 @@ -using System.IO; -#if FEATURE_SPAN -using System.Threading.Tasks; -#endif - -namespace Testably.Abstractions.Tests.FileSystem.FileStream; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class AdjustTimesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void CopyTo_ShouldAdjustTimes(string path, byte[] contents) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - byte[] buffer = new byte[contents.Length]; - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - using MemoryStream destination = new(buffer); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - - stream.CopyTo(destination); - destination.Flush(); - - DateTime lastAccessTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public void Read_AsSpan_ShouldAdjustTimes(string path, byte[] contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - byte[] buffer = new byte[2]; - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - _ = stream.Read(buffer.AsSpan()); - - DateTime lastAccessTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } -#endif - - [SkippableTheory] - [AutoData] - public void Read_ShouldAdjustTimes(string path, byte[] contents) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - byte[] buffer = new byte[2]; - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - _ = stream.Read(buffer, 0, 2); - - DateTime lastAccessTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public async Task ReadAsync_AsMemory_ShouldAdjustTimes(string path, byte[] contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - byte[] buffer = new byte[2]; - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - await FileSystem.File.WriteAllBytesAsync(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - await using FileSystemStream stream = FileSystem.File.OpenRead(path); - - _ = await stream.ReadAsync(buffer.AsMemory()); - - DateTime lastAccessTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } -#endif - -#if FEATURE_FILESYSTEM_ASYNC - [SkippableTheory] - [AutoData] - public async Task ReadAsync_ShouldAdjustTimes(string path, byte[] contents) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - byte[] buffer = new byte[2]; - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - await FileSystem.File.WriteAllBytesAsync(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - await using FileSystemStream stream = FileSystem.File.OpenRead(path); - - #pragma warning disable CA1835 - _ = await stream.ReadAsync(buffer, 0, 2); - #pragma warning restore CA1835 - - DateTime lastAccessTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } -#endif - - [SkippableTheory] - [AutoData] - public void ReadByte_ShouldAdjustTimes(string path, byte[] contents) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - stream.ReadByte(); - - DateTime lastAccessTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - [SkippableTheory] - [AutoData] - public void Seek_ShouldNotAdjustTimes(string path, byte[] contents) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - stream.Seek(2, SeekOrigin.Current); - - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public void Write_AsSpan_ShouldAdjustTimes(string path, byte[] contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, Array.Empty()); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - stream.Write(contents.AsSpan()); - stream.Dispose(); - - DateTime lastWriteTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime); - } -#endif - - [SkippableTheory] - [AutoData] - public void Write_ShouldAdjustTimes(string path, byte[] contents) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, Array.Empty()); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - stream.Write(contents, 0, 2); - stream.Dispose(); - - DateTime lastWriteTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public async Task WriteAsync_AsMemory_ShouldAdjustTimes(string path, byte[] contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - await FileSystem.File.WriteAllBytesAsync(path, Array.Empty()); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - await using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - await stream.WriteAsync(contents.AsMemory()); - await stream.DisposeAsync(); - - DateTime lastWriteTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime); - } -#endif - -#if FEATURE_FILESYSTEM_ASYNC - [SkippableTheory] - [AutoData] - public async Task WriteAsync_ShouldAdjustTimes(string path, byte[] contents) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - await FileSystem.File.WriteAllBytesAsync(path, Array.Empty()); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - await using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - #pragma warning disable CA1835 - await stream.WriteAsync(contents, 0, 2); - #pragma warning restore CA1835 - await stream.DisposeAsync(); - - DateTime lastWriteTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime); - } -#endif - - [SkippableTheory] - [AutoData] - public void WriteByte_ShouldAdjustTimes(string path, byte[] contents, byte content) - { - Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, - "Works unreliable on .NET Framework"); - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - DateTime updateTime = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - stream.WriteByte(content); - stream.Dispose(); - - DateTime lastWriteTime = WaitToBeUpdatedToAfter( - () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime); - } - - #region Helpers - - private DateTime WaitToBeUpdatedToAfter(Func callback, - DateTime expectedAfter) - { - for (int i = 0; i < 100; i++) - { - DateTime time = callback(); - if (time >= expectedAfter) - { - return time; - } - - TimeSystem.Thread.Sleep(100); - } - - return callback(); - } - - #endregion -} +using System.IO; +#if FEATURE_SPAN +using System.Threading.Tasks; +#endif + +namespace Testably.Abstractions.Tests.FileSystem.FileStream; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class AdjustTimesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void CopyTo_ShouldAdjustTimes(string path, byte[] contents) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + byte[] buffer = new byte[contents.Length]; + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + using MemoryStream destination = new(buffer); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + + stream.CopyTo(destination); + destination.Flush(); + + DateTime lastAccessTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public void Read_AsSpan_ShouldAdjustTimes(string path, byte[] contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + byte[] buffer = new byte[2]; + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + _ = stream.Read(buffer.AsSpan()); + + DateTime lastAccessTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } +#endif + + [SkippableTheory] + [AutoData] + public void Read_ShouldAdjustTimes(string path, byte[] contents) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + byte[] buffer = new byte[2]; + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + _ = stream.Read(buffer, 0, 2); + + DateTime lastAccessTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public async Task ReadAsync_AsMemory_ShouldAdjustTimes(string path, byte[] contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + byte[] buffer = new byte[2]; + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + await FileSystem.File.WriteAllBytesAsync(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + await using FileSystemStream stream = FileSystem.File.OpenRead(path); + + _ = await stream.ReadAsync(buffer.AsMemory()); + + DateTime lastAccessTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } +#endif + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableTheory] + [AutoData] + public async Task ReadAsync_ShouldAdjustTimes(string path, byte[] contents) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + byte[] buffer = new byte[2]; + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + await FileSystem.File.WriteAllBytesAsync(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + await using FileSystemStream stream = FileSystem.File.OpenRead(path); + + #pragma warning disable CA1835 + _ = await stream.ReadAsync(buffer, 0, 2); + #pragma warning restore CA1835 + + DateTime lastAccessTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } +#endif + + [SkippableTheory] + [AutoData] + public void ReadByte_ShouldAdjustTimes(string path, byte[] contents) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + stream.ReadByte(); + + DateTime lastAccessTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastAccessTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + [SkippableTheory] + [AutoData] + public void Seek_ShouldNotAdjustTimes(string path, byte[] contents) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + stream.Seek(2, SeekOrigin.Current); + + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public void Write_AsSpan_ShouldAdjustTimes(string path, byte[] contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, Array.Empty()); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + stream.Write(contents.AsSpan()); + stream.Dispose(); + + DateTime lastWriteTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime); + } +#endif + + [SkippableTheory] + [AutoData] + public void Write_ShouldAdjustTimes(string path, byte[] contents) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, Array.Empty()); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + stream.Write(contents, 0, 2); + stream.Dispose(); + + DateTime lastWriteTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public async Task WriteAsync_AsMemory_ShouldAdjustTimes(string path, byte[] contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + await FileSystem.File.WriteAllBytesAsync(path, Array.Empty()); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + await using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + await stream.WriteAsync(contents.AsMemory()); + await stream.DisposeAsync(); + + DateTime lastWriteTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime); + } +#endif + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableTheory] + [AutoData] + public async Task WriteAsync_ShouldAdjustTimes(string path, byte[] contents) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + await FileSystem.File.WriteAllBytesAsync(path, Array.Empty()); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + await TimeSystem.Task.Delay(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + await using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + #pragma warning disable CA1835 + await stream.WriteAsync(contents, 0, 2); + #pragma warning restore CA1835 + await stream.DisposeAsync(); + + DateTime lastWriteTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime); + } +#endif + + [SkippableTheory] + [AutoData] + public void WriteByte_ShouldAdjustTimes(string path, byte[] contents, byte content) + { + Skip.If(Test.IsNetFramework && FileSystem is RealFileSystem, + "Works unreliable on .NET Framework"); + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + DateTime updateTime = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + stream.WriteByte(content); + stream.Dispose(); + + DateTime lastWriteTime = WaitToBeUpdatedToAfter( + () => FileSystem.File.GetLastWriteTimeUtc(path), updateTime); + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime); + } + + #region Helpers + + private DateTime WaitToBeUpdatedToAfter(Func callback, + DateTime expectedAfter) + { + for (int i = 0; i < 100; i++) + { + DateTime time = callback(); + if (time >= expectedAfter) + { + return time; + } + + TimeSystem.Thread.Sleep(100); + } + + return callback(); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/DisposeTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/DisposeTests.cs index fd0de5f09..f6871037e 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/DisposeTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/DisposeTests.cs @@ -1,115 +1,115 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Threading; - -namespace Testably.Abstractions.Tests.FileSystem.FileStream; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class DisposeTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Dispose_CalledTwiceShouldDoNothing( - string path, byte[] contents) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - FileSystem.File.WriteAllBytes(path, contents); - - using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, - FileAccess.ReadWrite, FileShare.ReadWrite, 10, FileOptions.DeleteOnClose); - - stream.Dispose(); - FileSystem.File.Exists(path).Should().BeFalse(); - FileSystem.File.WriteAllText(path, "foo"); - - stream.Dispose(); - - FileSystem.File.Exists(path).Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void Dispose_ShouldNotResurrectFile(string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - FileSystemStream stream = FileSystem.File.Open(path, - FileMode.Open, - FileAccess.ReadWrite, - FileShare.Delete); - - int fileCount1 = FileSystem.Directory.GetFiles(".", "*").Length; - FileSystem.File.Delete(path); - int fileCount2 = FileSystem.Directory.GetFiles(".", "*").Length; - stream.Dispose(); - int fileCount3 = FileSystem.Directory.GetFiles(".", "*").Length; - - fileCount1.Should().Be(1, "File should have existed"); - fileCount2.Should().Be(0, "File should have been deleted"); - fileCount3.Should().Be(0, "Dispose should not have resurrected the file"); - } - - [Theory] - [MemberData(nameof(GetFileStreamCallbacks))] - public void Operations_ShouldThrowAfterStreamIsDisposed( - Expression> callback) - { - FileSystem.File.WriteAllText("foo", "some content"); - Exception? exception = Record.Exception(() => - { - FileSystemStream stream = - FileSystem.FileStream.New("foo", FileMode.Open, FileAccess.ReadWrite); - stream.Dispose(); - callback.Compile().Invoke(stream); - }); - - exception.Should() - .BeOfType( - $"\n{callback}\n executed after Dispose() was called.") - .Which.ObjectName.Should() - .BeEmpty($"\n{callback}\n executed after Dispose() was called."); - } - - #region Helpers - - public static IEnumerable GetFileStreamCallbacks() - => GetFileStreamCallbackTestParameters() - .Select(item => new object?[] - { - item - }); - - private static IEnumerable>> - GetFileStreamCallbackTestParameters() - { - yield return fileStream => fileStream.BeginRead(Array.Empty(), 0, 0, null, null); - yield return fileStream => fileStream.BeginWrite(Array.Empty(), 0, 0, null, null); - yield return fileStream => fileStream.CopyTo(new MemoryStream(), 1); - yield return fileStream - => fileStream.CopyToAsync(new MemoryStream(), 1, CancellationToken.None) - .GetAwaiter().GetResult(); - yield return fileStream => fileStream.Flush(); - yield return fileStream => fileStream.FlushAsync(CancellationToken.None) - .GetAwaiter().GetResult(); - // ReSharper disable once MustUseReturnValue - yield return fileStream => fileStream.Read(Array.Empty(), 0, 0); -#if FEATURE_SPAN - //yield return fileStream => fileStream.Read(Array.Empty().AsSpan()); -#endif - yield return fileStream => fileStream.ReadAsync(Array.Empty(), 0, 0) - .GetAwaiter().GetResult(); - yield return fileStream => fileStream.ReadByte(); - yield return fileStream => fileStream.Seek(0, SeekOrigin.Begin); - yield return fileStream => fileStream.SetLength(0); - yield return fileStream => fileStream.Write(Array.Empty(), 0, 0); - yield return fileStream => fileStream.WriteAsync(Array.Empty(), 0, 0) - .GetAwaiter().GetResult(); - yield return fileStream => fileStream.WriteByte(0x42); - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; + +namespace Testably.Abstractions.Tests.FileSystem.FileStream; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class DisposeTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Dispose_CalledTwiceShouldDoNothing( + string path, byte[] contents) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + FileSystem.File.WriteAllBytes(path, contents); + + using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, + FileAccess.ReadWrite, FileShare.ReadWrite, 10, FileOptions.DeleteOnClose); + + stream.Dispose(); + FileSystem.File.Exists(path).Should().BeFalse(); + FileSystem.File.WriteAllText(path, "foo"); + + stream.Dispose(); + + FileSystem.File.Exists(path).Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void Dispose_ShouldNotResurrectFile(string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + FileSystemStream stream = FileSystem.File.Open(path, + FileMode.Open, + FileAccess.ReadWrite, + FileShare.Delete); + + int fileCount1 = FileSystem.Directory.GetFiles(".", "*").Length; + FileSystem.File.Delete(path); + int fileCount2 = FileSystem.Directory.GetFiles(".", "*").Length; + stream.Dispose(); + int fileCount3 = FileSystem.Directory.GetFiles(".", "*").Length; + + fileCount1.Should().Be(1, "File should have existed"); + fileCount2.Should().Be(0, "File should have been deleted"); + fileCount3.Should().Be(0, "Dispose should not have resurrected the file"); + } + + [Theory] + [MemberData(nameof(GetFileStreamCallbacks))] + public void Operations_ShouldThrowAfterStreamIsDisposed( + Expression> callback) + { + FileSystem.File.WriteAllText("foo", "some content"); + Exception? exception = Record.Exception(() => + { + FileSystemStream stream = + FileSystem.FileStream.New("foo", FileMode.Open, FileAccess.ReadWrite); + stream.Dispose(); + callback.Compile().Invoke(stream); + }); + + exception.Should() + .BeOfType( + $"\n{callback}\n executed after Dispose() was called.") + .Which.ObjectName.Should() + .BeEmpty($"\n{callback}\n executed after Dispose() was called."); + } + + #region Helpers + + public static IEnumerable GetFileStreamCallbacks() + => GetFileStreamCallbackTestParameters() + .Select(item => new object?[] + { + item + }); + + private static IEnumerable>> + GetFileStreamCallbackTestParameters() + { + yield return fileStream => fileStream.BeginRead(Array.Empty(), 0, 0, null, null); + yield return fileStream => fileStream.BeginWrite(Array.Empty(), 0, 0, null, null); + yield return fileStream => fileStream.CopyTo(new MemoryStream(), 1); + yield return fileStream + => fileStream.CopyToAsync(new MemoryStream(), 1, CancellationToken.None) + .GetAwaiter().GetResult(); + yield return fileStream => fileStream.Flush(); + yield return fileStream => fileStream.FlushAsync(CancellationToken.None) + .GetAwaiter().GetResult(); + // ReSharper disable once MustUseReturnValue + yield return fileStream => fileStream.Read(Array.Empty(), 0, 0); +#if FEATURE_SPAN + //yield return fileStream => fileStream.Read(Array.Empty().AsSpan()); +#endif + yield return fileStream => fileStream.ReadAsync(Array.Empty(), 0, 0) + .GetAwaiter().GetResult(); + yield return fileStream => fileStream.ReadByte(); + yield return fileStream => fileStream.Seek(0, SeekOrigin.Begin); + yield return fileStream => fileStream.SetLength(0); + yield return fileStream => fileStream.Write(Array.Empty(), 0, 0); + yield return fileStream => fileStream.WriteAsync(Array.Empty(), 0, 0) + .GetAwaiter().GetResult(); + yield return fileStream => fileStream.WriteByte(0x42); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/FileAccessTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/FileAccessTests.cs index 70e255e35..8e6a0be33 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/FileAccessTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/FileAccessTests.cs @@ -1,242 +1,242 @@ -using System.Collections.Concurrent; -using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Testably.Abstractions.Tests.FileSystem.FileStream; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class FileAccessTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [InlineAutoData(FileAccess.Read, FileShare.Read, - FileAccess.ReadWrite, FileShare.Read)] - [InlineAutoData(FileAccess.ReadWrite, FileShare.Read, - FileAccess.Read, FileShare.Read)] - [InlineAutoData(FileAccess.ReadWrite, FileShare.ReadWrite, - FileAccess.ReadWrite, FileShare.Read)] - [InlineAutoData(FileAccess.ReadWrite, FileShare.ReadWrite, - FileAccess.ReadWrite, FileShare.Write)] - [InlineAutoData(FileAccess.Read, FileShare.Read, - FileAccess.ReadWrite, FileShare.ReadWrite)] - public void FileAccess_ConcurrentAccessWithInvalidScenarios_ShouldThrowIOException( - FileAccess access1, FileShare share1, - FileAccess access2, FileShare share2, - string path, string contents) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.File.WriteAllText(path, contents); - - Exception? exception = Record.Exception(() => - { - _ = FileSystem.FileStream.New(path, FileMode.Open, - access1, share1); - _ = FileSystem.FileStream.New(path, FileMode.Open, - access2, share2); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024864, - because: - $"Access {access1}, Share {share1} of file 1 is incompatible with Access {access2}, Share {share2} of file 2"); - } - else - { - exception.Should().BeNull(); - } - } - - [SkippableTheory] - [InlineAutoData(FileAccess.Read, FileShare.Read, FileAccess.Read, FileShare.Read)] - [InlineAutoData(FileAccess.Read, FileShare.ReadWrite, FileAccess.ReadWrite, - FileShare.Read)] - public void FileAccess_ConcurrentReadAccessWithValidScenarios_ShouldNotThrowException( - FileAccess access1, FileShare share1, - FileAccess access2, FileShare share2, - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Open, - access1, share1); - FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Open, - access2, share2); - - using StreamReader sr1 = new(stream1, Encoding.UTF8); - using StreamReader sr2 = new(stream2, Encoding.UTF8); - string result1 = sr1.ReadToEnd(); - string result2 = sr2.ReadToEnd(); - - result1.Should().Be(contents); - result2.Should().Be(contents); - } - - [SkippableTheory] - [InlineAutoData(FileAccess.Write, FileShare.Write, FileAccess.Write, FileShare.Write)] - [InlineAutoData(FileAccess.ReadWrite, FileShare.ReadWrite, FileAccess.ReadWrite, - FileShare.ReadWrite)] - public void - FileAccess_ConcurrentWriteAccessWithValidScenarios_ShouldNotThrowException( - FileAccess access1, FileShare share1, - FileAccess access2, FileShare share2, - string path, string contents1, string contents2) - { - FileSystem.File.WriteAllText(path, null); - - FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Open, - access1, share1); - FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Open, - access2, share2); - - byte[] bytes1 = Encoding.UTF8.GetBytes(contents1); - stream1.Write(bytes1, 0, bytes1.Length); - stream1.Flush(); - byte[] bytes2 = Encoding.UTF8.GetBytes(contents2); - stream2.Write(bytes2, 0, bytes2.Length); - stream2.Flush(); - - stream1.Dispose(); - stream2.Dispose(); - string result = FileSystem.File.ReadAllText(path); - - result.Should().Be(contents2); - } - - [SkippableTheory] - [AutoData] - public void FileAccess_ReadAfterFirstAppend_ShouldContainBothContents( - string path, string contents1, string contents2) - { - FileSystem.File.WriteAllText(path, null); - - FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Append, - FileAccess.Write, FileShare.Write); - - byte[] bytes1 = Encoding.UTF8.GetBytes(contents1); - stream1.Write(bytes1, 0, bytes1.Length); - stream1.Flush(); - - FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Append, - FileAccess.Write, FileShare.Write); - byte[] bytes2 = Encoding.UTF8.GetBytes(contents2); - stream2.Write(bytes2, 0, bytes2.Length); - stream2.Flush(); - - stream1.Dispose(); - stream2.Dispose(); - string result = FileSystem.File.ReadAllText(path); - result.Should().Be(contents1 + contents2); - } - - [SkippableTheory] - [AutoData] - public void FileAccess_ReadBeforeFirstAppend_ShouldOnlyContainSecondContent( - string path, string contents1, string contents2) - { - FileSystem.File.WriteAllText(path, null); - - FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Append, - FileAccess.Write, FileShare.Write); - FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Append, - FileAccess.Write, FileShare.Write); - - byte[] bytes1 = Encoding.UTF8.GetBytes(contents1); - stream1.Write(bytes1, 0, bytes1.Length); - stream1.Flush(); - byte[] bytes2 = Encoding.UTF8.GetBytes(contents2); - stream2.Write(bytes2, 0, bytes2.Length); - stream2.Flush(); - - stream1.Dispose(); - stream2.Dispose(); - string result = FileSystem.File.ReadAllText(path); - - result.Should().Be(contents2); - } - - [SkippableTheory] - [AutoData] - public void FileAccess_ReadWhileWriteLockActive_ShouldThrowIOException( - string path, string contents) - { - using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Create); - - byte[] bytes = Encoding.UTF8.GetBytes(contents); - stream.Write(bytes, 0, bytes.Length); - - Exception? exception = Record.Exception(() => - { - FileSystem.File.ReadAllText(path); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024864); - } - else - { - exception.Should().BeNull(); - } - } - - [SkippableTheory] - [AutoData] - public void MultipleParallelReads_ShouldBeAllowed(string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - ConcurrentBag results = new(); - - ParallelLoopResult wait = Parallel.For(0, 100, _ => - { - FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, - FileAccess.Read, FileShare.Read); - using StreamReader sr = new(stream, Encoding.UTF8); - results.Add(sr.ReadToEnd()); - }); - - while (!wait.IsCompleted) - { - Thread.Sleep(10); - } - - results.Should().HaveCount(100); - results.Should().AllBeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void Read_ShouldCreateValidFileStream(string path, string contents) - { - FileSystem.File.WriteAllText(path, contents, Encoding.UTF8); - FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open); - using StreamReader sr = new(stream, Encoding.UTF8); - string result = sr.ReadToEnd(); - - result.Should().Be(contents); - } - - [SkippableTheory] - [AutoData] - public void Write_ShouldCreateValidFileStream(string path, string contents) - { - FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.CreateNew); - - byte[] bytes = Encoding.UTF8.GetBytes(contents); - stream.Write(bytes, 0, bytes.Length); - - stream.Dispose(); - - string result = FileSystem.File.ReadAllText(path); - - result.Should().Be(contents); - } -} +using System.Collections.Concurrent; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Testably.Abstractions.Tests.FileSystem.FileStream; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class FileAccessTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [InlineAutoData(FileAccess.Read, FileShare.Read, + FileAccess.ReadWrite, FileShare.Read)] + [InlineAutoData(FileAccess.ReadWrite, FileShare.Read, + FileAccess.Read, FileShare.Read)] + [InlineAutoData(FileAccess.ReadWrite, FileShare.ReadWrite, + FileAccess.ReadWrite, FileShare.Read)] + [InlineAutoData(FileAccess.ReadWrite, FileShare.ReadWrite, + FileAccess.ReadWrite, FileShare.Write)] + [InlineAutoData(FileAccess.Read, FileShare.Read, + FileAccess.ReadWrite, FileShare.ReadWrite)] + public void FileAccess_ConcurrentAccessWithInvalidScenarios_ShouldThrowIOException( + FileAccess access1, FileShare share1, + FileAccess access2, FileShare share2, + string path, string contents) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.File.WriteAllText(path, contents); + + Exception? exception = Record.Exception(() => + { + _ = FileSystem.FileStream.New(path, FileMode.Open, + access1, share1); + _ = FileSystem.FileStream.New(path, FileMode.Open, + access2, share2); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024864, + because: + $"Access {access1}, Share {share1} of file 1 is incompatible with Access {access2}, Share {share2} of file 2"); + } + else + { + exception.Should().BeNull(); + } + } + + [SkippableTheory] + [InlineAutoData(FileAccess.Read, FileShare.Read, FileAccess.Read, FileShare.Read)] + [InlineAutoData(FileAccess.Read, FileShare.ReadWrite, FileAccess.ReadWrite, + FileShare.Read)] + public void FileAccess_ConcurrentReadAccessWithValidScenarios_ShouldNotThrowException( + FileAccess access1, FileShare share1, + FileAccess access2, FileShare share2, + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Open, + access1, share1); + FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Open, + access2, share2); + + using StreamReader sr1 = new(stream1, Encoding.UTF8); + using StreamReader sr2 = new(stream2, Encoding.UTF8); + string result1 = sr1.ReadToEnd(); + string result2 = sr2.ReadToEnd(); + + result1.Should().Be(contents); + result2.Should().Be(contents); + } + + [SkippableTheory] + [InlineAutoData(FileAccess.Write, FileShare.Write, FileAccess.Write, FileShare.Write)] + [InlineAutoData(FileAccess.ReadWrite, FileShare.ReadWrite, FileAccess.ReadWrite, + FileShare.ReadWrite)] + public void + FileAccess_ConcurrentWriteAccessWithValidScenarios_ShouldNotThrowException( + FileAccess access1, FileShare share1, + FileAccess access2, FileShare share2, + string path, string contents1, string contents2) + { + FileSystem.File.WriteAllText(path, null); + + FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Open, + access1, share1); + FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Open, + access2, share2); + + byte[] bytes1 = Encoding.UTF8.GetBytes(contents1); + stream1.Write(bytes1, 0, bytes1.Length); + stream1.Flush(); + byte[] bytes2 = Encoding.UTF8.GetBytes(contents2); + stream2.Write(bytes2, 0, bytes2.Length); + stream2.Flush(); + + stream1.Dispose(); + stream2.Dispose(); + string result = FileSystem.File.ReadAllText(path); + + result.Should().Be(contents2); + } + + [SkippableTheory] + [AutoData] + public void FileAccess_ReadAfterFirstAppend_ShouldContainBothContents( + string path, string contents1, string contents2) + { + FileSystem.File.WriteAllText(path, null); + + FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Append, + FileAccess.Write, FileShare.Write); + + byte[] bytes1 = Encoding.UTF8.GetBytes(contents1); + stream1.Write(bytes1, 0, bytes1.Length); + stream1.Flush(); + + FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Append, + FileAccess.Write, FileShare.Write); + byte[] bytes2 = Encoding.UTF8.GetBytes(contents2); + stream2.Write(bytes2, 0, bytes2.Length); + stream2.Flush(); + + stream1.Dispose(); + stream2.Dispose(); + string result = FileSystem.File.ReadAllText(path); + result.Should().Be(contents1 + contents2); + } + + [SkippableTheory] + [AutoData] + public void FileAccess_ReadBeforeFirstAppend_ShouldOnlyContainSecondContent( + string path, string contents1, string contents2) + { + FileSystem.File.WriteAllText(path, null); + + FileSystemStream stream1 = FileSystem.FileStream.New(path, FileMode.Append, + FileAccess.Write, FileShare.Write); + FileSystemStream stream2 = FileSystem.FileStream.New(path, FileMode.Append, + FileAccess.Write, FileShare.Write); + + byte[] bytes1 = Encoding.UTF8.GetBytes(contents1); + stream1.Write(bytes1, 0, bytes1.Length); + stream1.Flush(); + byte[] bytes2 = Encoding.UTF8.GetBytes(contents2); + stream2.Write(bytes2, 0, bytes2.Length); + stream2.Flush(); + + stream1.Dispose(); + stream2.Dispose(); + string result = FileSystem.File.ReadAllText(path); + + result.Should().Be(contents2); + } + + [SkippableTheory] + [AutoData] + public void FileAccess_ReadWhileWriteLockActive_ShouldThrowIOException( + string path, string contents) + { + using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Create); + + byte[] bytes = Encoding.UTF8.GetBytes(contents); + stream.Write(bytes, 0, bytes.Length); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.ReadAllText(path); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024864); + } + else + { + exception.Should().BeNull(); + } + } + + [SkippableTheory] + [AutoData] + public void MultipleParallelReads_ShouldBeAllowed(string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + ConcurrentBag results = new(); + + ParallelLoopResult wait = Parallel.For(0, 100, _ => + { + FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, + FileAccess.Read, FileShare.Read); + using StreamReader sr = new(stream, Encoding.UTF8); + results.Add(sr.ReadToEnd()); + }); + + while (!wait.IsCompleted) + { + Thread.Sleep(10); + } + + results.Should().HaveCount(100); + results.Should().AllBeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void Read_ShouldCreateValidFileStream(string path, string contents) + { + FileSystem.File.WriteAllText(path, contents, Encoding.UTF8); + FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open); + using StreamReader sr = new(stream, Encoding.UTF8); + string result = sr.ReadToEnd(); + + result.Should().Be(contents); + } + + [SkippableTheory] + [AutoData] + public void Write_ShouldCreateValidFileStream(string path, string contents) + { + FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.CreateNew); + + byte[] bytes = Encoding.UTF8.GetBytes(contents); + stream.Write(bytes, 0, bytes.Length); + + stream.Dispose(); + + string result = FileSystem.File.ReadAllText(path); + + result.Should().Be(contents); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/OptionsTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/OptionsTests.cs index 4870de81d..7ee3e2379 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/OptionsTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/OptionsTests.cs @@ -1,108 +1,108 @@ -using System.IO; -using System.Text; - -namespace Testably.Abstractions.Tests.FileSystem.FileStream; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class OptionsTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Options_DeleteOnClose_ShouldDeleteFileOnClose( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, - FileAccess.ReadWrite, FileShare.None, 10, FileOptions.DeleteOnClose); - FileSystem.File.Exists(path).Should().BeTrue(); - - stream.Close(); - - FileSystem.File.Exists(path).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Options_DeleteOnClose_ShouldDeleteFileOnDispose( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, - FileAccess.ReadWrite, FileShare.None, 10, FileOptions.DeleteOnClose); - - stream.Dispose(); - - FileSystem.File.Exists(path).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - [SupportedOSPlatform("windows")] - public void Options_Encrypt_ShouldKeepEncryptionFlag( - string path, string contents1, string contents2) - { - Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, - "Encryption depends on the underlying device, if it is supported or not."); - - FileSystem.File.WriteAllText(path, contents1); - FileSystem.File.Encrypt(path); - - using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, - FileAccess.ReadWrite, FileShare.None, 10, FileOptions.Encrypted); - byte[] bytes = Encoding.Default.GetBytes(contents2); - - stream.Write(bytes, 0, bytes.Length); - stream.SetLength(bytes.Length); - stream.Dispose(); - - FileSystem.File.GetAttributes(path).Should().HaveFlag(FileAttributes.Encrypted); - FileSystem.File.ReadAllText(path).Should().Be(contents2); - } - - [SkippableTheory] - [AutoData] - public void Options_Encrypt_Unencrypted_ShouldBeIgnored( - string path, string contents1, string contents2) - { - FileSystem.File.WriteAllText(path, contents1); - - using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, - FileAccess.ReadWrite, FileShare.None, 10, FileOptions.Encrypted); - byte[] bytes = Encoding.Default.GetBytes(contents2); - - stream.Write(bytes, 0, bytes.Length); - stream.Dispose(); - - FileSystem.File.GetAttributes(path).Should() - .NotHaveFlag(FileAttributes.Encrypted); - FileSystem.File.ReadAllText(path).Should().Be(contents2); - } - - [SkippableTheory] - [AutoData] - [SupportedOSPlatform("windows")] - public void Options_EncryptedWithoutEncryptionOption_ShouldKeepEncryptionFlag( - string path, string contents1, string contents2) - { - Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, - "Encryption depends on the underlying device, if it is supported or not."); - - FileSystem.File.WriteAllText(path, contents1); - FileSystem.File.Encrypt(path); - - using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, - FileAccess.ReadWrite, FileShare.None, 10); - byte[] bytes = Encoding.Default.GetBytes(contents2); - - stream.Write(bytes, 0, bytes.Length); - stream.SetLength(bytes.Length); - stream.Dispose(); - - FileSystem.File.GetAttributes(path).Should().HaveFlag(FileAttributes.Encrypted); - FileSystem.File.ReadAllText(path).Should().Be(contents2); - } -} +using System.IO; +using System.Text; + +namespace Testably.Abstractions.Tests.FileSystem.FileStream; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class OptionsTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Options_DeleteOnClose_ShouldDeleteFileOnClose( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, + FileAccess.ReadWrite, FileShare.None, 10, FileOptions.DeleteOnClose); + FileSystem.File.Exists(path).Should().BeTrue(); + + stream.Close(); + + FileSystem.File.Exists(path).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Options_DeleteOnClose_ShouldDeleteFileOnDispose( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, + FileAccess.ReadWrite, FileShare.None, 10, FileOptions.DeleteOnClose); + + stream.Dispose(); + + FileSystem.File.Exists(path).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + [SupportedOSPlatform("windows")] + public void Options_Encrypt_ShouldKeepEncryptionFlag( + string path, string contents1, string contents2) + { + Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, + "Encryption depends on the underlying device, if it is supported or not."); + + FileSystem.File.WriteAllText(path, contents1); + FileSystem.File.Encrypt(path); + + using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, + FileAccess.ReadWrite, FileShare.None, 10, FileOptions.Encrypted); + byte[] bytes = Encoding.Default.GetBytes(contents2); + + stream.Write(bytes, 0, bytes.Length); + stream.SetLength(bytes.Length); + stream.Dispose(); + + FileSystem.File.GetAttributes(path).Should().HaveFlag(FileAttributes.Encrypted); + FileSystem.File.ReadAllText(path).Should().Be(contents2); + } + + [SkippableTheory] + [AutoData] + public void Options_Encrypt_Unencrypted_ShouldBeIgnored( + string path, string contents1, string contents2) + { + FileSystem.File.WriteAllText(path, contents1); + + using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, + FileAccess.ReadWrite, FileShare.None, 10, FileOptions.Encrypted); + byte[] bytes = Encoding.Default.GetBytes(contents2); + + stream.Write(bytes, 0, bytes.Length); + stream.Dispose(); + + FileSystem.File.GetAttributes(path).Should() + .NotHaveFlag(FileAttributes.Encrypted); + FileSystem.File.ReadAllText(path).Should().Be(contents2); + } + + [SkippableTheory] + [AutoData] + [SupportedOSPlatform("windows")] + public void Options_EncryptedWithoutEncryptionOption_ShouldKeepEncryptionFlag( + string path, string contents1, string contents2) + { + Skip.IfNot(Test.RunsOnWindows && FileSystem is MockFileSystem, + "Encryption depends on the underlying device, if it is supported or not."); + + FileSystem.File.WriteAllText(path, contents1); + FileSystem.File.Encrypt(path); + + using FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Open, + FileAccess.ReadWrite, FileShare.None, 10); + byte[] bytes = Encoding.Default.GetBytes(contents2); + + stream.Write(bytes, 0, bytes.Length); + stream.SetLength(bytes.Length); + stream.Dispose(); + + FileSystem.File.GetAttributes(path).Should().HaveFlag(FileAttributes.Encrypted); + FileSystem.File.ReadAllText(path).Should().Be(contents2); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/ReadTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/ReadTests.cs index b51e2185b..7b5fb9c44 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/ReadTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/ReadTests.cs @@ -1,275 +1,275 @@ -using System.Threading; -#if FEATURE_FILESYSTEM_ASYNC -using System.Threading.Tasks; -#endif - -namespace Testably.Abstractions.Tests.FileSystem.FileStream; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ReadTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void BeginRead_ShouldCopyContentsToBuffer( - string path, byte[] contents) - { - ManualResetEventSlim ms = new(); - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - byte[] buffer = new byte[contents.Length]; - stream.BeginRead(buffer, 0, buffer.Length, ar => - { - // ReSharper disable once AccessToDisposedClosure - stream.EndRead(ar); - ms.Set(); - }, null); - - ms.Wait(1000); - buffer.Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void BeginRead_CanReadFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - FileSystem.File.WriteAllBytes(path, contents); - FileSystemStream stream = FileSystem.FileInfo.New(path).OpenWrite(); - - byte[] buffer = new byte[contents.Length]; - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.BeginRead(buffer, 0, buffer.Length, _ => { }, null); - }); - - stream.Dispose(); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [AutoData] - public void EndRead_Null_ShouldThrowArgumentNullException( - string path, byte[] contents) - { - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.EndRead(null!); - }); - - exception.Should().BeException(hResult: -2147467261); - } - - [SkippableTheory] - [AutoData] - public void EndRead_ShouldNotAdjustTimes(string path, byte[] contents) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - ManualResetEventSlim ms = new(); - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.OpenRead(path); - DateTime updateTime = DateTime.MinValue; - - byte[] buffer = new byte[contents.Length]; - stream.BeginRead(buffer, 0, buffer.Length, ar => - { - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - updateTime = TimeSystem.DateTime.UtcNow; - // ReSharper disable once AccessToDisposedClosure - stream.EndRead(ar); - ms.Set(); - }, null); - - ms.Wait(10000); - stream.Dispose(); - - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - lastWriteTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - [SkippableTheory] - [AutoData] - public void Read_CanReadFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - _ = stream.Read(buffer, 0, contents.Length); - }); - - stream.Dispose(); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [AutoData] - public void Read_ShouldFillBuffer(string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - int result = stream.Read(buffer, 0, contents.Length); - - result.Should().Be(contents.Length); - buffer.Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void ReadByte_CanReadFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - FileSystem.File.WriteAllBytes(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - _ = stream.ReadByte(); - }); - - stream.Dispose(); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [AutoData] - public void ReadByte_ShouldReadSingleByteAndAdvancePosition( - string path, byte[] contents) - { - FileSystem.File.WriteAllBytes(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - int result1 = stream.ReadByte(); - int result2 = stream.ReadByte(); - - stream.Position.Should().Be(2); - result1.Should().Be(contents[0]); - result2.Should().Be(contents[1]); - } - - [SkippableTheory] - [AutoData] - public void ReadTimeout_ShouldThrowInvalidOperationException( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - _ = stream.ReadTimeout; - }); - - exception.Should().BeException(hResult: -2146233079); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public void Read_AsSpan_ShouldFillBuffer(string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - int result = stream.Read(buffer.AsSpan()); - - result.Should().Be(contents.Length); - buffer.Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void Read_AsSpan_CanReadFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - _ = stream.Read(buffer.AsSpan()); - }); - - stream.Dispose(); - - exception.Should().BeException(hResult: -2146233067); - } -#endif - -#if FEATURE_FILESYSTEM_ASYNC - [SkippableTheory] - [AutoData] - public async Task ReadAsync_ShouldFillBuffer(string path, byte[] contents) - { - CancellationTokenSource cts = new(10000); - byte[] buffer = new byte[contents.Length]; - await FileSystem.File.WriteAllBytesAsync(path, contents, cts.Token); - await using FileSystemStream stream = FileSystem.File.OpenRead(path); - - #pragma warning disable CA1835 - int result = await stream.ReadAsync(buffer, 0, contents.Length, cts.Token); - #pragma warning restore CA1835 - - result.Should().Be(contents.Length); - buffer.Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public async Task ReadAsync_CanReadFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - CancellationTokenSource cts = new(10000); - byte[] buffer = new byte[contents.Length]; - await FileSystem.File.WriteAllBytesAsync(path, contents, cts.Token); - await using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - Exception? exception = await Record.ExceptionAsync(async () => - { - // ReSharper disable once AccessToDisposedClosure - #pragma warning disable CA1835 - _ = await stream.ReadAsync(buffer, 0, contents.Length, cts.Token); - #pragma warning restore CA1835 - }); - - await stream.DisposeAsync(); - - exception.Should().BeException(hResult: -2146233067); - } -#endif -} +using System.Threading; +#if FEATURE_FILESYSTEM_ASYNC +using System.Threading.Tasks; +#endif + +namespace Testably.Abstractions.Tests.FileSystem.FileStream; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ReadTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void BeginRead_ShouldCopyContentsToBuffer( + string path, byte[] contents) + { + ManualResetEventSlim ms = new(); + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + byte[] buffer = new byte[contents.Length]; + stream.BeginRead(buffer, 0, buffer.Length, ar => + { + // ReSharper disable once AccessToDisposedClosure + stream.EndRead(ar); + ms.Set(); + }, null); + + ms.Wait(1000); + buffer.Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void BeginRead_CanReadFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + FileSystem.File.WriteAllBytes(path, contents); + FileSystemStream stream = FileSystem.FileInfo.New(path).OpenWrite(); + + byte[] buffer = new byte[contents.Length]; + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.BeginRead(buffer, 0, buffer.Length, _ => { }, null); + }); + + stream.Dispose(); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [AutoData] + public void EndRead_Null_ShouldThrowArgumentNullException( + string path, byte[] contents) + { + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.EndRead(null!); + }); + + exception.Should().BeException(hResult: -2147467261); + } + + [SkippableTheory] + [AutoData] + public void EndRead_ShouldNotAdjustTimes(string path, byte[] contents) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + ManualResetEventSlim ms = new(); + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.OpenRead(path); + DateTime updateTime = DateTime.MinValue; + + byte[] buffer = new byte[contents.Length]; + stream.BeginRead(buffer, 0, buffer.Length, ar => + { + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + updateTime = TimeSystem.DateTime.UtcNow; + // ReSharper disable once AccessToDisposedClosure + stream.EndRead(ar); + ms.Set(); + }, null); + + ms.Wait(10000); + stream.Dispose(); + + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + lastWriteTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + [SkippableTheory] + [AutoData] + public void Read_CanReadFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + _ = stream.Read(buffer, 0, contents.Length); + }); + + stream.Dispose(); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [AutoData] + public void Read_ShouldFillBuffer(string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + int result = stream.Read(buffer, 0, contents.Length); + + result.Should().Be(contents.Length); + buffer.Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void ReadByte_CanReadFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + FileSystem.File.WriteAllBytes(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + _ = stream.ReadByte(); + }); + + stream.Dispose(); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [AutoData] + public void ReadByte_ShouldReadSingleByteAndAdvancePosition( + string path, byte[] contents) + { + FileSystem.File.WriteAllBytes(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + int result1 = stream.ReadByte(); + int result2 = stream.ReadByte(); + + stream.Position.Should().Be(2); + result1.Should().Be(contents[0]); + result2.Should().Be(contents[1]); + } + + [SkippableTheory] + [AutoData] + public void ReadTimeout_ShouldThrowInvalidOperationException( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + _ = stream.ReadTimeout; + }); + + exception.Should().BeException(hResult: -2146233079); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public void Read_AsSpan_ShouldFillBuffer(string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + int result = stream.Read(buffer.AsSpan()); + + result.Should().Be(contents.Length); + buffer.Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void Read_AsSpan_CanReadFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + _ = stream.Read(buffer.AsSpan()); + }); + + stream.Dispose(); + + exception.Should().BeException(hResult: -2146233067); + } +#endif + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableTheory] + [AutoData] + public async Task ReadAsync_ShouldFillBuffer(string path, byte[] contents) + { + CancellationTokenSource cts = new(10000); + byte[] buffer = new byte[contents.Length]; + await FileSystem.File.WriteAllBytesAsync(path, contents, cts.Token); + await using FileSystemStream stream = FileSystem.File.OpenRead(path); + + #pragma warning disable CA1835 + int result = await stream.ReadAsync(buffer, 0, contents.Length, cts.Token); + #pragma warning restore CA1835 + + result.Should().Be(contents.Length); + buffer.Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public async Task ReadAsync_CanReadFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + CancellationTokenSource cts = new(10000); + byte[] buffer = new byte[contents.Length]; + await FileSystem.File.WriteAllBytesAsync(path, contents, cts.Token); + await using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + Exception? exception = await Record.ExceptionAsync(async () => + { + // ReSharper disable once AccessToDisposedClosure + #pragma warning disable CA1835 + _ = await stream.ReadAsync(buffer, 0, contents.Length, cts.Token); + #pragma warning restore CA1835 + }); + + await stream.DisposeAsync(); + + exception.Should().BeException(hResult: -2146233067); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/Tests.cs index 12225baaa..e254324a9 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/Tests.cs @@ -1,299 +1,299 @@ -using System.IO; -using System.Threading.Tasks; - -namespace Testably.Abstractions.Tests.FileSystem.FileStream; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void CanSeek_ShouldReturnTrue( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - stream.CanSeek.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void CanTimeout_ShouldReturnFalse( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - stream.CanTimeout.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void Close_CalledMultipleTimes_ShouldNotThrow( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - stream.Close(); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.Close(); - }); - - exception.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void CopyTo_ShouldCopyBytes( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - using MemoryStream destination = new(buffer); - - stream.CopyTo(destination); - - destination.Flush(); - buffer.Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void CopyTo_BufferSizeZero_ShouldThrowArgumentOutOfRangeException( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - using MemoryStream destination = new(buffer); - stream.CopyTo(destination, 0); - }); - - exception.Should().BeException( - paramName: "bufferSize"); - } - -#if FEATURE_FILESYSTEM_ASYNC - [SkippableTheory] - [AutoData] - public async Task CopyToAsync_ShouldCopyBytes( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - await FileSystem.File.WriteAllBytesAsync(path, contents); - await using FileSystemStream stream = FileSystem.File.OpenRead(path); - using MemoryStream destination = new(buffer); - - await stream.CopyToAsync(destination); - - await destination.FlushAsync(); - buffer.Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public async Task CopyToAsync_BufferSizeZero_ShouldThrowArgumentOutOfRangeException( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - await FileSystem.File.WriteAllBytesAsync(path, contents); - - Exception? exception = await Record.ExceptionAsync(async () => - { - await using FileSystemStream stream = FileSystem.File.OpenRead(path); - using MemoryStream destination = new(buffer); - await stream.CopyToAsync(destination, 0); - }); - - exception.Should().BeException( - paramName: "bufferSize"); - } -#endif - - [SkippableTheory] - [AutoData] - public void Extensibility_ShouldWrapFileStreamOnRealFileSystem( - string path) - { - FileSystem.File.WriteAllText(path, null); - using FileSystemStream readStream = FileSystem.File.OpenRead(path); - bool result = readStream.Extensibility - .TryGetWrappedInstance(out System.IO.FileStream? fileStream); - - if (FileSystem is RealFileSystem) - { - result.Should().BeTrue(); - fileStream!.Name.Should().Be(readStream.Name); - } - else - { - result.Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void Flush_ShouldNotChangePosition( - string path, byte[] bytes) - { - using FileSystemStream stream = FileSystem.File.Create(path); - stream.Write(bytes, 0, bytes.Length); - stream.Seek(2, SeekOrigin.Begin); - stream.Position.Should().Be(2); - - stream.Flush(); - - stream.Position.Should().Be(2); - } - - [SkippableTheory] - [AutoData] - public void Flush_ShouldNotUpdateFileContentWhenAlreadyFlushed( - string path, byte[] bytes1, byte[] bytes2) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - using FileSystemStream stream1 = FileSystem.File.Open( - path, - FileMode.OpenOrCreate, - FileAccess.ReadWrite, - FileShare.ReadWrite); - using FileSystemStream stream2 = FileSystem.File.Open( - path, - FileMode.OpenOrCreate, - FileAccess.ReadWrite, - FileShare.ReadWrite); - stream1.Write(bytes1, 0, bytes1.Length); - stream1.Flush(); - stream2.Write(bytes2, 0, bytes2.Length); - stream2.Flush(); - - stream1.Flush(); - - stream2.Dispose(); - stream1.Dispose(); - FileSystem.File.ReadAllBytes(path).Should().BeEquivalentTo(bytes2); - } - - [SkippableTheory] - [AutoData] - public async Task FlushAsync_ShouldNotChangePosition( - string path, byte[] bytes) - { - using FileSystemStream stream = FileSystem.File.Create(path); - stream.Write(bytes, 0, bytes.Length); - stream.Seek(2, SeekOrigin.Begin); - stream.Position.Should().Be(2); - - await stream.FlushAsync(); - - stream.Position.Should().Be(2); - } - - [SkippableTheory] - [AutoData] - public void Name_ShouldReturnFullPath(string path) - { - string expectedName = FileSystem.Path.GetFullPath(path); - using FileSystemStream stream = FileSystem.File.Create(path); - - stream.Name.Should().Be(expectedName); - } - - [SkippableTheory] - [AutoData] - public void Position_ShouldChangeWhenReading( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - stream.Position.Should().Be(0); - stream.ReadByte(); - stream.Position.Should().Be(1); - } - - [SkippableTheory] - [AutoData] - public void Seek_Begin_ShouldSetAbsolutePositionFromBegin( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - stream.Position.Should().Be(0); - stream.Seek(4, SeekOrigin.Begin); - stream.Position.Should().Be(4); - } - - [SkippableTheory] - [AutoData] - public void Seek_Current_ShouldSetRelativePosition(string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - stream.Position.Should().Be(0); - stream.Seek(4, SeekOrigin.Current); - stream.Position.Should().Be(4); - stream.Seek(3, SeekOrigin.Current); - stream.Position.Should().Be(7); - stream.Seek(-1, SeekOrigin.Current); - stream.Position.Should().Be(6); - } - - [SkippableTheory] - [AutoData] - public void Seek_End_ShouldSetAbsolutePositionFromEnd(string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - stream.Position.Should().Be(0); - stream.Seek(-4, SeekOrigin.End); - stream.Position.Should().Be(contents.Length - 4); - } - - [SkippableTheory] - [AutoData] - public void SetLength(string path, int length) - { - using FileSystemStream stream = FileSystem.File.Create(path); - - stream.SetLength(length); - - stream.Length.Should().Be(length); - } - - [SkippableTheory] - [AutoData] - public void SetLength_ReadOnlyStream_ShouldThrowNotSupportedException( - string path, int length) - { - FileSystem.File.WriteAllText(path, null); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - stream.SetLength(length); - }); - - exception.Should().BeException(hResult: -2146233067); - } -} +using System.IO; +using System.Threading.Tasks; + +namespace Testably.Abstractions.Tests.FileSystem.FileStream; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void CanSeek_ShouldReturnTrue( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + stream.CanSeek.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void CanTimeout_ShouldReturnFalse( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + stream.CanTimeout.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void Close_CalledMultipleTimes_ShouldNotThrow( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + stream.Close(); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.Close(); + }); + + exception.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void CopyTo_ShouldCopyBytes( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + using MemoryStream destination = new(buffer); + + stream.CopyTo(destination); + + destination.Flush(); + buffer.Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void CopyTo_BufferSizeZero_ShouldThrowArgumentOutOfRangeException( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + using MemoryStream destination = new(buffer); + stream.CopyTo(destination, 0); + }); + + exception.Should().BeException( + paramName: "bufferSize"); + } + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableTheory] + [AutoData] + public async Task CopyToAsync_ShouldCopyBytes( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + await FileSystem.File.WriteAllBytesAsync(path, contents); + await using FileSystemStream stream = FileSystem.File.OpenRead(path); + using MemoryStream destination = new(buffer); + + await stream.CopyToAsync(destination); + + await destination.FlushAsync(); + buffer.Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public async Task CopyToAsync_BufferSizeZero_ShouldThrowArgumentOutOfRangeException( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + await FileSystem.File.WriteAllBytesAsync(path, contents); + + Exception? exception = await Record.ExceptionAsync(async () => + { + await using FileSystemStream stream = FileSystem.File.OpenRead(path); + using MemoryStream destination = new(buffer); + await stream.CopyToAsync(destination, 0); + }); + + exception.Should().BeException( + paramName: "bufferSize"); + } +#endif + + [SkippableTheory] + [AutoData] + public void Extensibility_ShouldWrapFileStreamOnRealFileSystem( + string path) + { + FileSystem.File.WriteAllText(path, null); + using FileSystemStream readStream = FileSystem.File.OpenRead(path); + bool result = readStream.Extensibility + .TryGetWrappedInstance(out System.IO.FileStream? fileStream); + + if (FileSystem is RealFileSystem) + { + result.Should().BeTrue(); + fileStream!.Name.Should().Be(readStream.Name); + } + else + { + result.Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void Flush_ShouldNotChangePosition( + string path, byte[] bytes) + { + using FileSystemStream stream = FileSystem.File.Create(path); + stream.Write(bytes, 0, bytes.Length); + stream.Seek(2, SeekOrigin.Begin); + stream.Position.Should().Be(2); + + stream.Flush(); + + stream.Position.Should().Be(2); + } + + [SkippableTheory] + [AutoData] + public void Flush_ShouldNotUpdateFileContentWhenAlreadyFlushed( + string path, byte[] bytes1, byte[] bytes2) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + using FileSystemStream stream1 = FileSystem.File.Open( + path, + FileMode.OpenOrCreate, + FileAccess.ReadWrite, + FileShare.ReadWrite); + using FileSystemStream stream2 = FileSystem.File.Open( + path, + FileMode.OpenOrCreate, + FileAccess.ReadWrite, + FileShare.ReadWrite); + stream1.Write(bytes1, 0, bytes1.Length); + stream1.Flush(); + stream2.Write(bytes2, 0, bytes2.Length); + stream2.Flush(); + + stream1.Flush(); + + stream2.Dispose(); + stream1.Dispose(); + FileSystem.File.ReadAllBytes(path).Should().BeEquivalentTo(bytes2); + } + + [SkippableTheory] + [AutoData] + public async Task FlushAsync_ShouldNotChangePosition( + string path, byte[] bytes) + { + using FileSystemStream stream = FileSystem.File.Create(path); + stream.Write(bytes, 0, bytes.Length); + stream.Seek(2, SeekOrigin.Begin); + stream.Position.Should().Be(2); + + await stream.FlushAsync(); + + stream.Position.Should().Be(2); + } + + [SkippableTheory] + [AutoData] + public void Name_ShouldReturnFullPath(string path) + { + string expectedName = FileSystem.Path.GetFullPath(path); + using FileSystemStream stream = FileSystem.File.Create(path); + + stream.Name.Should().Be(expectedName); + } + + [SkippableTheory] + [AutoData] + public void Position_ShouldChangeWhenReading( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + stream.Position.Should().Be(0); + stream.ReadByte(); + stream.Position.Should().Be(1); + } + + [SkippableTheory] + [AutoData] + public void Seek_Begin_ShouldSetAbsolutePositionFromBegin( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + stream.Position.Should().Be(0); + stream.Seek(4, SeekOrigin.Begin); + stream.Position.Should().Be(4); + } + + [SkippableTheory] + [AutoData] + public void Seek_Current_ShouldSetRelativePosition(string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + stream.Position.Should().Be(0); + stream.Seek(4, SeekOrigin.Current); + stream.Position.Should().Be(4); + stream.Seek(3, SeekOrigin.Current); + stream.Position.Should().Be(7); + stream.Seek(-1, SeekOrigin.Current); + stream.Position.Should().Be(6); + } + + [SkippableTheory] + [AutoData] + public void Seek_End_ShouldSetAbsolutePositionFromEnd(string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + stream.Position.Should().Be(0); + stream.Seek(-4, SeekOrigin.End); + stream.Position.Should().Be(contents.Length - 4); + } + + [SkippableTheory] + [AutoData] + public void SetLength(string path, int length) + { + using FileSystemStream stream = FileSystem.File.Create(path); + + stream.SetLength(length); + + stream.Length.Should().Be(length); + } + + [SkippableTheory] + [AutoData] + public void SetLength_ReadOnlyStream_ShouldThrowNotSupportedException( + string path, int length) + { + FileSystem.File.WriteAllText(path, null); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + stream.SetLength(length); + }); + + exception.Should().BeException(hResult: -2146233067); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/WriteTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/WriteTests.cs index 880c325df..df5428b68 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/WriteTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStream/WriteTests.cs @@ -1,284 +1,284 @@ -using System.IO; -using System.Threading; -#if FEATURE_FILESYSTEM_ASYNC -using System.Threading.Tasks; -#endif - -namespace Testably.Abstractions.Tests.FileSystem.FileStream; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class WriteTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void BeginWrite_CanWriteFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - FileSystem.File.WriteAllBytes(path, contents); - FileSystemStream stream = FileSystem.FileInfo.New(path).OpenRead(); - - byte[] buffer = new byte[contents.Length]; - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.BeginWrite(buffer, 0, buffer.Length, _ => { }, null); - }); - - stream.Dispose(); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [AutoData] - public void BeginWrite_ShouldCopyContentsToFile( - string path, byte[] contents) - { - ManualResetEventSlim ms = new(); - using FileSystemStream stream = FileSystem.File.Create(path); - stream.Flush(); - - stream.BeginWrite(contents, 0, contents.Length, ar => - { - // ReSharper disable once AccessToDisposedClosure - stream.EndWrite(ar); - ms.Set(); - }, null); - - ms.Wait(1000); - stream.Dispose(); - FileSystem.File.ReadAllBytes(path).Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void EndWrite_Null_ShouldThrowArgumentNullException(string path) - { - using FileSystemStream stream = FileSystem.File.Create(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.EndWrite(null!); - }); - - exception.Should().BeException(hResult: -2147467261); - } - - [SkippableTheory] - [AutoData] - public void EndWrite_ShouldAdjustTimes(string path, byte[] contents) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - ManualResetEventSlim ms = new(); - DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; - FileSystem.File.WriteAllBytes(path, contents); - DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; - using FileSystemStream stream = FileSystem.File.Create(path); - DateTime updateTime = DateTime.MinValue; - - stream.BeginWrite(contents, 0, contents.Length, ar => - { - TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); - updateTime = TimeSystem.DateTime.UtcNow; - // ReSharper disable once AccessToDisposedClosure - stream.EndWrite(ar); - ms.Set(); - }, null); - - ms.Wait(10000); - stream.Dispose(); - - DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); - DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); - DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); - - if (Test.RunsOnWindows) - { - creationTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - lastAccessTime.Should() - .BeOnOrAfter(updateTime); - } - else - { - lastAccessTime.Should() - .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And - .BeOnOrBefore(creationTimeEnd); - } - - lastWriteTime.Should() - .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); - } - - [SkippableTheory] - [AutoData] - public void Write_CanWriteFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.Write(buffer, 0, contents.Length); - }); - - stream.Dispose(); - - exception.Should().BeException(hResult: -2146233067); - } - - [SkippableTheory] - [AutoData] - public void Write_ShouldFillBuffer(string path, byte[] contents) - { - using FileSystemStream stream = FileSystem.File.Create(path); - - stream.Write(contents, 0, contents.Length); - - stream.Dispose(); - FileSystem.File.ReadAllBytes(path) - .Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void WriteByte_HiddenFile_ShouldNotThrow( - string path, byte[] contents) - { - FileSystem.File.WriteAllBytes(path, contents); - FileSystem.File.SetAttributes(path, FileAttributes.Hidden); - - using FileSystemStream stream = FileSystem.File.OpenWrite(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.WriteByte(0); - }); - - stream.Dispose(); - - exception.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void WriteByte_ShouldWriteSingleByteAndAdvancePosition( - string path, byte byte1, byte byte2) - { - using FileSystemStream stream = FileSystem.File.Create(path); - - stream.WriteByte(byte1); - stream.WriteByte(byte2); - - stream.Position.Should().Be(2); - stream.Dispose(); - FileSystem.File.ReadAllBytes(path) - .Should().BeEquivalentTo(new[] - { - byte1, byte2 - }); - } - - [SkippableTheory] - [AutoData] - public void WriteTimeout_ShouldThrowInvalidOperationException( - string path, string contents) - { - FileSystem.File.WriteAllText(path, contents); - - Exception? exception = Record.Exception(() => - { - using FileSystemStream stream = FileSystem.File.OpenRead(path); - _ = stream.WriteTimeout; - }); - - exception.Should().BeException( - hResult: -2146233079); - } - -#if FEATURE_SPAN - [SkippableTheory] - [AutoData] - public void Write_AsSpan_ShouldFillBuffer(string path, byte[] contents) - { - using FileSystemStream stream = FileSystem.File.Create(path); - - stream.Write(contents.AsSpan()); - - stream.Dispose(); - FileSystem.File.ReadAllBytes(path) - .Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public void Write_AsSpan_CanWriteFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - byte[] buffer = new byte[contents.Length]; - FileSystem.File.WriteAllBytes(path, contents); - using FileSystemStream stream = FileSystem.File.OpenRead(path); - - Exception? exception = Record.Exception(() => - { - // ReSharper disable once AccessToDisposedClosure - stream.Write(buffer.AsSpan()); - }); - - stream.Dispose(); - - exception.Should().BeException(hResult: -2146233067); - } -#endif - -#if FEATURE_FILESYSTEM_ASYNC - [SkippableTheory] - [AutoData] - public async Task WriteAsync_ShouldFillBuffer(string path, byte[] contents) - { - CancellationTokenSource cts = new(10000); - await using FileSystemStream stream = FileSystem.File.Create(path); - - #pragma warning disable CA1835 - await stream.WriteAsync(contents, 0, contents.Length, cts.Token); - #pragma warning restore CA1835 - - await stream.DisposeAsync(); - (await FileSystem.File.ReadAllBytesAsync(path, cts.Token)) - .Should().BeEquivalentTo(contents); - } - - [SkippableTheory] - [AutoData] - public async Task WriteAsync_CanWriteFalse_ShouldThrowNotSupportedException( - string path, byte[] contents) - { - CancellationTokenSource cts = new(10000); - byte[] buffer = new byte[contents.Length]; - await FileSystem.File.WriteAllBytesAsync(path, contents, cts.Token); - await using FileSystemStream stream = FileSystem.File.OpenRead(path); - - Exception? exception = await Record.ExceptionAsync(async () => - { - // ReSharper disable once AccessToDisposedClosure - #pragma warning disable CA1835 - await stream.WriteAsync(buffer, 0, contents.Length, cts.Token); - #pragma warning restore CA1835 - }); - - await stream.DisposeAsync(); - - exception.Should().BeException( - hResult: -2146233067); - } -#endif -} +using System.IO; +using System.Threading; +#if FEATURE_FILESYSTEM_ASYNC +using System.Threading.Tasks; +#endif + +namespace Testably.Abstractions.Tests.FileSystem.FileStream; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class WriteTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void BeginWrite_CanWriteFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + FileSystem.File.WriteAllBytes(path, contents); + FileSystemStream stream = FileSystem.FileInfo.New(path).OpenRead(); + + byte[] buffer = new byte[contents.Length]; + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.BeginWrite(buffer, 0, buffer.Length, _ => { }, null); + }); + + stream.Dispose(); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [AutoData] + public void BeginWrite_ShouldCopyContentsToFile( + string path, byte[] contents) + { + ManualResetEventSlim ms = new(); + using FileSystemStream stream = FileSystem.File.Create(path); + stream.Flush(); + + stream.BeginWrite(contents, 0, contents.Length, ar => + { + // ReSharper disable once AccessToDisposedClosure + stream.EndWrite(ar); + ms.Set(); + }, null); + + ms.Wait(1000); + stream.Dispose(); + FileSystem.File.ReadAllBytes(path).Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void EndWrite_Null_ShouldThrowArgumentNullException(string path) + { + using FileSystemStream stream = FileSystem.File.Create(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.EndWrite(null!); + }); + + exception.Should().BeException(hResult: -2147467261); + } + + [SkippableTheory] + [AutoData] + public void EndWrite_ShouldAdjustTimes(string path, byte[] contents) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + ManualResetEventSlim ms = new(); + DateTime creationTimeStart = TimeSystem.DateTime.UtcNow; + FileSystem.File.WriteAllBytes(path, contents); + DateTime creationTimeEnd = TimeSystem.DateTime.UtcNow; + using FileSystemStream stream = FileSystem.File.Create(path); + DateTime updateTime = DateTime.MinValue; + + stream.BeginWrite(contents, 0, contents.Length, ar => + { + TimeSystem.Thread.Sleep(FileTestHelper.AdjustTimesDelay); + updateTime = TimeSystem.DateTime.UtcNow; + // ReSharper disable once AccessToDisposedClosure + stream.EndWrite(ar); + ms.Set(); + }, null); + + ms.Wait(10000); + stream.Dispose(); + + DateTime creationTime = FileSystem.File.GetCreationTimeUtc(path); + DateTime lastAccessTime = FileSystem.File.GetLastAccessTimeUtc(path); + DateTime lastWriteTime = FileSystem.File.GetLastWriteTimeUtc(path); + + if (Test.RunsOnWindows) + { + creationTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + lastAccessTime.Should() + .BeOnOrAfter(updateTime); + } + else + { + lastAccessTime.Should() + .BeOnOrAfter(creationTimeStart.ApplySystemClockTolerance()).And + .BeOnOrBefore(creationTimeEnd); + } + + lastWriteTime.Should() + .BeOnOrAfter(updateTime.ApplySystemClockTolerance()); + } + + [SkippableTheory] + [AutoData] + public void Write_CanWriteFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.Write(buffer, 0, contents.Length); + }); + + stream.Dispose(); + + exception.Should().BeException(hResult: -2146233067); + } + + [SkippableTheory] + [AutoData] + public void Write_ShouldFillBuffer(string path, byte[] contents) + { + using FileSystemStream stream = FileSystem.File.Create(path); + + stream.Write(contents, 0, contents.Length); + + stream.Dispose(); + FileSystem.File.ReadAllBytes(path) + .Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void WriteByte_HiddenFile_ShouldNotThrow( + string path, byte[] contents) + { + FileSystem.File.WriteAllBytes(path, contents); + FileSystem.File.SetAttributes(path, FileAttributes.Hidden); + + using FileSystemStream stream = FileSystem.File.OpenWrite(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.WriteByte(0); + }); + + stream.Dispose(); + + exception.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void WriteByte_ShouldWriteSingleByteAndAdvancePosition( + string path, byte byte1, byte byte2) + { + using FileSystemStream stream = FileSystem.File.Create(path); + + stream.WriteByte(byte1); + stream.WriteByte(byte2); + + stream.Position.Should().Be(2); + stream.Dispose(); + FileSystem.File.ReadAllBytes(path) + .Should().BeEquivalentTo(new[] + { + byte1, byte2 + }); + } + + [SkippableTheory] + [AutoData] + public void WriteTimeout_ShouldThrowInvalidOperationException( + string path, string contents) + { + FileSystem.File.WriteAllText(path, contents); + + Exception? exception = Record.Exception(() => + { + using FileSystemStream stream = FileSystem.File.OpenRead(path); + _ = stream.WriteTimeout; + }); + + exception.Should().BeException( + hResult: -2146233079); + } + +#if FEATURE_SPAN + [SkippableTheory] + [AutoData] + public void Write_AsSpan_ShouldFillBuffer(string path, byte[] contents) + { + using FileSystemStream stream = FileSystem.File.Create(path); + + stream.Write(contents.AsSpan()); + + stream.Dispose(); + FileSystem.File.ReadAllBytes(path) + .Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public void Write_AsSpan_CanWriteFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + byte[] buffer = new byte[contents.Length]; + FileSystem.File.WriteAllBytes(path, contents); + using FileSystemStream stream = FileSystem.File.OpenRead(path); + + Exception? exception = Record.Exception(() => + { + // ReSharper disable once AccessToDisposedClosure + stream.Write(buffer.AsSpan()); + }); + + stream.Dispose(); + + exception.Should().BeException(hResult: -2146233067); + } +#endif + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableTheory] + [AutoData] + public async Task WriteAsync_ShouldFillBuffer(string path, byte[] contents) + { + CancellationTokenSource cts = new(10000); + await using FileSystemStream stream = FileSystem.File.Create(path); + + #pragma warning disable CA1835 + await stream.WriteAsync(contents, 0, contents.Length, cts.Token); + #pragma warning restore CA1835 + + await stream.DisposeAsync(); + (await FileSystem.File.ReadAllBytesAsync(path, cts.Token)) + .Should().BeEquivalentTo(contents); + } + + [SkippableTheory] + [AutoData] + public async Task WriteAsync_CanWriteFalse_ShouldThrowNotSupportedException( + string path, byte[] contents) + { + CancellationTokenSource cts = new(10000); + byte[] buffer = new byte[contents.Length]; + await FileSystem.File.WriteAllBytesAsync(path, contents, cts.Token); + await using FileSystemStream stream = FileSystem.File.OpenRead(path); + + Exception? exception = await Record.ExceptionAsync(async () => + { + // ReSharper disable once AccessToDisposedClosure + #pragma warning disable CA1835 + await stream.WriteAsync(buffer, 0, contents.Length, cts.Token); + #pragma warning restore CA1835 + }); + + await stream.DisposeAsync(); + + exception.Should().BeException( + hResult: -2146233067); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/ExceptionTests.cs index a7aaa4fc8..a18f228ce 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/ExceptionTests.cs @@ -1,145 +1,145 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; - -namespace Testably.Abstractions.Tests.FileSystem.FileStreamFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetFileStreamFactoryCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileStream); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileStreamFactoryCallbacks), parameters: " ")] - public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileStream); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetFileStreamFactoryCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileStream); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileStreamFactoryCallbacks), - parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileStream); - }); - - if (!Test.RunsOnWindows) - { - if (exception is IOException ioException) - { - ioException.HResult.Should().NotBe(-2147024809, - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - else - { - if (Test.IsNetFramework) - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - else - { - exception.Should().BeException( - hResult: -2147024773, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - } - - #region Helpers - - public static IEnumerable GetFileStreamFactoryCallbacks(string? path) - => GetFileStreamFactoryCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetFileStreamFactoryCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory - => fileStreamFactory.New(value, FileMode.Open)); - yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory - => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite)); - yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory - => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite, - FileShare.None)); - yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory - => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite, - FileShare.None, 1024)); - yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory - => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite, - FileShare.None, 1024, FileOptions.None)); -#if FEATURE_FILESYSTEM_STREAM_OPTIONS - yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory - => fileStreamFactory.New(value, new FileStreamOptions())); -#endif - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.FileStreamFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetFileStreamFactoryCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileStream); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileStreamFactoryCallbacks), parameters: " ")] + public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileStream); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetFileStreamFactoryCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileStream); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileStreamFactoryCallbacks), + parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileStream); + }); + + if (!Test.RunsOnWindows) + { + if (exception is IOException ioException) + { + ioException.HResult.Should().NotBe(-2147024809, + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + else + { + if (Test.IsNetFramework) + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + else + { + exception.Should().BeException( + hResult: -2147024773, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + } + + #region Helpers + + public static IEnumerable GetFileStreamFactoryCallbacks(string? path) + => GetFileStreamFactoryCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetFileStreamFactoryCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory + => fileStreamFactory.New(value, FileMode.Open)); + yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory + => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite)); + yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory + => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite, + FileShare.None)); + yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory + => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite, + FileShare.None, 1024)); + yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory + => fileStreamFactory.New(value, FileMode.Open, FileAccess.ReadWrite, + FileShare.None, 1024, FileOptions.None)); +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + yield return (ExceptionTestHelper.TestTypes.All, "path", fileStreamFactory + => fileStreamFactory.New(value, new FileStreamOptions())); +#endif + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/SafeFileHandleTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/SafeFileHandleTests.cs index 7d6041bc4..51f1a76ab 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/SafeFileHandleTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/SafeFileHandleTests.cs @@ -1,156 +1,156 @@ -#if EXECUTE_SAFEFILEHANDLE_TESTS -using Microsoft.Win32.SafeHandles; -using System.IO; -using System.Text; -using Testably.Abstractions.Testing.FileSystem; - -namespace Testably.Abstractions.Tests.FileSystem.FileStreamFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class SafeFileHandleTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void New_SafeFileHandle_InvalidHandle_ShouldThrowArgumentException( - string filename) - { - string path = FileSystem.Path.GetFullPath(filename); - SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); - - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(handle, FileAccess.ReadWrite); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: "handle"); - } - - [SkippableTheory] - [AutoData] - public void New_SafeFileHandle_InvalidHandle_WithBufferSize_ShouldThrowArgumentException( - string filename) - { - string path = FileSystem.Path.GetFullPath(filename); - SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); - - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: "handle"); - } - - [SkippableTheory] - [AutoData] - public void - New_SafeFileHandle_InvalidHandle_WithBufferSizeAndAsync_ShouldThrowArgumentException( - string filename) - { - string path = FileSystem.Path.GetFullPath(filename); - SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); - - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024, true); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: "handle"); - } - - [SkippableTheory] - [AutoData] - public void New_SafeFileHandle_Valid_ShouldCreateWritableStream( - string filename, string contents) - { - IDisposable? cleanup = null; - if (FileSystem is not RealFileSystem realFileSystem) - { - realFileSystem = new RealFileSystem(); - cleanup = realFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); - FileSystem.InitializeIn(realFileSystem.Directory.GetCurrentDirectory()); - } - - string path = realFileSystem.Path.GetFullPath(filename); - realFileSystem.File.WriteAllText(path, contents); - FileSystem.File.WriteAllText(path, contents); - SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); - (FileSystem as MockFileSystem)?.WithSafeFileHandleStrategy( - new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); - - FileSystemStream stream = FileSystem.FileStream.New(handle, FileAccess.ReadWrite); - stream.Write(Encoding.UTF8.GetBytes("foo"), 0, 3); - stream.Dispose(); - - FileSystem.File.ReadAllText(path).Should().StartWith("foo"); - cleanup?.Dispose(); - } - - [SkippableTheory] - [AutoData] - public void New_SafeFileHandle_Valid_WithBufferSize_ShouldCreateWritableStream( - string filename, string contents) - { - IDisposable? cleanup = null; - if (FileSystem is not RealFileSystem realFileSystem) - { - realFileSystem = new RealFileSystem(); - cleanup = realFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); - FileSystem.InitializeIn(realFileSystem.Directory.GetCurrentDirectory()); - } - - string path = realFileSystem.Path.GetFullPath(filename); - realFileSystem.File.WriteAllText(path, contents); - FileSystem.File.WriteAllText(path, contents); - SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); - (FileSystem as MockFileSystem)?.WithSafeFileHandleStrategy( - new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); - - FileSystemStream stream = - FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024); - stream.Write(Encoding.UTF8.GetBytes("foo"), 0, 3); - stream.Dispose(); - - FileSystem.File.ReadAllText(path).Should().StartWith("foo"); - cleanup?.Dispose(); - } - - [SkippableTheory] - [AutoData] - public void - New_SafeFileHandle_Valid_WithBufferSizeAndAsync_ShouldCreateWritableStream( - string filename, string contents) - { - IDisposable? cleanup = null; - if (FileSystem is not RealFileSystem realFileSystem) - { - realFileSystem = new RealFileSystem(); - cleanup = realFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); - FileSystem.InitializeIn(realFileSystem.Directory.GetCurrentDirectory()); - } - - string path = realFileSystem.Path.GetFullPath(filename); - realFileSystem.File.WriteAllText(path, contents); - FileSystem.File.WriteAllText(path, contents); - SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); - (FileSystem as MockFileSystem)?.WithSafeFileHandleStrategy( - new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); - - FileSystemStream stream = - FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024, false); - stream.Write(Encoding.UTF8.GetBytes("foo"), 0, 3); - stream.Dispose(); - - FileSystem.File.ReadAllText(path).Should().StartWith("foo"); - cleanup?.Dispose(); - } -} -#endif +#if EXECUTE_SAFEFILEHANDLE_TESTS +using Microsoft.Win32.SafeHandles; +using System.IO; +using System.Text; +using Testably.Abstractions.Testing.FileSystem; + +namespace Testably.Abstractions.Tests.FileSystem.FileStreamFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class SafeFileHandleTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void New_SafeFileHandle_InvalidHandle_ShouldThrowArgumentException( + string filename) + { + string path = FileSystem.Path.GetFullPath(filename); + SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(handle, FileAccess.ReadWrite); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: "handle"); + } + + [SkippableTheory] + [AutoData] + public void New_SafeFileHandle_InvalidHandle_WithBufferSize_ShouldThrowArgumentException( + string filename) + { + string path = FileSystem.Path.GetFullPath(filename); + SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: "handle"); + } + + [SkippableTheory] + [AutoData] + public void + New_SafeFileHandle_InvalidHandle_WithBufferSizeAndAsync_ShouldThrowArgumentException( + string filename) + { + string path = FileSystem.Path.GetFullPath(filename); + SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024, true); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: "handle"); + } + + [SkippableTheory] + [AutoData] + public void New_SafeFileHandle_Valid_ShouldCreateWritableStream( + string filename, string contents) + { + IDisposable? cleanup = null; + if (FileSystem is not RealFileSystem realFileSystem) + { + realFileSystem = new RealFileSystem(); + cleanup = realFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); + FileSystem.InitializeIn(realFileSystem.Directory.GetCurrentDirectory()); + } + + string path = realFileSystem.Path.GetFullPath(filename); + realFileSystem.File.WriteAllText(path, contents); + FileSystem.File.WriteAllText(path, contents); + SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); + (FileSystem as MockFileSystem)?.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); + + FileSystemStream stream = FileSystem.FileStream.New(handle, FileAccess.ReadWrite); + stream.Write(Encoding.UTF8.GetBytes("foo"), 0, 3); + stream.Dispose(); + + FileSystem.File.ReadAllText(path).Should().StartWith("foo"); + cleanup?.Dispose(); + } + + [SkippableTheory] + [AutoData] + public void New_SafeFileHandle_Valid_WithBufferSize_ShouldCreateWritableStream( + string filename, string contents) + { + IDisposable? cleanup = null; + if (FileSystem is not RealFileSystem realFileSystem) + { + realFileSystem = new RealFileSystem(); + cleanup = realFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); + FileSystem.InitializeIn(realFileSystem.Directory.GetCurrentDirectory()); + } + + string path = realFileSystem.Path.GetFullPath(filename); + realFileSystem.File.WriteAllText(path, contents); + FileSystem.File.WriteAllText(path, contents); + SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); + (FileSystem as MockFileSystem)?.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); + + FileSystemStream stream = + FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024); + stream.Write(Encoding.UTF8.GetBytes("foo"), 0, 3); + stream.Dispose(); + + FileSystem.File.ReadAllText(path).Should().StartWith("foo"); + cleanup?.Dispose(); + } + + [SkippableTheory] + [AutoData] + public void + New_SafeFileHandle_Valid_WithBufferSizeAndAsync_ShouldCreateWritableStream( + string filename, string contents) + { + IDisposable? cleanup = null; + if (FileSystem is not RealFileSystem realFileSystem) + { + realFileSystem = new RealFileSystem(); + cleanup = realFileSystem.SetCurrentDirectoryToEmptyTemporaryDirectory(); + FileSystem.InitializeIn(realFileSystem.Directory.GetCurrentDirectory()); + } + + string path = realFileSystem.Path.GetFullPath(filename); + realFileSystem.File.WriteAllText(path, contents); + FileSystem.File.WriteAllText(path, contents); + SafeFileHandle handle = UnmanagedFileLoader.CreateSafeFileHandle(path); + (FileSystem as MockFileSystem)?.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock(path))); + + FileSystemStream stream = + FileSystem.FileStream.New(handle, FileAccess.ReadWrite, 1024, false); + stream.Write(Encoding.UTF8.GetBytes("foo"), 0, 3); + stream.Dispose(); + + FileSystem.File.ReadAllText(path).Should().StartWith("foo"); + cleanup?.Dispose(); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/Tests.cs index 78ed87dd5..0233ae24b 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileStreamFactory/Tests.cs @@ -1,182 +1,182 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileStreamFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void New_AppendAccessWithReadWriteMode_ShouldThrowArgumentException( - string path) - { - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(path, FileMode.Append, FileAccess.ReadWrite); - }); - - exception.Should().BeException( - messageContains: FileMode.Append.ToString(), - hResult: -2147024809, - paramName: Test.IsNetFramework ? null : "access"); - } - - [SkippableTheory] - [AutoData] - public void New_ExistingFileWithCreateMode_ShouldIgnoreContent( - string path) - { - FileSystem.File.WriteAllText(path, "foo"); - FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Create); - stream.Dispose(); - - FileSystem.File.ReadAllText(path).Should().BeEmpty(); - } - - [SkippableTheory] - [AutoData] - public void New_ExistingFileWithCreateNewMode_ShouldThrowIOException( - string path) - { - FileSystem.File.WriteAllText(path, "foo"); - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(path, FileMode.CreateNew); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: Test.RunsOnWindows ? -2147024816 : 17); - } - - [SkippableTheory] - [AutoData] - public void New_ExistingFileWithTruncateMode_ShouldIgnoreContent( - string path) - { - FileSystem.File.WriteAllText(path, "foo"); - FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Truncate); - stream.Dispose(); - - FileSystem.File.ReadAllText(path).Should().BeEmpty(); - } - - [SkippableTheory] - [InlineAutoData(FileMode.Append)] - [InlineAutoData(FileMode.Truncate)] - [InlineAutoData(FileMode.Create)] - [InlineAutoData(FileMode.CreateNew)] - [InlineAutoData(FileMode.Append)] - public void New_InvalidModeForReadAccess_ShouldThrowArgumentException( - FileMode mode, string path) - { - FileAccess access = FileAccess.Read; - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(path, mode, access); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: Test.IsNetFramework ? null : "access"); - exception!.Message.Should() - .Contain(mode.ToString()).And - .Contain(access.ToString()); - } - - [SkippableTheory] - [InlineAutoData(FileMode.Open)] - [InlineAutoData(FileMode.Truncate)] - public void New_MissingFileWithIncorrectMode_ShouldThrowFileNotFoundException( - FileMode mode, string path) - { - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(path, mode); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - - [SkippableTheory] - [AutoData] - public void New_MissingFileWithTruncateMode_ShouldThrowFileNotFoundException( - string path) - { - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(path, FileMode.Truncate); - }); - - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024894); - } - - [SkippableTheory] - [InlineAutoData(FileAccess.Read)] - [InlineAutoData(FileAccess.ReadWrite)] - [InlineAutoData(FileAccess.Write)] - public void - New_ReadOnlyFlag_ShouldThrowUnauthorizedAccessException_WhenAccessContainsWrite( - FileAccess access, - string path) - { - FileSystem.File.WriteAllText(path, "some content"); - FileSystem.File.SetAttributes(path, FileAttributes.ReadOnly); - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(path, FileMode.Open, access); - }); - - if (access.HasFlag(FileAccess.Write)) - { - exception.Should().BeException(hResult: -2147024891); - } - else - { - exception.Should().BeNull(); - } - } - - [SkippableTheory] - [AutoData] - public void New_SamePathAsExistingDirectory_ShouldThrowCorrectException( - string path) - { - FileSystem.Directory.CreateDirectory(path); - Exception? exception = Record.Exception(() => - { - FileSystem.FileStream.New(path, FileMode.CreateNew); - }); - - if (Test.RunsOnWindows) - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: -2147024891); - } - else - { - exception.Should().BeException( - $"'{FileSystem.Path.GetFullPath(path)}'", - hResult: 17); - } - } - - [SkippableTheory] - [InlineAutoData(false)] - [InlineAutoData(true)] - public void New_WithUseAsyncSet_ShouldSetProperty(bool useAsync, string path) - { - using FileSystemStream stream = FileSystem.FileStream.New( - path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 1, - useAsync); - - stream.IsAsync.Should().Be(useAsync); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileStreamFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void New_AppendAccessWithReadWriteMode_ShouldThrowArgumentException( + string path) + { + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(path, FileMode.Append, FileAccess.ReadWrite); + }); + + exception.Should().BeException( + messageContains: FileMode.Append.ToString(), + hResult: -2147024809, + paramName: Test.IsNetFramework ? null : "access"); + } + + [SkippableTheory] + [AutoData] + public void New_ExistingFileWithCreateMode_ShouldIgnoreContent( + string path) + { + FileSystem.File.WriteAllText(path, "foo"); + FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Create); + stream.Dispose(); + + FileSystem.File.ReadAllText(path).Should().BeEmpty(); + } + + [SkippableTheory] + [AutoData] + public void New_ExistingFileWithCreateNewMode_ShouldThrowIOException( + string path) + { + FileSystem.File.WriteAllText(path, "foo"); + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(path, FileMode.CreateNew); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: Test.RunsOnWindows ? -2147024816 : 17); + } + + [SkippableTheory] + [AutoData] + public void New_ExistingFileWithTruncateMode_ShouldIgnoreContent( + string path) + { + FileSystem.File.WriteAllText(path, "foo"); + FileSystemStream stream = FileSystem.FileStream.New(path, FileMode.Truncate); + stream.Dispose(); + + FileSystem.File.ReadAllText(path).Should().BeEmpty(); + } + + [SkippableTheory] + [InlineAutoData(FileMode.Append)] + [InlineAutoData(FileMode.Truncate)] + [InlineAutoData(FileMode.Create)] + [InlineAutoData(FileMode.CreateNew)] + [InlineAutoData(FileMode.Append)] + public void New_InvalidModeForReadAccess_ShouldThrowArgumentException( + FileMode mode, string path) + { + FileAccess access = FileAccess.Read; + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(path, mode, access); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: Test.IsNetFramework ? null : "access"); + exception!.Message.Should() + .Contain(mode.ToString()).And + .Contain(access.ToString()); + } + + [SkippableTheory] + [InlineAutoData(FileMode.Open)] + [InlineAutoData(FileMode.Truncate)] + public void New_MissingFileWithIncorrectMode_ShouldThrowFileNotFoundException( + FileMode mode, string path) + { + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(path, mode); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + + [SkippableTheory] + [AutoData] + public void New_MissingFileWithTruncateMode_ShouldThrowFileNotFoundException( + string path) + { + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(path, FileMode.Truncate); + }); + + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024894); + } + + [SkippableTheory] + [InlineAutoData(FileAccess.Read)] + [InlineAutoData(FileAccess.ReadWrite)] + [InlineAutoData(FileAccess.Write)] + public void + New_ReadOnlyFlag_ShouldThrowUnauthorizedAccessException_WhenAccessContainsWrite( + FileAccess access, + string path) + { + FileSystem.File.WriteAllText(path, "some content"); + FileSystem.File.SetAttributes(path, FileAttributes.ReadOnly); + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(path, FileMode.Open, access); + }); + + if (access.HasFlag(FileAccess.Write)) + { + exception.Should().BeException(hResult: -2147024891); + } + else + { + exception.Should().BeNull(); + } + } + + [SkippableTheory] + [AutoData] + public void New_SamePathAsExistingDirectory_ShouldThrowCorrectException( + string path) + { + FileSystem.Directory.CreateDirectory(path); + Exception? exception = Record.Exception(() => + { + FileSystem.FileStream.New(path, FileMode.CreateNew); + }); + + if (Test.RunsOnWindows) + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: -2147024891); + } + else + { + exception.Should().BeException( + $"'{FileSystem.Path.GetFullPath(path)}'", + hResult: 17); + } + } + + [SkippableTheory] + [InlineAutoData(false)] + [InlineAutoData(true)] + public void New_WithUseAsyncSet_ShouldSetProperty(bool useAsync, string path) + { + using FileSystemStream stream = FileSystem.FileStream.New( + path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 1, + useAsync); + + stream.IsAsync.Should().Be(useAsync); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/CreateAsSymbolicLinkTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/CreateAsSymbolicLinkTests.cs index 70ef3bdc0..d64775cb7 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/CreateAsSymbolicLinkTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/CreateAsSymbolicLinkTests.cs @@ -1,44 +1,45 @@ -#if FEATURE_FILESYSTEM_LINK -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class CreateAsSymbolicLinkTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void CreateAsSymbolicLink_ShouldCreateSymbolicLink( - string path, string pathToTarget) - { - FileSystem.File.WriteAllText(pathToTarget, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - fileInfo.CreateAsSymbolicLink(pathToTarget); - - FileSystem.File.GetAttributes(path) - .HasFlag(FileAttributes.ReparsePoint) - .Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void CreateAsSymbolicLink_SourceFileAlreadyExists_ShouldThrowIOException( - string path, string pathToTarget) - { - FileSystem.File.WriteAllText(pathToTarget, null); - FileSystem.File.WriteAllText(path, "foo"); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - fileInfo.CreateAsSymbolicLink(pathToTarget); - }); - - exception.Should().BeException($"'{path}'", - hResult: Test.RunsOnWindows ? -2147024713 : 17); - } -} -#endif +#if FEATURE_FILESYSTEM_LINK +using System.IO; + + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class CreateAsSymbolicLinkTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void CreateAsSymbolicLink_ShouldCreateSymbolicLink( + string path, string pathToTarget) + { + FileSystem.File.WriteAllText(pathToTarget, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + fileInfo.CreateAsSymbolicLink(pathToTarget); + + FileSystem.File.GetAttributes(path) + .HasFlag(FileAttributes.ReparsePoint) + .Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void CreateAsSymbolicLink_SourceFileAlreadyExists_ShouldThrowIOException( + string path, string pathToTarget) + { + FileSystem.File.WriteAllText(pathToTarget, null); + FileSystem.File.WriteAllText(path, "foo"); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + fileInfo.CreateAsSymbolicLink(pathToTarget); + }); + + exception.Should().BeException($"'{path}'", + hResult: Test.RunsOnWindows ? -2147024713 : 17); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ExceptionTests.cs index 8a6e2a658..14298b74d 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ExceptionTests.cs @@ -1,74 +1,75 @@ -#if FEATURE_FILESYSTEM_LINK -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetFileSystemInfoCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetFileSystemInfoCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - #region Helpers - - public static IEnumerable GetFileSystemInfoCallbacks(string? path) - => GetFileSystemInfoCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes - .IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetFileSystemInfoCallbackTestParameters(string value) - { -#if FEATURE_FILESYSTEM_LINK - yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "pathToTarget", - fileSystemInfo - => fileSystemInfo.CreateAsSymbolicLink(value)); -#endif - } - - #endregion -} -#endif +#if FEATURE_FILESYSTEM_LINK +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetFileSystemInfoCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetFileSystemInfoCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileInfo.New("foo")); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + #region Helpers + + public static IEnumerable GetFileSystemInfoCallbacks(string? path) + => GetFileSystemInfoCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes + .IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetFileSystemInfoCallbackTestParameters(string value) + { +#if FEATURE_FILESYSTEM_LINK + yield return (ExceptionTestHelper.TestTypes.AllExceptInvalidPath, "pathToTarget", + fileSystemInfo + => fileSystemInfo.CreateAsSymbolicLink(value)); +#endif + } + + #endregion +} +#endif diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ResolveLinkTargetTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ResolveLinkTargetTests.cs index 5da2ed94d..1b4612d8a 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ResolveLinkTargetTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/ResolveLinkTargetTests.cs @@ -1,176 +1,177 @@ -#if FEATURE_FILESYSTEM_LINK -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ResolveLinkTargetTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - #region Test Setup - - /// - /// The maximum number of symbolic links that are followed.
- /// - ///
- private static int MaxResolveLinks => - Test.RunsOnWindows ? 63 : 40; - - #endregion - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FileWithDifferentCase_ShouldReturnPathToMissingFile( - string path, string pathToTarget, string contents) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget, "foo"); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - fileInfo.CreateAsSymbolicLink(pathToTarget); - FileSystem.File.Delete(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget.ToUpper(), contents); - - IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); - - target!.FullName.Should().Be(targetFullPath); - if (!Test.RunsOnLinux) - { - target.Exists.Should().BeTrue(); - FileSystem.File.ReadAllText(target.FullName).Should().Be(contents); - } - else - { - target.Exists.Should().BeFalse(); - } - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FinalTarget_ShouldFollowSymbolicLinkToFinalTarget( - string path, string pathToFinalTarget) - { - int maxLinks = MaxResolveLinks; - - FileSystem.File.WriteAllText(pathToFinalTarget, null); - string previousPath = pathToFinalTarget; - for (int i = 0; i < maxLinks; i++) - { - string newPath = $"{path}-{i}"; - IFileInfo linkFileInfo = FileSystem.FileInfo.New(newPath); - linkFileInfo.CreateAsSymbolicLink(previousPath); - previousPath = newPath; - } - - IFileInfo fileInfo = FileSystem.FileInfo.New(previousPath); - - IFileSystemInfo? target = fileInfo.ResolveLinkTarget(true); - - target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToFinalTarget)); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_FinalTargetWithTooManyLevels_ShouldThrowIOException( - string path, string pathToFinalTarget) - { - int maxLinks = MaxResolveLinks + 1; - FileSystem.File.WriteAllText(pathToFinalTarget, null); - string previousPath = pathToFinalTarget; - for (int i = 0; i < maxLinks; i++) - { - string newPath = $"{path}-{i}"; - IFileInfo linkFileInfo = FileSystem.FileInfo.New(newPath); - linkFileInfo.CreateAsSymbolicLink(previousPath); - previousPath = newPath; - } - - IFileInfo fileInfo = FileSystem.FileInfo.New(previousPath); - - Exception? exception = Record.Exception(() => - { - _ = fileInfo.ResolveLinkTarget(true); - }); - - exception.Should().BeException( - hResult: Test.RunsOnWindows ? -2147022975 : -2146232800, - messageContains: $"'{fileInfo.FullName}'"); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_MissingFileInLinkChain_ShouldReturnPathToMissingFile( - string path, string pathToFinalTarget, string pathToMissingFile) - { - FileSystem.File.WriteAllText(pathToFinalTarget, null); - IFileInfo linkFileInfo1 = FileSystem.FileInfo.New(pathToMissingFile); - linkFileInfo1.CreateAsSymbolicLink(pathToFinalTarget); - IFileInfo linkFileInfo2 = FileSystem.FileInfo.New(path); - linkFileInfo2.CreateAsSymbolicLink(pathToMissingFile); - linkFileInfo1.Delete(); - - IFileSystemInfo? target = linkFileInfo2.ResolveLinkTarget(true); - - target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToMissingFile)); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_NormalDirectory_ShouldReturnNull( - string path) - { - FileSystem.Directory.CreateDirectory(path); - IDirectoryInfo fileInfo = FileSystem.DirectoryInfo.New(path); - - IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); - - target.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_NormalFile_ShouldReturnNull( - string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - - IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); - - target.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_ShouldFollowSymbolicLink( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - fileInfo.CreateAsSymbolicLink(pathToTarget); - - IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); - - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeTrue(); - } - - [SkippableTheory] - [AutoData] - public void ResolveLinkTarget_TargetDeletedAfterLinkCreation_ShouldReturnNull( - string path, string pathToTarget) - { - string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); - FileSystem.File.WriteAllText(pathToTarget, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - fileInfo.CreateAsSymbolicLink(pathToTarget); - FileSystem.File.Delete(pathToTarget); - - IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); - - target!.FullName.Should().Be(targetFullPath); - target.Exists.Should().BeFalse(); - } -} -#endif +#if FEATURE_FILESYSTEM_LINK +using System.IO; + + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ResolveLinkTargetTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + #region Test Setup + + /// + /// The maximum number of symbolic links that are followed.
+ /// + ///
+ private static int MaxResolveLinks => + Test.RunsOnWindows ? 63 : 40; + + #endregion + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FileWithDifferentCase_ShouldReturnPathToMissingFile( + string path, string pathToTarget, string contents) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget, "foo"); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + fileInfo.CreateAsSymbolicLink(pathToTarget); + FileSystem.File.Delete(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget.ToUpper(), contents); + + IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); + + target!.FullName.Should().Be(targetFullPath); + if (!Test.RunsOnLinux) + { + target.Exists.Should().BeTrue(); + FileSystem.File.ReadAllText(target.FullName).Should().Be(contents); + } + else + { + target.Exists.Should().BeFalse(); + } + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FinalTarget_ShouldFollowSymbolicLinkToFinalTarget( + string path, string pathToFinalTarget) + { + int maxLinks = MaxResolveLinks; + + FileSystem.File.WriteAllText(pathToFinalTarget, null); + string previousPath = pathToFinalTarget; + for (int i = 0; i < maxLinks; i++) + { + string newPath = $"{path}-{i}"; + IFileInfo linkFileInfo = FileSystem.FileInfo.New(newPath); + linkFileInfo.CreateAsSymbolicLink(previousPath); + previousPath = newPath; + } + + IFileInfo fileInfo = FileSystem.FileInfo.New(previousPath); + + IFileSystemInfo? target = fileInfo.ResolveLinkTarget(true); + + target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToFinalTarget)); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_FinalTargetWithTooManyLevels_ShouldThrowIOException( + string path, string pathToFinalTarget) + { + int maxLinks = MaxResolveLinks + 1; + FileSystem.File.WriteAllText(pathToFinalTarget, null); + string previousPath = pathToFinalTarget; + for (int i = 0; i < maxLinks; i++) + { + string newPath = $"{path}-{i}"; + IFileInfo linkFileInfo = FileSystem.FileInfo.New(newPath); + linkFileInfo.CreateAsSymbolicLink(previousPath); + previousPath = newPath; + } + + IFileInfo fileInfo = FileSystem.FileInfo.New(previousPath); + + Exception? exception = Record.Exception(() => + { + _ = fileInfo.ResolveLinkTarget(true); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147022975 : -2146232800, + messageContains: $"'{fileInfo.FullName}'"); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_MissingFileInLinkChain_ShouldReturnPathToMissingFile( + string path, string pathToFinalTarget, string pathToMissingFile) + { + FileSystem.File.WriteAllText(pathToFinalTarget, null); + IFileInfo linkFileInfo1 = FileSystem.FileInfo.New(pathToMissingFile); + linkFileInfo1.CreateAsSymbolicLink(pathToFinalTarget); + IFileInfo linkFileInfo2 = FileSystem.FileInfo.New(path); + linkFileInfo2.CreateAsSymbolicLink(pathToMissingFile); + linkFileInfo1.Delete(); + + IFileSystemInfo? target = linkFileInfo2.ResolveLinkTarget(true); + + target!.FullName.Should().Be(FileSystem.Path.GetFullPath(pathToMissingFile)); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_NormalDirectory_ShouldReturnNull( + string path) + { + FileSystem.Directory.CreateDirectory(path); + IDirectoryInfo fileInfo = FileSystem.DirectoryInfo.New(path); + + IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); + + target.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_NormalFile_ShouldReturnNull( + string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + + IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); + + target.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_ShouldFollowSymbolicLink( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + fileInfo.CreateAsSymbolicLink(pathToTarget); + + IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); + + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeTrue(); + } + + [SkippableTheory] + [AutoData] + public void ResolveLinkTarget_TargetDeletedAfterLinkCreation_ShouldReturnNull( + string path, string pathToTarget) + { + string targetFullPath = FileSystem.Path.GetFullPath(pathToTarget); + FileSystem.File.WriteAllText(pathToTarget, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + fileInfo.CreateAsSymbolicLink(pathToTarget); + FileSystem.File.Delete(pathToTarget); + + IFileSystemInfo? target = fileInfo.ResolveLinkTarget(false); + + target!.FullName.Should().Be(targetFullPath); + target.Exists.Should().BeFalse(); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/Tests.cs index b9e108729..4d7ccff09 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/Tests.cs @@ -1,156 +1,156 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Extensibility_ShouldWrapFileSystemInfoOnRealFileSystem( - string path) - { - FileSystem.File.WriteAllText(path, null); - IFileInfo fileInfo = FileSystem.FileInfo.New(path); - bool result = fileInfo.Extensibility - .TryGetWrappedInstance(out System.IO.FileSystemInfo? fileSystemInfo); - - if (FileSystem is RealFileSystem) - { - result.Should().BeTrue(); - fileSystemInfo!.Name.Should().Be(fileInfo.Name); - } - else - { - result.Should().BeFalse(); - } - } - -#if FEATURE_FILESYSTEM_LINK - [SkippableTheory] - [AutoData] - public void LinkTarget_ShouldBeSetByCreateAsSymbolicLink( - string path, string pathToTarget) - { - FileSystem.File.WriteAllText(pathToTarget, null); - IFileInfo sut = FileSystem.FileInfo.New(path); - sut.LinkTarget.Should().BeNull(); - - sut.CreateAsSymbolicLink(pathToTarget); - - sut.LinkTarget.Should().Be(pathToTarget); - } -#endif - - [SkippableTheory] - [AutoData] - public void SetAttributes_Hidden_OnFileStartingWithDot_ShouldBeSet(string path) - { - Skip.IfNot(Test.RunsOnLinux); - - path = $".{path}"; - FileSystem.File.WriteAllText(path, null); - - FileAttributes result1 = FileSystem.File.GetAttributes(path); - FileSystem.File.SetAttributes(path, FileAttributes.Normal); - FileAttributes result2 = FileSystem.File.GetAttributes(path); - - result1.Should().Be(FileAttributes.Hidden); - result2.Should().Be(FileAttributes.Hidden); - } - - [SkippableTheory] - [AutoData] - public void SetAttributes_Hidden_OnNormalFile_ShouldBeIgnored(string path) - { - Skip.IfNot(Test.RunsOnLinux); - - FileSystem.File.WriteAllText(path, null); - - FileAttributes result1 = FileSystem.File.GetAttributes(path); - FileSystem.File.SetAttributes(path, FileAttributes.Hidden); - FileAttributes result2 = FileSystem.File.GetAttributes(path); - - result1.Should().Be(FileAttributes.Normal); - result2.Should().Be(FileAttributes.Normal); - } - - [SkippableTheory] - [InlineAutoData(FileAttributes.Compressed)] - [InlineAutoData(FileAttributes.Device)] - [InlineAutoData(FileAttributes.Encrypted)] - [InlineAutoData(FileAttributes.IntegrityStream)] - [InlineAutoData(FileAttributes.SparseFile)] - [InlineAutoData(FileAttributes.ReparsePoint)] - public void SetAttributes_ShouldBeIgnoredOnAllPlatforms(FileAttributes attributes, - string path) - { - FileSystem.File.WriteAllText(path, null); - FileSystem.File.SetAttributes(path, attributes); - - FileAttributes result = FileSystem.File.GetAttributes(path); - - result.Should().Be(FileAttributes.Normal); - } - - [SkippableTheory] - [InlineAutoData(FileAttributes.Hidden)] - public void SetAttributes_ShouldBeIgnoredOnLinux(FileAttributes attributes, - string path) - { - FileSystem.File.WriteAllText(path, null); - FileSystem.File.SetAttributes(path, attributes); - - FileAttributes result = FileSystem.File.GetAttributes(path); - - if (Test.RunsOnLinux) - { - result.Should().Be(FileAttributes.Normal); - } - else - { - result.Should().Be(attributes); - } - } - - [SkippableTheory] - [InlineAutoData(FileAttributes.ReadOnly)] - public void SetAttributes_ShouldBeSupportedOnAllPlatforms( - FileAttributes attributes, - string path) - { - FileSystem.File.WriteAllText(path, null); - FileSystem.File.SetAttributes(path, attributes); - - FileAttributes result = FileSystem.File.GetAttributes(path); - - result.Should().Be(attributes); - } - - [SkippableTheory] - [InlineAutoData(FileAttributes.Archive)] - [InlineAutoData(FileAttributes.NoScrubData)] - [InlineAutoData(FileAttributes.NotContentIndexed)] - [InlineAutoData(FileAttributes.Offline)] - [InlineAutoData(FileAttributes.System)] - [InlineAutoData(FileAttributes.Temporary)] - public void SetAttributes_ShouldOnlyWork_OnWindows(FileAttributes attributes, - string path) - { - FileSystem.File.WriteAllText(path, null); - FileSystem.File.SetAttributes(path, attributes); - - FileAttributes result = FileSystem.File.GetAttributes(path); - - if (Test.RunsOnWindows) - { - result.Should().Be(attributes); - } - else - { - result.Should().Be(FileAttributes.Normal); - } - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Extensibility_ShouldWrapFileSystemInfoOnRealFileSystem( + string path) + { + FileSystem.File.WriteAllText(path, null); + IFileInfo fileInfo = FileSystem.FileInfo.New(path); + bool result = fileInfo.Extensibility + .TryGetWrappedInstance(out System.IO.FileSystemInfo? fileSystemInfo); + + if (FileSystem is RealFileSystem) + { + result.Should().BeTrue(); + fileSystemInfo!.Name.Should().Be(fileInfo.Name); + } + else + { + result.Should().BeFalse(); + } + } + +#if FEATURE_FILESYSTEM_LINK + [SkippableTheory] + [AutoData] + public void LinkTarget_ShouldBeSetByCreateAsSymbolicLink( + string path, string pathToTarget) + { + FileSystem.File.WriteAllText(pathToTarget, null); + IFileInfo sut = FileSystem.FileInfo.New(path); + sut.LinkTarget.Should().BeNull(); + + sut.CreateAsSymbolicLink(pathToTarget); + + sut.LinkTarget.Should().Be(pathToTarget); + } +#endif + + [SkippableTheory] + [AutoData] + public void SetAttributes_Hidden_OnFileStartingWithDot_ShouldBeSet(string path) + { + Skip.IfNot(Test.RunsOnLinux); + + path = $".{path}"; + FileSystem.File.WriteAllText(path, null); + + FileAttributes result1 = FileSystem.File.GetAttributes(path); + FileSystem.File.SetAttributes(path, FileAttributes.Normal); + FileAttributes result2 = FileSystem.File.GetAttributes(path); + + result1.Should().Be(FileAttributes.Hidden); + result2.Should().Be(FileAttributes.Hidden); + } + + [SkippableTheory] + [AutoData] + public void SetAttributes_Hidden_OnNormalFile_ShouldBeIgnored(string path) + { + Skip.IfNot(Test.RunsOnLinux); + + FileSystem.File.WriteAllText(path, null); + + FileAttributes result1 = FileSystem.File.GetAttributes(path); + FileSystem.File.SetAttributes(path, FileAttributes.Hidden); + FileAttributes result2 = FileSystem.File.GetAttributes(path); + + result1.Should().Be(FileAttributes.Normal); + result2.Should().Be(FileAttributes.Normal); + } + + [SkippableTheory] + [InlineAutoData(FileAttributes.Compressed)] + [InlineAutoData(FileAttributes.Device)] + [InlineAutoData(FileAttributes.Encrypted)] + [InlineAutoData(FileAttributes.IntegrityStream)] + [InlineAutoData(FileAttributes.SparseFile)] + [InlineAutoData(FileAttributes.ReparsePoint)] + public void SetAttributes_ShouldBeIgnoredOnAllPlatforms(FileAttributes attributes, + string path) + { + FileSystem.File.WriteAllText(path, null); + FileSystem.File.SetAttributes(path, attributes); + + FileAttributes result = FileSystem.File.GetAttributes(path); + + result.Should().Be(FileAttributes.Normal); + } + + [SkippableTheory] + [InlineAutoData(FileAttributes.Hidden)] + public void SetAttributes_ShouldBeIgnoredOnLinux(FileAttributes attributes, + string path) + { + FileSystem.File.WriteAllText(path, null); + FileSystem.File.SetAttributes(path, attributes); + + FileAttributes result = FileSystem.File.GetAttributes(path); + + if (Test.RunsOnLinux) + { + result.Should().Be(FileAttributes.Normal); + } + else + { + result.Should().Be(attributes); + } + } + + [SkippableTheory] + [InlineAutoData(FileAttributes.ReadOnly)] + public void SetAttributes_ShouldBeSupportedOnAllPlatforms( + FileAttributes attributes, + string path) + { + FileSystem.File.WriteAllText(path, null); + FileSystem.File.SetAttributes(path, attributes); + + FileAttributes result = FileSystem.File.GetAttributes(path); + + result.Should().Be(attributes); + } + + [SkippableTheory] + [InlineAutoData(FileAttributes.Archive)] + [InlineAutoData(FileAttributes.NoScrubData)] + [InlineAutoData(FileAttributes.NotContentIndexed)] + [InlineAutoData(FileAttributes.Offline)] + [InlineAutoData(FileAttributes.System)] + [InlineAutoData(FileAttributes.Temporary)] + public void SetAttributes_ShouldOnlyWork_OnWindows(FileAttributes attributes, + string path) + { + FileSystem.File.WriteAllText(path, null); + FileSystem.File.SetAttributes(path, attributes); + + FileAttributes result = FileSystem.File.GetAttributes(path); + + if (Test.RunsOnWindows) + { + result.Should().Be(attributes); + } + else + { + result.Should().Be(FileAttributes.Normal); + } + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/UnixFileModeTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/UnixFileModeTests.cs index af5135cbd..62d8dc859 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/UnixFileModeTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemInfo/UnixFileModeTests.cs @@ -1,72 +1,73 @@ -#if FEATURE_FILESYSTEM_UNIXFILEMODE -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class UnixFileModeTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void UnixFileMode_MissingFile_ShouldBeInitializedToMinusOne( - string path) - { - UnixFileMode expected = (UnixFileMode)(-1); - IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); - - fileSystemInfo.UnixFileMode.Should().Be(expected); - } - - [SkippableTheory] - [AutoData] - public void UnixFileMode_ShouldBeInitializedToMinusOne( - string path) - { - Skip.IfNot(Test.RunsOnWindows); - - UnixFileMode expected = (UnixFileMode)(-1); - FileSystem.File.WriteAllText(path, "some content"); - IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); - - fileSystemInfo.UnixFileMode.Should().Be(expected); - } - - [SkippableTheory] - [AutoData] - public void UnixFileMode_ShouldBeSettableOnLinux( - string path, UnixFileMode unixFileMode) - { - Skip.If(Test.RunsOnWindows); - - FileSystem.File.WriteAllText(path, "some content"); - IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); - - #pragma warning disable CA1416 - fileSystemInfo.UnixFileMode = unixFileMode; - #pragma warning restore CA1416 - - fileSystemInfo.UnixFileMode.Should().Be(unixFileMode); - } - - [SkippableTheory] - [AutoData] - public void UnixFileMode_SetterShouldThrowPlatformNotSupportedException_OnWindows( - string path, UnixFileMode unixFileMode) - { - Skip.IfNot(Test.RunsOnWindows); - - IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); - - Exception? exception = Record.Exception(() => - { - #pragma warning disable CA1416 - fileSystemInfo.UnixFileMode = unixFileMode; - #pragma warning restore CA1416 - }); - - exception.Should().BeException(hResult: -2146233031); - } -} -#endif +#if FEATURE_FILESYSTEM_UNIXFILEMODE +using System.IO; + + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemInfo; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class UnixFileModeTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void UnixFileMode_MissingFile_ShouldBeInitializedToMinusOne( + string path) + { + UnixFileMode expected = (UnixFileMode)(-1); + IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); + + fileSystemInfo.UnixFileMode.Should().Be(expected); + } + + [SkippableTheory] + [AutoData] + public void UnixFileMode_ShouldBeInitializedToMinusOne( + string path) + { + Skip.IfNot(Test.RunsOnWindows); + + UnixFileMode expected = (UnixFileMode)(-1); + FileSystem.File.WriteAllText(path, "some content"); + IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); + + fileSystemInfo.UnixFileMode.Should().Be(expected); + } + + [SkippableTheory] + [AutoData] + public void UnixFileMode_ShouldBeSettableOnLinux( + string path, UnixFileMode unixFileMode) + { + Skip.If(Test.RunsOnWindows); + + FileSystem.File.WriteAllText(path, "some content"); + IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); + + #pragma warning disable CA1416 + fileSystemInfo.UnixFileMode = unixFileMode; + #pragma warning restore CA1416 + + fileSystemInfo.UnixFileMode.Should().Be(unixFileMode); + } + + [SkippableTheory] + [AutoData] + public void UnixFileMode_SetterShouldThrowPlatformNotSupportedException_OnWindows( + string path, UnixFileMode unixFileMode) + { + Skip.IfNot(Test.RunsOnWindows); + + IFileInfo fileSystemInfo = FileSystem.FileInfo.New(path); + + Exception? exception = Record.Exception(() => + { + #pragma warning disable CA1416 + fileSystemInfo.UnixFileMode = unixFileMode; + #pragma warning restore CA1416 + }); + + exception.Should().BeException(hResult: -2146233031); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemTests.Extensibility.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemTests.Extensibility.cs index a57dca506..5697edd81 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemTests.Extensibility.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemTests.Extensibility.cs @@ -1,5 +1,3 @@ -using Testably.Abstractions.FileSystem; - namespace Testably.Abstractions.Tests.FileSystem; public abstract partial class FileSystemTests diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/EnableRaisingEventsTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/EnableRaisingEventsTests.cs index 8cdedc980..84fd78f55 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/EnableRaisingEventsTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/EnableRaisingEventsTests.cs @@ -1,50 +1,50 @@ -using System.Threading; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class EnableRaisingEventsTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void EnableRaisingEvents_SetToFalse_ShouldStop(string path1, string path2) - { - FileSystem.Initialize().WithSubdirectory(path1).WithSubdirectory(path2); - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, _) => - { - ms.Set(); - }; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.Delete(path1); - ms.Wait(10000).Should().BeTrue(); - ms.Reset(); - - fileSystemWatcher.EnableRaisingEvents = false; - - FileSystem.Directory.Delete(path2); - ms.Wait(30).Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void EnableRaisingEvents_ShouldBeInitializedAsFalse(string path) - { - FileSystem.Initialize().WithSubdirectory(path); - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, _) => - { - ms.Set(); - }; - - FileSystem.Directory.Delete(path); - - ms.Wait(30).Should().BeFalse(); - } -} +using System.Threading; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class EnableRaisingEventsTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void EnableRaisingEvents_SetToFalse_ShouldStop(string path1, string path2) + { + FileSystem.Initialize().WithSubdirectory(path1).WithSubdirectory(path2); + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, _) => + { + ms.Set(); + }; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.Delete(path1); + ms.Wait(10000).Should().BeTrue(); + ms.Reset(); + + fileSystemWatcher.EnableRaisingEvents = false; + + FileSystem.Directory.Delete(path2); + ms.Wait(30).Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void EnableRaisingEvents_ShouldBeInitializedAsFalse(string path) + { + FileSystem.Initialize().WithSubdirectory(path); + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, _) => + { + ms.Set(); + }; + + FileSystem.Directory.Delete(path); + + ms.Wait(30).Should().BeFalse(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/FilterTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/FilterTests.cs index 0b75d5ab0..c255e21fd 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/FilterTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/FilterTests.cs @@ -1,111 +1,111 @@ -using System.IO; -using System.Threading; -#if FEATURE_FILESYSTEMWATCHER_ADVANCED -using System.Collections.Generic; -using System.Linq; -#endif - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class FilterTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void Filter_Matching_ShouldTriggerNotification(string path) - { - FileSystem.Initialize().WithSubdirectory(path); - ManualResetEventSlim ms = new(); - FileSystemEventArgs? result = null; - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.Filter = path; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.Delete(path); - ms.Wait(10000).Should().BeTrue(); - - result.Should().NotBeNull(); - result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); - result.ChangeType.Should().Be(WatcherChangeTypes.Deleted); - result.Name.Should().Be(FileSystem.Path.GetFileName(path)); - } - - [SkippableTheory] - [AutoData] - public void Filter_NotMatching_ShouldNotTriggerNotification( - string path, string filter) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize().WithSubdirectory(path); - ManualResetEventSlim ms = new(); - FileSystemEventArgs? result = null; - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.Filter = filter; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.Delete(path); - ms.Wait(30).Should().BeFalse(); - - result.Should().BeNull(); - } - -#if FEATURE_FILESYSTEMWATCHER_ADVANCED - [SkippableTheory] - [AutoData] - public void Filters_ShouldMatchAnyOfTheSpecifiedFilters( - string[] filteredPaths, string[] otherPaths) - { - foreach (string path in otherPaths.Concat(filteredPaths)) - { - FileSystem.Directory.CreateDirectory(path); - } - - CountdownEvent ms = new(filteredPaths.Length); - List results = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - results.Add(eventArgs); - ms.Signal(); - }; - foreach (string filter in filteredPaths) - { - fileSystemWatcher.Filters.Add(filter); - } - - fileSystemWatcher.EnableRaisingEvents = true; - foreach (string path in otherPaths.Concat(filteredPaths)) - { - FileSystem.Directory.Delete(path); - } - - ms.Wait(10000).Should().BeTrue(); - - foreach (string path in otherPaths) - { - results.Should() - .NotContain(f => f.FullPath == FileSystem.Path.GetFullPath(path)); - } - - foreach (string path in filteredPaths) - { - results.Should() - .Contain(f => f.FullPath == FileSystem.Path.GetFullPath(path)); - } - } -#endif -} +using System.IO; +using System.Threading; +#if FEATURE_FILESYSTEMWATCHER_ADVANCED +using System.Collections.Generic; +using System.Linq; +#endif + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class FilterTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void Filter_Matching_ShouldTriggerNotification(string path) + { + FileSystem.Initialize().WithSubdirectory(path); + ManualResetEventSlim ms = new(); + FileSystemEventArgs? result = null; + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.Filter = path; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.Delete(path); + ms.Wait(10000).Should().BeTrue(); + + result.Should().NotBeNull(); + result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); + result.ChangeType.Should().Be(WatcherChangeTypes.Deleted); + result.Name.Should().Be(FileSystem.Path.GetFileName(path)); + } + + [SkippableTheory] + [AutoData] + public void Filter_NotMatching_ShouldNotTriggerNotification( + string path, string filter) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize().WithSubdirectory(path); + ManualResetEventSlim ms = new(); + FileSystemEventArgs? result = null; + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.Filter = filter; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.Delete(path); + ms.Wait(30).Should().BeFalse(); + + result.Should().BeNull(); + } + +#if FEATURE_FILESYSTEMWATCHER_ADVANCED + [SkippableTheory] + [AutoData] + public void Filters_ShouldMatchAnyOfTheSpecifiedFilters( + string[] filteredPaths, string[] otherPaths) + { + foreach (string path in otherPaths.Concat(filteredPaths)) + { + FileSystem.Directory.CreateDirectory(path); + } + + CountdownEvent ms = new(filteredPaths.Length); + List results = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + results.Add(eventArgs); + ms.Signal(); + }; + foreach (string filter in filteredPaths) + { + fileSystemWatcher.Filters.Add(filter); + } + + fileSystemWatcher.EnableRaisingEvents = true; + foreach (string path in otherPaths.Concat(filteredPaths)) + { + FileSystem.Directory.Delete(path); + } + + ms.Wait(10000).Should().BeTrue(); + + foreach (string path in otherPaths) + { + results.Should() + .NotContain(f => f.FullPath == FileSystem.Path.GetFullPath(path)); + } + + foreach (string path in filteredPaths) + { + results.Should() + .Contain(f => f.FullPath == FileSystem.Path.GetFullPath(path)); + } + } +#endif +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/IncludeSubdirectoriesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/IncludeSubdirectoriesTests.cs index 8fc770e2e..fa60b5a26 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/IncludeSubdirectoriesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/IncludeSubdirectoriesTests.cs @@ -1,94 +1,94 @@ -using System.IO; -using System.Threading; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class IncludeSubdirectoriesTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void IncludeSubdirectories_SetToFalse_ShouldNotTriggerNotification( - string baseDirectory, string path) - { - FileSystem.Initialize() - .WithSubdirectory(baseDirectory).Initialized(s => s - .WithSubdirectory(path)); - ManualResetEventSlim ms = new(); - FileSystemEventArgs? result = null; - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.IncludeSubdirectories = false; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.Delete(FileSystem.Path.Combine(baseDirectory, path)); - ms.Wait(30).Should().BeFalse(); - - result.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void - IncludeSubdirectories_SetToTrue_ShouldOnlyTriggerNotificationOnSubdirectories( - string baseDirectory, string subdirectoryName, string otherDirectory) - { - FileSystem.Initialize() - .WithSubdirectory(baseDirectory).Initialized(s => s - .WithSubdirectory(subdirectoryName)) - .WithSubdirectory(otherDirectory); - ManualResetEventSlim ms = new(); - FileSystemEventArgs? result = null; - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(baseDirectory); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.IncludeSubdirectories = true; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.Delete(otherDirectory); - ms.Wait(30).Should().BeFalse(); - - result.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void IncludeSubdirectories_SetToTrue_ShouldTriggerNotificationOnSubdirectories( - string baseDirectory, string subdirectoryName) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem, !Test.RunsOnWindows); - - FileSystem.Initialize() - .WithSubdirectory(baseDirectory).Initialized(s => s - .WithSubdirectory(subdirectoryName)); - string subdirectoryPath = - FileSystem.Path.Combine(baseDirectory, subdirectoryName); - ManualResetEventSlim ms = new(); - FileSystemEventArgs? result = null; - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.IncludeSubdirectories = true; - fileSystemWatcher.EnableRaisingEvents = true; - FileSystem.Directory.Delete(subdirectoryPath); - ms.Wait(10000).Should().BeTrue(); - - result.Should().NotBeNull(); - result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(subdirectoryPath)); - result.Name.Should().Be(subdirectoryPath); - result!.ChangeType.Should().Be(WatcherChangeTypes.Deleted); - } -} +using System.IO; +using System.Threading; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class IncludeSubdirectoriesTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void IncludeSubdirectories_SetToFalse_ShouldNotTriggerNotification( + string baseDirectory, string path) + { + FileSystem.Initialize() + .WithSubdirectory(baseDirectory).Initialized(s => s + .WithSubdirectory(path)); + ManualResetEventSlim ms = new(); + FileSystemEventArgs? result = null; + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.IncludeSubdirectories = false; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.Delete(FileSystem.Path.Combine(baseDirectory, path)); + ms.Wait(30).Should().BeFalse(); + + result.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void + IncludeSubdirectories_SetToTrue_ShouldOnlyTriggerNotificationOnSubdirectories( + string baseDirectory, string subdirectoryName, string otherDirectory) + { + FileSystem.Initialize() + .WithSubdirectory(baseDirectory).Initialized(s => s + .WithSubdirectory(subdirectoryName)) + .WithSubdirectory(otherDirectory); + ManualResetEventSlim ms = new(); + FileSystemEventArgs? result = null; + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(baseDirectory); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.IncludeSubdirectories = true; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.Delete(otherDirectory); + ms.Wait(30).Should().BeFalse(); + + result.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void IncludeSubdirectories_SetToTrue_ShouldTriggerNotificationOnSubdirectories( + string baseDirectory, string subdirectoryName) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem, !Test.RunsOnWindows); + + FileSystem.Initialize() + .WithSubdirectory(baseDirectory).Initialized(s => s + .WithSubdirectory(subdirectoryName)); + string subdirectoryPath = + FileSystem.Path.Combine(baseDirectory, subdirectoryName); + ManualResetEventSlim ms = new(); + FileSystemEventArgs? result = null; + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.IncludeSubdirectories = true; + fileSystemWatcher.EnableRaisingEvents = true; + FileSystem.Directory.Delete(subdirectoryPath); + ms.Wait(10000).Should().BeTrue(); + + result.Should().NotBeNull(); + result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(subdirectoryPath)); + result.Name.Should().Be(subdirectoryPath); + result!.ChangeType.Should().Be(WatcherChangeTypes.Deleted); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/NotifyFiltersTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/NotifyFiltersTests.cs index 5ab06a203..927caa651 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/NotifyFiltersTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/NotifyFiltersTests.cs @@ -1,533 +1,533 @@ -using System.IO; -using System.Threading; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class NotifyFiltersTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - #region Test Setup - - /// - /// The delay in milliseconds when expecting a success in the test. - /// - private const int ExpectSuccess = 3000; - - /// - /// The delay in milliseconds when expecting a timeout in the test. - /// - private const int ExpectTimeout = 500; - - #endregion - - [SkippableTheory] - [AutoData] - public void NotifyFilter_AppendFile_ShouldNotNotifyOnOtherFilters(string fileName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize(); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Changed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = NotifyFilters.DirectoryName | - NotifyFilters.FileName; - if (!Test.RunsOnMac) - { - fileSystemWatcher.NotifyFilter |= NotifyFilters.CreationTime; - } - - if (!Test.RunsOnLinux) - { - fileSystemWatcher.NotifyFilter |= NotifyFilters.Security; - } - else - { - fileSystemWatcher.NotifyFilter |= NotifyFilters.Attributes; - } - - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.AppendAllText(fileName, "foo"); - - ms.Wait(ExpectTimeout).Should().BeFalse(); - result.Should().BeNull(); - } - - [SkippableTheory] - [InlineAutoData(NotifyFilters.CreationTime)] - [InlineAutoData(NotifyFilters.LastAccess)] - [InlineAutoData(NotifyFilters.LastWrite)] - [InlineAutoData(NotifyFilters.Security)] - [InlineAutoData(NotifyFilters.Size)] - public void NotifyFilter_AppendFile_ShouldTriggerChangedEventOnNotifyFilters( - NotifyFilters notifyFilter, string fileName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - if (!Test.RunsOnLinux) - { - Skip.If(notifyFilter == NotifyFilters.Security, - "`Security` is only set on Linux"); - } - - if (!Test.RunsOnMac) - { - Skip.If(notifyFilter == NotifyFilters.CreationTime, - "`CreationTime` is only set on MAC"); - } - - if (Test.RunsOnWindows) - { - Skip.If(notifyFilter == NotifyFilters.LastAccess, - "`LastAccess` is not consistently set on the real file system."); - } - - FileSystem.Initialize(); - FileSystem.File.WriteAllText(fileName, null); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Changed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = notifyFilter; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.AppendAllText(fileName, "foo"); - - ms.Wait(ExpectSuccess).Should().BeTrue(); - result.Should().NotBeNull(); - result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(fileName)); - result.ChangeType.Should().Be(WatcherChangeTypes.Changed); - result.Name.Should().Be(FileSystem.Path.GetFileName(fileName)); - } - - [SkippableTheory] - [AutoData] - public void NotifyFilter_CreateDirectory_ShouldNotNotifyOnOtherFilters(string path) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize(); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Created += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | - NotifyFilters.CreationTime | - NotifyFilters.FileName | - NotifyFilters.LastAccess | - NotifyFilters.LastWrite | - NotifyFilters.Security | - NotifyFilters.Size; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.Directory.CreateDirectory(path); - - ms.Wait(ExpectTimeout).Should().BeFalse(); - result.Should().BeNull(); - } - - [SkippableTheory] - [InlineAutoData(NotifyFilters.DirectoryName)] - public void NotifyFilter_CreateDirectory_ShouldTriggerCreatedEventOnNotifyFilters( - NotifyFilters notifyFilter, string path) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize(); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Created += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = notifyFilter; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.Directory.CreateDirectory(path); - - ms.Wait(ExpectSuccess).Should().BeTrue(); - result.Should().NotBeNull(); - result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); - result.ChangeType.Should().Be(WatcherChangeTypes.Created); - result.Name.Should().Be(FileSystem.Path.GetFileName(path)); - } - - [SkippableTheory] - [AutoData] - public void NotifyFilter_DeleteDirectory_ShouldNotNotifyOnOtherFilters(string path) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize().WithSubdirectory(path); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | - NotifyFilters.CreationTime | - NotifyFilters.FileName | - NotifyFilters.LastAccess | - NotifyFilters.LastWrite | - NotifyFilters.Security | - NotifyFilters.Size; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.Directory.Delete(path); - - ms.Wait(ExpectTimeout).Should().BeFalse(); - result.Should().BeNull(); - } - - [SkippableTheory] - [InlineAutoData(NotifyFilters.DirectoryName)] - public void NotifyFilter_DeleteDirectory_ShouldTriggerDeletedEventOnNotifyFilters( - NotifyFilters notifyFilter, string path) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize().WithSubdirectory(path); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = notifyFilter; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.Directory.Delete(path); - - ms.Wait(ExpectSuccess).Should().BeTrue(); - result.Should().NotBeNull(); - result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); - result.ChangeType.Should().Be(WatcherChangeTypes.Deleted); - result.Name.Should().Be(FileSystem.Path.GetFileName(path)); - } - - [SkippableTheory] - [AutoData] - public void NotifyFilter_DeleteFile_ShouldNotNotifyOnOtherFilters(string path) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize().WithFile(path); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | - NotifyFilters.CreationTime | - NotifyFilters.DirectoryName | - NotifyFilters.LastAccess | - NotifyFilters.LastWrite | - NotifyFilters.Security | - NotifyFilters.Size; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.Delete(path); - - ms.Wait(ExpectTimeout).Should().BeFalse(); - result.Should().BeNull(); - } - - [SkippableTheory] - [InlineAutoData(NotifyFilters.FileName)] - public void NotifyFilter_DeleteFile_ShouldTriggerDeletedEventOnNotifyFilters( - NotifyFilters notifyFilter, string path) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize().WithFile(path); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Deleted += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = notifyFilter; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.Delete(path); - - ms.Wait(ExpectSuccess).Should().BeTrue(); - result.Should().NotBeNull(); - result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); - result.ChangeType.Should().Be(WatcherChangeTypes.Deleted); - result.Name.Should().Be(FileSystem.Path.GetFileName(path)); - } - - [SkippableTheory] - [AutoData] - public void - NotifyFilter_MoveFile_DifferentDirectories_ShouldNotifyOnLinuxOrMac( - string sourcePath, string sourceName, - string destinationPath, string destinationName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - Skip.If(Test.RunsOnWindows); - - FileSystem.Initialize() - .WithSubdirectory(sourcePath).Initialized(s => s - .WithFile(sourceName)) - .WithSubdirectory(destinationPath); - RenamedEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Renamed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - - fileSystemWatcher.IncludeSubdirectories = true; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.Move( - FileSystem.Path.Combine(sourcePath, sourceName), - FileSystem.Path.Combine(destinationPath, destinationName)); - - ms.Wait(ExpectSuccess).Should().BeTrue(); - result.Should().NotBeNull(); - result!.ChangeType.Should().Be(WatcherChangeTypes.Renamed); - result.FullPath.Should() - .Be(FileSystem.Path.Combine(BasePath, destinationPath, destinationName)); - result.Name.Should() - .Be(FileSystem.Path.Combine(destinationPath, destinationName)); - result.OldFullPath.Should() - .Be(FileSystem.Path.Combine(BasePath, sourcePath, sourceName)); - result.OldName.Should().Be(FileSystem.Path.Combine(sourcePath, sourceName)); - } - - [SkippableTheory] - [AutoData] - public void - NotifyFilter_MoveFile_DifferentDirectories_ShouldNotNotify_OnWindows( - string sourcePath, string sourceName, - string destinationPath, string destinationName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - Skip.IfNot(Test.RunsOnWindows); - - FileSystem.Initialize() - .WithSubdirectory(sourcePath).Initialized(s => s - .WithFile(sourceName)) - .WithSubdirectory(destinationPath); - RenamedEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Renamed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - - fileSystemWatcher.IncludeSubdirectories = true; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.Move( - FileSystem.Path.Combine(sourcePath, sourceName), - FileSystem.Path.Combine(destinationPath, destinationName)); - - ms.Wait(ExpectTimeout).Should().BeFalse(); - result.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void NotifyFilter_MoveFile_ShouldNotNotifyOnOtherFilters( - string sourceName, string destinationName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize(); - FileSystem.File.WriteAllText(sourceName, null); - RenamedEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Renamed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | - NotifyFilters.CreationTime | - NotifyFilters.DirectoryName | - NotifyFilters.LastAccess | - NotifyFilters.LastWrite | - NotifyFilters.Security | - NotifyFilters.Size; - - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.Move(sourceName, destinationName); - - ms.Wait(ExpectTimeout).Should().BeFalse(); - result.Should().BeNull(); - } - - [SkippableTheory] - [InlineAutoData(NotifyFilters.FileName)] - public void NotifyFilter_MoveFile_ShouldTriggerChangedEventOnNotifyFilters( - NotifyFilters notifyFilter, string sourceName, string destinationName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize(); - FileSystem.File.WriteAllText(sourceName, "foo"); - RenamedEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Renamed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - - fileSystemWatcher.NotifyFilter = notifyFilter; - fileSystemWatcher.IncludeSubdirectories = true; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.Move(sourceName, destinationName); - - ms.Wait(ExpectSuccess).Should().BeTrue(); - result.Should().NotBeNull(); - result!.ChangeType.Should().Be(WatcherChangeTypes.Renamed); - result.FullPath.Should().Be(FileSystem.Path.GetFullPath(destinationName)); - result.Name.Should().Be(FileSystem.Path.GetFileName(destinationName)); - result.OldFullPath.Should().Be(FileSystem.Path.GetFullPath(sourceName)); - result.OldName.Should().Be(FileSystem.Path.GetFileName(sourceName)); - } - - [SkippableTheory] - [AutoData] - public void NotifyFilter_WriteFile_ShouldNotNotifyOnOtherFilters(string fileName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - FileSystem.Initialize(); - FileSystem.File.WriteAllText(fileName, null); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Changed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = NotifyFilters.DirectoryName | - NotifyFilters.FileName; - if (!Test.RunsOnMac) - { - fileSystemWatcher.NotifyFilter |= NotifyFilters.CreationTime; - } - - if (!Test.RunsOnLinux) - { - fileSystemWatcher.NotifyFilter |= NotifyFilters.Security; - } - else - { - fileSystemWatcher.NotifyFilter |= NotifyFilters.Attributes; - } - - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.WriteAllText(fileName, "foo"); - - ms.Wait(ExpectTimeout).Should().BeFalse(); - result.Should().BeNull(); - } - - [SkippableTheory] - [InlineAutoData(NotifyFilters.CreationTime)] - [InlineAutoData(NotifyFilters.LastAccess)] - [InlineAutoData(NotifyFilters.LastWrite)] - [InlineAutoData(NotifyFilters.Security)] - [InlineAutoData(NotifyFilters.Size)] - public void NotifyFilter_WriteFile_ShouldTriggerChangedEventOnNotifyFilters( - NotifyFilters notifyFilter, string fileName) - { - Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); - - if (!Test.RunsOnLinux) - { - Skip.If(notifyFilter == NotifyFilters.Security, - "`Security` is only set on Linux"); - } - - if (!Test.RunsOnMac) - { - Skip.If(notifyFilter == NotifyFilters.CreationTime, - "`CreationTime` is only set on MAC"); - } - - if (Test.RunsOnWindows) - { - Skip.If(notifyFilter == NotifyFilters.LastAccess, - "`LastAccess` is not consistently set on the real file system."); - } - - FileSystem.Initialize(); - FileSystemEventArgs? result = null; - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.Changed += (_, eventArgs) => - { - result = eventArgs; - ms.Set(); - }; - fileSystemWatcher.NotifyFilter = notifyFilter; - fileSystemWatcher.EnableRaisingEvents = true; - - FileSystem.File.WriteAllText(fileName, "foo"); - - ms.Wait(ExpectSuccess).Should().BeTrue(); - result.Should().NotBeNull(); - result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(fileName)); - result.ChangeType.Should().Be(WatcherChangeTypes.Changed); - result.Name.Should().Be(FileSystem.Path.GetFileName(fileName)); - } -} +using System.IO; +using System.Threading; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class NotifyFiltersTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + #region Test Setup + + /// + /// The delay in milliseconds when expecting a success in the test. + /// + private const int ExpectSuccess = 3000; + + /// + /// The delay in milliseconds when expecting a timeout in the test. + /// + private const int ExpectTimeout = 500; + + #endregion + + [SkippableTheory] + [AutoData] + public void NotifyFilter_AppendFile_ShouldNotNotifyOnOtherFilters(string fileName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize(); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Changed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = NotifyFilters.DirectoryName | + NotifyFilters.FileName; + if (!Test.RunsOnMac) + { + fileSystemWatcher.NotifyFilter |= NotifyFilters.CreationTime; + } + + if (!Test.RunsOnLinux) + { + fileSystemWatcher.NotifyFilter |= NotifyFilters.Security; + } + else + { + fileSystemWatcher.NotifyFilter |= NotifyFilters.Attributes; + } + + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.AppendAllText(fileName, "foo"); + + ms.Wait(ExpectTimeout).Should().BeFalse(); + result.Should().BeNull(); + } + + [SkippableTheory] + [InlineAutoData(NotifyFilters.CreationTime)] + [InlineAutoData(NotifyFilters.LastAccess)] + [InlineAutoData(NotifyFilters.LastWrite)] + [InlineAutoData(NotifyFilters.Security)] + [InlineAutoData(NotifyFilters.Size)] + public void NotifyFilter_AppendFile_ShouldTriggerChangedEventOnNotifyFilters( + NotifyFilters notifyFilter, string fileName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + if (!Test.RunsOnLinux) + { + Skip.If(notifyFilter == NotifyFilters.Security, + "`Security` is only set on Linux"); + } + + if (!Test.RunsOnMac) + { + Skip.If(notifyFilter == NotifyFilters.CreationTime, + "`CreationTime` is only set on MAC"); + } + + if (Test.RunsOnWindows) + { + Skip.If(notifyFilter == NotifyFilters.LastAccess, + "`LastAccess` is not consistently set on the real file system."); + } + + FileSystem.Initialize(); + FileSystem.File.WriteAllText(fileName, null); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Changed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.AppendAllText(fileName, "foo"); + + ms.Wait(ExpectSuccess).Should().BeTrue(); + result.Should().NotBeNull(); + result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(fileName)); + result.ChangeType.Should().Be(WatcherChangeTypes.Changed); + result.Name.Should().Be(FileSystem.Path.GetFileName(fileName)); + } + + [SkippableTheory] + [AutoData] + public void NotifyFilter_CreateDirectory_ShouldNotNotifyOnOtherFilters(string path) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize(); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Created += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | + NotifyFilters.CreationTime | + NotifyFilters.FileName | + NotifyFilters.LastAccess | + NotifyFilters.LastWrite | + NotifyFilters.Security | + NotifyFilters.Size; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.Directory.CreateDirectory(path); + + ms.Wait(ExpectTimeout).Should().BeFalse(); + result.Should().BeNull(); + } + + [SkippableTheory] + [InlineAutoData(NotifyFilters.DirectoryName)] + public void NotifyFilter_CreateDirectory_ShouldTriggerCreatedEventOnNotifyFilters( + NotifyFilters notifyFilter, string path) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize(); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Created += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.Directory.CreateDirectory(path); + + ms.Wait(ExpectSuccess).Should().BeTrue(); + result.Should().NotBeNull(); + result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); + result.ChangeType.Should().Be(WatcherChangeTypes.Created); + result.Name.Should().Be(FileSystem.Path.GetFileName(path)); + } + + [SkippableTheory] + [AutoData] + public void NotifyFilter_DeleteDirectory_ShouldNotNotifyOnOtherFilters(string path) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize().WithSubdirectory(path); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | + NotifyFilters.CreationTime | + NotifyFilters.FileName | + NotifyFilters.LastAccess | + NotifyFilters.LastWrite | + NotifyFilters.Security | + NotifyFilters.Size; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.Directory.Delete(path); + + ms.Wait(ExpectTimeout).Should().BeFalse(); + result.Should().BeNull(); + } + + [SkippableTheory] + [InlineAutoData(NotifyFilters.DirectoryName)] + public void NotifyFilter_DeleteDirectory_ShouldTriggerDeletedEventOnNotifyFilters( + NotifyFilters notifyFilter, string path) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize().WithSubdirectory(path); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.Directory.Delete(path); + + ms.Wait(ExpectSuccess).Should().BeTrue(); + result.Should().NotBeNull(); + result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); + result.ChangeType.Should().Be(WatcherChangeTypes.Deleted); + result.Name.Should().Be(FileSystem.Path.GetFileName(path)); + } + + [SkippableTheory] + [AutoData] + public void NotifyFilter_DeleteFile_ShouldNotNotifyOnOtherFilters(string path) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize().WithFile(path); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | + NotifyFilters.CreationTime | + NotifyFilters.DirectoryName | + NotifyFilters.LastAccess | + NotifyFilters.LastWrite | + NotifyFilters.Security | + NotifyFilters.Size; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.Delete(path); + + ms.Wait(ExpectTimeout).Should().BeFalse(); + result.Should().BeNull(); + } + + [SkippableTheory] + [InlineAutoData(NotifyFilters.FileName)] + public void NotifyFilter_DeleteFile_ShouldTriggerDeletedEventOnNotifyFilters( + NotifyFilters notifyFilter, string path) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize().WithFile(path); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Deleted += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.Delete(path); + + ms.Wait(ExpectSuccess).Should().BeTrue(); + result.Should().NotBeNull(); + result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(path)); + result.ChangeType.Should().Be(WatcherChangeTypes.Deleted); + result.Name.Should().Be(FileSystem.Path.GetFileName(path)); + } + + [SkippableTheory] + [AutoData] + public void + NotifyFilter_MoveFile_DifferentDirectories_ShouldNotifyOnLinuxOrMac( + string sourcePath, string sourceName, + string destinationPath, string destinationName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + Skip.If(Test.RunsOnWindows); + + FileSystem.Initialize() + .WithSubdirectory(sourcePath).Initialized(s => s + .WithFile(sourceName)) + .WithSubdirectory(destinationPath); + RenamedEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Renamed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + + fileSystemWatcher.IncludeSubdirectories = true; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.Move( + FileSystem.Path.Combine(sourcePath, sourceName), + FileSystem.Path.Combine(destinationPath, destinationName)); + + ms.Wait(ExpectSuccess).Should().BeTrue(); + result.Should().NotBeNull(); + result!.ChangeType.Should().Be(WatcherChangeTypes.Renamed); + result.FullPath.Should() + .Be(FileSystem.Path.Combine(BasePath, destinationPath, destinationName)); + result.Name.Should() + .Be(FileSystem.Path.Combine(destinationPath, destinationName)); + result.OldFullPath.Should() + .Be(FileSystem.Path.Combine(BasePath, sourcePath, sourceName)); + result.OldName.Should().Be(FileSystem.Path.Combine(sourcePath, sourceName)); + } + + [SkippableTheory] + [AutoData] + public void + NotifyFilter_MoveFile_DifferentDirectories_ShouldNotNotify_OnWindows( + string sourcePath, string sourceName, + string destinationPath, string destinationName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + Skip.IfNot(Test.RunsOnWindows); + + FileSystem.Initialize() + .WithSubdirectory(sourcePath).Initialized(s => s + .WithFile(sourceName)) + .WithSubdirectory(destinationPath); + RenamedEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Renamed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + + fileSystemWatcher.IncludeSubdirectories = true; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.Move( + FileSystem.Path.Combine(sourcePath, sourceName), + FileSystem.Path.Combine(destinationPath, destinationName)); + + ms.Wait(ExpectTimeout).Should().BeFalse(); + result.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void NotifyFilter_MoveFile_ShouldNotNotifyOnOtherFilters( + string sourceName, string destinationName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize(); + FileSystem.File.WriteAllText(sourceName, null); + RenamedEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Renamed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | + NotifyFilters.CreationTime | + NotifyFilters.DirectoryName | + NotifyFilters.LastAccess | + NotifyFilters.LastWrite | + NotifyFilters.Security | + NotifyFilters.Size; + + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.Move(sourceName, destinationName); + + ms.Wait(ExpectTimeout).Should().BeFalse(); + result.Should().BeNull(); + } + + [SkippableTheory] + [InlineAutoData(NotifyFilters.FileName)] + public void NotifyFilter_MoveFile_ShouldTriggerChangedEventOnNotifyFilters( + NotifyFilters notifyFilter, string sourceName, string destinationName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize(); + FileSystem.File.WriteAllText(sourceName, "foo"); + RenamedEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Renamed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.IncludeSubdirectories = true; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.Move(sourceName, destinationName); + + ms.Wait(ExpectSuccess).Should().BeTrue(); + result.Should().NotBeNull(); + result!.ChangeType.Should().Be(WatcherChangeTypes.Renamed); + result.FullPath.Should().Be(FileSystem.Path.GetFullPath(destinationName)); + result.Name.Should().Be(FileSystem.Path.GetFileName(destinationName)); + result.OldFullPath.Should().Be(FileSystem.Path.GetFullPath(sourceName)); + result.OldName.Should().Be(FileSystem.Path.GetFileName(sourceName)); + } + + [SkippableTheory] + [AutoData] + public void NotifyFilter_WriteFile_ShouldNotNotifyOnOtherFilters(string fileName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + FileSystem.Initialize(); + FileSystem.File.WriteAllText(fileName, null); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Changed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = NotifyFilters.DirectoryName | + NotifyFilters.FileName; + if (!Test.RunsOnMac) + { + fileSystemWatcher.NotifyFilter |= NotifyFilters.CreationTime; + } + + if (!Test.RunsOnLinux) + { + fileSystemWatcher.NotifyFilter |= NotifyFilters.Security; + } + else + { + fileSystemWatcher.NotifyFilter |= NotifyFilters.Attributes; + } + + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.WriteAllText(fileName, "foo"); + + ms.Wait(ExpectTimeout).Should().BeFalse(); + result.Should().BeNull(); + } + + [SkippableTheory] + [InlineAutoData(NotifyFilters.CreationTime)] + [InlineAutoData(NotifyFilters.LastAccess)] + [InlineAutoData(NotifyFilters.LastWrite)] + [InlineAutoData(NotifyFilters.Security)] + [InlineAutoData(NotifyFilters.Size)] + public void NotifyFilter_WriteFile_ShouldTriggerChangedEventOnNotifyFilters( + NotifyFilters notifyFilter, string fileName) + { + Test.SkipIfLongRunningTestsShouldBeSkipped(FileSystem); + + if (!Test.RunsOnLinux) + { + Skip.If(notifyFilter == NotifyFilters.Security, + "`Security` is only set on Linux"); + } + + if (!Test.RunsOnMac) + { + Skip.If(notifyFilter == NotifyFilters.CreationTime, + "`CreationTime` is only set on MAC"); + } + + if (Test.RunsOnWindows) + { + Skip.If(notifyFilter == NotifyFilters.LastAccess, + "`LastAccess` is not consistently set on the real file system."); + } + + FileSystem.Initialize(); + FileSystemEventArgs? result = null; + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.Changed += (_, eventArgs) => + { + result = eventArgs; + ms.Set(); + }; + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.WriteAllText(fileName, "foo"); + + ms.Wait(ExpectSuccess).Should().BeTrue(); + result.Should().NotBeNull(); + result!.FullPath.Should().Be(FileSystem.Path.GetFullPath(fileName)); + result.ChangeType.Should().Be(WatcherChangeTypes.Changed); + result.Name.Should().Be(FileSystem.Path.GetFileName(fileName)); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/PathTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/PathTests.cs index 3c7a2d2f5..dd4e406fa 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/PathTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/PathTests.cs @@ -1,5 +1,3 @@ -using Testably.Abstractions.FileSystem; - namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; // ReSharper disable once PartialTypeWithSinglePart diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs index b9a57cce5..a22838a4a 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs @@ -1,165 +1,165 @@ -using Moq; -using System.ComponentModel; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void BeginInit_ShouldStopListening(string path) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - FileSystem.Initialize(); - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.EnableRaisingEvents = true; - - fileSystemWatcher.BeginInit(); - - fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); - try - { - Task.Run(() => - { - while (!ms.IsSet) - { - Thread.Sleep(10); - FileSystem.Directory.CreateDirectory(path); - FileSystem.Directory.Delete(path); - } - }); - IWaitForChangedResult result = - fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created, 1000); - - fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); - result.TimedOut.Should().BeTrue(); - result.ChangeType.Should().Be(0); - result.Name.Should().BeNull(); - result.OldName.Should().BeNull(); - } - finally - { - ms.Set(); - } - } - - [SkippableFact] - public void Container_ShouldBeInitializedWithNull() - { - FileSystem.Initialize(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - - fileSystemWatcher.Container.Should().BeNull(); - } - - [SkippableTheory] - [AutoData] - public void EndInit_ShouldRestartListening(string path) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - FileSystem.Initialize(); - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - fileSystemWatcher.EnableRaisingEvents = true; - fileSystemWatcher.BeginInit(); - - fileSystemWatcher.EndInit(); - - fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); - try - { - Task.Run(() => - { - while (!ms.IsSet) - { - Thread.Sleep(10); - FileSystem.Directory.CreateDirectory(path); - FileSystem.Directory.Delete(path); - } - }); - IWaitForChangedResult result = - fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created, 100); - - fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); - result.TimedOut.Should().BeFalse(); - } - finally - { - ms.Set(); - } - } - - [SkippableTheory] - [InlineData(-1, 4096)] - [InlineData(4095, 4096)] - [InlineData(4097, 4097)] - public void InternalBufferSize_ShouldAtLeastHave4096Bytes( - int bytes, int expectedBytes) - { - FileSystem.Initialize(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - - fileSystemWatcher.InternalBufferSize = bytes; - - fileSystemWatcher.InternalBufferSize.Should().Be(expectedBytes); - } - - [SkippableFact] - public void Site_ShouldBeInitializedWithNull() - { - FileSystem.Initialize(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - - fileSystemWatcher.Site.Should().BeNull(); - } - - [SkippableFact] - public void Site_ShouldBeWritable() - { - ISite? site = new Mock().Object; - FileSystem.Initialize(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - - fileSystemWatcher.Site = site; - - fileSystemWatcher.Site.Should().Be(site); - } - - [SkippableFact] - public void SynchronizingObject_ShouldBeInitializedWithNull() - { - FileSystem.Initialize(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - - fileSystemWatcher.SynchronizingObject.Should().BeNull(); - } - - [SkippableFact] - public void SynchronizingObject_ShouldBeWritable() - { - ISynchronizeInvoke? synchronizingObject = new Mock().Object; - FileSystem.Initialize(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - - fileSystemWatcher.SynchronizingObject = synchronizingObject; - - fileSystemWatcher.SynchronizingObject.Should().Be(synchronizingObject); - } -} +using Moq; +using System.ComponentModel; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void BeginInit_ShouldStopListening(string path) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + FileSystem.Initialize(); + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.EnableRaisingEvents = true; + + fileSystemWatcher.BeginInit(); + + fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); + try + { + Task.Run(() => + { + while (!ms.IsSet) + { + Thread.Sleep(10); + FileSystem.Directory.CreateDirectory(path); + FileSystem.Directory.Delete(path); + } + }); + IWaitForChangedResult result = + fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created, 1000); + + fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); + result.TimedOut.Should().BeTrue(); + result.ChangeType.Should().Be(0); + result.Name.Should().BeNull(); + result.OldName.Should().BeNull(); + } + finally + { + ms.Set(); + } + } + + [SkippableFact] + public void Container_ShouldBeInitializedWithNull() + { + FileSystem.Initialize(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + + fileSystemWatcher.Container.Should().BeNull(); + } + + [SkippableTheory] + [AutoData] + public void EndInit_ShouldRestartListening(string path) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + FileSystem.Initialize(); + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + fileSystemWatcher.EnableRaisingEvents = true; + fileSystemWatcher.BeginInit(); + + fileSystemWatcher.EndInit(); + + fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); + try + { + Task.Run(() => + { + while (!ms.IsSet) + { + Thread.Sleep(10); + FileSystem.Directory.CreateDirectory(path); + FileSystem.Directory.Delete(path); + } + }); + IWaitForChangedResult result = + fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created, 100); + + fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); + result.TimedOut.Should().BeFalse(); + } + finally + { + ms.Set(); + } + } + + [SkippableTheory] + [InlineData(-1, 4096)] + [InlineData(4095, 4096)] + [InlineData(4097, 4097)] + public void InternalBufferSize_ShouldAtLeastHave4096Bytes( + int bytes, int expectedBytes) + { + FileSystem.Initialize(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + + fileSystemWatcher.InternalBufferSize = bytes; + + fileSystemWatcher.InternalBufferSize.Should().Be(expectedBytes); + } + + [SkippableFact] + public void Site_ShouldBeInitializedWithNull() + { + FileSystem.Initialize(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + + fileSystemWatcher.Site.Should().BeNull(); + } + + [SkippableFact] + public void Site_ShouldBeWritable() + { + ISite? site = new Mock().Object; + FileSystem.Initialize(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + + fileSystemWatcher.Site = site; + + fileSystemWatcher.Site.Should().Be(site); + } + + [SkippableFact] + public void SynchronizingObject_ShouldBeInitializedWithNull() + { + FileSystem.Initialize(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + + fileSystemWatcher.SynchronizingObject.Should().BeNull(); + } + + [SkippableFact] + public void SynchronizingObject_ShouldBeWritable() + { + ISynchronizeInvoke? synchronizingObject = new Mock().Object; + FileSystem.Initialize(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + + fileSystemWatcher.SynchronizingObject = synchronizingObject; + + fileSystemWatcher.SynchronizingObject.Should().Be(synchronizingObject); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/WaitForChangedTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/WaitForChangedTests.cs index 8652af60d..878645966 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/WaitForChangedTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/WaitForChangedTests.cs @@ -1,88 +1,88 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class WaitForChangedTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableTheory] - [AutoData] - public void WaitForChanged_ShouldBlockUntilEventHappens(string path) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - FileSystem.Initialize(); - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - try - { - Task.Run(() => - { - while (!ms.IsSet) - { - Thread.Sleep(10); - FileSystem.Directory.CreateDirectory(path); - FileSystem.Directory.Delete(path); - } - }); - - using (CancellationTokenSource cts = new(5000)) - { - cts.Token.Register(() => throw new TimeoutException()); - IWaitForChangedResult result = - fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created); - fileSystemWatcher.EnableRaisingEvents.Should().BeFalse(); - result.TimedOut.Should().BeFalse(); - result.ChangeType.Should().Be(WatcherChangeTypes.Created); - result.Name.Should().Be(path); - result.OldName.Should().BeNull(); - } - } - finally - { - ms.Set(); - } - } - - [SkippableTheory] - [AutoData] - public void WaitForChanged_Timeout_ShouldReturnTimedOut(string path) - { - Test.SkipBrittleTestsOnRealFileSystem(FileSystem); - - FileSystem.Initialize(); - ManualResetEventSlim ms = new(); - IFileSystemWatcher fileSystemWatcher = - FileSystem.FileSystemWatcher.New(BasePath); - try - { - fileSystemWatcher.EnableRaisingEvents = true; - Task.Run(() => - { - while (!ms.IsSet) - { - Thread.Sleep(10); - FileSystem.Directory.CreateDirectory(path); - FileSystem.Directory.Delete(path); - } - }); - IWaitForChangedResult result = - fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Changed, 100); - - fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); - result.TimedOut.Should().BeTrue(); - result.ChangeType.Should().Be(0); - result.Name.Should().BeNull(); - result.OldName.Should().BeNull(); - } - finally - { - ms.Set(); - } - } -} +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class WaitForChangedTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableTheory] + [AutoData] + public void WaitForChanged_ShouldBlockUntilEventHappens(string path) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + FileSystem.Initialize(); + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + try + { + Task.Run(() => + { + while (!ms.IsSet) + { + Thread.Sleep(10); + FileSystem.Directory.CreateDirectory(path); + FileSystem.Directory.Delete(path); + } + }); + + using (CancellationTokenSource cts = new(5000)) + { + cts.Token.Register(() => throw new TimeoutException()); + IWaitForChangedResult result = + fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Created); + fileSystemWatcher.EnableRaisingEvents.Should().BeFalse(); + result.TimedOut.Should().BeFalse(); + result.ChangeType.Should().Be(WatcherChangeTypes.Created); + result.Name.Should().Be(path); + result.OldName.Should().BeNull(); + } + } + finally + { + ms.Set(); + } + } + + [SkippableTheory] + [AutoData] + public void WaitForChanged_Timeout_ShouldReturnTimedOut(string path) + { + Test.SkipBrittleTestsOnRealFileSystem(FileSystem); + + FileSystem.Initialize(); + ManualResetEventSlim ms = new(); + IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(BasePath); + try + { + fileSystemWatcher.EnableRaisingEvents = true; + Task.Run(() => + { + while (!ms.IsSet) + { + Thread.Sleep(10); + FileSystem.Directory.CreateDirectory(path); + FileSystem.Directory.Delete(path); + } + }); + IWaitForChangedResult result = + fileSystemWatcher.WaitForChanged(WatcherChangeTypes.Changed, 100); + + fileSystemWatcher.EnableRaisingEvents.Should().BeTrue(); + result.TimedOut.Should().BeTrue(); + result.ChangeType.Should().Be(0); + result.Name.Should().BeNull(); + result.OldName.Should().BeNull(); + } + finally + { + ms.Set(); + } + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/ExceptionTests.cs index b49d5bc73..8087e97c8 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/ExceptionTests.cs @@ -1,122 +1,122 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcherFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class ExceptionTests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [Theory] - [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), parameters: "")] - public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileSystemWatcher); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), parameters: " ")] - public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Skip.IfNot(Test.RunsOnWindows); - - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileSystemWatcher); - }); - - exception.Should().BeException( - hResult: -2147024809, - paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, - because: - $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [Theory] - [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), parameters: (string?)null)] - public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileSystemWatcher); - }); - - exception.Should().BeException( - paramName: ignoreParamCheck ? null : paramName, - because: - $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); - } - - [SkippableTheory] - [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), - parameters: "Illegal\tCharacter?InPath")] - public void - Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( - Expression> callback, string paramName, - bool ignoreParamCheck) - { - Exception? exception = Record.Exception(() => - { - callback.Compile().Invoke(FileSystem.FileSystemWatcher); - }); - - if (!Test.RunsOnWindows) - { - if (exception is IOException ioException) - { - ioException.HResult.Should().NotBe(-2147024809, - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - else - { - exception.Should().BeException( - hResult: -2147024809, - because: - $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); - } - } - - #region Helpers - - public static IEnumerable GetFileSystemWatcherFactoryCallbacks( - string? path) - => GetFileSystemWatcherFactoryCallbackTestParameters(path!) - .Where(item => item.TestType.HasFlag(path.ToTestType())) - .Select(item => new object?[] - { - item.Callback, - item.ParamName, - item.TestType.HasFlag(ExceptionTestHelper.TestTypes.IgnoreParamNameCheck) - }); - - private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, - Expression> Callback)> - GetFileSystemWatcherFactoryCallbackTestParameters(string value) - { - yield return (ExceptionTestHelper.TestTypes.All, "path", fileSystemWatcherFactory - => fileSystemWatcherFactory.New(value)); - yield return (ExceptionTestHelper.TestTypes.All, "path", fileSystemWatcherFactory - => fileSystemWatcherFactory.New(value, "*")); - } - - #endregion -} +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcherFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class ExceptionTests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [Theory] + [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), parameters: "")] + public void Operations_WhenValueIsEmpty_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileSystemWatcher); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has empty parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), parameters: " ")] + public void Operations_WhenValueIsWhitespace_ShouldThrowArgumentException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Skip.IfNot(Test.RunsOnWindows); + + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileSystemWatcher); + }); + + exception.Should().BeException( + hResult: -2147024809, + paramName: ignoreParamCheck || Test.IsNetFramework ? null : paramName, + because: + $"\n{callback}\n has whitespace parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [Theory] + [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), parameters: (string?)null)] + public void Operations_WhenValueIsNull_ShouldThrowArgumentNullException( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileSystemWatcher); + }); + + exception.Should().BeException( + paramName: ignoreParamCheck ? null : paramName, + because: + $"\n{callback}\n has `null` parameter for '{paramName}' (ignored: {ignoreParamCheck})"); + } + + [SkippableTheory] + [MemberData(nameof(GetFileSystemWatcherFactoryCallbacks), + parameters: "Illegal\tCharacter?InPath")] + public void + Operations_WhenValueContainsIllegalPathCharacters_ShouldThrowCorrectException_OnWindows( + Expression> callback, string paramName, + bool ignoreParamCheck) + { + Exception? exception = Record.Exception(() => + { + callback.Compile().Invoke(FileSystem.FileSystemWatcher); + }); + + if (!Test.RunsOnWindows) + { + if (exception is IOException ioException) + { + ioException.HResult.Should().NotBe(-2147024809, + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + else + { + exception.Should().BeException( + hResult: -2147024809, + because: + $"\n{callback}\n contains invalid path characters for '{paramName}' (ignored: {ignoreParamCheck})"); + } + } + + #region Helpers + + public static IEnumerable GetFileSystemWatcherFactoryCallbacks( + string? path) + => GetFileSystemWatcherFactoryCallbackTestParameters(path!) + .Where(item => item.TestType.HasFlag(path.ToTestType())) + .Select(item => new object?[] + { + item.Callback, + item.ParamName, + item.TestType.HasFlag(ExceptionTestHelper.TestTypes.IgnoreParamNameCheck) + }); + + private static IEnumerable<(ExceptionTestHelper.TestTypes TestType, string? ParamName, + Expression> Callback)> + GetFileSystemWatcherFactoryCallbackTestParameters(string value) + { + yield return (ExceptionTestHelper.TestTypes.All, "path", fileSystemWatcherFactory + => fileSystemWatcherFactory.New(value)); + yield return (ExceptionTestHelper.TestTypes.All, "path", fileSystemWatcherFactory + => fileSystemWatcherFactory.New(value, "*")); + } + + #endregion +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/Tests.cs index 89cc2680e..3938c6e60 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcherFactory/Tests.cs @@ -1,78 +1,78 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcherFactory; - -// ReSharper disable once PartialTypeWithSinglePart -public abstract partial class Tests - : FileSystemTestBase - where TFileSystem : IFileSystem -{ - [SkippableFact] - public void New_ShouldInitializeWithDefaultValues() - { - IFileSystemWatcher result = - FileSystem.FileSystemWatcher.New(); - - result.Path.Should().Be(""); -#if NETFRAMEWORK - result.Filter.Should().Be("*.*"); -#else - result.Filter.Should().Be("*"); -#endif - result.IncludeSubdirectories.Should().BeFalse(); - result.InternalBufferSize.Should().Be(8192); - result.NotifyFilter.Should().Be(NotifyFilters.FileName | - NotifyFilters.DirectoryName | - NotifyFilters.LastWrite); - result.EnableRaisingEvents.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void New_WithPath_ShouldInitializeWithDefaultValues(string path) - { - FileSystem.Directory.CreateDirectory(path); - IFileSystemWatcher result = - FileSystem.FileSystemWatcher.New(path); - - result.Path.Should().Be(path); -#if NETFRAMEWORK - result.Filter.Should().Be("*.*"); -#else - result.Filter.Should().Be("*"); -#endif - result.IncludeSubdirectories.Should().BeFalse(); - result.InternalBufferSize.Should().Be(8192); - result.NotifyFilter.Should().Be(NotifyFilters.FileName | - NotifyFilters.DirectoryName | - NotifyFilters.LastWrite); - result.EnableRaisingEvents.Should().BeFalse(); - } - - [SkippableTheory] - [AutoData] - public void New_WithPathAndFilter_ShouldInitializeWithDefaultValues( - string path, string filter) - { - FileSystem.Directory.CreateDirectory(path); - IFileSystemWatcher result = - FileSystem.FileSystemWatcher.New(path, filter); - - result.Path.Should().Be(path); - result.Filter.Should().Be(filter); - result.IncludeSubdirectories.Should().BeFalse(); - result.InternalBufferSize.Should().Be(8192); - result.NotifyFilter.Should().Be(NotifyFilters.FileName | - NotifyFilters.DirectoryName | - NotifyFilters.LastWrite); - result.EnableRaisingEvents.Should().BeFalse(); - } - - [SkippableFact] - public void Wrap_Null_ShouldReturnNull() - { - IFileSystemWatcher? result = FileSystem.FileSystemWatcher.Wrap(null); - - result.Should().BeNull(); - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcherFactory; + +// ReSharper disable once PartialTypeWithSinglePart +public abstract partial class Tests + : FileSystemTestBase + where TFileSystem : IFileSystem +{ + [SkippableFact] + public void New_ShouldInitializeWithDefaultValues() + { + IFileSystemWatcher result = + FileSystem.FileSystemWatcher.New(); + + result.Path.Should().Be(""); +#if NETFRAMEWORK + result.Filter.Should().Be("*.*"); +#else + result.Filter.Should().Be("*"); +#endif + result.IncludeSubdirectories.Should().BeFalse(); + result.InternalBufferSize.Should().Be(8192); + result.NotifyFilter.Should().Be(NotifyFilters.FileName | + NotifyFilters.DirectoryName | + NotifyFilters.LastWrite); + result.EnableRaisingEvents.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void New_WithPath_ShouldInitializeWithDefaultValues(string path) + { + FileSystem.Directory.CreateDirectory(path); + IFileSystemWatcher result = + FileSystem.FileSystemWatcher.New(path); + + result.Path.Should().Be(path); +#if NETFRAMEWORK + result.Filter.Should().Be("*.*"); +#else + result.Filter.Should().Be("*"); +#endif + result.IncludeSubdirectories.Should().BeFalse(); + result.InternalBufferSize.Should().Be(8192); + result.NotifyFilter.Should().Be(NotifyFilters.FileName | + NotifyFilters.DirectoryName | + NotifyFilters.LastWrite); + result.EnableRaisingEvents.Should().BeFalse(); + } + + [SkippableTheory] + [AutoData] + public void New_WithPathAndFilter_ShouldInitializeWithDefaultValues( + string path, string filter) + { + FileSystem.Directory.CreateDirectory(path); + IFileSystemWatcher result = + FileSystem.FileSystemWatcher.New(path, filter); + + result.Path.Should().Be(path); + result.Filter.Should().Be(filter); + result.IncludeSubdirectories.Should().BeFalse(); + result.InternalBufferSize.Should().Be(8192); + result.NotifyFilter.Should().Be(NotifyFilters.FileName | + NotifyFilters.DirectoryName | + NotifyFilters.LastWrite); + result.EnableRaisingEvents.Should().BeFalse(); + } + + [SkippableFact] + public void Wrap_Null_ShouldReturnNull() + { + IFileSystemWatcher? result = FileSystem.FileSystemWatcher.Wrap(null); + + result.Should().BeNull(); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Path/GetRelativePathTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Path/GetRelativePathTests.cs index 43d36fa0d..45ffebc32 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Path/GetRelativePathTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Path/GetRelativePathTests.cs @@ -1,5 +1,4 @@ #if FEATURE_PATH_RELATIVE - namespace Testably.Abstractions.Tests.FileSystem.Path; // ReSharper disable once PartialTypeWithSinglePart diff --git a/Tests/Testably.Abstractions.Tests/RandomSystem/GuidTests.cs b/Tests/Testably.Abstractions.Tests/RandomSystem/GuidTests.cs index 02b289f08..0a1dbc54a 100644 --- a/Tests/Testably.Abstractions.Tests/RandomSystem/GuidTests.cs +++ b/Tests/Testably.Abstractions.Tests/RandomSystem/GuidTests.cs @@ -1,5 +1,4 @@ using System.Collections.Concurrent; -using System.Collections.Generic; using System.Threading.Tasks; namespace Testably.Abstractions.Tests.RandomSystem; diff --git a/Tests/Testably.Abstractions.Tests/TestHelpers/FileTestHelper.cs b/Tests/Testably.Abstractions.Tests/TestHelpers/FileTestHelper.cs index 92ee7a489..7ee2b2bd4 100644 --- a/Tests/Testably.Abstractions.Tests/TestHelpers/FileTestHelper.cs +++ b/Tests/Testably.Abstractions.Tests/TestHelpers/FileTestHelper.cs @@ -1,80 +1,80 @@ -using System.IO; - -namespace Testably.Abstractions.Tests.TestHelpers; - -public static class FileTestHelper -{ - /// - /// The minimum delay to test if times were adjusted in the real file system. - /// - internal static readonly TimeSpan AdjustTimesDelay = TimeSpan.FromMilliseconds(1500); - - /// - /// The default time returned by the file system if no time has been set. - /// : - /// A file time is a 64-bit value that represents the number of 100-nanosecond intervals that have elapsed - /// since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC). - /// - internal static readonly DateTime NullTime = new(1601, 1, 1, 0, 0, 0, - DateTimeKind.Utc); - - public static FileAccess CheckFileAccess(FileSystemStream stream) - { - FileAccess fileAccess = 0; - if (stream.CanRead) - { - fileAccess |= FileAccess.Read; - } - - if (stream.CanWrite) - { - fileAccess |= FileAccess.Write; - } - - return fileAccess; - } - - public static FileShare CheckFileShare(IFileSystem fileSystem, string path) - { - FileShare fileShare = FileShare.None; - Exception? exception = Record.Exception(() => - { - fileSystem.File.Open( - path, - FileMode.Open, - FileAccess.Read, - FileShare.ReadWrite) - .Dispose(); - }); - if (exception == null) - { - fileShare |= FileShare.Read; - } - - exception = Record.Exception(() => - { - fileSystem.File.Open( - path, - FileMode.Open, - FileAccess.Write, - FileShare.ReadWrite) - .Dispose(); - }); - if (exception == null) - { - fileShare |= FileShare.Write; - } - - return fileShare; - } - - public static string RootDrive(string path = "", char driveLetter = 'C') - { - if (Test.RunsOnWindows) - { - return $"{driveLetter}:\\{path}"; - } - - return "/" + path; - } -} +using System.IO; + +namespace Testably.Abstractions.Tests.TestHelpers; + +public static class FileTestHelper +{ + /// + /// The minimum delay to test if times were adjusted in the real file system. + /// + internal static readonly TimeSpan AdjustTimesDelay = TimeSpan.FromMilliseconds(1500); + + /// + /// The default time returned by the file system if no time has been set. + /// : + /// A file time is a 64-bit value that represents the number of 100-nanosecond intervals that have elapsed + /// since 12:00 A.M. January 1, 1601 Coordinated Universal Time (UTC). + /// + internal static readonly DateTime NullTime = new(1601, 1, 1, 0, 0, 0, + DateTimeKind.Utc); + + public static FileAccess CheckFileAccess(FileSystemStream stream) + { + FileAccess fileAccess = 0; + if (stream.CanRead) + { + fileAccess |= FileAccess.Read; + } + + if (stream.CanWrite) + { + fileAccess |= FileAccess.Write; + } + + return fileAccess; + } + + public static FileShare CheckFileShare(IFileSystem fileSystem, string path) + { + FileShare fileShare = FileShare.None; + Exception? exception = Record.Exception(() => + { + fileSystem.File.Open( + path, + FileMode.Open, + FileAccess.Read, + FileShare.ReadWrite) + .Dispose(); + }); + if (exception == null) + { + fileShare |= FileShare.Read; + } + + exception = Record.Exception(() => + { + fileSystem.File.Open( + path, + FileMode.Open, + FileAccess.Write, + FileShare.ReadWrite) + .Dispose(); + }); + if (exception == null) + { + fileShare |= FileShare.Write; + } + + return fileShare; + } + + public static string RootDrive(string path = "", char driveLetter = 'C') + { + if (Test.RunsOnWindows) + { + return $"{driveLetter}:\\{path}"; + } + + return "/" + path; + } +} diff --git a/Tests/Testably.Abstractions.Tests/TestHelpers/Polyfills/StringExtensions.cs b/Tests/Testably.Abstractions.Tests/TestHelpers/Polyfills/StringExtensions.cs index b2978196f..ca50e2f26 100644 --- a/Tests/Testably.Abstractions.Tests/TestHelpers/Polyfills/StringExtensions.cs +++ b/Tests/Testably.Abstractions.Tests/TestHelpers/Polyfills/StringExtensions.cs @@ -5,11 +5,11 @@ namespace System; public static class StringExtensions { public static bool Contains(this string @this, string value, - StringComparison comparisonType) + StringComparison comparisonType) { -#pragma warning disable CA2249 // Consider using 'string.Contains' instead of 'string.IndexOf'... this is the implementation of Contains! + #pragma warning disable CA2249 // Consider using 'string.Contains' instead of 'string.IndexOf'... this is the implementation of Contains! return @this.IndexOf(value, comparisonType) >= 0; -#pragma warning restore CA2249 + #pragma warning restore CA2249 } } #endif From 4c59edd3a5858c6b84ded9903df4739e501beee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Tue, 15 Nov 2022 19:44:57 +0100 Subject: [PATCH 7/9] Adapt examples to renaming --- .../AccessControlLists/CustomAccessControlStrategy.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Examples/AccessControlLists/AccessControlLists/CustomAccessControlStrategy.cs b/Examples/AccessControlLists/AccessControlLists/CustomAccessControlStrategy.cs index 41107f1ed..6c859079c 100644 --- a/Examples/AccessControlLists/AccessControlLists/CustomAccessControlStrategy.cs +++ b/Examples/AccessControlLists/AccessControlLists/CustomAccessControlStrategy.cs @@ -13,7 +13,7 @@ public CustomAccessControlStrategy(Func callback) _callback = callback; } - /// - public bool IsAccessGranted(string fullPath, IFileSystemExtensionContainer extensionContainer) + /// + public bool IsAccessGranted(string fullPath, IFileSystemExtensibility extensibility) => _callback(fullPath); } From ab5dffe5b52ada21e3f370619b66ef3bc5561ae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Tue, 15 Nov 2022 19:56:33 +0100 Subject: [PATCH 8/9] Fix incorrectly removed usings during Cleanup --- .../FileSystem/File/ExceptionMissingFileTests.cs | 3 +++ .../FileSystem/File/ExceptionTests.cs | 3 +++ Tests/Testably.Abstractions.Tests/RandomSystem/GuidTests.cs | 3 +++ 3 files changed, 9 insertions(+) diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionMissingFileTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionMissingFileTests.cs index d26a2a66b..835ae7394 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionMissingFileTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionMissingFileTests.cs @@ -3,6 +3,9 @@ using System.Linq; using System.Linq.Expressions; using System.Text; +#if FEATURE_FILESYSTEM_ASYNC +using System.Threading; +#endif namespace Testably.Abstractions.Tests.FileSystem.File; diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionTests.cs index 95387735c..4a5d0eb3c 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/ExceptionTests.cs @@ -3,6 +3,9 @@ using System.Linq; using System.Linq.Expressions; using System.Text; +#if FEATURE_FILESYSTEM_ASYNC +using System.Threading; +#endif namespace Testably.Abstractions.Tests.FileSystem.File; diff --git a/Tests/Testably.Abstractions.Tests/RandomSystem/GuidTests.cs b/Tests/Testably.Abstractions.Tests/RandomSystem/GuidTests.cs index 0a1dbc54a..9997fede5 100644 --- a/Tests/Testably.Abstractions.Tests/RandomSystem/GuidTests.cs +++ b/Tests/Testably.Abstractions.Tests/RandomSystem/GuidTests.cs @@ -1,4 +1,7 @@ using System.Collections.Concurrent; +#if FEATURE_GUID_PARSE +using System.Collections.Generic; +#endif using System.Threading.Tasks; namespace Testably.Abstractions.Tests.RandomSystem; From 8b915246e9632b89bb166656209bdef68c66ca79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Tue, 15 Nov 2022 20:07:08 +0100 Subject: [PATCH 9/9] Fix incorrectly defined constants --- Feature.Flags.props | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Feature.Flags.props b/Feature.Flags.props index 79753d072..3ae357710 100644 --- a/Feature.Flags.props +++ b/Feature.Flags.props @@ -2,10 +2,7 @@ $(DefineConstants);NETFRAMEWORK - - $(DefineConstants);FEATURE_FILESYSTEM_ASYNC;FEATURE_FILESYSTEM_ENUMERATION_OPTIONS;FEATURE_PATH_JOIN;FEATURE_PATH_RELATIVE;FEATURE_SPAN;FEATURE_GUID_PARSE;FEATURE_VALUETASK;FEATURE_COMPRESSION_OVERWRITE;FEATURE_COMPRESSION_ADVANCED - + $(DefineConstants);FEATURE_FILESYSTEM_ASYNC;FEATURE_FILESYSTEM_ENUMERATION_OPTIONS;FEATURE_PATH_JOIN;FEATURE_PATH_RELATIVE;FEATURE_SPAN;FEATURE_GUID_PARSE;FEATURE_VALUETASK;FEATURE_COMPRESSION_OVERWRITE;FEATURE_COMPRESSION_ADVANCED $(DefineConstants);FEATURE_FILESYSTEM_STREAM_OPTIONS;FEATURE_FILESYSTEM_LINK;FEATURE_PATH_ADVANCED;FEATURE_FILE_MOVETO_OVERWRITE;FEATURE_RANDOM_ADVANCED;FEATURE_FILESYSTEMWATCHER_ADVANCED;FEATURE_EXCEPTION_HRESULT $(DefineConstants);FEATURE_ZIPFILE_NET7;FEATURE_FILESYSTEM_NET7;FEATURE_FILESYSTEM_SAFEFILEHANDLE;FEATURE_FILESYSTEM_UNIXFILEMODE;FEATURE_GUID_FORMATPROVIDER