Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions SevenZipExtractor.Tests/Test7Zip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,17 @@ public void TestKnownFormatAndExtractToStream_OK()
{
this.TestExtractToStream(Resources.TestFiles.SevenZip, this.TestEntriesWithoutFolder, SevenZipFormat.SevenZip);
}

[TestMethod]
public void TestProgressWithArchiveExtraction_OK()
{
this.TestExtractArchiveWithProgress(Resources.TestFiles.SevenZip, SevenZipFormat.SevenZip);
}

[TestMethod]
public void TestProgressWithEntryExtraction_OK()
{
this.TestExtractEntriesWithProgress(Resources.TestFiles.SevenZip, SevenZipFormat.SevenZip);
}
}
}
12 changes: 12 additions & 0 deletions SevenZipExtractor.Tests/TestArj.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,17 @@ public void Text_UnboxAndCast_OK()

this.TestExtractToStream(Resources.TestFiles.ansimate_arj, testEntries, SevenZipFormat.Arj);
}

[TestMethod]
public void TestProgressWithArchiveExtraction_OK()
{
this.TestExtractArchiveWithProgress(Resources.TestFiles.ansimate_arj, SevenZipFormat.Arj);
}

[TestMethod]
public void TestProgressWithEntryExtraction_OK()
{
this.TestExtractEntriesWithProgress(Resources.TestFiles.ansimate_arj, SevenZipFormat.Arj);
}
}
}
88 changes: 88 additions & 0 deletions SevenZipExtractor.Tests/TestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,95 @@ protected void TestExtractToStream(byte[] archiveBytes, IList<TestFileEntry> exp
}
}
}
}

protected void TestExtractEntriesWithProgress(byte[] archiveBytes, SevenZipFormat? sevenZipFormat = null)
{
MemoryStream memoryStream = new MemoryStream(archiveBytes);

using (ArchiveFile archiveFile = new ArchiveFile(memoryStream, sevenZipFormat))
{
foreach (var entry in archiveFile.Entries)
{
if (entry.IsFolder)
{
continue;
}

using (MemoryStream entryMemoryStream = new MemoryStream())
{
bool progressCalledAtBeginning = false;
bool progressCalledAtEnd = false;

entry.Extract(entryMemoryStream, (s, e) =>
{
if (e.Total > 0)
{
if (e.Completed == 0)
{
progressCalledAtBeginning = true;
}
else if (e.Completed == e.Total)
{
progressCalledAtEnd = true;
}
}
});

Assert.IsTrue(progressCalledAtBeginning, $"Progress callback was not called at the beginning of extracting file {entry.FileName}.");
Assert.IsTrue(progressCalledAtEnd, $"Progress callback was not called at the end of extracting file {entry.FileName}.");
}
}
}
}

protected void TestExtractArchiveWithProgress(byte[] archiveBytes, SevenZipFormat? sevenZipFormat = null)
{
MemoryStream memoryStream = new MemoryStream(archiveBytes);

string tempPath = Path.Combine(Path.GetTempPath(), "SevenZipExtractorUnitTests");
Directory.CreateDirectory(tempPath);

try
{
using (ArchiveFile archiveFile = new ArchiveFile(memoryStream, sevenZipFormat))
{
int progressCalledAtBeginning = 0;
int progressCalledAtEnd = 0;
HashSet<uint> progressCalledForIndex = new HashSet<uint>();
HashSet<int> reportedTotalEntryCounts = new HashSet<int>();

archiveFile.Extract(tempPath, true, (s, e) =>
{
reportedTotalEntryCounts.Add(e.EntryCount);
progressCalledForIndex.Add(e.EntryIndex);

if (e.Total > 0)
{
if (e.Completed == 0)
{
progressCalledAtBeginning++;
}
else if (e.Completed == e.Total)
{
progressCalledAtEnd++;
}
}
});

Assert.AreEqual(1, reportedTotalEntryCounts.Count, "None or more than one total file count reported in progress callback.");

var entryCount = reportedTotalEntryCounts.First();
Assert.IsTrue(entryCount > 0, "No entries to extract in test archive, or progress callback never called, or progress callback called with zero total entry count.");
Assert.AreEqual(entryCount, progressCalledForIndex.Count, "Progress callback was not called at all for one or more entries or was called more than expected.");
Assert.IsTrue(progressCalledAtBeginning > 0, "Progress callback was not called at the beginning of extraction.");
Assert.IsTrue(progressCalledAtEnd > 0, "Progress callback was not called at the end of extraction.");
}
}
finally
{
Directory.Delete(tempPath, true);
}
}
}
}
12 changes: 12 additions & 0 deletions SevenZipExtractor.Tests/TestLzh.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,17 @@ public void TestKnownFormatAndExtractToStream_OK()
{
this.TestExtractToStream(Resources.TestFiles.lzh, this.TestEntriesWithoutFolder, SevenZipFormat.Lzh);
}

[TestMethod]
public void TestProgressWithArchiveExtraction_OK()
{
this.TestExtractArchiveWithProgress(Resources.TestFiles.lzh, SevenZipFormat.Lzh);
}

[TestMethod]
public void TestProgressWithEntryExtraction_OK()
{
this.TestExtractEntriesWithProgress(Resources.TestFiles.lzh, SevenZipFormat.Lzh);
}
}
}
12 changes: 12 additions & 0 deletions SevenZipExtractor.Tests/TestRar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ public void TestGuessAndExtractToStream_OK()
public void TestKnownFormatAndExtractToStream_OK()
{
this.TestExtractToStream(Resources.TestFiles.rar, this.TestEntriesWithFolder, SevenZipFormat.Rar5);
}

[TestMethod]
public void TestProgressWithArchiveExtraction_OK()
{
this.TestExtractArchiveWithProgress(Resources.TestFiles.rar, SevenZipFormat.Rar5);
}

[TestMethod]
public void TestProgressWithEntryExtraction_OK()
{
this.TestExtractEntriesWithProgress(Resources.TestFiles.rar, SevenZipFormat.Rar5);
}
}
}
12 changes: 12 additions & 0 deletions SevenZipExtractor.Tests/TestZip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ public void TestGuessAndExtractToStream_OK()
public void TestKnownFormatAndExtractToStream_OK()
{
this.TestExtractToStream(Resources.TestFiles.zip, this.TestEntriesWithFolder, SevenZipFormat.Zip);
}

[TestMethod]
public void TestProgressWithArchiveExtraction_OK()
{
this.TestExtractArchiveWithProgress(Resources.TestFiles.zip, SevenZipFormat.Zip);
}

[TestMethod]
public void TestProgressWithEntryExtraction_OK()
{
this.TestExtractEntriesWithProgress(Resources.TestFiles.zip, SevenZipFormat.Zip);
}
}
}
27 changes: 27 additions & 0 deletions SevenZipExtractor/ArchiveExtractionProgressEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SevenZipExtractor
{
public class ArchiveExtractionProgressEventArgs : EntryExtractionProgressEventArgs
{
/// <summary>
/// The index of the entry currently being extracted.
/// </summary>
public uint EntryIndex {get; }

/// <summary>
/// The total number of entries that will be extracted.
/// </summary>
public int EntryCount {get; }

internal ArchiveExtractionProgressEventArgs(uint entryIndex, int entryCount, ulong completed, ulong total) : base(completed, total)
{
EntryIndex = entryIndex;
EntryCount = entryCount;
}
}
}
10 changes: 6 additions & 4 deletions SevenZipExtractor/ArchiveFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public ArchiveFile(Stream archiveStream, SevenZipFormat? format = null, string l
this.archiveStream = new InStreamWrapper(archiveStream);
}

public void Extract(string outputFolder, bool overwrite = false)
public void Extract(string outputFolder, bool overwrite = false, EventHandler<ArchiveExtractionProgressEventArgs> progressEventHandler = null)
{
this.Extract(entry =>
{
Expand All @@ -92,10 +92,10 @@ public void Extract(string outputFolder, bool overwrite = false)
}

return null;
});
}, progressEventHandler);
}

public void Extract(Func<Entry, string> getOutputPath)
public void Extract(Func<Entry, string> getOutputPath, EventHandler<ArchiveExtractionProgressEventArgs> progressEventHandler = null)
{
IList<Stream> fileStreams = new List<Stream>();

Expand Down Expand Up @@ -128,7 +128,9 @@ public void Extract(Func<Entry, string> getOutputPath)
fileStreams.Add(File.Create(outputPath));
}

this.archive.Extract(null, 0xFFFFFFFF, 0, new ArchiveStreamsCallback(fileStreams));
ArchiveStreamsCallback extractCallback = new ArchiveStreamsCallback(fileStreams, progressEventHandler);
this.archive.Extract(null, 0xFFFFFFFF, 0, extractCallback);
extractCallback.InvokeFinalProgressCallback();
}
finally
{
Expand Down
37 changes: 35 additions & 2 deletions SevenZipExtractor/ArchiveStreamCallback.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
using System.IO;
using System;
using System.IO;

namespace SevenZipExtractor
{
internal class ArchiveStreamCallback : IArchiveExtractCallback
{
private readonly uint fileNumber;
private readonly Stream stream;
private readonly EventHandler<EntryExtractionProgressEventArgs> progressEventHandler;

private ulong currentCompleteValue;
private ulong currentTotal;
private bool finalProgressReported = false;

public ArchiveStreamCallback(uint fileNumber, Stream stream)
public ArchiveStreamCallback(uint fileNumber, Stream stream, EventHandler<EntryExtractionProgressEventArgs> progressEventHandler)
{
this.fileNumber = fileNumber;
this.stream = stream;
this.progressEventHandler = progressEventHandler;
}

public void SetTotal(ulong total)
{
this.currentTotal = total;
this.InvokeProgressCallback();
}

public void SetCompleted(ref ulong completeValue)
{
this.currentCompleteValue = completeValue;
this.InvokeProgressCallback();
}

public int GetStream(uint index, out ISequentialOutStream outStream, AskMode askExtractMode)
Expand All @@ -41,5 +52,27 @@ public void PrepareOperation(AskMode askExtractMode)
public void SetOperationResult(OperationResult resultEOperationResult)
{
}

public void InvokeFinalProgressCallback()
{
if (!this.finalProgressReported)
{
// 7z doesn't invoke SetCompleted for all formats when an entry is fully extracted, so we fake it.
this.SetCompleted(ref this.currentTotal);
}
}

private void InvokeProgressCallback()
{
progressEventHandler?.Invoke(
this,
new EntryExtractionProgressEventArgs(this.currentCompleteValue, this.currentTotal)
);

if (this.currentCompleteValue == this.currentTotal)
{
this.finalProgressReported = true;
}
}
}
}
Loading