diff --git a/docs/comparer.md b/docs/comparer.md index cf13dc690..22f93e6e7 100644 --- a/docs/comparer.md +++ b/docs/comparer.md @@ -34,7 +34,7 @@ static Task CompareImages( return Task.FromResult(result); } ``` -snippet source | anchor +snippet source | anchor The returned `CompareResult.NotEqual` takes an optional message that will be rendered in the resulting text displayed to the user on test failure. @@ -103,7 +103,7 @@ VerifierSettings.RegisterStreamComparer( compare: CompareImages); await VerifyFile("TheImage.png"); ``` -snippet source | anchor +snippet source | anchor @@ -210,6 +210,27 @@ public static class ModuleInitializer snippet source | anchor +SSIM can also be enabled per-verification, via an instance of `VerifySettings` or fluently on a `SettingsTask`: + + + +```cs +[Fact] +public Task InstanceSsimForPng() +{ + var settings = new VerifySettings(); + settings.UseSsimForPng(); + return VerifyFile("sample.png", settings); +} + +[Fact] +public Task InstanceSsimForPngFluent() => + VerifyFile("sample.png") + .UseSsimForPng(threshold: 0.995); +``` +snippet source | anchor + + Dimension mismatches between the received and verified images are always reported as not equal, regardless of threshold. diff --git a/docs/mdsource/comparer.source.md b/docs/mdsource/comparer.source.md index 6dd416659..195734c3c 100644 --- a/docs/mdsource/comparer.source.md +++ b/docs/mdsource/comparer.source.md @@ -52,6 +52,10 @@ The default threshold is `0.98`. SSIM scores range from `0` (completely differen snippet: UseSsimForPngThreshold +SSIM can also be enabled per-verification, via an instance of `VerifySettings` or fluently on a `SettingsTask`: + +snippet: InstanceSsimForPng + Dimension mismatches between the received and verified images are always reported as not equal, regardless of threshold. diff --git a/src/Directory.Build.props b/src/Directory.Build.props index d1e63b7bd..5c49a9f03 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ CA1822;CS1591;CS0649;xUnit1026;xUnit1013;CS1573;VerifyTestsProjectDir;VerifySetParameters;PolyFillTargetsForNuget;xUnit1051;NU1608;NU1109 - 31.18.0 + 31.19.0 enable preview 1.0.0 diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 28c50ea6c..9c0622626 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -32,8 +32,8 @@ - - + + diff --git a/src/Verify.Tests/Comparer/PngSsimComparerTests.cs b/src/Verify.Tests/Comparer/PngSsimComparerTests.cs index 9c239bf98..b2df9ca17 100644 --- a/src/Verify.Tests/Comparer/PngSsimComparerTests.cs +++ b/src/Verify.Tests/Comparer/PngSsimComparerTests.cs @@ -86,6 +86,27 @@ public async Task Threshold_Tuning_Tightens_Comparison() } } + [Fact] + public async Task BuildCompare_Captures_Threshold_Per_Instance() + { + var a = PngTestHelper.EncodeRgba(32, 32, RandomRgba(32, 32, seed: 3)); + var modified = RandomRgba(32, 32, seed: 3); + for (var i = 0; i < modified.Length; i += 4) + { + modified[i] = (byte)Math.Clamp(modified[i] + 20, 0, 255); + } + + var b = PngTestHelper.EncodeRgba(32, 32, modified); + + var lenient = PngSsimComparer.BuildCompare(0.5); + var lenientResult = await lenient(new MemoryStream(a), new MemoryStream(b), emptyContext); + Assert.True(lenientResult.IsEqual); + + var strict = PngSsimComparer.BuildCompare(0.9999); + var strictResult = await strict(new MemoryStream(a), new MemoryStream(b), emptyContext); + Assert.False(strictResult.IsEqual); + } + [Fact] public async Task Single_Pixel_Grayscale_Identical() { diff --git a/src/Verify.Tests/Snippets/ComparerSnippets.cs b/src/Verify.Tests/Snippets/ComparerSnippets.cs index 596a12ce4..6ffc7250c 100644 --- a/src/Verify.Tests/Snippets/ComparerSnippets.cs +++ b/src/Verify.Tests/Snippets/ComparerSnippets.cs @@ -19,6 +19,23 @@ public Task InstanceComparerFluent() => #endregion + #region InstanceSsimForPng + + [Fact] + public Task InstanceSsimForPng() + { + var settings = new VerifySettings(); + settings.UseSsimForPng(); + return VerifyFile("sample.png", settings); + } + + [Fact] + public Task InstanceSsimForPngFluent() => + VerifyFile("sample.png") + .UseSsimForPng(threshold: 0.995); + + #endregion + public async Task StaticComparer() { #region StaticComparer diff --git a/src/Verify/Compare/Png/PngSsimComparer.cs b/src/Verify/Compare/Png/PngSsimComparer.cs index 271c1eca0..1086ba79b 100644 --- a/src/Verify/Compare/Png/PngSsimComparer.cs +++ b/src/Verify/Compare/Png/PngSsimComparer.cs @@ -2,7 +2,13 @@ static class PngSsimComparer { public static double Threshold { get; set; } = 0.98; - internal static Task Compare(Stream received, Stream verified, IReadOnlyDictionary context) + internal static Task Compare(Stream received, Stream verified, IReadOnlyDictionary context) => + Compare(received, verified, Threshold); + + internal static StreamCompare BuildCompare(double threshold) => + (received, verified, _) => Compare(received, verified, threshold); + + internal static Task Compare(Stream received, Stream verified, double threshold) { var receivedImage = PngDecoder.Decode(received); var verifiedImage = PngDecoder.Decode(verified); @@ -14,12 +20,12 @@ internal static Task Compare(Stream received, Stream verified, IR } var score = Ssim.Compare(receivedImage, verifiedImage); - if (score >= Threshold) + if (score >= threshold) { return Task.FromResult(CompareResult.Equal); } return Task.FromResult(CompareResult.NotEqual( - $"PNG SSIM {score:F4} below threshold {Threshold:F4}.")); + $"PNG SSIM {score:F4} below threshold {threshold:F4}.")); } } diff --git a/src/Verify/Compare/VerifySettings.cs b/src/Verify/Compare/VerifySettings.cs index 2ed3b6315..62eb83c05 100644 --- a/src/Verify/Compare/VerifySettings.cs +++ b/src/Verify/Compare/VerifySettings.cs @@ -33,6 +33,9 @@ public void UseStreamComparer(StreamCompare compare, params ReadOnlySpan } } + public void UseSsimForPng(double threshold = 0.98) => + UseStreamComparer(PngSsimComparer.BuildCompare(threshold), "png"); + [OverloadResolutionPriority(1)] public void UseStringComparer(StringCompare compare, params ReadOnlySpan extensions) { diff --git a/src/Verify/SettingsTask.cs b/src/Verify/SettingsTask.cs index c68c499ac..43f63b546 100644 --- a/src/Verify/SettingsTask.cs +++ b/src/Verify/SettingsTask.cs @@ -93,6 +93,14 @@ public SettingsTask UseStringComparer(StringCompare compare, params ReadOnlySpan return this; } + /// + [Pure] + public SettingsTask UseSsimForPng(double threshold = 0.98) + { + CurrentSettings.UseSsimForPng(threshold); + return this; + } + /// [Pure] public SettingsTask DisableDiff()