mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-07-09 02:26:47 +00:00
Add another convience method and tests
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -10,6 +11,45 @@ namespace SharpCompress.Archives;
|
||||
|
||||
public static partial class ArchiveFactory
|
||||
{
|
||||
public static async ValueTask ExtractToDirectoryAsync(
|
||||
string sourceArchive,
|
||||
string destinationDirectory,
|
||||
ExtractionOptions? options = null,
|
||||
IProgress<ProgressReport>? progress = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
sourceArchive.NotNullOrEmpty(nameof(sourceArchive));
|
||||
await ExtractToDirectoryAsync(
|
||||
new FileInfo(sourceArchive),
|
||||
destinationDirectory,
|
||||
options,
|
||||
progress,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static async ValueTask ExtractToDirectoryAsync(
|
||||
FileInfo sourceArchive,
|
||||
string destinationDirectory,
|
||||
ExtractionOptions? options = null,
|
||||
IProgress<ProgressReport>? progress = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
sourceArchive.NotNull(nameof(sourceArchive));
|
||||
await using var archive = await OpenAsyncArchive(
|
||||
sourceArchive,
|
||||
ReaderOptions.ForFilePath,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
await archive
|
||||
.WriteToDirectoryAsync(destinationDirectory, options, progress, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(
|
||||
Stream stream,
|
||||
ReaderOptions? readerOptions = null,
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test;
|
||||
|
||||
public class ArchiveFactoryAsyncExtractionTests : ArchiveTests
|
||||
{
|
||||
public ArchiveFactoryAsyncExtractionTests() => UseExtensionInsteadOfNameToVerify = true;
|
||||
|
||||
[Fact]
|
||||
public async ValueTask ExtractToDirectoryAsync_Zip_DefaultAuto_Extracts()
|
||||
{
|
||||
await ArchiveFactory.ExtractToDirectoryAsync(
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip"),
|
||||
SCRATCH_FILES_PATH
|
||||
);
|
||||
|
||||
VerifyFiles();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async ValueTask ExtractToDirectoryAsync_Zip_FileInfo_RequireParallel_Extracts()
|
||||
{
|
||||
await ArchiveFactory.ExtractToDirectoryAsync(
|
||||
new FileInfo(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip")),
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { Parallelism = ExtractionParallelism.RequireParallel }
|
||||
);
|
||||
|
||||
VerifyFiles();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async ValueTask ExtractToDirectoryAsync_Zip_SingleThreaded_Extracts()
|
||||
{
|
||||
await ArchiveFactory.ExtractToDirectoryAsync(
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip"),
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { Parallelism = ExtractionParallelism.SingleThreaded }
|
||||
);
|
||||
|
||||
VerifyFiles();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async ValueTask ExtractToDirectoryAsync_ForwardsProgress()
|
||||
{
|
||||
var progressReports = new List<ProgressReport>();
|
||||
var progress = new SynchronousProgress<ProgressReport>(progressReports.Add);
|
||||
|
||||
await ArchiveFactory.ExtractToDirectoryAsync(
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip"),
|
||||
SCRATCH_FILES_PATH,
|
||||
progress: progress
|
||||
);
|
||||
|
||||
VerifyFiles();
|
||||
Assert.NotEmpty(progressReports);
|
||||
}
|
||||
|
||||
private sealed class SynchronousProgress<T> : IProgress<T>
|
||||
{
|
||||
private readonly Action<T> handler;
|
||||
|
||||
internal SynchronousProgress(Action<T> handler) => this.handler = handler;
|
||||
|
||||
public void Report(T value) => handler(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.GZip;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Writers.GZip;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.GZip;
|
||||
|
||||
public class GZipArchiveAsyncFactoryExtractionTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
public async ValueTask ExtractToDirectoryAsync_RequireParallel_Fails()
|
||||
{
|
||||
var archivePath = GetScratch2Path("single.gz");
|
||||
var entryBytes = Encoding.UTF8.GetBytes("single file content");
|
||||
|
||||
await using (var archive = (GZipArchive)await GZipArchive.CreateAsyncArchive())
|
||||
{
|
||||
await archive.AddEntryAsync(
|
||||
"single.txt",
|
||||
new MemoryStream(entryBytes),
|
||||
closeStream: true,
|
||||
size: entryBytes.Length
|
||||
);
|
||||
await archive.SaveToAsync(archivePath, new GZipWriterOptions());
|
||||
}
|
||||
|
||||
await Assert.ThrowsAsync<NotSupportedException>(async () =>
|
||||
await ArchiveFactory.ExtractToDirectoryAsync(
|
||||
archivePath,
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { Parallelism = ExtractionParallelism.RequireParallel }
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.Rar;
|
||||
|
||||
public class RarArchiveAsyncFactoryExtractionTests : ArchiveTests
|
||||
{
|
||||
[Fact]
|
||||
public async ValueTask ExtractToDirectoryAsync_NonSolid_RequireParallel_Extracts()
|
||||
{
|
||||
await ArchiveFactory.ExtractToDirectoryAsync(
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.rar"),
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { Parallelism = ExtractionParallelism.RequireParallel }
|
||||
);
|
||||
|
||||
VerifyFiles();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async ValueTask ExtractToDirectoryAsync_Solid_RequireParallel_Fails()
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(async () =>
|
||||
await ArchiveFactory.ExtractToDirectoryAsync(
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.solid.rar"),
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { Parallelism = ExtractionParallelism.RequireParallel }
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.SevenZip;
|
||||
|
||||
public class SevenZipArchiveAsyncFactoryExtractionTests : ArchiveTests
|
||||
{
|
||||
[Fact]
|
||||
public async ValueTask ExtractToDirectoryAsync_NonSolid_RequireParallel_Extracts()
|
||||
{
|
||||
await ArchiveFactory.ExtractToDirectoryAsync(
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "7Zip.nonsolid.7z"),
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { Parallelism = ExtractionParallelism.RequireParallel }
|
||||
);
|
||||
|
||||
VerifyFiles();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async ValueTask ExtractToDirectoryAsync_SolidSingleBlock_RequireParallel_Fails()
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(async () =>
|
||||
await ArchiveFactory.ExtractToDirectoryAsync(
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.1block.7z"),
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { Parallelism = ExtractionParallelism.RequireParallel }
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Writers;
|
||||
using SharpCompress.Writers.Zip;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.Zip;
|
||||
|
||||
public class ZipArchiveAsyncParallelExtractionTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
public async ValueTask RequireParallel_DetectsDuplicateOutputPathsBeforeWriting()
|
||||
{
|
||||
var archivePath = GetScratch2Path("duplicate-output.zip");
|
||||
CreateZip(archivePath, ("duplicate.txt", "first"), ("duplicate.txt", "second"));
|
||||
|
||||
var exception = await Assert.ThrowsAsync<ExtractionException>(async () =>
|
||||
await ArchiveFactory.ExtractToDirectoryAsync(
|
||||
archivePath,
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { Parallelism = ExtractionParallelism.RequireParallel }
|
||||
)
|
||||
);
|
||||
|
||||
Assert.Contains("multiple entries", exception.Message);
|
||||
Assert.False(File.Exists(GetScratchPath("duplicate.txt")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async ValueTask RequireParallel_HonorsCancellation()
|
||||
{
|
||||
using var cancellationTokenSource = new CancellationTokenSource();
|
||||
cancellationTokenSource.Cancel();
|
||||
|
||||
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () =>
|
||||
await ArchiveFactory.ExtractToDirectoryAsync(
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip"),
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { Parallelism = ExtractionParallelism.RequireParallel },
|
||||
cancellationToken: cancellationTokenSource.Token
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async ValueTask RequireParallel_EntryFailureIncludesEntryName()
|
||||
{
|
||||
var archivePath = GetScratch2Path("overwrite-failure.zip");
|
||||
CreateZip(archivePath, ("blocked.txt", "archive"));
|
||||
File.WriteAllText(GetScratchPath("blocked.txt"), "existing");
|
||||
|
||||
var exception = await Assert.ThrowsAnyAsync<Exception>(async () =>
|
||||
await ArchiveFactory.ExtractToDirectoryAsync(
|
||||
archivePath,
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions
|
||||
{
|
||||
Overwrite = false,
|
||||
Parallelism = ExtractionParallelism.RequireParallel,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
Assert.Contains("blocked.txt", exception.Message);
|
||||
}
|
||||
|
||||
private static void CreateZip(string archivePath, params (string Key, string Content)[] entries)
|
||||
{
|
||||
using var stream = File.Create(archivePath);
|
||||
using var writer = new ZipWriter(stream, new ZipWriterOptions(CompressionType.Deflate));
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
using var entryStream = new MemoryStream(Encoding.UTF8.GetBytes(entry.Content));
|
||||
writer.Write(entry.Key, entryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user