Browse Source

Save async tests

pull/1196/head
Scott Williams 6 years ago
parent
commit
4d95e2e92a
  1. 56
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  2. 16
      src/ImageSharp/Advanced/IImageVisitor.cs
  3. 20
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  4. 15
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  5. 30
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  6. 30
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  7. 17
      src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
  8. 30
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  9. 15
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  10. 30
      src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
  11. 15
      src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
  12. 33
      src/ImageSharp/Image.cs
  13. 57
      src/ImageSharp/ImageExtensions.cs
  14. 9
      src/ImageSharp/Image{TPixel}.cs
  15. 109
      tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs
  16. 117
      tests/ImageSharp.Tests/TestUtilities/AsyncOnlyStream.cs

56
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

@ -2,9 +2,13 @@
// Licensed under the GNU Affero General Public License, Version 3.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -15,6 +19,47 @@ namespace SixLabors.ImageSharp.Advanced
/// </summary>
public static class AdvancedImageExtensions
{
/// <summary>
/// For a given path find the best encoder to use
/// </summary>
/// <param name="source">The source.</param>
/// <param name="path">The Path</param>
/// <returns>The matching encoder.</returns>
public static IImageEncoder FindEncoded(this Image source, string path)
{
Guard.NotNull(path, nameof(path));
string ext = Path.GetExtension(path);
IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext);
if (format is null)
{
var sb = new StringBuilder();
sb.AppendLine($"No encoder was found for extension '{ext}'. Registered encoders include:");
foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats)
{
sb.AppendFormat(" - {0} : {1}{2}", fmt.Name, string.Join(", ", fmt.FileExtensions), Environment.NewLine);
}
throw new NotSupportedException(sb.ToString());
}
IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format);
if (encoder is null)
{
var sb = new StringBuilder();
sb.AppendLine($"No encoder was found for extension '{ext}' using image format '{format.Name}'. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
sb.AppendFormat(" - {0} : {1}{2}", enc.Key, enc.Value.GetType().Name, Environment.NewLine);
}
throw new NotSupportedException(sb.ToString());
}
return encoder;
}
/// <summary>
/// Accepts a <see cref="IImageVisitor"/> to implement a double-dispatch pattern in order to
/// apply pixel-specific operations on non-generic <see cref="Image"/> instances
@ -24,6 +69,15 @@ namespace SixLabors.ImageSharp.Advanced
public static void AcceptVisitor(this Image source, IImageVisitor visitor)
=> source.Accept(visitor);
/// <summary>
/// Accepts a <see cref="IImageVisitor"/> to implement a double-dispatch pattern in order to
/// apply pixel-specific operations on non-generic <see cref="Image"/> instances
/// </summary>
/// <param name="source">The source.</param>
/// <param name="visitor">The visitor.</param>
public static Task AcceptVisitorAsync(this Image source, IImageVisitorAsync visitor)
=> source.AcceptAsync(visitor);
/// <summary>
/// Gets the configuration for the image.
/// </summary>

16
src/ImageSharp/Advanced/IImageVisitor.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the GNU Affero General Public License, Version 3.
using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Advanced
@ -19,4 +20,19 @@ namespace SixLabors.ImageSharp.Advanced
void Visit<TPixel>(Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>;
}
/// <summary>
/// A visitor to implement a double-dispatch pattern in order to apply pixel-specific operations
/// on non-generic <see cref="Image"/> instances.
/// </summary>
public interface IImageVisitorAsync
{
/// <summary>
/// Provides a pixel-specific implementation for a given operation.
/// </summary>
/// <param name="image">The image.</param>
/// <typeparam name="TPixel">The pixel type.</typeparam>
Task VisitAsync<TPixel>(Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>;
}
}

20
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -134,13 +134,21 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
// cheat for now do async copy of the stream into memory stream and use the sync version
// we should use an array pool backed memorystream implementation
using (var ms = new MemoryStream())
// if we can seek then we arn't in a context that errors on async operations
if (stream.CanSeek)
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
return this.Decode<TPixel>(stream);
}
else
{
// cheat for now do async copy of the stream into memory stream and use the sync version
// we should use an array pool backed memorystream implementation
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
}
}
}

15
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -100,11 +100,18 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public async Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
using (var ms = new MemoryStream())
if (stream.CanSeek)
{
this.Encode(image, ms);
ms.Position = 0;
await ms.CopyToAsync(stream).ConfigureAwait(false);
this.Encode(image, stream);
}
else
{
using (var ms = new MemoryStream())
{
this.Encode(image, ms);
ms.Position = 0;
await ms.CopyToAsync(stream).ConfigureAwait(false);
}
}
}

30
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -106,11 +106,18 @@ namespace SixLabors.ImageSharp.Formats.Gif
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
using (var ms = new MemoryStream())
if (stream.CanSeek)
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
return this.Decode<TPixel>(stream);
}
else
{
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
}
}
}
@ -186,11 +193,18 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public async Task<IImageInfo> IdentifyAsync(Stream stream)
{
using (var ms = new MemoryStream())
if (stream.CanSeek)
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
return this.Identify(stream);
}
else
{
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
}
}
}

30
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -222,11 +222,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
using (var ms = new MemoryStream())
if (stream.CanSeek)
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
return this.Decode<TPixel>(stream);
}
else
{
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
}
}
}
@ -253,11 +260,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public async Task<IImageInfo> IdentifyAsync(Stream stream)
{
using (var ms = new MemoryStream())
if (stream.CanSeek)
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
return this.Identify(stream);
}
else
{
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
}
}
}

17
src/ImageSharp/Formats/Jpeg/JpegEncoder.cs

@ -48,12 +48,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
var encoder = new JpegEncoderCore(this);
// this hack has to be be here because JpegEncoderCore is unsafe
using (var ms = new MemoryStream())
if (stream.CanSeek)
{
encoder.Encode(image, ms);
ms.Position = 0;
await ms.CopyToAsync(stream).ConfigureAwait(false);
encoder.Encode(image, stream);
}
else
{
// this hack has to be be here because JpegEncoderCore is unsafe
using (var ms = new MemoryStream())
{
encoder.Encode(image, ms);
ms.Position = 0;
await ms.CopyToAsync(stream).ConfigureAwait(false);
}
}
}
}

30
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -152,11 +152,18 @@ namespace SixLabors.ImageSharp.Formats.Png
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
using (var ms = new MemoryStream())
if (stream.CanSeek)
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
return this.Decode<TPixel>(stream);
}
else
{
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
}
}
}
@ -269,11 +276,18 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public async Task<IImageInfo> IdentifyAsync(Stream stream)
{
using (var ms = new MemoryStream())
if (stream.CanSeek)
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
return this.Identify(stream);
}
else
{
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
}
}
}

15
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -135,11 +135,18 @@ namespace SixLabors.ImageSharp.Formats.Png
public async Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
using (var ms = new MemoryStream())
if (stream.CanSeek)
{
this.Encode(image, ms);
ms.Position = 0;
await ms.CopyToAsync(stream).ConfigureAwait(false);
this.Encode(image, stream);
}
else
{
using (var ms = new MemoryStream())
{
this.Encode(image, ms);
ms.Position = 0;
await ms.CopyToAsync(stream).ConfigureAwait(false);
}
}
}

30
src/ImageSharp/Formats/Tga/TgaDecoderCore.cs

@ -91,11 +91,18 @@ namespace SixLabors.ImageSharp.Formats.Tga
public async Task<Image<TPixel>> DecodeAsync<TPixel>(Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
using (var ms = new MemoryStream())
if (stream.CanSeek)
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
return this.Decode<TPixel>(stream);
}
else
{
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Decode<TPixel>(ms);
}
}
}
@ -676,11 +683,18 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public async Task<IImageInfo> IdentifyAsync(Stream stream)
{
using (var ms = new MemoryStream())
if (stream.CanSeek)
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
return this.Identify(stream);
}
else
{
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
return this.Identify(ms);
}
}
}

15
src/ImageSharp/Formats/Tga/TgaEncoderCore.cs

@ -64,11 +64,18 @@ namespace SixLabors.ImageSharp.Formats.Tga
public async Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
using (var ms = new MemoryStream())
if (stream.CanSeek)
{
this.Encode(image, ms);
ms.Position = 0;
await ms.CopyToAsync(stream).ConfigureAwait(false);
this.Encode(image, stream);
}
else
{
using (var ms = new MemoryStream())
{
this.Encode(image, ms);
ms.Position = 0;
await ms.CopyToAsync(stream).ConfigureAwait(false);
}
}
}

33
src/ImageSharp/Image.cs

@ -3,7 +3,7 @@
using System;
using System.IO;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Metadata;
@ -98,6 +98,21 @@ namespace SixLabors.ImageSharp
this.AcceptVisitor(new EncodeVisitor(encoder, stream));
}
/// <summary>
/// Saves the image to the given stream using the given image encoder.
/// </summary>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream or encoder is null.</exception>
public Task SaveAsync(Stream stream, IImageEncoder encoder)
{
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(encoder, nameof(encoder));
this.EnsureNotDisposed();
return this.AcceptVisitorAsync(new EncodeVisitor(encoder, stream));
}
/// <summary>
/// Returns a copy of the image in the given pixel format.
/// </summary>
@ -140,7 +155,15 @@ namespace SixLabors.ImageSharp
/// <param name="visitor">The visitor.</param>
internal abstract void Accept(IImageVisitor visitor);
private class EncodeVisitor : IImageVisitor
/// <summary>
/// Accepts a <see cref="IImageVisitor"/>.
/// Implemented by <see cref="Image{TPixel}"/> invoking <see cref="IImageVisitor.Visit{TPixel}"/>
/// with the pixel type of the image.
/// </summary>
/// <param name="visitor">The visitor.</param>
internal abstract Task AcceptAsync(IImageVisitorAsync visitor);
private class EncodeVisitor : IImageVisitor, IImageVisitorAsync
{
private readonly IImageEncoder encoder;
@ -157,6 +180,12 @@ namespace SixLabors.ImageSharp
{
this.encoder.Encode(image, this.stream);
}
public Task VisitAsync<TPixel>(Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
return this.encoder.EncodeAsync(image, this.stream);
}
}
}
}

57
src/ImageSharp/ImageExtensions.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
@ -22,40 +23,36 @@ namespace SixLabors.ImageSharp
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="ArgumentNullException">The path is null.</exception>
public static void Save(this Image source, string path)
{
Guard.NotNull(path, nameof(path));
=> source.Save(path, source.FindEncoded(path));
string ext = Path.GetExtension(path);
IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext);
if (format is null)
{
var sb = new StringBuilder();
sb.AppendLine($"No encoder was found for extension '{ext}'. Registered encoders include:");
foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats)
{
sb.AppendFormat(" - {0} : {1}{2}", fmt.Name, string.Join(", ", fmt.FileExtensions), Environment.NewLine);
}
throw new NotSupportedException(sb.ToString());
}
IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format);
/// <summary>
/// Writes the image to the given stream using the currently loaded image format.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="ArgumentNullException">The path is null.</exception>
public static Task SaveAsync(this Image source, string path)
=> source.SaveAsync(path, source.FindEncoded(path));
if (encoder is null)
/// <summary>
/// Writes the image to the given stream using the currently loaded image format.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="path">The file path to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="ArgumentNullException">The encoder is null.</exception>
public static void Save(this Image source, string path, IImageEncoder encoder)
{
Guard.NotNull(path, nameof(path));
Guard.NotNull(encoder, nameof(encoder));
using (Stream fs = source.GetConfiguration().FileSystem.Create(path))
{
var sb = new StringBuilder();
sb.AppendLine($"No encoder was found for extension '{ext}' using image format '{format.Name}'. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
sb.AppendFormat(" - {0} : {1}{2}", enc.Key, enc.Value.GetType().Name, Environment.NewLine);
}
throw new NotSupportedException(sb.ToString());
source.Save(fs, encoder);
}
source.Save(path, encoder);
}
/// <summary>
/// Writes the image to the given stream using the currently loaded image format.
/// </summary>
@ -64,13 +61,13 @@ namespace SixLabors.ImageSharp
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="ArgumentNullException">The path is null.</exception>
/// <exception cref="ArgumentNullException">The encoder is null.</exception>
public static void Save(this Image source, string path, IImageEncoder encoder)
public static async Task SaveAsync(this Image source, string path, IImageEncoder encoder)
{
Guard.NotNull(path, nameof(path));
Guard.NotNull(encoder, nameof(encoder));
using (Stream fs = source.GetConfiguration().FileSystem.Create(path))
{
source.Save(fs, encoder);
await source.SaveAsync(fs, encoder).ConfigureAwait(false);
}
}

9
src/ImageSharp/Image{TPixel}.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
@ -288,6 +289,14 @@ namespace SixLabors.ImageSharp
visitor.Visit(this);
}
/// <inheritdoc />
internal override Task AcceptAsync(IImageVisitorAsync visitor)
{
this.EnsureNotDisposed();
return visitor.VisitAsync(this);
}
/// <summary>
/// Switches the buffers used by the image and the pixelSource meaning that the Image will "own" the buffer from the pixelSource and the pixelSource will now own the Images buffer.
/// </summary>

109
tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs

@ -0,0 +1,109 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the GNU Affero General Public License, Version 3.
using System;
using System.IO;
using Moq;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Tests.TestUtilities;
public partial class ImageTests
{
public class SaveAsync
{
[Fact]
public async Task DetectedEncoding()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests));
string file = System.IO.Path.Combine(dir, "DetectedEncodingAsync.png");
using (var image = new Image<Rgba32>(10, 10))
{
await image.SaveAsync(file);
}
using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
}
[Fact]
public async Task WhenExtensionIsUnknown_Throws()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests));
string file = System.IO.Path.Combine(dir, "UnknownExtensionsEncoding_Throws.tmp");
await Assert.ThrowsAsync<NotSupportedException>(
async () =>
{
using (var image = new Image<Rgba32>(10, 10))
{
await image.SaveAsync(file);
}
});
}
[Fact]
public async Task SetEncoding()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests));
string file = System.IO.Path.Combine(dir, "SetEncoding.dat");
using (var image = new Image<Rgba32>(10, 10))
{
await image.SaveAsync(file, new PngEncoder());
}
using (Image.Load(file, out var mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
}
[Fact]
public async Task ThrowsWhenDisposed()
{
var image = new Image<Rgba32>(5, 5);
image.Dispose();
IImageEncoder encoder = Mock.Of<IImageEncoder>();
using (var stream = new MemoryStream())
{
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await image.SaveAsync(stream, encoder));
}
}
[Theory]
[InlineData("test.png")]
[InlineData("test.tga")]
[InlineData("test.bmp")]
[InlineData("test.jpg")]
[InlineData("test.gif")]
public async Task SaveNeverCallsSyncMethods(string filename)
{
using (var image = new Image<Rgba32>(5, 5))
{
IImageEncoder encoder = image.FindEncoded(filename);
using (var stream = new MemoryStream())
{
var asyncStream = new AsyncStreamWrapper(stream, () => false);
await image.SaveAsync(asyncStream, encoder);
}
}
}
}
}
}

117
tests/ImageSharp.Tests/TestUtilities/AsyncOnlyStream.cs

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SixLabors.ImageSharp.Tests.TestUtilities
{
// https://github.com/dotnet/aspnetcore/blob/620c673705bb17b33cbc5ff32872d85a5fbf82b9/src/Hosting/TestHost/src/AsyncStreamWrapper.cs
internal class AsyncStreamWrapper : Stream
{
private Stream inner;
private Func<bool> allowSynchronousIO;
internal AsyncStreamWrapper(Stream inner, Func<bool> allowSynchronousIO)
{
this.inner = inner;
this.allowSynchronousIO = allowSynchronousIO;
}
public override bool CanRead => this.inner.CanRead;
public override bool CanSeek => false;
public override bool CanWrite => this.inner.CanWrite;
public override long Length => throw new NotSupportedException("The stream is not seekable.");
public override long Position
{
get => throw new NotSupportedException("The stream is not seekable.");
set => throw new NotSupportedException("The stream is not seekable.");
}
public override void Flush()
{
// Not blocking Flush because things like StreamWriter.Dispose() always call it.
this.inner.Flush();
}
public override Task FlushAsync(CancellationToken cancellationToken)
{
return this.inner.FlushAsync(cancellationToken);
}
public override int Read(byte[] buffer, int offset, int count)
{
if (!this.allowSynchronousIO())
{
throw new InvalidOperationException("Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true.");
}
return this.inner.Read(buffer, offset, count);
}
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return this.inner.ReadAsync(buffer, offset, count, cancellationToken);
}
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
return this.inner.BeginRead(buffer, offset, count, callback, state);
}
public override int EndRead(IAsyncResult asyncResult)
{
return this.inner.EndRead(asyncResult);
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException("The stream is not seekable.");
}
public override void SetLength(long value)
{
throw new NotSupportedException("The stream is not seekable.");
}
public override void Write(byte[] buffer, int offset, int count)
{
if (!this.allowSynchronousIO())
{
throw new InvalidOperationException("Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true.");
}
this.inner.Write(buffer, offset, count);
}
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
return this.inner.BeginWrite(buffer, offset, count, callback, state);
}
public override void EndWrite(IAsyncResult asyncResult)
{
this.inner.EndWrite(asyncResult);
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return this.inner.WriteAsync(buffer, offset, count, cancellationToken);
}
public override void Close()
{
// Don't dispose the inner stream, we don't want to impact the client stream
}
protected override void Dispose(bool disposing)
{
// Don't dispose the inner stream, we don't want to impact the client stream
}
}
}
Loading…
Cancel
Save