diff --git a/ConsoleInterface/Program.cs b/ConsoleInterface/Program.cs index 32e6b95..edb4920 100644 --- a/ConsoleInterface/Program.cs +++ b/ConsoleInterface/Program.cs @@ -31,7 +31,6 @@ namespace ConsoleInterface private static void RunOptions(ProgramOptions options) { SetupLogging(options.LogLevel); - CreateDestinationDirectory(options.DestinationDirectory); var outputFormatter = SimpleOutputSink.Create(options.DestinationDirectory); var executor = TaskExecutor.Create(new TaskExecutorOptions { @@ -44,15 +43,6 @@ namespace ConsoleInterface executor.ParallelCleanImages(filesRetriever.GetFilenamesFromPath(options.SourceDirectory)); } - /// - /// Creates the directory if it doesn't exist. - /// - /// The destination directory. - private static void CreateDestinationDirectory(string destinationDirectory) - { - FileSystemHelpers.CreateDestinationDirectory(destinationDirectory); - } - public static void SetupLogging(string logLevel) { _loggerFactory = LoggerFactory.Create(b => diff --git a/ImageCore.Tests/TestKeepFilenameFormatter.cs b/ImageCore.Tests/TestKeepFilenameFormatter.cs deleted file mode 100644 index c85cfd0..0000000 --- a/ImageCore.Tests/TestKeepFilenameFormatter.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using Image.Files; -using Xunit; - -namespace ImageCore.Tests -{ - public class TestSimpleOutputSink - { - - [Theory] - [InlineData("", "./", @".\.jpg")] - [InlineData("", @".\", @".\.jpg")] - [InlineData("", "asd", @".\asd.jpg")] - [InlineData("dir", "asd", @"dir\asd.jpg")] - public void TestGetOutputPath(string directory, string file, string expectedPath) - { - var outputPathFormatter = SimpleOutputSink.Create(directory); - Assert.Equal(expectedPath, outputPathFormatter.GetOutputPath(file)); - } - - [Fact] - public void TestGetOutputPathNull() - { - var outputPathFormatter = SimpleOutputSink.Create("directory"); - Assert.Throws(() => outputPathFormatter.GetOutputPath("")); - } - } -} \ No newline at end of file diff --git a/ImageCore.Tests/TestLocalFileBrowser.cs b/ImageCore.Tests/TestLocalFileBrowser.cs index f515bf4..5a764a9 100644 --- a/ImageCore.Tests/TestLocalFileBrowser.cs +++ b/ImageCore.Tests/TestLocalFileBrowser.cs @@ -32,7 +32,7 @@ namespace ImageCore.Tests var filePathsList = filePaths.ToList(); var expectedFileNames = new List { - "IMG_0138.HEIC", "IMG_0140.HEIC", + "IMG_0138.HEIC", "IMG_0138.jpg", "IMG_0140.HEIC", }; Assert.NotEmpty(filePathsList); diff --git a/ImageCore.Tests/TestMetadataRemover.cs b/ImageCore.Tests/TestMetadataRemover.cs index 6b15c6b..d09212d 100644 --- a/ImageCore.Tests/TestMetadataRemover.cs +++ b/ImageCore.Tests/TestMetadataRemover.cs @@ -8,7 +8,7 @@ namespace ImageCore.Tests public class TestMetadataRemover { [Fact] - public void TestCleanImage() + public void TestExifRemoverAndCompressorCleanImage() { // Setup var magicImageMock = new Mock(); @@ -23,5 +23,22 @@ namespace ImageCore.Tests magicImageMock.Verify( i => i.Write("path")); compressorMock.Verify( i => i.Compress("path")); } + + [Fact] + public void TestExifRemoverAndCompressorGetImagePath() + { + // Setup + var magicImageMock = new Mock(); + magicImageMock.Setup(i => i.FileName).Returns("P4th"); + + var compressorMock = new Mock(); + var metadataRemover = new ExifRemoverAndCompressor(magicImageMock.Object, compressorMock.Object); + + // Test + var result = metadataRemover.GetImagePath(); + + // Assert + Assert.Equal("P4th", result); + } } } \ No newline at end of file diff --git a/ImageCore.Tests/TestSimpleOutputSink.cs b/ImageCore.Tests/TestSimpleOutputSink.cs new file mode 100644 index 0000000..90f6177 --- /dev/null +++ b/ImageCore.Tests/TestSimpleOutputSink.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using Image.Core; +using Image.Files; +using Moq; +using Xunit; + +namespace ImageCore.Tests +{ + public class TestSimpleOutputSink + { + private readonly string _testsProjectDirectory; + + public TestSimpleOutputSink() + { + _testsProjectDirectory = Environment.GetEnvironmentVariable("IMAGE_CORE_TESTS"); + } + + [Theory] + [InlineData("", "./", @".\.jpg")] + [InlineData("", @".\", @".\.jpg")] + [InlineData("", "asd", @".\asd.jpg")] + [InlineData("dir", "asd", @"dir\asd.jpg")] + public void TestGetOutputPath(string directory, string file, string expectedPath) + { + var sink = SimpleOutputSink.Create(directory); + Assert.Equal(expectedPath, sink.GetOutputPath(file)); + } + + [Fact] + public void TestGetOutputPathNull() + { + var sink = SimpleOutputSink.Create("directory"); + Assert.Throws(() => sink.GetOutputPath("")); + } + + [Fact] + public void TestSave() + { + // Setup + var sink = SimpleOutputSink.Create("directory"); + var metadataRemoverMock = new Mock(); + metadataRemoverMock.Setup(i => i.GetImagePath()).Returns("alo.wtf"); + + // Test + sink.Save(metadataRemoverMock.Object); + + // Assert + metadataRemoverMock.Verify(i => i.CleanImage("directory\\alo.jpg")); + } + + [Fact] + public void TestSaveFileExists() + { + // Setup + var sink = SimpleOutputSink.Create(Path.Join(_testsProjectDirectory, "test_pictures")); + var metadataRemoverMock = new Mock(); + var sourceFileName = Path.Join(_testsProjectDirectory, "test_pictures\\IMG_0138.HEIC"); + metadataRemoverMock.Setup(i => i.GetImagePath()).Returns(sourceFileName); + + // Test + sink.Save(metadataRemoverMock.Object); + + // Assert + metadataRemoverMock.Verify(i => i.CleanImage(It.IsAny()), Times.Never); + } + } +} \ No newline at end of file diff --git a/ImageCore.Tests/test_pictures/IMG_0138.jpg b/ImageCore.Tests/test_pictures/IMG_0138.jpg new file mode 100644 index 0000000..de6943f Binary files /dev/null and b/ImageCore.Tests/test_pictures/IMG_0138.jpg differ diff --git a/ImageCore/Core/ExifRemoverAndCompressor.cs b/ImageCore/Core/ExifRemoverAndCompressor.cs index c22a6d2..03f2d1a 100644 --- a/ImageCore/Core/ExifRemoverAndCompressor.cs +++ b/ImageCore/Core/ExifRemoverAndCompressor.cs @@ -31,5 +31,11 @@ namespace Image.Core _magickImage.Write(newFilePath); _compressor.Compress(newFilePath); } + + /// + public string GetImagePath() + { + return _magickImage.FileName; + } } } \ No newline at end of file diff --git a/ImageCore/Core/IMetadataRemover.cs b/ImageCore/Core/IMetadataRemover.cs index ac27938..cd62aa3 100644 --- a/ImageCore/Core/IMetadataRemover.cs +++ b/ImageCore/Core/IMetadataRemover.cs @@ -6,9 +6,15 @@ public interface IMetadataRemover { /// - /// CleanImage cleans an image and saves it.. + /// Cleans an image and saves it under a new path. /// /// The file path to save the clean image. void CleanImage(string newFilePath); + + /// + /// GetImagePath gets the current image path on the filesystem. + /// + /// A string representing the absolute path. + string GetImagePath(); } } \ No newline at end of file diff --git a/ImageCore/Files/IOutputSink.cs b/ImageCore/Files/IOutputSink.cs index 4dcbc3b..532435d 100644 --- a/ImageCore/Files/IOutputSink.cs +++ b/ImageCore/Files/IOutputSink.cs @@ -1,4 +1,6 @@ -namespace Image.Files +using Image.Core; + +namespace Image.Files { /// /// IOutputSink is an interface for generating saving the generated files.. @@ -6,10 +8,10 @@ public interface IOutputSink { /// - /// Generates an absolute output path given the initial absolute file path. + /// Saves the image. /// - /// The initial file path. - /// The formatted absolute output path. - string GetOutputPath(string initialFilePath); + /// Metadata remover instance. + /// True if the image was saved successfully, false otherwise. + bool Save(IMetadataRemover metadataRemover); } } \ No newline at end of file diff --git a/ImageCore/Files/SimpleOutputSink.cs b/ImageCore/Files/SimpleOutputSink.cs index 3da91e2..c769763 100644 --- a/ImageCore/Files/SimpleOutputSink.cs +++ b/ImageCore/Files/SimpleOutputSink.cs @@ -1,5 +1,6 @@ using System.IO; using Ardalis.GuardClauses; +using Image.Core; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -25,6 +26,10 @@ namespace Image.Files { outputDirectory = "."; } + else + { + FileSystemHelpers.CreateDestinationDirectory(outputDirectory); + } _outputDirectory = outputDirectory; } @@ -39,10 +44,23 @@ namespace Image.Files Guard.Against.NullOrEmpty(initialFilePath, nameof(initialFilePath)); var fileName = Path.GetFileName(initialFilePath).Split('.')[0]; var path = Path.Combine(_outputDirectory, $"{fileName}.jpg"); - return path; } + public bool Save(IMetadataRemover metadataRemover) + { + var newFilePath = GetOutputPath(metadataRemover.GetImagePath()); + var fileExists = FileSystemHelpers.CheckIfFileExists(newFilePath); + if (fileExists) + { + Logger.LogWarning($"File {newFilePath} exists, skipping"); + return false; + } + // Save the image under the same name in the new directory. + metadataRemover.CleanImage(newFilePath); + return true; + } + /// /// Creates an instance of OriginalFilenameFileOutputPathFormatter. /// diff --git a/ImageCore/Tasks/TaskExecutor.cs b/ImageCore/Tasks/TaskExecutor.cs index c18a95f..aa399bb 100644 --- a/ImageCore/Tasks/TaskExecutor.cs +++ b/ImageCore/Tasks/TaskExecutor.cs @@ -41,27 +41,19 @@ namespace Image.Tasks /// Cleans an image. Errors are silenced by default. /// /// The file path of the image to be cleaned. - /// The new file path of the cleaned image. + /// The output sink for the image.. /// True of the image was cleaned, false otherwise. - public bool CleanImage(string filePath, string newFilePath) + public bool CleanImage(string filePath, IOutputSink outputSink) { try { - var fileExists = FileSystemHelpers.CheckIfFileExists(newFilePath); - if (fileExists) - { - Logger.LogWarning($"File {newFilePath} exists, skipping"); - return false; - } + Logger.LogDebug($"Cleaning {filePath}, compression {_options.EnableCompression}, outputFormatter {nameof(_options.OutputSink)}."); + ICompressor compressor = NullCompressor.Instance; - var imageMagick = new MagickImage(filePath); if (_options.EnableCompression) compressor = LosslessCompressor.Instance; - - Logger.LogDebug( - $"Cleaning {filePath}, compression {_options.EnableCompression}, outputFormatter {nameof(_options.OutputSink)}."); + var imageMagick = new MagickImage(filePath); IMetadataRemover metadataRemover = new ExifRemoverAndCompressor(imageMagick, compressor); - metadataRemover.CleanImage(newFilePath); - return true; + return outputSink.Save(metadataRemover); } catch (Exception e) { @@ -87,8 +79,7 @@ namespace Image.Tasks var tasks = new List>(); foreach (var fileName in filenamesArray) { - var task = new Task(() => - CleanImage(fileName, _options.OutputSink.GetOutputPath(fileName))); + var task = new Task(() => CleanImage(fileName, _options.OutputSink)); tasks.Add(task); task.Start(); } diff --git a/ImageCore/Tasks/TaskExecutorOptions.cs b/ImageCore/Tasks/TaskExecutorOptions.cs index 2e1cf2d..e4b34cb 100644 --- a/ImageCore/Tasks/TaskExecutorOptions.cs +++ b/ImageCore/Tasks/TaskExecutorOptions.cs @@ -17,7 +17,7 @@ namespace Image.Tasks public IOutputSink OutputSink { get => _outputSink; - set => _outputSink = value ?? throw new ArgumentException("Output formatter cannot be null!"); + set => _outputSink = value ?? throw new ArgumentException("OutputSink cannot be null!"); } /// diff --git a/ImgMetadataRemover.sln.DotSettings.user b/ImgMetadataRemover.sln.DotSettings.user index 1c497a9..0d860a4 100644 --- a/ImgMetadataRemover.sln.DotSettings.user +++ b/ImgMetadataRemover.sln.DotSettings.user @@ -4,6 +4,7 @@ <Assembly Path="C:\Users\nutiu\.nuget\packages\magick.net.core\8.6.1\lib\netstandard21\Magick.NET.Core.dll" /> <Assembly Path="C:\Users\nutiu\.nuget\packages\microsoft.extensions.logging.abstractions\6.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll" /> </AssemblyExplorer> + C:\Users\nutiu\AppData\Local\JetBrains\Rider2021.3\resharper-host\temp\Rider\vAny\CoverageData\_ImgMetadataRemover.61395890\Snapshot\snapshot.utdcvr <SessionState ContinuousTestingMode="0" IsActive="True" Name="TestGetFilenamesFromPath" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Project Location="C:\Users\nutiu\RiderProjects\ImgMetadataRemover\ImageCore.Tests" Presentation="&lt;ImageCore.Tests&gt;" /> </SessionState> \ No newline at end of file