2026-05-29 08:02:33 +00:00
|
|
|
using System;
|
2026-01-06 14:13:48 +00:00
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using SharpCompress.Archives;
|
2026-02-11 12:14:05 +00:00
|
|
|
using SharpCompress.Archives.SevenZip;
|
2026-05-29 08:02:33 +00:00
|
|
|
using SharpCompress.Common;
|
2026-02-11 12:14:05 +00:00
|
|
|
using SharpCompress.Readers;
|
2026-01-15 14:52:10 +00:00
|
|
|
using SharpCompress.Test.Mocks;
|
2026-01-06 14:13:48 +00:00
|
|
|
using Xunit;
|
|
|
|
|
|
|
|
|
|
namespace SharpCompress.Test.SevenZip;
|
|
|
|
|
|
|
|
|
|
public class SevenZipArchiveAsyncTests : ArchiveTests
|
|
|
|
|
{
|
2026-01-15 16:41:58 +00:00
|
|
|
[Fact]
|
2026-01-15 14:52:10 +00:00
|
|
|
public async Task SevenZipArchive_LZMA_AsyncStreamExtraction()
|
2026-01-06 14:13:48 +00:00
|
|
|
{
|
|
|
|
|
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.LZMA.7z");
|
2026-01-16 08:43:13 +00:00
|
|
|
#if NETFRAMEWORK
|
2026-01-06 14:13:48 +00:00
|
|
|
using var stream = File.OpenRead(testArchive);
|
2026-01-16 08:43:13 +00:00
|
|
|
#else
|
|
|
|
|
await using var stream = File.OpenRead(testArchive);
|
|
|
|
|
#endif
|
2026-01-15 14:52:10 +00:00
|
|
|
await using var archive = await ArchiveFactory.OpenAsyncArchive(
|
|
|
|
|
new AsyncOnlyStream(stream)
|
|
|
|
|
);
|
2026-01-06 14:13:48 +00:00
|
|
|
|
2026-01-15 14:52:10 +00:00
|
|
|
await foreach (var entry in archive.EntriesAsync.Where(entry => !entry.IsDirectory))
|
2026-01-06 14:13:48 +00:00
|
|
|
{
|
|
|
|
|
var targetPath = Path.Combine(SCRATCH_FILES_PATH, entry.Key!);
|
|
|
|
|
var targetDir = Path.GetDirectoryName(targetPath);
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(targetDir) && !Directory.Exists(targetDir))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(targetDir);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 08:43:13 +00:00
|
|
|
#if NETFRAMEWORK
|
2026-01-06 14:13:48 +00:00
|
|
|
using var sourceStream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
2026-01-16 08:43:13 +00:00
|
|
|
#else
|
|
|
|
|
await using var sourceStream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
|
|
|
|
#endif
|
|
|
|
|
#if NETFRAMEWORK
|
|
|
|
|
using var targetStream = File.Create(targetPath);
|
|
|
|
|
#else
|
2026-01-15 11:55:56 +00:00
|
|
|
await using var targetStream = File.Create(targetPath);
|
2026-01-16 08:43:13 +00:00
|
|
|
#endif
|
|
|
|
|
#if NETFRAMEWORK
|
|
|
|
|
await sourceStream.CopyToAsync(targetStream, 81920, CancellationToken.None);
|
|
|
|
|
#else
|
2026-01-06 14:13:48 +00:00
|
|
|
await sourceStream.CopyToAsync(targetStream, CancellationToken.None);
|
2026-01-16 08:43:13 +00:00
|
|
|
#endif
|
2026-01-06 14:13:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VerifyFiles();
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-15 15:09:23 +00:00
|
|
|
//[Fact]
|
2026-01-15 14:52:10 +00:00
|
|
|
public async Task SevenZipArchive_LZMA2_AsyncStreamExtraction()
|
2026-01-06 14:13:48 +00:00
|
|
|
{
|
|
|
|
|
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.LZMA2.7z");
|
2026-01-16 08:43:13 +00:00
|
|
|
#if NETFRAMEWORK
|
2026-01-06 14:13:48 +00:00
|
|
|
using var stream = File.OpenRead(testArchive);
|
2026-01-16 08:43:13 +00:00
|
|
|
#else
|
|
|
|
|
await using var stream = File.OpenRead(testArchive);
|
|
|
|
|
#endif
|
2026-01-15 14:52:10 +00:00
|
|
|
await using var archive = await ArchiveFactory.OpenAsyncArchive(
|
|
|
|
|
new AsyncOnlyStream(stream)
|
|
|
|
|
);
|
2026-01-06 14:13:48 +00:00
|
|
|
|
2026-01-15 14:52:10 +00:00
|
|
|
await foreach (var entry in archive.EntriesAsync.Where(entry => !entry.IsDirectory))
|
2026-01-06 14:13:48 +00:00
|
|
|
{
|
|
|
|
|
var targetPath = Path.Combine(SCRATCH_FILES_PATH, entry.Key!);
|
|
|
|
|
var targetDir = Path.GetDirectoryName(targetPath);
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(targetDir) && !Directory.Exists(targetDir))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(targetDir);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 08:43:13 +00:00
|
|
|
#if NETFRAMEWORK
|
2026-01-06 14:13:48 +00:00
|
|
|
using var sourceStream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
2026-01-16 08:43:13 +00:00
|
|
|
#else
|
|
|
|
|
await using var sourceStream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
|
|
|
|
#endif
|
|
|
|
|
#if NETFRAMEWORK
|
|
|
|
|
using var targetStream = File.Create(targetPath);
|
|
|
|
|
#else
|
2026-01-15 11:55:56 +00:00
|
|
|
await using var targetStream = File.Create(targetPath);
|
2026-01-16 08:43:13 +00:00
|
|
|
#endif
|
|
|
|
|
#if NETFRAMEWORK
|
|
|
|
|
await sourceStream.CopyToAsync(targetStream, 81920, CancellationToken.None);
|
|
|
|
|
#else
|
2026-01-06 14:13:48 +00:00
|
|
|
await sourceStream.CopyToAsync(targetStream, CancellationToken.None);
|
2026-01-16 08:43:13 +00:00
|
|
|
#endif
|
2026-01-06 14:13:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VerifyFiles();
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 08:35:49 +00:00
|
|
|
[Fact]
|
2026-01-15 14:52:10 +00:00
|
|
|
public async Task SevenZipArchive_Solid_AsyncStreamExtraction()
|
2026-01-06 14:13:48 +00:00
|
|
|
{
|
|
|
|
|
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.7z");
|
2026-01-16 08:43:13 +00:00
|
|
|
#if NETFRAMEWORK
|
2026-01-06 14:13:48 +00:00
|
|
|
using var stream = File.OpenRead(testArchive);
|
2026-01-16 08:43:13 +00:00
|
|
|
#else
|
|
|
|
|
await using var stream = File.OpenRead(testArchive);
|
|
|
|
|
#endif
|
2026-01-15 14:52:10 +00:00
|
|
|
await using var archive = await ArchiveFactory.OpenAsyncArchive(
|
|
|
|
|
new AsyncOnlyStream(stream)
|
|
|
|
|
);
|
2026-01-06 14:13:48 +00:00
|
|
|
|
2026-01-15 14:52:10 +00:00
|
|
|
await foreach (var entry in archive.EntriesAsync.Where(entry => !entry.IsDirectory))
|
2026-01-06 14:13:48 +00:00
|
|
|
{
|
|
|
|
|
var targetPath = Path.Combine(SCRATCH_FILES_PATH, entry.Key!);
|
|
|
|
|
var targetDir = Path.GetDirectoryName(targetPath);
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(targetDir) && !Directory.Exists(targetDir))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(targetDir);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 08:43:13 +00:00
|
|
|
#if NETFRAMEWORK
|
2026-01-06 14:13:48 +00:00
|
|
|
using var sourceStream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
2026-01-16 08:43:13 +00:00
|
|
|
#else
|
|
|
|
|
await using var sourceStream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
|
|
|
|
#endif
|
|
|
|
|
#if NETFRAMEWORK
|
|
|
|
|
using var targetStream = File.Create(targetPath);
|
|
|
|
|
#else
|
2026-01-15 11:55:56 +00:00
|
|
|
await using var targetStream = File.Create(targetPath);
|
2026-01-16 08:43:13 +00:00
|
|
|
#endif
|
|
|
|
|
#if NETFRAMEWORK
|
|
|
|
|
await sourceStream.CopyToAsync(targetStream, 81920, CancellationToken.None);
|
|
|
|
|
#else
|
2026-01-06 14:13:48 +00:00
|
|
|
await sourceStream.CopyToAsync(targetStream, CancellationToken.None);
|
2026-01-16 08:43:13 +00:00
|
|
|
#endif
|
2026-01-06 14:13:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VerifyFiles();
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-29 08:02:33 +00:00
|
|
|
[Fact]
|
|
|
|
|
public async Task SevenZipArchive_Solid_WriteToDirectoryAsync_WithProgress()
|
|
|
|
|
{
|
|
|
|
|
var progressReports = new System.Collections.Generic.List<ProgressReport>();
|
2026-05-29 09:25:52 +01:00
|
|
|
var progress = new SynchronousProgress<ProgressReport>(report =>
|
|
|
|
|
progressReports.Add(report)
|
|
|
|
|
);
|
2026-05-29 08:02:33 +00:00
|
|
|
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.7z");
|
|
|
|
|
#if NETFRAMEWORK
|
|
|
|
|
using var stream = File.OpenRead(testArchive);
|
|
|
|
|
#else
|
|
|
|
|
await using var stream = File.OpenRead(testArchive);
|
|
|
|
|
#endif
|
2026-05-29 09:25:52 +01:00
|
|
|
await using var archive = await ArchiveFactory.OpenAsyncArchive(
|
|
|
|
|
new AsyncOnlyStream(stream)
|
|
|
|
|
);
|
2026-05-29 08:02:33 +00:00
|
|
|
|
|
|
|
|
await archive.WriteToDirectoryAsync(SCRATCH_FILES_PATH, progress: progress);
|
|
|
|
|
|
|
|
|
|
VerifyFiles();
|
|
|
|
|
Assert.True(progressReports.Count > 0, "Progress reports should be generated");
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 08:35:49 +00:00
|
|
|
[Fact]
|
2026-01-15 14:52:10 +00:00
|
|
|
public async Task SevenZipArchive_BZip2_AsyncStreamExtraction()
|
2026-01-06 14:13:48 +00:00
|
|
|
{
|
|
|
|
|
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.BZip2.7z");
|
2026-01-16 08:43:13 +00:00
|
|
|
#if NETFRAMEWORK
|
2026-01-06 14:13:48 +00:00
|
|
|
using var stream = File.OpenRead(testArchive);
|
2026-01-16 08:43:13 +00:00
|
|
|
#else
|
|
|
|
|
await using var stream = File.OpenRead(testArchive);
|
|
|
|
|
#endif
|
2026-01-15 14:52:10 +00:00
|
|
|
await using var archive = await ArchiveFactory.OpenAsyncArchive(
|
|
|
|
|
new AsyncOnlyStream(stream)
|
|
|
|
|
);
|
2026-01-06 14:13:48 +00:00
|
|
|
|
2026-01-15 14:52:10 +00:00
|
|
|
await foreach (var entry in archive.EntriesAsync.Where(entry => !entry.IsDirectory))
|
2026-01-06 14:13:48 +00:00
|
|
|
{
|
|
|
|
|
var targetPath = Path.Combine(SCRATCH_FILES_PATH, entry.Key!);
|
|
|
|
|
var targetDir = Path.GetDirectoryName(targetPath);
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(targetDir) && !Directory.Exists(targetDir))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(targetDir);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 08:43:13 +00:00
|
|
|
#if NETFRAMEWORK
|
2026-01-06 14:13:48 +00:00
|
|
|
using var sourceStream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
2026-01-16 08:43:13 +00:00
|
|
|
#else
|
|
|
|
|
await using var sourceStream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
|
|
|
|
#endif
|
|
|
|
|
#if NETFRAMEWORK
|
|
|
|
|
using var targetStream = File.Create(targetPath);
|
|
|
|
|
#else
|
2026-01-15 11:55:56 +00:00
|
|
|
await using var targetStream = File.Create(targetPath);
|
2026-01-16 08:43:13 +00:00
|
|
|
#endif
|
|
|
|
|
#if NETFRAMEWORK
|
|
|
|
|
await sourceStream.CopyToAsync(targetStream, 81920, CancellationToken.None);
|
|
|
|
|
#else
|
2026-01-06 14:13:48 +00:00
|
|
|
await sourceStream.CopyToAsync(targetStream, CancellationToken.None);
|
2026-01-16 08:43:13 +00:00
|
|
|
#endif
|
2026-01-06 14:13:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VerifyFiles();
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 08:35:49 +00:00
|
|
|
[Fact]
|
2026-01-15 14:52:10 +00:00
|
|
|
public async Task SevenZipArchive_PPMd_AsyncStreamExtraction()
|
2026-01-06 14:13:48 +00:00
|
|
|
{
|
|
|
|
|
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.PPMd.7z");
|
2026-01-16 08:43:13 +00:00
|
|
|
#if NETFRAMEWORK
|
2026-01-06 14:13:48 +00:00
|
|
|
using var stream = File.OpenRead(testArchive);
|
2026-01-16 08:43:13 +00:00
|
|
|
#else
|
|
|
|
|
await using var stream = File.OpenRead(testArchive);
|
|
|
|
|
#endif
|
2026-01-15 14:52:10 +00:00
|
|
|
await using var archive = await ArchiveFactory.OpenAsyncArchive(
|
|
|
|
|
new AsyncOnlyStream(stream)
|
|
|
|
|
);
|
2026-01-06 14:13:48 +00:00
|
|
|
|
2026-01-15 14:52:10 +00:00
|
|
|
await foreach (var entry in archive.EntriesAsync.Where(entry => !entry.IsDirectory))
|
2026-01-06 14:13:48 +00:00
|
|
|
{
|
|
|
|
|
var targetPath = Path.Combine(SCRATCH_FILES_PATH, entry.Key!);
|
|
|
|
|
var targetDir = Path.GetDirectoryName(targetPath);
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(targetDir) && !Directory.Exists(targetDir))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(targetDir);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 08:43:13 +00:00
|
|
|
#if NETFRAMEWORK
|
2026-01-06 14:13:48 +00:00
|
|
|
using var sourceStream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
2026-01-16 08:43:13 +00:00
|
|
|
#else
|
|
|
|
|
await using var sourceStream = await entry.OpenEntryStreamAsync(CancellationToken.None);
|
|
|
|
|
#endif
|
|
|
|
|
#if NETFRAMEWORK
|
|
|
|
|
using var targetStream = File.Create(targetPath);
|
|
|
|
|
#else
|
2026-01-15 11:55:56 +00:00
|
|
|
await using var targetStream = File.Create(targetPath);
|
2026-01-16 08:43:13 +00:00
|
|
|
#endif
|
|
|
|
|
#if NETFRAMEWORK
|
|
|
|
|
await sourceStream.CopyToAsync(targetStream, 81920, CancellationToken.None);
|
|
|
|
|
#else
|
2026-01-06 14:13:48 +00:00
|
|
|
await sourceStream.CopyToAsync(targetStream, CancellationToken.None);
|
2026-01-16 08:43:13 +00:00
|
|
|
#endif
|
2026-01-06 14:13:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VerifyFiles();
|
|
|
|
|
}
|
2026-02-11 12:14:05 +00:00
|
|
|
|
2026-04-13 16:59:23 +01:00
|
|
|
[Fact]
|
|
|
|
|
public async Task SevenZipArchive_TestSolidDetectionAsync()
|
|
|
|
|
{
|
|
|
|
|
await using var oneBlockSolidArchive = await SevenZipArchive.OpenAsyncArchive(
|
|
|
|
|
Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.1block.7z")
|
|
|
|
|
);
|
|
|
|
|
Assert.True(await oneBlockSolidArchive.IsSolidAsync());
|
|
|
|
|
|
|
|
|
|
await using var solidArchive = await SevenZipArchive.OpenAsyncArchive(
|
|
|
|
|
Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.7z")
|
|
|
|
|
);
|
|
|
|
|
Assert.True(await solidArchive.IsSolidAsync());
|
|
|
|
|
|
|
|
|
|
await using var nonSolidArchive = await SevenZipArchive.OpenAsyncArchive(
|
|
|
|
|
Path.Combine(TEST_ARCHIVES_PATH, "7Zip.nonsolid.7z")
|
|
|
|
|
);
|
|
|
|
|
Assert.False(await nonSolidArchive.IsSolidAsync());
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-11 12:14:05 +00:00
|
|
|
[Fact]
|
|
|
|
|
public async Task SevenZipArchive_Solid_ExtractAllEntries_Contiguous_Async()
|
|
|
|
|
{
|
|
|
|
|
// 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-02-12 10:18:43 +00:00
|
|
|
await using var archive = await SevenZipArchive.OpenAsyncArchive(testArchive);
|
2026-04-13 16:59:23 +01:00
|
|
|
Assert.True(await archive.IsSolidAsync());
|
2026-02-11 12:14:05 +00:00
|
|
|
|
|
|
|
|
await using var reader = await archive.ExtractAllEntriesAsync();
|
|
|
|
|
while (await reader.MoveToNextEntryAsync())
|
|
|
|
|
{
|
|
|
|
|
if (!reader.Entry.IsDirectory)
|
|
|
|
|
{
|
|
|
|
|
await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VerifyFiles();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task 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-02-12 10:18:43 +00:00
|
|
|
await using var archive = await SevenZipArchive.OpenAsyncArchive(testArchive);
|
2026-04-13 16:59:23 +01:00
|
|
|
Assert.True(await archive.IsSolidAsync());
|
2026-02-11 12:14:05 +00:00
|
|
|
|
|
|
|
|
await using var reader = await archive.ExtractAllEntriesAsync();
|
|
|
|
|
|
|
|
|
|
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 (await reader.MoveToNextEntryAsync())
|
|
|
|
|
{
|
|
|
|
|
if (!reader.Entry.IsDirectory)
|
|
|
|
|
{
|
|
|
|
|
// Extract the entry to trigger GetEntryStream
|
|
|
|
|
using var entryStream = await reader.OpenEntryStreamAsync();
|
|
|
|
|
var buffer = new byte[4096];
|
|
|
|
|
while (entryStream.Read(buffer, 0, buffer.Length) > 0)
|
|
|
|
|
{
|
|
|
|
|
// Read the stream to completion
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entryCount++;
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
Assert.Equal(0, streamRecreationsWithinFolder); // Folder stream should remain the same for all entries in the same folder
|
|
|
|
|
}
|
2026-05-29 08:13:41 +00:00
|
|
|
|
|
|
|
|
private sealed class SynchronousProgress<T> : IProgress<T>
|
|
|
|
|
{
|
|
|
|
|
private readonly Action<T> _handler;
|
|
|
|
|
|
|
|
|
|
public SynchronousProgress(Action<T> handler) => _handler = handler;
|
|
|
|
|
|
|
|
|
|
public void Report(T value) => _handler(value);
|
|
|
|
|
}
|
2026-01-06 14:13:48 +00:00
|
|
|
}
|