diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index 5630596..9e24d20 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -21,7 +21,7 @@ jobs: path-to-document: 'https://github.com/Codeuctivity/SkiaSharp.Compare/blob/main/cla.md' # e.g. a CLA or a DCO document # branch should not be protected branch: 'cla' - allowlist: dependabot[bot],stesee,github-copilot[bot] + allowlist: dependabot[bot],stesee,github-actions[bot],github-copilot[bot],copilot[bot] #below are the optional inputs - If the optional inputs are not given, then default values will be taken #remote-organization-name: enter the remote organization name where the signatures should be stored (Default is storing the signatures in the same repository) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index eb1e77c..b70ff7c 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -1,6 +1,6 @@ name: .NET build and test env: - CURRENT_VERSION: 3.1.${{ github.run_number }} + CURRENT_VERSION: 3.2.${{ github.run_number }} LAST_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} on: @@ -27,6 +27,30 @@ jobs: run: dotnet restore - name: Build run: dotnet build --configuration Release --no-restore + + - name: Publish SkiaSharpCompare.Cli (Linux) + if: runner.os == 'Linux' + run: | + dotnet publish SkiaSharpCompare.Cli/SkiaSharpCompare.Cli.csproj -c Release -r linux-x64 --self-contained true -p:PublishSingleFile=true -o ./artifacts/cli/linux-x64 + + - name: Publish SkiaSharpCompare.Cli (macOS) + if: runner.os == 'macOS' + run: | + dotnet publish SkiaSharpCompare.Cli/SkiaSharpCompare.Cli.csproj -c Release -r osx-x64 --self-contained true -p:PublishSingleFile=true -o ./artifacts/cli/osx-x64 + + - name: Publish SkiaSharpCompare.Cli (Windows) + if: runner.os == 'Windows' + shell: powershell + run: | + dotnet publish SkiaSharpCompare.Cli/SkiaSharpCompare.Cli.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o ./artifacts/cli/win-x64 + dotnet publish SkiaSharpCompare.Cli/SkiaSharpCompare.Cli.csproj -c Release -r win-arm64 --self-contained true -p:PublishSingleFile=true -o ./artifacts/cli/win-arm64 + + - name: Upload CLI artifacts + uses: actions/upload-artifact@v4 + with: + name: cli-artifacts-${{ matrix.os }} + path: ./artifacts/cli + - name: Test run: dotnet test --no-build --verbosity normal --configuration Release @@ -47,6 +71,10 @@ jobs: run: dotnet restore - name: Build run: dotnet build --configuration Release --no-restore + - name: Download CLI artifacts + uses: actions/download-artifact@v4 + with: + path: ./artifacts_download - name: NugetPush env: NUGET_TOKEN_EXISTS: ${{ secrets.NUGET_TOKEN }} @@ -54,11 +82,14 @@ jobs: run: | dotnet nuget push ./SkiaSharpCompare/bin/Release/*.nupkg --skip-duplicate --api-key ${{secrets.NUGET_TOKEN}} --source https://api.nuget.org/v3/index.json - name: Github release + shell: bash env: GITHUB_TOKEN: ${{ github.TOKEN }} if: env.GITHUB_TOKEN != '' run: | - gh release create ${{env.CURRENT_VERSION}} ./SkiaSharpCompare/bin/Release/*.*nupkg --generate-notes + # Attach all downloaded CLI artifacts regardless of OS + zip -r cli-artifacts.zip ./artifacts_download + gh release create ${{env.CURRENT_VERSION}} ./SkiaSharpCompare/bin/Release/*.*nupkg cli-artifacts.zip --generate-notes deployTest: if: ${{ !startsWith(github.ref, 'refs/heads/release') }} @@ -77,6 +108,10 @@ jobs: run: dotnet restore - name: Build run: dotnet build --configuration Release --no-restore + - name: Download CLI artifacts + uses: actions/download-artifact@v4 + with: + path: ./artifacts_download - name: NugetPush env: NUGET_TOKEN_EXISTS: ${{ secrets.NUGET_TEST_TOKEN }} @@ -90,4 +125,5 @@ jobs: GITHUB_TOKEN: ${{ github.TOKEN }} if: env.GITHUB_TOKEN != '' run: | - gh release create ${{env.CURRENT_VERSION}} ./SkiaSharpCompare/bin/Release/*.*nupkg --prerelease --generate-notes + zip -r cli-artifacts.zip ./artifacts_download + gh release create ${{env.CURRENT_VERSION}} ./SkiaSharpCompare/bin/Release/*.*nupkg cli-artifacts.zip --prerelease --generate-notes diff --git a/SkiaSharpCompare.Cli.Tests/SkiaSharpCompare.Cli.Tests.csproj b/SkiaSharpCompare.Cli.Tests/SkiaSharpCompare.Cli.Tests.csproj new file mode 100644 index 0000000..0414ea4 --- /dev/null +++ b/SkiaSharpCompare.Cli.Tests/SkiaSharpCompare.Cli.Tests.csproj @@ -0,0 +1,20 @@ + + + + net10.0 + false + 12 + enable + + + + + + + + + + + + + \ No newline at end of file diff --git a/SkiaSharpCompare.Cli.Tests/SkiaSharpCompareCliIntegrationTests.cs b/SkiaSharpCompare.Cli.Tests/SkiaSharpCompareCliIntegrationTests.cs new file mode 100644 index 0000000..c78cf6c --- /dev/null +++ b/SkiaSharpCompare.Cli.Tests/SkiaSharpCompareCliIntegrationTests.cs @@ -0,0 +1,64 @@ +using NUnit.Framework; +using System; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; + +namespace SkiaSharpCompare.Cli.Tests +{ + public class SkiaSharpCompareCliIntegrationTests + { + private const int ProcessTimeoutMs = 30_000; + + [Test] + public async Task SkiaSharpCompareCli_ShouldExitSuccessfully() + { + // Static precompiled CLI project directory (relative to test assembly output). + var cliProjectDir = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "SkiaSharpCompare.Cli")); + Assert.That(Directory.Exists(cliProjectDir), Is.True, $"Could not locate CLI project directory at '{cliProjectDir}'. Ensure the solution layout is unchanged and the path is valid."); + + // Find a JPG file in the repository to pass to the CLI. + var jpgFile = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "SkiaSharpCompareTestNunit", "TestData", "imageWithGpsMetadata.jpg")); + Assert.That(jpgFile, Is.Not.Null, "Could not locate any .jpg file in the repository to use for the integration test."); + + var startInfo = new ProcessStartInfo + { + FileName = "dotnet", + Arguments = $"run -- \"{jpgFile}\" --meta", + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = false, + WorkingDirectory = cliProjectDir + }; + + using var process = new Process { StartInfo = startInfo }; + + process.Start(); + + var stdOutTask = process.StandardOutput.ReadToEndAsync(); + var stdErrTask = process.StandardError.ReadToEndAsync(); + + var exited = await Task.Run(() => process.WaitForExit(ProcessTimeoutMs)); + if (!exited) + { + try + { + process.Kill(entireProcessTree: true); + } + catch + { + // Best effort + } + + Assert.Fail($"Process did not exit within {ProcessTimeoutMs} ms. StdOut:{Environment.NewLine}{await stdOutTask}{Environment.NewLine}StdErr:{Environment.NewLine}{await stdErrTask}"); + } + + var stdout = await stdOutTask; + var stderr = await stdErrTask; + + Assert.That(process.ExitCode, Is.EqualTo(0), $"Process exited with non-zero exit code {process.ExitCode}.{Environment.NewLine}StdOut:{Environment.NewLine}{stdout}{Environment.NewLine}StdErr:{Environment.NewLine}{stderr}"); + Assert.That(stdout, Does.Contain("GPS:GPS Altitude: 201")); + } + } +} \ No newline at end of file diff --git a/SkiaSharpCompare.Cli.Tests/SkiaSharpCompareCliTests.cs b/SkiaSharpCompare.Cli.Tests/SkiaSharpCompareCliTests.cs new file mode 100644 index 0000000..0087b4a --- /dev/null +++ b/SkiaSharpCompare.Cli.Tests/SkiaSharpCompareCliTests.cs @@ -0,0 +1,75 @@ +using Codeuctivity.SkiaSharpCompare.Cli; +using NUnit.Framework; +using System; +using System.IO; + +namespace SkiaSharpCompare.Cli.Tests +{ + [TestFixture] + public class SkiaSharpCompareCliTests + { + [Test] + public void CompareFiles_NonImageFiles_AreReportedUnsupported() + { + var dir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(dir); + + try + { + var a = Path.Combine(dir, "a.txt"); + var b = Path.Combine(dir, "b.txt"); + + File.WriteAllText(a, "not an image"); + File.WriteAllText(b, "not an image"); + + var info = CliRunner.CompareFiles(a, b); + Assert.That(info.Unsupported, Is.True); + Assert.That(info.Result, Is.Null); + Assert.That(info.ErrorMessage, Is.Not.Null.And.Not.Empty); + } + finally + { + Directory.Delete(dir, true); + } + } + + [Test] + public void CompareDirectories_MatchesMissingAndUnsupported() + { + var root = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")); + var dirA = Path.Combine(root, "A"); + var dirB = Path.Combine(root, "B"); + Directory.CreateDirectory(dirA); + Directory.CreateDirectory(dirB); + + try + { + // file only in A + var onlyA = Path.Combine(dirA, "onlyA.txt"); + File.WriteAllText(onlyA, "not image"); + + // file only in B + var onlyB = Path.Combine(dirB, "onlyB.txt"); + File.WriteAllText(onlyB, "not image"); + + // common file (but not a valid image) + var commonA = Path.Combine(dirA, "common.jpg"); + var commonB = Path.Combine(dirB, "common.jpg"); + File.WriteAllText(commonA, "not an image"); + File.WriteAllText(commonB, "not an image"); + + var summary = CliRunner.CompareDirectories(dirA, dirB); + + Assert.That(summary.OnlyInA, Has.Member("onlyA.txt")); + Assert.That(summary.OnlyInB, Has.Member("onlyB.txt")); + Assert.That(summary.MatchedResults.Keys, Has.Member("common.jpg")); + Assert.That(summary.MatchedResults["common.jpg"], Is.Null); + Assert.That(summary.UnsupportedFiles, Has.Member("common.jpg")); + } + finally + { + Directory.Delete(root, true); + } + } + } +} \ No newline at end of file diff --git a/SkiaSharpCompare.Cli/CliRunner.cs b/SkiaSharpCompare.Cli/CliRunner.cs new file mode 100644 index 0000000..fea20fe --- /dev/null +++ b/SkiaSharpCompare.Cli/CliRunner.cs @@ -0,0 +1,247 @@ +namespace Codeuctivity.SkiaSharpCompare.Cli +{ + /// + /// Helper that exposes programmatic entry points for the console and tests. + /// + public static class CliRunner + { + /// + /// Result wrapper for a single file comparison. + /// + public sealed class FileCompareInfo + { + public ICompareResult? Result { get; init; } + public bool Unsupported { get; init; } + public string? ErrorMessage { get; init; } + } + + /// + /// Compare two image files. If an image cannot be loaded or compared, + /// will be true and will be null. + /// + public static FileCompareInfo CompareFiles(string pathA, string pathB, bool compareMetadata = true) + { + if (string.IsNullOrWhiteSpace(pathA)) + { + throw new ArgumentException("PathA is required", nameof(pathA)); + } + + if (string.IsNullOrWhiteSpace(pathB)) + { + throw new ArgumentException("PathB is required", nameof(pathB)); + } + + var absoluteA = Path.GetFullPath(pathA); + var absoluteB = Path.GetFullPath(pathB); + + var comparer = new ImageCompare(compareMetadata: compareMetadata); + + try + { + var result = comparer.CalcDiff(absoluteA, absoluteB); + return new FileCompareInfo + { + Result = result, + Unsupported = false, + ErrorMessage = null + }; + } + catch (Exception ex) + { + return new FileCompareInfo + { + Result = null, + Unsupported = true, + ErrorMessage = ex.Message + }; + } + } + + /// + /// Compare two directories. Matches files by filename. Returns compare results for matched names, + /// lists of files existing only in one directory and a list of files which failed to be compared (unsupported). + /// + public static DirectoryCompareSummary CompareDirectories(string directoryA, string directoryB, bool compareMetadata = true) + { + if (string.IsNullOrWhiteSpace(directoryA)) + { + throw new ArgumentException("directoryA is required", nameof(directoryA)); + } + + if (string.IsNullOrWhiteSpace(directoryB)) + { + throw new ArgumentException("directoryB is required", nameof(directoryB)); + } + + var dirA = Path.GetFullPath(directoryA); + var dirB = Path.GetFullPath(directoryB); + + if (!Directory.Exists(dirA)) + { + throw new DirectoryNotFoundException(dirA); + } + + if (!Directory.Exists(dirB)) + { + throw new DirectoryNotFoundException(dirB); + } + + var filesA = Directory.GetFiles(dirA).Select(Path.GetFileName).Where(n => n != null).Cast().ToHashSet(StringComparer.OrdinalIgnoreCase); + var filesB = Directory.GetFiles(dirB).Select(Path.GetFileName).Where(n => n != null).Cast().ToHashSet(StringComparer.OrdinalIgnoreCase); + + var onlyInA = filesA.Except(filesB, StringComparer.OrdinalIgnoreCase).OrderBy(n => n).ToList(); + var onlyInB = filesB.Except(filesA, StringComparer.OrdinalIgnoreCase).OrderBy(n => n).ToList(); + var matched = filesA.Intersect(filesB, StringComparer.OrdinalIgnoreCase).OrderBy(n => n).ToList(); + + var matchedResults = new Dictionary(StringComparer.OrdinalIgnoreCase); + var unsupported = new List(); + + foreach (var fileName in matched) + { + var pathA = Path.Combine(dirA, fileName); + var pathB = Path.Combine(dirB, fileName); + + var info = CompareFiles(pathA, pathB, compareMetadata: compareMetadata); + if (info.Unsupported) + { + matchedResults[fileName] = null; + unsupported.Add(fileName); + } + else + { + matchedResults[fileName] = info.Result; + } + } + + return new DirectoryCompareSummary + { + MatchedResults = matchedResults, + OnlyInA = onlyInA, + OnlyInB = onlyInB, + UnsupportedFiles = unsupported + }; + } + + /// + /// Print a file comparison result to the provided text writer. + /// + public static void PrintCompareResult(FileCompareInfo info, TextWriter writer, string? nameA = null, string? nameB = null) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + nameA ??= "A"; + nameB ??= "B"; + + writer.WriteLine($"Comparing: {nameA} <> {nameB}"); + + if (info.Unsupported) + { + writer.WriteLine($" Unsupported or failed to compare: {info.ErrorMessage}"); + return; + } + + var result = info.Result!; + writer.WriteLine($" PixelErrorCount: {result.PixelErrorCount}"); + writer.WriteLine($" PixelErrorPercentage: {result.PixelErrorPercentage:F4}"); + writer.WriteLine($" AbsoluteError: {result.AbsoluteError}"); + writer.WriteLine($" MeanError: {result.MeanError:F4}"); + + if (result.MetadataDifferences is null) + { + writer.WriteLine(" Metadata comparison disabled (null)."); + } + else if (result.MetadataDifferences.Count == 0) + { + writer.WriteLine(" Metadata: no differences."); + } + else + { + writer.WriteLine(" Metadata differences:"); + foreach (var kvp in result.MetadataDifferences) + { + writer.WriteLine($" {kvp.Key}: (A: {kvp.Value.ValueA ?? string.Empty}, B: {kvp.Value.ValueB ?? string.Empty})"); + } + } + } + + /// + /// Print directory compare summary to the writer. + /// + public static void PrintDirectorySummary(DirectoryCompareSummary summary, TextWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteLine("Directory comparison summary:"); + writer.WriteLine(); + + writer.WriteLine("Matched files:"); + if (summary.MatchedResults.Count == 0) + { + writer.WriteLine(" (none)"); + } + else + { + foreach (var kvp in summary.MatchedResults.OrderBy(k => k.Key, StringComparer.OrdinalIgnoreCase)) + { + writer.WriteLine($" {kvp.Key}:"); + if (kvp.Value is null) + { + writer.WriteLine(" Unsupported or failed to compare"); + } + else + { + writer.WriteLine($" PixelErrorCount: {kvp.Value.PixelErrorCount}, PixelErrorPercentage: {kvp.Value.PixelErrorPercentage:F4}"); + } + } + } + + writer.WriteLine(); + writer.WriteLine("Only in A:"); + if (summary.OnlyInA.Count == 0) + { + writer.WriteLine(" (none)"); + } + else + { + foreach (var n in summary.OnlyInA) + { + writer.WriteLine($" {n}"); + } + } + + writer.WriteLine(); + writer.WriteLine("Only in B:"); + if (summary.OnlyInB.Count == 0) + { + writer.WriteLine(" (none)"); + } + else + { + foreach (var n in summary.OnlyInB) + { + writer.WriteLine($" {n}"); + } + } + + writer.WriteLine(); + writer.WriteLine("Unsupported files:"); + if (summary.UnsupportedFiles.Count == 0) + { + writer.WriteLine(" (none)"); + } + else + { + foreach (var n in summary.UnsupportedFiles) + { + writer.WriteLine($" {n}"); + } + } + } + } +} \ No newline at end of file diff --git a/SkiaSharpCompare.Cli/DirectoryCompareSummary.cs b/SkiaSharpCompare.Cli/DirectoryCompareSummary.cs new file mode 100644 index 0000000..d6d0f99 --- /dev/null +++ b/SkiaSharpCompare.Cli/DirectoryCompareSummary.cs @@ -0,0 +1,13 @@ +namespace Codeuctivity.SkiaSharpCompare.Cli +{ + /// + /// Summary of comparing two directories. + /// + public sealed class DirectoryCompareSummary + { + public Dictionary MatchedResults { get; init; } = new(); + public List OnlyInA { get; init; } = new(); + public List OnlyInB { get; init; } = new(); + public List UnsupportedFiles { get; init; } = new(); + } +} \ No newline at end of file diff --git a/SkiaSharpCompare.Cli/Program.cs b/SkiaSharpCompare.Cli/Program.cs new file mode 100644 index 0000000..8cac708 --- /dev/null +++ b/SkiaSharpCompare.Cli/Program.cs @@ -0,0 +1,81 @@ +using SkiaSharpCompare.Metadata; + +namespace Codeuctivity.SkiaSharpCompare.Cli +{ + internal static class Program + { + private static int Main(string[] args) + { + if (args is null || args.Length < 1) + { + Console.WriteLine("Usage:"); + Console.WriteLine(" dotnet run -- "); + Console.WriteLine(" dotnet run -- --dir"); + Console.WriteLine(" dotnet run -- --meta # show metadata for a file"); + return 2; + } + + try + { + // If user requested metadata display + if (Array.IndexOf(args, "--meta") >= 0) + { + var fileArg = args[0]; + var absolute = Path.GetFullPath(fileArg); + if (!File.Exists(absolute)) + { + Console.Error.WriteLine($"File not found: {absolute}"); + return 4; + } + + try + { + var meta = MetadataExtractorAdapter.Extract(absolute); + Console.WriteLine($"Metadata for: {absolute}"); + if (meta.Count == 0) + { + Console.WriteLine(" (no metadata found)"); + } + else + { + foreach (var kv in meta.OrderBy(k => k.Key)) + { + Console.WriteLine($" {kv.Key}: {kv.Value}"); + } + } + + return 0; + } + catch (Exception ex) + { + Console.Error.WriteLine($"Failed to extract metadata: {ex.Message}"); + return 5; + } + } + + if (args.Length >= 3 && args[2] == "--dir") + { + var summary = CliRunner.CompareDirectories(args[0], args[1]); + CliRunner.PrintDirectorySummary(summary, Console.Out); + } + else if (args.Length >= 2) + { + var resultInfo = CliRunner.CompareFiles(args[0], args[1], compareMetadata: true); + CliRunner.PrintCompareResult(resultInfo, Console.Out, Path.GetFileName(args[0]), Path.GetFileName(args[1])); + } + else + { + Console.WriteLine("Insufficient arguments. See usage."); + return 2; + } + + return 0; + } + catch (Exception ex) + { + Console.Error.WriteLine($"Fatal error: {ex.Message}"); + return 3; + } + } + } +} \ No newline at end of file diff --git a/SkiaSharpCompare.Cli/SkiaSharpCompare.Cli.csproj b/SkiaSharpCompare.Cli/SkiaSharpCompare.Cli.csproj new file mode 100644 index 0000000..70af7c5 --- /dev/null +++ b/SkiaSharpCompare.Cli/SkiaSharpCompare.Cli.csproj @@ -0,0 +1,15 @@ + + + + Exe + net10.0 + enable + enable + 12 + + + + + + + \ No newline at end of file diff --git a/SkiaSharpCompare.sln b/SkiaSharpCompare.sln index 1524ac7..55a8a70 100644 --- a/SkiaSharpCompare.sln +++ b/SkiaSharpCompare.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11222.15 d18.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A1B6F6C3-ECBE-471C-AE18-199DEF5982C3}" ProjectSection(SolutionItems) = preProject @@ -17,6 +17,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharpCompare", "SkiaSha EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharpCompareTestNunit", "SkiaSharpCompareTestNunit\SkiaSharpCompareTestNunit.csproj", "{9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharpCompare.Cli", "SkiaSharpCompare.Cli\SkiaSharpCompare.Cli.csproj", "{296B4D6C-C8F6-81DE-9B65-D4CE48783B52}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharpCompare.Cli.Tests", "SkiaSharpCompare.Cli.Tests\SkiaSharpCompare.Cli.Tests.csproj", "{059AD3EF-A494-850F-6457-E5C4D706CC94}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -31,6 +35,14 @@ Global {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Debug|Any CPU.Build.0 = Debug|Any CPU {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Release|Any CPU.ActiveCfg = Release|Any CPU {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Release|Any CPU.Build.0 = Release|Any CPU + {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Debug|Any CPU.Build.0 = Debug|Any CPU + {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Release|Any CPU.ActiveCfg = Release|Any CPU + {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Release|Any CPU.Build.0 = Release|Any CPU + {059AD3EF-A494-850F-6457-E5C4D706CC94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {059AD3EF-A494-850F-6457-E5C4D706CC94}.Debug|Any CPU.Build.0 = Debug|Any CPU + {059AD3EF-A494-850F-6457-E5C4D706CC94}.Release|Any CPU.ActiveCfg = Release|Any CPU + {059AD3EF-A494-850F-6457-E5C4D706CC94}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SkiaSharpCompare/CompareResult.cs b/SkiaSharpCompare/CompareResult.cs index 30aac45..bed79df 100644 --- a/SkiaSharpCompare/CompareResult.cs +++ b/SkiaSharpCompare/CompareResult.cs @@ -1,45 +1,48 @@ +using System.Collections.Generic; + namespace Codeuctivity.SkiaSharpCompare { /// /// Dto - outcome of compared images /// - public class CompareResult : ICompareResult + /// + /// ctor for CompareResult + /// + /// Mean error + /// Absolute error + /// Number of pixels that differ between images + /// Percentage of pixels that differ between images + /// Metadata that differ between images + public class CompareResult(int absoluteError, double meanError, int pixelErrorCount, double pixelErrorPercentage, Dictionary? metadataDifference = null) : ICompareResult { /// /// Mean pixel error of absolute pixel error /// /// 0-765 - public double MeanError { get; } + public double MeanError { get; } = meanError; /// /// Absolute pixel error, counts each color channel on every pixel the delta /// - public int AbsoluteError { get; } + public int AbsoluteError { get; } = absoluteError; /// /// Number of pixels that differ between images /// - public int PixelErrorCount { get; } + public int PixelErrorCount { get; } = pixelErrorCount; /// /// Percentage of pixels that differ between images /// /// 0-100.0 - public double PixelErrorPercentage { get; } + public double PixelErrorPercentage { get; } = pixelErrorPercentage; /// - /// ctor for CompareResult + /// Gets a collection of metadata keys and their differing values between two sources. /// - /// Mean error - /// Absolute error - /// Number of pixels that differ between images - /// Percentage of pixels that differ between images - public CompareResult(int absoluteError, double meanError, int pixelErrorCount, double pixelErrorPercentage) - { - MeanError = meanError; - AbsoluteError = absoluteError; - PixelErrorCount = pixelErrorCount; - PixelErrorPercentage = pixelErrorPercentage; - } + /// Each entry in the dictionary represents a metadata key for which the values differ. + /// The tuple contains the value from the first source and the value from the second source, respectively. If a + /// value is null, the key may be missing from that source. + public Dictionary? MetadataDifferences { get; } = metadataDifference; } } \ No newline at end of file diff --git a/SkiaSharpCompare/CompareResultWithMetadata.cs b/SkiaSharpCompare/CompareResultWithMetadata.cs new file mode 100644 index 0000000..abb087e --- /dev/null +++ b/SkiaSharpCompare/CompareResultWithMetadata.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Codeuctivity.SkiaSharpCompare +{ + internal class CompareResultWithMetadata(ICompareResult compareResult, Dictionary? metadataDiff) : ICompareResult + { + public double MeanError { get; } = compareResult.MeanError; + public int AbsoluteError { get; } = compareResult.AbsoluteError; + public int PixelErrorCount { get; } = compareResult.PixelErrorCount; + public double PixelErrorPercentage { get; } = compareResult.PixelErrorPercentage; + public Dictionary? MetadataDifferences { get; } = metadataDiff; + } +} \ No newline at end of file diff --git a/SkiaSharpCompare/ICompareResult.cs b/SkiaSharpCompare/ICompareResult.cs index 9d737ca..8099b25 100644 --- a/SkiaSharpCompare/ICompareResult.cs +++ b/SkiaSharpCompare/ICompareResult.cs @@ -1,4 +1,6 @@ -namespace Codeuctivity.SkiaSharpCompare +using System.Collections.Generic; + +namespace Codeuctivity.SkiaSharpCompare { /// /// Dto - of compared images @@ -24,5 +26,13 @@ public interface ICompareResult /// Percentage of pixels that differ between images /// double PixelErrorPercentage { get; } + + /// + /// Gets a collection of metadata keys and their differing values between two sources. + /// + /// Each entry in the dictionary represents a metadata key for which the values differ + /// between the two compared sources. The tuple contains the value from the first source and the value from the + /// second source. If there are no differences or metadata is unavailable, the property returns null. + Dictionary? MetadataDifferences { get; } } } \ No newline at end of file diff --git a/SkiaSharpCompare/ImageCompare.cs b/SkiaSharpCompare/ImageCompare.cs index 4696815..4579787 100644 --- a/SkiaSharpCompare/ImageCompare.cs +++ b/SkiaSharpCompare/ImageCompare.cs @@ -1,4 +1,5 @@ using SkiaSharp; +using System; using System.IO; namespace Codeuctivity.SkiaSharpCompare @@ -22,11 +23,13 @@ public class ImageCompare /// cref="TransparencyOptions.CompareAlphaChannel"/>. /// Specifies the tolerance for color shifts in pixel values during comparison. A value of 0 means no /// tolerance, and higher values allow for greater differences. The default is 0. - public ImageCompare(ResizeOption resizeOption = ResizeOption.DontResize, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel, int pixelColorShiftTolerance = 0) + /// If true, compares image metadata (EXIF, etc.) in addition to pixel data. + public ImageCompare(ResizeOption resizeOption = ResizeOption.DontResize, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel, int pixelColorShiftTolerance = 0, bool compareMetadata = false) { ResizeOption = resizeOption; TransparencyOptions = transparencyOptions; PixelColorShiftTolerance = pixelColorShiftTolerance; + CompareMetadata = compareMetadata; } /// @@ -46,121 +49,203 @@ public ImageCompare(ResizeOption resizeOption = ResizeOption.DontResize, Transpa /// similar in color during image comparison or analysis tasks. public int PixelColorShiftTolerance { get; } + /// + /// Gets a value indicating whether metadata should be included in the comparison operation. + /// + public bool CompareMetadata { get; } + /// /// Calculates the difference between two images located at the specified file paths. /// /// The comparison process may involve resizing the images or applying tolerances for /// pixel color shifts and transparency, depending on the configured options. - /// The absolute file path to the first image. This cannot be null or empty. - /// The absolute file path to the second image. This cannot be null or empty. + /// The file path to the first image. This cannot be null or empty. + /// The file path to the second image. This cannot be null or empty. /// An object representing the differences between the two images. - public ICompareResult CalcDiff(string absolutePathPic1, string absolutePathPic2) + public ICompareResult CalcDiff(string pathImage1, string pathImage2) { - return Compare.CalcDiff(absolutePathPic1, absolutePathPic2, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + var compareResult = Compare.CalcDiff(pathImage1, pathImage2, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + + if (CompareMetadata) + { + var metadataDiff = MetadataComparer.CompareMetadata(pathImage1, pathImage2); + return new CompareResultWithMetadata(compareResult, metadataDiff); + } + + return compareResult; } /// /// Calculates the difference between two images located at the specified file paths, using a provided difference mask. /// - /// - /// + /// + /// /// /// - public ICompareResult CalcDiff(string absolutePathPic1, string absolutePathPic2, string differenceMask) + public ICompareResult CalcDiff(string pahtImage1, string pathImage2, string differenceMask) + { + var compareResult = Compare.CalcDiff(pahtImage1, pathImage2, differenceMask, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + + if (CompareMetadata) + { + var metadataDiff = MetadataComparer.CompareMetadata(pahtImage1, pathImage2); + return new CompareResultWithMetadata(compareResult, metadataDiff); + } + + return compareResult; + } + + /// + /// Calculates the difference between two in-memory images represented as objects. + /// + /// + /// + /// + public ICompareResult CalcDiff(SKBitmap image1, SKBitmap image2) { - return Compare.CalcDiff(absolutePathPic1, absolutePathPic2, differenceMask, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + ArgumentNullException.ThrowIfNull(image1); + ArgumentNullException.ThrowIfNull(image2); + + if (CompareMetadata) + { + throw new NotSupportedException("Metadata comparison is not implemented for SKBitmap inputs. https://github.com/mono/SkiaSharp/issues/1139 Use the overload with streams or filepath to get support for metadata comparison."); + } + + return Compare.CalcDiffInternal(image1, image2, null, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); } /// /// Calculates the difference between two in-memory images represented as objects. /// - /// - /// + /// + /// /// /// - public ICompareResult CalcDiff(SKBitmap absolutePic1, SKBitmap absolutePic2, SKBitmap differenceMaskPic) + public ICompareResult CalcDiff(SKBitmap image1, SKBitmap image2, SKBitmap differenceMaskPic) { - return Compare.CalcDiff(absolutePic1, absolutePic2, differenceMaskPic, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + if (CompareMetadata) + { + throw new NotSupportedException("Metadata comparison is not implemented for SKBitmap inputs. https://github.com/mono/SkiaSharp/issues/1139 Use the overload with streams or filepath to get support for metadata comparison."); + } + + return Compare.CalcDiff(image1, image2, differenceMaskPic, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); } /// /// Calculates the difference between two in-memory images represented as objects. /// - /// - /// + /// + /// /// - public ICompareResult CalcDiff(FileStream pic1, FileStream pic2) + public ICompareResult CalcDiff(Stream image1, Stream image2) { - return Compare.CalcDiff(pic1, pic2, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + var compareResult = Compare.CalcDiff(image1, image2, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + + if (CompareMetadata) + { + if (image1.CanSeek) + { + image1.Position = 0; + } + + if (image2.CanSeek) + { + image2.Position = 0; + } + + var metadataDiff = MetadataComparer.CompareMetadata(image1, image2); + + if (image1.CanSeek) + { + image1.Position = 0; + } + + if (image2.CanSeek) + { + image2.Position = 0; + } + + return new CompareResultWithMetadata(compareResult, metadataDiff); + } + + return compareResult; } /// /// Calculates the difference between two images provided as file streams, using a specified mask image to focus the comparison. /// - /// - /// + /// + /// /// /// - public ICompareResult CalcDiff(FileStream pic1, FileStream pic2, SKBitmap maskImage) + public ICompareResult CalcDiff(Stream image1, Stream image2, SKBitmap maskImage) { - return Compare.CalcDiff(pic1, pic2, maskImage, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + var compareResult = Compare.CalcDiff(image1, image2, maskImage, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + + if (CompareMetadata) + { + var metadataDiff = MetadataComparer.CompareMetadata(image1, image2); + return new CompareResultWithMetadata(compareResult, metadataDiff); + } + + return compareResult; } /// /// Calculates a difference mask image that highlights the differences between two images located at the specified file paths. /// - /// - /// + /// + /// /// - public SKBitmap CalcDiffMaskImage(string absolutePathPic1, string absolutePathPic2) + public SKBitmap CalcDiffMaskImage(string pathImage1, string pathImage2) { - return Compare.CalcDiffMaskImage(absolutePathPic1, absolutePathPic2, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + return Compare.CalcDiffMaskImage(pathImage1, pathImage2, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); } /// /// Calculates a difference mask image that highlights the differences between two in-memory images represented as objects. /// - /// - /// + /// + /// /// - public SKBitmap CalcDiffMaskImage(SKBitmap absolutePic1, SKBitmap absolutePic2) + public SKBitmap CalcDiffMaskImage(SKBitmap image1, SKBitmap image2) { - return Compare.CalcDiffMaskImage(absolutePic1, absolutePic2, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + return Compare.CalcDiffMaskImage(image1, image2, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); } /// /// Calculates a difference mask image that highlights the differences between two images located at the specified file paths, /// - /// - /// - /// + /// + /// + /// /// - public SKBitmap CalcDiffMaskImage(string image1Path, string image2Path, string diffMask1Path) + public SKBitmap CalcDiffMaskImage(string pathImage1, string pathImage2, string pathDiffMask) { - return Compare.CalcDiffMaskImage(image1Path, image2Path, diffMask1Path, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + return Compare.CalcDiffMaskImage(pathImage1, pathImage2, pathDiffMask, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); } /// /// Calculates a difference mask image that highlights the differences between two images provided as file streams. /// - /// - /// + /// + /// /// - public SKBitmap CalcDiffMaskImage(FileStream image1Stream, FileStream image2Stream) + public SKBitmap CalcDiffMaskImage(Stream image1, Stream image2) { - return Compare.CalcDiffMaskImage(image1Stream, image2Stream, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + return Compare.CalcDiffMaskImage(image1, image2, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); } /// /// Calculates a difference mask image that highlights the differences between two images provided as file streams, /// - /// - /// - /// + /// + /// + /// /// - public SKBitmap CalcDiffMaskImage(FileStream image1Stream, FileStream image2Stream, FileStream diffMask1Stream) + public SKBitmap CalcDiffMaskImage(Stream image1, Stream image2, Stream diffMask) { - return Compare.CalcDiffMaskImage(image1Stream, image2Stream, diffMask1Stream, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + return Compare.CalcDiffMaskImage(image1, image2, diffMask, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); } /// @@ -168,55 +253,55 @@ public SKBitmap CalcDiffMaskImage(FileStream image1Stream, FileStream image2Stre /// /// /// - /// + /// /// - public SKBitmap CalcDiffMaskImage(SKBitmap image1, SKBitmap image2, SKBitmap diffMask1Image) + public SKBitmap CalcDiffMaskImage(SKBitmap image1, SKBitmap image2, SKBitmap diffMask) { - return Compare.CalcDiffMaskImage(image1, image2, diffMask1Image, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + return Compare.CalcDiffMaskImage(image1, image2, diffMask, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); } /// /// Checks if two images located at the specified file paths are identical, considering the configured options for resizing, /// - /// - /// + /// + /// /// - public bool ImagesAreEqual(string absolutePathActual, string absolutePathExpected) + public bool ImagesAreEqual(string pathImage1, string pathImage2) { - return Compare.ImagesAreEqual(absolutePathActual, absolutePathExpected, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + return Compare.ImagesAreEqual(pathImage1, pathImage2, ResizeOption, PixelColorShiftTolerance, TransparencyOptions, CompareMetadata); } /// /// Checks if two images provided as file streams are identical, considering the configured options for resizing, /// - /// - /// + /// + /// /// - public bool ImagesAreEqual(FileStream actual, FileStream expected) + public bool ImagesAreEqual(Stream image1, Stream image2) { - return Compare.ImagesAreEqual(actual, expected, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + return Compare.ImagesAreEqual(image1, image2, ResizeOption, PixelColorShiftTolerance, TransparencyOptions, CompareMetadata); } /// /// Checks if two in-memory images represented as objects are identical, considering the configured options for resizing, /// - /// - /// + /// + /// /// - public bool ImagesAreEqual(SKBitmap actual, SKBitmap expected) + public bool ImagesAreEqual(SKBitmap image1, SKBitmap image2) { - return Compare.ImagesAreEqual(actual, expected, ResizeOption, PixelColorShiftTolerance, TransparencyOptions); + return Compare.ImagesAreEqual(image1, image2, ResizeOption, PixelColorShiftTolerance, TransparencyOptions, CompareMetadata); } /// /// Checks if two images have the same dimensions (width and height). /// - /// - /// + /// + /// /// - public static bool ImagesHaveEqualSize(string absolutePathActual, string absolutePathExpected) + public static bool ImagesHaveEqualSize(string pathImage1, string pathImage2) { - return Compare.ImagesHaveEqualSize(absolutePathActual, absolutePathExpected); + return Compare.ImagesHaveEqualSize(pathImage1, pathImage2); } } } \ No newline at end of file diff --git a/SkiaSharpCompare/MetadataComparer.cs b/SkiaSharpCompare/MetadataComparer.cs new file mode 100644 index 0000000..c77e946 --- /dev/null +++ b/SkiaSharpCompare/MetadataComparer.cs @@ -0,0 +1,52 @@ +using SkiaSharpCompare.Metadata; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Codeuctivity.SkiaSharpCompare +{ + internal static class MetadataComparer + { + internal static Dictionary? CompareMetadata(string pathImageActual, string pathImageExpected) + { + var metaA = MetadataExtractorAdapter.Extract(pathImageActual); + var metaB = MetadataExtractorAdapter.Extract(pathImageExpected); + + return GetMetadataDifferences(metaA, metaB); + } + + internal static Dictionary? CompareMetadata(Stream imageActual, Stream imageExpected) + { + var metaA = MetadataExtractorAdapter.Extract(imageActual); + var metaB = MetadataExtractorAdapter.Extract(imageExpected); + + return GetMetadataDifferences(metaA, metaB); + } + + private static Dictionary? GetMetadataDifferences(IReadOnlyDictionary a, IReadOnlyDictionary b) + { + var diffs = new List<(string, string?, string?)>(); + var allKeys = new HashSet(a.Keys, StringComparer.OrdinalIgnoreCase); + allKeys.UnionWith(b.Keys); + + foreach (var key in allKeys.OrderBy(k => k, StringComparer.OrdinalIgnoreCase)) + { + a.TryGetValue(key, out var valA); + b.TryGetValue(key, out var valB); + + // Normalize whitespace for comparison + var normA = valA?.Trim() ?? string.Empty; + var normB = valB?.Trim() ?? string.Empty; + + if (!string.Equals(normA, normB, StringComparison.Ordinal)) + { + diffs.Add((key, valA, valB)); + } + } + + return diffs.ToDictionary(diff => diff.Item1, diff => (diff.Item2, diff.Item3) + ); + } + } +} \ No newline at end of file diff --git a/SkiaSharpCompare/MetadataExtractorAdapter.cs b/SkiaSharpCompare/MetadataExtractorAdapter.cs new file mode 100644 index 0000000..8fb10bf --- /dev/null +++ b/SkiaSharpCompare/MetadataExtractorAdapter.cs @@ -0,0 +1,76 @@ +using MetadataExtractor; +using System.Collections.Generic; +using System.IO; + +namespace SkiaSharpCompare.Metadata +{ + /// + /// Provides static methods for extracting image metadata and returning it as a normalized dictionary of tag names + /// and values. + /// + /// This class serves as an adapter for reading metadata from image files or streams using the + /// underlying MetadataExtractor library. All returned tag keys are normalized in the format "Directory:TagName" for + /// consistency. The class is thread-safe as it contains only stateless static methods. + public static class MetadataExtractorAdapter + { + /// + /// Extracts metadata tags from the specified image stream and returns them as a normalized, case-insensitive + /// dictionary. + /// + /// The caller is responsible for ensuring that the provided stream is positioned at the + /// start of the image data and remains open for the duration of the operation. The returned dictionary uses + /// case-insensitive keys. If the stream is not seekable, consider passing a seekable copy to avoid + /// errors. + /// A seekable stream containing image data from which to read metadata. The stream must support reading and + /// seeking. + /// A read-only dictionary containing metadata tag names and their corresponding values extracted from the + /// image. The dictionary is empty if no metadata is found. + public static IReadOnlyDictionary Extract(Stream imageStream) + { + var map = new Dictionary(System.StringComparer.OrdinalIgnoreCase); + + // MetadataExtractor reads from stream + var directories = ImageMetadataReader.ReadMetadata(imageStream); + return CreateResult(map, directories); + } + + /// + /// Extracts metadata from the specified image file and returns it as a read-only dictionary of tag names and + /// values. + /// + /// The path to the image file from which to extract metadata. Cannot be null or empty. + /// A read-only dictionary containing metadata tag names and their corresponding values. The dictionary is empty + /// if no metadata is found. + public static IReadOnlyDictionary Extract(string imagePath) + { + var map = new Dictionary(System.StringComparer.OrdinalIgnoreCase); + + // MetadataExtractor reads from stream + var directories = ImageMetadataReader.ReadMetadata(imagePath); + return CreateResult(map, directories); + } + + private static Dictionary CreateResult(Dictionary map, IReadOnlyList directories) + { + foreach (var directory in directories) + { + foreach (var tag in directory.Tags) + { + // Normalize key as "Directory:TagName" + var key = $"{directory.Name}:{tag.Name}"; + if (map.TryGetValue(key, out var existingValue)) + { + // If duplicate keys occur, append with a separator + map[key] = existingValue + "; " + (tag.Description ?? string.Empty); + } + else + { + map[key] = tag.Description ?? string.Empty; + } + } + } + + return map; + } + } +} \ No newline at end of file diff --git a/SkiaSharpCompare/SkiaSharpCompare.cs b/SkiaSharpCompare/SkiaSharpCompare.cs index afe9ba9..cc16dd8 100644 --- a/SkiaSharpCompare/SkiaSharpCompare.cs +++ b/SkiaSharpCompare/SkiaSharpCompare.cs @@ -1,5 +1,6 @@ using SkiaSharp; using System; +using System.Collections.Generic; using System.IO; namespace Codeuctivity.SkiaSharpCompare @@ -14,105 +15,138 @@ public static class Compare /// /// Is true if width and height of both images are equal /// - /// - /// + /// + /// /// - public static bool ImagesHaveEqualSize(string pathImageActual, string pathImageExpected) + public static bool ImagesHaveEqualSize(string pathImage1, string pathImage2) { - using var actualImage = SKBitmap.Decode(pathImageActual); - using var expectedImage = SKBitmap.Decode(pathImageExpected); - return ImagesHaveEqualSize(actualImage, expectedImage); + using var image1 = SKBitmap.Decode(pathImage1); + using var image2 = SKBitmap.Decode(pathImage2); + return ImagesHaveEqualSize(image1, image2); } /// /// Is true if width and height of both images are equal /// - /// - /// + /// + /// /// - public static bool ImagesHaveEqualSize(Stream actual, Stream expected) + public static bool ImagesHaveEqualSize(Stream image1, Stream image2) { - using var actualImage = SKBitmap.Decode(actual); - using var expectedImage = SKBitmap.Decode(expected); + ArgumentNullException.ThrowIfNull(image1); + ArgumentNullException.ThrowIfNull(image2); + using var actualImage = DecodeStream(image1); + using var expectedImage = DecodeStream(image2); return ImagesHaveEqualSize(actualImage, expectedImage); } /// /// Is true if width and height of both images are equal /// - /// - /// + /// + /// /// - public static bool ImagesHaveEqualSize(SKBitmap actualImage, SKBitmap expectedImage) + public static bool ImagesHaveEqualSize(SKBitmap image1, SKBitmap image2) { - return ImagesHaveSameDimension(actualImage, expectedImage); + return ImagesHaveSameDimension(image1, image2); } /// /// Compares two images for equivalence /// - /// - /// + /// + /// /// /// /// + /// If true, compares image metadata (EXIF, etc.) in addition to pixel data. /// True if every pixel of actual is equal to expected - public static bool ImagesAreEqual(string pathImageActual, string pathImageExpected, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) + public static bool ImagesAreEqual(string pathImage1, string pathImage2, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel, bool compareMetadata = false) { - using var actualImage = SKBitmap.Decode(pathImageActual); - using var expectedImage = SKBitmap.Decode(pathImageExpected); - return ImagesAreEqual(actualImage, expectedImage, resizeOption, pixelColorShiftTolerance, transparencyOptions); + if (compareMetadata) + { + var hasMetadataDiff = MetadataComparer.CompareMetadata(pathImage1, pathImage2)?.Count != 0; + + if (hasMetadataDiff) + { + return false; + } + } + + using var image1 = SKBitmap.Decode(pathImage1); + using var image2 = SKBitmap.Decode(pathImage2); + return ImagesAreEqual(image1, image2, resizeOption, pixelColorShiftTolerance, transparencyOptions); } /// /// Compares two images for equivalence /// - /// - /// + /// + /// /// /// /// + /// If true, compares image metadata (EXIF, etc.) in addition to pixel data. /// True if every pixel of actual is equal to expected - public static bool ImagesAreEqual(Stream actual, Stream expected, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) + public static bool ImagesAreEqual(Stream image1, Stream image2, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel, bool compareMetadata = false) { - using var actualImage = SKBitmap.Decode(actual); - using var expectedImage = SKBitmap.Decode(expected); - return ImagesAreEqual(actualImage, expectedImage, resizeOption, pixelColorShiftTolerance, transparencyOptions); + if (compareMetadata) + { + var hasMetadataDiff = MetadataComparer.CompareMetadata(image1, image2)?.Count != 0; + + if (hasMetadataDiff) + { + return false; + } + } + + ArgumentNullException.ThrowIfNull(image1); + ArgumentNullException.ThrowIfNull(image2); + + using var decodedImage1 = DecodeStream(image1); + using var decodedImage2 = DecodeStream(image2); + return ImagesAreEqual(decodedImage1, decodedImage2, resizeOption, pixelColorShiftTolerance, transparencyOptions); } /// /// Compares two images for equivalence /// - /// - /// + /// + /// /// /// /// + /// If true, compares image metadata (EXIF, etc.) in addition to pixel data. /// True if every pixel of actual is equal to expected - public static bool ImagesAreEqual(SKBitmap actual, SKBitmap expected, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) + public static bool ImagesAreEqual(SKBitmap image1, SKBitmap image2, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel, bool compareMetadata = false) { - ArgumentNullException.ThrowIfNull(actual); - ArgumentNullException.ThrowIfNull(expected); + if (compareMetadata) + { + throw new NotSupportedException("Metadata comparison is not implemented for SKBitmap inputs. https://github.com/mono/SkiaSharp/issues/1139 Use the overload with streams or filepath to get support for metadata comparison."); + } + + ArgumentNullException.ThrowIfNull(image1); + ArgumentNullException.ThrowIfNull(image2); - if (resizeOption == ResizeOption.DontResize && !ImagesHaveSameDimension(actual, expected)) + if (resizeOption == ResizeOption.DontResize && !ImagesHaveSameDimension(image1, image2)) { return false; } - if (resizeOption == ResizeOption.DontResize || ImagesHaveSameDimension(actual, expected)) + if (resizeOption == ResizeOption.DontResize || ImagesHaveSameDimension(image1, image2)) { - for (var x = 0; x < actual.Width; x++) + for (var x = 0; x < image1.Width; x++) { - for (var y = 0; y < actual.Height; y++) + for (var y = 0; y < image1.Height; y++) { - if (transparencyOptions == TransparencyOptions.CompareAlphaChannel && pixelColorShiftTolerance == 0 && !actual.GetPixel(x, y).Equals(expected.GetPixel(x, y))) + if (transparencyOptions == TransparencyOptions.CompareAlphaChannel && pixelColorShiftTolerance == 0 && !image1.GetPixel(x, y).Equals(image2.GetPixel(x, y))) { return false; } else { - var actualPixel = actual.GetPixel(x, y); - var expectedPixel = expected.GetPixel(x, y); + var actualPixel = image1.GetPixel(x, y); + var expectedPixel = image2.GetPixel(x, y); var a = 0; if (transparencyOptions == TransparencyOptions.CompareAlphaChannel) @@ -136,7 +170,7 @@ public static bool ImagesAreEqual(SKBitmap actual, SKBitmap expected, ResizeOpti return true; } - var grown = GrowToSameDimension(actual, expected); + var grown = GrowToSameDimension(image1, image2); try { return ImagesAreEqual(grown.Item1, grown.Item2, ResizeOption.DontResize); @@ -151,54 +185,117 @@ public static bool ImagesAreEqual(SKBitmap actual, SKBitmap expected, ResizeOpti /// /// Calculates ICompareResult expressing the amount of difference of both images /// + /// + /// + /// + /// + /// + /// Mean and absolute pixel error + public static ICompareResult CalcDiff(string pathImage1, string pathImage2, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.IgnoreAlphaChannel) + { + using var actual = SKBitmap.Decode(pathImage1); + using var expected = SKBitmap.Decode(pathImage2); + return CalcDiffInternal(actual, expected, null, resizeOption, pixelColorShiftTolerance, transparencyOptions); + } + + /// + /// Calculates ICompareResult expressing the amount of difference of both images + /// + /// + /// + /// + /// + /// + /// Mean and absolute pixel error + public static ICompareResult CalcDiff(Stream image1, Stream image2, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.IgnoreAlphaChannel) + { + ArgumentNullException.ThrowIfNull(image1); + ArgumentNullException.ThrowIfNull(image2); + + using var actual = DecodeStream(image1); + using var expected = DecodeStream(image2); + return CalcDiffInternal(actual, expected, null, resizeOption, pixelColorShiftTolerance, transparencyOptions); + } + + /// + /// Calculates ICompareResult expressing the amount of difference of both images using a mask image for tolerated difference between the two images + /// /// /// + /// /// /// /// /// Mean and absolute pixel error - public static ICompareResult CalcDiff(string pathActualImage, string pathExpectedImage, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.IgnoreAlphaChannel) + public static ICompareResult CalcDiff(string pathActualImage, string pathExpectedImage, string pathMaskImage, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) { - using var actual = SKBitmap.Decode(pathActualImage); - using var expected = SKBitmap.Decode(pathExpectedImage); - return CalcDiff(actual, expected, resizeOption, pixelColorShiftTolerance, transparencyOptions); + using var image1 = SKBitmap.Decode(pathActualImage); + using var image2 = SKBitmap.Decode(pathExpectedImage); + using var mask = SKBitmap.Decode(pathMaskImage); + return CalcDiff(image1, image2, mask, resizeOption, pixelColorShiftTolerance, transparencyOptions); } /// /// Calculates ICompareResult expressing the amount of difference of both images /// - /// - /// + /// + /// + /// /// /// /// - /// Mean and absolute pixel error - public static ICompareResult CalcDiff(Stream actualImage, Stream expectedImage, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.IgnoreAlphaChannel) + /// + public static ICompareResult CalcDiff(Stream image1, Stream image2, SKBitmap maskImage, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) + { + ArgumentNullException.ThrowIfNull(image1); + ArgumentNullException.ThrowIfNull(image2); + ArgumentNullException.ThrowIfNull(maskImage); + + using var actual = DecodeStream(image1); + using var expected = DecodeStream(image2); + return CalcDiff(actual, expected, maskImage, resizeOption, pixelColorShiftTolerance, transparencyOptions); + } + + /// + /// Compares two images for equivalence + /// + /// + /// + /// + /// + /// + /// + /// + public static ICompareResult CalcDiff(SKBitmap image1, SKBitmap image2, SKBitmap maskImage, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) { - using var actual = SKBitmap.Decode(actualImage); - using var expected = SKBitmap.Decode(expectedImage); - return CalcDiff(actual, expected, resizeOption, pixelColorShiftTolerance, transparencyOptions); + ArgumentNullException.ThrowIfNull(image1); + ArgumentNullException.ThrowIfNull(image2); + ArgumentNullException.ThrowIfNull(maskImage); + + var metadataDifference = new Dictionary(); + return CalcDiff(image1, image2, maskImage, metadataDifference, resizeOption, pixelColorShiftTolerance, transparencyOptions); } /// /// Calculates ICompareResult expressing the amount of difference of both images /// - /// - /// + /// + /// + /// /// /// /// /// Mean and absolute pixel error - public static ICompareResult CalcDiff(SKBitmap actual, SKBitmap expected, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.IgnoreAlphaChannel) + internal static ICompareResult CalcDiffInternal(SKBitmap image1, SKBitmap image2, Dictionary? metadataDifference, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.IgnoreAlphaChannel) { - var imagesHaveSameDimension = ImagesHaveSameDimension(actual, expected); + var imagesHaveSameDimension = ImagesHaveSameDimension(image1, image2); if (resizeOption == ResizeOption.Resize && !imagesHaveSameDimension) { - var grown = GrowToSameDimension(actual, expected); + var grown = GrowToSameDimension(image1, image2); try { - return CalcDiff(grown.Item1, grown.Item2, ResizeOption.DontResize, pixelColorShiftTolerance, transparencyOptions); + return CalcDiffInternal(grown.Item1, grown.Item2, metadataDifference, ResizeOption.DontResize, pixelColorShiftTolerance, transparencyOptions); } finally { @@ -207,21 +304,21 @@ public static ICompareResult CalcDiff(SKBitmap actual, SKBitmap expected, Resize } } - if (!ImagesHaveSameDimension(actual, expected)) + if (!ImagesHaveSameDimension(image1, image2)) { throw new SkiaSharpCompareException(sizeDiffersExceptionMessage); } - var quantity = actual.Width * actual.Height; + var quantity = image1.Width * image1.Height; var absoluteError = 0; var pixelErrorCount = 0; - for (var x = 0; x < actual.Width; x++) + for (var x = 0; x < image1.Width; x++) { - for (var y = 0; y < actual.Height; y++) + for (var y = 0; y < image1.Height; y++) { - var actualPixel = actual.GetPixel(x, y); - var expectedPixel = expected.GetPixel(x, y); + var actualPixel = image1.GetPixel(x, y); + var expectedPixel = image2.GetPixel(x, y); var r = Math.Abs(expectedPixel.Red - actualPixel.Red); var g = Math.Abs(expectedPixel.Green - actualPixel.Green); @@ -230,72 +327,51 @@ public static ICompareResult CalcDiff(SKBitmap actual, SKBitmap expected, Resize if (transparencyOptions == TransparencyOptions.CompareAlphaChannel) { var a = Math.Abs(expectedPixel.Alpha - actualPixel.Alpha); - sum = sum + a; + sum += a; } - absoluteError = absoluteError + (sum > pixelColorShiftTolerance ? sum : 0); + absoluteError += (sum > pixelColorShiftTolerance ? sum : 0); pixelErrorCount += (sum > pixelColorShiftTolerance) ? 1 : 0; } } var meanError = (double)absoluteError / quantity; var pixelErrorPercentage = (double)pixelErrorCount / quantity * 100; - return new CompareResult(absoluteError, meanError, pixelErrorCount, pixelErrorPercentage); + return new CompareResult(absoluteError, meanError, pixelErrorCount, pixelErrorPercentage, metadataDifference); } - /// - /// Calculates ICompareResult expressing the amount of difference of both images using a mask image for tolerated difference between the two images - /// - /// - /// - /// - /// - /// - /// - /// Mean and absolute pixel error - public static ICompareResult CalcDiff(string pathActualImage, string pathExpectedImage, string pathMaskImage, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) + private static SKBitmap DecodeStream(Stream stream) { - using var actual = SKBitmap.Decode(pathActualImage); - using var expected = SKBitmap.Decode(pathExpectedImage); - using var mask = SKBitmap.Decode(pathMaskImage); - return CalcDiff(actual, expected, mask, resizeOption, pixelColorShiftTolerance, transparencyOptions); - } + // Addressing https://github.com/mono/SkiaSharp/issues/2263 - /// - /// Calculates ICompareResult expressing the amount of difference of both images - /// - /// - /// - /// - /// - /// - /// - /// - public static ICompareResult CalcDiff(Stream actualImage, Stream expectedImage, SKBitmap maskImage, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) - { - using var actual = SKBitmap.Decode(actualImage); - using var expected = SKBitmap.Decode(expectedImage); - return CalcDiff(actual, expected, maskImage, resizeOption, pixelColorShiftTolerance, transparencyOptions); + using var copy = new MemoryStream(); + + if (stream.CanSeek) + { + stream.Position = 0; + } + + stream.CopyTo(copy); + + if (stream.CanSeek) + { + stream.Position = 0; + } + + var bytes = copy.ToArray(); + using var skData = SKData.CreateCopy(bytes); + + return SKBitmap.Decode(skData); } - /// - /// Compares two images for equivalence - /// - /// - /// - /// - /// - /// - /// - /// - public static ICompareResult CalcDiff(SKBitmap actual, SKBitmap expected, SKBitmap maskImage, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) + internal static ICompareResult CalcDiff(SKBitmap image1, SKBitmap image2, SKBitmap maskImage, Dictionary metadataDifference, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) { ArgumentNullException.ThrowIfNull(maskImage); - var imagesHaveSameDimension = ImagesHaveSameDimension(actual, expected) && ImagesHaveSameDimension(actual, maskImage); + var imagesHaveSameDimension = ImagesHaveSameDimension(image1, image2) && ImagesHaveSameDimension(image1, maskImage); if (resizeOption == ResizeOption.Resize && !imagesHaveSameDimension) { - var grown = GrowToSameDimension(actual, expected, maskImage); + var grown = GrowToSameDimension(image1, image2, maskImage); try { return CalcDiff(grown.Item1, grown.Item2, grown.Item3, ResizeOption.DontResize, pixelColorShiftTolerance, transparencyOptions); @@ -313,17 +389,17 @@ public static ICompareResult CalcDiff(SKBitmap actual, SKBitmap expected, SKBitm throw new SkiaSharpCompareException(sizeDiffersExceptionMessage); } - var quantity = actual.Width * actual.Height; + var quantity = image1.Width * image1.Height; var absoluteError = 0; var pixelErrorCount = 0; - for (var x = 0; x < actual.Width; x++) + for (var x = 0; x < image1.Width; x++) { - for (var y = 0; y < actual.Height; y++) + for (var y = 0; y < image1.Height; y++) { var maskImagePixel = maskImage.GetPixel(x, y); - var actualPixel = actual.GetPixel(x, y); - var expectedPixel = expected.GetPixel(x, y); + var actualPixel = image1.GetPixel(x, y); + var expectedPixel = image2.GetPixel(x, y); var r = Math.Abs(expectedPixel.Red - actualPixel.Red); var g = Math.Abs(expectedPixel.Green - actualPixel.Green); @@ -355,13 +431,13 @@ public static ICompareResult CalcDiff(SKBitmap actual, SKBitmap expected, SKBitm } } - absoluteError = absoluteError + (error > pixelColorShiftTolerance ? error : 0); + absoluteError += (error > pixelColorShiftTolerance ? error : 0); pixelErrorCount += error > pixelColorShiftTolerance ? 1 : 0; } } var meanError = (double)absoluteError / quantity; var pixelErrorPercentage = (double)pixelErrorCount / quantity * 100; - return new CompareResult(absoluteError, meanError, pixelErrorCount, pixelErrorPercentage); + return new CompareResult(absoluteError, meanError, pixelErrorCount, pixelErrorPercentage, metadataDifference); } private static bool ImagesHaveSameDimension(SKBitmap actual, SKBitmap expected) @@ -375,33 +451,33 @@ private static bool ImagesHaveSameDimension(SKBitmap actual, SKBitmap expected) /// /// Creates a diff mask image of two images /// - /// - /// + /// + /// /// /// /// /// Image representing diff, black means no diff between actual image and expected image, white means max diff - public static SKBitmap CalcDiffMaskImage(string pathActualImage, string pathExpectedImage, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) + public static SKBitmap CalcDiffMaskImage(string pathImage1, string pathImage2, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) { - using var actual = SKBitmap.Decode(pathActualImage); - using var expected = SKBitmap.Decode(pathExpectedImage); + using var actual = SKBitmap.Decode(pathImage1); + using var expected = SKBitmap.Decode(pathImage2); return CalcDiffMaskImage(actual, expected, resizeOption, pixelColorShiftTolerance, transparencyOptions); } /// /// Creates a diff mask image of two images /// - /// - /// + /// + /// /// /// /// /// /// Image representing diff, black means no diff between actual image and expected image, white means max diff - public static SKBitmap CalcDiffMaskImage(string pathActualImage, string pathExpectedImage, string pathMaskImage, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) + public static SKBitmap CalcDiffMaskImage(string pathImage1, string pathImage2, string pathMaskImage, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) { - using var actual = SKBitmap.Decode(pathActualImage); - using var expected = SKBitmap.Decode(pathExpectedImage); + using var actual = SKBitmap.Decode(pathImage1); + using var expected = SKBitmap.Decode(pathImage2); using var mask = SKBitmap.Decode(pathMaskImage); return CalcDiffMaskImage(actual, expected, mask, resizeOption, pixelColorShiftTolerance, transparencyOptions); } @@ -409,97 +485,58 @@ public static SKBitmap CalcDiffMaskImage(string pathActualImage, string pathExpe /// /// Creates a diff mask image of two images /// - /// - /// + /// + /// /// /// /// /// Image representing diff, black means no diff between actual image and expected image, white means max diff - public static SKBitmap CalcDiffMaskImage(Stream actualImage, Stream expectedImage, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) + public static SKBitmap CalcDiffMaskImage(Stream image1, Stream image2, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) { - ArgumentNullException.ThrowIfNull(actualImage); - - ArgumentNullException.ThrowIfNull(expectedImage); + ArgumentNullException.ThrowIfNull(image1); + ArgumentNullException.ThrowIfNull(image2); - if (actualImage.CanSeek) - { - actualImage.Position = 0; - } - if (expectedImage.CanSeek) - { - expectedImage.Position = 0; - } + using var actual = DecodeStream(image1); + using var expected = DecodeStream(image2); - using var actualImageCopy = new MemoryStream(); - using var expectedImageCopy = new MemoryStream(); - actualImage.CopyTo(actualImageCopy); - expectedImage.CopyTo(expectedImageCopy); - actualImageCopy.Position = 0; - expectedImageCopy.Position = 0; - using var actual = SKBitmap.Decode(actualImageCopy); - using var expected = SKBitmap.Decode(expectedImageCopy); return CalcDiffMaskImage(actual, expected, resizeOption, pixelColorShiftTolerance, transparencyOptions); } /// /// Creates a diff mask image of two images /// - /// - /// + /// + /// /// /// /// /// /// Image representing diff, black means no diff between actual image and expected image, white means max diff - public static SKBitmap CalcDiffMaskImage(Stream actualImage, Stream expectedImage, Stream maskImage, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) + public static SKBitmap CalcDiffMaskImage(Stream image1, Stream image2, Stream maskImage, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) { - ArgumentNullException.ThrowIfNull(actualImage); - - ArgumentNullException.ThrowIfNull(expectedImage); - + ArgumentNullException.ThrowIfNull(image1); + ArgumentNullException.ThrowIfNull(image2); ArgumentNullException.ThrowIfNull(maskImage); - if (actualImage.CanSeek) - { - actualImage.Position = 0; - } - if (expectedImage.CanSeek) - { - expectedImage.Position = 0; - } - - if (maskImage.CanSeek) - { - maskImage.Position = 0; - } + using var actual = DecodeStream(image1); + using var expected = DecodeStream(image2); + using var mask = DecodeStream(maskImage); - using var actualImageCopy = new MemoryStream(); - using var expectedImageCopy = new MemoryStream(); - using var maskCopy = new MemoryStream(); - actualImage.CopyTo(actualImageCopy); - expectedImage.CopyTo(expectedImageCopy); - maskImage.CopyTo(maskCopy); - actualImageCopy.Position = 0; - expectedImageCopy.Position = 0; - maskCopy.Position = 0; - using var actual = SKBitmap.Decode(actualImageCopy); - using var expected = SKBitmap.Decode(expectedImageCopy); - using var mask = SKBitmap.Decode(maskCopy); return CalcDiffMaskImage(actual, expected, mask, resizeOption, pixelColorShiftTolerance, transparencyOptions); } /// /// Creates a diff mask image of two images /// - /// - /// + /// + /// /// /// /// /// Image representing diff, black means no diff between actual image and expected image, white means max diff - public static SKBitmap CalcDiffMaskImage(SKBitmap actual, SKBitmap expected, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) + public static SKBitmap CalcDiffMaskImage(SKBitmap image1, SKBitmap image2, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) { - var imagesHAveSameDimension = ImagesHaveSameDimension(actual, expected); + var imagesHAveSameDimension = ImagesHaveSameDimension(image1, image2); if (resizeOption == ResizeOption.DontResize && !imagesHAveSameDimension) { @@ -508,14 +545,14 @@ public static SKBitmap CalcDiffMaskImage(SKBitmap actual, SKBitmap expected, Res if (imagesHAveSameDimension) { - var maskImage = new SKBitmap(actual.Width, actual.Height); + var maskImage = new SKBitmap(image1.Width, image1.Height); - for (var x = 0; x < actual.Width; x++) + for (var x = 0; x < image1.Width; x++) { - for (var y = 0; y < actual.Height; y++) + for (var y = 0; y < image1.Height; y++) { - var actualPixel = actual.GetPixel(x, y); - var expectedPixel = expected.GetPixel(x, y); + var actualPixel = image1.GetPixel(x, y); + var expectedPixel = image2.GetPixel(x, y); var red = (byte)Math.Abs(actualPixel.Red - expectedPixel.Red); var green = (byte)Math.Abs(actualPixel.Green - expectedPixel.Green); @@ -581,7 +618,7 @@ public static SKBitmap CalcDiffMaskImage(SKBitmap actual, SKBitmap expected, Res return maskImage; } - var grown = GrowToSameDimension(actual, expected); + var grown = GrowToSameDimension(image1, image2); try { return CalcDiffMaskImage(grown.Item1, grown.Item2, ResizeOption.DontResize, pixelColorShiftTolerance, transparencyOptions); @@ -596,18 +633,18 @@ public static SKBitmap CalcDiffMaskImage(SKBitmap actual, SKBitmap expected, Res /// /// Creates a diff mask image of two images /// - /// - /// + /// + /// /// /// /// /// /// Image representing diff, black means no diff between actual image and expected image, white means max diff - public static SKBitmap CalcDiffMaskImage(SKBitmap actual, SKBitmap expected, SKBitmap mask, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) + public static SKBitmap CalcDiffMaskImage(SKBitmap image1, SKBitmap image2, SKBitmap mask, ResizeOption resizeOption = ResizeOption.DontResize, int pixelColorShiftTolerance = 0, TransparencyOptions transparencyOptions = TransparencyOptions.CompareAlphaChannel) { ArgumentNullException.ThrowIfNull(mask); - var imagesHaveSameDimension = ImagesHaveSameDimension(actual, expected) && ImagesHaveSameDimension(actual, mask); + var imagesHaveSameDimension = ImagesHaveSameDimension(image1, image2) && ImagesHaveSameDimension(image1, mask); if (resizeOption == ResizeOption.DontResize && !imagesHaveSameDimension) { @@ -616,14 +653,14 @@ public static SKBitmap CalcDiffMaskImage(SKBitmap actual, SKBitmap expected, SKB if (imagesHaveSameDimension) { - var maskImage = new SKBitmap(actual.Width, actual.Height); + var maskImage = new SKBitmap(image1.Width, image1.Height); - for (var x = 0; x < actual.Width; x++) + for (var x = 0; x < image1.Width; x++) { - for (var y = 0; y < actual.Height; y++) + for (var y = 0; y < image1.Height; y++) { - var actualPixel = actual.GetPixel(x, y); - var expectedPixel = expected.GetPixel(x, y); + var actualPixel = image1.GetPixel(x, y); + var expectedPixel = image2.GetPixel(x, y); var maskPixel = mask.GetPixel(x, y); var redDiff = Math.Abs(actualPixel.Red - expectedPixel.Red); @@ -692,7 +729,7 @@ public static SKBitmap CalcDiffMaskImage(SKBitmap actual, SKBitmap expected, SKB return maskImage; } - var grown = GrowToSameDimension(actual, expected, mask); + var grown = GrowToSameDimension(image1, image2, mask); try { return CalcDiffMaskImage(grown.Item1, grown.Item2, grown.Item3, ResizeOption.DontResize, pixelColorShiftTolerance, transparencyOptions); @@ -705,26 +742,26 @@ public static SKBitmap CalcDiffMaskImage(SKBitmap actual, SKBitmap expected, SKB } } - private static (SKBitmap, SKBitmap) GrowToSameDimension(SKBitmap actual, SKBitmap expected) + private static (SKBitmap, SKBitmap) GrowToSameDimension(SKBitmap image1, SKBitmap image2) { - var biggestWidth = actual.Width > expected.Width ? actual.Width : expected.Width; - var biggestHeight = actual.Height > expected.Height ? actual.Height : expected.Height; + var biggestWidth = image1.Width > image2.Width ? image1.Width : image2.Width; + var biggestHeight = image1.Height > image2.Height ? image1.Height : image2.Height; var skSize = new SKSizeI(biggestWidth, biggestHeight); - var grownExpected = expected.Resize(skSize, SKSamplingOptions.Default); - var grownActual = actual.Resize(skSize, SKSamplingOptions.Default); + var grownExpected = image2.Resize(skSize, SKSamplingOptions.Default); + var grownActual = image1.Resize(skSize, SKSamplingOptions.Default); return (grownActual, grownExpected); } - private static (SKBitmap, SKBitmap, SKBitmap) GrowToSameDimension(SKBitmap actual, SKBitmap expected, SKBitmap mask) + private static (SKBitmap, SKBitmap, SKBitmap) GrowToSameDimension(SKBitmap image1, SKBitmap image2, SKBitmap mask) { - var biggestWidth = actual.Width > expected.Width ? actual.Width : expected.Width; + var biggestWidth = image1.Width > image2.Width ? image1.Width : image2.Width; biggestWidth = biggestWidth > mask.Width ? biggestWidth : mask.Width; - var biggestHeight = actual.Height > expected.Height ? actual.Height : expected.Height; + var biggestHeight = image1.Height > image2.Height ? image1.Height : image2.Height; biggestHeight = biggestHeight > mask.Height ? biggestHeight : mask.Height; var skSize = new SKSizeI(biggestWidth, biggestHeight); - var grownExpected = expected.Resize(skSize, SKSamplingOptions.Default); - var grownActual = actual.Resize(skSize, SKSamplingOptions.Default); + var grownExpected = image2.Resize(skSize, SKSamplingOptions.Default); + var grownActual = image1.Resize(skSize, SKSamplingOptions.Default); var grownMask = mask.Resize(skSize, SKSamplingOptions.Default); return (grownActual, grownExpected, grownMask); diff --git a/SkiaSharpCompare/SkiaSharpCompare.csproj b/SkiaSharpCompare/SkiaSharpCompare.csproj index b72b88d..339a4c3 100644 --- a/SkiaSharpCompare/SkiaSharpCompare.csproj +++ b/SkiaSharpCompare/SkiaSharpCompare.csproj @@ -38,16 +38,16 @@ Codeuctivity.SkiaSharpCompare Codeuctivity.SkiaSharpCompare true - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/SkiaSharpCompareTestNunit/SkiaSharpCompareMetaData_PathTests.cs b/SkiaSharpCompareTestNunit/SkiaSharpCompareMetaData_PathTests.cs new file mode 100644 index 0000000..8692ac4 --- /dev/null +++ b/SkiaSharpCompareTestNunit/SkiaSharpCompareMetaData_PathTests.cs @@ -0,0 +1,95 @@ +using Codeuctivity.SkiaSharpCompare; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace SkiaSharpCompareTestNunit +{ + internal class SkiaSharpCompareMetaData_PathTests + { + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, true, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, false, true)] + [TestCase(TestFiles.imageWithoutGpsMetadata, TestFiles.imageWithGpsMetadata, true, false)] + [TestCase(TestFiles.imageWithoutGpsMetadata, TestFiles.imageWithGpsMetadata, false, true)] + public void ImagesAreEqual_SamePixelComparedByMetadataShouldReturnResult(string pic1Path, string pic2Path, bool shouldCompareMetadata, bool expectOutcomeComparisonOfImage) + { + var pic1 = Path.Combine(AppContext.BaseDirectory, pic1Path); + var pic2 = Path.Combine(AppContext.BaseDirectory, pic2Path); + + var sut = new ImageCompare(compareMetadata: shouldCompareMetadata); + Assert.That(sut.ImagesAreEqual(pic1, pic2), Is.EqualTo(expectOutcomeComparisonOfImage)); + } + + [Test] + public void CalcDiff_SamePixelComparedByMetadataShouldReturnResult_Null() + { + var pic1 = Path.Combine(AppContext.BaseDirectory, TestFiles.imageWithGpsMetadata); + var pic2 = Path.Combine(AppContext.BaseDirectory, TestFiles.imageWithoutGpsMetadata); + + var sut = new ImageCompare(compareMetadata: false); + var actual = sut.CalcDiff(pic1, pic2); + + Assert.That(actual.MetadataDifferences, Is.Null); + Assert.That(actual.PixelErrorCount, Is.Zero); + } + + [Test] + public void CalcDiff_SamePixelComparedByMetadataShouldReturnEmptyResult() + { + var pic1 = Path.Combine(AppContext.BaseDirectory, TestFiles.imageWithoutGpsMetadata); + var pic2 = Path.Combine(AppContext.BaseDirectory, TestFiles.imageWithoutGpsMetadata); + + var sut = new ImageCompare(compareMetadata: true); + var actual = sut.CalcDiff(pic1, pic2); + + Assert.That(actual.MetadataDifferences, Is.Empty); + Assert.That(actual.PixelErrorCount, Is.Zero); + } + + [Test] + [SetCulture("en-US")] + public void CalcDiff_SamePixelComparedByMetadataShouldReturnCollectionOfMetadataThatDiffers() + { + var pic1 = Path.Combine(AppContext.BaseDirectory, TestFiles.imageWithoutGpsMetadata); + var pic2 = Path.Combine(AppContext.BaseDirectory, TestFiles.imageWithGpsMetadata); + + var sut = new ImageCompare(compareMetadata: true); + var actual = sut.CalcDiff(pic1, pic2); + + // Output actual and expected to test run logs for easier diagnosis + TestContext.WriteLine($"Actual image: {pic1}"); + TestContext.WriteLine($"Expected image: {pic2}"); + TestContext.WriteLine($"Actual MetadataDifferences: {FormatMetadata(actual.MetadataDifferences)}"); + + var expected = new Dictionary + { + { "File:File Name", (Path.GetFileName(pic1), Path.GetFileName(pic2)) }, + { "GPS:GPS Altitude", ("0 metres", "201.62 metres") }, + { "GPS:GPS Date Stamp", ("", "2025:01:03") }, + { "GPS:GPS Latitude", ("0° 0' 0\"", "48° 12' 7.17\"") }, + { "GPS:GPS Latitude Ref", ("", "N") }, + { "GPS:GPS Longitude", ("0° 0' 0\"", "16° 24' 7.53\"") }, + { "GPS:GPS Longitude Ref", ("", "E") }, + { "GPS:GPS Processing Method", ("", "GPS") }, + { "GPS:GPS Time-Stamp", ("00:00:00.000 UTC", "14:41:20.000 UTC") } + }; + + TestContext.WriteLine($"Expected MetadataDifferences: {FormatMetadata(expected)}"); + + Assert.That(actual.MetadataDifferences, Is.EqualTo(expected)); + Assert.That(actual.PixelErrorCount, Is.Zero); + } + + private static string FormatMetadata(Dictionary? metadata) + { + if (metadata is null) + { + return "null"; + } + + return string.Join("; ", metadata.Select(kvp => $"{kvp.Key}=[{kvp.Value.ValueA ?? ""} | {kvp.Value.ValueB ?? ""}]")); + } + } +} \ No newline at end of file diff --git a/SkiaSharpCompareTestNunit/SkiaSharpCompareMetaData_SkBitmap.cs b/SkiaSharpCompareTestNunit/SkiaSharpCompareMetaData_SkBitmap.cs new file mode 100644 index 0000000..5a3386a --- /dev/null +++ b/SkiaSharpCompareTestNunit/SkiaSharpCompareMetaData_SkBitmap.cs @@ -0,0 +1,84 @@ +using Codeuctivity.SkiaSharpCompare; +using NUnit.Framework; +using SkiaSharp; +using System; +using System.IO; + +namespace SkiaSharpCompareTestNunit +{ + internal class SkiaSharpCompareMetaData_SkBitmap + { + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32)] + [TestCase(TestFiles.imageWithoutGpsMetadata, TestFiles.imageWithGpsMetadata)] + public void ImagesAreEqual_SamePixelComparedByMetadataShouldReturnResult(string pic1Path, string pic2Path) + { + var pic1 = SKBitmap.Decode(Path.Combine(AppContext.BaseDirectory, pic1Path)); + var pic2 = SKBitmap.Decode(Path.Combine(AppContext.BaseDirectory, pic2Path)); + + var sut = new ImageCompare(compareMetadata: false); + Assert.That(sut.ImagesAreEqual(pic1, pic2)); + } + + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32)] + [TestCase(TestFiles.imageWithoutGpsMetadata, TestFiles.imageWithGpsMetadata)] + public void ImagesAreEqual_SamePixelComparedByMetadataShouldThrows(string pic1Path, string pic2Path) + { + var pic1 = SKBitmap.Decode(Path.Combine(AppContext.BaseDirectory, pic1Path)); + var pic2 = SKBitmap.Decode(Path.Combine(AppContext.BaseDirectory, pic2Path)); + + var sut = new ImageCompare(compareMetadata: true); + + var ex = Assert.Throws(() => + { + sut.ImagesAreEqual(pic1, pic2); + }); + + Assert.That(ex?.Message, Is.EqualTo("Metadata comparison is not implemented for SKBitmap inputs. https://github.com/mono/SkiaSharp/issues/1139 Use the overload with streams or filepath to get support for metadata comparison.")); + } + + [Test] + public void CalcDiff_SamePixelComparedByMetadataShouldReturnResult_Null() + { + var pic1 = SKBitmap.Decode(Path.Combine(AppContext.BaseDirectory, TestFiles.imageWithGpsMetadata)); + var pic2 = SKBitmap.Decode(Path.Combine(AppContext.BaseDirectory, TestFiles.imageWithoutGpsMetadata)); + + var sut = new ImageCompare(compareMetadata: false); + var actual = sut.CalcDiff(pic1, pic2); + + Assert.That(actual.MetadataDifferences, Is.Null); + Assert.That(actual.PixelErrorCount, Is.Zero); + } + + [Test] + public void CalcDiff_SamePixelComparedByMetadataShouldReturnEmptyResult() + { + var pic1 = SKBitmap.Decode(Path.Combine(AppContext.BaseDirectory, TestFiles.imageWithoutGpsMetadata)); + var pic2 = SKBitmap.Decode(Path.Combine(AppContext.BaseDirectory, TestFiles.imageWithoutGpsMetadata)); + + var sut = new ImageCompare(compareMetadata: true); + var ex = Assert.Throws(() => + { + sut.CalcDiff(pic1, pic2); + }); + + Assert.That(ex?.Message, Is.EqualTo("Metadata comparison is not implemented for SKBitmap inputs. https://github.com/mono/SkiaSharp/issues/1139 Use the overload with streams or filepath to get support for metadata comparison.")); + } + + [Test] + [SetCulture("en-US")] + public void CalcDiff_SamePixelComparedByMetadataShouldReturnCollectionOfMetadataThatDiffers() + { + var pic1 = SKBitmap.Decode(Path.Combine(AppContext.BaseDirectory, TestFiles.imageWithoutGpsMetadata)); + var pic2 = SKBitmap.Decode(Path.Combine(AppContext.BaseDirectory, TestFiles.imageWithGpsMetadata)); + + var sut = new ImageCompare(compareMetadata: true); + + var ex = Assert.Throws(() => + { + sut.CalcDiff(pic1, pic2); + }); + + Assert.That(ex?.Message, Is.EqualTo("Metadata comparison is not implemented for SKBitmap inputs. https://github.com/mono/SkiaSharp/issues/1139 Use the overload with streams or filepath to get support for metadata comparison.")); + } + } +} \ No newline at end of file diff --git a/SkiaSharpCompareTestNunit/SkiaSharpCompareMetaData_StreamTests.cs b/SkiaSharpCompareTestNunit/SkiaSharpCompareMetaData_StreamTests.cs new file mode 100644 index 0000000..5490fc2 --- /dev/null +++ b/SkiaSharpCompareTestNunit/SkiaSharpCompareMetaData_StreamTests.cs @@ -0,0 +1,92 @@ +using Codeuctivity.SkiaSharpCompare; +using NUnit.Framework; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace SkiaSharpCompareTestNunit +{ + internal class SkiaSharpCompareMetaData_StreamTests + { + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, true, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, false, true)] + [TestCase(TestFiles.imageWithoutGpsMetadata, TestFiles.imageWithGpsMetadata, true, false)] + [TestCase(TestFiles.imageWithoutGpsMetadata, TestFiles.imageWithGpsMetadata, false, true)] + public void ImagesAreEqual_SamePixelComparedByMetadataShouldReturnResult(string pic1Path, string pic2Path, bool shouldCompareMetadata, bool expectOutcomeComparisonOfImage) + { + using var pic1 = File.OpenRead(pic1Path); + using var pic2 = File.OpenRead(pic2Path); + + var sut = new ImageCompare(compareMetadata: shouldCompareMetadata); + Assert.That(sut.ImagesAreEqual(pic1, pic2), Is.EqualTo(expectOutcomeComparisonOfImage)); + } + + [Test] + public void CalcDiff_SamePixelComparedByMetadataShouldReturnResult_Null() + { + using var pic1 = File.OpenRead(TestFiles.imageWithoutGpsMetadata); + using var pic2 = File.OpenRead(TestFiles.imageWithoutGpsMetadata); + + var sut = new ImageCompare(compareMetadata: false); + var actual = sut.CalcDiff(pic1, pic2); + + Assert.That(actual.MetadataDifferences, Is.Null); + Assert.That(actual.PixelErrorCount, Is.Zero); + } + + [Test] + public void CalcDiff_SamePixelComparedByMetadataShouldReturnEmptyResult() + { + using var pic1 = File.OpenRead(TestFiles.imageWithoutGpsMetadata); + using var pic2 = File.OpenRead(TestFiles.imageWithoutGpsMetadata); + + var sut = new ImageCompare(compareMetadata: true); + var actual = sut.CalcDiff(pic1, pic2); + + Assert.That(actual.MetadataDifferences, Is.Empty); + Assert.That(actual.PixelErrorCount, Is.Zero); + } + + [Test] + [SetCulture("en-US")] + public void CalcDiff_SamePixelComparedByMetadataShouldReturnCollectionOfMetadataThatDiffers() + { + using var pic1 = File.OpenRead(TestFiles.imageWithoutGpsMetadata); + using var pic2 = File.OpenRead(TestFiles.imageWithGpsMetadata); + var sut = new ImageCompare(compareMetadata: true); + var actual = sut.CalcDiff(pic1, pic2); + + // Output actual and expected to test run logs for easier diagnosis + TestContext.WriteLine($"Actual image: {pic1}"); + TestContext.WriteLine($"Expected image: {pic2}"); + TestContext.WriteLine($"Actual MetadataDifferences: {FormatMetadata(actual.MetadataDifferences)}"); + + var expected = new Dictionary + { + { "GPS:GPS Altitude", ("0 metres", "201.62 metres") }, + { "GPS:GPS Date Stamp", ("", "2025:01:03") }, + { "GPS:GPS Latitude", ("0° 0' 0\"", "48° 12' 7.17\"") }, + { "GPS:GPS Latitude Ref", ("", "N") }, + { "GPS:GPS Longitude", ("0° 0' 0\"", "16° 24' 7.53\"") }, + { "GPS:GPS Longitude Ref", ("", "E") }, + { "GPS:GPS Processing Method", ("", "GPS") }, + { "GPS:GPS Time-Stamp", ("00:00:00.000 UTC", "14:41:20.000 UTC") } + }; + + TestContext.WriteLine($"Expected MetadataDifferences: {FormatMetadata(expected)}"); + + Assert.That(actual.MetadataDifferences, Is.EqualTo(expected)); + Assert.That(actual.PixelErrorCount, Is.Zero); + } + + private static string FormatMetadata(Dictionary? metadata) + { + if (metadata is null) + { + return "null"; + } + + return string.Join("; ", metadata.Select(kvp => $"{kvp.Key}=[{kvp.Value.ValueA ?? ""} | {kvp.Value.ValueB ?? ""}]")); + } + } +} \ No newline at end of file diff --git a/SkiaSharpCompareTestNunit/SkiaSharpCompareTestNunit.csproj b/SkiaSharpCompareTestNunit/SkiaSharpCompareTestNunit.csproj index 8aa6580..1a6ae10 100644 --- a/SkiaSharpCompareTestNunit/SkiaSharpCompareTestNunit.csproj +++ b/SkiaSharpCompareTestNunit/SkiaSharpCompareTestNunit.csproj @@ -13,19 +13,15 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + diff --git a/SkiaSharpCompareTestNunit/SkiaSharpCompareTests.cs b/SkiaSharpCompareTestNunit/SkiaSharpCompareTests.cs index afd4505..8ab5f5c 100644 --- a/SkiaSharpCompareTestNunit/SkiaSharpCompareTests.cs +++ b/SkiaSharpCompareTestNunit/SkiaSharpCompareTests.cs @@ -8,26 +8,12 @@ namespace SkiaSharpCompareTestNunit { public class SkiaSharpCompareTests { - private const string jpg0Rgb24 = "../../../TestData/Calc0.jpg"; - private const string jpg1Rgb24 = "../../../TestData/Calc1.jpg"; - private const string png0Rgba32 = "../../../TestData/Calc0.png"; - private const string png1Rgba32 = "../../../TestData/Calc1.png"; - private const string pngBlack2x2px = "../../../TestData/Black.png"; - private const string pngBlack4x4px = "../../../TestData/BlackDoubleSize.png"; - private const string pngWhite2x2px = "../../../TestData/White.png"; - private const string pngTransparent2x2px = "../../../TestData/pngTransparent2x2px.png"; - private const string pngPartialTransparent2x2px = "../../../TestData/pngPartialTransparent2x2px.png"; - private const string renderedForm1 = "../../../TestData/HC007-Test-02-3-OxPt.html1.png"; - private const string renderedForm2 = "../../../TestData/HC007-Test-02-3-OxPt.html2.png"; - private const string colorShift1 = "../../../TestData/ColorShift1.png"; - private const string colorShift2 = "../../../TestData/ColorShift2.png"; - [Test] - [TestCase(jpg0Rgb24, jpg0Rgb24, true)] - [TestCase(png0Rgba32, png0Rgba32, true)] - [TestCase(png0Rgba32, jpg0Rgb24, true)] - [TestCase(png0Rgba32, jpg1Rgb24, true)] - [TestCase(png0Rgba32, pngBlack2x2px, false)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg0Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.jpg0Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.jpg1Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.pngBlack2x2px, false)] public void ShouldVerifyThatImagesFromFilePathSizeAreEqual(string pathActual, string pathExpected, bool expectedOutcome) { var absolutePathActual = Path.Combine(AppContext.BaseDirectory, pathActual); @@ -37,11 +23,11 @@ public void ShouldVerifyThatImagesFromFilePathSizeAreEqual(string pathActual, st } [Test] - [TestCase(jpg0Rgb24, jpg0Rgb24, true)] - [TestCase(png0Rgba32, png0Rgba32, true)] - [TestCase(png0Rgba32, jpg0Rgb24, true)] - [TestCase(png0Rgba32, jpg1Rgb24, true)] - [TestCase(png0Rgba32, pngBlack2x2px, false)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg0Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.jpg0Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.jpg1Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.pngBlack2x2px, false)] public void ShouldVerifyThatImagesSizeAreEqual(string pathActual, string pathExpected, bool expectedOutcome) { var absolutePathActual = Path.Combine(AppContext.BaseDirectory, pathActual); @@ -54,11 +40,11 @@ public void ShouldVerifyThatImagesSizeAreEqual(string pathActual, string pathExp } [Test] - [TestCase(jpg0Rgb24, jpg0Rgb24, true)] - [TestCase(png0Rgba32, png0Rgba32, true)] - [TestCase(png0Rgba32, jpg0Rgb24, true)] - [TestCase(png0Rgba32, jpg1Rgb24, true)] - [TestCase(png0Rgba32, pngBlack2x2px, false)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg0Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.jpg0Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.jpg1Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.pngBlack2x2px, false)] public void ShouldVerifyThatImageStreamsSizeAreEqual(string pathActual, string pathExpected, bool expectedOutcome) { var absolutePathActual = Path.Combine(AppContext.BaseDirectory, pathActual); @@ -71,11 +57,11 @@ public void ShouldVerifyThatImageStreamsSizeAreEqual(string pathActual, string p } [Test] - [TestCase(jpg0Rgb24, jpg0Rgb24, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] - [TestCase(png0Rgba32, png0Rgba32, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] - [TestCase(jpg0Rgb24, jpg0Rgb24, ResizeOption.DontResize, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(png0Rgba32, png0Rgba32, ResizeOption.DontResize, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(pngTransparent2x2px, pngPartialTransparent2x2px, ResizeOption.DontResize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg0Rgb24, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg0Rgb24, ResizeOption.DontResize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, ResizeOption.DontResize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.pngTransparent2x2px, TestFiles.pngPartialTransparent2x2px, ResizeOption.DontResize, TransparencyOptions.IgnoreAlphaChannel)] public void ShouldVerifyThatImagesAreEqual(string pathActual, string pathExpected, ResizeOption resizeOption, TransparencyOptions transparencyOptions) { var sut = new ImageCompare(resizeOption, transparencyOptions); @@ -86,10 +72,10 @@ public void ShouldVerifyThatImagesAreEqual(string pathActual, string pathExpecte } [Test] - [TestCase(pngBlack2x2px, pngBlack2x2px, ResizeOption.Resize, true)] - [TestCase(pngBlack2x2px, pngBlack4x4px, ResizeOption.Resize, true)] - [TestCase(pngBlack2x2px, pngBlack4x4px, ResizeOption.DontResize, false)] - [TestCase(colorShift1, colorShift2, ResizeOption.DontResize, false)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack2x2px, ResizeOption.Resize, true)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, ResizeOption.Resize, true)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, ResizeOption.DontResize, false)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2, ResizeOption.DontResize, false)] public void ShouldVerifyThatImagesWithDifferentSizeAreEqual(string pathActual, string pathExpected, ResizeOption resizeOption, bool expectedResult) { var sut = new ImageCompare(resizeOption); @@ -100,8 +86,8 @@ public void ShouldVerifyThatImagesWithDifferentSizeAreEqual(string pathActual, s } [Test] - [TestCase(jpg0Rgb24, jpg0Rgb24)] - [TestCase(png0Rgba32, png0Rgba32)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg0Rgb24)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32)] public void ShouldVerifyThatImageStreamsAreEqual(string pathActual, string pathExpected) { var sut = new ImageCompare(); @@ -115,8 +101,8 @@ public void ShouldVerifyThatImageStreamsAreEqual(string pathActual, string pathE } [Test] - [TestCase(jpg0Rgb24, jpg0Rgb24)] - [TestCase(png0Rgba32, png0Rgba32)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg0Rgb24)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32)] public void ShouldVerifyThatSkiaSharpImagesAreEqual(string pathActual, string pathExpected) { var sut = new ImageCompare(); @@ -130,26 +116,26 @@ public void ShouldVerifyThatSkiaSharpImagesAreEqual(string pathActual, string pa } [Test] - [TestCase(jpg0Rgb24, png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, null, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(jpg0Rgb24, png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(jpg0Rgb24, png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.Resize, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(jpg1Rgb24, png1Rgba32, 460034, 2.8459701566405187d, 158121, 97.820519165573728d, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(jpg1Rgb24, png1Rgba32, 460034, 2.8459701566405187d, 158121, 97.820519165573728d, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(png1Rgba32, png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(jpg1Rgb24, jpg1Rgb24, 0, 0, 0, 0, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(jpg0Rgb24, jpg1Rgb24, 208890, 1.2922842790329365d, 2101, 1.2997698646408156d, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(png0Rgba32, png1Rgba32, 203027, 1.25601321422385d, 681, 0.42129618173269651d, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(pngBlack2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(pngBlack4x4px, pngWhite2x2px, 12240, 765, 16, 100.0d, ResizeOption.Resize, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(renderedForm1, renderedForm2, 49267623, 60.794204096742348d, 174178, 21.49284304047384d, ResizeOption.Resize, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(renderedForm2, renderedForm1, 49267623, 60.794204096742348d, 174178, 21.49284304047384d, ResizeOption.Resize, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(colorShift1, colorShift2, 117896, 3.437201166180758d, 30398, 88.623906705539355d, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(colorShift1, colorShift2, 0, 0, 0, 0, ResizeOption.DontResize, 15, TransparencyOptions.CompareAlphaChannel)] - [TestCase(pngPartialTransparent2x2px, pngTransparent2x2px, 2048, 128.0d, 16, 100, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] - [TestCase(pngPartialTransparent2x2px, pngTransparent2x2px, 0, 0, 0, 0, ResizeOption.DontResize, 0, TransparencyOptions.IgnoreAlphaChannel)] - public void ShouldVerifyThatImagesAreSemiEqual(string pathPic1, string pathPic2, int expectedAbsoluteError, double expectedMeanError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption, int pixelColorShiftTolerance, TransparencyOptions transparencyOptions) + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, null, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.Resize, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.jpg1Rgb24, TestFiles.png1Rgba32, 460034, 2.8459701566405187d, 158121, 97.820519165573728d, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.jpg1Rgb24, TestFiles.png1Rgba32, 460034, 2.8459701566405187d, 158121, 97.820519165573728d, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.png1Rgba32, TestFiles.png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.jpg1Rgb24, TestFiles.jpg1Rgb24, 0, 0, 0, 0, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg1Rgb24, 208890, 1.2922842790329365d, 2101, 1.2997698646408156d, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, 203027, 1.25601321422385d, 681, 0.42129618173269651d, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.pngBlack4x4px, TestFiles.pngWhite2x2px, 12240, 765, 16, 100.0d, ResizeOption.Resize, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.renderedForm1, TestFiles.renderedForm2, 49267623, 60.794204096742348d, 174178, 21.49284304047384d, ResizeOption.Resize, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.renderedForm2, TestFiles.renderedForm1, 49267623, 60.794204096742348d, 174178, 21.49284304047384d, ResizeOption.Resize, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2, 117896, 3.437201166180758d, 30398, 88.623906705539355d, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2, 0, 0, 0, 0, ResizeOption.DontResize, 15, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.pngPartialTransparent2x2px, TestFiles.pngTransparent2x2px, 2048, 128.0d, 16, 100, ResizeOption.DontResize, 0, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.pngPartialTransparent2x2px, TestFiles.pngTransparent2x2px, 0, 0, 0, 0, ResizeOption.DontResize, 0, TransparencyOptions.IgnoreAlphaChannel)] + public void ShouldVerifyThatImagesAreSemiEqual(string pathPic1, string pathPic2, int expectedAbsoluteError, double expectedMeanError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption, int colorShiftTolerance, TransparencyOptions transparencyOptions) { - var sut = new ImageCompare(resizeOption, transparencyOptions, pixelColorShiftTolerance); + var sut = new ImageCompare(resizeOption, transparencyOptions, colorShiftTolerance); var absolutePathPic1 = Path.Combine(AppContext.BaseDirectory, pathPic1); var absolutePathPic2 = Path.Combine(AppContext.BaseDirectory, pathPic2); var diff = sut.CalcDiff(absolutePathPic1, absolutePathPic2); @@ -165,8 +151,8 @@ public void ShouldVerifyThatImagesAreSemiEqual(string pathPic1, string pathPic2, Assert.That(diff.PixelErrorPercentage, Is.EqualTo(expectedPixelErrorPercentage), "PixelErrorPercentage"); } - [TestCase(pngBlack2x2px, pngBlack4x4px)] - [TestCase(pngBlack4x4px, pngWhite2x2px)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px)] + [TestCase(TestFiles.pngBlack4x4px, TestFiles.pngWhite2x2px)] public void ShouldVerifyThatCalcDiffThrowsOnDifferentImageSizes(string pathPic1, string pathPic2) { var sut = new ImageCompare(); @@ -179,17 +165,17 @@ public void ShouldVerifyThatCalcDiffThrowsOnDifferentImageSizes(string pathPic1, } [Test] - [TestCase(jpg0Rgb24, png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, null)] - [TestCase(jpg0Rgb24, png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.DontResize)] - [TestCase(jpg0Rgb24, png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.Resize)] - [TestCase(jpg1Rgb24, png1Rgba32, 460034, 2.8459701566405187d, 158121, 97.820519165573728d, ResizeOption.DontResize)] - [TestCase(png1Rgba32, png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize)] - [TestCase(jpg1Rgb24, jpg1Rgb24, 0, 0, 0, 0, ResizeOption.DontResize)] - [TestCase(jpg0Rgb24, jpg1Rgb24, 208890, 1.2922842790329365d, 2101, 1.2997698646408156d, ResizeOption.DontResize)] - [TestCase(png0Rgba32, png1Rgba32, 203027, 1.25601321422385d, 681, 0.42129618173269651d, ResizeOption.DontResize)] - [TestCase(pngBlack2x2px, pngWhite2x2px, 3060, 765, 4, 100.0d, ResizeOption.DontResize)] - [TestCase(pngBlack2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize)] - [TestCase(pngBlack4x4px, pngWhite2x2px, 12240, 765, 16, 100.0d, ResizeOption.Resize)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, null)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.DontResize)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.Resize)] + [TestCase(TestFiles.jpg1Rgb24, TestFiles.png1Rgba32, 460034, 2.8459701566405187d, 158121, 97.820519165573728d, ResizeOption.DontResize)] + [TestCase(TestFiles.png1Rgba32, TestFiles.png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize)] + [TestCase(TestFiles.jpg1Rgb24, TestFiles.jpg1Rgb24, 0, 0, 0, 0, ResizeOption.DontResize)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg1Rgb24, 208890, 1.2922842790329365d, 2101, 1.2997698646408156d, ResizeOption.DontResize)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, 203027, 1.25601321422385d, 681, 0.42129618173269651d, ResizeOption.DontResize)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngWhite2x2px, 3060, 765, 4, 100.0d, ResizeOption.DontResize)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize)] + [TestCase(TestFiles.pngBlack4x4px, TestFiles.pngWhite2x2px, 12240, 765, 16, 100.0d, ResizeOption.Resize)] public void ShouldVerifyThatImageStreamsAreSemiEqual(string pathPic1, string pathPic2, int expectedAbsoluteError, double expectedMeanError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption) { var sut = new ImageCompare(resizeOption); @@ -205,10 +191,10 @@ public void ShouldVerifyThatImageStreamsAreSemiEqual(string pathPic1, string pat Assert.That(diff.PixelErrorPercentage, Is.EqualTo(expectedPixelErrorPercentage), "PixelErrorPercentage"); } - [TestCase(pngBlack2x2px, pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, true)] - [TestCase(pngBlack2x2px, pngBlack2x2px, 0, 0, 0, 0, ResizeOption.DontResize, true)] - [TestCase(pngBlack2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, true)] - [TestCase(pngBlack2x2px, pngWhite2x2px, 0, 0, 0, 0, ResizeOption.DontResize, false)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, true)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack2x2px, 0, 0, 0, 0, ResizeOption.DontResize, true)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, true)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngWhite2x2px, 0, 0, 0, 0, ResizeOption.DontResize, false)] public void ShouldCalcDiffMaskSKBitmap(string pathPic1, string pathPic2, int expectedMeanError, int expectedAbsoluteError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption, bool expectedOutcome) { var sut = new ImageCompare(resizeOption); @@ -224,15 +210,15 @@ public void ShouldCalcDiffMaskSKBitmap(string pathPic1, string pathPic2, int exp } } - [TestCase(png0Rgba32, png1Rgba32, null, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(pngWhite2x2px, pngBlack2x2px, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] - [TestCase(pngTransparent2x2px, pngPartialTransparent2x2px, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] - [TestCase(png0Rgba32, png1Rgba32, ResizeOption.DontResize, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(png0Rgba32, png1Rgba32, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(pngWhite2x2px, pngBlack4x4px, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(pngBlack4x4px, pngWhite2x2px, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(renderedForm1, renderedForm2, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(renderedForm2, renderedForm1, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, null, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.pngWhite2x2px, TestFiles.pngBlack2x2px, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.pngTransparent2x2px, TestFiles.pngPartialTransparent2x2px, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, ResizeOption.DontResize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.pngWhite2x2px, TestFiles.pngBlack4x4px, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.pngBlack4x4px, TestFiles.pngWhite2x2px, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.renderedForm1, TestFiles.renderedForm2, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.renderedForm2, TestFiles.renderedForm1, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] public void CalcDiff(string pathPic1, string pathPic2, ResizeOption resizeOption, TransparencyOptions transparencyOptions) { var sut = new ImageCompare(resizeOption, transparencyOptions); @@ -268,20 +254,20 @@ private static void SaveAsPng(SKBitmap maskImage, FileStream fileStreamDifferenc encodedData.SaveTo(fileStreamDifferenceMask); } - [TestCase(png0Rgba32, png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize, TransparencyOptions.CompareAlphaChannel)] - [TestCase(jpg0Rgb24, jpg1Rgb24, 0, 0, 0, 0, ResizeOption.DontResize, TransparencyOptions.CompareAlphaChannel)] - [TestCase(jpg0Rgb24, jpg1Rgb24, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] - [TestCase(pngBlack2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] - [TestCase(pngBlack2x2px, pngWhite2x2px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] - [TestCase(pngTransparent2x2px, pngWhite2x2px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] - [TestCase(pngTransparent2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] - [TestCase(png0Rgba32, png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(jpg0Rgb24, jpg1Rgb24, 0, 0, 0, 0, ResizeOption.DontResize, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(jpg0Rgb24, jpg1Rgb24, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(pngBlack2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(pngBlack2x2px, pngWhite2x2px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(pngTransparent2x2px, pngWhite2x2px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(pngTransparent2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg1Rgb24, 0, 0, 0, 0, ResizeOption.DontResize, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg1Rgb24, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngWhite2x2px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.pngTransparent2x2px, TestFiles.pngWhite2x2px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.pngTransparent2x2px, TestFiles.pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg1Rgb24, 0, 0, 0, 0, ResizeOption.DontResize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg1Rgb24, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngWhite2x2px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.pngTransparent2x2px, TestFiles.pngWhite2x2px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.pngTransparent2x2px, TestFiles.pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, TransparencyOptions.IgnoreAlphaChannel)] public void ShouldCalcDiffMaskSKBitmapAndUseOutcome(string pathPic1, string pathPic2, int expectedMeanError, int expectedAbsoluteError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption, TransparencyOptions transparencyOptions) { var sut = new ImageCompare(resizeOption, transparencyOptions); @@ -308,16 +294,16 @@ public void ShouldCalcDiffMaskSKBitmapAndUseOutcome(string pathPic1, string path Assert.That(maskedDiff.PixelErrorPercentage, Is.EqualTo(expectedPixelErrorPercentage), "PixelErrorPercentage"); } - [TestCase(pngWhite2x2px, pngBlack2x2px, pngTransparent2x2px, 765, 12240, 16, 100d, ResizeOption.Resize, 0)] - [TestCase(pngWhite2x2px, pngBlack2x2px, pngBlack4x4px, 765, 12240, 16, 100d, ResizeOption.Resize, 0)] - [TestCase(pngBlack2x2px, pngBlack2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, 0)] - [TestCase(pngBlack2x2px, pngBlack4x4px, pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 0)] - [TestCase(pngBlack4x4px, pngBlack2x2px, pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 0)] - [TestCase(colorShift1, colorShift1, pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 0)] - [TestCase(colorShift1, colorShift2, pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 20)] - public void ShouldUseDiffMask(string pathPic1, string pathPic2, string pathPic3, double expectedMeanError, int expectedAbsoluteError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption, int pixelColorShiftTolerance) + [TestCase(TestFiles.pngWhite2x2px, TestFiles.pngBlack2x2px, TestFiles.pngTransparent2x2px, 765, 12240, 16, 100d, ResizeOption.Resize, 0)] + [TestCase(TestFiles.pngWhite2x2px, TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, 765, 12240, 16, 100d, ResizeOption.Resize, 0)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, 0)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, TestFiles.pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 0)] + [TestCase(TestFiles.pngBlack4x4px, TestFiles.pngBlack2x2px, TestFiles.pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 0)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift1, TestFiles.pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 0)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2, TestFiles.pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 20)] + public void ShouldUseDiffMask(string pathPic1, string pathPic2, string pathPic3, double expectedMeanError, int expectedAbsoluteError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption, int colorShiftTolerance) { - var sut = new ImageCompare(resizeOption, TransparencyOptions.CompareAlphaChannel, pixelColorShiftTolerance); + var sut = new ImageCompare(resizeOption, TransparencyOptions.CompareAlphaChannel, colorShiftTolerance); var absolutePathPic1 = Path.Combine(AppContext.BaseDirectory, pathPic1); var absolutePathPic2 = Path.Combine(AppContext.BaseDirectory, pathPic2); var differenceMaskPic = Path.Combine(AppContext.BaseDirectory, pathPic3); @@ -333,9 +319,9 @@ public void ShouldUseDiffMask(string pathPic1, string pathPic2, string pathPic3, Assert.That(maskedDiff.PixelErrorPercentage, Is.EqualTo(expectedPixelErrorPercentage), "PixelErrorPercentage"); } - [TestCase(pngBlack2x2px, pngBlack2x2px, pngBlack4x4px)] - [TestCase(pngBlack2x2px, pngBlack4x4px, pngBlack2x2px)] - [TestCase(pngBlack4x4px, pngBlack2x2px, pngBlack2x2px)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, TestFiles.pngBlack2x2px)] + [TestCase(TestFiles.pngBlack4x4px, TestFiles.pngBlack2x2px, TestFiles.pngBlack2x2px)] public void ShouldThrowUsingInvalidImageDimensionsDiffMask(string pathPic1, string pathPic2, string pathPic3) { var sut = new ImageCompare(); @@ -351,7 +337,7 @@ public void ShouldThrowUsingInvalidImageDimensionsDiffMask(string pathPic1, stri Assert.That(exception?.Message, Is.EqualTo("Size of images differ.")); } - [TestCase(png0Rgba32, png1Rgba32, 0, 0, 0, 0)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, 0, 0, 0, 0)] public void CalcDiffStreams(string pathPic1, string pathPic2, int expectedMeanError, int expectedAbsoluteError, int expectedPixelErrorCount, double expectedPixelErrorPercentage) { var sut = new ImageCompare(transparencyOptions: TransparencyOptions.IgnoreAlphaChannel); @@ -373,15 +359,15 @@ public void CalcDiffStreams(string pathPic1, string pathPic2, int expectedMeanEr Assert.That(maskedDiff.PixelErrorPercentage, Is.EqualTo(expectedPixelErrorPercentage), "PixelErrorPercentage"); } - [TestCase(png0Rgba32, png1Rgba32, TransparencyOptions.CompareAlphaChannel, 0)] - [TestCase(colorShift1, colorShift2, TransparencyOptions.CompareAlphaChannel, 20)] - [TestCase(png0Rgba32, png1Rgba32, TransparencyOptions.IgnoreAlphaChannel, 0)] - [TestCase(colorShift1, colorShift2, TransparencyOptions.IgnoreAlphaChannel, 20)] - [TestCase(pngTransparent2x2px, pngPartialTransparent2x2px, TransparencyOptions.IgnoreAlphaChannel, 0)] - [TestCase(pngTransparent2x2px, pngPartialTransparent2x2px, TransparencyOptions.CompareAlphaChannel, 0)] - public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByFilePath_NoDifferences(string image1RelativePath, string image2RelativePath, TransparencyOptions transparencyOptions, int pixelColorShiftTolerance) + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, TransparencyOptions.CompareAlphaChannel, 0)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2, TransparencyOptions.CompareAlphaChannel, 20)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, TransparencyOptions.IgnoreAlphaChannel, 0)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2, TransparencyOptions.IgnoreAlphaChannel, 20)] + [TestCase(TestFiles.pngTransparent2x2px, TestFiles.pngPartialTransparent2x2px, TransparencyOptions.IgnoreAlphaChannel, 0)] + [TestCase(TestFiles.pngTransparent2x2px, TestFiles.pngPartialTransparent2x2px, TransparencyOptions.CompareAlphaChannel, 0)] + public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByFilePath_NoDifferences(string image1RelativePath, string image2RelativePath, TransparencyOptions transparencyOptions, int colorShiftTolerance) { - var sut = new ImageCompare(ResizeOption.DontResize, transparencyOptions, pixelColorShiftTolerance); + var sut = new ImageCompare(ResizeOption.DontResize, transparencyOptions, colorShiftTolerance); var image1Path = Path.Combine(AppContext.BaseDirectory, image1RelativePath); var image2Path = Path.Combine(AppContext.BaseDirectory, image2RelativePath); var diffMask1Path = Path.GetTempFileName() + "differenceMask.png"; @@ -404,7 +390,7 @@ public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByFilePath_NoDiffe File.Delete(diffMask1Path); } - [TestCase(png0Rgba32, png1Rgba32)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32)] public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByStream_NoDifferences(string image1RelativePath, string image2RelativePath) { var sut = new ImageCompare(); @@ -434,7 +420,7 @@ public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByStream_NoDiffere File.Delete(diffMask1Path); } - [TestCase(png0Rgba32, png1Rgba32)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32)] public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByImage_NoDifferences(string image1RelativePath, string image2RelativePath) { var sut = new ImageCompare(); @@ -451,13 +437,13 @@ public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByImage_NoDifferen } [Test] - [TestCase(jpg0Rgb24, jpg1Rgb24)] - [TestCase(png0Rgba32, png1Rgba32)] - [TestCase(jpg0Rgb24, png1Rgba32)] - [TestCase(jpg0Rgb24, png0Rgba32)] - [TestCase(jpg1Rgb24, png1Rgba32)] - [TestCase(colorShift1, colorShift2)] - [TestCase(pngTransparent2x2px, pngPartialTransparent2x2px)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg1Rgb24)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png1Rgba32)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32)] + [TestCase(TestFiles.jpg1Rgb24, TestFiles.png1Rgba32)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2)] + [TestCase(TestFiles.pngTransparent2x2px, TestFiles.pngPartialTransparent2x2px)] public void ShouldVerifyThatImagesAreNotEqual(string pathActual, string pathExpected) { var sut = new ImageCompare(); @@ -468,13 +454,13 @@ public void ShouldVerifyThatImagesAreNotEqual(string pathActual, string pathExpe } [Test] - [TestCase(jpg0Rgb24, jpg1Rgb24)] - [TestCase(png0Rgba32, png1Rgba32)] - [TestCase(jpg0Rgb24, png1Rgba32)] - [TestCase(jpg0Rgb24, png0Rgba32)] - [TestCase(jpg1Rgb24, png1Rgba32)] - [TestCase(colorShift1, colorShift2)] - [TestCase(pngTransparent2x2px, pngPartialTransparent2x2px)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg1Rgb24)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png1Rgba32)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32)] + [TestCase(TestFiles.jpg1Rgb24, TestFiles.png1Rgba32)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2)] + [TestCase(TestFiles.pngTransparent2x2px, TestFiles.pngPartialTransparent2x2px)] public void ShouldVerifyThatImageStreamAreNotEqual(string pathActual, string pathExpected) { var sut = new ImageCompare(); @@ -487,8 +473,8 @@ public void ShouldVerifyThatImageStreamAreNotEqual(string pathActual, string pat Assert.That(sut.ImagesAreEqual(actual, expected), Is.False); } - [TestCase(png0Rgba32, pngBlack2x2px, TransparencyOptions.IgnoreAlphaChannel)] - [TestCase(png0Rgba32, pngBlack2x2px, TransparencyOptions.CompareAlphaChannel)] + [TestCase(TestFiles.png0Rgba32, TestFiles.pngBlack2x2px, TransparencyOptions.IgnoreAlphaChannel)] + [TestCase(TestFiles.png0Rgba32, TestFiles.pngBlack2x2px, TransparencyOptions.CompareAlphaChannel)] public void ShouldVerifyThatImageWithDifferentSizeThrows(string pathPic1, string pathPic2, TransparencyOptions transparencyOptions) { var sut = new ImageCompare(ResizeOption.DontResize, transparencyOptions); @@ -500,11 +486,11 @@ public void ShouldVerifyThatImageWithDifferentSizeThrows(string pathPic1, string Assert.That(exception?.Message, Is.EqualTo("Size of images differ.")); } - [TestCase(png0Rgba32, png0Rgba32, pngBlack2x2px)] - [TestCase(png0Rgba32, pngBlack2x2px, png0Rgba32)] - [TestCase(pngBlack2x2px, png0Rgba32, png0Rgba32)] - [TestCase(pngPartialTransparent2x2px, png0Rgba32, png0Rgba32)] - [TestCase(png0Rgba32, png0Rgba32, pngPartialTransparent2x2px)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, TestFiles.pngBlack2x2px)] + [TestCase(TestFiles.png0Rgba32, TestFiles.pngBlack2x2px, TestFiles.png0Rgba32)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.png0Rgba32, TestFiles.png0Rgba32)] + [TestCase(TestFiles.pngPartialTransparent2x2px, TestFiles.png0Rgba32, TestFiles.png0Rgba32)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, TestFiles.pngPartialTransparent2x2px)] public void ShouldVerifyThatImageWithDifferentSizeThrows(string pathPic1, string pathPic2, string pathPic3) { var sut = new ImageCompare(); diff --git a/SkiaSharpCompareTestNunit/SkiaSharpStaticCompareTests.cs b/SkiaSharpCompareTestNunit/SkiaSharpStaticCompareTests.cs index 31e7182..730a416 100644 --- a/SkiaSharpCompareTestNunit/SkiaSharpStaticCompareTests.cs +++ b/SkiaSharpCompareTestNunit/SkiaSharpStaticCompareTests.cs @@ -8,25 +8,12 @@ namespace SkiaSharpCompareTestNunit { public class SkiaSharpStaticCompareTests { - private const string jpg0Rgb24 = "../../../TestData/Calc0.jpg"; - private const string jpg1Rgb24 = "../../../TestData/Calc1.jpg"; - private const string png0Rgba32 = "../../../TestData/Calc0.png"; - private const string png1Rgba32 = "../../../TestData/Calc1.png"; - private const string pngBlack2x2px = "../../../TestData/Black.png"; - private const string pngBlack4x4px = "../../../TestData/BlackDoubleSize.png"; - private const string pngWhite2x2px = "../../../TestData/White.png"; - private const string pngTransparent2x2px = "../../../TestData/pngTransparent2x2px.png"; - private const string renderedForm1 = "../../../TestData/HC007-Test-02-3-OxPt.html1.png"; - private const string renderedForm2 = "../../../TestData/HC007-Test-02-3-OxPt.html2.png"; - private const string colorShift1 = "../../../TestData/ColorShift1.png"; - private const string colorShift2 = "../../../TestData/ColorShift2.png"; - [Test] - [TestCase(jpg0Rgb24, jpg0Rgb24, true)] - [TestCase(png0Rgba32, png0Rgba32, true)] - [TestCase(png0Rgba32, jpg0Rgb24, true)] - [TestCase(png0Rgba32, jpg1Rgb24, true)] - [TestCase(png0Rgba32, pngBlack2x2px, false)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg0Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.jpg0Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.jpg1Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.pngBlack2x2px, false)] public void ShouldVerifyThatImagesFromFilePathSizeAreEqual(string pathActual, string pathExpected, bool expectedOutcome) { var absolutePathActual = Path.Combine(AppContext.BaseDirectory, pathActual); @@ -36,11 +23,11 @@ public void ShouldVerifyThatImagesFromFilePathSizeAreEqual(string pathActual, st } [Test] - [TestCase(jpg0Rgb24, jpg0Rgb24, true)] - [TestCase(png0Rgba32, png0Rgba32, true)] - [TestCase(png0Rgba32, jpg0Rgb24, true)] - [TestCase(png0Rgba32, jpg1Rgb24, true)] - [TestCase(png0Rgba32, pngBlack2x2px, false)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg0Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.jpg0Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.jpg1Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.pngBlack2x2px, false)] public void ShouldVerifyThatImagesSizeAreEqual(string pathActual, string pathExpected, bool expectedOutcome) { var absolutePathActual = Path.Combine(AppContext.BaseDirectory, pathActual); @@ -53,11 +40,11 @@ public void ShouldVerifyThatImagesSizeAreEqual(string pathActual, string pathExp } [Test] - [TestCase(jpg0Rgb24, jpg0Rgb24, true)] - [TestCase(png0Rgba32, png0Rgba32, true)] - [TestCase(png0Rgba32, jpg0Rgb24, true)] - [TestCase(png0Rgba32, jpg1Rgb24, true)] - [TestCase(png0Rgba32, pngBlack2x2px, false)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg0Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.jpg0Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.jpg1Rgb24, true)] + [TestCase(TestFiles.png0Rgba32, TestFiles.pngBlack2x2px, false)] public void ShouldVerifyThatImageStreamsSizeAreEqual(string pathActual, string pathExpected, bool expectedOutcome) { var absolutePathActual = Path.Combine(AppContext.BaseDirectory, pathActual); @@ -70,22 +57,22 @@ public void ShouldVerifyThatImageStreamsSizeAreEqual(string pathActual, string p } [Test] - [TestCase(jpg0Rgb24, jpg0Rgb24, 0)] - [TestCase(png0Rgba32, png0Rgba32, 0)] - [TestCase(colorShift1, colorShift2, 15)] - public void ShouldVerifyThatImagesAreEqual(string pathActual, string pathExpected, int pixelColorShiftTolerance) + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg0Rgb24, 0)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, 0)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2, 15)] + public void ShouldVerifyThatImagesAreEqual(string pathActual, string pathExpected, int colorShiftTolerance) { var absolutePathActual = Path.Combine(AppContext.BaseDirectory, pathActual); var absolutePathExpected = Path.Combine(AppContext.BaseDirectory, pathExpected); - Assert.That(Compare.ImagesAreEqual(absolutePathActual, absolutePathExpected, pixelColorShiftTolerance: pixelColorShiftTolerance), Is.True); + Assert.That(Compare.ImagesAreEqual(absolutePathActual, absolutePathExpected, pixelColorShiftTolerance: colorShiftTolerance), Is.True); } [Test] - [TestCase(pngBlack2x2px, pngBlack2x2px, ResizeOption.Resize, true)] - [TestCase(pngBlack2x2px, pngBlack4x4px, ResizeOption.Resize, true)] - [TestCase(pngBlack2x2px, pngBlack4x4px, ResizeOption.DontResize, false)] - [TestCase(colorShift1, colorShift2, ResizeOption.DontResize, false)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack2x2px, ResizeOption.Resize, true)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, ResizeOption.Resize, true)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, ResizeOption.DontResize, false)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2, ResizeOption.DontResize, false)] public void ShouldVerifyThatImagesWithDifferentSizeAreEqual(string pathActual, string pathExpected, ResizeOption resizeOption, bool expectedResult) { var absolutePathActual = Path.Combine(AppContext.BaseDirectory, pathActual); @@ -95,8 +82,8 @@ public void ShouldVerifyThatImagesWithDifferentSizeAreEqual(string pathActual, s } [Test] - [TestCase(jpg0Rgb24, jpg0Rgb24)] - [TestCase(png0Rgba32, png0Rgba32)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg0Rgb24)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32)] public void ShouldVerifyThatImageStreamsAreEqual(string pathActual, string pathExpected) { var absolutePathActual = Path.Combine(AppContext.BaseDirectory, pathActual); @@ -109,8 +96,8 @@ public void ShouldVerifyThatImageStreamsAreEqual(string pathActual, string pathE } [Test] - [TestCase(jpg0Rgb24, jpg0Rgb24)] - [TestCase(png0Rgba32, png0Rgba32)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg0Rgb24)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32)] public void ShouldVerifyThatSkiaSharpImagesAreEqual(string pathActual, string pathExpected) { var absolutePathActual = Path.Combine(AppContext.BaseDirectory, pathActual); @@ -123,26 +110,26 @@ public void ShouldVerifyThatSkiaSharpImagesAreEqual(string pathActual, string pa } [Test] - [TestCase(jpg0Rgb24, png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, null, 0)] - [TestCase(jpg0Rgb24, png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.DontResize, 0)] - [TestCase(jpg0Rgb24, png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.Resize, 0)] - [TestCase(jpg1Rgb24, png1Rgba32, 460034, 2.8459701566405187d, 158121, 97.820519165573728d, ResizeOption.DontResize, 0)] - [TestCase(jpg1Rgb24, png1Rgba32, 460034, 2.8459701566405187d, 158121, 97.820519165573728d, ResizeOption.DontResize, 0)] - [TestCase(png1Rgba32, png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize, 0)] - [TestCase(jpg1Rgb24, jpg1Rgb24, 0, 0, 0, 0, ResizeOption.DontResize, 0)] - [TestCase(jpg0Rgb24, jpg1Rgb24, 208890, 1.2922842790329365d, 2101, 1.2997698646408156d, ResizeOption.DontResize, 0)] - [TestCase(png0Rgba32, png1Rgba32, 203027, 1.25601321422385d, 681, 0.42129618173269651d, ResizeOption.DontResize, 0)] - [TestCase(pngBlack2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, 0)] - [TestCase(pngBlack4x4px, pngWhite2x2px, 12240, 765, 16, 100.0d, ResizeOption.Resize, 0)] - [TestCase(renderedForm1, renderedForm2, 49267623, 60.794204096742348d, 174178, 21.49284304047384d, ResizeOption.Resize, 0)] - [TestCase(renderedForm2, renderedForm1, 49267623, 60.794204096742348d, 174178, 21.49284304047384d, ResizeOption.Resize, 0)] - [TestCase(colorShift1, colorShift2, 117896, 3.437201166180758d, 30398, 88.623906705539355d, ResizeOption.DontResize, 0)] - [TestCase(colorShift1, colorShift2, 0, 0, 0, 0, ResizeOption.DontResize, 15)] - public void ShouldVerifyThatImagesAreSemiEqual(string pathPic1, string pathPic2, int expectedAbsoluteError, double expectedMeanError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption, int pixelColorShiftTolerance) + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, null, 0)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.DontResize, 0)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.Resize, 0)] + [TestCase(TestFiles.jpg1Rgb24, TestFiles.png1Rgba32, 460034, 2.8459701566405187d, 158121, 97.820519165573728d, ResizeOption.DontResize, 0)] + [TestCase(TestFiles.jpg1Rgb24, TestFiles.png1Rgba32, 460034, 2.8459701566405187d, 158121, 97.820519165573728d, ResizeOption.DontResize, 0)] + [TestCase(TestFiles.png1Rgba32, TestFiles.png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize, 0)] + [TestCase(TestFiles.jpg1Rgb24, TestFiles.jpg1Rgb24, 0, 0, 0, 0, ResizeOption.DontResize, 0)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg1Rgb24, 208890, 1.2922842790329365d, 2101, 1.2997698646408156d, ResizeOption.DontResize, 0)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, 203027, 1.25601321422385d, 681, 0.42129618173269651d, ResizeOption.DontResize, 0)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, 0)] + [TestCase(TestFiles.pngBlack4x4px, TestFiles.pngWhite2x2px, 12240, 765, 16, 100.0d, ResizeOption.Resize, 0)] + [TestCase(TestFiles.renderedForm1, TestFiles.renderedForm2, 49267623, 60.794204096742348d, 174178, 21.49284304047384d, ResizeOption.Resize, 0)] + [TestCase(TestFiles.renderedForm2, TestFiles.renderedForm1, 49267623, 60.794204096742348d, 174178, 21.49284304047384d, ResizeOption.Resize, 0)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2, 117896, 3.437201166180758d, 30398, 88.623906705539355d, ResizeOption.DontResize, 0)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2, 0, 0, 0, 0, ResizeOption.DontResize, 15)] + public void ShouldVerifyThatImagesAreSemiEqual(string pathPic1, string pathPic2, int expectedAbsoluteError, double expectedMeanError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption, int colorShiftTolerance) { var absolutePathPic1 = Path.Combine(AppContext.BaseDirectory, pathPic1); var absolutePathPic2 = Path.Combine(AppContext.BaseDirectory, pathPic2); - var diff = Compare.CalcDiff(absolutePathPic1, absolutePathPic2, resizeOption, pixelColorShiftTolerance); + var diff = Compare.CalcDiff(absolutePathPic1, absolutePathPic2, resizeOption, colorShiftTolerance); Console.WriteLine($"PixelErrorCount: {diff.PixelErrorCount}"); Console.WriteLine($"PixelErrorPercentage: {diff.PixelErrorPercentage}"); @@ -155,8 +142,8 @@ public void ShouldVerifyThatImagesAreSemiEqual(string pathPic1, string pathPic2, Assert.That(diff.PixelErrorPercentage, Is.EqualTo(expectedPixelErrorPercentage), "PixelErrorPercentage"); } - [TestCase(pngBlack2x2px, pngBlack4x4px)] - [TestCase(pngBlack4x4px, pngWhite2x2px)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px)] + [TestCase(TestFiles.pngBlack4x4px, TestFiles.pngWhite2x2px)] public void ShouldVerifyThatCalcDiffThrowsOnDifferentImageSizes(string pathPic1, string pathPic2) { var absolutePathPic1 = Path.Combine(AppContext.BaseDirectory, pathPic1); @@ -169,17 +156,17 @@ public void ShouldVerifyThatCalcDiffThrowsOnDifferentImageSizes(string pathPic1, } [Test] - [TestCase(jpg0Rgb24, png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, null)] - [TestCase(jpg0Rgb24, png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.DontResize)] - [TestCase(jpg0Rgb24, png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.Resize)] - [TestCase(jpg1Rgb24, png1Rgba32, 460034, 2.8459701566405187d, 158121, 97.820519165573728d, ResizeOption.DontResize)] - [TestCase(png1Rgba32, png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize)] - [TestCase(jpg1Rgb24, jpg1Rgb24, 0, 0, 0, 0, ResizeOption.DontResize)] - [TestCase(jpg0Rgb24, jpg1Rgb24, 208890, 1.2922842790329365d, 2101, 1.2997698646408156d, ResizeOption.DontResize)] - [TestCase(png0Rgba32, png1Rgba32, 203027, 1.25601321422385d, 681, 0.42129618173269651d, ResizeOption.DontResize)] - [TestCase(pngBlack2x2px, pngWhite2x2px, 3060, 765, 4, 100.0d, ResizeOption.DontResize)] - [TestCase(pngBlack2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize)] - [TestCase(pngBlack4x4px, pngWhite2x2px, 12240, 765, 16, 100.0d, ResizeOption.Resize)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, null)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.DontResize)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32, 461891, 2.8574583652965777d, 158087, 97.799485288659028d, ResizeOption.Resize)] + [TestCase(TestFiles.jpg1Rgb24, TestFiles.png1Rgba32, 460034, 2.8459701566405187d, 158121, 97.820519165573728d, ResizeOption.DontResize)] + [TestCase(TestFiles.png1Rgba32, TestFiles.png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize)] + [TestCase(TestFiles.jpg1Rgb24, TestFiles.jpg1Rgb24, 0, 0, 0, 0, ResizeOption.DontResize)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg1Rgb24, 208890, 1.2922842790329365d, 2101, 1.2997698646408156d, ResizeOption.DontResize)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, 203027, 1.25601321422385d, 681, 0.42129618173269651d, ResizeOption.DontResize)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngWhite2x2px, 3060, 765, 4, 100.0d, ResizeOption.DontResize)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize)] + [TestCase(TestFiles.pngBlack4x4px, TestFiles.pngWhite2x2px, 12240, 765, 16, 100.0d, ResizeOption.Resize)] public void ShouldVerifyThatImageStreamsAreSemiEqual(string pathPic1, string pathPic2, int expectedAbsoluteError, double expectedMeanError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption) { var absolutePathPic1 = Path.Combine(AppContext.BaseDirectory, pathPic1); @@ -194,13 +181,13 @@ public void ShouldVerifyThatImageStreamsAreSemiEqual(string pathPic1, string pat Assert.That(diff.PixelErrorPercentage, Is.EqualTo(expectedPixelErrorPercentage), "PixelErrorPercentage"); } - [TestCase(png0Rgba32, png1Rgba32, 0, 0, 0, 0, null)] - [TestCase(png0Rgba32, png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize)] - [TestCase(png0Rgba32, png1Rgba32, 0, 0, 0, 0, ResizeOption.Resize)] - [TestCase(pngWhite2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize)] - [TestCase(pngBlack4x4px, pngWhite2x2px, 0, 0, 0, 0, ResizeOption.Resize)] - [TestCase(renderedForm1, renderedForm2, 0, 0, 0, 0, ResizeOption.Resize)] - [TestCase(renderedForm2, renderedForm1, 0, 0, 0, 0, ResizeOption.Resize)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, 0, 0, 0, 0, null)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, 0, 0, 0, 0, ResizeOption.Resize)] + [TestCase(TestFiles.pngWhite2x2px, TestFiles.pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize)] + [TestCase(TestFiles.pngBlack4x4px, TestFiles.pngWhite2x2px, 0, 0, 0, 0, ResizeOption.Resize)] + [TestCase(TestFiles.renderedForm1, TestFiles.renderedForm2, 0, 0, 0, 0, ResizeOption.Resize)] + [TestCase(TestFiles.renderedForm2, TestFiles.renderedForm1, 0, 0, 0, 0, ResizeOption.Resize)] public void DiffMask(string pathPic1, string pathPic2, int expectedMeanError, int expectedAbsoluteError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption) { var absolutePathPic1 = Path.Combine(AppContext.BaseDirectory, pathPic1); @@ -228,9 +215,9 @@ private static void SaveAsPng(SKBitmap maskImage, FileStream fileStreamDifferenc encodedData.SaveTo(fileStreamDifferenceMask); } - [TestCase(pngBlack2x2px, pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize)] - [TestCase(pngBlack2x2px, pngBlack2x2px, 0, 0, 0, 0, ResizeOption.DontResize)] - [TestCase(pngBlack2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack2x2px, 0, 0, 0, 0, ResizeOption.DontResize)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize)] public void ShouldCalcDiffMaskSKBitmap(string pathPic1, string pathPic2, int expectedMeanError, int expectedAbsoluteError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption) { var absolutePathPic1 = Path.Combine(AppContext.BaseDirectory, pathPic1); @@ -245,10 +232,10 @@ public void ShouldCalcDiffMaskSKBitmap(string pathPic1, string pathPic2, int exp } } - [TestCase(png0Rgba32, png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize)] - [TestCase(jpg0Rgb24, jpg1Rgb24, 0, 0, 0, 0, ResizeOption.DontResize)] - [TestCase(jpg0Rgb24, jpg1Rgb24, 0, 0, 0, 0, ResizeOption.Resize)] - [TestCase(pngBlack2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, 0, 0, 0, 0, ResizeOption.DontResize)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg1Rgb24, 0, 0, 0, 0, ResizeOption.DontResize)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg1Rgb24, 0, 0, 0, 0, ResizeOption.Resize)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize)] public void ShouldCalcDiffMaskSKBitmapAndUseOutcome(string pathPic1, string pathPic2, int expectedMeanError, int expectedAbsoluteError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption) { var absolutePathPic1 = Path.Combine(AppContext.BaseDirectory, pathPic1); @@ -274,14 +261,14 @@ public void ShouldCalcDiffMaskSKBitmapAndUseOutcome(string pathPic1, string path Assert.That(maskedDiff.PixelErrorPercentage, Is.EqualTo(expectedPixelErrorPercentage), "PixelErrorPercentage"); } - [TestCase(pngWhite2x2px, pngBlack2x2px, pngTransparent2x2px, 765, 12240, 16, 100d, ResizeOption.Resize, 0)] - [TestCase(pngWhite2x2px, pngBlack2x2px, pngBlack4x4px, 765, 12240, 16, 100d, ResizeOption.Resize, 0)] - [TestCase(pngBlack2x2px, pngBlack2x2px, pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, 0)] - [TestCase(pngBlack2x2px, pngBlack4x4px, pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 0)] - [TestCase(pngBlack4x4px, pngBlack2x2px, pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 0)] - [TestCase(colorShift1, colorShift1, pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 0)] - [TestCase(colorShift1, colorShift2, pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 20)] - public void ShouldUseDiffMask(string pathPic1, string pathPic2, string pathPic3, double expectedMeanError, int expectedAbsoluteError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption, int pixelColorShiftTolerance) + [TestCase(TestFiles.pngWhite2x2px, TestFiles.pngBlack2x2px, TestFiles.pngTransparent2x2px, 765, 12240, 16, 100d, ResizeOption.Resize, 0)] + [TestCase(TestFiles.pngWhite2x2px, TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, 765, 12240, 16, 100d, ResizeOption.Resize, 0)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, 0, 0, 0, 0, ResizeOption.Resize, 0)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, TestFiles.pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 0)] + [TestCase(TestFiles.pngBlack4x4px, TestFiles.pngBlack2x2px, TestFiles.pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 0)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift1, TestFiles.pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 0)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2, TestFiles.pngBlack2x2px, 0, 0, 0, 0, ResizeOption.Resize, 20)] + public void ShouldUseDiffMask(string pathPic1, string pathPic2, string pathPic3, double expectedMeanError, int expectedAbsoluteError, int expectedPixelErrorCount, double expectedPixelErrorPercentage, ResizeOption resizeOption, int colorShiftTolerance) { var absolutePathPic1 = Path.Combine(AppContext.BaseDirectory, pathPic1); var absolutePathPic2 = Path.Combine(AppContext.BaseDirectory, pathPic2); @@ -290,7 +277,7 @@ public void ShouldUseDiffMask(string pathPic1, string pathPic2, string pathPic3, using var pic2 = SKBitmap.Decode(absolutePathPic2); using var maskPic = SKBitmap.Decode(differenceMaskPic); - var maskedDiff = Compare.CalcDiff(pic1, pic2, maskPic, resizeOption, pixelColorShiftTolerance); + var maskedDiff = Compare.CalcDiff(pic1, pic2, maskPic, resizeOption, colorShiftTolerance); Assert.That(maskedDiff.MeanError, Is.EqualTo(expectedMeanError), "MeanError"); Assert.That(maskedDiff.AbsoluteError, Is.EqualTo(expectedAbsoluteError), "AbsoluteError"); @@ -298,9 +285,9 @@ public void ShouldUseDiffMask(string pathPic1, string pathPic2, string pathPic3, Assert.That(maskedDiff.PixelErrorPercentage, Is.EqualTo(expectedPixelErrorPercentage), "PixelErrorPercentage"); } - [TestCase(pngBlack2x2px, pngBlack2x2px, pngBlack4x4px)] - [TestCase(pngBlack2x2px, pngBlack4x4px, pngBlack2x2px)] - [TestCase(pngBlack4x4px, pngBlack2x2px, pngBlack2x2px)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.pngBlack4x4px, TestFiles.pngBlack2x2px)] + [TestCase(TestFiles.pngBlack4x4px, TestFiles.pngBlack2x2px, TestFiles.pngBlack2x2px)] public void ShouldThrowUsingInvalidImageDimensionsDiffMask(string pathPic1, string pathPic2, string pathPic3) { var absolutePathPic1 = Path.Combine(AppContext.BaseDirectory, pathPic1); @@ -315,7 +302,7 @@ public void ShouldThrowUsingInvalidImageDimensionsDiffMask(string pathPic1, stri Assert.That(exception?.Message, Is.EqualTo("Size of images differ.")); } - [TestCase(png0Rgba32, png1Rgba32, 0, 0, 0, 0)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, 0, 0, 0, 0)] public void DiffMaskStreams(string pathPic1, string pathPic2, int expectedMeanError, int expectedAbsoluteError, int expectedPixelErrorCount, double expectedPixelErrorPercentage) { var absolutePathPic1 = Path.Combine(AppContext.BaseDirectory, pathPic1); @@ -336,10 +323,10 @@ public void DiffMaskStreams(string pathPic1, string pathPic2, int expectedMeanEr Assert.That(maskedDiff.PixelErrorPercentage, Is.EqualTo(expectedPixelErrorPercentage), "PixelErrorPercentage"); } - [TestCase(png0Rgba32, png1Rgba32, 0, false)] - [TestCase(colorShift1, colorShift2, 20, true)] - [TestCase(colorShift1, colorShift2, 0, false)] - public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByFilePath_NoDifferences(string image1RelativePath, string image2RelativePath, int pixelColorShiftTolerance, bool expectIsImageEntirelyBlack) + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, 0, false)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2, 20, true)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2, 0, false)] + public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByFilePath_NoDifferences(string image1RelativePath, string image2RelativePath, int colorShiftTolerance, bool expectIsImageEntirelyBlack) { var image1Path = Path.Combine(AppContext.BaseDirectory, image1RelativePath); var image2Path = Path.Combine(AppContext.BaseDirectory, image2RelativePath); @@ -347,7 +334,7 @@ public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByFilePath_NoDiffe using (var diffMask1Stream = File.Create(diffMask1Path)) { - using var diffMask1Image = Compare.CalcDiffMaskImage(image1Path, image2Path, ResizeOption.DontResize, pixelColorShiftTolerance); + using var diffMask1Image = Compare.CalcDiffMaskImage(image1Path, image2Path, ResizeOption.DontResize, colorShiftTolerance); ImageExtensions.SaveAsPng(diffMask1Image, diffMask1Stream); Assert.That(ImageExtensions.IsImageEntirelyBlack(diffMask1Image, TransparencyOptions.IgnoreAlphaChannel), Is.EqualTo(expectIsImageEntirelyBlack)); } @@ -364,7 +351,7 @@ public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByFilePath_NoDiffe File.Delete(diffMask1Path); } - [TestCase(png0Rgba32, png1Rgba32)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32)] public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByStream_NoDifferences(string image1RelativePath, string image2RelativePath) { var image1Path = Path.Combine(AppContext.BaseDirectory, image1RelativePath); @@ -393,9 +380,9 @@ public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByStream_NoDiffere File.Delete(diffMask1Path); } - [TestCase(png0Rgba32, png1Rgba32, png1Rgba32, 0, false)] - [TestCase(colorShift1, colorShift1, colorShift2, 15, true)] - public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByImage_NoDifferences(string image1RelativePath, string image2RelativePath, string image3RelativePath, int expectedPixelColorShiftTolerance, bool expectToleranceMaskToEntirelyBlack) + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32, TestFiles.png1Rgba32, 0, false)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift1, TestFiles.colorShift2, 15, true)] + public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByImage_NoDifferences(string image1RelativePath, string image2RelativePath, string image3RelativePath, int colorShiftTolerance, bool expectToleranceMaskToEntirelyBlack) { var image1Path = Path.Combine(AppContext.BaseDirectory, image1RelativePath); var image2Path = Path.Combine(AppContext.BaseDirectory, image2RelativePath); @@ -407,16 +394,16 @@ public void CalcDiffMaskImage_WhenSupplyingDiffMaskOfTwoImagesByImage_NoDifferen using var diffMask1Image = Compare.CalcDiffMaskImage(image1, image2); - using var diffMask2Image = Compare.CalcDiffMaskImage(image1, image3, diffMask1Image, pixelColorShiftTolerance: expectedPixelColorShiftTolerance); + using var diffMask2Image = Compare.CalcDiffMaskImage(image1, image3, diffMask1Image, pixelColorShiftTolerance: colorShiftTolerance); Assert.That(ImageExtensions.IsImageEntirelyBlack(diffMask1Image, TransparencyOptions.IgnoreAlphaChannel), Is.EqualTo(expectToleranceMaskToEntirelyBlack)); Assert.That(ImageExtensions.IsImageEntirelyBlack(diffMask2Image, TransparencyOptions.IgnoreAlphaChannel), Is.True); } - [TestCase(png0Rgba32, png0Rgba32, 0)] - [TestCase(png0Rgba32, png0Rgba32, 15)] - [TestCase(colorShift1, colorShift2, 15)] - public void CalcDiffMaskImage_ToleranceColorShift_NoDifferences(string image1RelativePath, string image2RelativePath, int expectedPixelColorShiftTolerance) + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, 0)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, 15)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2, 15)] + public void CalcDiffMaskImage_ToleranceColorShift_NoDifferences(string image1RelativePath, string image2RelativePath, int colorShiftTolerance) { var image1Path = Path.Combine(AppContext.BaseDirectory, image1RelativePath); var image2Path = Path.Combine(AppContext.BaseDirectory, image2RelativePath); @@ -424,18 +411,18 @@ public void CalcDiffMaskImage_ToleranceColorShift_NoDifferences(string image1Rel using var image1 = SKBitmap.Decode(image1Path); using var image2 = SKBitmap.Decode(image2Path); - using var diffMask1Image = Compare.CalcDiffMaskImage(image1, image2, pixelColorShiftTolerance: expectedPixelColorShiftTolerance); + using var diffMask1Image = Compare.CalcDiffMaskImage(image1, image2, pixelColorShiftTolerance: colorShiftTolerance); Assert.That(ImageExtensions.IsImageEntirelyBlack(diffMask1Image, TransparencyOptions.IgnoreAlphaChannel), Is.True); } [Test] - [TestCase(jpg0Rgb24, jpg1Rgb24)] - [TestCase(png0Rgba32, png1Rgba32)] - [TestCase(jpg0Rgb24, png1Rgba32)] - [TestCase(jpg0Rgb24, png0Rgba32)] - [TestCase(jpg1Rgb24, png1Rgba32)] - [TestCase(colorShift1, colorShift2)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg1Rgb24)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png1Rgba32)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32)] + [TestCase(TestFiles.jpg1Rgb24, TestFiles.png1Rgba32)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2)] public void ShouldVerifyThatImagesAreNotEqual(string pathActual, string pathExpected) { var absolutePathActual = Path.Combine(AppContext.BaseDirectory, pathActual); @@ -445,12 +432,12 @@ public void ShouldVerifyThatImagesAreNotEqual(string pathActual, string pathExpe } [Test] - [TestCase(jpg0Rgb24, jpg1Rgb24)] - [TestCase(png0Rgba32, png1Rgba32)] - [TestCase(jpg0Rgb24, png1Rgba32)] - [TestCase(jpg0Rgb24, png0Rgba32)] - [TestCase(jpg1Rgb24, png1Rgba32)] - [TestCase(colorShift1, colorShift2)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.jpg1Rgb24)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png1Rgba32)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png1Rgba32)] + [TestCase(TestFiles.jpg0Rgb24, TestFiles.png0Rgba32)] + [TestCase(TestFiles.jpg1Rgb24, TestFiles.png1Rgba32)] + [TestCase(TestFiles.colorShift1, TestFiles.colorShift2)] public void ShouldVerifyThatImageStreamAreNotEqual(string pathActual, string pathExpected) { var absolutePathActual = Path.Combine(AppContext.BaseDirectory, pathActual); @@ -462,7 +449,7 @@ public void ShouldVerifyThatImageStreamAreNotEqual(string pathActual, string pat Assert.That(Compare.ImagesAreEqual(actual, expected), Is.False); } - [TestCase(png0Rgba32, pngBlack2x2px)] + [TestCase(TestFiles.png0Rgba32, TestFiles.pngBlack2x2px)] public void ShouldVerifyThatImageWithDifferentSizeThrows(string pathPic1, string pathPic2) { var absolutePathPic1 = Path.Combine(AppContext.BaseDirectory, pathPic1); @@ -473,9 +460,9 @@ public void ShouldVerifyThatImageWithDifferentSizeThrows(string pathPic1, string Assert.That(exception?.Message, Is.EqualTo("Size of images differ.")); } - [TestCase(png0Rgba32, png0Rgba32, pngBlack2x2px)] - [TestCase(png0Rgba32, pngBlack2x2px, png0Rgba32)] - [TestCase(pngBlack2x2px, png0Rgba32, png0Rgba32)] + [TestCase(TestFiles.png0Rgba32, TestFiles.png0Rgba32, TestFiles.pngBlack2x2px)] + [TestCase(TestFiles.png0Rgba32, TestFiles.pngBlack2x2px, TestFiles.png0Rgba32)] + [TestCase(TestFiles.pngBlack2x2px, TestFiles.png0Rgba32, TestFiles.png0Rgba32)] public void ShouldVerifyThatImageWithDifferentSizeThrows(string pathPic1, string pathPic2, string pathPic3) { var absolutePathPic1 = Path.Combine(AppContext.BaseDirectory, pathPic1); diff --git a/SkiaSharpCompareTestNunit/TestData/imageWithGpsMetadata.jpg b/SkiaSharpCompareTestNunit/TestData/imageWithGpsMetadata.jpg new file mode 100644 index 0000000..4e4c0ff Binary files /dev/null and b/SkiaSharpCompareTestNunit/TestData/imageWithGpsMetadata.jpg differ diff --git a/SkiaSharpCompareTestNunit/TestData/imageWithoutGpsMetadata.jpg b/SkiaSharpCompareTestNunit/TestData/imageWithoutGpsMetadata.jpg new file mode 100644 index 0000000..1313728 Binary files /dev/null and b/SkiaSharpCompareTestNunit/TestData/imageWithoutGpsMetadata.jpg differ diff --git a/SkiaSharpCompareTestNunit/TestFiles.cs b/SkiaSharpCompareTestNunit/TestFiles.cs new file mode 100644 index 0000000..4471bc1 --- /dev/null +++ b/SkiaSharpCompareTestNunit/TestFiles.cs @@ -0,0 +1,24 @@ +namespace SkiaSharpCompareTestNunit +{ + internal static class TestFiles + { + public const string jpg0Rgb24 = "./../../../TestData/Calc0.jpg"; + public const string jpg1Rgb24 = "./../../../TestData/Calc1.jpg"; + public const string png0Rgba32 = "./../../../TestData/Calc0.png"; + public const string png1Rgba32 = "./../../../TestData/Calc1.png"; + public const string pngBlack2x2px = "./../../../TestData/Black.png"; + public const string pngBlack4x4px = "./../../../TestData/BlackDoubleSize.png"; + public const string pngWhite2x2px = "./../../../TestData/White.png"; + public const string pngTransparent2x2px = "./../../../TestData/pngTransparent2x2px.png"; + public const string pngPartialTransparent2x2px = "./../../../TestData/pngPartialTransparent2x2px.png"; + public const string renderedForm1 = "./../../../TestData/HC007-Test-02-3-OxPt.html1.png"; + public const string renderedForm2 = "./../../../TestData/HC007-Test-02-3-OxPt.html2.png"; + public const string colorShift1 = "./../../../TestData/ColorShift1.png"; + public const string colorShift2 = "./../../../TestData/ColorShift2.png"; + + // These changes are related to https://github.com/nextcloud/android/issues/6248#issuecomment-1207315271 + public const string imageWithoutGpsMetadata = "./../../../TestData/imageWithoutGpsMetadata.jpg"; + + public const string imageWithGpsMetadata = "./../../../TestData/imageWithGpsMetadata.jpg"; + } +} \ No newline at end of file