Merge pull request #1297 from adamhathcock/adam/enforce-seekable

Enforce seekable, readable and writable on streams
This commit is contained in:
Adam Hathcock
2026-04-23 11:50:24 +01:00
committed by GitHub
36 changed files with 350 additions and 105 deletions

1
.gitignore vendored
View File

@@ -24,3 +24,4 @@ profiler-snapshots/
.DS_Store
*.snupkg
benchmark-results/
/.opencode

View File

@@ -92,13 +92,7 @@ public static partial class ArchiveFactory
)
{
cancellationToken.ThrowIfCancellationRequested();
streams.NotNull(nameof(streams));
var streamsArray = streams;
if (streamsArray.Count == 0)
{
throw new ArchiveOperationException("No streams");
}
var streamsArray = streams.RequireReadable().RequireSeekable().ToList();
var firstStream = streamsArray[0];
if (streamsArray.Count == 1)
{
@@ -143,11 +137,8 @@ public static partial class ArchiveFactory
)
where T : IFactory
{
stream.NotNull(nameof(stream));
if (!stream.CanRead || !stream.CanSeek)
{
throw new ArgumentException("Stream should be readable and seekable");
}
stream.RequireReadable();
stream.RequireSeekable();
var factories = Factory.Factories.OfType<T>();

View File

@@ -73,8 +73,7 @@ public static partial class ArchiveFactory
public static IArchive OpenArchive(IReadOnlyList<Stream> streams, ReaderOptions? options = null)
{
streams.NotNull(nameof(streams));
var streamsArray = streams;
var streamsArray = streams.RequireReadable().RequireSeekable().ToList();
if (streamsArray.Count == 0)
{
throw new ArchiveOperationException("No streams");
@@ -121,11 +120,8 @@ public static partial class ArchiveFactory
public static T FindFactory<T>(Stream stream)
where T : IFactory
{
stream.NotNull(nameof(stream));
if (!stream.CanRead || !stream.CanSeek)
{
throw new ArgumentException("Stream should be readable and seekable");
}
stream.RequireReadable();
stream.RequireSeekable();
var factories = Factory.Factories.OfType<T>();
@@ -160,12 +156,8 @@ public static partial class ArchiveFactory
public static bool IsArchive(Stream stream, out ArchiveType? type)
{
type = null;
stream.NotNull(nameof(stream));
if (!stream.CanRead || !stream.CanSeek)
{
throw new ArgumentException("Stream should be readable and seekable");
}
stream.RequireReadable();
stream.RequireSeekable();
var startPosition = stream.Position;
@@ -199,12 +191,8 @@ public static partial class ArchiveFactory
CancellationToken cancellationToken = default
)
{
stream.NotNull(nameof(stream));
if (!stream.CanRead || !stream.CanSeek)
{
throw new ArgumentException("Stream should be readable and seekable");
}
stream.RequireReadable();
stream.RequireSeekable();
var startPosition = stream.Position;

View File

@@ -76,8 +76,7 @@ public partial class GZipArchive
ReaderOptions? readerOptions = null
)
{
streams.NotNull(nameof(streams));
var strms = streams;
var strms = streams.RequireReadable().RequireSeekable().ToList();
return new GZipArchive(
new SourceStream(
strms[0],
@@ -92,12 +91,8 @@ public partial class GZipArchive
ReaderOptions? readerOptions = null
)
{
stream.NotNull(nameof(stream));
if (stream is not { CanSeek: true })
{
throw new ArgumentException("Stream must be seekable", nameof(stream));
}
stream.RequireReadable();
stream.RequireSeekable();
return new GZipArchive(
new SourceStream(stream, _ => null, readerOptions ?? ReaderOptions.ForExternalStream)

View File

@@ -58,12 +58,8 @@ public partial class RarArchive
public static IRarArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
{
stream.NotNull(nameof(stream));
if (stream is not { CanSeek: true })
{
throw new ArgumentException("Stream must be seekable", nameof(stream));
}
stream.RequireReadable();
stream.RequireSeekable();
return new RarArchive(
new SourceStream(stream, _ => null, readerOptions ?? ReaderOptions.ForExternalStream)
@@ -91,8 +87,7 @@ public partial class RarArchive
ReaderOptions? readerOptions = null
)
{
streams.NotNull(nameof(streams));
var strms = streams;
var strms = streams.RequireReadable().RequireSeekable().ToList();
return new RarArchive(
new SourceStream(
strms[0],

View File

@@ -71,8 +71,7 @@ public partial class SevenZipArchive
ReaderOptions? readerOptions = null
)
{
streams.NotNull(nameof(streams));
var strms = streams;
var strms = streams.RequireReadable().RequireSeekable().ToList();
return new SevenZipArchive(
new SourceStream(
strms[0],
@@ -84,12 +83,8 @@ public partial class SevenZipArchive
public static IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
{
stream.NotNull(nameof(stream));
if (stream is not { CanSeek: true })
{
throw new ArgumentException("Stream must be seekable", nameof(stream));
}
stream.RequireReadable();
stream.RequireSeekable();
return new SevenZipArchive(
new SourceStream(stream, _ => null, readerOptions ?? ReaderOptions.ForExternalStream)

View File

@@ -66,8 +66,7 @@ public partial class TarArchive
ReaderOptions? readerOptions = null
)
{
streams.NotNull(nameof(streams));
var strms = streams;
var strms = streams.RequireReadable().RequireSeekable().ToList();
var sourceStream = new SourceStream(
strms[0],
i => i < strms.Count ? strms[i] : null,
@@ -86,12 +85,8 @@ public partial class TarArchive
ReaderOptions? readerOptions = null
)
{
stream.NotNull(nameof(stream));
if (stream is not { CanSeek: true })
{
throw new ArgumentException("Stream must be seekable", nameof(stream));
}
stream.RequireReadable();
stream.RequireSeekable();
return OpenArchive([stream], readerOptions);
}
@@ -102,7 +97,8 @@ public partial class TarArchive
CancellationToken cancellationToken = default
)
{
stream.NotNull(nameof(stream));
stream.RequireReadable();
stream.RequireSeekable();
var sourceStream = new SourceStream(
stream,
i => null,
@@ -158,8 +154,7 @@ public partial class TarArchive
)
{
cancellationToken.ThrowIfCancellationRequested();
streams.NotNull(nameof(streams));
var strms = streams;
var strms = streams.RequireReadable().RequireSeekable().ToList();
var sourceStream = new SourceStream(
strms[0],
i => i < strms.Count ? strms[i] : null,

View File

@@ -67,8 +67,7 @@ public partial class ZipArchive
ReaderOptions? readerOptions = null
)
{
streams.NotNull(nameof(streams));
var strms = streams;
var strms = streams.RequireReadable().RequireSeekable().ToList();
return new ZipArchive(
new SourceStream(
strms[0],
@@ -83,12 +82,8 @@ public partial class ZipArchive
ReaderOptions? readerOptions = null
)
{
stream.NotNull(nameof(stream));
if (stream is not { CanSeek: true })
{
throw new ArgumentException("Stream must be seekable", nameof(stream));
}
stream.RequireReadable();
stream.RequireSeekable();
return new ZipArchive(
new SourceStream(stream, i => null, readerOptions ?? ReaderOptions.ForExternalStream)

View File

@@ -19,7 +19,7 @@ public partial class AceReader
/// <returns>An AceReader instance.</returns>
public static IReader OpenReader(Stream stream, ReaderOptions? readerOptions = null)
{
stream.NotNull(nameof(stream));
stream.RequireReadable();
return new SingleVolumeAceReader(stream, readerOptions ?? ReaderOptions.ForExternalStream);
}
@@ -31,8 +31,8 @@ public partial class AceReader
/// <returns></returns>
public static IReader OpenReader(IEnumerable<Stream> streams, ReaderOptions? options = null)
{
streams.NotNull(nameof(streams));
return new MultiVolumeAceReader(streams, options ?? ReaderOptions.ForExternalStream);
var streamArray = streams.RequireReadable();
return new MultiVolumeAceReader(streamArray, options ?? ReaderOptions.ForExternalStream);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
@@ -61,8 +61,8 @@ public partial class AceReader
ReaderOptions? options = null
)
{
streams.NotNull(nameof(streams));
return new MultiVolumeAceReader(streams, options ?? ReaderOptions.ForExternalStream);
var streamArray = streams.RequireReadable();
return new MultiVolumeAceReader(streamArray, options ?? ReaderOptions.ForExternalStream);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(

View File

@@ -12,7 +12,7 @@ internal class SingleVolumeAceReader : AceReader
internal SingleVolumeAceReader(Stream stream, ReaderOptions options)
: base(options)
{
stream.NotNull(nameof(stream));
stream.RequireReadable();
_stream = stream;
}

View File

@@ -24,7 +24,7 @@ public partial class ArcReader : AbstractReader<ArcEntry, ArcVolume>
/// <returns></returns>
public static IReader OpenReader(Stream stream, ReaderOptions? readerOptions = null)
{
stream.NotNull(nameof(stream));
stream.RequireReadable();
return new ArcReader(stream, readerOptions ?? ReaderOptions.ForExternalStream);
}

View File

@@ -31,7 +31,7 @@ public abstract partial class ArjReader : AbstractReader<ArjEntry, ArjVolume>
/// <returns></returns>
public static IReader OpenReader(Stream stream, ReaderOptions? readerOptions = null)
{
stream.NotNull(nameof(stream));
stream.RequireReadable();
return new SingleVolumeArjReader(stream, readerOptions ?? ReaderOptions.ForExternalStream);
}
@@ -43,8 +43,8 @@ public abstract partial class ArjReader : AbstractReader<ArjEntry, ArjVolume>
/// <returns></returns>
public static IReader OpenReader(IEnumerable<Stream> streams, ReaderOptions? options = null)
{
streams.NotNull(nameof(streams));
return new MultiVolumeArjReader(streams, options ?? ReaderOptions.ForExternalStream);
var streamArray = streams.RequireReadable();
return new MultiVolumeArjReader(streamArray, options ?? ReaderOptions.ForExternalStream);
}
protected abstract void ValidateArchive(ArjVolume archive);

View File

@@ -12,7 +12,7 @@ internal class SingleVolumeArjReader : ArjReader
internal SingleVolumeArjReader(Stream stream, ReaderOptions options)
: base(options)
{
stream.NotNull(nameof(stream));
stream.RequireReadable();
_stream = stream;
}

View File

@@ -55,7 +55,7 @@ public partial class GZipReader
public static IReader OpenReader(Stream stream, ReaderOptions? readerOptions = null)
{
stream.NotNull(nameof(stream));
stream.RequireReadable();
return new GZipReader(stream, readerOptions ?? ReaderOptions.ForExternalStream);
}
}

View File

@@ -55,7 +55,7 @@ public partial class LzwReader
public static IReader OpenReader(Stream stream, ReaderOptions? readerOptions = null)
{
stream.NotNull(nameof(stream));
stream.RequireReadable();
return new LzwReader(stream, readerOptions ?? ReaderOptions.ForExternalStream);
}
}

View File

@@ -71,7 +71,7 @@ public abstract partial class RarReader : AbstractReader<RarReaderEntry, RarVolu
/// <returns></returns>
public static IReader OpenReader(Stream stream, ReaderOptions? readerOptions = null)
{
stream.NotNull(nameof(stream));
stream.RequireReadable();
return new SingleVolumeRarReader(stream, readerOptions ?? ReaderOptions.ForExternalStream);
}
@@ -83,8 +83,8 @@ public abstract partial class RarReader : AbstractReader<RarReaderEntry, RarVolu
/// <returns></returns>
public static IReader OpenReader(IEnumerable<Stream> streams, ReaderOptions? options = null)
{
streams.NotNull(nameof(streams));
return new MultiVolumeRarReader(streams, options ?? ReaderOptions.ForExternalStream);
var streamArray = streams.RequireReadable();
return new MultiVolumeRarReader(streamArray, options ?? ReaderOptions.ForExternalStream);
}
protected override IEnumerable<RarReaderEntry> GetEntries(Stream stream)

View File

@@ -56,7 +56,7 @@ public static partial class ReaderFactory
CancellationToken cancellationToken = default
)
{
stream.NotNull(nameof(stream));
stream.RequireReadable();
options ??= ReaderOptions.ForExternalStream;
var sharpCompressStream = SharpCompressStream.Create(

View File

@@ -29,7 +29,7 @@ public static partial class ReaderFactory
/// <returns></returns>
public static IReader OpenReader(Stream stream, ReaderOptions? options = null)
{
stream.NotNull(nameof(stream));
stream.RequireReadable();
options ??= ReaderOptions.ForExternalStream;
var sharpCompressStream = SharpCompressStream.Create(

View File

@@ -170,7 +170,7 @@ public partial class TarReader
/// <returns></returns>
public static IReader OpenReader(Stream stream, ReaderOptions? readerOptions = null)
{
stream.NotNull(nameof(stream));
stream.RequireReadable();
readerOptions ??= ReaderOptions.ForExternalStream;
var sharpCompressStream = SharpCompressStream.Create(
stream,

View File

@@ -47,7 +47,7 @@ public partial class ZipReader : AbstractReader<ZipEntry, ZipVolume>
/// <returns></returns>
public static IReader OpenReader(Stream stream, ReaderOptions? readerOptions = null)
{
stream.NotNull(nameof(stream));
stream.RequireReadable();
return new ZipReader(stream, readerOptions ?? ReaderOptions.ForExternalStream);
}
@@ -57,7 +57,7 @@ public partial class ZipReader : AbstractReader<ZipEntry, ZipVolume>
IEnumerable<ZipEntry> entries
)
{
stream.NotNull(nameof(stream));
stream.RequireReadable();
return new ZipReader(stream, options ?? ReaderOptions.ForExternalStream, entries);
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace SharpCompress;
internal static class StreamValidationExtensions
{
internal static void RequireReadable(this Stream stream)
{
stream.NotNull(nameof(stream));
if (!stream.CanRead)
{
throw new ArgumentException("Stream must be readable", nameof(stream));
}
}
internal static void RequireSeekable(this Stream stream)
{
stream.NotNull(nameof(stream));
if (!stream.CanSeek)
{
throw new ArgumentException("Stream must be seekable", nameof(stream));
}
}
internal static void RequireWritable(this Stream stream)
{
stream.NotNull(nameof(stream));
if (!stream.CanWrite)
{
throw new ArgumentException("Stream must be writable", nameof(stream));
}
}
internal static IEnumerable<Stream> RequireSeekable(this IEnumerable<Stream> streams)
{
foreach (var stream in streams)
{
stream.RequireSeekable();
yield return stream;
}
}
internal static IEnumerable<Stream> RequireReadable(this IEnumerable<Stream> streams)
{
foreach (var stream in streams)
{
stream.RequireReadable();
yield return stream;
}
}
}

View File

@@ -22,7 +22,7 @@ public partial class GZipWriter : IWriterOpenable<GZipWriterOptions>
public static IWriter OpenWriter(Stream stream, GZipWriterOptions writerOptions)
{
stream.NotNull(nameof(stream));
stream.RequireWritable();
return new GZipWriter(stream, writerOptions);
}

View File

@@ -36,7 +36,7 @@ public partial class SevenZipWriter : IWriterOpenable<SevenZipWriterOptions>
/// </summary>
public static IWriter OpenWriter(Stream stream, SevenZipWriterOptions writerOptions)
{
stream.NotNull(nameof(stream));
stream.RequireWritable();
return new SevenZipWriter(stream, writerOptions);
}

View File

@@ -22,7 +22,7 @@ public partial class TarWriter : IWriterOpenable<TarWriterOptions>
public static IWriter OpenWriter(Stream stream, TarWriterOptions writerOptions)
{
stream.NotNull(nameof(stream));
stream.RequireWritable();
return new TarWriter(stream, writerOptions);
}

View File

@@ -75,6 +75,8 @@ public static class WriterFactory
IWriterOptions writerOptions
)
{
stream.RequireWritable();
var factory = Factories
.Factory.Factories.OfType<IWriterFactory>()
.FirstOrDefault(item => item.KnownArchiveType == archiveType);
@@ -102,6 +104,8 @@ public static class WriterFactory
CancellationToken cancellationToken = default
)
{
stream.RequireWritable();
var factory = Factories
.Factory.Factories.OfType<IWriterFactory>()
.FirstOrDefault(item => item.KnownArchiveType == archiveType);

View File

@@ -22,7 +22,7 @@ public partial class ZipWriter : IWriterOpenable<ZipWriterOptions>
public static IWriter OpenWriter(Stream stream, ZipWriterOptions writerOptions)
{
stream.NotNull(nameof(stream));
stream.RequireWritable();
return new ZipWriter(stream, writerOptions);
}

View File

@@ -268,9 +268,9 @@
"net10.0": {
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[10.0.0, )",
"resolved": "10.0.0",
"contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA=="
"requested": "[10.0.6, )",
"resolved": "10.0.6",
"contentHash": "QKuvS0LWX4fjFqeDkyM7Kqt8P3wYTiPD4nwU+9y59n0sCiG714fxDgbbN82vDnzq89AF/PiHl92TP2C4aFDUQA=="
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
@@ -400,9 +400,9 @@
"net8.0": {
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[8.0.22, )",
"resolved": "8.0.22",
"contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ=="
"requested": "[8.0.26, )",
"resolved": "8.0.26",
"contentHash": "o7/yVssM2r9Wyln2s9edBd5ANZXqdSdBI+g7JqXkyJmXrhs2WsJp25K5yPnYrTgdKBCjKB8bg+O2oew4sgzFaA=="
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",

View File

@@ -1,9 +1,11 @@
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using SharpCompress.Archives;
using SharpCompress.Common;
using SharpCompress.Factories;
using SharpCompress.Test.Mocks;
using Xunit;
namespace SharpCompress.Test;
@@ -61,6 +63,26 @@ public class ArchiveFactoryTests : TestBase
Assert.Equal(startPosition, stream.Position);
}
[Fact]
public void OpenArchive_StreamCollection_Throws_On_NonSeekable_Stream()
{
using var nonSeekable = new ForwardOnlyStream(new MemoryStream());
using var seekable = new MemoryStream();
Assert.Throws<ArgumentException>(() => ArchiveFactory.OpenArchive([nonSeekable, seekable]));
}
[Fact]
public async ValueTask OpenAsyncArchive_StreamCollection_Throws_On_NonSeekable_Stream()
{
using var nonSeekable = new ForwardOnlyStream(new MemoryStream());
using var seekable = new MemoryStream();
await Assert.ThrowsAsync<ArgumentException>(() =>
ArchiveFactory.OpenAsyncArchive([nonSeekable, seekable]).AsTask()
);
}
[Fact]
public async ValueTask FindFactoryAsync_InvalidData_ThrowsArchiveOperationException()
{
@@ -71,6 +93,24 @@ public class ArchiveFactoryTests : TestBase
);
}
[Fact]
public void OpenArchive_Stream_Throws_On_Unreadable_Stream()
{
using var unreadable = new TestStream(new MemoryStream(), false, true, true);
Assert.Throws<ArgumentException>(() => ArchiveFactory.OpenArchive(unreadable));
}
[Fact]
public async ValueTask OpenAsyncArchive_Stream_Throws_On_Unreadable_Stream()
{
using var unreadable = new TestStream(new MemoryStream(), false, true, true);
await Assert.ThrowsAsync<ArgumentException>(() =>
ArchiveFactory.OpenAsyncArchive(unreadable).AsTask()
);
}
[Theory]
[InlineData("Zip.deflate.zip", ArchiveType.Zip)]
[InlineData("Tar.noEmptyDirs.tar", ArchiveType.Tar)]

View File

@@ -5,6 +5,7 @@ using SharpCompress.Archives;
using SharpCompress.Archives.GZip;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Test.Mocks;
using SharpCompress.Writers.GZip;
using Xunit;
@@ -127,6 +128,23 @@ public class GZipArchiveTests : ArchiveTests
Assert.Equal(archive.Type, ArchiveType.GZip);
}
[Fact]
public void GZipArchive_StreamCollection_Throws_On_NonSeekable_Stream()
{
using var nonSeekable = new ForwardOnlyStream(new MemoryStream());
using var seekable = new MemoryStream();
Assert.Throws<ArgumentException>(() => GZipArchive.OpenArchive([nonSeekable, seekable]));
}
[Fact]
public void GZipArchive_Stream_Throws_On_Unreadable_Stream()
{
using var unreadable = new TestStream(new MemoryStream(), false, true, true);
Assert.Throws<ArgumentException>(() => GZipArchive.OpenArchive(unreadable));
}
[Fact]
public void GZip_Archive_NonSeekableStream()
{

View File

@@ -122,6 +122,23 @@ public class RarArchiveTests : ArchiveTests
[Fact]
public void Rar_ArchiveStreamRead() => ArchiveStreamRead("Rar.rar");
[Fact]
public void RarArchive_StreamCollection_Throws_On_NonSeekable_Stream()
{
using var nonSeekable = new ForwardOnlyStream(new MemoryStream());
using var seekable = new MemoryStream();
Assert.Throws<ArgumentException>(() => RarArchive.OpenArchive([nonSeekable, seekable]));
}
[Fact]
public void RarArchive_Stream_Throws_On_Unreadable_Stream()
{
using var unreadable = new TestStream(new MemoryStream(), false, true, true);
Assert.Throws<ArgumentException>(() => RarArchive.OpenArchive(unreadable));
}
[Fact]
public void Rar5_ArchiveStreamRead() => ArchiveStreamRead("Rar5.rar");

View File

@@ -0,0 +1,41 @@
using System;
using System.IO;
using System.Threading.Tasks;
using SharpCompress.Readers;
using SharpCompress.Readers.Rar;
using SharpCompress.Test.Mocks;
using Xunit;
namespace SharpCompress.Test;
public class ReaderFactoryTests
{
[Fact]
public void OpenReader_Stream_Throws_On_Unreadable_Stream()
{
using var unreadable = new TestStream(new MemoryStream(), false, true, true);
Assert.Throws<ArgumentException>(() => ReaderFactory.OpenReader(unreadable));
}
[Fact]
public async ValueTask OpenAsyncReader_Stream_Throws_On_Unreadable_Stream()
{
using var unreadable = new TestStream(new MemoryStream(), false, true, true);
await Assert.ThrowsAsync<ArgumentException>(() =>
ReaderFactory.OpenAsyncReader(unreadable).AsTask()
);
}
[Fact]
public void RarReader_StreamCollection_Throws_On_Unreadable_Stream()
{
using var unreadable = new TestStream(new MemoryStream(), false, true, true);
using var readable = new MemoryStream();
Assert.Throws<ArgumentException>(() =>
RarReader.OpenReader([unreadable, readable]).MoveToNextEntry()
);
}
}

View File

@@ -7,6 +7,7 @@ using SharpCompress.Common;
using SharpCompress.Common.SevenZip;
using SharpCompress.Factories;
using SharpCompress.Readers;
using SharpCompress.Test.Mocks;
using Xunit;
namespace SharpCompress.Test.SevenZip;
@@ -25,6 +26,25 @@ public class SevenZipArchiveTests : ArchiveTests
[Fact]
public void SevenZipArchive_LZMA_PathRead() => ArchiveFileRead("7Zip.LZMA.7z");
[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])
);
}
[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));
}
[Fact]
public void SevenZipArchive_LZMAAES_StreamRead() =>
ArchiveStreamRead(

View File

@@ -22,6 +22,26 @@ public class TarArchiveAsyncTests : ArchiveTests
[Fact]
public async ValueTask TarArchiveStreamRead_Async() => await ArchiveStreamReadAsync("Tar.tar");
[Fact]
public async ValueTask TarArchiveOpenAsyncStream_Throws_On_NonSeekable_Stream()
{
using var stream = new ForwardOnlyStream(new MemoryStream());
await Assert.ThrowsAsync<ArgumentException>(() =>
TarArchive.OpenAsyncArchive(stream).AsTask()
);
}
[Fact]
public async ValueTask TarArchiveOpenAsyncStream_Throws_On_Unreadable_Stream()
{
using var stream = new TestStream(new MemoryStream(), false, true, true);
await Assert.ThrowsAsync<ArgumentException>(() =>
TarArchive.OpenAsyncArchive(stream).AsTask()
);
}
[Fact]
public async ValueTask Tar_FileName_Exactly_100_Characters_Async()
{

View File

@@ -34,6 +34,28 @@ public class TarArchiveTests : ArchiveTests
Assert.Throws<ArgumentException>(() => ArchiveFactory.OpenArchive(stream));
}
[Fact]
public void TarArchiveStreamRead_Throws_On_Unreadable_Stream()
{
using var unreadable = new TestStream(
File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")),
false,
true,
true
);
Assert.Throws<ArgumentException>(() => TarArchive.OpenArchive(unreadable));
}
[Fact]
public void TarArchive_StreamCollection_Throws_On_NonSeekable_Stream()
{
using var nonSeekable = new ForwardOnlyStream(new MemoryStream());
using var seekable = new MemoryStream();
Assert.Throws<ArgumentException>(() => TarArchive.OpenArchive([nonSeekable, seekable]));
}
[Fact]
public void Tar_FileName_Exactly_100_Characters()
{

View File

@@ -0,0 +1,34 @@
using System;
using System.IO;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Test.Mocks;
using SharpCompress.Writers;
using Xunit;
namespace SharpCompress.Test;
public class WriterFactoryTests
{
[Fact]
public void OpenWriter_Stream_Throws_On_Unwritable_Stream()
{
using var unwritable = new TestStream(new MemoryStream(), true, false, true);
Assert.Throws<ArgumentException>(() =>
WriterFactory.OpenWriter(unwritable, ArchiveType.Zip, WriterOptions.ForZip())
);
}
[Fact]
public async ValueTask OpenAsyncWriter_Stream_Throws_On_Unwritable_Stream()
{
using var unwritable = new TestStream(new MemoryStream(), true, false, true);
await Assert.ThrowsAsync<ArgumentException>(() =>
WriterFactory
.OpenAsyncWriter(unwritable, ArchiveType.Zip, WriterOptions.ForZip())
.AsTask()
);
}
}

View File

@@ -7,6 +7,7 @@ using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.Common.Zip;
using SharpCompress.Readers;
using SharpCompress.Test.Mocks;
using SharpCompress.Writers;
using SharpCompress.Writers.Zip;
using Xunit;
@@ -28,6 +29,23 @@ public class ZipArchiveTests : ArchiveTests
[Fact]
public void Zip_BZip2_ArchiveStreamRead() => ArchiveStreamRead("Zip.bzip2.zip");
[Fact]
public void ZipArchive_StreamCollection_Throws_On_NonSeekable_Stream()
{
using var nonSeekable = new NonSeekableMemoryStream();
using var seekable = new MemoryStream();
Assert.Throws<ArgumentException>(() => ZipArchive.OpenArchive([nonSeekable, seekable]));
}
[Fact]
public void ZipArchive_Stream_Throws_On_Unreadable_Stream()
{
using var unreadable = new TestStream(new MemoryStream(), false, true, true);
Assert.Throws<ArgumentException>(() => ZipArchive.OpenArchive(unreadable));
}
[Fact]
public void Zip_Deflate_Streamed2_ArchiveStreamRead() =>
ArchiveStreamRead("Zip.deflate.dd-.zip");