Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions docs/comparer.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ static Task<CompareResult> CompareImages(
return Task.FromResult(result);
}
```
<sup><a href='/src/Verify.Tests/Snippets/ComparerSnippets.cs#L34-L51' title='Snippet source file'>snippet source</a> | <a href='#snippet-ImageComparer' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Verify.Tests/Snippets/ComparerSnippets.cs#L51-L68' title='Snippet source file'>snippet source</a> | <a href='#snippet-ImageComparer' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

The returned `CompareResult.NotEqual` takes an optional message that will be rendered in the resulting text displayed to the user on test failure.
Expand Down Expand Up @@ -103,7 +103,7 @@ VerifierSettings.RegisterStreamComparer(
compare: CompareImages);
await VerifyFile("TheImage.png");
```
<sup><a href='/src/Verify.Tests/Snippets/ComparerSnippets.cs#L24-L31' title='Snippet source file'>snippet source</a> | <a href='#snippet-StaticComparer' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Verify.Tests/Snippets/ComparerSnippets.cs#L41-L48' title='Snippet source file'>snippet source</a> | <a href='#snippet-StaticComparer' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


Expand Down Expand Up @@ -210,6 +210,27 @@ public static class ModuleInitializer
<sup><a href='/src/ModuleInitDocs/UseSsimForPngThreshold.cs#L3-L12' title='Snippet source file'>snippet source</a> | <a href='#snippet-UseSsimForPngThreshold' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

SSIM can also be enabled per-verification, via an instance of `VerifySettings` or fluently on a `SettingsTask`:

<!-- snippet: InstanceSsimForPng -->
<a id='snippet-InstanceSsimForPng'></a>
```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);
```
<sup><a href='/src/Verify.Tests/Snippets/ComparerSnippets.cs#L22-L37' title='Snippet source file'>snippet source</a> | <a href='#snippet-InstanceSsimForPng' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Dimension mismatches between the received and verified images are always reported as not equal, regardless of threshold.


Expand Down
4 changes: 4 additions & 0 deletions docs/mdsource/comparer.source.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.


Expand Down
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Project>
<PropertyGroup>
<NoWarn>CA1822;CS1591;CS0649;xUnit1026;xUnit1013;CS1573;VerifyTestsProjectDir;VerifySetParameters;PolyFillTargetsForNuget;xUnit1051;NU1608;NU1109</NoWarn>
<Version>31.18.0</Version>
<Version>31.19.0</Version>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>preview</LangVersion>
<AssemblyVersion>1.0.0</AssemblyVersion>
Expand Down
4 changes: 2 additions & 2 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
<!-- keep at 4.5.0 -->
<PackageVersion Include="System.ValueTuple" Version="4.5.0" Pinned="true" />
<PackageVersion Include="TextCopy" Version="6.2.1" />
<PackageVersion Include="TUnit" Version="1.46.0" />
<PackageVersion Include="TUnit.Core" Version="1.46.0" />
<PackageVersion Include="TUnit" Version="1.48.0" />
<PackageVersion Include="TUnit.Core" Version="1.48.0" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="xunit.v3.extensibility.core" Version="3.2.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
Expand Down
21 changes: 21 additions & 0 deletions src/Verify.Tests/Comparer/PngSsimComparerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
17 changes: 17 additions & 0 deletions src/Verify.Tests/Snippets/ComparerSnippets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 9 additions & 3 deletions src/Verify/Compare/Png/PngSsimComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ static class PngSsimComparer
{
public static double Threshold { get; set; } = 0.98;

internal static Task<CompareResult> Compare(Stream received, Stream verified, IReadOnlyDictionary<string, object> context)
internal static Task<CompareResult> Compare(Stream received, Stream verified, IReadOnlyDictionary<string, object> context) =>
Compare(received, verified, Threshold);

internal static StreamCompare BuildCompare(double threshold) =>
(received, verified, _) => Compare(received, verified, threshold);

internal static Task<CompareResult> Compare(Stream received, Stream verified, double threshold)
{
var receivedImage = PngDecoder.Decode(received);
var verifiedImage = PngDecoder.Decode(verified);
Expand All @@ -14,12 +20,12 @@ internal static Task<CompareResult> 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}."));
}
}
3 changes: 3 additions & 0 deletions src/Verify/Compare/VerifySettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public void UseStreamComparer(StreamCompare compare, params ReadOnlySpan<string>
}
}

public void UseSsimForPng(double threshold = 0.98) =>
UseStreamComparer(PngSsimComparer.BuildCompare(threshold), "png");

[OverloadResolutionPriority(1)]
public void UseStringComparer(StringCompare compare, params ReadOnlySpan<string> extensions)
{
Expand Down
8 changes: 8 additions & 0 deletions src/Verify/SettingsTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ public SettingsTask UseStringComparer(StringCompare compare, params ReadOnlySpan
return this;
}

/// <inheritdoc cref="VerifySettings.UseSsimForPng(double)"/>
[Pure]
public SettingsTask UseSsimForPng(double threshold = 0.98)
{
CurrentSettings.UseSsimForPng(threshold);
return this;
}

/// <inheritdoc cref="VerifySettings.DisableDiff()"/>
[Pure]
public SettingsTask DisableDiff()
Expand Down
Loading