Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FsCheck in C#: generate a list of two dimension arrays with the same shape

Let's say I'm writing some code for video analysis. Here is a simplified version of a Video class:

public class Video
{
    public readonly int Width;
    public readonly int Height;
    public readonly List<int[,]> Frames;

    public Video(int width, int height, IEnumerable<int[,]> frames)
    {
        Width = width;
        Height = height;
        Frames = new List<int[,]>();
        foreach (var frame in frames)
        {
            if (frame.GetLength(0) != height || frame.GetLength(1) != width)
            {
                throw new ArgumentException("Incorrect frames dimensions");
            }
            Frames.Add(frame);
        }
    }
}

How do I make an Arbitrary<Video> and register it? How do I make a shrinker for that Arbitrary?

Tried this, couldn't understand how apply works:

public static Arbitrary<Video> Videos()
{
    var videoGen = Arb.Generate<PositiveInt>()
        .SelectMany(w => Arb.Generate<PositiveInt>(), (w, h) => new {w, h})
        .Apply( /* what is Gen<Func<a,b>> */);

    return videoGen.ToArbitrary();
}

Tried this, but couldn't plug the generator for list in here:

public static Arbitrary<Video> Videos()
{
    var videoGen = Arb.Generate<PositiveInt>()
        .SelectMany(w => Arb.Generate<PositiveInt>(), (w, h) => new Video(w, h, /* how to plug generator here? */));

    return videoGen.ToArbitrary();
}
like image 880
Pavel Murygin Avatar asked Sep 27 '15 18:09

Pavel Murygin


People also ask

What is FsCheck?

FsCheck is a tool for testing . NET programs automatically. The programmer provides a specification of the program, in the form of properties which functions, methods or objects should satisfy, and FsCheck then tests that the properties hold in a large number of randomly generated cases.


2 Answers

Using Kurt Schelfthout's answer as a foundation, you can write an Arbitrary for the video class like this:

public static class VideoArbitrary
{
    public static Arbitrary<Video> Videos()
    {
        var genVideo = from w in Arb.Generate<PositiveInt>()
                       from h in Arb.Generate<PositiveInt>()
                       from arrs in Gen.ListOf(
                           Gen.Array2DOf<int>(
                               h.Item,
                               w.Item,
                               Arb.Generate<int>()))
                       select new Video(w.Item, h.Item, arrs);
        return genVideo.ToArbitrary();
    }
}

You can use this in various ways.

Plain vanilla FsCheck

Here's how to use the Video Arbitrary with plain vanilla FsCheck, here hosted within an xUnit.net test case, which isn't required: you can host this in whichever process you prefer:

[Fact]
public void VideoProperty()
{
    var property = Prop.ForAll(
        VideoArbitrary.Videos(),
        video =>
        {
            // Test goes here...
            Assert.NotNull(video);
        });
    property.QuickCheckThrowOnFailure();
}

Prop.ForAll is very useful for defining properties with custom Arbitraries. When you call QuickCheckThrowOnFailure, it's going to run the test for 'all' (by defailt: 100) values of the Video class.

Untyped xUnit.net property

You can also use the FsCheck.Xunit Glue Library, but you have to pass the Arbitrary as a weakly typed value to the attribute:

[Property(Arbitrary = new[] { typeof(VideoArbitrary) })]
public void XunitPropertyWithWeaklyTypedArbitrary(Video video)
{
    // Test goes here...
    Assert.NotNull(video);
}

This is simple and easy to understand, but there's no static type checking involved when assigning that Arbitrary property, so I'm not too fond of this approach.

Typed xUnit.net property

A better way to use FsCheck.Xunit with custom Arbitraries is to combine it with Prop.ForAll:

[Property]
public Property XUnitPropertyWithStronglyTypedArbitrary()
{
    return Prop.ForAll(
        VideoArbitrary.Videos(),
        video =>
        {
            // Test goes here...
            Assert.NotNull(video);
        });
}

Notice that the return type of this method is no longer void, but Property; the [Property] attribute understands this type and executes the test accordingly.

This third option is my preferred way of using custom Arbitraries from within xUnit.net, because it brings back compile-time checking.

like image 86
Mark Seemann Avatar answered Oct 11 '22 04:10

Mark Seemann


Just a spur of the moment sketch - not compiled :)

var genVideo = from w in Arb.Generate<PositiveInt>()
               from h in Arb.Generate<PositiveInt>()
               from arrs in Gen.ListOf(Gen.Array2DOf(h, w, Arb.Generate<int>))
               select new Video(w, h, arrs);
like image 30
Kurt Schelfthout Avatar answered Oct 11 '22 04:10

Kurt Schelfthout