From 665bd9b87d8c7b33980c32afee1668805e942e34 Mon Sep 17 00:00:00 2001 From: Derek Christensen Date: Sun, 18 Feb 2018 16:12:39 -0700 Subject: [PATCH 1/3] Added UnitTest project. --- .../AllReady.Processing.UnitTest.csproj | 22 +++++++++++++++++++ .../QueueTestShould.cs | 22 +++++++++++++++++++ AllReady.Processing/AllReady.Processing.sln | 10 +++++++-- 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 AllReady.Processing.UnitTest/AllReady.Processing.UnitTest.csproj create mode 100644 AllReady.Processing.UnitTest/QueueTestShould.cs diff --git a/AllReady.Processing.UnitTest/AllReady.Processing.UnitTest.csproj b/AllReady.Processing.UnitTest/AllReady.Processing.UnitTest.csproj new file mode 100644 index 0000000..bc3a38d --- /dev/null +++ b/AllReady.Processing.UnitTest/AllReady.Processing.UnitTest.csproj @@ -0,0 +1,22 @@ + + + + net461 + + false + + + + + + + + + + + + + + + + diff --git a/AllReady.Processing.UnitTest/QueueTestShould.cs b/AllReady.Processing.UnitTest/QueueTestShould.cs new file mode 100644 index 0000000..3e2dde4 --- /dev/null +++ b/AllReady.Processing.UnitTest/QueueTestShould.cs @@ -0,0 +1,22 @@ +using System.Diagnostics; +using Microsoft.Azure.WebJobs.Host; +using Moq; +using Xunit; + +namespace AllReady.Processing.UnitTest +{ + public class QueueTestShould + { + [Fact] + public void Log_that_the_queue_message_was_dequeued() + { + string item = "Test Item"; + var mockTraceWriter = new Mock(TraceLevel.Verbose); + + QueueTest.Run(item, mockTraceWriter.Object); + + + mockTraceWriter.Verify(x => x.Trace(It.Is(e => e.Level == TraceLevel.Info && e.Message.Contains(item))), Times.Once); + } + } +} \ No newline at end of file diff --git a/AllReady.Processing/AllReady.Processing.sln b/AllReady.Processing/AllReady.Processing.sln index b664a56..00c70fa 100644 --- a/AllReady.Processing/AllReady.Processing.sln +++ b/AllReady.Processing/AllReady.Processing.sln @@ -1,9 +1,11 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27130.2027 +VisualStudioVersion = 15.0.27130.2026 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AllReady.Processing", "AllReady.Processing\AllReady.Processing.csproj", "{C619B518-08A3-4CFC-BC22-CB0B4A63CB31}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AllReady.Processing", "AllReady.Processing\AllReady.Processing.csproj", "{C619B518-08A3-4CFC-BC22-CB0B4A63CB31}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AllReady.Processing.UnitTest", "..\AllReady.Processing.UnitTest\AllReady.Processing.UnitTest.csproj", "{CBA25B72-7C92-4C78-ABF6-1E35079214B5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +17,10 @@ Global {C619B518-08A3-4CFC-BC22-CB0B4A63CB31}.Debug|Any CPU.Build.0 = Debug|Any CPU {C619B518-08A3-4CFC-BC22-CB0B4A63CB31}.Release|Any CPU.ActiveCfg = Release|Any CPU {C619B518-08A3-4CFC-BC22-CB0B4A63CB31}.Release|Any CPU.Build.0 = Release|Any CPU + {CBA25B72-7C92-4C78-ABF6-1E35079214B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBA25B72-7C92-4C78-ABF6-1E35079214B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBA25B72-7C92-4C78-ABF6-1E35079214B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBA25B72-7C92-4C78-ABF6-1E35079214B5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 997a48d9464f8a6fc460aee3cf55bd33abb3a4dc Mon Sep 17 00:00:00 2001 From: Derek Christensen Date: Fri, 2 Mar 2018 20:37:45 -0700 Subject: [PATCH 2/3] Added unit test project. --- .../AllReady.Processing.UnitTest.csproj | 2 +- AllReady.Processing.UnitTest/QueueTestShould.cs | 1 - .../AllReady.Processing/AllReady.Processing.csproj | 2 +- AllReady.Processing/AllReady.Processing/QueueTest.cs | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/AllReady.Processing.UnitTest/AllReady.Processing.UnitTest.csproj b/AllReady.Processing.UnitTest/AllReady.Processing.UnitTest.csproj index bc3a38d..c8be6c6 100644 --- a/AllReady.Processing.UnitTest/AllReady.Processing.UnitTest.csproj +++ b/AllReady.Processing.UnitTest/AllReady.Processing.UnitTest.csproj @@ -1,7 +1,7 @@ - net461 + netcoreapp2.0 false diff --git a/AllReady.Processing.UnitTest/QueueTestShould.cs b/AllReady.Processing.UnitTest/QueueTestShould.cs index 3e2dde4..4fa6522 100644 --- a/AllReady.Processing.UnitTest/QueueTestShould.cs +++ b/AllReady.Processing.UnitTest/QueueTestShould.cs @@ -15,7 +15,6 @@ public void Log_that_the_queue_message_was_dequeued() QueueTest.Run(item, mockTraceWriter.Object); - mockTraceWriter.Verify(x => x.Trace(It.Is(e => e.Level == TraceLevel.Info && e.Message.Contains(item))), Times.Once); } } diff --git a/AllReady.Processing/AllReady.Processing/AllReady.Processing.csproj b/AllReady.Processing/AllReady.Processing/AllReady.Processing.csproj index 0715787..0b02e57 100644 --- a/AllReady.Processing/AllReady.Processing/AllReady.Processing.csproj +++ b/AllReady.Processing/AllReady.Processing/AllReady.Processing.csproj @@ -1,6 +1,6 @@ - net461 + netstandard2.0 diff --git a/AllReady.Processing/AllReady.Processing/QueueTest.cs b/AllReady.Processing/AllReady.Processing/QueueTest.cs index dac8cde..cba166f 100644 --- a/AllReady.Processing/AllReady.Processing/QueueTest.cs +++ b/AllReady.Processing/AllReady.Processing/QueueTest.cs @@ -10,7 +10,7 @@ public static class QueueTest // local storage emulator. [FunctionName("QueueTest")] - public static void Run([QueueTrigger("queue-test", Connection = "")]string item, TraceWriter log) + public static void Run([QueueTrigger("queue-test")]string item, TraceWriter log) { log.Info($"A message was dequeued from the queue-test queue: {item}"); } From 164e2204c6da234998a071c5648b9bccde96afc5 Mon Sep 17 00:00:00 2001 From: Derek Christensen Date: Fri, 2 Mar 2018 20:38:17 -0700 Subject: [PATCH 3/3] #3 Process Uploaded Images --- AllReady.Processing.UnitTest/ImageHelpers.cs | 31 ++++++ .../ImageResizeShould.cs | 100 ++++++++++++++++++ .../AllReady.Processing.csproj | 3 +- .../AllReady.Processing/Configuration.cs | 13 +++ .../AllReady.Processing/ImageResize.cs | 42 ++++++++ 5 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 AllReady.Processing.UnitTest/ImageHelpers.cs create mode 100644 AllReady.Processing.UnitTest/ImageResizeShould.cs create mode 100644 AllReady.Processing/AllReady.Processing/Configuration.cs create mode 100644 AllReady.Processing/AllReady.Processing/ImageResize.cs diff --git a/AllReady.Processing.UnitTest/ImageHelpers.cs b/AllReady.Processing.UnitTest/ImageHelpers.cs new file mode 100644 index 0000000..d5c6497 --- /dev/null +++ b/AllReady.Processing.UnitTest/ImageHelpers.cs @@ -0,0 +1,31 @@ +using System.IO; +using Shouldly; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats; + +namespace AllReady.Processing.UnitTest +{ + public static class ImageHelpers + { + public static Stream GetImageStream(int width, int height, IImageFormat format = null) + { + var stream = new MemoryStream(); + using (var img = new Image(width, height)) + { + img.Save(stream, format ?? ImageFormats.Png); + } + + stream.Seek(0, SeekOrigin.Begin); + return stream; + } + + public static void ShouldBeImageWithDimensions(this Stream stream, int width, int height) + { + using (var img = Image.Load(stream)) + { + img.Width.ShouldBe(width); + img.Height.ShouldBe(height); + } + } + } +} \ No newline at end of file diff --git a/AllReady.Processing.UnitTest/ImageResizeShould.cs b/AllReady.Processing.UnitTest/ImageResizeShould.cs new file mode 100644 index 0000000..66eae9f --- /dev/null +++ b/AllReady.Processing.UnitTest/ImageResizeShould.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using Microsoft.Azure.WebJobs.Host; +using Moq; +using Shouldly; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats; +using Xunit; + +namespace AllReady.Processing.UnitTest +{ + public class ImageResizeShould + { + private const string BlobName = "/container/image.png"; + private readonly TraceWriter _log = new Mock(TraceLevel.Verbose).Object; + + public ImageResizeShould() + { + Environment.SetEnvironmentVariable("maxDimension", "400"); + } + + [Fact] + public void Not_resize_an_image_within_maxDimensions_in_both_directions() + { + using (var src = ImageHelpers.GetImageStream(400, 400)) + using (var result = new MemoryStream()) + { + ImageResize.Run(src, BlobName, result, _log); + + result.Seek(0, SeekOrigin.Begin); + result.ReadByte().ShouldBe(-1); + } + } + + [Fact] + public void Resize_an_image_with_width_larger_than_maxDimensions() + { + using (var src = ImageHelpers.GetImageStream(700, 400)) + using (var result = new MemoryStream()) + { + ImageResize.Run(src, BlobName, result, _log); + + result.Seek(0, SeekOrigin.Begin); + result.ShouldBeImageWithDimensions(400, 228); + } + } + + [Fact] + public void Resize_an_image_with_height_larger_than_maxDimensions() + { + using (var src = ImageHelpers.GetImageStream(250, 500)) + using (var result = new MemoryStream()) + { + ImageResize.Run(src, BlobName, result, _log); + + result.Seek(0, SeekOrigin.Begin); + result.ShouldBeImageWithDimensions(200, 400); + } + } + + [Fact] + public void Resize_an_image_with_both_dimensions_larger_than_maxDimensions() + { + using (var src = ImageHelpers.GetImageStream(500, 650)) + using (var result = new MemoryStream()) + { + ImageResize.Run(src, BlobName, result, _log); + + result.Seek(0, SeekOrigin.Begin); + result.ShouldBeImageWithDimensions(307, 400); + } + } + + public static readonly IEnumerable TestImageFormats = + new[] + { + new object[] {ImageFormats.Png}, + new object[] {ImageFormats.Gif}, + new object[] {ImageFormats.Bmp}, + new object[] {ImageFormats.Jpeg} + }; + + [Theory] + [MemberData(nameof(TestImageFormats))] + public void Maintain_the_same_image_format_when_resizing(IImageFormat expected) + { + using (var src = ImageHelpers.GetImageStream(500, 650, expected)) + using (var result = new MemoryStream()) + { + ImageResize.Run(src, BlobName, result, _log); + + result.Seek(0, SeekOrigin.Begin); + var format = Image.DetectFormat(result); + format.ShouldBe(format); + } + } + } +} \ No newline at end of file diff --git a/AllReady.Processing/AllReady.Processing/AllReady.Processing.csproj b/AllReady.Processing/AllReady.Processing/AllReady.Processing.csproj index 0b02e57..cda8cc9 100644 --- a/AllReady.Processing/AllReady.Processing/AllReady.Processing.csproj +++ b/AllReady.Processing/AllReady.Processing/AllReady.Processing.csproj @@ -3,7 +3,8 @@ netstandard2.0 - + + diff --git a/AllReady.Processing/AllReady.Processing/Configuration.cs b/AllReady.Processing/AllReady.Processing/Configuration.cs new file mode 100644 index 0000000..cb520b9 --- /dev/null +++ b/AllReady.Processing/AllReady.Processing/Configuration.cs @@ -0,0 +1,13 @@ +using System; + +namespace AllReady.Processing +{ + public static class Configuration + { + public static string GetEnvironmentVariable(string key, string defaultValue = "") => + Environment.GetEnvironmentVariable(key) ?? defaultValue; + + public static int GetEnvironmentVariableAsInt(string key, int defaultValue = 0) => + int.Parse(GetEnvironmentVariable(key, defaultValue.ToString())); + } +} \ No newline at end of file diff --git a/AllReady.Processing/AllReady.Processing/ImageResize.cs b/AllReady.Processing/AllReady.Processing/ImageResize.cs new file mode 100644 index 0000000..30de0fa --- /dev/null +++ b/AllReady.Processing/AllReady.Processing/ImageResize.cs @@ -0,0 +1,42 @@ +using System; +using System.IO; +using Microsoft.Azure.WebJobs; +using Microsoft.Azure.WebJobs.Host; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats; + +namespace AllReady.Processing +{ + public class ImageResize + { + [FunctionName("ImageResize")] + public static void Run( + [BlobTrigger("images/{name}")] Stream imageBlob, + string name, + [Blob("images/{name}", FileAccess.Write)] Stream outputBlob, + TraceWriter log) + { + int maxDimension = Configuration.GetEnvironmentVariableAsInt("maxDimension", 800); + using (var img = Image.Load(imageBlob, out IImageFormat imageFormat)) + { + int bigDimension = Math.Max(img.Width, img.Height); + if (bigDimension <= maxDimension) + { + log.Info($"Image {name} does not need resizing."); + return; + } + + double ratio = bigDimension == img.Width + ? (double) maxDimension / img.Width + : (double) maxDimension / img.Height; + int width = (int) (img.Width * ratio); + int height = (int) (img.Height * ratio); + + log.Info($"Resizing image {name} to {width}x{height}"); + img.Mutate(ctx => ctx.Resize(width, height)); + + img.Save(outputBlob, imageFormat); + } + } + } +} \ No newline at end of file