Verify/docs/comparer.md

5.3 KiB

Comparer

Comparers are used to compare non-text files.

Custom Comparer

Using a custom comparer can be helpful when a result has changed, but not enough to fail verification. For example when rendering images/forms on different operating systems.

For samples purposes only the image sizes will be compared:

static Task<CompareResult> CompareImages(
    Stream received,
    Stream verified,
    IReadOnlyDictionary<string, object> context)
{
    // Fake comparison
    if (received.Length == verified.Length)
    {
        return Task.FromResult(CompareResult.Equal);
    }

    var result = CompareResult.NotEqual();
    return Task.FromResult(result);
}

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.

If an input is split into multiple files, and a text file fails, then all subsequent binary comparisons will revert to the default comparison.

Instance comparer

[Fact]
public Task InstanceComparer()
{
    var settings = new VerifySettings();
    settings.UseStreamComparer(CompareImages);
    return VerifyFile("sample.png", settings);
}

[Fact]
public Task InstanceComparerFluent() =>
    VerifyFile("sample.png")
        .UseStreamComparer(CompareImages);

snippet source | anchor

Static comparer

VerifierSettings.RegisterStreamComparer(
    extension: "png",
    compare: CompareImages);
await VerifyFile("TheImage.png");

snippet source | anchor

Default Comparison

const int bufferSize = 1024 * sizeof(long);

public static async Task<CompareResult> AreEqual(Stream stream1, Stream stream2)
{
    EnsureAtStart(stream1);
    EnsureAtStart(stream2);

    var buffer1 = new byte[bufferSize];
    var buffer2 = new byte[bufferSize];

    while (true)
    {
        var count = await ReadBufferAsync(stream1, buffer1);

        //no need to compare size here since only enter on files being same size

        if (count == 0)
        {
            return CompareResult.Equal;
        }

        await ReadBufferAsync(stream2, buffer2);

        for (var i = 0; i < count; i += sizeof(long))
        {
            if (BitConverter.ToInt64(buffer1, i) != BitConverter.ToInt64(buffer2, i))
            {
                return CompareResult.NotEqual();
            }
        }
    }
}

static void EnsureAtStart(Stream stream)
{
    if (stream.CanSeek &&
        stream.Position != 0)
    {
        throw new("Expected stream to be at position 0.");
    }
}

static async Task<int> ReadBufferAsync(Stream stream, byte[] buffer)
{
    var bytesRead = 0;
    while (bytesRead < buffer.Length)
    {
        var read = await stream.ReadAsync(buffer, bytesRead, buffer.Length - bytesRead);
        if (read == 0)
        {
            // Reached end of stream.
            return bytesRead;
        }

        bytesRead += read;
    }

    return bytesRead;
}

snippet source | anchor

Pre-packaged comparers