mirror of https://github.com/SixLabors/ImageSharp
16 changed files with 511 additions and 88 deletions
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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…
Reference in new issue