Files
sharpcompress/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs

449 lines
15 KiB
C#
Raw Permalink Normal View History

using System;
2023-03-19 12:34:59 +01:00
using System.IO;
2023-09-11 19:24:54 +02:00
using System.Linq;
2023-09-17 17:46:56 -06:00
using SharpCompress.Archives;
using SharpCompress.Archives.SevenZip;
2015-12-30 11:19:42 +00:00
using SharpCompress.Common;
using SharpCompress.Common.SevenZip;
using SharpCompress.Factories;
using SharpCompress.Readers;
2026-04-23 09:56:26 +01:00
using SharpCompress.Test.Mocks;
2015-12-30 11:19:42 +00:00
using Xunit;
2022-12-20 15:06:44 +00:00
namespace SharpCompress.Test.SevenZip;
public class SevenZipArchiveTests : ArchiveTests
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_Solid_StreamRead() => ArchiveStreamRead("7Zip.solid.7z");
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_NonSolid_StreamRead() => ArchiveStreamRead("7Zip.nonsolid.7z");
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_LZMA_StreamRead() => ArchiveStreamRead("7Zip.LZMA.7z");
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_LZMA_PathRead() => ArchiveFileRead("7Zip.LZMA.7z");
2022-12-20 15:06:44 +00:00
2026-04-23 09:56:26 +01:00
[Fact]
public void SevenZipArchive_StreamCollection_Throws_On_NonSeekable_Stream()
{
using var nonSeekable = new ForwardOnlyStream(new MemoryStream());
using var seekable = new MemoryStream();
Assert.Throws<ArgumentException>(() =>
SevenZipArchive.OpenArchive([nonSeekable, seekable])
);
}
2026-04-23 10:19:37 +01:00
[Fact]
public void SevenZipArchive_Stream_Throws_On_Unreadable_Stream()
{
using var unreadable = new TestStream(new MemoryStream(), false, true, true);
Assert.Throws<ArgumentException>(() => SevenZipArchive.OpenArchive(unreadable));
}
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_LZMAAES_StreamRead() =>
2026-04-21 10:55:59 +01:00
ArchiveStreamRead(
"7Zip.LZMA.Aes.7z",
ReaderOptions.ForExternalStream with
{
Password = "testpassword",
}
);
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_LZMAAES_PathRead() =>
2026-04-21 10:55:59 +01:00
ArchiveFileRead(
"7Zip.LZMA.Aes.7z",
ReaderOptions.ForFilePath with
{
Password = "testpassword",
}
);
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_LZMAAES_NoPasswordExceptionTest() =>
2026-02-16 09:27:06 +00:00
Assert.Throws<CryptographicException>(() =>
2026-04-21 11:04:14 +01:00
ArchiveFileRead("7Zip.LZMA.Aes.7z", ReaderOptions.ForFilePath.WithPassword(null))
2022-12-20 15:06:44 +00:00
); //was failing with ArgumentNullException not CryptographicException like rar
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_PPMd_StreamRead() => ArchiveStreamRead("7Zip.PPMd.7z");
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_PPMd_StreamRead_Extract_All() =>
2022-12-20 15:06:44 +00:00
ArchiveStreamReadExtractAll("7Zip.PPMd.7z", CompressionType.PPMd);
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_PPMd_PathRead() => ArchiveFileRead("7Zip.PPMd.7z");
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_LZMA2_StreamRead() => ArchiveStreamRead("7Zip.LZMA2.7z");
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_LZMA2_PathRead() => ArchiveFileRead("7Zip.LZMA2.7z");
2022-12-20 15:06:44 +00:00
[Fact]
public void SevenZipArchive_LZMA2_EXE_StreamRead() =>
2026-04-21 11:04:14 +01:00
ArchiveStreamRead(
new SevenZipFactory(),
"7Zip.LZMA2.exe",
ReaderOptions.ForExternalStream.WithLookForHeader(true)
);
[Fact]
public void SevenZipArchive_LZMA2_EXE_PathRead() =>
2026-04-21 11:04:14 +01:00
ArchiveFileRead(
"7Zip.LZMA2.exe",
ReaderOptions.ForFilePath.WithLookForHeader(true),
new SevenZipFactory()
);
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_LZMA2AES_StreamRead() =>
2026-04-21 10:55:59 +01:00
ArchiveStreamRead(
"7Zip.LZMA2.Aes.7z",
ReaderOptions.ForExternalStream with
{
Password = "testpassword",
}
);
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_LZMA2AES_PathRead() =>
2026-04-21 10:55:59 +01:00
ArchiveFileRead(
"7Zip.LZMA2.Aes.7z",
ReaderOptions.ForFilePath with
{
Password = "testpassword",
}
);
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_BZip2_StreamRead() => ArchiveStreamRead("7Zip.BZip2.7z");
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_BZip2_PathRead() => ArchiveFileRead("7Zip.BZip2.7z");
2022-12-20 15:06:44 +00:00
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_LZMA_Time_Attributes_PathRead() =>
2022-12-20 15:06:44 +00:00
ArchiveFileReadEx("7Zip.LZMA.7z");
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_BZip2_Split() =>
Assert.Throws<ArchiveOperationException>(() =>
2026-01-22 14:15:47 +00:00
ArchiveStreamRead(
".001",
null,
"Original.7z.001",
"Original.7z.002",
"Original.7z.003",
"Original.7z.004",
"Original.7z.005",
"Original.7z.006",
"Original.7z.007"
)
2026-01-22 09:05:26 +00:00
);
2022-12-20 15:06:44 +00:00
//Same as archive as Original.7z.001 ... 007 files without the root directory 'Original\' in the archive - this caused the verify to fail
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_BZip2_Split_Working() =>
Assert.Throws<ArchiveOperationException>(() =>
2026-01-22 14:15:47 +00:00
ArchiveStreamRead(
".001",
null,
"7Zip.BZip2.split.001",
"7Zip.BZip2.split.002",
"7Zip.BZip2.split.003",
"7Zip.BZip2.split.004",
"7Zip.BZip2.split.005",
"7Zip.BZip2.split.006",
"7Zip.BZip2.split.007"
)
2022-12-20 15:06:44 +00:00
);
//will detect and load other files
[Fact]
2022-12-20 15:20:49 +00:00
public void SevenZipArchive_BZip2_Split_FirstFileRead() =>
2022-12-20 15:06:44 +00:00
ArchiveFileRead("7Zip.BZip2.split.001");
2022-12-20 15:20:49 +00:00
//"7Zip.BZip2.split.002",
//"7Zip.BZip2.split.003",
//"7Zip.BZip2.split.004",
//"7Zip.BZip2.split.005",
//"7Zip.BZip2.split.006",
//"7Zip.BZip2.split.007"
[Fact]
public void SevenZipArchive_Copy_StreamRead() => ArchiveStreamRead("7Zip.Copy.7z");
[Fact]
public void SevenZipArchive_Copy_PathRead() => ArchiveFileRead("7Zip.Copy.7z");
[Fact]
public void SevenZipArchive_Copy_CompressionType()
{
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "7Zip.Copy.7z")))
2026-01-15 11:41:30 +00:00
using (var archive = SevenZipArchive.OpenArchive(stream))
{
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
{
Assert.Equal(CompressionType.None, entry.CompressionType);
}
}
}
[Fact]
public void SevenZipArchive_ZSTD_StreamRead() => ArchiveStreamRead("7Zip.ZSTD.7z");
[Fact]
public void SevenZipArchive_ZSTD_PathRead() => ArchiveFileRead("7Zip.ZSTD.7z");
[Fact]
public void SevenZipArchive_ZSTD_Split() =>
2026-01-22 08:52:00 +00:00
ArchiveStreamMultiRead(
null,
"7Zip.ZSTD.Split.7z.001",
"7Zip.ZSTD.Split.7z.002",
"7Zip.ZSTD.Split.7z.003",
"7Zip.ZSTD.Split.7z.004",
"7Zip.ZSTD.Split.7z.005",
"7Zip.ZSTD.Split.7z.006"
);
2023-12-09 13:41:35 +01:00
[Fact]
public void SevenZipArchive_EOS_FileRead() => ArchiveFileRead("7Zip.eos.7z");
2023-09-11 19:24:54 +02:00
[Fact]
2023-03-21 13:14:08 +00:00
public void SevenZipArchive_Delta_FileRead() => ArchiveFileRead("7Zip.delta.7z");
2023-03-19 12:34:59 +01:00
2023-09-11 19:24:54 +02:00
[Fact]
public void SevenZipArchive_ARM_FileRead() => ArchiveFileRead("7Zip.ARM.7z");
[Fact]
public void SevenZipArchive_ARMT_FileRead() => ArchiveFileRead("7Zip.ARMT.7z");
[Fact]
public void SevenZipArchive_BCJ_FileRead() => ArchiveFileRead("7Zip.BCJ.7z");
[Fact]
public void SevenZipArchive_BCJ2_FileRead() => ArchiveFileRead("7Zip.BCJ2.7z");
[Fact]
public void SevenZipArchive_IA64_FileRead() => ArchiveFileRead("7Zip.IA64.7z");
[Fact]
public void SevenZipArchive_PPC_FileRead() => ArchiveFileRead("7Zip.PPC.7z");
[Fact]
public void SevenZipArchive_SPARC_FileRead() => ArchiveFileRead("7Zip.SPARC.7z");
[Fact]
public void SevenZipArchive_ARM64_FileRead() => ArchiveFileRead("7Zip.ARM64.7z");
[Fact]
public void SevenZipArchive_RISCV_FileRead() => ArchiveFileRead("7Zip.RISCV.7z");
2023-09-11 19:24:54 +02:00
[Fact]
public void SevenZipArchive_Filters_FileRead() => ArchiveFileRead("7Zip.Filters.7z");
2023-03-19 12:34:59 +01:00
[Fact]
2023-03-21 13:14:08 +00:00
public void SevenZipArchive_Delta_Distance() =>
ArchiveDeltaDistanceRead("7Zip.delta.distance.7z");
2023-09-17 17:46:56 -06:00
[Fact]
public void SevenZipArchive_Tar_PathRead()
{
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "7Zip.Tar.tar.7z")))
2026-01-15 11:41:30 +00:00
using (var archive = SevenZipArchive.OpenArchive(stream))
2023-09-17 17:46:56 -06:00
{
var entry = archive.Entries.First();
2024-04-18 14:25:10 +01:00
entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key.NotNull()));
2023-09-17 17:46:56 -06:00
var size = entry.Size;
var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "7Zip.Tar.tar"));
var test = new FileInfo(Path.Combine(TEST_ARCHIVES_PATH, "7Zip.Tar.tar"));
Assert.Equal(size, scratch.Length);
Assert.Equal(size, test.Length);
}
CompareArchivesByPath(
2023-09-18 09:48:31 +01:00
Path.Combine(SCRATCH_FILES_PATH, "7Zip.Tar.tar"),
Path.Combine(TEST_ARCHIVES_PATH, "7Zip.Tar.tar")
);
2023-09-17 17:46:56 -06:00
}
2025-07-13 13:38:53 +02:00
2025-12-02 09:55:24 -05:00
[Fact]
public void SevenZipArchive_TestEncryptedDetection()
{
2026-01-15 11:41:30 +00:00
using var passwordProtectedFilesArchive = SevenZipArchive.OpenArchive(
2025-12-02 09:55:24 -05:00
Path.Combine(TEST_ARCHIVES_PATH, "7Zip.encryptedFiles.7z")
);
Assert.True(passwordProtectedFilesArchive.IsEncrypted);
}
2025-07-13 13:38:53 +02:00
[Fact]
public void SevenZipArchive_TestSolidDetection()
{
2026-01-15 11:41:30 +00:00
using var oneBlockSolidArchive = SevenZipArchive.OpenArchive(
2025-07-13 13:38:53 +02:00
Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.1block.7z")
);
Assert.True(oneBlockSolidArchive.IsSolid);
2026-01-15 11:41:30 +00:00
using var solidArchive = SevenZipArchive.OpenArchive(
2025-07-13 13:38:53 +02:00
Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.7z")
);
Assert.True(solidArchive.IsSolid);
2026-01-15 11:41:30 +00:00
using var nonSolidArchive = SevenZipArchive.OpenArchive(
2025-07-13 13:38:53 +02:00
Path.Combine(TEST_ARCHIVES_PATH, "7Zip.nonsolid.7z")
);
Assert.False(nonSolidArchive.IsSolid);
}
[Fact]
public void SevenZipArchive_Solid_ExtractAllEntries_Contiguous()
{
// This test verifies that solid archives iterate entries as contiguous streams
// rather than recreating the decompression stream for each entry
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.7z");
2026-01-28 11:37:24 +00:00
using var archive = SevenZipArchive.OpenArchive(testArchive);
Assert.True(archive.IsSolid);
using var reader = archive.ExtractAllEntries();
while (reader.MoveToNextEntry())
{
if (!reader.Entry.IsDirectory)
{
reader.WriteEntryToDirectory(SCRATCH_FILES_PATH);
}
}
VerifyFiles();
}
[Fact]
public void SevenZipArchive_Solid_VerifyStreamReuse()
{
// This test verifies that the folder stream is reused within each folder
// and not recreated for each entry in solid archives
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.7z");
2026-01-28 11:37:24 +00:00
using var archive = SevenZipArchive.OpenArchive(testArchive);
Assert.True(archive.IsSolid);
using var reader = archive.ExtractAllEntries();
2026-01-28 08:10:06 +00:00
var sevenZipReader = Assert.IsType<SevenZipArchive.SevenZipReader>(reader);
sevenZipReader.DiagnosticsEnabled = true;
Stream? currentFolderStreamInstance = null;
object? currentFolder = null;
var entryCount = 0;
var entriesInCurrentFolder = 0;
var streamRecreationsWithinFolder = 0;
while (reader.MoveToNextEntry())
{
if (!reader.Entry.IsDirectory)
{
// Extract the entry to trigger GetEntryStream
using var entryStream = reader.OpenEntryStream();
var buffer = new byte[4096];
while (entryStream.Read(buffer, 0, buffer.Length) > 0)
{
// Read the stream to completion
}
entryCount++;
2026-01-28 08:10:06 +00:00
var folderStream = sevenZipReader.DiagnosticsCurrentFolderStream;
var folder = sevenZipReader.DiagnosticsCurrentFolder;
Assert.NotNull(folderStream); // Folder stream should exist
// Check if we're in a new folder
if (currentFolder == null || !ReferenceEquals(currentFolder, folder))
{
// Starting a new folder
currentFolder = folder;
currentFolderStreamInstance = folderStream;
entriesInCurrentFolder = 1;
}
else
{
// Same folder - verify stream wasn't recreated
entriesInCurrentFolder++;
if (!ReferenceEquals(currentFolderStreamInstance, folderStream))
{
// Stream was recreated within the same folder - this is the bug we're testing for!
streamRecreationsWithinFolder++;
}
currentFolderStreamInstance = folderStream;
}
}
}
// Verify we actually tested multiple entries
Assert.True(entryCount > 1, "Test should have multiple entries to verify stream reuse");
// The critical check: within a single folder, the stream should NEVER be recreated
2026-01-28 08:10:06 +00:00
Assert.Equal(0, streamRecreationsWithinFolder); // Folder stream should remain the same for all entries in the same folder
}
[Fact]
public void SevenZipArchive_EmptyStream_WriteToDirectory()
{
// This test specifically verifies that archives with empty-stream entries
// (files with size 0 and no compressed data) can be extracted without throwing
// NullReferenceException. This was previously failing because the folder was null
// for empty-stream entries.
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.EmptyStream.7z");
using var archive = SevenZipArchive.OpenArchive(testArchive);
var emptyStreamFileCount = 0;
foreach (var entry in archive.Entries)
{
if (!entry.IsDirectory)
{
// Verify this is actually an empty-stream entry (HasStream == false)
var sevenZipEntry = entry as SevenZipEntry;
if (sevenZipEntry?.FilePart.Header.HasStream == false)
{
emptyStreamFileCount++;
}
// This should not throw NullReferenceException
entry.WriteToDirectory(SCRATCH_FILES_PATH);
}
}
// Ensure we actually tested empty-stream entries
Assert.True(
emptyStreamFileCount > 0,
"Test archive should contain at least one empty-stream entry"
);
// Verify that empty files were created
var extractedFiles = Directory.GetFiles(
SCRATCH_FILES_PATH,
"*",
SearchOption.AllDirectories
);
Assert.NotEmpty(extractedFiles);
// All extracted files should be empty (0 bytes)
foreach (var file in extractedFiles)
{
var fileInfo = new FileInfo(file);
Assert.Equal(0, fileInfo.Length);
}
}
2022-04-21 08:59:35 +01:00
}