Merge pull request #2 from dnutiu/uwp-testing

Overload ICompressor and IMetadataRemover and accept a Stream
This commit is contained in:
Denis-Cosmin Nutiu 2022-04-03 18:18:17 +03:00 committed by GitHub
commit 1cec529c7b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 166 additions and 46 deletions

View file

@ -7,14 +7,17 @@ using Xunit;
namespace ConsoleInterface.Tests;
public class TestSimpleOutputSink
public class TestDirectoryOutputSink
{
private readonly string? _testsProjectDirectory;
public TestSimpleOutputSink()
public TestDirectoryOutputSink()
{
_testsProjectDirectory = Environment.GetEnvironmentVariable("IMAGE_CORE_TESTS");
if (_testsProjectDirectory == null) throw new Exception("Environment variable IMAGE_CORE_TESTS is not set!");
if (_testsProjectDirectory == null)
{
throw new Exception("Environment variable IMAGE_CORE_TESTS is not set!");
}
}
[Theory]
@ -24,14 +27,14 @@ public class TestSimpleOutputSink
[InlineData("dir", "asd", @"dir\asd.jpg")]
public void TestGetOutputPath(string directory, string file, string expectedPath)
{
var sink = SimpleOutputSink.Create(directory);
var sink = DirectoryOutputSink.Create(directory);
Assert.Equal(expectedPath, sink.GetOutputPath(file));
}
[Fact]
public void TestGetOutputPathNull()
{
var sink = SimpleOutputSink.Create("directory");
var sink = DirectoryOutputSink.Create("directory");
Assert.Throws<ArgumentException>(() => sink.GetOutputPath(""));
}
@ -39,7 +42,7 @@ public class TestSimpleOutputSink
public void TestSave()
{
// Setup
var sink = SimpleOutputSink.Create("directory");
var sink = DirectoryOutputSink.Create("directory");
var metadataRemoverMock = new Mock<IMetadataRemover>();
metadataRemoverMock.Setup(i => i.GetImagePath()).Returns("alo.wtf");
@ -47,7 +50,8 @@ public class TestSimpleOutputSink
sink.Save(metadataRemoverMock.Object);
// Assert
metadataRemoverMock.Verify(i => i.CleanImage("directory\\alo.jpg"));
metadataRemoverMock.Verify(i => i.CleanImage());
metadataRemoverMock.Verify(i => i.SaveImage("directory\\alo.jpg"));
}
[Fact]
@ -55,7 +59,7 @@ public class TestSimpleOutputSink
{
// Setup
Debug.Assert(_testsProjectDirectory != null, nameof(_testsProjectDirectory) + " != null");
var sink = SimpleOutputSink.Create(Path.Combine(_testsProjectDirectory, "test_pictures"));
var sink = DirectoryOutputSink.Create(Path.Combine(_testsProjectDirectory, "test_pictures"));
var metadataRemoverMock = new Mock<IMetadataRemover>();
var sourceFileName = Path.Combine(_testsProjectDirectory, "test_pictures\\IMG_0138.HEIC");
metadataRemoverMock.Setup(i => i.GetImagePath()).Returns(sourceFileName);
@ -64,6 +68,7 @@ public class TestSimpleOutputSink
sink.Save(metadataRemoverMock.Object);
// Assert
metadataRemoverMock.Verify(i => i.CleanImage(It.IsAny<string>()), Times.Never);
metadataRemoverMock.Verify(i => i.CleanImage(), Times.Never);
metadataRemoverMock.Verify(i => i.SaveImage(It.IsAny<string>()), Times.Never);
}
}

View file

@ -14,7 +14,10 @@ public class TestLocalFileBrowser
public TestLocalFileBrowser()
{
_testsProjectDirectory = Environment.GetEnvironmentVariable("IMAGE_CORE_TESTS");
if (_testsProjectDirectory == null) throw new Exception("Environment variable IMAGE_CORE_TESTS is not set!");
if (_testsProjectDirectory == null)
{
throw new Exception("Environment variable IMAGE_CORE_TESTS is not set!");
}
}
[Fact]
@ -39,6 +42,8 @@ public class TestLocalFileBrowser
Assert.NotEmpty(filePathsList);
for (var i = 0; i < filePathsList.Count; i++)
{
Assert.Equal(expectedFileNames[i], Path.GetFileName(filePathsList[i]));
}
}
}

View file

@ -8,25 +8,30 @@ using Microsoft.Extensions.Logging.Abstractions;
namespace ConsoleInterface
{
/// <summary>
/// SimpleOutputFormatter keeps the original file name of the image when formatting it.
/// SimpleOutputFormatter also saves all the file names into a new directory.
/// DirectoryOutputSink keeps the original file name of the image when formatting it.
/// DirectoryOutputSink also saves all the file names into a new directory.
/// path.
/// </summary>
public class SimpleOutputSink : IOutputSink
public class DirectoryOutputSink : IOutputSink
{
public static ILogger Logger = NullLogger.Instance;
private readonly string _outputDirectory;
/// <summary>
/// Creates an instance of SimpleOutputFormatter.
/// Creates an instance of DirectoryOutputSink.
/// </summary>
/// <param name="outputDirectory">The output directory.</param>
public SimpleOutputSink(string outputDirectory)
public DirectoryOutputSink(string outputDirectory)
{
if (outputDirectory.Equals(""))
{
outputDirectory = ".";
}
else
{
FileSystemHelpers.CreateDestinationDirectory(outputDirectory);
}
_outputDirectory = outputDirectory;
}
@ -41,7 +46,8 @@ namespace ConsoleInterface
}
// Save the image under the same name in the new directory.
metadataRemover.CleanImage(newFilePath);
metadataRemover.CleanImage();
metadataRemover.SaveImage(newFilePath);
return true;
}
@ -54,7 +60,9 @@ namespace ConsoleInterface
{
Logger.LogDebug($"KeepFilenameFormatter - {_outputDirectory} - {initialFilePath}");
if (string.IsNullOrEmpty(initialFilePath))
{
throw new ArgumentException("The output file path cannot be null or empty!");
}
var fileName = Path.GetFileName(initialFilePath).Split('.')[0];
var path = Path.Combine(_outputDirectory, $"{fileName}.jpg");
@ -62,12 +70,12 @@ namespace ConsoleInterface
}
/// <summary>
/// Creates an instance of OriginalFilenameFileOutputPathFormatter.
/// Creates an instance of DirectoryOutputSink.
/// </summary>
/// <param name="outputDirectory">The output directory.</param>
public static SimpleOutputSink Create(string outputDirectory)
public static DirectoryOutputSink Create(string outputDirectory)
{
return new SimpleOutputSink(outputDirectory);
return new DirectoryOutputSink(outputDirectory);
}
}
}

View file

@ -14,7 +14,11 @@ namespace ConsoleInterface
/// <param name="directoryPath">The destination directory's path.</param>
public static void CreateDestinationDirectory(string directoryPath)
{
if (Directory.Exists(directoryPath)) return;
if (Directory.Exists(directoryPath))
{
return;
}
Logger.LogWarning("Output directory does not exist. Creating.");
Directory.CreateDirectory(directoryPath);
}

View file

@ -29,7 +29,7 @@ namespace ConsoleInterface
private static void RunOptions(ProgramOptions options)
{
SetupLogging(options.LogLevel);
var outputFormatter = SimpleOutputSink.Create(options.DestinationDirectory);
var outputFormatter = DirectoryOutputSink.Create(options.DestinationDirectory);
var executor = TaskExecutor.Create(new TaskExecutorOptions
{
EnableCompression = options.CompressFiles is true,
@ -105,8 +105,8 @@ namespace ConsoleInterface
// File Retriever
LocalFileBrowser.Logger = _loggerFactory.CreateLogger(nameof(LocalFileBrowser));
// FileName formatter
SimpleOutputSink.Logger =
_loggerFactory.CreateLogger(nameof(SimpleOutputSink));
DirectoryOutputSink.Logger =
_loggerFactory.CreateLogger(nameof(DirectoryOutputSink));
FileSystemHelpers.Logger = _loggerFactory.CreateLogger(nameof(FileSystemHelpers));
Logger.LogTrace("SetupLogging - exit");
}

View file

@ -51,7 +51,11 @@ namespace ConsoleInterface
$"Cleaning {filePath}, compression {_options.EnableCompression}, outputFormatter {nameof(_options.OutputSink)}.");
ICompressor compressor = NullCompressor.Instance;
if (_options.EnableCompression) compressor = LosslessCompressor.Instance;
if (_options.EnableCompression)
{
compressor = LosslessCompressor.Instance;
}
var imageMagick = new MagickImage(filePath);
IMetadataRemover metadataRemover = new ExifRemoverAndCompressor(imageMagick, compressor);
return outputSink.Save(metadataRemover);

View file

@ -5,7 +5,7 @@ using Xunit;
namespace ImageCore.Tests
{
public class TestMetadataRemover
public class TestExifRemoverAndCompressor
{
[Fact]
public void TestExifRemoverAndCompressorCleanImage()
@ -16,7 +16,8 @@ namespace ImageCore.Tests
var metadataRemover = new ExifRemoverAndCompressor(magicImageMock.Object, compressorMock.Object);
// Test
metadataRemover.CleanImage("path");
metadataRemover.CleanImage();
metadataRemover.SaveImage("path");
// Assert
magicImageMock.Verify(i => i.RemoveProfile("exif"));

View file

@ -34,22 +34,19 @@ namespace ImageCore.Tests
}
[Fact]
public void TestNullCompressor_Compress()
public void TestLosslessCompressor_Compress_Stream()
{
ICompressor compressor = new NullCompressor();
ICompressor compressor = new LosslessCompressor();
var sourceFileName = Path.Combine(_testsProjectDirectory, "test_pictures/IMG_0138.HEIC");
var destinationFileName = Path.GetTempFileName();
File.Copy(sourceFileName, destinationFileName, true);
compressor.Compress(destinationFileName);
var originalFile = File.Open(sourceFileName, FileMode.Open);
var compressedFile = File.Open(destinationFileName, FileMode.Open);
var destinationFileHandle = File.Open(destinationFileName, FileMode.Open);
var lengthBeforeCompression = destinationFileHandle.Length;
Assert.True(compressedFile.Length == originalFile.Length);
compressor.Compress(destinationFileHandle);
originalFile.Close();
compressedFile.Close();
File.Delete(destinationFileName);
Assert.True(destinationFileHandle.Length < lengthBeforeCompression);
}
}
}

View file

@ -0,0 +1,37 @@
using System;
using System.IO;
using Image.Core;
using Xunit;
namespace ImageCore.Tests
{
public class TestNullCompressor
{
private readonly string _testsProjectDirectory;
public TestNullCompressor()
{
_testsProjectDirectory = Environment.GetEnvironmentVariable("IMAGE_CORE_TESTS");
}
[Fact]
public void TestNullCompressor_Compress()
{
ICompressor compressor = new NullCompressor();
var sourceFileName = Path.Combine(_testsProjectDirectory, "test_pictures/IMG_0138.HEIC");
var destinationFileName = Path.GetTempFileName();
File.Copy(sourceFileName, destinationFileName, true);
compressor.Compress(destinationFileName);
var originalFile = File.Open(sourceFileName, FileMode.Open);
var compressedFile = File.Open(destinationFileName, FileMode.Open);
Assert.True(compressedFile.Length == originalFile.Length);
originalFile.Close();
compressedFile.Close();
File.Delete(destinationFileName);
}
}
}

View file

@ -1,4 +1,5 @@
using ImageMagick;
using System.IO;
using ImageMagick;
namespace Image.Core
{
@ -22,12 +23,19 @@ namespace Image.Core
}
/// <summary>
/// Cleans the images and compresses it.
/// Cleans the image.
/// </summary>
/// <param name="newFilePath">The file path to save the clean image.</param>
public void CleanImage(string newFilePath)
public void CleanImage()
{
_magickImage.RemoveProfile("exif");
}
/// <summary>
/// Save the image under a new file path.
/// </summary>
/// <param name="newFilePath">The new path of the image.</param>
public void SaveImage(string newFilePath)
{
_magickImage.Write(newFilePath);
_compressor.Compress(newFilePath);
}
@ -37,5 +45,15 @@ namespace Image.Core
{
return _magickImage.FileName;
}
/// <summary>
/// Saves the image.
/// </summary>
/// <param name="stream">The stream.</param>
public void SaveImage(Stream stream)
{
_magickImage.Write(stream);
_compressor.Compress(stream);
}
}
}

View file

@ -1,4 +1,6 @@
namespace Image.Core
using System.IO;
namespace Image.Core
{
/// <summary>
/// ICompressor is an interface for implementing image compressors.
@ -10,5 +12,11 @@
/// </summary>
/// <param name="fileName">The file name of the image to be compressed.</param>
void Compress(string fileName);
/// <summary>
/// The method compresses an image in place.
/// </summary>
/// <param name="stream">The stream of the image to be compressed.</param>
void Compress(Stream stream);
}
}

View file

@ -1,4 +1,6 @@
namespace Image.Core
using System.IO;
namespace Image.Core
{
/// <summary>
/// Interface for implementing metadata removers.
@ -6,10 +8,21 @@
public interface IMetadataRemover
{
/// <summary>
/// Cleans an image and saves it under a new path.
/// Cleans an image.
/// </summary>
void CleanImage();
/// <summary>
/// Saves an image under a new file path.
/// </summary>
/// <param name="newFilePath">The file path to save the clean image.</param>
void CleanImage(string newFilePath);
void SaveImage(string newFilePath);
/// <summary>
/// Saves the image.
/// </summary>
/// <param name="stream">The stream.</param>
void SaveImage(Stream stream);
/// <summary>
/// GetImagePath gets the current image path on the filesystem.

View file

@ -1,4 +1,5 @@
using ImageMagick;
using System.IO;
using ImageMagick;
namespace Image.Core
{
@ -22,5 +23,13 @@ namespace Image.Core
{
_imageOptimizer.LosslessCompress(fileName);
}
/// <summary>
/// <inheritdoc />
/// </summary>
public void Compress(Stream stream)
{
_imageOptimizer.LosslessCompress(stream);
}
}
}

View file

@ -1,4 +1,6 @@
namespace Image.Core
using System.IO;
namespace Image.Core
{
/// <summary>
/// Does nothing. Using this Compressor will have no effect.
@ -13,5 +15,9 @@
public void Compress(string fileName)
{
}
public void Compress(Stream stream)
{
}
}
}

View file

@ -6,5 +6,10 @@
&lt;/AssemblyExplorer&gt;</s:String>
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=761234b5_002D46ba_002D4312_002Dab60_002Dd15d5d83a61a/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="TestGetFilenamesFromPath" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Project Location="C:\Users\nutiu\RiderProjects\ImgMetadataRemover\ImageCore.Tests" Presentation="&amp;lt;ImageCore.Tests&amp;gt;" /&gt;&#xD;
&lt;TestAncestor&gt;&#xD;
&lt;TestId&gt;xUnit::B915AC83-B6E9-4E0A-BA88-915F629F57C8::net6.0::ConsoleInterface.Tests.TestSimpleOutputSink&lt;/TestId&gt;&#xD;
&lt;/TestAncestor&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/SessionState&gt;</s:String></wpf:ResourceDictionary>