diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 28817cde9..3cca35aef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,6 +25,7 @@ jobs: 6.0.x 8.0.x 9.0.x + 10.0.x - name: Run unit tests (windows) if: matrix.os == 'windows-latest' run: ./build.ps1 CodeCoverage @@ -56,6 +57,7 @@ jobs: 6.0.x 8.0.x 9.0.x + 10.0.x - name: API checks run: ./build.sh ApiChecks - name: Upload artifacts @@ -85,6 +87,7 @@ jobs: 6.0.x 8.0.x 9.0.x + 10.0.x - name: Run sonarcloud analysis run: ./build.sh CodeAnalysis @@ -124,6 +127,7 @@ jobs: 6.0.x 8.0.x 9.0.x + 10.0.x - name: Pack nuget packages run: ./build.sh Pack - name: Upload packages diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0e694216..f86e3e3b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ jobs: 6.0.x 8.0.x 9.0.x + 10.0.x - name: Run unit tests (windows) if: matrix.os == 'windows-latest' run: ./build.ps1 CodeCoverage @@ -55,6 +56,7 @@ jobs: 6.0.x 8.0.x 9.0.x + 10.0.x - name: API checks run: ./build.sh ApiChecks - name: Upload artifacts @@ -86,6 +88,7 @@ jobs: 6.0.x 8.0.x 9.0.x + 10.0.x - name: Run mutation tests run: ./build.sh MutationTestsLinux MutationComment env: @@ -111,6 +114,7 @@ jobs: 6.0.x 8.0.x 9.0.x + 10.0.x - name: Run mutation tests run: ./build.ps1 MutationTestsWindows MutationComment env: @@ -135,6 +139,7 @@ jobs: 6.0.x 8.0.x 9.0.x + 10.0.x - name: Run sonarcloud analysis run: ./build.sh CodeAnalysis diff --git a/Directory.Packages.props b/Directory.Packages.props index 87d052ca3..8a0e60e9b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -10,10 +10,10 @@ - + - + @@ -47,8 +47,8 @@ - - + + diff --git a/Feature.Flags.props b/Feature.Flags.props index 96c24686f..40304cde5 100644 --- a/Feature.Flags.props +++ b/Feature.Flags.props @@ -1,10 +1,11 @@ - 1 - 1 - 1 - 1 + 1 + 1 + 1 + 1 + 1 $(DefineConstants);NETFRAMEWORK $(DefineConstants);CAN_SIMULATE_OTHER_OS @@ -35,6 +36,9 @@ $(DefineConstants);FEATURE_PATH_SPAN $(DefineConstants);FEATURE_FILE_SPAN $(DefineConstants);FEATURE_GUID_V7 + $(DefineConstants);FEATURE_GUID_PARSE_UTF8 + $(DefineConstants);FEATURE_RANDOM_STRINGS + $(DefineConstants);FEATURE_COMPRESSION_ASYNC diff --git a/Pipeline/Build.ApiChecks.cs b/Pipeline/Build.ApiChecks.cs index 43ea1a8a3..79535423a 100644 --- a/Pipeline/Build.ApiChecks.cs +++ b/Pipeline/Build.ApiChecks.cs @@ -13,11 +13,16 @@ partial class Build .DependsOn(Compile) .Executes(() => { - Project[] projects = - [ - Solution.Tests.Api.Testably_Abstractions_Api_Tests, - Solution.Tests.Api.Testably_Abstractions_Core_Api_Tests, - ]; + Project[] projects = BuildScope switch + { + BuildScope.CoreOnly => [Solution.Tests.Api.Testably_Abstractions_Core_Api_Tests,], + BuildScope.MainOnly => [Solution.Tests.Api.Testably_Abstractions_Api_Tests,], + _ => + [ + Solution.Tests.Api.Testably_Abstractions_Api_Tests, + Solution.Tests.Api.Testably_Abstractions_Core_Api_Tests, + ], + }; DotNetTest(s => s .SetConfiguration(Configuration) diff --git a/Pipeline/Build.CodeAnalysis.cs b/Pipeline/Build.CodeAnalysis.cs index f0df41c73..3eddf4cd7 100644 --- a/Pipeline/Build.CodeAnalysis.cs +++ b/Pipeline/Build.CodeAnalysis.cs @@ -13,6 +13,7 @@ partial class Build .Unlisted() .Before(Compile) .Before(CodeCoverage) + .OnlyWhenDynamic(() => BuildScope == BuildScope.Default) .Executes(() => { SonarScannerTasks.SonarScannerBegin(s => s @@ -29,7 +30,7 @@ partial class Build .Unlisted() .DependsOn(Compile) .DependsOn(CodeCoverage) - .OnlyWhenDynamic(() => IsServerBuild) + .OnlyWhenDynamic(() => IsServerBuild && BuildScope == BuildScope.Default) .Executes(() => { SonarScannerTasks.SonarScannerEnd(s => s diff --git a/Pipeline/Build.CodeCoverage.cs b/Pipeline/Build.CodeCoverage.cs index d43d562c7..03956d850 100644 --- a/Pipeline/Build.CodeCoverage.cs +++ b/Pipeline/Build.CodeCoverage.cs @@ -11,6 +11,7 @@ partial class Build { Target CodeCoverage => _ => _ .DependsOn(UnitTests) + .OnlyWhenDynamic(() => BuildScope != BuildScope.CoreOnly) .Executes(() => { ReportGenerator(s => s diff --git a/Pipeline/Build.MutationTests.cs b/Pipeline/Build.MutationTests.cs index c913b0f60..4e5b64a60 100644 --- a/Pipeline/Build.MutationTests.cs +++ b/Pipeline/Build.MutationTests.cs @@ -27,6 +27,7 @@ partial class Build .After(MutationTestsLinux) .After(MutationTestsWindows) .OnlyWhenDynamic(() => GitHubActions.IsPullRequest) + .OnlyWhenDynamic(() => BuildScope == BuildScope.Default) .Executes(async () => { int? prId = GitHubActions.PullRequestNumber; @@ -83,6 +84,7 @@ await gitHubClient.Issue.Comment.Update("Testably", "Testably.Abstractions", }); Target MutationTestPreparation => _ => _ + .OnlyWhenDynamic(() => BuildScope == BuildScope.Default) .Executes(() => { StrykerToolPath.CreateOrCleanDirectory(); @@ -97,11 +99,13 @@ await gitHubClient.Issue.Comment.Update("Testably", "Testably.Abstractions", Target MutationTests => _ => _ .DependsOn(MutationTestsWindows) .DependsOn(MutationTestsLinux) - .DependsOn(MutationComment); + .DependsOn(MutationComment) + .OnlyWhenDynamic(() => BuildScope == BuildScope.Default); Target MutationTestsLinux => _ => _ .DependsOn(Compile) .DependsOn(MutationTestPreparation) + .OnlyWhenDynamic(() => BuildScope == BuildScope.Default) .Executes(() => { AbsolutePath configFile = StrykerToolPath / "Stryker.Config.json"; @@ -210,6 +214,7 @@ await gitHubClient.Issue.Comment.Update("Testably", "Testably.Abstractions", Target MutationTestsWindows => _ => _ .DependsOn(Compile) .DependsOn(MutationTestPreparation) + .OnlyWhenDynamic(() => BuildScope == BuildScope.Default) .Executes(() => { AbsolutePath configFile = StrykerToolPath / "Stryker.Config.json"; diff --git a/Pipeline/Build.Pack.cs b/Pipeline/Build.Pack.cs index f735d6112..15245f3a6 100644 --- a/Pipeline/Build.Pack.cs +++ b/Pipeline/Build.Pack.cs @@ -24,47 +24,53 @@ partial class Build packagesDirectory.CreateOrCleanDirectory(); List packages = new(); - Directory.CreateDirectory(packagesDirectory / "Main"); - foreach (Project mainProject in MainProjects) + if (BuildScope != BuildScope.CoreOnly) { - foreach (string package in - Directory.EnumerateFiles(mainProject.Directory / "bin", "*.nupkg", - SearchOption.AllDirectories)) + Directory.CreateDirectory(packagesDirectory / "Main"); + foreach (Project mainProject in MainProjects) { - File.Move(package, packagesDirectory / "Main" / Path.GetFileName(package)); - Debug("Found nuget package: {PackagePath}", package); - packages.Add(Path.GetFileName(package)); - } + foreach (string package in + Directory.EnumerateFiles(mainProject.Directory / "bin", "*.nupkg", + SearchOption.AllDirectories)) + { + File.Move(package, packagesDirectory / "Main" / Path.GetFileName(package)); + Debug("Found nuget package: {PackagePath}", package); + packages.Add(Path.GetFileName(package)); + } - foreach (string symbolPackage in - Directory.EnumerateFiles(mainProject.Directory / "bin", "*.snupkg", - SearchOption.AllDirectories)) - { - File.Move(symbolPackage, - packagesDirectory / "Main" / Path.GetFileName(symbolPackage)); - Debug("Found symbol package: {PackagePath}", symbolPackage); + foreach (string symbolPackage in + Directory.EnumerateFiles(mainProject.Directory / "bin", "*.snupkg", + SearchOption.AllDirectories)) + { + File.Move(symbolPackage, + packagesDirectory / "Main" / Path.GetFileName(symbolPackage)); + Debug("Found symbol package: {PackagePath}", symbolPackage); + } } } - Directory.CreateDirectory(packagesDirectory / "Core"); - foreach (Project coreProject in CoreProjects) + if (BuildScope != BuildScope.MainOnly) { - foreach (string package in - Directory.EnumerateFiles(coreProject.Directory / "bin", "*.nupkg", - SearchOption.AllDirectories)) + Directory.CreateDirectory(packagesDirectory / "Core"); + foreach (Project coreProject in CoreProjects) { - File.Move(package, packagesDirectory / "Core" / Path.GetFileName(package)); - Debug("Found nuget package: {PackagePath}", package); - packages.Add(Path.GetFileName(package)); - } + foreach (string package in + Directory.EnumerateFiles(coreProject.Directory / "bin", "*.nupkg", + SearchOption.AllDirectories)) + { + File.Move(package, packagesDirectory / "Core" / Path.GetFileName(package)); + Debug("Found nuget package: {PackagePath}", package); + packages.Add(Path.GetFileName(package)); + } - foreach (string symbolPackage in - Directory.EnumerateFiles(coreProject.Directory / "bin", "*.snupkg", - SearchOption.AllDirectories)) - { - File.Move(symbolPackage, - packagesDirectory / "Core" / Path.GetFileName(symbolPackage)); - Debug("Found symbol package: {PackagePath}", symbolPackage); + foreach (string symbolPackage in + Directory.EnumerateFiles(coreProject.Directory / "bin", "*.snupkg", + SearchOption.AllDirectories)) + { + File.Move(symbolPackage, + packagesDirectory / "Core" / Path.GetFileName(symbolPackage)); + Debug("Found symbol package: {PackagePath}", symbolPackage); + } } } diff --git a/Pipeline/Build.UnitTest.cs b/Pipeline/Build.UnitTest.cs index db7a81471..0b3629be5 100644 --- a/Pipeline/Build.UnitTest.cs +++ b/Pipeline/Build.UnitTest.cs @@ -18,6 +18,7 @@ partial class Build Target DotNetUnitTests => _ => _ .Unlisted() .DependsOn(Compile) + .OnlyWhenDynamic(() => BuildScope != BuildScope.CoreOnly) .Executes(() => { string[] excludedFrameworks = diff --git a/Pipeline/Build.cs b/Pipeline/Build.cs index fc553e4cc..65bdc27a7 100644 --- a/Pipeline/Build.cs +++ b/Pipeline/Build.cs @@ -14,6 +14,14 @@ namespace Build; )] partial class Build : NukeBuild { + /// + /// Set this flag temporarily when you introduce breaking changes in the core library. + /// This will change the build pipeline to only build and publish the aweXpect.Core or aweXpect package. + /// + /// Afterward, you can update the package reference in `Directory.Packages.props` and reset this flag. + /// + readonly BuildScope BuildScope = BuildScope.Default; + [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release; diff --git a/Pipeline/BuildScope.cs b/Pipeline/BuildScope.cs new file mode 100644 index 000000000..77cc2ee8e --- /dev/null +++ b/Pipeline/BuildScope.cs @@ -0,0 +1,8 @@ +namespace Build; + +enum BuildScope +{ + CoreOnly, + MainOnly, + Default, +} diff --git a/Source/Directory.Build.props b/Source/Directory.Build.props index 4c4fb8288..18233f836 100644 --- a/Source/Directory.Build.props +++ b/Source/Directory.Build.props @@ -12,12 +12,12 @@ - net6.0;net8.0;net9.0;netstandard2.1;netstandard2.0 + net6.0;net8.0;net9.0;net10.0;netstandard2.1;netstandard2.0 netstandard2.0 - latest + preview enable $(NoWarn);1701;1702;MA0003;MA0004;MA0042;MA0076;MA0105;MA0106;NU5104 true diff --git a/Source/Testably.Abstractions.Compression/IZipArchive.cs b/Source/Testably.Abstractions.Compression/IZipArchive.cs index e483f8d08..537b80577 100644 --- a/Source/Testably.Abstractions.Compression/IZipArchive.cs +++ b/Source/Testably.Abstractions.Compression/IZipArchive.cs @@ -1,11 +1,18 @@ using System; using System.Collections.ObjectModel; using System.IO.Compression; +#if FEATURE_COMPRESSION_ASYNC +using System.Threading; +using System.Threading.Tasks; +#endif namespace Testably.Abstractions; /// public interface IZipArchive : IFileSystemEntity, IDisposable +#if FEATURE_COMPRESSION_ASYNC +, IAsyncDisposable +#endif { #if FEATURE_FILESYSTEM_COMMENT_ENCRYPTED /// @@ -33,6 +40,22 @@ IZipArchiveEntry CreateEntryFromFile(string sourceFileName, IZipArchiveEntry CreateEntryFromFile(string sourceFileName, string entryName, CompressionLevel compressionLevel); + +#if FEATURE_COMPRESSION_ASYNC + /// + Task CreateEntryFromFileAsync(string sourceFileName, + string entryName, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task CreateEntryFromFileAsync(string sourceFileName, + string entryName, + CompressionLevel compressionLevel, + CancellationToken cancellationToken = default); +#endif /// void ExtractToDirectory(string destinationDirectoryName); @@ -43,6 +66,19 @@ void ExtractToDirectory(string destinationDirectoryName, bool overwriteFiles); #endif +#if FEATURE_COMPRESSION_ASYNC + /// + Task ExtractToDirectoryAsync(string destinationDirectoryName, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task ExtractToDirectoryAsync(string destinationDirectoryName, + bool overwriteFiles, + CancellationToken cancellationToken = default); +#endif + /// IZipArchiveEntry? GetEntry(string entryName); } diff --git a/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs b/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs index 04838c465..8030c165d 100644 --- a/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs +++ b/Source/Testably.Abstractions.Compression/IZipArchiveEntry.cs @@ -1,6 +1,10 @@ using System; using System.IO; using System.IO.Compression; +using System.Threading; +#if FEATURE_COMPRESSION_ASYNC +using System.Threading.Tasks; +#endif namespace Testably.Abstractions; @@ -54,7 +58,22 @@ public interface IZipArchiveEntry : IFileSystemEntity /// void ExtractToFile(string destinationFileName, bool overwrite); + +#if FEATURE_COMPRESSION_ASYNC + /// + Task ExtractToFileAsync(string destinationFileName, CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task ExtractToFileAsync(string destinationFileName, bool overwrite, CancellationToken cancellationToken = default); +#endif /// Stream Open(); + +#if FEATURE_COMPRESSION_ASYNC + /// + Task OpenAsync(CancellationToken cancellationToken = default); +#endif } diff --git a/Source/Testably.Abstractions.Compression/IZipFile.cs b/Source/Testably.Abstractions.Compression/IZipFile.cs index 71f4e529a..ca7d296d3 100644 --- a/Source/Testably.Abstractions.Compression/IZipFile.cs +++ b/Source/Testably.Abstractions.Compression/IZipFile.cs @@ -3,6 +3,10 @@ #if FEATURE_COMPRESSION_STREAM using System.IO; #endif +#if FEATURE_COMPRESSION_ASYNC +using System.Threading; +using System.Threading.Tasks; +#endif namespace Testably.Abstractions; @@ -55,6 +59,64 @@ void CreateFromDirectory( bool includeBaseDirectory, Encoding entryNameEncoding); +#if FEATURE_COMPRESSION_ASYNC + /// + Task CreateFromDirectoryAsync( + string sourceDirectoryName, + Stream destination, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task CreateFromDirectoryAsync( + string sourceDirectoryName, + Stream destination, + CompressionLevel compressionLevel, + bool includeBaseDirectory, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task CreateFromDirectoryAsync( + string sourceDirectoryName, + Stream destination, + CompressionLevel compressionLevel, + bool includeBaseDirectory, + Encoding entryNameEncoding, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task CreateFromDirectoryAsync( + string sourceDirectoryName, + string destinationArchiveFileName, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task CreateFromDirectoryAsync( + string sourceDirectoryName, + string destinationArchiveFileName, + CompressionLevel compressionLevel, + bool includeBaseDirectory, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task CreateFromDirectoryAsync( + string sourceDirectoryName, + string destinationArchiveFileName, + CompressionLevel compressionLevel, + bool includeBaseDirectory, + Encoding entryNameEncoding, + CancellationToken cancellationToken = default); +#endif + #if FEATURE_COMPRESSION_STREAM /// void ExtractToDirectory( @@ -114,6 +176,79 @@ void ExtractToDirectory( Encoding? entryNameEncoding, bool overwriteFiles); #endif + + +#if FEATURE_COMPRESSION_ASYNC + /// + Task ExtractToDirectoryAsync( + Stream source, + string destinationDirectoryName, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task ExtractToDirectoryAsync( + Stream source, + string destinationDirectoryName, + bool overwriteFiles, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task ExtractToDirectoryAsync( + Stream source, + string destinationDirectoryName, + Encoding entryNameEncoding, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task ExtractToDirectoryAsync( + Stream source, + string destinationDirectoryName, + Encoding entryNameEncoding, + bool overwriteFiles, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task ExtractToDirectoryAsync( + string sourceArchiveFileName, + string destinationDirectoryName, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task ExtractToDirectoryAsync( + string sourceArchiveFileName, + string destinationDirectoryName, + bool overwriteFiles, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task ExtractToDirectoryAsync( + string sourceArchiveFileName, + string destinationDirectoryName, + Encoding? entryNameEncoding, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task ExtractToDirectoryAsync( + string sourceArchiveFileName, + string destinationDirectoryName, + Encoding? entryNameEncoding, + bool overwriteFiles, + CancellationToken cancellationToken = default); +#endif /// IZipArchive Open( @@ -126,7 +261,31 @@ IZipArchive Open( ZipArchiveMode mode, Encoding? entryNameEncoding); +#if FEATURE_COMPRESSION_ASYNC + /// + Task OpenAsync( + string archiveFileName, + ZipArchiveMode mode, + CancellationToken cancellationToken = default); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + Task OpenAsync( + string archiveFileName, + ZipArchiveMode mode, + Encoding? entryNameEncoding, + CancellationToken cancellationToken = default); +#endif + /// IZipArchive OpenRead( string archiveFileName); + +#if FEATURE_COMPRESSION_ASYNC + /// + Task OpenReadAsync( + string archiveFileName, + CancellationToken cancellationToken = default); +#endif } diff --git a/Source/Testably.Abstractions.Compression/Internal/Execute.cs b/Source/Testably.Abstractions.Compression/Internal/Execute.cs index 66775ba0a..4d241a2f2 100644 --- a/Source/Testably.Abstractions.Compression/Internal/Execute.cs +++ b/Source/Testably.Abstractions.Compression/Internal/Execute.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; namespace Testably.Abstractions.Internal; @@ -33,6 +34,37 @@ public static T WhenRealFileSystem(IFileSystem fileSystem, ? onRealFileSystem() : onMockFileSystem(); + /// + /// Returns the value from when + /// the is a real file system, + /// otherwise returns the value from . + /// + public static async Task WhenRealFileSystemAsync(IFileSystem fileSystem, + Func onRealFileSystem, + Action onMockFileSystem) + { + if (IsRealFileSystem(fileSystem)) + { + await onRealFileSystem(); + } + else + { + onMockFileSystem(); + } + } + + /// + /// Returns the value from when + /// the is a real file system, + /// otherwise returns the value from . + /// + public static async Task WhenRealFileSystemAsync(IFileSystem fileSystem, + Func> onRealFileSystem, + Func onMockFileSystem) + => IsRealFileSystem(fileSystem) + ? await onRealFileSystem() + : onMockFileSystem(); + private static bool IsRealFileSystem(IFileSystem fileSystem) => string.Equals(fileSystem.GetType().Name, "RealFileSystem", StringComparison.Ordinal); diff --git a/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs b/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs index 3b86747f4..eec06f415 100644 --- a/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs +++ b/Source/Testably.Abstractions.Compression/ZipArchiveEntryWrapper.cs @@ -3,6 +3,10 @@ using System.IO; using System.IO.Compression; using Testably.Abstractions.Internal; +#if FEATURE_COMPRESSION_ASYNC +using System.Threading; +using System.Threading.Tasks; +#endif namespace Testably.Abstractions; @@ -102,11 +106,47 @@ public void ExtractToFile(string destinationFileName, bool overwrite) () => _instance.ExtractToFile(destinationFileName, overwrite), () => ZipUtilities.ExtractToFile(this, destinationFileName, overwrite)); } + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task ExtractToFileAsync(string destinationFileName, CancellationToken cancellationToken = default) + { + if (destinationFileName == null) + { + throw new ArgumentNullException(nameof(destinationFileName)); + } + + await Execute.WhenRealFileSystemAsync(FileSystem, + async () => await _instance.ExtractToFileAsync(destinationFileName, cancellationToken), + () => ZipUtilities.ExtractToFile(this, destinationFileName, false)); + } +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task ExtractToFileAsync(string destinationFileName, bool overwrite, CancellationToken cancellationToken = default) + { + if (destinationFileName == null) + { + throw new ArgumentNullException(nameof(destinationFileName)); + } + + await Execute.WhenRealFileSystemAsync(FileSystem, + async () => await _instance.ExtractToFileAsync(destinationFileName, overwrite, cancellationToken), + () => ZipUtilities.ExtractToFile(this, destinationFileName, overwrite)); + } +#endif /// public Stream Open() => _instance.Open(); +#if FEATURE_COMPRESSION_ASYNC + /// + public Task OpenAsync(CancellationToken cancellationToken = default) + => _instance.OpenAsync(cancellationToken); +#endif + #endregion /// diff --git a/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs b/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs index 8c20e0b34..9bf45a6da 100644 --- a/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs +++ b/Source/Testably.Abstractions.Compression/ZipArchiveWrapper.cs @@ -3,6 +3,10 @@ using System.IO.Compression; using System.Linq; using Testably.Abstractions.Internal; +#if FEATURE_COMPRESSION_ASYNC +using System.Threading; +using System.Threading.Tasks; +#endif namespace Testably.Abstractions; @@ -73,10 +77,52 @@ public IZipArchiveEntry CreateEntryFromFile(string sourceFileName, string entryN entryName, compressionLevel)); +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task CreateEntryFromFileAsync(string sourceFileName, + string entryName, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + async () => ZipArchiveEntryWrapper.New(FileSystem, this, + await _instance.CreateEntryFromFileAsync( + sourceFileName, + entryName, + cancellationToken)), + () => ZipUtilities.CreateEntryFromFile(this, + sourceFileName, + entryName)); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task CreateEntryFromFileAsync(string sourceFileName, + string entryName, + CompressionLevel compressionLevel, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + async () => ZipArchiveEntryWrapper.New(FileSystem, this, + await _instance.CreateEntryFromFileAsync( + sourceFileName, + entryName, + compressionLevel, + cancellationToken)), + () => ZipUtilities.CreateEntryFromFile(this, + sourceFileName, + entryName, + compressionLevel)); +#endif + /// public void Dispose() => _instance.Dispose(); +#if FEATURE_COMPRESSION_ASYNC + /// + public ValueTask DisposeAsync() + => _instance.DisposeAsync(); +#endif + /// public void ExtractToDirectory(string destinationDirectoryName) { @@ -118,6 +164,54 @@ public void ExtractToDirectory(string destinationDirectoryName, bool overwriteFi } #endif +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task ExtractToDirectoryAsync(string destinationDirectoryName, + CancellationToken cancellationToken = default) + { + if (destinationDirectoryName == null) + { + throw new ArgumentNullException(nameof(destinationDirectoryName)); + } + + await Execute.WhenRealFileSystemAsync(FileSystem, + async () => await _instance.ExtractToDirectoryAsync(destinationDirectoryName, + cancellationToken), + () => + { + foreach (IZipArchiveEntry entry in Entries) + { + entry.ExtractRelativeToDirectory(destinationDirectoryName, overwrite: false); + } + }); + } +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task ExtractToDirectoryAsync(string destinationDirectoryName, + bool overwriteFiles, + CancellationToken cancellationToken = default) + { + if (destinationDirectoryName == null) + { + throw new ArgumentNullException(nameof(destinationDirectoryName)); + } + + await Execute.WhenRealFileSystemAsync(FileSystem, + async () => await _instance.ExtractToDirectoryAsync(destinationDirectoryName, + overwriteFiles, cancellationToken), + () => + { + foreach (IZipArchiveEntry entry in Entries) + { + entry.ExtractRelativeToDirectory(destinationDirectoryName, + overwrite: overwriteFiles); + } + }); + } +#endif + /// public IZipArchiveEntry? GetEntry(string entryName) => ZipArchiveEntryWrapper.New(FileSystem, this, _instance.GetEntry(entryName)); diff --git a/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs b/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs index 0398b48b7..2b93c7eef 100644 --- a/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs +++ b/Source/Testably.Abstractions.Compression/ZipFileWrapper.cs @@ -1,9 +1,13 @@ using System.IO.Compression; using System.Text; +using System.Threading; using Testably.Abstractions.Internal; #if FEATURE_COMPRESSION_STREAM using System.IO; #endif +#if FEATURE_COMPRESSION_ASYNC +using System.Threading.Tasks; +#endif namespace Testably.Abstractions; @@ -133,6 +137,138 @@ public void CreateFromDirectory( includeBaseDirectory, entryNameEncoding)); +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task CreateFromDirectoryAsync( + string sourceDirectoryName, + Stream destination, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.CreateFromDirectoryAsync( + sourceDirectoryName, + destination, + cancellationToken), + () => ZipUtilities.CreateFromDirectory( + FileSystem, + sourceDirectoryName, + destination)); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task CreateFromDirectoryAsync( + string sourceDirectoryName, + Stream destination, + CompressionLevel compressionLevel, + bool includeBaseDirectory, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.CreateFromDirectoryAsync( + sourceDirectoryName, + destination, + compressionLevel, + includeBaseDirectory, + cancellationToken), + () => ZipUtilities.CreateFromDirectory( + FileSystem, + sourceDirectoryName, + destination, + compressionLevel, + includeBaseDirectory)); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task CreateFromDirectoryAsync( + string sourceDirectoryName, + Stream destination, + CompressionLevel compressionLevel, + bool includeBaseDirectory, + Encoding entryNameEncoding, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.CreateFromDirectoryAsync( + sourceDirectoryName, + destination, + compressionLevel, + includeBaseDirectory, + entryNameEncoding, + cancellationToken), + () => ZipUtilities.CreateFromDirectory( + FileSystem, + sourceDirectoryName, + destination, + compressionLevel, + includeBaseDirectory, + entryNameEncoding)); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task CreateFromDirectoryAsync( + string sourceDirectoryName, + string destinationArchiveFileName, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.CreateFromDirectoryAsync( + sourceDirectoryName, + destinationArchiveFileName, + cancellationToken), + () => ZipUtilities.CreateFromDirectory( + FileSystem, + sourceDirectoryName, + destinationArchiveFileName)); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task CreateFromDirectoryAsync( + string sourceDirectoryName, + string destinationArchiveFileName, + CompressionLevel compressionLevel, + bool includeBaseDirectory, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.CreateFromDirectoryAsync( + sourceDirectoryName, + destinationArchiveFileName, + compressionLevel, + includeBaseDirectory, + cancellationToken), + () => ZipUtilities.CreateFromDirectory( + FileSystem, + sourceDirectoryName, + destinationArchiveFileName, + compressionLevel, + includeBaseDirectory)); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task CreateFromDirectoryAsync( + string sourceDirectoryName, + string destinationArchiveFileName, + CompressionLevel compressionLevel, + bool includeBaseDirectory, + Encoding entryNameEncoding, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.CreateFromDirectoryAsync( + sourceDirectoryName, + destinationArchiveFileName, + compressionLevel, + includeBaseDirectory, + entryNameEncoding, + cancellationToken), + () => ZipUtilities.CreateFromDirectory( + FileSystem, + sourceDirectoryName, + destinationArchiveFileName, + compressionLevel, + includeBaseDirectory, + entryNameEncoding)); +#endif + #if FEATURE_COMPRESSION_STREAM /// public void ExtractToDirectory( @@ -272,7 +408,167 @@ public void ExtractToDirectory( entryNameEncoding, overwriteFiles)); #endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task ExtractToDirectoryAsync( + Stream source, + string destinationDirectoryName, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.ExtractToDirectoryAsync( + source, + destinationDirectoryName, + cancellationToken), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + source, + destinationDirectoryName)); +#endif +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task ExtractToDirectoryAsync( + Stream source, + string destinationDirectoryName, + bool overwriteFiles, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.ExtractToDirectoryAsync( + source, + destinationDirectoryName, + overwriteFiles, + cancellationToken), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + source, + destinationDirectoryName, + overwriteFiles: overwriteFiles)); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task ExtractToDirectoryAsync( + Stream source, + string destinationDirectoryName, + Encoding entryNameEncoding, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.ExtractToDirectoryAsync( + source, + destinationDirectoryName, + entryNameEncoding, + cancellationToken), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + source, + destinationDirectoryName, + entryNameEncoding)); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task ExtractToDirectoryAsync( + Stream source, + string destinationDirectoryName, + Encoding entryNameEncoding, + bool overwriteFiles, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.ExtractToDirectoryAsync( + source, + destinationDirectoryName, + entryNameEncoding, + overwriteFiles, + cancellationToken), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + source, + destinationDirectoryName, + entryNameEncoding, + overwriteFiles)); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task ExtractToDirectoryAsync( + string sourceArchiveFileName, + string destinationDirectoryName, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.ExtractToDirectoryAsync( + sourceArchiveFileName, + destinationDirectoryName, + cancellationToken), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + sourceArchiveFileName, + destinationDirectoryName)); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task ExtractToDirectoryAsync( + string sourceArchiveFileName, + string destinationDirectoryName, + bool overwriteFiles, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.ExtractToDirectoryAsync( + sourceArchiveFileName, + destinationDirectoryName, + overwriteFiles, + cancellationToken), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + sourceArchiveFileName, + destinationDirectoryName, + overwriteFiles: overwriteFiles)); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task ExtractToDirectoryAsync( + string sourceArchiveFileName, + string destinationDirectoryName, + Encoding? entryNameEncoding, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.ExtractToDirectoryAsync( + sourceArchiveFileName, + destinationDirectoryName, + entryNameEncoding, + cancellationToken), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + sourceArchiveFileName, + destinationDirectoryName, + entryNameEncoding: entryNameEncoding)); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task ExtractToDirectoryAsync( + string sourceArchiveFileName, + string destinationDirectoryName, + Encoding? entryNameEncoding, + bool overwriteFiles, + CancellationToken cancellationToken = default) + => await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.ExtractToDirectoryAsync( + sourceArchiveFileName, + destinationDirectoryName, + entryNameEncoding, + overwriteFiles, + cancellationToken), + () => ZipUtilities.ExtractToDirectory( + FileSystem, + sourceArchiveFileName, + destinationDirectoryName, + entryNameEncoding, + overwriteFiles)); +#endif + /// public IZipArchive Open( string archiveFileName, @@ -296,6 +592,36 @@ public IZipArchive Open( archiveFileName, mode, entryNameEncoding))); + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task OpenAsync( + string archiveFileName, + ZipArchiveMode mode, + CancellationToken cancellationToken = default) + => new ZipArchiveWrapper(FileSystem, + await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.OpenAsync(archiveFileName, mode, cancellationToken), + () => ZipUtilities.Open(FileSystem, + archiveFileName, + mode))); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task OpenAsync( + string archiveFileName, + ZipArchiveMode mode, + Encoding? entryNameEncoding, + CancellationToken cancellationToken = default) + => new ZipArchiveWrapper(FileSystem, + await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.OpenAsync(archiveFileName, mode, entryNameEncoding, cancellationToken), + () => ZipUtilities.Open(FileSystem, + archiveFileName, + mode, + entryNameEncoding))); +#endif /// public IZipArchive OpenRead( @@ -306,6 +632,19 @@ public IZipArchive OpenRead( () => ZipUtilities.Open(FileSystem, archiveFileName, ZipArchiveMode.Read))); + +#if FEATURE_COMPRESSION_ASYNC + /// + public async Task OpenReadAsync( + string archiveFileName, + CancellationToken cancellationToken = default) + => new ZipArchiveWrapper(FileSystem, + await Execute.WhenRealFileSystemAsync(FileSystem, + () => ZipFile.OpenReadAsync(archiveFileName, cancellationToken), + () => ZipUtilities.Open(FileSystem, + archiveFileName, + ZipArchiveMode.Read))); +#endif #endregion } diff --git a/Source/Testably.Abstractions.Interface/Helpers/GuidSystemBase.cs b/Source/Testably.Abstractions.Interface/Helpers/GuidSystemBase.cs index e3cbe06ac..09e6f4fcf 100644 --- a/Source/Testably.Abstractions.Interface/Helpers/GuidSystemBase.cs +++ b/Source/Testably.Abstractions.Interface/Helpers/GuidSystemBase.cs @@ -29,9 +29,6 @@ protected GuidSystemBase(IRandomSystem randomSystem) /// public IRandomSystem RandomSystem { get; } - /// - public abstract Guid NewGuid(); - #if FEATURE_GUID_V7 /// public abstract Guid CreateVersion7(); @@ -42,6 +39,9 @@ protected GuidSystemBase(IRandomSystem randomSystem) public abstract Guid CreateVersion7(DateTimeOffset timestamp); #endif + /// + public abstract Guid NewGuid(); + #if FEATURE_GUID_PARSE #pragma warning disable MA0011 /// @@ -58,6 +58,12 @@ public Guid Parse(ReadOnlySpan input) #pragma warning restore MA0011 #endif +#if FEATURE_GUID_PARSE_UTF8 + /// + public Guid Parse(ReadOnlySpan utf8Text) + => Guid.Parse(utf8Text); +#endif + #if FEATURE_GUID_FORMATPROVIDER /// public Guid Parse(string s, IFormatProvider? provider) @@ -70,6 +76,12 @@ public Guid Parse(ReadOnlySpan s, IFormatProvider? provider) => Guid.Parse(s, provider); #endif +#if FEATURE_GUID_PARSE_UTF8 + /// + public Guid Parse(ReadOnlySpan utf8Text, IFormatProvider provider) + => Guid.Parse(utf8Text, provider); +#endif + #if FEATURE_GUID_PARSE /// public Guid ParseExact(string input, string format) @@ -110,6 +122,18 @@ public bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out Guid r => Guid.TryParse(s, provider, out result); #endif +#if FEATURE_GUID_PARSE_UTF8 + /// + public bool TryParse(ReadOnlySpan utf8Text, out Guid result) + => Guid.TryParse(utf8Text, out result); +#endif + +#if FEATURE_GUID_PARSE_UTF8 + /// + public bool TryParse(ReadOnlySpan utf8Text, IFormatProvider provider, out Guid result) + => Guid.TryParse(utf8Text, provider, out result); +#endif + #if FEATURE_GUID_PARSE /// public bool TryParseExact([NotNullWhen(true)] string? input, diff --git a/Source/Testably.Abstractions.Interface/Helpers/RandomWrapper.cs b/Source/Testably.Abstractions.Interface/Helpers/RandomWrapper.cs index cfa5adea3..a2068ab66 100644 --- a/Source/Testably.Abstractions.Interface/Helpers/RandomWrapper.cs +++ b/Source/Testably.Abstractions.Interface/Helpers/RandomWrapper.cs @@ -21,6 +21,18 @@ public RandomWrapper(Random instance) #region IRandom Members +#if FEATURE_RANDOM_STRINGS + /// + public string GetHexString(int stringLength, bool lowercase = false) + => _instance.GetHexString(stringLength, lowercase); +#endif + +#if FEATURE_RANDOM_STRINGS + /// + public void GetHexString(Span destination, bool lowercase = false) + => _instance.GetHexString(destination, lowercase); +#endif + #if FEATURE_RANDOM_ITEMS /// public void GetItems(ReadOnlySpan choices, Span destination) @@ -39,6 +51,12 @@ public T[] GetItems(ReadOnlySpan choices, int length) => _instance.GetItems(choices, length); #endif +#if FEATURE_RANDOM_STRINGS + /// + public string GetString(ReadOnlySpan choices, int length) + => _instance.GetString(choices, length); +#endif + /// public int Next() => _instance.Next(); diff --git a/Source/Testably.Abstractions.Interface/RandomSystem/IGuid.cs b/Source/Testably.Abstractions.Interface/RandomSystem/IGuid.cs index 61b10d62c..2a73003cf 100644 --- a/Source/Testably.Abstractions.Interface/RandomSystem/IGuid.cs +++ b/Source/Testably.Abstractions.Interface/RandomSystem/IGuid.cs @@ -13,9 +13,6 @@ public interface IGuid : IRandomSystemEntity /// Guid Empty { get; } - /// - Guid NewGuid(); - #if FEATURE_GUID_V7 /// Guid CreateVersion7(); @@ -26,6 +23,9 @@ public interface IGuid : IRandomSystemEntity Guid CreateVersion7(DateTimeOffset timestamp); #endif + /// + Guid NewGuid(); + #if FEATURE_GUID_PARSE /// Guid Parse(string input); @@ -36,6 +36,16 @@ public interface IGuid : IRandomSystemEntity Guid Parse(ReadOnlySpan input); #endif +#if FEATURE_GUID_PARSE_UTF8 + /// + Guid Parse(ReadOnlySpan utf8Text); +#endif + +#if FEATURE_GUID_PARSE_UTF8 + /// + Guid Parse(ReadOnlySpan utf8Text, IFormatProvider provider); +#endif + #if FEATURE_GUID_FORMATPROVIDER /// Guid Parse(string s, IFormatProvider? provider); @@ -76,6 +86,16 @@ public interface IGuid : IRandomSystemEntity bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out Guid result); #endif +#if FEATURE_GUID_PARSE_UTF8 + /// + bool TryParse(ReadOnlySpan utf8Text, out Guid result); +#endif + +#if FEATURE_GUID_PARSE_UTF8 + /// + bool TryParse(ReadOnlySpan utf8Text, IFormatProvider provider, out Guid result); +#endif + #if FEATURE_GUID_PARSE /// bool TryParseExact([NotNullWhen(true)] string? input, diff --git a/Source/Testably.Abstractions.Interface/RandomSystem/IRandom.cs b/Source/Testably.Abstractions.Interface/RandomSystem/IRandom.cs index e16353656..d00de78b5 100644 --- a/Source/Testably.Abstractions.Interface/RandomSystem/IRandom.cs +++ b/Source/Testably.Abstractions.Interface/RandomSystem/IRandom.cs @@ -7,6 +7,15 @@ namespace Testably.Abstractions.RandomSystem; /// public interface IRandom { +#if FEATURE_RANDOM_STRINGS + /// + string GetHexString(int stringLength, bool lowercase = false); +#endif + +#if FEATURE_RANDOM_STRINGS + /// + void GetHexString(Span destination, bool lowercase = false); +#endif #if FEATURE_RANDOM_ITEMS /// void GetItems(ReadOnlySpan choices, Span destination); @@ -22,6 +31,11 @@ public interface IRandom T[] GetItems(ReadOnlySpan choices, int length); #endif +#if FEATURE_RANDOM_STRINGS + /// + string GetString(ReadOnlySpan choices, int length); +#endif + /// int Next(); diff --git a/Source/Testably.Abstractions.Testing/RandomSystem/RandomMock.cs b/Source/Testably.Abstractions.Testing/RandomSystem/RandomMock.cs index 071c5451b..fda209969 100644 --- a/Source/Testably.Abstractions.Testing/RandomSystem/RandomMock.cs +++ b/Source/Testably.Abstractions.Testing/RandomSystem/RandomMock.cs @@ -62,6 +62,32 @@ public RandomMock( #region IRandom Members +#if FEATURE_RANDOM_STRINGS + /// + public string GetHexString(int stringLength, bool lowercase = false) + { + char[] choices = lowercase + ? "0123456789abcdef".ToCharArray() + : "0123456789ABCDEF".ToCharArray(); + char[] chars = GetItems(choices, stringLength); + return new string(chars); + } +#endif + +#if FEATURE_RANDOM_STRINGS + /// + public void GetHexString(Span destination, bool lowercase = false) + { + char[] choices = lowercase + ? "0123456789abcdef".ToCharArray() + : "0123456789ABCDEF".ToCharArray(); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = choices[Next(choices.Length)]; + } + } +#endif + #if FEATURE_RANDOM_ITEMS /// public void GetItems(ReadOnlySpan choices, Span destination) @@ -99,6 +125,15 @@ public T[] GetItems(ReadOnlySpan choices, int length) } #endif +#if FEATURE_RANDOM_STRINGS + /// + public string GetString(ReadOnlySpan choices, int length) + { + char[] chars = GetItems(choices, length); + return new string(chars); + } +#endif + /// public int Next() => _intGenerator?.GetNext() ?? _random.Next(); diff --git a/Source/Testably.Abstractions.Testing/Testably.Abstractions.Testing.csproj b/Source/Testably.Abstractions.Testing/Testably.Abstractions.Testing.csproj index 3ca4e851c..d1abe24c5 100644 --- a/Source/Testably.Abstractions.Testing/Testably.Abstractions.Testing.csproj +++ b/Source/Testably.Abstractions.Testing/Testably.Abstractions.Testing.csproj @@ -23,8 +23,12 @@ - + + + + + diff --git a/Tests/Api/Directory.Build.props b/Tests/Api/Directory.Build.props index 5d1cdaeed..2495c306c 100644 --- a/Tests/Api/Directory.Build.props +++ b/Tests/Api/Directory.Build.props @@ -5,7 +5,7 @@ - net8.0 + net10.0 diff --git a/Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions.AccessControl_net10.0.txt b/Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions.AccessControl_net10.0.txt new file mode 100644 index 000000000..e5fa4f74f --- /dev/null +++ b/Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions.AccessControl_net10.0.txt @@ -0,0 +1,54 @@ +[assembly: System.CLSCompliant(true)] +[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Testably/Testably.Abstractions.git")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Testably.Abstractions.AccessControl.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001006104741100251820044d92b34b0519a1de0bccd80d6199aadbdcd5931d035462d42f70b0ae7a7db37bab63afb8a8ad0dc21392bb01f1243bfc51df4b5f1975b1b9746fecbed88913b783fccb69efc59e23b0e019e065abd38731711a2d6ac2569ab57d4b4d529f5903f5bee0f4388b2a5f4d5e0fddab6aac18d96aa78c2e73e0")] +[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v10.0", FrameworkDisplayName=".NET 10.0")] +namespace Testably.Abstractions +{ + public static class DirectoryAclExtensions + { + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static void CreateDirectory(this System.IO.Abstractions.IDirectory directory, string path, System.Security.AccessControl.DirectorySecurity directorySecurity) { } + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static System.Security.AccessControl.DirectorySecurity GetAccessControl(this System.IO.Abstractions.IDirectory directory, string path) { } + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static System.Security.AccessControl.DirectorySecurity GetAccessControl(this System.IO.Abstractions.IDirectory directory, string path, System.Security.AccessControl.AccessControlSections includeSections) { } + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static void SetAccessControl(this System.IO.Abstractions.IDirectory directory, string path, System.Security.AccessControl.DirectorySecurity directorySecurity) { } + } + public static class DirectoryInfoAclExtensions + { + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static void Create(this System.IO.Abstractions.IDirectoryInfo directoryInfo, System.Security.AccessControl.DirectorySecurity directorySecurity) { } + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static System.Security.AccessControl.DirectorySecurity GetAccessControl(this System.IO.Abstractions.IDirectoryInfo directoryInfo) { } + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static System.Security.AccessControl.DirectorySecurity GetAccessControl(this System.IO.Abstractions.IDirectoryInfo directoryInfo, System.Security.AccessControl.AccessControlSections includeSections) { } + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static void SetAccessControl(this System.IO.Abstractions.IDirectoryInfo directoryInfo, System.Security.AccessControl.DirectorySecurity directorySecurity) { } + } + public static class FileAclExtensions + { + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static System.Security.AccessControl.FileSecurity GetAccessControl(this System.IO.Abstractions.IFile file, string path) { } + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static System.Security.AccessControl.FileSecurity GetAccessControl(this System.IO.Abstractions.IFile file, string path, System.Security.AccessControl.AccessControlSections includeSections) { } + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static void SetAccessControl(this System.IO.Abstractions.IFile file, string path, System.Security.AccessControl.FileSecurity fileSecurity) { } + } + public static class FileInfoAclExtensions + { + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static System.Security.AccessControl.FileSecurity GetAccessControl(this System.IO.Abstractions.IFileInfo fileInfo) { } + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static System.Security.AccessControl.FileSecurity GetAccessControl(this System.IO.Abstractions.IFileInfo fileInfo, System.Security.AccessControl.AccessControlSections includeSections) { } + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static void SetAccessControl(this System.IO.Abstractions.IFileInfo fileInfo, System.Security.AccessControl.FileSecurity fileSecurity) { } + } + public static class FileStreamAclExtensions + { + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static System.Security.AccessControl.FileSecurity GetAccessControl(this System.IO.Abstractions.FileSystemStream fileStream) { } + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public static void SetAccessControl(this System.IO.Abstractions.FileSystemStream fileStream, System.Security.AccessControl.FileSecurity fileSecurity) { } + } +} \ No newline at end of file diff --git a/Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions.Compression_net10.0.txt b/Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions.Compression_net10.0.txt new file mode 100644 index 000000000..9b9419403 --- /dev/null +++ b/Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions.Compression_net10.0.txt @@ -0,0 +1,92 @@ +[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Testably/Testably.Abstractions.git")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Testably.Abstractions.Compression.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001006104741100251820044d92b34b0519a1de0bccd80d6199aadbdcd5931d035462d42f70b0ae7a7db37bab63afb8a8ad0dc21392bb01f1243bfc51df4b5f1975b1b9746fecbed88913b783fccb69efc59e23b0e019e065abd38731711a2d6ac2569ab57d4b4d529f5903f5bee0f4388b2a5f4d5e0fddab6aac18d96aa78c2e73e0")] +[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v10.0", FrameworkDisplayName=".NET 10.0")] +namespace Testably.Abstractions +{ + public static class FileSystemExtensions + { + public static Testably.Abstractions.IZipArchiveFactory ZipArchive(this System.IO.Abstractions.IFileSystem fileSystem) { } + public static Testably.Abstractions.IZipFile ZipFile(this System.IO.Abstractions.IFileSystem fileSystem) { } + } + public interface IZipArchive : System.IAsyncDisposable, System.IDisposable, System.IO.Abstractions.IFileSystemEntity + { + string Comment { get; set; } + System.Collections.ObjectModel.ReadOnlyCollection Entries { get; } + System.IO.Compression.ZipArchiveMode Mode { get; } + Testably.Abstractions.IZipArchiveEntry CreateEntry(string entryName); + Testably.Abstractions.IZipArchiveEntry CreateEntry(string entryName, System.IO.Compression.CompressionLevel compressionLevel); + Testably.Abstractions.IZipArchiveEntry CreateEntryFromFile(string sourceFileName, string entryName); + Testably.Abstractions.IZipArchiveEntry CreateEntryFromFile(string sourceFileName, string entryName, System.IO.Compression.CompressionLevel compressionLevel); + System.Threading.Tasks.Task CreateEntryFromFileAsync(string sourceFileName, string entryName, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task CreateEntryFromFileAsync(string sourceFileName, string entryName, System.IO.Compression.CompressionLevel compressionLevel, System.Threading.CancellationToken cancellationToken = default); + void ExtractToDirectory(string destinationDirectoryName); + void ExtractToDirectory(string destinationDirectoryName, bool overwriteFiles); + System.Threading.Tasks.Task ExtractToDirectoryAsync(string destinationDirectoryName, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task ExtractToDirectoryAsync(string destinationDirectoryName, bool overwriteFiles, System.Threading.CancellationToken cancellationToken = default); + Testably.Abstractions.IZipArchiveEntry? GetEntry(string entryName); + } + public interface IZipArchiveEntry : System.IO.Abstractions.IFileSystemEntity + { + Testably.Abstractions.IZipArchive Archive { get; } + string Comment { get; set; } + long CompressedLength { get; } + uint Crc32 { get; } + int ExternalAttributes { get; set; } + string FullName { get; } + bool IsEncrypted { get; } + System.DateTimeOffset LastWriteTime { get; set; } + long Length { get; } + string Name { get; } + void Delete(); + void ExtractToFile(string destinationFileName); + void ExtractToFile(string destinationFileName, bool overwrite); + System.Threading.Tasks.Task ExtractToFileAsync(string destinationFileName, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task ExtractToFileAsync(string destinationFileName, bool overwrite, System.Threading.CancellationToken cancellationToken = default); + System.IO.Stream Open(); + System.Threading.Tasks.Task OpenAsync(System.Threading.CancellationToken cancellationToken = default); + } + public interface IZipArchiveFactory : System.IO.Abstractions.IFileSystemEntity + { + Testably.Abstractions.IZipArchive New(System.IO.Stream stream); + Testably.Abstractions.IZipArchive New(System.IO.Stream stream, System.IO.Compression.ZipArchiveMode mode); + Testably.Abstractions.IZipArchive New(System.IO.Stream stream, System.IO.Compression.ZipArchiveMode mode, bool leaveOpen); + Testably.Abstractions.IZipArchive New(System.IO.Stream stream, System.IO.Compression.ZipArchiveMode mode, bool leaveOpen, System.Text.Encoding? entryNameEncoding); + } + public interface IZipFile : System.IO.Abstractions.IFileSystemEntity + { + void CreateFromDirectory(string sourceDirectoryName, System.IO.Stream destination); + void CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName); + void CreateFromDirectory(string sourceDirectoryName, System.IO.Stream destination, System.IO.Compression.CompressionLevel compressionLevel, bool includeBaseDirectory); + void CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName, System.IO.Compression.CompressionLevel compressionLevel, bool includeBaseDirectory); + void CreateFromDirectory(string sourceDirectoryName, System.IO.Stream destination, System.IO.Compression.CompressionLevel compressionLevel, bool includeBaseDirectory, System.Text.Encoding entryNameEncoding); + void CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName, System.IO.Compression.CompressionLevel compressionLevel, bool includeBaseDirectory, System.Text.Encoding entryNameEncoding); + System.Threading.Tasks.Task CreateFromDirectoryAsync(string sourceDirectoryName, System.IO.Stream destination, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task CreateFromDirectoryAsync(string sourceDirectoryName, string destinationArchiveFileName, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task CreateFromDirectoryAsync(string sourceDirectoryName, System.IO.Stream destination, System.IO.Compression.CompressionLevel compressionLevel, bool includeBaseDirectory, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task CreateFromDirectoryAsync(string sourceDirectoryName, string destinationArchiveFileName, System.IO.Compression.CompressionLevel compressionLevel, bool includeBaseDirectory, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task CreateFromDirectoryAsync(string sourceDirectoryName, System.IO.Stream destination, System.IO.Compression.CompressionLevel compressionLevel, bool includeBaseDirectory, System.Text.Encoding entryNameEncoding, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task CreateFromDirectoryAsync(string sourceDirectoryName, string destinationArchiveFileName, System.IO.Compression.CompressionLevel compressionLevel, bool includeBaseDirectory, System.Text.Encoding entryNameEncoding, System.Threading.CancellationToken cancellationToken = default); + void ExtractToDirectory(System.IO.Stream source, string destinationDirectoryName); + void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName); + void ExtractToDirectory(System.IO.Stream source, string destinationDirectoryName, System.Text.Encoding entryNameEncoding); + void ExtractToDirectory(System.IO.Stream source, string destinationDirectoryName, bool overwriteFiles); + void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, System.Text.Encoding? entryNameEncoding); + void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, bool overwriteFiles); + void ExtractToDirectory(System.IO.Stream source, string destinationDirectoryName, System.Text.Encoding entryNameEncoding, bool overwriteFiles); + void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, System.Text.Encoding? entryNameEncoding, bool overwriteFiles); + System.Threading.Tasks.Task ExtractToDirectoryAsync(System.IO.Stream source, string destinationDirectoryName, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task ExtractToDirectoryAsync(string sourceArchiveFileName, string destinationDirectoryName, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task ExtractToDirectoryAsync(System.IO.Stream source, string destinationDirectoryName, System.Text.Encoding entryNameEncoding, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task ExtractToDirectoryAsync(System.IO.Stream source, string destinationDirectoryName, bool overwriteFiles, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task ExtractToDirectoryAsync(string sourceArchiveFileName, string destinationDirectoryName, System.Text.Encoding? entryNameEncoding, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task ExtractToDirectoryAsync(string sourceArchiveFileName, string destinationDirectoryName, bool overwriteFiles, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task ExtractToDirectoryAsync(System.IO.Stream source, string destinationDirectoryName, System.Text.Encoding entryNameEncoding, bool overwriteFiles, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task ExtractToDirectoryAsync(string sourceArchiveFileName, string destinationDirectoryName, System.Text.Encoding? entryNameEncoding, bool overwriteFiles, System.Threading.CancellationToken cancellationToken = default); + Testably.Abstractions.IZipArchive Open(string archiveFileName, System.IO.Compression.ZipArchiveMode mode); + Testably.Abstractions.IZipArchive Open(string archiveFileName, System.IO.Compression.ZipArchiveMode mode, System.Text.Encoding? entryNameEncoding); + System.Threading.Tasks.Task OpenAsync(string archiveFileName, System.IO.Compression.ZipArchiveMode mode, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task OpenAsync(string archiveFileName, System.IO.Compression.ZipArchiveMode mode, System.Text.Encoding? entryNameEncoding, System.Threading.CancellationToken cancellationToken = default); + Testably.Abstractions.IZipArchive OpenRead(string archiveFileName); + System.Threading.Tasks.Task OpenReadAsync(string archiveFileName, System.Threading.CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions.Testing_net10.0.txt b/Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions.Testing_net10.0.txt new file mode 100644 index 000000000..a8541142e --- /dev/null +++ b/Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions.Testing_net10.0.txt @@ -0,0 +1,442 @@ +[assembly: System.CLSCompliant(true)] +[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Testably/Testably.Abstractions.git")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Testably.Abstractions.Testing.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001006104741100251820044d92b34b0519a1de0bccd80d6199aadbdcd5931d035462d42f70b0ae7a7db37bab63afb8a8ad0dc21392bb01f1243bfc51df4b5f1975b1b9746fecbed88913b783fccb69efc59e23b0e019e065abd38731711a2d6ac2569ab57d4b4d529f5903f5bee0f4388b2a5f4d5e0fddab6aac18d96aa78c2e73e0")] +[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v10.0", FrameworkDisplayName=".NET 10.0")] +namespace Testably.Abstractions.Testing.FileSystem +{ + public class ChangeDescription + { + public System.IO.WatcherChangeTypes ChangeType { get; } + public Testably.Abstractions.Testing.FileSystemTypes FileSystemType { get; } + public string? Name { get; } + public System.IO.NotifyFilters NotifyFilters { get; } + public string? OldName { get; } + public string? OldPath { get; } + public string Path { get; } + public override string ToString() { } + } + public class DefaultAccessControlStrategy : Testably.Abstractions.Testing.FileSystem.IAccessControlStrategy + { + public DefaultAccessControlStrategy(System.Func callback) { } + public bool IsAccessGranted(string fullPath, Testably.Abstractions.Helpers.IFileSystemExtensibility extensibility) { } + } + public class DefaultSafeFileHandleStrategy : Testably.Abstractions.Testing.FileSystem.ISafeFileHandleStrategy + { + public DefaultSafeFileHandleStrategy(System.Func callback) { } + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification="SafeFileHandle cannot be unit tested.")] + public Testably.Abstractions.Testing.FileSystem.SafeFileHandleMock MapSafeFileHandle(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle) { } + } + public interface IAccessControlStrategy + { + bool IsAccessGranted(string fullPath, Testably.Abstractions.Helpers.IFileSystemExtensibility extensibility); + } + public interface IInterceptionHandler : System.IO.Abstractions.IFileSystemEntity + { + Testably.Abstractions.Testing.IAwaitableCallback Event(System.Action interceptionCallback, System.Func? predicate = null); + } + public interface INotificationHandler : System.IO.Abstractions.IFileSystemEntity + { + Testably.Abstractions.Testing.IAwaitableCallback OnEvent(System.Action? notificationCallback = null, System.Func? predicate = null); + } + public interface ISafeFileHandleStrategy + { + Testably.Abstractions.Testing.FileSystem.SafeFileHandleMock MapSafeFileHandle(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle); + } + public interface IUnixFileModeStrategy + { + bool IsAccessGranted(string fullPath, Testably.Abstractions.Helpers.IFileSystemExtensibility extensibility, System.IO.UnixFileMode mode, System.IO.FileAccess requestedAccess); + void OnSetUnixFileMode(string fullPath, Testably.Abstractions.Helpers.IFileSystemExtensibility extensibility, System.IO.UnixFileMode mode); + } + public class NullAccessControlStrategy : Testably.Abstractions.Testing.FileSystem.IAccessControlStrategy + { + public NullAccessControlStrategy() { } + public bool IsAccessGranted(string fullPath, Testably.Abstractions.Helpers.IFileSystemExtensibility extensibility) { } + } + public class NullSafeFileHandleStrategy : Testably.Abstractions.Testing.FileSystem.ISafeFileHandleStrategy + { + public NullSafeFileHandleStrategy() { } + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification="SafeFileHandle cannot be unit tested.")] + public Testably.Abstractions.Testing.FileSystem.SafeFileHandleMock MapSafeFileHandle(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle) { } + } + public class NullUnixFileModeStrategy : Testably.Abstractions.Testing.FileSystem.IUnixFileModeStrategy + { + public NullUnixFileModeStrategy() { } + public bool IsAccessGranted(string fullPath, Testably.Abstractions.Helpers.IFileSystemExtensibility extensibility, System.IO.UnixFileMode mode, System.IO.FileAccess requestedAccess) { } + public void OnSetUnixFileMode(string fullPath, Testably.Abstractions.Helpers.IFileSystemExtensibility extensibility, System.IO.UnixFileMode mode) { } + } + public class SafeFileHandleMock + { + public SafeFileHandleMock(string path, System.IO.FileMode mode = 3, System.IO.FileShare share = 0) { } + public System.IO.FileMode Mode { get; } + public string Path { get; } + public System.IO.FileShare Share { get; } + } +} +namespace Testably.Abstractions.Testing +{ + public static class FileSystemInitializerExtensions + { + public static Testably.Abstractions.Testing.Initializer.IFileSystemInitializer Initialize(this TFileSystem fileSystem, System.Action? options = null) + where TFileSystem : System.IO.Abstractions.IFileSystem { } + public static void InitializeEmbeddedResourcesFromAssembly(this System.IO.Abstractions.IFileSystem fileSystem, string directoryPath, System.Reflection.Assembly assembly, string? relativePath = null, string searchPattern = "*", System.IO.SearchOption searchOption = 1) { } + public static Testably.Abstractions.Testing.Initializer.IFileSystemInitializer InitializeIn(this TFileSystem fileSystem, string basePath, System.Action? options = null) + where TFileSystem : System.IO.Abstractions.IFileSystem { } + public static Testably.Abstractions.Testing.Initializer.IDirectoryCleaner SetCurrentDirectoryToEmptyTemporaryDirectory(this System.IO.Abstractions.IFileSystem fileSystem, string? prefix = null, System.Action? logger = null) { } + } + public class FileSystemInitializerOptions + { + public FileSystemInitializerOptions() { } + public bool InitializeTempDirectory { get; set; } + } + [System.Flags] + public enum FileSystemTypes + { + Directory = 1, + File = 2, + DirectoryOrFile = 3, + } + public interface IAwaitableCallback : System.IDisposable + { + void Wait(System.Func? filter = null, int timeout = 30000, int count = 1, System.Action? executeWhenWaiting = null); + } + public static class InterceptionHandlerExtensions + { + public static Testably.Abstractions.Testing.IAwaitableCallback Changing(this Testably.Abstractions.Testing.FileSystem.IInterceptionHandler handler, Testably.Abstractions.Testing.FileSystemTypes fileSystemType, System.Action interceptionCallback, string globPattern = "*", System.Func? predicate = null) { } + public static Testably.Abstractions.Testing.IAwaitableCallback Creating(this Testably.Abstractions.Testing.FileSystem.IInterceptionHandler handler, Testably.Abstractions.Testing.FileSystemTypes fileSystemType, System.Action interceptionCallback, string globPattern = "*", System.Func? predicate = null) { } + public static Testably.Abstractions.Testing.IAwaitableCallback Deleting(this Testably.Abstractions.Testing.FileSystem.IInterceptionHandler handler, Testably.Abstractions.Testing.FileSystemTypes fileSystemType, System.Action interceptionCallback, string globPattern = "*", System.Func? predicate = null) { } + } + public sealed class MockFileSystem : System.IO.Abstractions.IFileSystem + { + public MockFileSystem() { } + public MockFileSystem(System.Func options) { } + public System.IO.Abstractions.IDirectory Directory { get; } + public System.IO.Abstractions.IDirectoryInfoFactory DirectoryInfo { get; } + public System.IO.Abstractions.IDriveInfoFactory DriveInfo { get; } + public System.IO.Abstractions.IFile File { get; } + public System.IO.Abstractions.IFileInfoFactory FileInfo { get; } + public System.IO.Abstractions.IFileStreamFactory FileStream { get; } + public System.IO.Abstractions.IFileSystemWatcherFactory FileSystemWatcher { get; } + public System.IO.Abstractions.IFileVersionInfoFactory FileVersionInfo { get; } + public Testably.Abstractions.Testing.FileSystem.IInterceptionHandler Intercept { get; } + public Testably.Abstractions.Testing.FileSystem.INotificationHandler Notify { get; } + public System.IO.Abstractions.IPath Path { get; } + public Testably.Abstractions.IRandomSystem RandomSystem { get; } + public Testably.Abstractions.Testing.SimulationMode SimulationMode { get; } + public Testably.Abstractions.Testing.Statistics.IFileSystemStatistics Statistics { get; } + public Testably.Abstractions.ITimeSystem TimeSystem { get; } + public override string ToString() { } + public Testably.Abstractions.Testing.MockFileSystem WithAccessControlStrategy(Testably.Abstractions.Testing.FileSystem.IAccessControlStrategy accessControlStrategy) { } + public Testably.Abstractions.Testing.MockFileSystem WithDrive(string? drive, System.Action? driveCallback = null) { } + public Testably.Abstractions.Testing.MockFileSystem WithFileVersionInfo(string globPattern, System.Action fileVersionInfoBuilder) { } + public Testably.Abstractions.Testing.MockFileSystem WithSafeFileHandleStrategy(Testably.Abstractions.Testing.FileSystem.ISafeFileHandleStrategy safeFileHandleStrategy) { } + public Testably.Abstractions.Testing.MockFileSystem WithUnixFileModeStrategy(Testably.Abstractions.Testing.FileSystem.IUnixFileModeStrategy unixFileModeStrategy) { } + public class MockFileSystemOptions + { + public MockFileSystemOptions() { } + public Testably.Abstractions.Testing.MockFileSystem.MockFileSystemOptions SimulatingOperatingSystem(Testably.Abstractions.Testing.SimulationMode simulationMode) { } + public Testably.Abstractions.Testing.MockFileSystem.MockFileSystemOptions UseCurrentDirectory() { } + public Testably.Abstractions.Testing.MockFileSystem.MockFileSystemOptions UseCurrentDirectory(string path) { } + public Testably.Abstractions.Testing.MockFileSystem.MockFileSystemOptions UseRandomProvider(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProvider) { } + public Testably.Abstractions.Testing.MockFileSystem.MockFileSystemOptions UseTimeSystem(Testably.Abstractions.ITimeSystem timeSystem) { } + } + } + public static class MockFileSystemExtensions + { + public static System.IO.Abstractions.IDriveInfo GetDefaultDrive(this Testably.Abstractions.Testing.MockFileSystem mockFileSystem) { } + public static Testably.Abstractions.Testing.MockFileSystem WithDrive(this Testably.Abstractions.Testing.MockFileSystem mockFileSystem, System.Action driveCallback) { } + public static Testably.Abstractions.Testing.MockFileSystem WithUncDrive(this Testably.Abstractions.Testing.MockFileSystem mockFileSystem, string server, System.Action? driveCallback = null) { } + } + public sealed class MockRandomSystem : Testably.Abstractions.IRandomSystem + { + public MockRandomSystem() { } + public MockRandomSystem(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProvider) { } + public Testably.Abstractions.RandomSystem.IGuid Guid { get; } + public Testably.Abstractions.RandomSystem.IRandomFactory Random { get; } + public Testably.Abstractions.Testing.RandomSystem.IRandomProvider RandomProvider { get; } + public override string ToString() { } + } + public sealed class MockTimeSystem : Testably.Abstractions.ITimeSystem + { + public MockTimeSystem() { } + public MockTimeSystem(System.DateTime time) { } + public MockTimeSystem(Testably.Abstractions.Testing.TimeSystem.ITimeProvider timeProvider) { } + public Testably.Abstractions.TimeSystem.IDateTime DateTime { get; } + public Testably.Abstractions.Testing.TimeSystem.INotificationHandler On { get; } + public Testably.Abstractions.TimeSystem.ITask Task { get; } + public Testably.Abstractions.TimeSystem.IThread Thread { get; } + public Testably.Abstractions.Testing.TimeSystem.ITimeProvider TimeProvider { get; } + public Testably.Abstractions.TimeSystem.ITimerFactory Timer { get; } + public Testably.Abstractions.Testing.TimeSystem.ITimerHandler TimerHandler { get; } + public override string ToString() { } + public Testably.Abstractions.Testing.MockTimeSystem WithTimerStrategy(Testably.Abstractions.Testing.TimeSystem.ITimerStrategy timerStrategy) { } + } + public static class Notification + { + public static Testably.Abstractions.Testing.IAwaitableCallback ExecuteWhileWaiting(this Testably.Abstractions.Testing.IAwaitableCallback awaitable, System.Action callback) { } + public static Testably.Abstractions.Testing.Notification.IAwaitableCallback ExecuteWhileWaiting(this Testably.Abstractions.Testing.IAwaitableCallback awaitable, System.Func callback) { } + public interface IAwaitableCallback : System.IDisposable, Testably.Abstractions.Testing.IAwaitableCallback + { + TFunc Wait(System.Func? filter = null, int timeout = 30000, int count = 1, System.Action? executeWhenWaiting = null); + } + } + public static class NotificationHandlerExtensions + { + public static Testably.Abstractions.Testing.IAwaitableCallback OnChanged(this Testably.Abstractions.Testing.FileSystem.INotificationHandler handler, Testably.Abstractions.Testing.FileSystemTypes fileSystemType, System.Action? notificationCallback = null, string globPattern = "*", System.Func? predicate = null) { } + public static Testably.Abstractions.Testing.IAwaitableCallback OnCreated(this Testably.Abstractions.Testing.FileSystem.INotificationHandler handler, Testably.Abstractions.Testing.FileSystemTypes fileSystemType, System.Action? notificationCallback = null, string globPattern = "*", System.Func? predicate = null) { } + public static Testably.Abstractions.Testing.IAwaitableCallback OnDeleted(this Testably.Abstractions.Testing.FileSystem.INotificationHandler handler, Testably.Abstractions.Testing.FileSystemTypes fileSystemType, System.Action? notificationCallback = null, string globPattern = "*", System.Func? predicate = null) { } + } + public static class RandomProvider + { + public static Testably.Abstractions.Testing.RandomSystem.IRandomProvider Default() { } + public static Testably.Abstractions.Testing.RandomSystem.IRandomProvider Generate(int seed = -1, Testably.Abstractions.Testing.RandomProvider.Generator? guidGenerator = null, Testably.Abstractions.Testing.RandomProvider.Generator? intGenerator = null, Testably.Abstractions.Testing.RandomProvider.Generator? longGenerator = null, Testably.Abstractions.Testing.RandomProvider.Generator? singleGenerator = null, Testably.Abstractions.Testing.RandomProvider.Generator? doubleGenerator = null, Testably.Abstractions.Testing.RandomProvider.Generator? byteGenerator = null) { } + public abstract class Generator + { + protected Generator() { } + public static Testably.Abstractions.Testing.RandomProvider.Generator FromArray(T[] values) { } + public static Testably.Abstractions.Testing.RandomProvider.Generator FromCallback(System.Func callback) { } + public static Testably.Abstractions.Testing.RandomProvider.Generator FromEnumerable(System.Collections.Generic.IEnumerable enumerable) { } + public static Testably.Abstractions.Testing.RandomProvider.Generator FromValue(T value) { } + } + public sealed class Generator : Testably.Abstractions.Testing.RandomProvider.Generator, System.IDisposable + { + public void Dispose() { } + public T GetNext() { } + public static Testably.Abstractions.Testing.RandomProvider.Generator op_Implicit(System.Func callback) { } + public static Testably.Abstractions.Testing.RandomProvider.Generator op_Implicit(T value) { } + public static Testably.Abstractions.Testing.RandomProvider.Generator op_Implicit(T[] values) { } + } + } + public enum SimulationMode + { + Native = 0, + Linux = 1, + MacOS = 2, + Windows = 3, + } + public static class TimeProvider + { + public static Testably.Abstractions.Testing.TimeSystem.ITimeProvider Now() { } + public static Testably.Abstractions.Testing.TimeSystem.ITimeProvider Random() { } + public static Testably.Abstractions.Testing.TimeSystem.ITimeProvider Use(System.DateTime time) { } + } +} +namespace Testably.Abstractions.Testing.Initializer +{ + public class DirectoryDescription : Testably.Abstractions.Testing.Initializer.FileSystemInfoDescription + { + public DirectoryDescription(string name) { } + public DirectoryDescription(string name, params Testably.Abstractions.Testing.Initializer.FileSystemInfoDescription[] children) { } + public Testably.Abstractions.Testing.Initializer.FileSystemInfoDescription[] Children { get; } + public override Testably.Abstractions.Testing.Initializer.FileSystemInfoDescription this[string path] { get; } + } + public class FileDescription : Testably.Abstractions.Testing.Initializer.FileSystemInfoDescription + { + public FileDescription(string name, byte[] bytes) { } + public FileDescription(string name, string? content = null) { } + public byte[]? Bytes { get; } + public string? Content { get; } + public bool IsReadOnly { get; set; } + public override Testably.Abstractions.Testing.Initializer.FileSystemInfoDescription this[string? path] { get; } + } + public abstract class FileSystemInfoDescription + { + protected FileSystemInfoDescription(string name) { } + public abstract Testably.Abstractions.Testing.Initializer.FileSystemInfoDescription this[string path] { get; } + public string Name { get; } + } + public sealed class FileVersionInfoBuilder + { + public FileVersionInfoBuilder() { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetComments(string? comments) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetCompanyName(string? companyName) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetFileDescription(string? fileDescription) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetFileVersion(string? fileVersion) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetInternalName(string? internalName) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetIsDebug(bool isDebug) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetIsPatched(bool isPatched) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetIsPreRelease(bool isPreRelease) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetIsPrivateBuild(bool isPrivateBuild) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetIsSpecialBuild(bool isSpecialBuild) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetLanguage(string? language) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetLegalCopyright(string? legalCopyright) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetLegalTrademarks(string? legalTrademarks) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetOriginalFilename(string? originalFilename) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetPrivateBuild(string? privateBuild) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetProductName(string? productName) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetProductVersion(string? productVersion) { } + public Testably.Abstractions.Testing.Initializer.FileVersionInfoBuilder SetSpecialBuild(string? specialBuild) { } + } + public interface IDirectoryCleaner : System.IDisposable + { + string BasePath { get; } + } + public interface IFileManipulator : System.IO.Abstractions.IFileSystemEntity + { + System.IO.Abstractions.IFileInfo File { get; } + Testably.Abstractions.Testing.Initializer.IFileManipulator HasBytesContent(byte[] bytes); + Testably.Abstractions.Testing.Initializer.IFileManipulator HasStringContent(string contents); + } + public interface IFileSystemDirectoryInitializer : Testably.Abstractions.Testing.Initializer.IFileSystemInitializer + where out TFileSystem : System.IO.Abstractions.IFileSystem + { + System.IO.Abstractions.IDirectoryInfo Directory { get; } + Testably.Abstractions.Testing.Initializer.IFileSystemDirectoryInitializer Initialized(System.Action> subdirectoryInitializer); + } + public interface IFileSystemFileInitializer : Testably.Abstractions.Testing.Initializer.IFileSystemInitializer + where out TFileSystem : System.IO.Abstractions.IFileSystem + { + System.IO.Abstractions.IFileInfo File { get; } + Testably.Abstractions.Testing.Initializer.IFileSystemFileInitializer Which(System.Action fileManipulation); + } + public interface IFileSystemInitializer + where out TFileSystem : System.IO.Abstractions.IFileSystem + { + System.IO.Abstractions.IDirectoryInfo BaseDirectory { get; } + TFileSystem FileSystem { get; } + System.IO.Abstractions.IFileSystemInfo this[int index] { get; } + Testably.Abstractions.Testing.Initializer.IFileSystemInitializer With(params Testably.Abstractions.Testing.Initializer.FileSystemInfoDescription[] descriptions); + Testably.Abstractions.Testing.Initializer.IFileSystemInitializer With(TDescription[] descriptions) + where TDescription : Testably.Abstractions.Testing.Initializer.FileSystemInfoDescription; + Testably.Abstractions.Testing.Initializer.IFileSystemFileInitializer WithAFile(string? extension = null); + Testably.Abstractions.Testing.Initializer.IFileSystemDirectoryInitializer WithASubdirectory(); + Testably.Abstractions.Testing.Initializer.IFileSystemFileInitializer WithFile(string fileName); + Testably.Abstractions.Testing.Initializer.IFileSystemInitializer WithSubdirectories(params string[] paths); + Testably.Abstractions.Testing.Initializer.IFileSystemDirectoryInitializer WithSubdirectory(string directoryName); + } + public class TestingException : System.Exception + { + public TestingException(string message) { } + public TestingException(string message, System.Exception inner) { } + } +} +namespace Testably.Abstractions.Testing.RandomSystem +{ + public interface IRandomProvider + { + System.Guid GetGuid(); + Testably.Abstractions.RandomSystem.IRandom GetRandom(int seed = -1); + } +} +namespace Testably.Abstractions.Testing.Statistics +{ + public interface IFileSystemStatistics + { + Testably.Abstractions.Testing.Statistics.IStatistics Directory { get; } + Testably.Abstractions.Testing.Statistics.IPathStatistics DirectoryInfo { get; } + Testably.Abstractions.Testing.Statistics.IPathStatistics DriveInfo { get; } + Testably.Abstractions.Testing.Statistics.IStatistics File { get; } + Testably.Abstractions.Testing.Statistics.IPathStatistics FileInfo { get; } + Testably.Abstractions.Testing.Statistics.IPathStatistics FileStream { get; } + Testably.Abstractions.Testing.Statistics.IPathStatistics FileSystemWatcher { get; } + Testably.Abstractions.Testing.Statistics.IPathStatistics FileVersionInfo { get; } + Testably.Abstractions.Testing.Statistics.IStatistics Path { get; } + int TotalCount { get; } + } + public interface IPathStatistics : Testably.Abstractions.Testing.Statistics.IStatistics, Testably.Abstractions.Testing.Statistics.IStatistics + { + Testably.Abstractions.Testing.Statistics.IStatistics this[string path] { get; } + } + public interface IStatistics + { + Testably.Abstractions.Testing.Statistics.MethodStatistic[] Methods { get; } + Testably.Abstractions.Testing.Statistics.PropertyStatistic[] Properties { get; } + } + public interface IStatistics : Testably.Abstractions.Testing.Statistics.IStatistics { } + public sealed class MethodStatistic + { + public int Counter { get; } + public string Name { get; } + public Testably.Abstractions.Testing.Statistics.ParameterDescription[] Parameters { get; } + public override string ToString() { } + } + public abstract class ParameterDescription + { + protected ParameterDescription(bool isOutParameter) { } + public bool IsOutParameter { get; } + public bool Is(System.Func comparer) { } + public bool Is(System.ReadOnlySpan value) { } + public bool Is(System.Span value) { } + public bool Is(T value) { } + public bool Is(T[] value) { } + public bool Is(Testably.Abstractions.Testing.Statistics.ParameterDescription.SpanParameterDescription value) { } + public static Testably.Abstractions.Testing.Statistics.ParameterDescription FromOutParameter(T value) { } + public static Testably.Abstractions.Testing.Statistics.ParameterDescription FromParameter(System.ReadOnlySpan value) { } + public static Testably.Abstractions.Testing.Statistics.ParameterDescription FromParameter(System.Span value) { } + public static Testably.Abstractions.Testing.Statistics.ParameterDescription FromParameter(T value) { } + public sealed class SpanParameterDescription : Testably.Abstractions.Testing.Statistics.ParameterDescription + { + public SpanParameterDescription(System.ReadOnlySpan value) { } + public SpanParameterDescription(System.Span value) { } + public bool IsReadOnly { get; } + public T[] Value { get; } + public override string ToString() { } + } + } + public enum PropertyAccess + { + Get = 0, + Set = 1, + } + public sealed class PropertyStatistic + { + public Testably.Abstractions.Testing.Statistics.PropertyAccess Access { get; } + public int Counter { get; } + public string Name { get; } + public override string ToString() { } + } +} +namespace Testably.Abstractions.Testing.Storage +{ + public interface IStorageDrive : System.IO.Abstractions.IDriveInfo, System.IO.Abstractions.IFileSystemEntity + { + bool IsUncPath { get; } + Testably.Abstractions.Testing.Storage.IStorageDrive ChangeUsedBytes(long usedBytesDelta); + Testably.Abstractions.Testing.Storage.IStorageDrive SetDriveFormat(string driveFormat = "NTFS"); + Testably.Abstractions.Testing.Storage.IStorageDrive SetDriveType(System.IO.DriveType driveType = 3); + Testably.Abstractions.Testing.Storage.IStorageDrive SetIsReady(bool isReady = true); + Testably.Abstractions.Testing.Storage.IStorageDrive SetTotalSize(long totalSize = 1073741824); + } +} +namespace Testably.Abstractions.Testing.TimeSystem +{ + public interface INotificationHandler + { + Testably.Abstractions.Testing.IAwaitableCallback DateTimeRead(System.Action? callback = null, System.Func? predicate = null); + Testably.Abstractions.Testing.IAwaitableCallback TaskDelay(System.Action? callback = null, System.Func? predicate = null); + Testably.Abstractions.Testing.IAwaitableCallback ThreadSleep(System.Action? callback = null, System.Func? predicate = null); + } + public interface ITimeProvider + { + System.DateTime MaxValue { get; set; } + System.DateTime MinValue { get; set; } + System.DateTime UnixEpoch { get; set; } + void AdvanceBy(System.TimeSpan interval); + System.DateTime Read(); + void SetTo(System.DateTime value); + } + public interface ITimerHandler + { + Testably.Abstractions.Testing.TimeSystem.ITimerMock this[int index] { get; } + } + public interface ITimerMock : System.IAsyncDisposable, System.IDisposable, Testably.Abstractions.TimeSystem.ITimeSystemEntity, Testably.Abstractions.TimeSystem.ITimer + { + Testably.Abstractions.Testing.TimeSystem.ITimerMock Wait(int executionCount = 1, int timeout = 10000, System.Action? callback = null); + } + public interface ITimerStrategy + { + Testably.Abstractions.Testing.TimeSystem.TimerMode Mode { get; } + bool SwallowExceptions { get; } + } + public enum TimerMode + { + StartImmediately = 1, + StartOnMockWait = 2, + } + public class TimerStrategy : Testably.Abstractions.Testing.TimeSystem.ITimerStrategy + { + public TimerStrategy(Testably.Abstractions.Testing.TimeSystem.TimerMode mode = 1, bool swallowExceptions = false) { } + public Testably.Abstractions.Testing.TimeSystem.TimerMode Mode { get; } + public bool SwallowExceptions { get; } + public static Testably.Abstractions.Testing.TimeSystem.ITimerStrategy Default { get; } + } +} \ No newline at end of file diff --git a/Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions_net10.0.txt b/Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions_net10.0.txt new file mode 100644 index 000000000..b6839ae06 --- /dev/null +++ b/Tests/Api/Testably.Abstractions.Api.Tests/Expected/Testably.Abstractions_net10.0.txt @@ -0,0 +1,34 @@ +[assembly: System.CLSCompliant(true)] +[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Testably/Testably.Abstractions.git")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Testably.Abstractions.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001006104741100251820044d92b34b0519a1de0bccd80d6199aadbdcd5931d035462d42f70b0ae7a7db37bab63afb8a8ad0dc21392bb01f1243bfc51df4b5f1975b1b9746fecbed88913b783fccb69efc59e23b0e019e065abd38731711a2d6ac2569ab57d4b4d529f5903f5bee0f4388b2a5f4d5e0fddab6aac18d96aa78c2e73e0")] +[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v10.0", FrameworkDisplayName=".NET 10.0")] +namespace Testably.Abstractions +{ + public sealed class RealFileSystem : System.IO.Abstractions.IFileSystem + { + public RealFileSystem() { } + public System.IO.Abstractions.IDirectory Directory { get; } + public System.IO.Abstractions.IDirectoryInfoFactory DirectoryInfo { get; } + public System.IO.Abstractions.IDriveInfoFactory DriveInfo { get; } + public System.IO.Abstractions.IFile File { get; } + public System.IO.Abstractions.IFileInfoFactory FileInfo { get; } + public System.IO.Abstractions.IFileStreamFactory FileStream { get; } + public System.IO.Abstractions.IFileSystemWatcherFactory FileSystemWatcher { get; } + public System.IO.Abstractions.IFileVersionInfoFactory FileVersionInfo { get; } + public System.IO.Abstractions.IPath Path { get; } + } + public sealed class RealRandomSystem : Testably.Abstractions.IRandomSystem + { + public RealRandomSystem() { } + public Testably.Abstractions.RandomSystem.IGuid Guid { get; } + public Testably.Abstractions.RandomSystem.IRandomFactory Random { get; } + } + public sealed class RealTimeSystem : Testably.Abstractions.ITimeSystem + { + public RealTimeSystem() { } + public Testably.Abstractions.TimeSystem.IDateTime DateTime { get; } + public Testably.Abstractions.TimeSystem.ITask Task { get; } + public Testably.Abstractions.TimeSystem.IThread Thread { get; } + public Testably.Abstractions.TimeSystem.ITimerFactory Timer { get; } + } +} \ No newline at end of file diff --git a/Tests/Api/Testably.Abstractions.Core.Api.Tests/Expected/Testably.Abstractions.FileSystem.Interface_net10.0.txt b/Tests/Api/Testably.Abstractions.Core.Api.Tests/Expected/Testably.Abstractions.FileSystem.Interface_net10.0.txt new file mode 100644 index 000000000..2e9f89dfd --- /dev/null +++ b/Tests/Api/Testably.Abstractions.Core.Api.Tests/Expected/Testably.Abstractions.FileSystem.Interface_net10.0.txt @@ -0,0 +1,501 @@ +[assembly: System.CLSCompliant(true)] +[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Testably/Testably.Abstractions.git")] +[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v10.0", FrameworkDisplayName=".NET 10.0")] +namespace System.IO.Abstractions +{ + public abstract class FileSystemStream : System.IO.Stream + { + protected FileSystemStream(System.IO.Stream stream, string path, bool isAsync) { } + public override bool CanRead { get; } + public override bool CanSeek { get; } + public override bool CanTimeout { get; } + public override bool CanWrite { get; } + public virtual bool IsAsync { get; } + public override long Length { get; } + public virtual string Name { get; } + public override long Position { get; set; } + public override int ReadTimeout { get; set; } + public override int WriteTimeout { get; set; } + public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback? callback, object? state) { } + public override System.IAsyncResult BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback? callback, object? state) { } + public override void Close() { } + public override void CopyTo(System.IO.Stream destination, int bufferSize) { } + public override System.Threading.Tasks.Task CopyToAsync(System.IO.Stream destination, int bufferSize, System.Threading.CancellationToken cancellationToken) { } + protected override void Dispose(bool disposing) { } + public override int EndRead(System.IAsyncResult asyncResult) { } + public override void EndWrite(System.IAsyncResult asyncResult) { } + public override void Flush() { } + public virtual void Flush(bool flushToDisk) { } + public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) { } + public override int Read(System.Span buffer) { } + public override int Read(byte[] buffer, int offset, int count) { } + public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken = default) { } + public override System.Threading.Tasks.Task ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { } + public override int ReadByte() { } + public override long Seek(long offset, System.IO.SeekOrigin origin) { } + public override void SetLength(long value) { } + public override string? ToString() { } + public override void Write(System.ReadOnlySpan buffer) { } + public override void Write(byte[] buffer, int offset, int count) { } + public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default) { } + public override System.Threading.Tasks.Task WriteAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { } + public override void WriteByte(byte value) { } + public static System.IO.FileStream op_Explicit(System.IO.Abstractions.FileSystemStream fsStream) { } + } + public interface IDirectory : System.IO.Abstractions.IFileSystemEntity + { + System.IO.Abstractions.IDirectoryInfo CreateDirectory(string path); + System.IO.Abstractions.IDirectoryInfo CreateDirectory(string path, System.IO.UnixFileMode unixCreateMode); + System.IO.Abstractions.IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget); + System.IO.Abstractions.IDirectoryInfo CreateTempSubdirectory(string? prefix = null); + void Delete(string path); + void Delete(string path, bool recursive); + System.Collections.Generic.IEnumerable EnumerateDirectories(string path); + System.Collections.Generic.IEnumerable EnumerateDirectories(string path, string searchPattern); + System.Collections.Generic.IEnumerable EnumerateDirectories(string path, string searchPattern, System.IO.EnumerationOptions enumerationOptions); + System.Collections.Generic.IEnumerable EnumerateDirectories(string path, string searchPattern, System.IO.SearchOption searchOption); + System.Collections.Generic.IEnumerable EnumerateFileSystemEntries(string path); + System.Collections.Generic.IEnumerable EnumerateFileSystemEntries(string path, string searchPattern); + System.Collections.Generic.IEnumerable EnumerateFileSystemEntries(string path, string searchPattern, System.IO.EnumerationOptions enumerationOptions); + System.Collections.Generic.IEnumerable EnumerateFileSystemEntries(string path, string searchPattern, System.IO.SearchOption searchOption); + System.Collections.Generic.IEnumerable EnumerateFiles(string path); + System.Collections.Generic.IEnumerable EnumerateFiles(string path, string searchPattern); + System.Collections.Generic.IEnumerable EnumerateFiles(string path, string searchPattern, System.IO.EnumerationOptions enumerationOptions); + System.Collections.Generic.IEnumerable EnumerateFiles(string path, string searchPattern, System.IO.SearchOption searchOption); + bool Exists([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? path); + System.DateTime GetCreationTime(string path); + System.DateTime GetCreationTimeUtc(string path); + string GetCurrentDirectory(); + string[] GetDirectories(string path); + string[] GetDirectories(string path, string searchPattern); + string[] GetDirectories(string path, string searchPattern, System.IO.EnumerationOptions enumerationOptions); + string[] GetDirectories(string path, string searchPattern, System.IO.SearchOption searchOption); + string GetDirectoryRoot(string path); + string[] GetFileSystemEntries(string path); + string[] GetFileSystemEntries(string path, string searchPattern); + string[] GetFileSystemEntries(string path, string searchPattern, System.IO.EnumerationOptions enumerationOptions); + string[] GetFileSystemEntries(string path, string searchPattern, System.IO.SearchOption searchOption); + string[] GetFiles(string path); + string[] GetFiles(string path, string searchPattern); + string[] GetFiles(string path, string searchPattern, System.IO.EnumerationOptions enumerationOptions); + string[] GetFiles(string path, string searchPattern, System.IO.SearchOption searchOption); + System.DateTime GetLastAccessTime(string path); + System.DateTime GetLastAccessTimeUtc(string path); + System.DateTime GetLastWriteTime(string path); + System.DateTime GetLastWriteTimeUtc(string path); + string[] GetLogicalDrives(); + System.IO.Abstractions.IDirectoryInfo? GetParent(string path); + void Move(string sourceDirName, string destDirName); + System.IO.Abstractions.IFileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget); + void SetCreationTime(string path, System.DateTime creationTime); + void SetCreationTimeUtc(string path, System.DateTime creationTimeUtc); + void SetCurrentDirectory(string path); + void SetLastAccessTime(string path, System.DateTime lastAccessTime); + void SetLastAccessTimeUtc(string path, System.DateTime lastAccessTimeUtc); + void SetLastWriteTime(string path, System.DateTime lastWriteTime); + void SetLastWriteTimeUtc(string path, System.DateTime lastWriteTimeUtc); + } + public interface IDirectoryInfo : System.IO.Abstractions.IFileSystemInfo + { + System.IO.Abstractions.IDirectoryInfo? Parent { get; } + System.IO.Abstractions.IDirectoryInfo Root { get; } + void Create(); + System.IO.Abstractions.IDirectoryInfo CreateSubdirectory(string path); + void Delete(bool recursive); + System.Collections.Generic.IEnumerable EnumerateDirectories(); + System.Collections.Generic.IEnumerable EnumerateDirectories(string searchPattern); + System.Collections.Generic.IEnumerable EnumerateDirectories(string searchPattern, System.IO.EnumerationOptions enumerationOptions); + System.Collections.Generic.IEnumerable EnumerateDirectories(string searchPattern, System.IO.SearchOption searchOption); + System.Collections.Generic.IEnumerable EnumerateFileSystemInfos(); + System.Collections.Generic.IEnumerable EnumerateFileSystemInfos(string searchPattern); + System.Collections.Generic.IEnumerable EnumerateFileSystemInfos(string searchPattern, System.IO.EnumerationOptions enumerationOptions); + System.Collections.Generic.IEnumerable EnumerateFileSystemInfos(string searchPattern, System.IO.SearchOption searchOption); + System.Collections.Generic.IEnumerable EnumerateFiles(); + System.Collections.Generic.IEnumerable EnumerateFiles(string searchPattern); + System.Collections.Generic.IEnumerable EnumerateFiles(string searchPattern, System.IO.EnumerationOptions enumerationOptions); + System.Collections.Generic.IEnumerable EnumerateFiles(string searchPattern, System.IO.SearchOption searchOption); + System.IO.Abstractions.IDirectoryInfo[] GetDirectories(); + System.IO.Abstractions.IDirectoryInfo[] GetDirectories(string searchPattern); + System.IO.Abstractions.IDirectoryInfo[] GetDirectories(string searchPattern, System.IO.EnumerationOptions enumerationOptions); + System.IO.Abstractions.IDirectoryInfo[] GetDirectories(string searchPattern, System.IO.SearchOption searchOption); + System.IO.Abstractions.IFileSystemInfo[] GetFileSystemInfos(); + System.IO.Abstractions.IFileSystemInfo[] GetFileSystemInfos(string searchPattern); + System.IO.Abstractions.IFileSystemInfo[] GetFileSystemInfos(string searchPattern, System.IO.EnumerationOptions enumerationOptions); + System.IO.Abstractions.IFileSystemInfo[] GetFileSystemInfos(string searchPattern, System.IO.SearchOption searchOption); + System.IO.Abstractions.IFileInfo[] GetFiles(); + System.IO.Abstractions.IFileInfo[] GetFiles(string searchPattern); + System.IO.Abstractions.IFileInfo[] GetFiles(string searchPattern, System.IO.EnumerationOptions enumerationOptions); + System.IO.Abstractions.IFileInfo[] GetFiles(string searchPattern, System.IO.SearchOption searchOption); + void MoveTo(string destDirName); + } + public interface IDirectoryInfoFactory : System.IO.Abstractions.IFileSystemEntity + { + System.IO.Abstractions.IDirectoryInfo New(string path); + [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("directoryInfo")] + System.IO.Abstractions.IDirectoryInfo? Wrap(System.IO.DirectoryInfo? directoryInfo); + } + public interface IDriveInfo : System.IO.Abstractions.IFileSystemEntity + { + long AvailableFreeSpace { get; } + string DriveFormat { get; } + System.IO.DriveType DriveType { get; } + bool IsReady { get; } + string Name { get; } + System.IO.Abstractions.IDirectoryInfo RootDirectory { get; } + long TotalFreeSpace { get; } + long TotalSize { get; } + [set: System.Runtime.Versioning.SupportedOSPlatform("windows")] + string VolumeLabel { get; set; } + } + public interface IDriveInfoFactory : System.IO.Abstractions.IFileSystemEntity + { + System.IO.Abstractions.IDriveInfo[] GetDrives(); + System.IO.Abstractions.IDriveInfo New(string driveName); + [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("driveInfo")] + System.IO.Abstractions.IDriveInfo? Wrap(System.IO.DriveInfo? driveInfo); + } + public interface IFile : System.IO.Abstractions.IFileSystemEntity + { + void AppendAllBytes(string path, System.ReadOnlySpan bytes); + void AppendAllBytes(string path, byte[] bytes); + System.Threading.Tasks.Task AppendAllBytesAsync(string path, System.ReadOnlyMemory bytes, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task AppendAllBytesAsync(string path, byte[] bytes, System.Threading.CancellationToken cancellationToken = default); + void AppendAllLines(string path, System.Collections.Generic.IEnumerable contents); + void AppendAllLines(string path, System.Collections.Generic.IEnumerable contents, System.Text.Encoding encoding); + System.Threading.Tasks.Task AppendAllLinesAsync(string path, System.Collections.Generic.IEnumerable contents, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task AppendAllLinesAsync(string path, System.Collections.Generic.IEnumerable contents, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken = default); + void AppendAllText(string path, System.ReadOnlySpan contents); + void AppendAllText(string path, string? contents); + void AppendAllText(string path, System.ReadOnlySpan contents, System.Text.Encoding encoding); + void AppendAllText(string path, string? contents, System.Text.Encoding encoding); + System.Threading.Tasks.Task AppendAllTextAsync(string path, System.ReadOnlyMemory contents, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task AppendAllTextAsync(string path, string? contents, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task AppendAllTextAsync(string path, System.ReadOnlyMemory contents, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task AppendAllTextAsync(string path, string? contents, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken = default); + System.IO.StreamWriter AppendText(string path); + void Copy(string sourceFileName, string destFileName); + void Copy(string sourceFileName, string destFileName, bool overwrite); + System.IO.Abstractions.FileSystemStream Create(string path); + System.IO.Abstractions.FileSystemStream Create(string path, int bufferSize); + System.IO.Abstractions.FileSystemStream Create(string path, int bufferSize, System.IO.FileOptions options); + System.IO.Abstractions.IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget); + System.IO.StreamWriter CreateText(string path); + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + void Decrypt(string path); + void Delete(string path); + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + void Encrypt(string path); + bool Exists([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? path); + System.IO.FileAttributes GetAttributes(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle); + System.IO.FileAttributes GetAttributes(string path); + System.DateTime GetCreationTime(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle); + System.DateTime GetCreationTime(string path); + System.DateTime GetCreationTimeUtc(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle); + System.DateTime GetCreationTimeUtc(string path); + System.DateTime GetLastAccessTime(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle); + System.DateTime GetLastAccessTime(string path); + System.DateTime GetLastAccessTimeUtc(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle); + System.DateTime GetLastAccessTimeUtc(string path); + System.DateTime GetLastWriteTime(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle); + System.DateTime GetLastWriteTime(string path); + System.DateTime GetLastWriteTimeUtc(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle); + System.DateTime GetLastWriteTimeUtc(string path); + [System.Runtime.Versioning.UnsupportedOSPlatform("windows")] + System.IO.UnixFileMode GetUnixFileMode(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle); + [System.Runtime.Versioning.UnsupportedOSPlatform("windows")] + System.IO.UnixFileMode GetUnixFileMode(string path); + void Move(string sourceFileName, string destFileName); + void Move(string sourceFileName, string destFileName, bool overwrite); + System.IO.Abstractions.FileSystemStream Open(string path, System.IO.FileMode mode); + System.IO.Abstractions.FileSystemStream Open(string path, System.IO.FileStreamOptions options); + System.IO.Abstractions.FileSystemStream Open(string path, System.IO.FileMode mode, System.IO.FileAccess access); + System.IO.Abstractions.FileSystemStream Open(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share); + System.IO.Abstractions.FileSystemStream OpenRead(string path); + System.IO.StreamReader OpenText(string path); + System.IO.Abstractions.FileSystemStream OpenWrite(string path); + byte[] ReadAllBytes(string path); + System.Threading.Tasks.Task ReadAllBytesAsync(string path, System.Threading.CancellationToken cancellationToken = default); + string[] ReadAllLines(string path); + string[] ReadAllLines(string path, System.Text.Encoding encoding); + System.Threading.Tasks.Task ReadAllLinesAsync(string path, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task ReadAllLinesAsync(string path, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken = default); + string ReadAllText(string path); + string ReadAllText(string path, System.Text.Encoding encoding); + System.Threading.Tasks.Task ReadAllTextAsync(string path, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task ReadAllTextAsync(string path, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken = default); + System.Collections.Generic.IEnumerable ReadLines(string path); + System.Collections.Generic.IEnumerable ReadLines(string path, System.Text.Encoding encoding); + System.Collections.Generic.IAsyncEnumerable ReadLinesAsync(string path, System.Threading.CancellationToken cancellationToken = default); + System.Collections.Generic.IAsyncEnumerable ReadLinesAsync(string path, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken = default); + void Replace(string sourceFileName, string destinationFileName, string? destinationBackupFileName); + void Replace(string sourceFileName, string destinationFileName, string? destinationBackupFileName, bool ignoreMetadataErrors); + System.IO.Abstractions.IFileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget); + void SetAttributes(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle, System.IO.FileAttributes fileAttributes); + void SetAttributes(string path, System.IO.FileAttributes fileAttributes); + void SetCreationTime(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle, System.DateTime creationTime); + void SetCreationTime(string path, System.DateTime creationTime); + void SetCreationTimeUtc(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle, System.DateTime creationTimeUtc); + void SetCreationTimeUtc(string path, System.DateTime creationTimeUtc); + void SetLastAccessTime(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle, System.DateTime lastAccessTime); + void SetLastAccessTime(string path, System.DateTime lastAccessTime); + void SetLastAccessTimeUtc(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle, System.DateTime lastAccessTimeUtc); + void SetLastAccessTimeUtc(string path, System.DateTime lastAccessTimeUtc); + void SetLastWriteTime(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle, System.DateTime lastWriteTime); + void SetLastWriteTime(string path, System.DateTime lastWriteTime); + void SetLastWriteTimeUtc(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle, System.DateTime lastWriteTimeUtc); + void SetLastWriteTimeUtc(string path, System.DateTime lastWriteTimeUtc); + [System.Runtime.Versioning.UnsupportedOSPlatform("windows")] + void SetUnixFileMode(Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle, System.IO.UnixFileMode mode); + [System.Runtime.Versioning.UnsupportedOSPlatform("windows")] + void SetUnixFileMode(string path, System.IO.UnixFileMode mode); + void WriteAllBytes(string path, System.ReadOnlySpan bytes); + void WriteAllBytes(string path, byte[] bytes); + System.Threading.Tasks.Task WriteAllBytesAsync(string path, System.ReadOnlyMemory bytes, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task WriteAllBytesAsync(string path, byte[] bytes, System.Threading.CancellationToken cancellationToken = default); + void WriteAllLines(string path, System.Collections.Generic.IEnumerable contents); + void WriteAllLines(string path, string[] contents); + void WriteAllLines(string path, System.Collections.Generic.IEnumerable contents, System.Text.Encoding encoding); + void WriteAllLines(string path, string[] contents, System.Text.Encoding encoding); + System.Threading.Tasks.Task WriteAllLinesAsync(string path, System.Collections.Generic.IEnumerable contents, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task WriteAllLinesAsync(string path, System.Collections.Generic.IEnumerable contents, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken = default); + void WriteAllText(string path, System.ReadOnlySpan contents); + void WriteAllText(string path, string? contents); + void WriteAllText(string path, System.ReadOnlySpan contents, System.Text.Encoding encoding); + void WriteAllText(string path, string? contents, System.Text.Encoding encoding); + System.Threading.Tasks.Task WriteAllTextAsync(string path, System.ReadOnlyMemory contents, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task WriteAllTextAsync(string path, string? contents, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task WriteAllTextAsync(string path, System.ReadOnlyMemory contents, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task WriteAllTextAsync(string path, string? contents, System.Text.Encoding encoding, System.Threading.CancellationToken cancellationToken = default); + } + public interface IFileInfo : System.IO.Abstractions.IFileSystemInfo + { + System.IO.Abstractions.IDirectoryInfo? Directory { get; } + string? DirectoryName { get; } + bool IsReadOnly { get; set; } + long Length { get; } + System.IO.StreamWriter AppendText(); + System.IO.Abstractions.IFileInfo CopyTo(string destFileName); + System.IO.Abstractions.IFileInfo CopyTo(string destFileName, bool overwrite); + System.IO.Abstractions.FileSystemStream Create(); + System.IO.StreamWriter CreateText(); + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + void Decrypt(); + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + void Encrypt(); + void MoveTo(string destFileName); + void MoveTo(string destFileName, bool overwrite); + System.IO.Abstractions.FileSystemStream Open(System.IO.FileMode mode); + System.IO.Abstractions.FileSystemStream Open(System.IO.FileStreamOptions options); + System.IO.Abstractions.FileSystemStream Open(System.IO.FileMode mode, System.IO.FileAccess access); + System.IO.Abstractions.FileSystemStream Open(System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share); + System.IO.Abstractions.FileSystemStream OpenRead(); + System.IO.StreamReader OpenText(); + System.IO.Abstractions.FileSystemStream OpenWrite(); + System.IO.Abstractions.IFileInfo Replace(string destinationFileName, string? destinationBackupFileName); + System.IO.Abstractions.IFileInfo Replace(string destinationFileName, string? destinationBackupFileName, bool ignoreMetadataErrors); + } + public interface IFileInfoFactory : System.IO.Abstractions.IFileSystemEntity + { + System.IO.Abstractions.IFileInfo New(string fileName); + [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("fileInfo")] + System.IO.Abstractions.IFileInfo? Wrap(System.IO.FileInfo? fileInfo); + } + public interface IFileStreamFactory : System.IO.Abstractions.IFileSystemEntity + { + System.IO.Abstractions.FileSystemStream New(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.IO.FileAccess access); + System.IO.Abstractions.FileSystemStream New(string path, System.IO.FileMode mode); + System.IO.Abstractions.FileSystemStream New(string path, System.IO.FileStreamOptions options); + System.IO.Abstractions.FileSystemStream New(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.IO.FileAccess access, int bufferSize); + System.IO.Abstractions.FileSystemStream New(string path, System.IO.FileMode mode, System.IO.FileAccess access); + System.IO.Abstractions.FileSystemStream New(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.IO.FileAccess access, int bufferSize, bool isAsync); + System.IO.Abstractions.FileSystemStream New(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share); + System.IO.Abstractions.FileSystemStream New(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, int bufferSize); + System.IO.Abstractions.FileSystemStream New(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, int bufferSize, System.IO.FileOptions options); + System.IO.Abstractions.FileSystemStream New(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, int bufferSize, bool useAsync); + System.IO.Abstractions.FileSystemStream Wrap(System.IO.FileStream fileStream); + } + public interface IFileSystem + { + System.IO.Abstractions.IDirectory Directory { get; } + System.IO.Abstractions.IDirectoryInfoFactory DirectoryInfo { get; } + System.IO.Abstractions.IDriveInfoFactory DriveInfo { get; } + System.IO.Abstractions.IFile File { get; } + System.IO.Abstractions.IFileInfoFactory FileInfo { get; } + System.IO.Abstractions.IFileStreamFactory FileStream { get; } + System.IO.Abstractions.IFileSystemWatcherFactory FileSystemWatcher { get; } + System.IO.Abstractions.IFileVersionInfoFactory FileVersionInfo { get; } + System.IO.Abstractions.IPath Path { get; } + } + public interface IFileSystemAclSupport + { + object GetAccessControl(); + object GetAccessControl(System.IO.Abstractions.IFileSystemAclSupport.AccessControlSections includeSections); + void SetAccessControl(object value); + [System.Flags] + public enum AccessControlSections + { + None = 0, + Audit = 1, + Access = 2, + Owner = 4, + Group = 8, + All = 15, + } + } + public interface IFileSystemEntity + { + System.IO.Abstractions.IFileSystem FileSystem { get; } + } + public interface IFileSystemInfo + { + System.IO.FileAttributes Attributes { get; set; } + System.DateTime CreationTime { get; set; } + System.DateTime CreationTimeUtc { get; set; } + bool Exists { get; } + string Extension { get; } + System.IO.Abstractions.IFileSystem FileSystem { get; } + string FullName { get; } + System.DateTime LastAccessTime { get; set; } + System.DateTime LastAccessTimeUtc { get; set; } + System.DateTime LastWriteTime { get; set; } + System.DateTime LastWriteTimeUtc { get; set; } + string? LinkTarget { get; } + string Name { get; } + [set: System.Runtime.Versioning.UnsupportedOSPlatform("windows")] + System.IO.UnixFileMode UnixFileMode { get; set; } + void CreateAsSymbolicLink(string pathToTarget); + void Delete(); + void Refresh(); + System.IO.Abstractions.IFileSystemInfo? ResolveLinkTarget(bool returnFinalTarget); + } + public interface IFileSystemWatcher : System.IDisposable, System.IO.Abstractions.IFileSystemEntity + { + System.ComponentModel.IContainer? Container { get; } + bool EnableRaisingEvents { get; set; } + string Filter { get; set; } + System.Collections.ObjectModel.Collection Filters { get; } + bool IncludeSubdirectories { get; set; } + int InternalBufferSize { get; set; } + System.IO.NotifyFilters NotifyFilter { get; set; } + string Path { get; set; } + System.ComponentModel.ISite? Site { get; set; } + System.ComponentModel.ISynchronizeInvoke? SynchronizingObject { get; set; } + event System.IO.FileSystemEventHandler? Changed; + event System.IO.FileSystemEventHandler? Created; + event System.IO.FileSystemEventHandler? Deleted; + event System.IO.ErrorEventHandler? Error; + event System.IO.RenamedEventHandler? Renamed; + void BeginInit(); + void EndInit(); + System.IO.Abstractions.IWaitForChangedResult WaitForChanged(System.IO.WatcherChangeTypes changeType); + System.IO.Abstractions.IWaitForChangedResult WaitForChanged(System.IO.WatcherChangeTypes changeType, System.TimeSpan timeout); + System.IO.Abstractions.IWaitForChangedResult WaitForChanged(System.IO.WatcherChangeTypes changeType, int timeout); + } + public interface IFileSystemWatcherFactory : System.IO.Abstractions.IFileSystemEntity + { + System.IO.Abstractions.IFileSystemWatcher New(); + System.IO.Abstractions.IFileSystemWatcher New(string path); + System.IO.Abstractions.IFileSystemWatcher New(string path, string filter); + [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("fileSystemWatcher")] + System.IO.Abstractions.IFileSystemWatcher? Wrap(System.IO.FileSystemWatcher? fileSystemWatcher); + } + public interface IFileVersionInfo + { + string? Comments { get; } + string? CompanyName { get; } + int FileBuildPart { get; } + string? FileDescription { get; } + int FileMajorPart { get; } + int FileMinorPart { get; } + string FileName { get; } + int FilePrivatePart { get; } + string? FileVersion { get; } + string? InternalName { get; } + bool IsDebug { get; } + bool IsPatched { get; } + bool IsPreRelease { get; } + bool IsPrivateBuild { get; } + bool IsSpecialBuild { get; } + string? Language { get; } + string? LegalCopyright { get; } + string? LegalTrademarks { get; } + string? OriginalFilename { get; } + string? PrivateBuild { get; } + int ProductBuildPart { get; } + int ProductMajorPart { get; } + int ProductMinorPart { get; } + string? ProductName { get; } + int ProductPrivatePart { get; } + string? ProductVersion { get; } + string? SpecialBuild { get; } + } + public interface IFileVersionInfoFactory : System.IO.Abstractions.IFileSystemEntity + { + System.IO.Abstractions.IFileVersionInfo GetVersionInfo(string fileName); + } + public interface IPath : System.IO.Abstractions.IFileSystemEntity + { + char AltDirectorySeparatorChar { get; } + char DirectorySeparatorChar { get; } + char PathSeparator { get; } + char VolumeSeparatorChar { get; } + [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("path")] + string? ChangeExtension(string? path, string? extension); + string Combine([System.Runtime.CompilerServices.ParamCollection] [System.Runtime.CompilerServices.ScopedRef] System.ReadOnlySpan paths); + string Combine(params string[] paths); + string Combine(string path1, string path2); + string Combine(string path1, string path2, string path3); + string Combine(string path1, string path2, string path3, string path4); + bool EndsInDirectorySeparator(System.ReadOnlySpan path); + bool EndsInDirectorySeparator(string path); + bool Exists([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? path); + System.ReadOnlySpan GetDirectoryName(System.ReadOnlySpan path); + string? GetDirectoryName(string? path); + System.ReadOnlySpan GetExtension(System.ReadOnlySpan path); + [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("path")] + string? GetExtension(string? path); + System.ReadOnlySpan GetFileName(System.ReadOnlySpan path); + [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("path")] + string? GetFileName(string? path); + System.ReadOnlySpan GetFileNameWithoutExtension(System.ReadOnlySpan path); + [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("path")] + string? GetFileNameWithoutExtension(string? path); + string GetFullPath(string path); + string GetFullPath(string path, string basePath); + char[] GetInvalidFileNameChars(); + char[] GetInvalidPathChars(); + System.ReadOnlySpan GetPathRoot(System.ReadOnlySpan path); + string? GetPathRoot(string? path); + string GetRandomFileName(); + string GetRelativePath(string relativeTo, string path); + [System.Obsolete("Insecure temporary file creation methods should not be used. Use `Path.Combine(Pa" + + "th.GetTempPath(), Path.GetRandomFileName())` instead.")] + string GetTempFileName(); + string GetTempPath(); + bool HasExtension(System.ReadOnlySpan path); + bool HasExtension([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? path); + bool IsPathFullyQualified(System.ReadOnlySpan path); + bool IsPathFullyQualified(string path); + bool IsPathRooted(System.ReadOnlySpan path); + bool IsPathRooted(string? path); + string Join([System.Runtime.CompilerServices.ParamCollection] [System.Runtime.CompilerServices.ScopedRef] System.ReadOnlySpan paths); + string Join(params string?[] paths); + string Join(System.ReadOnlySpan path1, System.ReadOnlySpan path2); + string Join(string? path1, string? path2); + string Join(System.ReadOnlySpan path1, System.ReadOnlySpan path2, System.ReadOnlySpan path3); + string Join(string? path1, string? path2, string? path3); + string Join(System.ReadOnlySpan path1, System.ReadOnlySpan path2, System.ReadOnlySpan path3, System.ReadOnlySpan path4); + string Join(string? path1, string? path2, string? path3, string? path4); + System.ReadOnlySpan TrimEndingDirectorySeparator(System.ReadOnlySpan path); + string TrimEndingDirectorySeparator(string path); + bool TryJoin(System.ReadOnlySpan path1, System.ReadOnlySpan path2, System.Span destination, out int charsWritten); + bool TryJoin(System.ReadOnlySpan path1, System.ReadOnlySpan path2, System.ReadOnlySpan path3, System.Span destination, out int charsWritten); + } + public interface IWaitForChangedResult + { + System.IO.WatcherChangeTypes ChangeType { get; } + string? Name { get; } + string? OldName { get; } + bool TimedOut { get; } + } +} \ No newline at end of file diff --git a/Tests/Api/Testably.Abstractions.Core.Api.Tests/Expected/Testably.Abstractions.FileSystem.Interface_net9.0.txt b/Tests/Api/Testably.Abstractions.Core.Api.Tests/Expected/Testably.Abstractions.FileSystem.Interface_net9.0.txt index d26b9fc2f..21f39bac1 100644 --- a/Tests/Api/Testably.Abstractions.Core.Api.Tests/Expected/Testably.Abstractions.FileSystem.Interface_net9.0.txt +++ b/Tests/Api/Testably.Abstractions.Core.Api.Tests/Expected/Testably.Abstractions.FileSystem.Interface_net9.0.txt @@ -441,7 +441,7 @@ namespace System.IO.Abstractions char VolumeSeparatorChar { get; } [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("path")] string? ChangeExtension(string? path, string? extension); - string Combine([System.Runtime.CompilerServices.ScopedRef] System.ReadOnlySpan paths); + string Combine([System.Runtime.CompilerServices.ParamCollection] [System.Runtime.CompilerServices.ScopedRef] System.ReadOnlySpan paths); string Combine(params string[] paths); string Combine(string path1, string path2); string Combine(string path1, string path2, string path3); @@ -478,7 +478,7 @@ namespace System.IO.Abstractions bool IsPathFullyQualified(string path); bool IsPathRooted(System.ReadOnlySpan path); bool IsPathRooted(string? path); - string Join([System.Runtime.CompilerServices.ScopedRef] System.ReadOnlySpan paths); + string Join([System.Runtime.CompilerServices.ParamCollection] [System.Runtime.CompilerServices.ScopedRef] System.ReadOnlySpan paths); string Join(params string?[] paths); string Join(System.ReadOnlySpan path1, System.ReadOnlySpan path2); string Join(string? path1, string? path2); diff --git a/Tests/Api/Testably.Abstractions.Core.Api.Tests/Expected/Testably.Abstractions.Interface_net10.0.txt b/Tests/Api/Testably.Abstractions.Core.Api.Tests/Expected/Testably.Abstractions.Interface_net10.0.txt new file mode 100644 index 000000000..1d47ccc57 --- /dev/null +++ b/Tests/Api/Testably.Abstractions.Core.Api.Tests/Expected/Testably.Abstractions.Interface_net10.0.txt @@ -0,0 +1,174 @@ +[assembly: System.CLSCompliant(true)] +[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Testably/Testably.Abstractions.git")] +[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v10.0", FrameworkDisplayName=".NET 10.0")] +namespace Testably.Abstractions.Helpers +{ + public abstract class GuidSystemBase : Testably.Abstractions.RandomSystem.IGuid, Testably.Abstractions.RandomSystem.IRandomSystemEntity + { + protected GuidSystemBase(Testably.Abstractions.IRandomSystem randomSystem) { } + public System.Guid Empty { get; } + public Testably.Abstractions.IRandomSystem RandomSystem { get; } + public abstract System.Guid CreateVersion7(); + public abstract System.Guid CreateVersion7(System.DateTimeOffset timestamp); + public abstract System.Guid NewGuid(); + public System.Guid Parse(System.ReadOnlySpan utf8Text) { } + public System.Guid Parse(System.ReadOnlySpan input) { } + public System.Guid Parse(string input) { } + public System.Guid Parse(System.ReadOnlySpan utf8Text, System.IFormatProvider provider) { } + public System.Guid Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { } + public System.Guid Parse(string s, System.IFormatProvider? provider) { } + public System.Guid ParseExact(System.ReadOnlySpan input, System.ReadOnlySpan format) { } + public System.Guid ParseExact(string input, string format) { } + public bool TryParse(System.ReadOnlySpan utf8Text, out System.Guid result) { } + public bool TryParse(System.ReadOnlySpan input, out System.Guid result) { } + public bool TryParse(string? input, out System.Guid result) { } + public bool TryParse(System.ReadOnlySpan utf8Text, System.IFormatProvider provider, out System.Guid result) { } + public bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.Guid result) { } + public bool TryParse(string? s, System.IFormatProvider? provider, out System.Guid result) { } + public bool TryParseExact(System.ReadOnlySpan input, System.ReadOnlySpan format, out System.Guid result) { } + public bool TryParseExact([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? input, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? format, out System.Guid result) { } + } + public interface IFileSystemExtensibility + { + T? RetrieveMetadata(string key); + void StoreMetadata(string key, T? value); + bool TryGetWrappedInstance([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out T? wrappedInstance); + } + public sealed class RandomWrapper : Testably.Abstractions.RandomSystem.IRandom + { + public RandomWrapper(System.Random instance) { } + public void GetHexString(System.Span destination, bool lowercase = false) { } + public string GetHexString(int stringLength, bool lowercase = false) { } + public void GetItems(System.ReadOnlySpan choices, System.Span destination) { } + public T[] GetItems(System.ReadOnlySpan choices, int length) { } + public T[] GetItems(T[] choices, int length) { } + public string GetString(System.ReadOnlySpan choices, int length) { } + public int Next() { } + public int Next(int maxValue) { } + public int Next(int minValue, int maxValue) { } + public void NextBytes(System.Span buffer) { } + public void NextBytes(byte[] buffer) { } + public double NextDouble() { } + public long NextInt64() { } + public long NextInt64(long maxValue) { } + public long NextInt64(long minValue, long maxValue) { } + public float NextSingle() { } + public void Shuffle(System.Span values) { } + public void Shuffle(T[] values) { } + } +} +namespace Testably.Abstractions +{ + public interface IRandomSystem + { + Testably.Abstractions.RandomSystem.IGuid Guid { get; } + Testably.Abstractions.RandomSystem.IRandomFactory Random { get; } + } + public interface ITimeSystem + { + Testably.Abstractions.TimeSystem.IDateTime DateTime { get; } + Testably.Abstractions.TimeSystem.ITask Task { get; } + Testably.Abstractions.TimeSystem.IThread Thread { get; } + Testably.Abstractions.TimeSystem.ITimerFactory Timer { get; } + } +} +namespace Testably.Abstractions.RandomSystem +{ + public interface IGuid : Testably.Abstractions.RandomSystem.IRandomSystemEntity + { + System.Guid Empty { get; } + System.Guid CreateVersion7(); + System.Guid CreateVersion7(System.DateTimeOffset timestamp); + System.Guid NewGuid(); + System.Guid Parse(System.ReadOnlySpan utf8Text); + System.Guid Parse(System.ReadOnlySpan input); + System.Guid Parse(string input); + System.Guid Parse(System.ReadOnlySpan utf8Text, System.IFormatProvider provider); + System.Guid Parse(System.ReadOnlySpan s, System.IFormatProvider? provider); + System.Guid Parse(string s, System.IFormatProvider? provider); + System.Guid ParseExact(System.ReadOnlySpan input, System.ReadOnlySpan format); + System.Guid ParseExact(string input, string format); + bool TryParse(System.ReadOnlySpan utf8Text, out System.Guid result); + bool TryParse(System.ReadOnlySpan input, out System.Guid result); + bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? input, out System.Guid result); + bool TryParse(System.ReadOnlySpan utf8Text, System.IFormatProvider provider, out System.Guid result); + bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.Guid result); + bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, System.IFormatProvider? provider, out System.Guid result); + bool TryParseExact(System.ReadOnlySpan input, System.ReadOnlySpan format, out System.Guid result); + bool TryParseExact([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? input, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? format, out System.Guid result); + } + public interface IRandom + { + void GetHexString(System.Span destination, bool lowercase = false); + string GetHexString(int stringLength, bool lowercase = false); + void GetItems(System.ReadOnlySpan choices, System.Span destination); + T[] GetItems(System.ReadOnlySpan choices, int length); + T[] GetItems(T[] choices, int length); + string GetString(System.ReadOnlySpan choices, int length); + int Next(); + int Next(int maxValue); + int Next(int minValue, int maxValue); + void NextBytes(System.Span buffer); + void NextBytes(byte[] buffer); + double NextDouble(); + long NextInt64(); + long NextInt64(long maxValue); + long NextInt64(long minValue, long maxValue); + float NextSingle(); + void Shuffle(System.Span values); + void Shuffle(T[] values); + } + public interface IRandomFactory : Testably.Abstractions.RandomSystem.IRandomSystemEntity + { + Testably.Abstractions.RandomSystem.IRandom Shared { get; } + Testably.Abstractions.RandomSystem.IRandom New(); + Testably.Abstractions.RandomSystem.IRandom New(int seed); + } + public interface IRandomSystemEntity + { + Testably.Abstractions.IRandomSystem RandomSystem { get; } + } +} +namespace Testably.Abstractions.TimeSystem +{ + public interface IDateTime : Testably.Abstractions.TimeSystem.ITimeSystemEntity + { + System.DateTime MaxValue { get; } + System.DateTime MinValue { get; } + System.DateTime Now { get; } + System.DateTime Today { get; } + System.DateTime UnixEpoch { get; } + System.DateTime UtcNow { get; } + } + public interface ITask : Testably.Abstractions.TimeSystem.ITimeSystemEntity + { + System.Threading.Tasks.Task Delay(System.TimeSpan delay); + System.Threading.Tasks.Task Delay(int millisecondsDelay); + System.Threading.Tasks.Task Delay(System.TimeSpan delay, System.Threading.CancellationToken cancellationToken); + System.Threading.Tasks.Task Delay(int millisecondsDelay, System.Threading.CancellationToken cancellationToken); + } + public interface IThread : Testably.Abstractions.TimeSystem.ITimeSystemEntity + { + void Sleep(System.TimeSpan timeout); + void Sleep(int millisecondsTimeout); + } + public interface ITimeSystemEntity + { + Testably.Abstractions.ITimeSystem TimeSystem { get; } + } + public interface ITimer : System.IAsyncDisposable, System.IDisposable, Testably.Abstractions.TimeSystem.ITimeSystemEntity + { + bool Change(System.TimeSpan dueTime, System.TimeSpan period); + bool Change(int dueTime, int period); + bool Change(long dueTime, long period); + bool Dispose(System.Threading.WaitHandle notifyObject); + } + public interface ITimerFactory : Testably.Abstractions.TimeSystem.ITimeSystemEntity + { + Testably.Abstractions.TimeSystem.ITimer New(System.Threading.TimerCallback callback); + Testably.Abstractions.TimeSystem.ITimer New(System.Threading.TimerCallback callback, object? state, System.TimeSpan dueTime, System.TimeSpan period); + Testably.Abstractions.TimeSystem.ITimer New(System.Threading.TimerCallback callback, object? state, int dueTime, int period); + Testably.Abstractions.TimeSystem.ITimer New(System.Threading.TimerCallback callback, object? state, long dueTime, long period); + Testably.Abstractions.TimeSystem.ITimer Wrap(System.Threading.Timer timer); + } +} \ No newline at end of file diff --git a/Tests/Directory.Build.props b/Tests/Directory.Build.props index 4cb0fde90..7a5be08cf 100644 --- a/Tests/Directory.Build.props +++ b/Tests/Directory.Build.props @@ -5,17 +5,17 @@ - net8.0;net9.0;net48 - net8.0;net9.0 + net8.0;net9.0;net10.0;net48 + net8.0;net9.0;net10.0 net48 - net9.0 + net10.0 - latest + preview disable enable false diff --git a/Tests/Testably.Abstractions.Compression.Tests/Internal/ExecuteTests.cs b/Tests/Testably.Abstractions.Compression.Tests/Internal/ExecuteTests.cs index 14d4fa1d8..a823dd0b0 100644 --- a/Tests/Testably.Abstractions.Compression.Tests/Internal/ExecuteTests.cs +++ b/Tests/Testably.Abstractions.Compression.Tests/Internal/ExecuteTests.cs @@ -5,7 +5,8 @@ namespace Testably.Abstractions.Compression.Tests.Internal; public sealed class ExecuteTests { [Fact] - public async Task WhenRealFileSystem_MockFileSystem_WithActionCallback_ShouldExecuteOnMockFileSystem() + public async Task + WhenRealFileSystem_MockFileSystem_WithActionCallback_ShouldExecuteOnMockFileSystem() { bool onRealFileSystemExecuted = false; bool onMockFileSystemExecuted = false; @@ -25,7 +26,8 @@ public async Task WhenRealFileSystem_MockFileSystem_WithActionCallback_ShouldExe } [Fact] - public async Task WhenRealFileSystem_MockFileSystem_WithFuncCallback_ShouldExecuteOnMockFileSystem() + public async Task + WhenRealFileSystem_MockFileSystem_WithFuncCallback_ShouldExecuteOnMockFileSystem() { bool onRealFileSystemExecuted = false; bool onMockFileSystemExecuted = false; @@ -45,7 +47,8 @@ public async Task WhenRealFileSystem_MockFileSystem_WithFuncCallback_ShouldExecu } [Fact] - public async Task WhenRealFileSystem_RealFileSystem_WithActionCallback_ShouldExecuteOnRealFileSystem() + public async Task + WhenRealFileSystem_RealFileSystem_WithActionCallback_ShouldExecuteOnRealFileSystem() { bool onRealFileSystemExecuted = false; bool onMockFileSystemExecuted = false; @@ -65,7 +68,8 @@ public async Task WhenRealFileSystem_RealFileSystem_WithActionCallback_ShouldExe } [Fact] - public async Task WhenRealFileSystem_RealFileSystem_WithFuncCallback_ShouldExecuteOnRealFileSystem() + public async Task + WhenRealFileSystem_RealFileSystem_WithFuncCallback_ShouldExecuteOnRealFileSystem() { bool onRealFileSystemExecuted = false; bool onMockFileSystemExecuted = false; @@ -83,4 +87,92 @@ public async Task WhenRealFileSystem_RealFileSystem_WithFuncCallback_ShouldExecu await That(onRealFileSystemExecuted).IsTrue(); await That(onMockFileSystemExecuted).IsFalse(); } + + [Fact] + public async Task + WhenRealFileSystemAsync_MockFileSystem_WithActionCallback_ShouldExecuteOnMockFileSystem() + { + bool onRealFileSystemExecuted = false; + bool onMockFileSystemExecuted = false; + MockFileSystem fileSystem = new(); + await Execute.WhenRealFileSystemAsync(fileSystem, + () => + { + onRealFileSystemExecuted = true; + return Task.CompletedTask; + }, + () => + { + onMockFileSystemExecuted = true; + }); + + await That(onRealFileSystemExecuted).IsFalse(); + await That(onMockFileSystemExecuted).IsTrue(); + } + + [Fact] + public async Task + WhenRealFileSystemAsync_MockFileSystem_WithFuncCallback_ShouldExecuteOnMockFileSystem() + { + bool onRealFileSystemExecuted = false; + bool onMockFileSystemExecuted = false; + MockFileSystem fileSystem = new(); + await Execute.WhenRealFileSystemAsync(fileSystem, + async () => + { + await Task.Yield(); + return onRealFileSystemExecuted = true; + }, + () => + { + return onMockFileSystemExecuted = true; + }); + + await That(onRealFileSystemExecuted).IsFalse(); + await That(onMockFileSystemExecuted).IsTrue(); + } + + [Fact] + public async Task + WhenRealFileSystemAsync_RealFileSystem_WithActionCallback_ShouldExecuteOnRealFileSystem() + { + bool onRealFileSystemExecuted = false; + bool onMockFileSystemExecuted = false; + RealFileSystem fileSystem = new(); + await Execute.WhenRealFileSystemAsync(fileSystem, + () => + { + onRealFileSystemExecuted = true; + return Task.CompletedTask; + }, + () => + { + onMockFileSystemExecuted = true; + }); + + await That(onRealFileSystemExecuted).IsTrue(); + await That(onMockFileSystemExecuted).IsFalse(); + } + + [Fact] + public async Task + WhenRealFileSystemAsync_RealFileSystem_WithFuncCallback_ShouldExecuteOnRealFileSystem() + { + bool onRealFileSystemExecuted = false; + bool onMockFileSystemExecuted = false; + RealFileSystem fileSystem = new(); + await Execute.WhenRealFileSystemAsync(fileSystem, + async () => + { + await Task.Yield(); + return onRealFileSystemExecuted = true; + }, + () => + { + return onMockFileSystemExecuted = true; + }); + + await That(onRealFileSystemExecuted).IsTrue(); + await That(onMockFileSystemExecuted).IsFalse(); + } } diff --git a/Tests/Testably.Abstractions.Compression.Tests/Internal/ZipUtilitiesTests.cs b/Tests/Testably.Abstractions.Compression.Tests/Internal/ZipUtilitiesTests.cs index 27a8a3a96..d122dbc98 100644 --- a/Tests/Testably.Abstractions.Compression.Tests/Internal/ZipUtilitiesTests.cs +++ b/Tests/Testably.Abstractions.Compression.Tests/Internal/ZipUtilitiesTests.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Threading; using Testably.Abstractions.Internal; namespace Testably.Abstractions.Compression.Tests.Internal; @@ -94,10 +95,28 @@ public void ExtractToFile(string destinationFileName) /// public void ExtractToFile(string destinationFileName, bool overwrite) => throw new NotSupportedException(); + +#if FEATURE_COMPRESSION_ASYNC + /// + public Task ExtractToFileAsync(string destinationFileName, CancellationToken cancellationToken = default) + => throw new NotSupportedException(); +#endif + +#if FEATURE_COMPRESSION_ASYNC + /// + public Task ExtractToFileAsync(string destinationFileName, bool overwrite, CancellationToken cancellationToken = default) + => throw new NotSupportedException(); +#endif /// public Stream Open() => stream ?? throw new NotSupportedException(); + +#if FEATURE_COMPRESSION_ASYNC + /// + public Task OpenAsync(CancellationToken cancellationToken = default) + => Task.FromResult(Open()); +#endif #endregion } diff --git a/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ExtensionTests.Async.cs b/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ExtensionTests.Async.cs new file mode 100644 index 000000000..35b79f2c1 --- /dev/null +++ b/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/ExtensionTests.Async.cs @@ -0,0 +1,273 @@ +#if FEATURE_COMPRESSION_ASYNC +using System.Globalization; +using System.IO; +using System.IO.Compression; +using System.Linq; + +namespace Testably.Abstractions.Compression.Tests.ZipArchive; + +public partial class ExtensionTests +{ + [Theory] + [InlineData("2000-01-01T12:14:15")] + [InlineData("1980-01-01T00:00:00")] + [InlineData("2107-12-31T23:59:59")] + public async Task CreateEntryFromFileAsync_LastWriteTime_ShouldBeCopiedFromFile( + string lastWriteTimeString) + { + DateTime lastWriteTime = DateTime.Parse(lastWriteTimeString, CultureInfo.InvariantCulture); + 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); + await That(archive.Entries).IsEmpty(); + + await archive.CreateEntryFromFileAsync("bar/foo.txt", "foo/bar.txt", + CompressionLevel.NoCompression, TestContext.Current.CancellationToken); + + IZipArchiveEntry entry = archive.Entries.Single(); + await That(entry.LastWriteTime.DateTime).IsEqualTo(lastWriteTime); + } + + [Theory] + [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 async Task CreateEntryFromFileAsync_LastWriteTimeOutOfRange_ShouldBeFirstJanuary1980( + string lastWriteTimeString) + { + DateTime expectedTime = new(1980, 1, 1, 0, 0, 0); + DateTime lastWriteTime = DateTime.Parse(lastWriteTimeString, CultureInfo.InvariantCulture) + .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); + await That(archive.Entries).IsEmpty(); + + await archive.CreateEntryFromFileAsync("bar/foo.txt", "foo/bar.txt", + CompressionLevel.NoCompression, TestContext.Current.CancellationToken); + + IZipArchiveEntry entry = archive.Entries.Single(); + await That(entry.LastWriteTime.DateTime).IsEqualTo(expectedTime); + } + + [Fact] + public async Task CreateEntryFromFileAsync_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); + await That(archive.Entries).IsEmpty(); + + Task Act() + => archive.CreateEntryFromFileAsync("bar/foo.txt", null!, TestContext.Current.CancellationToken); + + await That(Act).Throws() + .WithParamName("entryName"); + } + + [Fact] + public async Task CreateEntryFromFileAsync_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); + await That(archive.Entries).IsEmpty(); + + Task Act() + => archive.CreateEntryFromFileAsync(null!, "foo/bar.txt", + CompressionLevel.NoCompression, TestContext.Current.CancellationToken); + + await That(Act).Throws() + .WithParamName("sourceFileName"); + } + + [Fact] + public async Task CreateEntryFromFileAsync_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); + await That(archive.Entries).IsEmpty(); + + Task Act() + => archive.CreateEntryFromFileAsync("bar/foo.txt", "foo/bar.txt", TestContext.Current.CancellationToken); + + await That(Act).Throws(); + } + + [Fact] + public async Task CreateEntryFromFileAsync_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); + await That(archive.Entries).IsEmpty(); + + await archive.CreateEntryFromFileAsync("bar/foo.txt", "foo/bar.txt", + CompressionLevel.NoCompression, TestContext.Current.CancellationToken); + + IZipArchiveEntry entry = archive.Entries.Single(); + await That(entry.FullName).IsEqualTo("foo/bar.txt"); + + entry.ExtractToFile("test.txt"); + await That(FileSystem).HasFile("test.txt").WithContent("FooFooFoo"); + } + + [Theory] + [AutoData] + public async Task ExtractToDirectoryAsync_DestinationNull_ShouldThrowArgumentNullException( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); + + async Task Act() + { + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + await archive.ExtractToDirectoryAsync(null!, TestContext.Current.CancellationToken); + } + + await That(Act).Throws() + .WithParamName("destinationDirectoryName"); + } + + [Theory] + [AutoData] + public async Task + ExtractToDirectoryAsync_DestinationNull_WithOverwrite_ShouldThrowArgumentNullException( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); + + async Task Act() + { + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + await archive.ExtractToDirectoryAsync(null!, true, TestContext.Current.CancellationToken); + } + + await That(Act).Throws(); + } + + [Fact] + public async Task ExtractToDirectoryAsync_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); + + await archive.ExtractToDirectoryAsync("bar", TestContext.Current.CancellationToken); + + await That(FileSystem).HasFile("bar/foo.txt").WithContent("FooFooFoo"); + await That(FileSystem).HasDirectory("bar/bar"); + await That(FileSystem).HasFile("bar/bar.txt"); + } + + [Fact] + public async Task ExtractToDirectoryAsync_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); + + async Task Act() + { + await archive.ExtractToDirectoryAsync("bar", TestContext.Current.CancellationToken); + } + + await That(Act).Throws() + .WithMessage($"*'{FileSystem.Path.GetFullPath("bar/foo.txt")}'*").AsWildcard(); + await That(FileSystem).HasFile("bar/foo.txt") + .WhoseContent(c => c.IsNotEqualTo("FooFooFoo")); + } + + [Fact] + public async Task ExtractToDirectoryAsync_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); + + await archive.ExtractToDirectoryAsync("bar", true, TestContext.Current.CancellationToken); + + await That(FileSystem).HasFile("bar/foo.txt") + .WithContent("FooFooFoo"); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/Tests.cs b/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/Tests.cs index 11cacaf5c..282ce91e7 100644 --- a/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/Tests.cs +++ b/Tests/Testably.Abstractions.Compression.Tests/ZipArchive/Tests.cs @@ -7,7 +7,7 @@ namespace Testably.Abstractions.Compression.Tests.ZipArchive; [FileSystemTests] public partial class Tests { -#if FEATURE_ZIPFILE_NET7 +#if FEATURE_FILESYSTEM_COMMENT_ENCRYPTED [Fact] public async Task Comment_ShouldBeInitializedEmpty() { @@ -25,7 +25,7 @@ public async Task Comment_ShouldBeInitializedEmpty() await That(archive.Comment).IsEqualTo(""); } #endif -#if FEATURE_ZIPFILE_NET7 +#if FEATURE_FILESYSTEM_COMMENT_ENCRYPTED [Theory] [AutoData] public async Task Comment_ShouldBeSettable(string comment) diff --git a/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ExtensionTests.Async.cs b/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ExtensionTests.Async.cs new file mode 100644 index 000000000..5318cacbb --- /dev/null +++ b/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/ExtensionTests.Async.cs @@ -0,0 +1,179 @@ +#if FEATURE_COMPRESSION_ASYNC +using System.Globalization; +using System.IO; +using System.IO.Compression; +using System.Linq; + +namespace Testably.Abstractions.Compression.Tests.ZipArchiveEntry; + +public partial class ExtensionTests +{ + [Fact] + public async Task + ExtractToFileAsync_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/"); + + Task Act() + => archive.ExtractToDirectoryAsync("bar", TestContext.Current.CancellationToken); + + await That(Act).Throws(); + } + + [Theory] + [AutoData] + public async Task ExtractToFileAsync_DestinationNull_ShouldThrowArgumentNullException( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithAFile()); + + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); + + async Task Act() + { + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + await archive.Entries.Single().ExtractToFileAsync(null!, TestContext.Current.CancellationToken); + } + + await That(Act).Throws() + .WithParamName("destinationFileName"); + } + + [Theory] + [AutoData] + public async Task + ExtractToFileAsync_DestinationNull_WithOverwrite_ShouldThrowArgumentNullException( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithAFile()); + + FileSystem.ZipFile() + .CreateFromDirectory("foo", "destination.zip", compressionLevel, false); + + async Task Act() + { + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + await archive.Entries.Single().ExtractToFileAsync(null!, true, TestContext.Current.CancellationToken); + } + + await That(Act).Throws(); + } + + [Fact] + public async Task ExtractToFileAsync_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); + + Task Act() + => archive2.ExtractToDirectoryAsync("bar", TestContext.Current.CancellationToken); + + await That(Act).Throws(); + } + + [Theory] + [InlineData("2000-01-01T12:14:15")] + [InlineData("1980-01-01T00:00:00")] + [InlineData("2107-12-31T23:59:59")] + public async Task ExtractToFileAsync_LastWriteTime_ShouldBeCopiedFromFile(string lastWriteTimeString) + { + DateTime lastWriteTime = DateTime.Parse(lastWriteTimeString, CultureInfo.InvariantCulture); + FileSystem.Initialize() + .WithSubdirectory("foo") + .WithSubdirectory("bar").Initialized(s => s + .WithFile("bar.txt")); + 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.CreateEntryFromFile("bar/foo.txt", "foo/bar.txt", + CompressionLevel.NoCompression); + IZipArchiveEntry entry = archive.Entries.Single(); + + await entry.ExtractToFileAsync("bar/bar.txt", true, TestContext.Current.CancellationToken); + + await That(FileSystem).HasFile("bar/bar.txt") + .WithContent("FooFooFoo").And + .WithLastWriteTime(lastWriteTime); + } + + [Fact] + public async Task ExtractToFileAsync_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(); + + Task Act() + => entry.ExtractToFileAsync("bar/bar.txt", TestContext.Current.CancellationToken); + + await That(Act).Throws() + .WithMessage($"*'{FileSystem.Path.GetFullPath("bar/bar.txt")}'*").AsWildcard(); + await That(FileSystem).HasFile("bar/bar.txt") + .WhoseContent(f => f.IsNotEqualTo("FooFooFoo")); + } + + [Fact] + public async Task ExtractToFileAsync_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(); + + await entry.ExtractToFileAsync("bar/bar.txt", true, TestContext.Current.CancellationToken); + + await That(FileSystem).HasFile("bar/bar.txt") + .WithContent("FooFooFoo"); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/Tests.cs b/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/Tests.cs index 2b10c5060..afeacae20 100644 --- a/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/Tests.cs +++ b/Tests/Testably.Abstractions.Compression.Tests/ZipArchiveEntry/Tests.cs @@ -27,7 +27,7 @@ public async Task Archive_ShouldBeSetToArchive() await That(entry.Archive).IsEqualTo(archive); } -#if FEATURE_ZIPFILE_NET7 +#if FEATURE_FILESYSTEM_COMMENT_ENCRYPTED [Fact] public async Task Comment_ShouldBeInitializedEmpty() { @@ -47,7 +47,7 @@ public async Task Comment_ShouldBeInitializedEmpty() } #endif -#if FEATURE_ZIPFILE_NET7 +#if FEATURE_FILESYSTEM_COMMENT_ENCRYPTED [Theory] [AutoData] public async Task Comment_ShouldBeSettable(string comment) @@ -258,6 +258,7 @@ public async Task LastWriteTime_ReadOnlyArchive_ShouldThrowNotSupportedException IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); IZipArchiveEntry entry1 = archive.Entries[0]; + void Act() { entry1.LastWriteTime = new DateTimeOffset(lastWriteTime); @@ -291,6 +292,50 @@ public async Task LastWriteTime_ShouldBeSettable(DateTime lastWriteTime) await That(entry2.LastWriteTime.DateTime).IsNotEqualTo(lastWriteTime); } + [Fact] + public async Task Open_ShouldBeSetToFileName() + { + 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.Open("destination.zip", FileMode.Open, FileAccess.ReadWrite); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + IZipArchiveEntry entry = archive.Entries.Single(); + + Stream resultStream = entry.Open(); + + await That(resultStream).HasLength().EqualTo("FooFooFoo".Length); + } + +#if FEATURE_COMPRESSION_ASYNC + [Fact] + public async Task OpenAsync_ShouldBeSetToFileName() + { + 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.Open("destination.zip", FileMode.Open, FileAccess.ReadWrite); + + IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Update); + IZipArchiveEntry entry = archive.Entries.Single(); + + Stream resultStream = await entry.OpenAsync(TestContext.Current.CancellationToken); + + await That(resultStream).HasLength().EqualTo("FooFooFoo".Length); + } +#endif + [Fact] public async Task ToString_ShouldBeSetToFileName() { diff --git a/Tests/Testably.Abstractions.Compression.Tests/ZipFile/CreateFromDirectoryAsyncTests.cs b/Tests/Testably.Abstractions.Compression.Tests/ZipFile/CreateFromDirectoryAsyncTests.cs new file mode 100644 index 000000000..56cc5b517 --- /dev/null +++ b/Tests/Testably.Abstractions.Compression.Tests/ZipFile/CreateFromDirectoryAsyncTests.cs @@ -0,0 +1,405 @@ +#if FEATURE_COMPRESSION_ASYNC +using System.IO.Compression; +using System.Text; +#if FEATURE_COMPRESSION_STREAM +using System.IO; +using Testably.Abstractions.Compression.Tests.TestHelpers; +#endif + +namespace Testably.Abstractions.Compression.Tests.ZipFile; + +[FileSystemTests] +public partial class CreateFromDirectoryAsyncTests +{ + [Theory] + [AutoData] + public async Task + CreateFromDirectoryAsync_EmptyDirectory_ShouldBeIncluded( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithSubdirectory("bar")); + + await FileSystem.ZipFile() + .CreateFromDirectoryAsync("foo", "destination.zip", compressionLevel, false, + TestContext.Current.CancellationToken); + + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + await That(archive.Entries).HasSingle() + .Which.For(x => x.FullName, f => f.IsEqualTo("bar/")); + } + + [Theory] + [AutoData] + public async Task CreateFromDirectoryAsync_EmptySource_DoNotIncludeBaseDirectory_ShouldBeEmpty( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + + await FileSystem.ZipFile() + .CreateFromDirectoryAsync("foo", "destination.zip", compressionLevel, false, + TestContext.Current.CancellationToken); + + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + await That(archive.Entries).IsEmpty(); + } + + [Theory] + [AutoData] + public async Task + CreateFromDirectoryAsync_EmptySource_IncludeBaseDirectory_ShouldPrependDirectoryName( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + + await FileSystem.ZipFile() + .CreateFromDirectoryAsync("foo", "destination.zip", compressionLevel, true, + TestContext.Current.CancellationToken); + + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + await That(archive.Entries).HasSingle() + .Which.For(x => x.FullName, f => f.IsEqualTo("foo/")); + } + + [Theory] + [MemberData(nameof(EntryNameEncoding))] + public async Task CreateFromDirectoryAsync_EntryNameEncoding_ShouldUseEncoding( + string entryName, Encoding encoding, bool encodedCorrectly) + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithFile(entryName)); + + await FileSystem.ZipFile() + .CreateFromDirectoryAsync("foo", "destination.zip", CompressionLevel.NoCompression, + false, encoding, TestContext.Current.CancellationToken); + + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + IZipArchiveEntry singleEntry = await That(archive.Entries).HasSingle(); + if (encodedCorrectly) + { + await That(singleEntry.Name).IsEqualTo(entryName); + } + else + { + await That(singleEntry.Name).IsNotEqualTo(entryName); + } + } + + [Theory] + [AutoData] + public async Task CreateFromDirectoryAsync_IncludeBaseDirectory_ShouldPrependDirectoryName( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + + await FileSystem.ZipFile() + .CreateFromDirectoryAsync("foo", "destination.zip", compressionLevel, true, + TestContext.Current.CancellationToken); + + using IZipArchive archive = + FileSystem.ZipFile().Open("destination.zip", ZipArchiveMode.Read); + + await That(archive.Entries).HasSingle() + .Which.For(x => x.FullName, f => f.IsEqualTo("foo/test.txt")); + } + + [Theory] + [AutoData] + public async Task CreateFromDirectoryAsync_Overwrite_WithEncoding_ShouldOverwriteFile( + string contents, Encoding encoding) + { + FileSystem.Initialize() + .WithSubdirectory("bar").Initialized(s => s + .WithFile("test.txt")) + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + FileSystem.File.WriteAllText(FileSystem.Path.Combine("foo", "test.txt"), + contents); + + await FileSystem.ZipFile().CreateFromDirectoryAsync("foo", "destination.zip", + CompressionLevel.Optimal, false, encoding, TestContext.Current.CancellationToken); + + IZipArchive archive = FileSystem.ZipFile() + .Open("destination.zip", ZipArchiveMode.Read, encoding); + + await That(archive.Entries).HasSingle() + .Which.For(x => x.FullName, f => f.IsEqualTo("test.txt")); + } + + [Fact] + public async Task CreateFromDirectoryAsync_ShouldZipDirectoryContent() + { + FileSystem.Initialize() + .WithSubdirectory("destination") + .WithSubdirectory("foo").Initialized(s => s + .WithSubdirectory("bar").Initialized(t => t + .WithFile("test.txt"))); + + await FileSystem.ZipFile().CreateFromDirectoryAsync("foo", "destination.zip", + TestContext.Current.CancellationToken); + + FileSystem.ZipFile().ExtractToDirectory("destination.zip", "destination"); + + await That(FileSystem).HasFile("destination/bar/test.txt") + .WithContent().SameAs("foo/bar/test.txt"); + } + + [Fact] + public async Task CreateFromDirectoryAsync_WithReadOnlyStream_ShouldThrowArgumentException() + { + FileSystem.Initialize() + .WithFile("target.zip") + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + using FileSystemStream stream = FileSystem.FileStream.New( + "target.zip", FileMode.Open, FileAccess.Read); + + async Task Act() + { + // ReSharper disable once AccessToDisposedClosure + await FileSystem.ZipFile().CreateFromDirectoryAsync("foo", stream, TestContext.Current.CancellationToken); + } + + await That(Act).Throws() + .WithMessage("*stream is unwritable*").AsWildcard().And + .WithParamName("destination").And + .WithHResult(-2147024809); + } + + [Theory] + [AutoData] + public async Task + CreateFromDirectoryAsync_WithStream_EmptyDirectory_ShouldBeIncluded( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithSubdirectory("bar")); + using MemoryStream stream = new(); + + await FileSystem.ZipFile() + .CreateFromDirectoryAsync("foo", stream, compressionLevel, false, + TestContext.Current.CancellationToken); + + using IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + await That(archive.Entries).HasSingle() + .Which.For(x => x.FullName, f => f.IsEqualTo("bar/")); + } + + [Theory] + [AutoData] + public async Task + CreateFromDirectoryAsync_WithStream_EmptySource_DoNotIncludeBaseDirectory_ShouldBeEmpty( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + using MemoryStream stream = new(); + + await FileSystem.ZipFile() + .CreateFromDirectoryAsync("foo", stream, compressionLevel, false, + TestContext.Current.CancellationToken); + + using IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + await That(archive.Entries).IsEmpty(); + } + + [Theory] + [AutoData] + public async Task + CreateFromDirectoryAsync_WithStream_EmptySource_IncludeBaseDirectory_ShouldPrependDirectoryName( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo"); + using MemoryStream stream = new(); + + await FileSystem.ZipFile() + .CreateFromDirectoryAsync("foo", stream, compressionLevel, true, + TestContext.Current.CancellationToken); + + using IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + await That(archive.Entries).HasSingle() + .Which.For(x => x.FullName, f => f.IsEqualTo("foo/")); + } + + [Theory] + [MemberData(nameof(EntryNameEncoding))] + public async Task CreateFromDirectoryAsync_WithStream_EntryNameEncoding_ShouldUseEncoding( + string entryName, Encoding encoding, bool encodedCorrectly) + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithFile(entryName)); + using MemoryStream stream = new(); + + await FileSystem.ZipFile() + .CreateFromDirectoryAsync("foo", stream, CompressionLevel.NoCompression, + false, encoding, TestContext.Current.CancellationToken); + + using IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + IZipArchiveEntry singleEntry = await That(archive.Entries).HasSingle(); + if (encodedCorrectly) + { + await That(singleEntry.Name).IsEqualTo(entryName); + } + else + { + await That(singleEntry.Name).IsNotEqualTo(entryName); + } + } + + [Theory] + [AutoData] + public async Task + CreateFromDirectoryAsync_WithStream_IncludeBaseDirectory_ShouldPrependDirectoryName( + CompressionLevel compressionLevel) + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + using MemoryStream stream = new(); + + await FileSystem.ZipFile() + .CreateFromDirectoryAsync("foo", stream, compressionLevel, true, + TestContext.Current.CancellationToken); + + using IZipArchive archive = FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read); + + await That(archive.Entries).HasSingle() + .Which.For(x => x.FullName, f => f.IsEqualTo("foo/test.txt")); + } + + [Fact] + public async Task + CreateFromDirectoryAsync_WithStream_NotWritable_ShouldThrowArgumentException() + { + Stream stream = new MemoryStreamMock(canWrite: false); + + async Task Act() + { + await FileSystem.ZipFile().CreateFromDirectoryAsync("foo", stream, TestContext.Current.CancellationToken); + } + + await That(Act).Throws() + .WithMessage("The stream is unwritable*").AsWildcard().And + .WithParamName("destination").And + .WithHResult(-2147024809); + } + + [Fact] + public async Task + CreateFromDirectoryAsync_WithStream_Null_ShouldThrowArgumentNullException() + { + Stream stream = null!; + + async Task Act() + { + await FileSystem.ZipFile().CreateFromDirectoryAsync("foo", stream, TestContext.Current.CancellationToken); + } + + await That(Act).Throws() + .WithParamName("destination"); + } + + [Theory] + [AutoData] + public async Task + CreateFromDirectoryAsync_WithStream_Overwrite_WithEncoding_ShouldOverwriteFile( + string contents, Encoding encoding) + { + FileSystem.Initialize() + .WithSubdirectory("bar").Initialized(s => s + .WithFile("test.txt")) + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + FileSystem.File.WriteAllText(FileSystem.Path.Combine("foo", "test.txt"), + contents); + using MemoryStream stream = new(); + + await FileSystem.ZipFile().CreateFromDirectoryAsync("foo", stream, + CompressionLevel.Optimal, false, encoding, TestContext.Current.CancellationToken); + + IZipArchive archive = + FileSystem.ZipArchive().New(stream, ZipArchiveMode.Read, true, encoding); + + await That(archive.Entries).HasSingle() + .Which.For(x => x.FullName, f => f.IsEqualTo("test.txt")); + } + + [Fact] + public async Task CreateFromDirectoryAsync_WithStream_ShouldZipDirectoryContent() + { + FileSystem.Initialize() + .WithSubdirectory("destination") + .WithSubdirectory("foo").Initialized(s => s + .WithSubdirectory("bar").Initialized(t => t + .WithFile("test.txt"))); + using MemoryStream stream = new(); + + await FileSystem.ZipFile() + .CreateFromDirectoryAsync("foo", stream, TestContext.Current.CancellationToken); + + FileSystem.ZipFile().ExtractToDirectory(stream, "destination"); + + await That(FileSystem).HasFile("destination/bar/test.txt") + .WithContent().SameAs("foo/bar/test.txt"); + } + + [Fact] + public async Task ShouldNotLock() + { + string directory = "ToBeZipped"; + string archive = "zippedDirectory.zip"; + FileSystem.Directory.CreateDirectory(directory); + FileSystem.File.WriteAllText(FileSystem.Path.Combine(directory, "file.txt"), + "Some content"); + void Act() => FileSystem.Directory.Delete(directory, true); + + await FileSystem.ZipFile() + .CreateFromDirectoryAsync(directory, archive, TestContext.Current.CancellationToken); + + await That(Act).DoesNotThrow(); + } + + #region Helpers + + #pragma warning disable MA0018 + public static TheoryData EntryNameEncoding() + { + // ReSharper disable StringLiteralTypo + TheoryData theoryData = new() + { + { + "Dans mes rêves.mp3", Encoding.Default, true + }, + { + "Dans mes rêves.mp3", Encoding.ASCII, false + }, + }; + // ReSharper restore StringLiteralTypo + return theoryData; + } + #pragma warning restore MA0018 + + #endregion +} +#endif diff --git a/Tests/Testably.Abstractions.Compression.Tests/ZipFile/ExtractToDirectoryAsyncTests.cs b/Tests/Testably.Abstractions.Compression.Tests/ZipFile/ExtractToDirectoryAsyncTests.cs new file mode 100644 index 000000000..b1bfe6bb4 --- /dev/null +++ b/Tests/Testably.Abstractions.Compression.Tests/ZipFile/ExtractToDirectoryAsyncTests.cs @@ -0,0 +1,331 @@ +#if FEATURE_COMPRESSION_ASYNC +using System.IO; +using System.IO.Compression; +using System.Text; +#if FEATURE_COMPRESSION_STREAM +using Testably.Abstractions.Compression.Tests.TestHelpers; +#endif + +namespace Testably.Abstractions.Compression.Tests.ZipFile; + +[FileSystemTests] +public partial class ExtractToDirectoryAsyncAsyncTests +{ + [Fact] + public async Task ExtractToDirectoryAsync_MissingDestinationDirectory_ShouldCreateDirectory() + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + + FileSystem.ZipFile().CreateFromDirectory("foo", "destination.zip"); + + await FileSystem.ZipFile().ExtractToDirectoryAsync("destination.zip", "bar", + TestContext.Current.CancellationToken); + + await That(FileSystem).HasFile("bar/test.txt") + .WithContent().SameAs("foo/test.txt"); + } + + [Fact] + public async Task + ExtractToDirectoryAsync_MissingSourceFileName_ShouldThrowArgumentNullException() + { + FileSystem.Initialize(); + string sourceArchiveFileName = "destination.zip"; + + async Task Act() + { + await FileSystem.ZipFile().ExtractToDirectoryAsync(sourceArchiveFileName, "bar", + TestContext.Current.CancellationToken); + } + + await That(Act).Throws() + .WithMessage($"*'{FileSystem.Path.GetFullPath(sourceArchiveFileName)}*").AsWildcard(); + } + + [Fact] + public async Task + ExtractToDirectoryAsync_NullAsSourceFileName_ShouldThrowArgumentNullException() + { + FileSystem.Initialize(); + string sourceArchiveFileName = null!; + + async Task Act() + { + await FileSystem.ZipFile().ExtractToDirectoryAsync(sourceArchiveFileName, "bar", TestContext.Current.CancellationToken); + } + + await That(Act).Throws() + .WithParamName("sourceArchiveFileName"); + } + + [Theory] + [AutoData] + public async Task ExtractToDirectoryAsync_Overwrite_ShouldOverwriteFile( + string contents) + { + FileSystem.Initialize() + .WithSubdirectory("bar").Initialized(s => s + .WithFile("test.txt")) + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + FileSystem.File.WriteAllText(FileSystem.Path.Combine("foo", "test.txt"), + contents); + + FileSystem.ZipFile().CreateFromDirectory("foo", "destination.zip"); + + await FileSystem.ZipFile().ExtractToDirectoryAsync("destination.zip", "bar", true, + TestContext.Current.CancellationToken); + + await That(FileSystem).HasFile(FileSystem.Path.Combine("bar", "test.txt")) + .WithContent(contents); + } + + [Theory] + [AutoData] + public async Task ExtractToDirectoryAsync_WithEncoding_Overwrite_ShouldOverwriteFile( + string contents, + Encoding encoding) + { + FileSystem.Initialize() + .WithSubdirectory("bar").Initialized(s => s + .WithFile("test.txt")) + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + FileSystem.File.WriteAllText(FileSystem.Path.Combine("foo", "test.txt"), + contents); + + FileSystem.ZipFile().CreateFromDirectory("foo", "destination.zip"); + + await FileSystem.ZipFile().ExtractToDirectoryAsync("destination.zip", "bar", encoding, true, + TestContext.Current.CancellationToken); + + await That(FileSystem).HasFile(FileSystem.Path.Combine("bar", "test.txt")) + .WithContent(contents); + } + + [Theory] + [AutoData] + public async Task ExtractToDirectoryAsync_WithEncoding_ShouldZipDirectoryContent( + Encoding encoding) + { + FileSystem.Initialize() + .WithSubdirectory("bar") + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + + FileSystem.ZipFile().CreateFromDirectory("foo", "destination.zip", + CompressionLevel.Fastest, false, encoding); + + await FileSystem.ZipFile().ExtractToDirectoryAsync("destination.zip", "bar", encoding, + TestContext.Current.CancellationToken); + + await That(FileSystem).HasFile(FileSystem.Path.Combine("bar", "test.txt")) + .WithContent().SameAs(FileSystem.Path.Combine("foo", "test.txt")); + } + + [Theory] + [AutoData] + public async Task ExtractToDirectoryAsync_WithoutOverwriteAndExistingFile_ShouldOverwriteFile( + string contents) + { + FileSystem.Initialize() + .WithSubdirectory("bar").Initialized(s => s + .WithFile("test.txt")) + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + FileSystem.File.WriteAllText(FileSystem.Path.Combine("foo", "test.txt"), + contents); + string destinationPath = + FileSystem.Path.Combine(FileSystem.Path.GetFullPath("bar"), "test.txt"); + + FileSystem.ZipFile().CreateFromDirectory("foo", "destination.zip"); + + async Task Act() + { + await FileSystem.ZipFile().ExtractToDirectoryAsync("destination.zip", "bar", TestContext.Current.CancellationToken); + } + + await That(Act).Throws() + .WithMessage($"*'{destinationPath}'*").AsWildcard(); + await That(FileSystem.File.ReadAllText(destinationPath)) + .IsNotEqualTo(contents); + } + + [Fact] + public async Task + ExtractToDirectoryAsync_WithStream_MissingDestinationDirectory_ShouldCreateDirectory() + { + FileSystem.Initialize() + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + using MemoryStream stream = new(); + + FileSystem.ZipFile().CreateFromDirectory("foo", stream); + + await FileSystem.ZipFile() + .ExtractToDirectoryAsync(stream, "bar", TestContext.Current.CancellationToken); + + await That(FileSystem).HasFile("bar/test.txt") + .WithContent().SameAs("foo/test.txt"); + } + + [Fact] + public async Task + ExtractToDirectoryAsync_WithStream_NotReadable_ShouldThrowArgumentNullException() + { + FileSystem.Initialize(); + Stream source = new MemoryStreamMock(canRead: false); + + async Task Act() + { + await FileSystem.ZipFile().ExtractToDirectoryAsync(source, "bar", TestContext.Current.CancellationToken); + } + + await That(Act).Throws() + .WithMessage("The stream is unreadable*").AsWildcard().And + .WithParamName("source").And + .WithHResult(-2147024809); + } + + [Fact] + public async Task + ExtractToDirectoryAsync_WithStream_Null_ShouldThrowArgumentNullException() + { + FileSystem.Initialize(); + Stream source = null!; + + async Task Act() + { + await FileSystem.ZipFile().ExtractToDirectoryAsync(source, "bar", TestContext.Current.CancellationToken); + } + + await That(Act).Throws() + .WithParamName("source"); + } + + [Theory] + [AutoData] + public async Task ExtractToDirectoryAsync_WithStream_Overwrite_ShouldOverwriteFile( + string contents) + { + FileSystem.Initialize() + .WithSubdirectory("bar").Initialized(s => s + .WithFile("test.txt")) + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + FileSystem.File.WriteAllText(FileSystem.Path.Combine("foo", "test.txt"), + contents); + using MemoryStream stream = new(); + + FileSystem.ZipFile().CreateFromDirectory("foo", stream); + + await FileSystem.ZipFile() + .ExtractToDirectoryAsync(stream, "bar", true, TestContext.Current.CancellationToken); + + await That(FileSystem).HasFile(FileSystem.Path.Combine("bar", "test.txt")) + .WithContent(contents); + } + + [Theory] + [AutoData] + public async Task ExtractToDirectoryAsync_WithStream_WithEncoding_Overwrite_ShouldOverwriteFile( + string contents, + Encoding encoding) + { + FileSystem.Initialize() + .WithSubdirectory("bar").Initialized(s => s + .WithFile("test.txt")) + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + FileSystem.File.WriteAllText(FileSystem.Path.Combine("foo", "test.txt"), + contents); + using MemoryStream stream = new(); + + FileSystem.ZipFile().CreateFromDirectory("foo", stream); + + await FileSystem.ZipFile().ExtractToDirectoryAsync(stream, "bar", encoding, true, + TestContext.Current.CancellationToken); + + await That(FileSystem).HasFile(FileSystem.Path.Combine("bar", "test.txt")) + .WithContent(contents); + } + + [Theory] + [AutoData] + public async Task ExtractToDirectoryAsync_WithStream_WithEncoding_ShouldZipDirectoryContent( + Encoding encoding) + { + FileSystem.Initialize() + .WithSubdirectory("bar") + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + using MemoryStream stream = new(); + + FileSystem.ZipFile().CreateFromDirectory("foo", stream, + CompressionLevel.Fastest, false, encoding); + + await FileSystem.ZipFile().ExtractToDirectoryAsync(stream, "bar", encoding, + TestContext.Current.CancellationToken); + + await That(FileSystem).HasFile(FileSystem.Path.Combine("bar", "test.txt")) + .WithContent().SameAs(FileSystem.Path.Combine("foo", "test.txt")); + } + + [Theory] + [AutoData] + public async Task + ExtractToDirectoryAsync_WithStream_WithoutOverwriteAndExistingFile_ShouldOverwriteFile( + string contents) + { + FileSystem.Initialize() + .WithSubdirectory("bar").Initialized(s => s + .WithFile("test.txt")) + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + FileSystem.File.WriteAllText(FileSystem.Path.Combine("foo", "test.txt"), + contents); + string destinationPath = + FileSystem.Path.Combine(FileSystem.Path.GetFullPath("bar"), "test.txt"); + using MemoryStream stream = new(); + + FileSystem.ZipFile().CreateFromDirectory("foo", stream); + + async Task Act() + { + // ReSharper disable once AccessToDisposedClosure + await FileSystem.ZipFile().ExtractToDirectoryAsync(stream, "bar", TestContext.Current.CancellationToken); + } + + await That(Act).Throws() + .WithMessage($"*'{destinationPath}'*").AsWildcard(); + await That(FileSystem.File.ReadAllText(destinationPath)) + .IsNotEqualTo(contents); + } + + [Fact] + public async Task ExtractToDirectoryAsync_WithWriteOnlyStream_ShouldThrowArgumentException() + { + FileSystem.Initialize() + .WithFile("target.zip") + .WithSubdirectory("foo").Initialized(s => s + .WithFile("test.txt")); + using FileSystemStream stream = FileSystem.FileStream.New( + "target.zip", FileMode.Open, FileAccess.Write); + + FileSystem.ZipFile().CreateFromDirectory("foo", stream); + + async Task Act() + { + // ReSharper disable once AccessToDisposedClosure + await FileSystem.ZipFile().ExtractToDirectoryAsync(stream, "bar", TestContext.Current.CancellationToken); + } + + await That(Act).Throws() + .WithMessage("*stream is unreadable*").AsWildcard().And + .WithParamName("source").And + .WithHResult(-2147024809); + } +} +#endif diff --git a/Tests/Testably.Abstractions.Parity.Tests/Net10ParityTests.cs b/Tests/Testably.Abstractions.Parity.Tests/Net10ParityTests.cs new file mode 100644 index 000000000..586d961fd --- /dev/null +++ b/Tests/Testably.Abstractions.Parity.Tests/Net10ParityTests.cs @@ -0,0 +1,17 @@ +#if NET10_0 +using System.IO; + +namespace Testably.Abstractions.Parity.Tests; + +// ReSharper disable once UnusedMember.Global +public class Net10ParityTests : ParityTests +{ + public Net10ParityTests(ITestOutputHelper testOutputHelper) + : base(new TestHelpers.Parity(), testOutputHelper) + { + Parity.File.MissingMethods.Add( + typeof(File).GetMethod(nameof(File.OpenHandle))); + } +} + +#endif diff --git a/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/ParityCheckHelper.cs b/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/ParityCheckHelper.cs index 664954d73..b1b4f1500 100644 --- a/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/ParityCheckHelper.cs +++ b/Tests/Testably.Abstractions.Parity.Tests/TestHelpers/ParityCheckHelper.cs @@ -126,7 +126,7 @@ public static string PrintMethod(this MethodInfo method, string namePrefix = "", string firstParameterPrefix = "") { return - $"{method.ReturnType.PrintType()} {namePrefix}{method.Name}({firstParameterPrefix}{string.Join(", ", method.GetParameters().Select(x => x.ParameterType.PrintType() + " " + x.Name))})"; + $"{method.ReturnType.PrintType()} {namePrefix}{method.Name}({firstParameterPrefix}{string.Join(", ", method.GetParameters().Select(x => x.PrintParameter()))})"; } public static string PrintProperty(this PropertyInfo property, string namePrefix = "") @@ -149,7 +149,6 @@ public static string PrintType(this Type type) if (type.GenericTypeArguments.Length > 0) { - #pragma warning disable MA0089 // Use an overload with char instead of string return type.Name.Substring(0, type.Name.Length - 2) + "<" + string.Join(",", @@ -345,6 +344,17 @@ private static bool ArePropertiesEqual(PropertyInfo systemProperty, return true; } + + private static string PrintParameter(this ParameterInfo parameter) + { + string value = $"{parameter.ParameterType.PrintType()} {parameter.Name}"; + if (parameter.HasDefaultValue) + { + value += $" = {parameter.DefaultValue}"; + } + + return value; + } } #pragma warning restore MA0029 diff --git a/Tests/Testably.Abstractions.Testing.Tests/TimeSystem/TimerMockTests.cs b/Tests/Testably.Abstractions.Testing.Tests/TimeSystem/TimerMockTests.cs index c51aacaff..5b98340cb 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/TimeSystem/TimerMockTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/TimeSystem/TimerMockTests.cs @@ -70,7 +70,7 @@ await That(Act).Throws() .Whose(x => x.Message, it => it.Satisfies(m => m!.Contains("Cannot access a disposed object.", StringComparison.Ordinal) && - m.Contains(nameof(ITimer.Change), StringComparison.Ordinal))); + m!.Contains(nameof(ITimer.Change), StringComparison.Ordinal))); #endif } diff --git a/Tests/Testably.Abstractions.Tests/RandomSystem/GuidTests.cs b/Tests/Testably.Abstractions.Tests/RandomSystem/GuidTests.cs index 35d582df4..3596baff0 100644 --- a/Tests/Testably.Abstractions.Tests/RandomSystem/GuidTests.cs +++ b/Tests/Testably.Abstractions.Tests/RandomSystem/GuidTests.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using System.Text; #if FEATURE_GUID_PARSE using System.Collections.Generic; #endif @@ -11,54 +12,53 @@ namespace Testably.Abstractions.Tests.RandomSystem; [RandomSystemTests] public partial class GuidTests { +#if FEATURE_GUID_V7 [Fact] - public async Task Empty_ShouldReturnEmptyGuid() - { - await That(RandomSystem.Guid.Empty).IsEqualTo(Guid.Empty); - } - - [Fact] - public async Task NewGuid_ShouldBeThreadSafeAndReturnUniqueItems() + public async Task CreateVersion7_ShouldBeThreadSafeAndReturnUniqueItems() { ConcurrentBag results = []; Parallel.For(0, 100, _ => { - results.Add(RandomSystem.Guid.NewGuid()); + results.Add(RandomSystem.Guid.CreateVersion7()); }); await That(results).AreAllUnique(); } +#endif #if FEATURE_GUID_V7 [Fact] - public async Task CreateVersion7_ShouldBeThreadSafeAndReturnUniqueItems() + public async Task CreateVersion7_WithOffset_ShouldBeThreadSafeAndReturnUniqueItems() { ConcurrentBag results = []; Parallel.For(0, 100, _ => { - results.Add(RandomSystem.Guid.CreateVersion7()); + results.Add(RandomSystem.Guid.CreateVersion7(DateTimeOffset.UtcNow)); }); await That(results).AreAllUnique(); } #endif + [Fact] + public async Task Empty_ShouldReturnEmptyGuid() + { + await That(RandomSystem.Guid.Empty).IsEqualTo(Guid.Empty); + } -#if FEATURE_GUID_V7 [Fact] - public async Task CreateVersion7_WithOffset_ShouldBeThreadSafeAndReturnUniqueItems() + public async Task NewGuid_ShouldBeThreadSafeAndReturnUniqueItems() { ConcurrentBag results = []; Parallel.For(0, 100, _ => { - results.Add(RandomSystem.Guid.CreateVersion7(DateTimeOffset.UtcNow)); + results.Add(RandomSystem.Guid.NewGuid()); }); await That(results).AreAllUnique(); } -#endif #if FEATURE_GUID_PARSE [Theory] @@ -67,9 +67,9 @@ public async Task Parse_SpanArray_ShouldReturnCorrectGuid(Guid guid) { ReadOnlySpan serializedGuid = guid.ToString().AsSpan(); -#pragma warning disable MA0011 + #pragma warning disable MA0011 Guid result = RandomSystem.Guid.Parse(serializedGuid); -#pragma warning restore MA0011 + #pragma warning restore MA0011 await That(result).IsEqualTo(guid); } @@ -82,9 +82,39 @@ public async Task Parse_String_ShouldReturnCorrectGuid(Guid guid) { string serializedGuid = guid.ToString(); -#pragma warning disable MA0011 + #pragma warning disable MA0011 Guid result = RandomSystem.Guid.Parse(serializedGuid); -#pragma warning restore MA0011 + #pragma warning restore MA0011 + + await That(result).IsEqualTo(guid); + } +#endif + +#if FEATURE_GUID_PARSE_UTF8 + [Theory] + [AutoData] + public async Task Parse_UTF8_ShouldReturnCorrectGuid(Guid guid) + { + byte[] bytes = Encoding.UTF8.GetBytes(guid.ToString()); + + #pragma warning disable MA0011 + Guid result = RandomSystem.Guid.Parse(bytes); + #pragma warning restore MA0011 + + await That(result).IsEqualTo(guid); + } +#endif + +#if FEATURE_GUID_PARSE_UTF8 + [Theory] + [AutoData] + public async Task Parse_UTF8_WithFormatProvider_ShouldReturnCorrectGuid(Guid guid) + { + byte[] bytes = Encoding.UTF8.GetBytes(guid.ToString()); + + #pragma warning disable MA0011 + Guid result = RandomSystem.Guid.Parse(bytes, CultureInfo.InvariantCulture); + #pragma warning restore MA0011 await That(result).IsEqualTo(guid); } @@ -150,9 +180,24 @@ public async Task TryParse_SpanArray_ShouldReturnTrue(Guid guid) { ReadOnlySpan serializedGuid = guid.ToString().AsSpan(); -#pragma warning disable MA0011 + #pragma warning disable MA0011 bool result = RandomSystem.Guid.TryParse(serializedGuid, out Guid value); -#pragma warning restore MA0011 + #pragma warning restore MA0011 + + await That(result).IsTrue(); + await That(value).IsEqualTo(guid); + } +#endif + +#if FEATURE_GUID_FORMATPROVIDER + [Theory] + [AutoData] + public async Task TryParse_SpanArray_WithFormatProvider_ShouldReturnTrue(Guid guid) + { + ReadOnlySpan serializedGuid = guid.ToString().AsSpan(); + + bool result = RandomSystem.Guid.TryParse(serializedGuid, CultureInfo.InvariantCulture, + out Guid value); await That(result).IsTrue(); await That(value).IsEqualTo(guid); @@ -166,9 +211,9 @@ public async Task TryParse_String_ShouldReturnTrue(Guid guid) { string serializedGuid = guid.ToString(); -#pragma warning disable MA0011 + #pragma warning disable MA0011 bool result = RandomSystem.Guid.TryParse(serializedGuid, out Guid value); -#pragma warning restore MA0011 + #pragma warning restore MA0011 await That(result).IsTrue(); await That(value).IsEqualTo(guid); @@ -178,9 +223,9 @@ public async Task TryParse_String_ShouldReturnTrue(Guid guid) #if FEATURE_GUID_FORMATPROVIDER [Theory] [AutoData] - public async Task TryParse_WithFormatProvider_SpanArray_ShouldReturnTrue(Guid guid) + public async Task TryParse_String_WithFormatProvider_ShouldReturnTrue(Guid guid) { - ReadOnlySpan serializedGuid = guid.ToString().AsSpan(); + string serializedGuid = guid.ToString(); bool result = RandomSystem.Guid.TryParse(serializedGuid, CultureInfo.InvariantCulture, out Guid value); @@ -190,14 +235,30 @@ public async Task TryParse_WithFormatProvider_SpanArray_ShouldReturnTrue(Guid gu } #endif -#if FEATURE_GUID_FORMATPROVIDER +#if FEATURE_GUID_PARSE_UTF8 [Theory] [AutoData] - public async Task TryParse_WithFormatProvider_String_ShouldReturnTrue(Guid guid) + public async Task TryParse_UTF8_ShouldReturnTrue(Guid guid) { - string serializedGuid = guid.ToString(); + byte[] bytes = Encoding.UTF8.GetBytes(guid.ToString()); - bool result = RandomSystem.Guid.TryParse(serializedGuid, CultureInfo.InvariantCulture, + #pragma warning disable MA0011 + bool result = RandomSystem.Guid.TryParse(bytes, out Guid value); + #pragma warning restore MA0011 + + await That(result).IsTrue(); + await That(value).IsEqualTo(guid); + } +#endif + +#if FEATURE_GUID_PARSE_UTF8 + [Theory] + [AutoData] + public async Task TryParse_UTF8_WithFormatProvider_ShouldReturnTrue(Guid guid) + { + byte[] bytes = Encoding.UTF8.GetBytes(guid.ToString()); + + bool result = RandomSystem.Guid.TryParse(bytes, CultureInfo.InvariantCulture, out Guid value); await That(result).IsTrue(); @@ -240,7 +301,7 @@ public async Task TryParseExact_String_ShouldReturnTrue(string format, Guid guid #region Helpers #if FEATURE_GUID_PARSE -#pragma warning disable MA0018 + #pragma warning disable MA0018 public static IEnumerable GuidFormats() { yield return ["N"]; diff --git a/Tests/Testably.Abstractions.Tests/RandomSystem/RandomTests.cs b/Tests/Testably.Abstractions.Tests/RandomSystem/RandomTests.cs index 9d1fa7e24..4dfebfc46 100644 --- a/Tests/Testably.Abstractions.Tests/RandomSystem/RandomTests.cs +++ b/Tests/Testably.Abstractions.Tests/RandomSystem/RandomTests.cs @@ -8,6 +8,74 @@ namespace Testably.Abstractions.Tests.RandomSystem; [RandomSystemTests] public partial class RandomTests { +#if FEATURE_RANDOM_STRINGS + [Theory] + [InlineData(2)] + [InlineData(100)] + [InlineData(1000)] + public async Task GetHexString_ShouldHaveExpectedLength(int length) + { + string result = RandomSystem.Random.Shared.GetHexString(length); + + await That(result).HasLength().EqualTo(length); + } +#endif + +#if FEATURE_RANDOM_STRINGS + [Fact] + public async Task GetHexString_ShouldOnlyContainHexadecimalCharacters() + { + char[] hexadecimalCharacters = "0123456789ABCDEF".ToCharArray(); + + string result = RandomSystem.Random.Shared.GetHexString(10000); + + await That(result.ToCharArray()).All().Satisfy(c => hexadecimalCharacters.Contains(c)); + } +#endif + +#if FEATURE_RANDOM_STRINGS + [Fact] + public async Task GetHexString_WithDestinationSpan_ShouldOnlyContainHexadecimalCharacters() + { + char[] buffer = new char[10000]; + Span destination = new(buffer); + char[] hexadecimalCharacters = "0123456789ABCDEF".ToCharArray(); + + RandomSystem.Random.Shared.GetHexString(destination); + + char[] destinationArray = destination.ToArray(); + await That(destinationArray).All().Satisfy(c => hexadecimalCharacters.Contains(c)); + } +#endif + +#if FEATURE_RANDOM_STRINGS + [Fact] + public async Task + GetHexString_WithDestinationSpan_WithLowercase_ShouldOnlyContainLowercaseHexadecimalCharacters() + { + char[] buffer = new char[10000]; + Span destination = new(buffer); + char[] hexadecimalCharacters = "0123456789abcdef".ToCharArray(); + + RandomSystem.Random.Shared.GetHexString(destination, true); + + char[] destinationArray = destination.ToArray(); + await That(destinationArray).All().Satisfy(c => hexadecimalCharacters.Contains(c)); + } +#endif + +#if FEATURE_RANDOM_STRINGS + [Fact] + public async Task GetHexString_WithLowercase_ShouldOnlyContainLowercaseHexadecimalCharacters() + { + char[] hexadecimalCharacters = "0123456789abcdef".ToCharArray(); + + string result = RandomSystem.Random.Shared.GetHexString(10000, true); + + await That(result.ToCharArray()).All().Satisfy(c => hexadecimalCharacters.Contains(c)); + } +#endif + #if FEATURE_RANDOM_ITEMS [Fact] public async Task GetItems_Array_EmptyChoices_ShouldThrowArgumentNullException() @@ -43,7 +111,8 @@ public async Task GetItems_Array_LengthLargerThanChoices_ShouldIncludeDuplicateV [Theory] [InlineData(-1)] [InlineData(-200)] - public async Task GetItems_Array_NegativeLength_ShouldThrowArgumentOutOfRangeException(int length) + public async Task GetItems_Array_NegativeLength_ShouldThrowArgumentOutOfRangeException( + int length) { int[] choices = Enumerable.Range(1, 10).ToArray(); @@ -53,7 +122,8 @@ void Act() } await That(Act).Throws() - .WithMessage($"length ('{length}') must be a non-negative value. (Parameter 'length'){Environment.NewLine}Actual value was {length}."); + .WithMessage( + $"length ('{length}') must be a non-negative value. (Parameter 'length'){Environment.NewLine}Actual value was {length}."); } #endif @@ -113,7 +183,8 @@ public async Task GetItems_ReadOnlySpan_ShouldSelectRandomElements() #if FEATURE_RANDOM_ITEMS [Fact] - public async Task GetItems_SpanDestination_LengthLargerThanChoices_ShouldIncludeDuplicateValues() + public async Task + GetItems_SpanDestination_LengthLargerThanChoices_ShouldIncludeDuplicateValues() { int[] buffer = new int[100]; Span destination = new(buffer); @@ -121,7 +192,7 @@ public async Task GetItems_SpanDestination_LengthLargerThanChoices_ShouldInclude RandomSystem.Random.Shared.GetItems(choices, destination); - var destinationArray = destination.ToArray(); + int[] destinationArray = destination.ToArray(); await That(destinationArray).All().Satisfy(r => r >= 1 && r <= 10); await That(destinationArray.Length).IsEqualTo(100); } @@ -137,11 +208,40 @@ public async Task GetItems_SpanDestination_ShouldSelectRandomElements() RandomSystem.Random.Shared.GetItems(choices, destination); - var destinationArray = destination.ToArray(); + int[] destinationArray = destination.ToArray(); await That(destinationArray).All().Satisfy(r => r >= 1 && r <= 100); await That(destinationArray.Length).IsEqualTo(10); } #endif + +#if FEATURE_RANDOM_STRINGS + [Theory] + [InlineData(2)] + [InlineData(100)] + [InlineData(1000)] + public async Task GetString_ShouldHaveExpectedLength(int length) + { + ReadOnlySpan choices = "abcde".ToCharArray(); + + string result = RandomSystem.Random.Shared.GetString(choices, length); + + await That(result).HasLength().EqualTo(length); + } +#endif + +#if FEATURE_RANDOM_STRINGS + [Theory] + [AutoData] + public async Task GetString_ShouldOnlyContainProvidedCharacters(char[] chars) + { + ReadOnlySpan choices = chars; + + string result = RandomSystem.Random.Shared.GetString(choices, 100); + + await That(result.ToCharArray()).All().Satisfy(c => chars.Contains(c)); + } +#endif + [Fact] public async Task Next_MaxValue_ShouldOnlyReturnValidValues() { @@ -303,7 +403,8 @@ void Act() RandomSystem.Random.Shared.Shuffle(values); } - await That(Act).ThrowsExactly().WithParamName(nameof(values)); } + await That(Act).ThrowsExactly().WithParamName(nameof(values)); + } #endif #if FEATURE_RANDOM_ITEMS diff --git a/global.json b/global.json index 4c1ade4fe..bb16faab1 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.304", + "version": "10.0.100-preview.7", "rollForward": "latestMinor" } }