|
|
|
@ -2,7 +2,7 @@ |
|
|
|
// Licensed under the Six Labors Split License.
|
|
|
|
|
|
|
|
using SixLabors.ImageSharp.Formats; |
|
|
|
using SixLabors.ImageSharp.PixelFormats; |
|
|
|
using SixLabors.ImageSharp.Tests.TestUtilities; |
|
|
|
|
|
|
|
namespace SixLabors.ImageSharp.Tests; |
|
|
|
|
|
|
|
@ -13,148 +13,68 @@ public partial class ImageTests |
|
|
|
private bool isTestStreamSeekable; |
|
|
|
private readonly SemaphoreSlim notifyWaitPositionReachedSemaphore = new(0); |
|
|
|
private readonly SemaphoreSlim continueSemaphore = new(0); |
|
|
|
private readonly CancellationTokenSource cts = new(); |
|
|
|
|
|
|
|
public Decode_Cancellation() => this.TopLevelConfiguration.StreamProcessingBufferSize = 128; |
|
|
|
|
|
|
|
[Theory] |
|
|
|
[InlineData(false)] |
|
|
|
[InlineData(true)] |
|
|
|
public Task LoadAsync_Specific_Stream(bool isInputStreamSeekable) |
|
|
|
public static readonly TheoryData<string> TestFiles = new() |
|
|
|
{ |
|
|
|
this.isTestStreamSeekable = isInputStreamSeekable; |
|
|
|
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); |
|
|
|
|
|
|
|
DecoderOptions options = new() |
|
|
|
{ |
|
|
|
Configuration = this.TopLevelConfiguration |
|
|
|
}; |
|
|
|
|
|
|
|
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.LoadAsync<Rgb24>(options, this.DataStream, this.cts.Token)); |
|
|
|
} |
|
|
|
TestImages.Png.BikeSmall, |
|
|
|
TestImages.Jpeg.Baseline.Jpeg420Small, |
|
|
|
TestImages.Bmp.Car, |
|
|
|
TestImages.Tiff.RgbUncompressed, |
|
|
|
TestImages.Gif.Kumin, |
|
|
|
TestImages.Tga.Bit32PalRleBottomLeft, |
|
|
|
TestImages.Webp.TestPatternOpaqueSmall, |
|
|
|
TestImages.Pbm.RgbPlainMagick |
|
|
|
}; |
|
|
|
|
|
|
|
[Theory] |
|
|
|
[InlineData(false)] |
|
|
|
[InlineData(true)] |
|
|
|
public Task LoadAsync_Agnostic_Stream(bool isInputStreamSeekable) |
|
|
|
[MemberData(nameof(TestFiles))] |
|
|
|
public async Task IdentifyAsync_IsCancellable(string file) |
|
|
|
{ |
|
|
|
this.isTestStreamSeekable = isInputStreamSeekable; |
|
|
|
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); |
|
|
|
|
|
|
|
DecoderOptions options = new() |
|
|
|
CancellationTokenSource cts = new(); |
|
|
|
string path = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, file); |
|
|
|
using PausedStream pausedStream = new(path); |
|
|
|
pausedStream.OnWaiting(_ => |
|
|
|
{ |
|
|
|
Configuration = this.TopLevelConfiguration |
|
|
|
}; |
|
|
|
|
|
|
|
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.LoadAsync(options, this.DataStream, this.cts.Token)); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public Task LoadAsync_Agnostic_Path() |
|
|
|
{ |
|
|
|
this.isTestStreamSeekable = true; |
|
|
|
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); |
|
|
|
cts.Cancel(); |
|
|
|
pausedStream.Release(); |
|
|
|
}); |
|
|
|
|
|
|
|
Configuration configuration = Configuration.CreateDefaultInstance(); |
|
|
|
configuration.FileSystem = new SingleStreamFileSystem(pausedStream); |
|
|
|
DecoderOptions options = new() |
|
|
|
{ |
|
|
|
Configuration = this.TopLevelConfiguration |
|
|
|
Configuration = configuration |
|
|
|
}; |
|
|
|
|
|
|
|
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.LoadAsync(options, this.MockFilePath, this.cts.Token)); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public Task LoadAsync_Specific_Path() |
|
|
|
{ |
|
|
|
this.isTestStreamSeekable = true; |
|
|
|
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); |
|
|
|
|
|
|
|
DecoderOptions options = new() |
|
|
|
{ |
|
|
|
Configuration = this.TopLevelConfiguration |
|
|
|
}; |
|
|
|
|
|
|
|
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.LoadAsync<Rgb24>(options, this.MockFilePath, this.cts.Token)); |
|
|
|
await Assert.ThrowsAsync<TaskCanceledException>(async () => await Image.IdentifyAsync(options, "someFakeFile", cts.Token)); |
|
|
|
} |
|
|
|
|
|
|
|
[Theory] |
|
|
|
[InlineData(false)] |
|
|
|
[InlineData(true)] |
|
|
|
public Task IdentifyAsync_Stream(bool isInputStreamSeekable) |
|
|
|
{ |
|
|
|
this.isTestStreamSeekable = isInputStreamSeekable; |
|
|
|
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); |
|
|
|
|
|
|
|
DecoderOptions options = new() |
|
|
|
{ |
|
|
|
Configuration = this.TopLevelConfiguration |
|
|
|
}; |
|
|
|
|
|
|
|
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.IdentifyAsync(options, this.DataStream, this.cts.Token)); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public Task IdentifyAsync_CustomConfiguration_Path() |
|
|
|
[MemberData(nameof(TestFiles))] |
|
|
|
public async Task DecodeAsync_IsCancellable(string file) |
|
|
|
{ |
|
|
|
this.isTestStreamSeekable = true; |
|
|
|
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); |
|
|
|
|
|
|
|
DecoderOptions options = new() |
|
|
|
CancellationTokenSource cts = new(); |
|
|
|
string path = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, file); |
|
|
|
using PausedStream pausedStream = new(path); |
|
|
|
pausedStream.OnWaiting(_ => |
|
|
|
{ |
|
|
|
Configuration = this.TopLevelConfiguration |
|
|
|
}; |
|
|
|
|
|
|
|
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.IdentifyAsync(options, this.MockFilePath, this.cts.Token)); |
|
|
|
} |
|
|
|
|
|
|
|
[Theory] |
|
|
|
[InlineData(false)] |
|
|
|
[InlineData(true)] |
|
|
|
public Task IdentifyWithFormatAsync_CustomConfiguration_Stream(bool isInputStreamSeekable) |
|
|
|
{ |
|
|
|
this.isTestStreamSeekable = isInputStreamSeekable; |
|
|
|
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); |
|
|
|
cts.Cancel(); |
|
|
|
pausedStream.Release(); |
|
|
|
}); |
|
|
|
|
|
|
|
Configuration configuration = Configuration.CreateDefaultInstance(); |
|
|
|
configuration.FileSystem = new SingleStreamFileSystem(pausedStream); |
|
|
|
DecoderOptions options = new() |
|
|
|
{ |
|
|
|
Configuration = this.TopLevelConfiguration |
|
|
|
Configuration = configuration |
|
|
|
}; |
|
|
|
|
|
|
|
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.IdentifyWithFormatAsync(options, this.DataStream, this.cts.Token)); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public Task IdentifyWithFormatAsync_CustomConfiguration_Path() |
|
|
|
{ |
|
|
|
this.isTestStreamSeekable = true; |
|
|
|
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); |
|
|
|
|
|
|
|
DecoderOptions options = new() |
|
|
|
await Assert.ThrowsAsync<TaskCanceledException>(async () => |
|
|
|
{ |
|
|
|
Configuration = this.TopLevelConfiguration |
|
|
|
}; |
|
|
|
|
|
|
|
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.IdentifyWithFormatAsync(options, this.MockFilePath, this.cts.Token)); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public Task IdentifyWithFormatAsync_DefaultConfiguration_Stream() |
|
|
|
{ |
|
|
|
_ = Task.Factory.StartNew(this.DoCancel, TaskCreationOptions.LongRunning); |
|
|
|
|
|
|
|
return Assert.ThrowsAsync<TaskCanceledException>(() => Image.IdentifyWithFormatAsync(this.DataStream, this.cts.Token)); |
|
|
|
} |
|
|
|
|
|
|
|
private async Task DoCancel() |
|
|
|
{ |
|
|
|
// wait until we reach the middle of the steam
|
|
|
|
await this.notifyWaitPositionReachedSemaphore.WaitAsync(); |
|
|
|
|
|
|
|
// set the cancellation
|
|
|
|
this.cts.Cancel(); |
|
|
|
|
|
|
|
// continue processing the stream
|
|
|
|
this.continueSemaphore.Release(); |
|
|
|
using Image image = await Image.LoadAsync(options, "someFakeFile", cts.Token); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
protected override Stream CreateStream() => this.TestFormat.CreateAsyncSemaphoreStream(this.notifyWaitPositionReachedSemaphore, this.continueSemaphore, this.isTestStreamSeekable); |
|
|
|
|