mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-11-15 17:02:03 +00:00
Make raw LZMA setup and disposal exception-safe
This commit is contained in:
@@ -16,6 +16,31 @@ namespace SharpCompress.Test.Streams;
|
||||
|
||||
public class BufferPoolLifetimeTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async Task OutWindow_FailedFlush_ReturnsBuffer(bool asyncDispose)
|
||||
{
|
||||
var pool = new TrackingPool<byte>();
|
||||
using var output = new ThrowingOutputStream();
|
||||
var window = new OutWindow(pool);
|
||||
window.Create(32);
|
||||
window.Init(output);
|
||||
window.SetLimit(1);
|
||||
window.PutByte(42);
|
||||
if (asyncDispose)
|
||||
{
|
||||
await Assert.ThrowsAsync<IOException>(async () => await window.DisposeAsync());
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Throws<IOException>(() => window.Dispose());
|
||||
}
|
||||
window.Dispose();
|
||||
Assert.Equal(1, pool.ReturnCount);
|
||||
Assert.Empty(pool.Active);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
|
||||
80
tests/SharpCompress.Test/Streams/LzmaLifetimeTests.cs
Normal file
80
tests/SharpCompress.Test/Streams/LzmaLifetimeTests.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.Streams;
|
||||
|
||||
public class LzmaLifetimeTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async Task FailedFinalizationReleasesEncoderBuffers(bool asyncDispose)
|
||||
{
|
||||
using var output = new ThrowingOutput();
|
||||
var stream = LzmaStream.Create(new LzmaEncoderProperties(), false, output);
|
||||
var encoder = GetField(stream, "_encoder")!;
|
||||
if (asyncDispose)
|
||||
{
|
||||
await Assert.ThrowsAsync<IOException>(async () => await stream.DisposeAsync());
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Throws<IOException>(() => stream.Dispose());
|
||||
}
|
||||
Assert.Null(GetField(encoder, "_matchFinder"));
|
||||
stream.Dispose();
|
||||
Assert.False(stream.CanWrite);
|
||||
Assert.Throws<ObjectDisposedException>(() => stream.Write(new byte[1], 0, 1));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(() =>
|
||||
stream.WriteAsync(new byte[1], 0, 1, CancellationToken.None)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DisposedLzma2ReaderCannotRentHeaderBuffer()
|
||||
{
|
||||
using var input = new MemoryStream(new byte[] { 0 });
|
||||
var stream = LzmaStream.Create(new byte[] { 0 }, input, leaveOpen: true);
|
||||
stream.Dispose();
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(() =>
|
||||
stream.ReadAsync(new byte[1], 0, 1, CancellationToken.None)
|
||||
);
|
||||
Assert.Null(GetField(stream, "_asyncHeaderBuffer"));
|
||||
Assert.False(stream.CanRead);
|
||||
Assert.True(input.CanRead);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidPropertiesLeaveInputOpen()
|
||||
{
|
||||
using var input = new MemoryStream();
|
||||
Assert.Throws<InvalidParamException>(() =>
|
||||
LzmaStream.Create(new byte[] { 225, 0, 0, 16, 0 }, input)
|
||||
);
|
||||
Assert.True(input.CanRead);
|
||||
}
|
||||
|
||||
private static object? GetField(object instance, string name) =>
|
||||
instance
|
||||
.GetType()
|
||||
.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic)!
|
||||
.GetValue(instance);
|
||||
|
||||
private sealed class ThrowingOutput : MemoryStream
|
||||
{
|
||||
public override void WriteByte(byte value) =>
|
||||
throw new IOException("Injected output failure");
|
||||
|
||||
public override Task WriteAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
CancellationToken cancellationToken
|
||||
) => Task.FromException(new IOException("Injected output failure"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user