From 418218a06b661e029df7ce3f54e7c6292ce9812b Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Tue, 14 Mar 2017 19:09:02 +0000 Subject: [PATCH 1/8] Add IFileSystem --- src/ImageSharp/Configuration.cs | 8 +++ src/ImageSharp/IO/IFileSystem.cs | 31 +++++++++++ src/ImageSharp/IO/LocalFileSystem.cs | 27 ++++++++++ src/ImageSharp/Image/Image{TColor}.cs | 42 ++++++++++----- tests/ImageSharp.Tests/ConfigurationTests.cs | 12 ++++- tests/ImageSharp.Tests/IO/LocalFileSystem.cs | 53 +++++++++++++++++++ .../ImageSharp.Tests/Image/ImageSaveTests.cs | 48 +++++++++++++++++ .../Image/SaveWatchingImage.cs | 39 ++++++++++++++ 8 files changed, 246 insertions(+), 14 deletions(-) create mode 100644 src/ImageSharp/IO/IFileSystem.cs create mode 100644 src/ImageSharp/IO/LocalFileSystem.cs create mode 100644 tests/ImageSharp.Tests/IO/LocalFileSystem.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageSaveTests.cs create mode 100644 tests/ImageSharp.Tests/Image/SaveWatchingImage.cs diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index e9120aa479..053ec02026 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -12,6 +12,7 @@ namespace ImageSharp using System.Threading.Tasks; using Formats; + using ImageSharp.IO; /// /// Provides initialization code which allows extending the library. @@ -53,6 +54,13 @@ namespace ImageSharp /// internal int MaxHeaderSize { get; private set; } +#if !NETSTANDARD1_1 + /// + /// Helper for accessing the local file system. + /// + internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); +#endif + /// /// Adds a new to the collection of supported image formats. /// diff --git a/src/ImageSharp/IO/IFileSystem.cs b/src/ImageSharp/IO/IFileSystem.cs new file mode 100644 index 0000000000..1ce4f25f4c --- /dev/null +++ b/src/ImageSharp/IO/IFileSystem.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +using System.IO; + +namespace ImageSharp.IO +{ +#if !NETSTANDARD1_1 + /// + /// A simple interface representing the filesystem. + /// + public interface IFileSystem + { + /// + /// Returns a readable stream as defined by the path. + /// + /// Path to the file to open. + /// A stream representing the file to open. + Stream OpenRead(string path); + + /// + /// Creates or opens a file and returns it as a writeable stream as defined by the path. + /// + /// Path to the file to open. + /// A stream representing the file to open. + Stream OpenWrite(string path); + } +#endif +} diff --git a/src/ImageSharp/IO/LocalFileSystem.cs b/src/ImageSharp/IO/LocalFileSystem.cs new file mode 100644 index 0000000000..e32fc67338 --- /dev/null +++ b/src/ImageSharp/IO/LocalFileSystem.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace ImageSharp.IO +{ +#if !NETSTANDARD1_1 + /// + /// A wrapper around the local File apis. + /// + public class LocalFileSystem : IFileSystem + { + /// + public Stream OpenRead(string path) + { + return File.OpenRead(path); + } + + /// + public Stream OpenWrite(string path) + { + return File.OpenWrite(path); + } + } +#endif +} diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 4b22424da8..0c4aaa7849 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -168,7 +168,7 @@ namespace ImageSharp : base(configuration) { Guard.NotNull(filePath, nameof(filePath)); - using (FileStream fs = File.OpenRead(filePath)) + using (Stream fs = File.OpenRead(filePath)) { this.Load(fs, options); } @@ -349,7 +349,7 @@ namespace ImageSharp public Image Save(Stream stream, IEncoderOptions options) { Guard.NotNull(stream, nameof(stream)); - this.CurrentImageFormat.Encoder.Encode(this, stream, options); + this.SaveInternal(stream, this.CurrentImageFormat?.Encoder, options); return this; } @@ -373,9 +373,10 @@ namespace ImageSharp /// The public Image Save(Stream stream, IImageFormat format, IEncoderOptions options) { - Guard.NotNull(stream, nameof(stream)); Guard.NotNull(format, nameof(format)); - format.Encoder.Encode(this, stream, options); + + this.SaveInternal(stream, format.Encoder, options); + return this; } @@ -405,15 +406,7 @@ namespace ImageSharp /// public Image Save(Stream stream, IImageEncoder encoder, IEncoderOptions options) { - Guard.NotNull(stream, nameof(stream)); - Guard.NotNull(encoder, nameof(encoder)); - encoder.Encode(this, stream, options); - - // Reset to the start of the stream. - if (stream.CanSeek) - { - stream.Position = 0; - } + this.SaveInternal(stream, encoder, options); return this; } @@ -569,6 +562,29 @@ namespace ImageSharp return target; } + /// + /// Internally saves the image to the given stream using the given image encoder and options. + /// Can be used by overridden by tests to verify save opperations. + /// + /// The stream to save the image to. + /// The encoder to save the image with. + /// The options for the encoder. + /// Thrown if the stream or encoder is null. + internal virtual void SaveInternal(Stream stream, IImageEncoder encoder, IEncoderOptions options) + { + Guard.NotNull(stream, nameof(stream)); + Guard.NotNull(encoder, nameof(encoder)); + + long startOfStream = stream.Position; + encoder.Encode(this, stream, options); + + // Reset to the start of the stream. + if (stream.CanSeek) + { + stream.Position = startOfStream; + } + } + /// /// Creates a new from this instance /// diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index 4f48c13232..043c3d3f1f 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -10,7 +10,7 @@ namespace ImageSharp.Tests using System.Linq; using ImageSharp.Formats; - + using ImageSharp.IO; using Xunit; /// @@ -18,6 +18,16 @@ namespace ImageSharp.Tests /// public class ConfigurationTests { + [Fact] + public void DefaultsToLocalFileSystem() + { + var configuration = Configuration.CreateDefaultInstance(); + + ImageSharp.IO.IFileSystem fs = configuration.FileSystem; + + Assert.IsType(fs); + } + [Fact] public void IfAutoloadWellknwonFormatesIsTrueAllFormateAreLoaded() { diff --git a/tests/ImageSharp.Tests/IO/LocalFileSystem.cs b/tests/ImageSharp.Tests/IO/LocalFileSystem.cs new file mode 100644 index 0000000000..71fbaa3822 --- /dev/null +++ b/tests/ImageSharp.Tests/IO/LocalFileSystem.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.IO +{ + using System; + using System.IO; + using ImageSharp.IO; + + using Xunit; + + public class LocalFileSystemTests + { + [Fact] + public void OpenRead() + { + string path = Path.GetTempFileName(); + string testData = Guid.NewGuid().ToString(); + File.WriteAllText(path, testData); + + LocalFileSystem fs = new LocalFileSystem(); + + using (var r = new StreamReader(fs.OpenRead(path))) + { + string data = r.ReadToEnd(); + + Assert.Equal(testData, data); + } + + File.Delete(path); + } + + [Fact] + public void OpenWrite() + { + string path = Path.GetTempFileName(); + string testData = Guid.NewGuid().ToString(); + LocalFileSystem fs = new LocalFileSystem(); + + using (var r = new StreamWriter(fs.OpenWrite(path))) + { + r.Write(testData); + } + + string data = File.ReadAllText(path); + Assert.Equal(testData, data); + + File.Delete(path); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs new file mode 100644 index 0000000000..b6339ce8a9 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs @@ -0,0 +1,48 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.IO; + using System.Linq; + using ImageSharp.Formats; + using ImageSharp.IO; + using Moq; + using Xunit; + + /// + /// Tests the class. + /// + public class ImageSaveTests : IDisposable + { + private readonly SaveWatchingImage Image; + private readonly Mock fileSystem; + + public ImageSaveTests() + { + this.fileSystem = new Mock(); + this.Image = new SaveWatchingImage(1, 1, this.fileSystem.Object); + } + + [Fact] + public void SavePath() + { + Stream stream = new MemoryStream(); + this.fileSystem.Setup(x => x.OpenWrite("path")).Returns(stream); + this.Image.Save("path"); + + SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); + Assert.Equal(stream, operation.stream); + Assert.Equal(this.Image.CurrentImageFormat.Encoder, operation.encoder); + Assert.Null(operation.options); + } + + public void Dispose() + { + this.Image.Dispose(); + } + } +} diff --git a/tests/ImageSharp.Tests/Image/SaveWatchingImage.cs b/tests/ImageSharp.Tests/Image/SaveWatchingImage.cs new file mode 100644 index 0000000000..010ad68294 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/SaveWatchingImage.cs @@ -0,0 +1,39 @@ + +namespace ImageSharp.Tests +{ + using System; + using System.IO; + using ImageSharp; + using Processing; + using System.Collections.Generic; + using ImageSharp.Formats; + using ImageSharp.IO; + + /// + /// Watches but does not actually run the processors against the image. + /// + /// + public class SaveWatchingImage : Image + { + public List Saves { get; } = new List(); + + public SaveWatchingImage(int width, int height, IFileSystem fs = null) + : base(width, height, Configuration.CreateDefaultInstance()) + { + //switch out the file system for tests + this.Configuration.FileSystem = fs ?? this.Configuration.FileSystem; + } + + internal override void SaveInternal(Stream stream, IImageEncoder encoder, IEncoderOptions options) + { + + } + + public struct OperationDetails + { + public Stream stream; + public IImageEncoder encoder; + public IEncoderOptions options; + } + } +} From b919105f16ad53864333760982073251d4aa99ac Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Wed, 15 Mar 2017 07:46:02 +0000 Subject: [PATCH 2/8] ensure all save operations pass along IEncoderOptions --- src/ImageSharp/Configuration.cs | 2 +- src/ImageSharp/IO/IFileSystem.cs | 10 +- src/ImageSharp/IO/LocalFileSystem.cs | 19 +- src/ImageSharp/Image/Image{TColor}.cs | 14 +- tests/ImageSharp.Tests/ConfigurationTests.cs | 2 +- tests/ImageSharp.Tests/IO/LocalFileSystem.cs | 6 +- .../ImageSharp.Tests/Image/ImageSaveTests.cs | 163 +++++++++++++++++- .../Image/SaveWatchingImage.cs | 9 +- 8 files changed, 195 insertions(+), 30 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 053ec02026..e0eb21865e 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -56,7 +56,7 @@ namespace ImageSharp #if !NETSTANDARD1_1 /// - /// Helper for accessing the local file system. + /// Gets or sets the fielsystem helper for accessing the local file system. /// internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); #endif diff --git a/src/ImageSharp/IO/IFileSystem.cs b/src/ImageSharp/IO/IFileSystem.cs index 1ce4f25f4c..ee1ef84d7b 100644 --- a/src/ImageSharp/IO/IFileSystem.cs +++ b/src/ImageSharp/IO/IFileSystem.cs @@ -1,13 +1,13 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -using System.IO; - namespace ImageSharp.IO { -#if !NETSTANDARD1_1 + using System.IO; + + #if !NETSTANDARD1_1 /// /// A simple interface representing the filesystem. /// @@ -25,7 +25,7 @@ namespace ImageSharp.IO /// /// Path to the file to open. /// A stream representing the file to open. - Stream OpenWrite(string path); + Stream Create(string path); } #endif } diff --git a/src/ImageSharp/IO/LocalFileSystem.cs b/src/ImageSharp/IO/LocalFileSystem.cs index e32fc67338..02a9914ead 100644 --- a/src/ImageSharp/IO/LocalFileSystem.cs +++ b/src/ImageSharp/IO/LocalFileSystem.cs @@ -1,11 +1,16 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// namespace ImageSharp.IO { -#if !NETSTANDARD1_1 + using System; + using System.Collections.Generic; + using System.IO; + using System.Text; + + #if !NETSTANDARD1_1 /// /// A wrapper around the local File apis. /// @@ -18,9 +23,9 @@ namespace ImageSharp.IO } /// - public Stream OpenWrite(string path) + public Stream Create(string path) { - return File.OpenWrite(path); + return File.Create(path); } } #endif diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 0c4aaa7849..2c9f15393b 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -168,7 +168,8 @@ namespace ImageSharp : base(configuration) { Guard.NotNull(filePath, nameof(filePath)); - using (Stream fs = File.OpenRead(filePath)) + configuration = configuration ?? Configuration.Default; + using (Stream fs = configuration.FileSystem.OpenRead(filePath)) { this.Load(fs, options); } @@ -439,7 +440,7 @@ namespace ImageSharp throw new InvalidOperationException($"No image formats have been registered for the file extension '{ext}'."); } - return this.Save(filePath, format); + return this.Save(filePath, format, options); } /// @@ -465,10 +466,7 @@ namespace ImageSharp public Image Save(string filePath, IImageFormat format, IEncoderOptions options) { Guard.NotNull(format, nameof(format)); - using (FileStream fs = File.Create(filePath)) - { - return this.Save(fs, format); - } + return this.Save(filePath, format.Encoder, options); } /// @@ -494,9 +492,9 @@ namespace ImageSharp public Image Save(string filePath, IImageEncoder encoder, IEncoderOptions options) { Guard.NotNull(encoder, nameof(encoder)); - using (FileStream fs = File.Create(filePath)) + using (Stream fs = this.Configuration.FileSystem.Create(filePath)) { - return this.Save(fs, encoder); + return this.Save(fs, encoder, options); } } #endif diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index 043c3d3f1f..c749239d71 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -21,7 +21,7 @@ namespace ImageSharp.Tests [Fact] public void DefaultsToLocalFileSystem() { - var configuration = Configuration.CreateDefaultInstance(); + Configuration configuration = Configuration.CreateDefaultInstance(); ImageSharp.IO.IFileSystem fs = configuration.FileSystem; diff --git a/tests/ImageSharp.Tests/IO/LocalFileSystem.cs b/tests/ImageSharp.Tests/IO/LocalFileSystem.cs index 71fbaa3822..472d643cd3 100644 --- a/tests/ImageSharp.Tests/IO/LocalFileSystem.cs +++ b/tests/ImageSharp.Tests/IO/LocalFileSystem.cs @@ -22,7 +22,7 @@ namespace ImageSharp.Tests.IO LocalFileSystem fs = new LocalFileSystem(); - using (var r = new StreamReader(fs.OpenRead(path))) + using (StreamReader r = new StreamReader(fs.OpenRead(path))) { string data = r.ReadToEnd(); @@ -33,13 +33,13 @@ namespace ImageSharp.Tests.IO } [Fact] - public void OpenWrite() + public void Create() { string path = Path.GetTempFileName(); string testData = Guid.NewGuid().ToString(); LocalFileSystem fs = new LocalFileSystem(); - using (var r = new StreamWriter(fs.OpenWrite(path))) + using (StreamWriter r = new StreamWriter(fs.Create(path))) { r.Write(testData); } diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs index b6339ce8a9..172a14dc2a 100644 --- a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs @@ -20,10 +20,12 @@ namespace ImageSharp.Tests { private readonly SaveWatchingImage Image; private readonly Mock fileSystem; + private readonly IEncoderOptions encoderOptions; public ImageSaveTests() { this.fileSystem = new Mock(); + this.encoderOptions = new Mock().Object; this.Image = new SaveWatchingImage(1, 1, this.fileSystem.Object); } @@ -31,15 +33,170 @@ namespace ImageSharp.Tests public void SavePath() { Stream stream = new MemoryStream(); - this.fileSystem.Setup(x => x.OpenWrite("path")).Returns(stream); - this.Image.Save("path"); + this.fileSystem.Setup(x => x.Create("path.png")).Returns(stream); + this.Image.Save("path.png"); SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); Assert.Equal(stream, operation.stream); - Assert.Equal(this.Image.CurrentImageFormat.Encoder, operation.encoder); + Assert.IsType(operation.encoder); Assert.Null(operation.options); } + [Fact] + public void SavePathWithOptions() + { + Stream stream = new MemoryStream(); + this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream); + + this.Image.Save("path.jpg", this.encoderOptions); + + SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); + Assert.Equal(stream, operation.stream); + Assert.IsType(operation.encoder); + Assert.Equal(this.encoderOptions, operation.options); + } + + [Fact] + public void SavePathWithEncoder() + { + Stream stream = new MemoryStream(); + this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream); + + this.Image.Save("path.jpg", new BmpEncoder()); + + SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); + Assert.Equal(stream, operation.stream); + Assert.IsType(operation.encoder); + Assert.Null(operation.options); + } + + [Fact] + public void SavePathWithEncoderAndOptions() + { + Stream stream = new MemoryStream(); + this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream); + + this.Image.Save("path.jpg", new BmpEncoder(), this.encoderOptions); + + SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); + Assert.Equal(stream, operation.stream); + Assert.IsType(operation.encoder); + Assert.Equal(this.encoderOptions, operation.options); + } + + + + [Fact] + public void SavePathWithFormat() + { + Stream stream = new MemoryStream(); + this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream); + + this.Image.Save("path.jpg", new GifFormat()); + + SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); + Assert.Equal(stream, operation.stream); + Assert.IsType(operation.encoder); + Assert.Null(operation.options); + } + + [Fact] + public void SavePathWithFormatAndOptions() + { + Stream stream = new MemoryStream(); + this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream); + + this.Image.Save("path.jpg", new BmpFormat(), this.encoderOptions); + + SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); + Assert.Equal(stream, operation.stream); + Assert.IsType(operation.encoder); + Assert.Equal(this.encoderOptions, operation.options); + } + + /// + /// ///////////////////////////////////////////////////////////// + /// + /// + + [Fact] + public void SaveStream() + { + Stream stream = new MemoryStream(); + this.Image.Save(stream); + + SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); + Assert.Equal(stream, operation.stream); + Assert.IsType(this.Image.CurrentImageFormat.Encoder.GetType(), operation.encoder); + Assert.Null(operation.options); + } + + [Fact] + public void SaveStreamWithOptions() + { + Stream stream = new MemoryStream(); + + this.Image.Save(stream, this.encoderOptions); + + SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); + Assert.Equal(stream, operation.stream); + Assert.IsType(this.Image.CurrentImageFormat.Encoder.GetType(), operation.encoder); + + Assert.Equal(this.encoderOptions, operation.options); + } + + [Fact] + public void SaveStreamWithEncoder() + { + Stream stream = new MemoryStream(); + + this.Image.Save(stream, new BmpEncoder()); + + SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); + Assert.Equal(stream, operation.stream); + Assert.IsType(operation.encoder); + Assert.Null(operation.options); + } + + [Fact] + public void SaveStreamWithEncoderAndOptions() + { + Stream stream = new MemoryStream(); + + this.Image.Save(stream, new BmpEncoder(), this.encoderOptions); + + SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); + Assert.Equal(stream, operation.stream); + Assert.IsType(operation.encoder); + Assert.Equal(this.encoderOptions, operation.options); + } + + [Fact] + public void SaveStreamWithFormat() + { + Stream stream = new MemoryStream(); + + this.Image.Save(stream, new GifFormat()); + + SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); + Assert.Equal(stream, operation.stream); + Assert.IsType(operation.encoder); + Assert.Null(operation.options); + } + + [Fact] + public void SaveStreamWithFormatAndOptions() + { + Stream stream = new MemoryStream(); + + this.Image.Save(stream, new BmpFormat(), this.encoderOptions); + + SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); + Assert.Equal(stream, operation.stream); + Assert.IsType(operation.encoder); + Assert.Equal(this.encoderOptions, operation.options); + } + public void Dispose() { this.Image.Dispose(); diff --git a/tests/ImageSharp.Tests/Image/SaveWatchingImage.cs b/tests/ImageSharp.Tests/Image/SaveWatchingImage.cs index 010ad68294..e9b45da696 100644 --- a/tests/ImageSharp.Tests/Image/SaveWatchingImage.cs +++ b/tests/ImageSharp.Tests/Image/SaveWatchingImage.cs @@ -16,7 +16,7 @@ namespace ImageSharp.Tests public class SaveWatchingImage : Image { public List Saves { get; } = new List(); - + public SaveWatchingImage(int width, int height, IFileSystem fs = null) : base(width, height, Configuration.CreateDefaultInstance()) { @@ -26,7 +26,12 @@ namespace ImageSharp.Tests internal override void SaveInternal(Stream stream, IImageEncoder encoder, IEncoderOptions options) { - + this.Saves.Add(new OperationDetails + { + encoder = encoder, + options = options, + stream = stream + }); } public struct OperationDetails From 91c1a8f51a0653b18247d862f204f0d583b890ec Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Wed, 15 Mar 2017 07:55:48 +0000 Subject: [PATCH 3/8] use correct configuration object --- src/ImageSharp/Image/Image{TColor}.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 2c9f15393b..a64b515031 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -168,8 +168,8 @@ namespace ImageSharp : base(configuration) { Guard.NotNull(filePath, nameof(filePath)); - configuration = configuration ?? Configuration.Default; - using (Stream fs = configuration.FileSystem.OpenRead(filePath)) + + using (Stream fs = this.Configuration.FileSystem.OpenRead(filePath)) { this.Load(fs, options); } From d610a8536c5b64fd2b07b67d043bd28e4aa055fa Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Wed, 15 Mar 2017 12:53:40 +0000 Subject: [PATCH 4/8] remove resetting stream position --- src/ImageSharp/Image/Image{TColor}.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index a64b515031..3ac1585a82 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -573,14 +573,7 @@ namespace ImageSharp Guard.NotNull(stream, nameof(stream)); Guard.NotNull(encoder, nameof(encoder)); - long startOfStream = stream.Position; encoder.Encode(this, stream, options); - - // Reset to the start of the stream. - if (stream.CanSeek) - { - stream.Position = startOfStream; - } } /// From 6f4d716884e7584c3782047822d80c2f56e50e31 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Thu, 16 Mar 2017 08:32:47 +0000 Subject: [PATCH 5/8] use callbacks instead of overrides --- src/ImageSharp/Image/IImageCallbacks.cs | 46 +++++++++++++++++++ src/ImageSharp/Image/ImageBase{TColor}.cs | 12 ++++- src/ImageSharp/Image/Image{TColor}.cs | 35 +++++++------- .../Drawing/Paths/ProcessorWatchingImage.cs | 20 ++++---- .../Image/SaveWatchingImage.cs | 13 +++++- 5 files changed, 99 insertions(+), 27 deletions(-) create mode 100644 src/ImageSharp/Image/IImageCallbacks.cs diff --git a/src/ImageSharp/Image/IImageCallbacks.cs b/src/ImageSharp/Image/IImageCallbacks.cs new file mode 100644 index 0000000000..ed9b1f97cb --- /dev/null +++ b/src/ImageSharp/Image/IImageCallbacks.cs @@ -0,0 +1,46 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Text; + using ImageSharp.Processing; + + /// + /// Provides a set of methods that are called as part of the images lifetime/processes. + /// + internal interface IImageCallbacks + { + /// + /// Invoked before the image is saved. + /// + /// The color + /// The image + /// The destination stream + /// The encoder + /// The options + /// + /// return true if the processor should be applied otherwise false. + /// + bool OnSaving(ImageBase image, Stream stream, Formats.IImageEncoder encoder, IEncoderOptions options) + where TColor : struct, IPixel; + + /// + /// Invoked before the image is processed. + /// + /// The color + /// The image + /// The processor. + /// The rectangle. + /// + /// return true if the processor should be applied otherwise false. + /// + bool OnProcessing(ImageBase image, IImageProcessor processor, Rectangle rectangle) + where TColor : struct, IPixel; + } +} diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index 878ba09b39..f905123132 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -116,6 +116,11 @@ namespace ImageSharp /// public Configuration Configuration { get; private set; } + /// + /// Gets or sets the callbacks item which will be called during the lifetime of the image being processed. + /// + internal IImageCallbacks Callbacks { get; set; } + /// /// Applies the processor. /// @@ -123,7 +128,12 @@ namespace ImageSharp /// The rectangle. public virtual void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { - processor.Apply(this, rectangle); + // this will be null true or false, if its null or true then apply the processor + // thus is not false then apply the processors (allows for tests to save time and not actually run the prcessor is required) + if (this.Callbacks?.OnProcessing(this, processor, rectangle) != false) + { + processor.Apply(this, rectangle); + } } /// diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 3ac1585a82..8e33715bb8 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -560,22 +560,6 @@ namespace ImageSharp return target; } - /// - /// Internally saves the image to the given stream using the given image encoder and options. - /// Can be used by overridden by tests to verify save opperations. - /// - /// The stream to save the image to. - /// The encoder to save the image with. - /// The options for the encoder. - /// Thrown if the stream or encoder is null. - internal virtual void SaveInternal(Stream stream, IImageEncoder encoder, IEncoderOptions options) - { - Guard.NotNull(stream, nameof(stream)); - Guard.NotNull(encoder, nameof(encoder)); - - encoder.Encode(this, stream, options); - } - /// /// Creates a new from this instance /// @@ -597,6 +581,25 @@ namespace ImageSharp base.Dispose(disposing); } + /// + /// Internally saves the image to the given stream using the given image encoder and options. + /// Can be used by overridden by tests to verify save opperations. + /// + /// The stream to save the image to. + /// The encoder to save the image with. + /// The options for the encoder. + /// Thrown if the stream or encoder is null. + private void SaveInternal(Stream stream, IImageEncoder encoder, IEncoderOptions options) + { + Guard.NotNull(stream, nameof(stream)); + Guard.NotNull(encoder, nameof(encoder)); + + if (this.Callbacks?.OnSaving(this, stream, encoder, options) != false) + { + encoder.Encode(this, stream, options); + } + } + /// /// Copies the properties from the other . /// diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs b/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs index 3bb3b3e777..ff4432fd16 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs @@ -6,31 +6,35 @@ namespace ImageSharp.Tests.Drawing.Paths using ImageSharp; using Processing; using System.Collections.Generic; + using ImageSharp.Formats; /// /// Watches but does not actually run the processors against the image. /// /// - public class ProcessorWatchingImage : Image + public class ProcessorWatchingImage : Image, IImageCallbacks { public List ProcessorApplications { get; } = new List(); public ProcessorWatchingImage(int width, int height) : base(width, height, Configuration.CreateDefaultInstance()) { + this.Callbacks = this; } - public override void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + public bool OnSaving(ImageBase image, Stream stream, IImageEncoder encoder, IEncoderOptions options) where TColor : struct, IPixel { - ProcessorApplications.Add(new ProcessorDetails + return true; + } + + public bool OnProcessing(ImageBase image, IImageProcessor processor, Rectangle rectangle) where TColor : struct, IPixel + { + this.ProcessorApplications.Add(new ProcessorDetails { - processor = processor, + processor = (IImageProcessor)processor, rectangle = rectangle }); - - // doesn't really apply the processor to the fake images as this is supposed - // to be just used to test which processor was finally applied and to interogate - // its settings + return false;// do not really apply the processor to speed up testing } public struct ProcessorDetails diff --git a/tests/ImageSharp.Tests/Image/SaveWatchingImage.cs b/tests/ImageSharp.Tests/Image/SaveWatchingImage.cs index e9b45da696..0436146913 100644 --- a/tests/ImageSharp.Tests/Image/SaveWatchingImage.cs +++ b/tests/ImageSharp.Tests/Image/SaveWatchingImage.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Tests /// Watches but does not actually run the processors against the image. /// /// - public class SaveWatchingImage : Image + public class SaveWatchingImage : Image, IImageCallbacks { public List Saves { get; } = new List(); @@ -22,9 +22,11 @@ namespace ImageSharp.Tests { //switch out the file system for tests this.Configuration.FileSystem = fs ?? this.Configuration.FileSystem; + + this.Callbacks = this; } - internal override void SaveInternal(Stream stream, IImageEncoder encoder, IEncoderOptions options) + public bool OnSaving(ImageBase image, Stream stream, IImageEncoder encoder, IEncoderOptions options) where TColor : struct, IPixel { this.Saves.Add(new OperationDetails { @@ -32,6 +34,13 @@ namespace ImageSharp.Tests options = options, stream = stream }); + + return false; + } + + public bool OnProcessing(ImageBase image, IImageProcessor processor, Rectangle rectangle) where TColor : struct, IPixel + { + return false; } public struct OperationDetails From 7de5545da1b67b1a3784a7ad2256bf8054344e56 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Thu, 16 Mar 2017 08:36:24 +0000 Subject: [PATCH 6/8] fixed header --- src/ImageSharp/Image/IImageCallbacks.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Image/IImageCallbacks.cs b/src/ImageSharp/Image/IImageCallbacks.cs index ed9b1f97cb..93267de057 100644 --- a/src/ImageSharp/Image/IImageCallbacks.cs +++ b/src/ImageSharp/Image/IImageCallbacks.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // From b9545631c12a0faf5ec8f4c13b8171fe91d6583b Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 18 Mar 2017 09:18:12 +0000 Subject: [PATCH 7/8] drop callbacks test by mocking encoder --- src/ImageSharp/Configuration.cs | 19 +++ src/ImageSharp/Image/IImageCallbacks.cs | 46 ------- src/ImageSharp/Image/ImageBase{TColor}.cs | 12 +- src/ImageSharp/Image/Image{TColor}.cs | 5 +- .../Drawing/Paths/ProcessorWatchingImage.cs | 13 +- .../ImageSharp.Tests/Image/ImageSaveTests.cs | 113 ++++++++---------- .../Image/SaveWatchingImage.cs | 53 -------- 7 files changed, 71 insertions(+), 190 deletions(-) delete mode 100644 src/ImageSharp/Image/IImageCallbacks.cs delete mode 100644 tests/ImageSharp.Tests/Image/SaveWatchingImage.cs diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index e0eb21865e..fa983d3557 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -34,6 +34,25 @@ namespace ImageSharp /// private readonly List imageFormatsList = new List(); + /// + /// Initializes a new instance of the class. + /// + public Configuration() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The inital set of image formats. + public Configuration(params IImageFormat[] providers) + { + foreach (IImageFormat p in providers) + { + this.AddImageFormat(p); + } + } + /// /// Gets the default instance. /// diff --git a/src/ImageSharp/Image/IImageCallbacks.cs b/src/ImageSharp/Image/IImageCallbacks.cs deleted file mode 100644 index 93267de057..0000000000 --- a/src/ImageSharp/Image/IImageCallbacks.cs +++ /dev/null @@ -1,46 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using ImageSharp.Processing; - - /// - /// Provides a set of methods that are called as part of the images lifetime/processes. - /// - internal interface IImageCallbacks - { - /// - /// Invoked before the image is saved. - /// - /// The color - /// The image - /// The destination stream - /// The encoder - /// The options - /// - /// return true if the processor should be applied otherwise false. - /// - bool OnSaving(ImageBase image, Stream stream, Formats.IImageEncoder encoder, IEncoderOptions options) - where TColor : struct, IPixel; - - /// - /// Invoked before the image is processed. - /// - /// The color - /// The image - /// The processor. - /// The rectangle. - /// - /// return true if the processor should be applied otherwise false. - /// - bool OnProcessing(ImageBase image, IImageProcessor processor, Rectangle rectangle) - where TColor : struct, IPixel; - } -} diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index f905123132..878ba09b39 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -116,11 +116,6 @@ namespace ImageSharp /// public Configuration Configuration { get; private set; } - /// - /// Gets or sets the callbacks item which will be called during the lifetime of the image being processed. - /// - internal IImageCallbacks Callbacks { get; set; } - /// /// Applies the processor. /// @@ -128,12 +123,7 @@ namespace ImageSharp /// The rectangle. public virtual void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { - // this will be null true or false, if its null or true then apply the processor - // thus is not false then apply the processors (allows for tests to save time and not actually run the prcessor is required) - if (this.Callbacks?.OnProcessing(this, processor, rectangle) != false) - { - processor.Apply(this, rectangle); - } + processor.Apply(this, rectangle); } /// diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 8e33715bb8..3a76cf63ff 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -594,10 +594,7 @@ namespace ImageSharp Guard.NotNull(stream, nameof(stream)); Guard.NotNull(encoder, nameof(encoder)); - if (this.Callbacks?.OnSaving(this, stream, encoder, options) != false) - { - encoder.Encode(this, stream, options); - } + encoder.Encode(this, stream, options); } /// diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs b/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs index ff4432fd16..2d3d2cc2b8 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs @@ -12,29 +12,22 @@ namespace ImageSharp.Tests.Drawing.Paths /// Watches but does not actually run the processors against the image. /// /// - public class ProcessorWatchingImage : Image, IImageCallbacks + public class ProcessorWatchingImage : Image { public List ProcessorApplications { get; } = new List(); public ProcessorWatchingImage(int width, int height) : base(width, height, Configuration.CreateDefaultInstance()) { - this.Callbacks = this; } - public bool OnSaving(ImageBase image, Stream stream, IImageEncoder encoder, IEncoderOptions options) where TColor : struct, IPixel - { - return true; - } - - public bool OnProcessing(ImageBase image, IImageProcessor processor, Rectangle rectangle) where TColor : struct, IPixel + public override void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { this.ProcessorApplications.Add(new ProcessorDetails { - processor = (IImageProcessor)processor, + processor = processor, rectangle = rectangle }); - return false;// do not really apply the processor to speed up testing } public struct ProcessorDetails diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs index 172a14dc2a..0d1c3e09b5 100644 --- a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs @@ -18,15 +18,38 @@ namespace ImageSharp.Tests /// public class ImageSaveTests : IDisposable { - private readonly SaveWatchingImage Image; + private readonly Image Image; private readonly Mock fileSystem; + private readonly Mock format; + private readonly Mock formatNotRegistered; + private readonly Mock encoder; + private readonly Mock encoderNotInFormat; private readonly IEncoderOptions encoderOptions; public ImageSaveTests() { + this.encoder = new Mock(); + this.format = new Mock(); + this.format.Setup(x => x.Encoder).Returns(this.encoder.Object); + this.format.Setup(x => x.Decoder).Returns(new Mock().Object); + this.format.Setup(x => x.MimeType).Returns("img/test"); + this.format.Setup(x => x.Extension).Returns("png"); + this.format.Setup(x => x.SupportedExtensions).Returns(new string[] { "png", "jpg" }); + + + this.encoderNotInFormat = new Mock(); + this.formatNotRegistered = new Mock(); + this.formatNotRegistered.Setup(x => x.Encoder).Returns(this.encoderNotInFormat.Object); + this.formatNotRegistered.Setup(x => x.Decoder).Returns(new Mock().Object); + this.formatNotRegistered.Setup(x => x.MimeType).Returns("img/test"); + this.formatNotRegistered.Setup(x => x.Extension).Returns("png"); + this.formatNotRegistered.Setup(x => x.SupportedExtensions).Returns(new string[] { "png", "jpg" }); + this.fileSystem = new Mock(); this.encoderOptions = new Mock().Object; - this.Image = new SaveWatchingImage(1, 1, this.fileSystem.Object); + this.Image = new Image(1, 1, new Configuration(this.format.Object) { + FileSystem = this.fileSystem.Object + }); } [Fact] @@ -36,10 +59,7 @@ namespace ImageSharp.Tests this.fileSystem.Setup(x => x.Create("path.png")).Returns(stream); this.Image.Save("path.png"); - SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); - Assert.Equal(stream, operation.stream); - Assert.IsType(operation.encoder); - Assert.Null(operation.options); + this.encoder.Verify(x => x.Encode(this.Image, stream, null)); } [Fact] @@ -49,11 +69,8 @@ namespace ImageSharp.Tests this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream); this.Image.Save("path.jpg", this.encoderOptions); - - SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); - Assert.Equal(stream, operation.stream); - Assert.IsType(operation.encoder); - Assert.Equal(this.encoderOptions, operation.options); + + this.encoder.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); } [Fact] @@ -62,12 +79,9 @@ namespace ImageSharp.Tests Stream stream = new MemoryStream(); this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream); - this.Image.Save("path.jpg", new BmpEncoder()); + this.Image.Save("path.jpg", this.encoderNotInFormat.Object); - SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); - Assert.Equal(stream, operation.stream); - Assert.IsType(operation.encoder); - Assert.Null(operation.options); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, null)); } [Fact] @@ -76,12 +90,9 @@ namespace ImageSharp.Tests Stream stream = new MemoryStream(); this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream); - this.Image.Save("path.jpg", new BmpEncoder(), this.encoderOptions); + this.Image.Save("path.jpg", this.encoderNotInFormat.Object, this.encoderOptions); - SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); - Assert.Equal(stream, operation.stream); - Assert.IsType(operation.encoder); - Assert.Equal(this.encoderOptions, operation.options); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); } @@ -92,12 +103,9 @@ namespace ImageSharp.Tests Stream stream = new MemoryStream(); this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream); - this.Image.Save("path.jpg", new GifFormat()); + this.Image.Save("path.jpg", this.encoderNotInFormat.Object); - SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); - Assert.Equal(stream, operation.stream); - Assert.IsType(operation.encoder); - Assert.Null(operation.options); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, null)); } [Fact] @@ -106,29 +114,18 @@ namespace ImageSharp.Tests Stream stream = new MemoryStream(); this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream); - this.Image.Save("path.jpg", new BmpFormat(), this.encoderOptions); + this.Image.Save("path.jpg", this.encoderNotInFormat.Object, this.encoderOptions); - SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); - Assert.Equal(stream, operation.stream); - Assert.IsType(operation.encoder); - Assert.Equal(this.encoderOptions, operation.options); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); } - /// - /// ///////////////////////////////////////////////////////////// - /// - /// - [Fact] public void SaveStream() { Stream stream = new MemoryStream(); this.Image.Save(stream); - - SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); - Assert.Equal(stream, operation.stream); - Assert.IsType(this.Image.CurrentImageFormat.Encoder.GetType(), operation.encoder); - Assert.Null(operation.options); + + this.encoder.Verify(x => x.Encode(this.Image, stream, null)); } [Fact] @@ -138,11 +135,7 @@ namespace ImageSharp.Tests this.Image.Save(stream, this.encoderOptions); - SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); - Assert.Equal(stream, operation.stream); - Assert.IsType(this.Image.CurrentImageFormat.Encoder.GetType(), operation.encoder); - - Assert.Equal(this.encoderOptions, operation.options); + this.encoder.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); } [Fact] @@ -150,12 +143,9 @@ namespace ImageSharp.Tests { Stream stream = new MemoryStream(); - this.Image.Save(stream, new BmpEncoder()); + this.Image.Save(stream, this.encoderNotInFormat.Object); - SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); - Assert.Equal(stream, operation.stream); - Assert.IsType(operation.encoder); - Assert.Null(operation.options); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, null)); } [Fact] @@ -163,12 +153,9 @@ namespace ImageSharp.Tests { Stream stream = new MemoryStream(); - this.Image.Save(stream, new BmpEncoder(), this.encoderOptions); + this.Image.Save(stream, this.encoderNotInFormat.Object, this.encoderOptions); - SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); - Assert.Equal(stream, operation.stream); - Assert.IsType(operation.encoder); - Assert.Equal(this.encoderOptions, operation.options); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); } [Fact] @@ -176,12 +163,9 @@ namespace ImageSharp.Tests { Stream stream = new MemoryStream(); - this.Image.Save(stream, new GifFormat()); + this.Image.Save(stream, this.formatNotRegistered.Object); - SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); - Assert.Equal(stream, operation.stream); - Assert.IsType(operation.encoder); - Assert.Null(operation.options); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, null)); } [Fact] @@ -189,12 +173,9 @@ namespace ImageSharp.Tests { Stream stream = new MemoryStream(); - this.Image.Save(stream, new BmpFormat(), this.encoderOptions); + this.Image.Save(stream, this.formatNotRegistered.Object, this.encoderOptions); - SaveWatchingImage.OperationDetails operation = this.Image.Saves.Single(); - Assert.Equal(stream, operation.stream); - Assert.IsType(operation.encoder); - Assert.Equal(this.encoderOptions, operation.options); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); } public void Dispose() diff --git a/tests/ImageSharp.Tests/Image/SaveWatchingImage.cs b/tests/ImageSharp.Tests/Image/SaveWatchingImage.cs deleted file mode 100644 index 0436146913..0000000000 --- a/tests/ImageSharp.Tests/Image/SaveWatchingImage.cs +++ /dev/null @@ -1,53 +0,0 @@ - -namespace ImageSharp.Tests -{ - using System; - using System.IO; - using ImageSharp; - using Processing; - using System.Collections.Generic; - using ImageSharp.Formats; - using ImageSharp.IO; - - /// - /// Watches but does not actually run the processors against the image. - /// - /// - public class SaveWatchingImage : Image, IImageCallbacks - { - public List Saves { get; } = new List(); - - public SaveWatchingImage(int width, int height, IFileSystem fs = null) - : base(width, height, Configuration.CreateDefaultInstance()) - { - //switch out the file system for tests - this.Configuration.FileSystem = fs ?? this.Configuration.FileSystem; - - this.Callbacks = this; - } - - public bool OnSaving(ImageBase image, Stream stream, IImageEncoder encoder, IEncoderOptions options) where TColor : struct, IPixel - { - this.Saves.Add(new OperationDetails - { - encoder = encoder, - options = options, - stream = stream - }); - - return false; - } - - public bool OnProcessing(ImageBase image, IImageProcessor processor, Rectangle rectangle) where TColor : struct, IPixel - { - return false; - } - - public struct OperationDetails - { - public Stream stream; - public IImageEncoder encoder; - public IEncoderOptions options; - } - } -} From 13a5d8edf56edbe0da2313d26551e7cacc055e47 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 18 Mar 2017 10:21:31 +0000 Subject: [PATCH 8/8] remove redundant SaveInternal --- src/ImageSharp/Image/Image{TColor}.cs | 29 ++++++--------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 3a76cf63ff..34724cc977 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -349,9 +349,7 @@ namespace ImageSharp /// The public Image Save(Stream stream, IEncoderOptions options) { - Guard.NotNull(stream, nameof(stream)); - this.SaveInternal(stream, this.CurrentImageFormat?.Encoder, options); - return this; + return this.Save(stream, this.CurrentImageFormat?.Encoder, options); } /// @@ -376,9 +374,7 @@ namespace ImageSharp { Guard.NotNull(format, nameof(format)); - this.SaveInternal(stream, format.Encoder, options); - - return this; + return this.Save(stream, format.Encoder, options); } /// @@ -407,7 +403,10 @@ namespace ImageSharp /// public Image Save(Stream stream, IImageEncoder encoder, IEncoderOptions options) { - this.SaveInternal(stream, encoder, options); + Guard.NotNull(stream, nameof(stream)); + Guard.NotNull(encoder, nameof(encoder)); + + encoder.Encode(this, stream, options); return this; } @@ -581,22 +580,6 @@ namespace ImageSharp base.Dispose(disposing); } - /// - /// Internally saves the image to the given stream using the given image encoder and options. - /// Can be used by overridden by tests to verify save opperations. - /// - /// The stream to save the image to. - /// The encoder to save the image with. - /// The options for the encoder. - /// Thrown if the stream or encoder is null. - private void SaveInternal(Stream stream, IImageEncoder encoder, IEncoderOptions options) - { - Guard.NotNull(stream, nameof(stream)); - Guard.NotNull(encoder, nameof(encoder)); - - encoder.Encode(this, stream, options); - } - /// /// Copies the properties from the other . ///