hedgehogqa / fsharp-hedgehog-xunit

Hedgehog with convenience attributes for xUnit.net
Other
8 stars 3 forks source link

Can Hedgehog.Xunit generate functions? #9

Open moodmosaic opened 2 years ago

moodmosaic commented 2 years ago

@dharmaturtle, with @cmeeren we've discussed function generation in the past. Could that work with Hedgehog.Xunit?

using Hedgehog;
using Hedgehog.Xunit;

using System;
using System.Collections.Generic;
using System.Linq;

using Xunit;
using Xunit.Abstractions;

public class Properties
{
    public static Func<Guid, int> Combine(
        Func<Guid, int> f,
        Func<Guid, int> g)
    {
        return x => f(x) + g(x);
    }

    [Property]
    public void CombineIsAssociative(
        Func<Guid, int> f,
        Func<Guid, int> g,
        Func<Guid, int> h,
        Guid guid)
    {
        Assert.Equal(
            Combine(Combine(f, g), h)(guid),
            Combine(f, Combine(g, h))(guid));
    }
}

Output:

System.NotSupportedException:
  Unable to auto-generate System.IntPtr. You can use
  'GenX.defaults |> AutoGenConfig.addGenerator myGen |> GenX.autoWith'
  to generate types not inherently supported by GenX.auto.
dharmaturtle commented 2 years ago

It should yeah, as long as you register the function generator with the correct types. This (trivially) passes on my machine:

using Hedgehog;
using Hedgehog.Xunit;

public class AutoGenConfigContainer
{
  public static AutoGenConfig X()
  {
    var fgen = Gen.constant<Func<Guid, int>>((_) => 1);
    return AutoGenConfigModule.addGenerator(fgen).Invoke(GenX.defaults);
  }
}

public class Properties
{
  public static Func<Guid, int> Combine(
      Func<Guid, int> f,
      Func<Guid, int> g)
  {
    return x => f(x) + g(x);
  }

  [Property(typeof(AutoGenConfigContainer))]
  public void CombineIsAssociative(
      Func<Guid, int> f,
      Func<Guid, int> g,
      Func<Guid, int> h,
      Guid guid)
  {
    Assert.Equal(
        Combine(Combine(f, g), h)(guid),
        Combine(f, Combine(g, h))(guid));
  }
}

My C# is very rusty, and I've little experience with its LINQ syntax, so I hope it's enough to get you unstuck. I think the ideal solution would be to use GenX.withMapTo like

var ingen = Gen.list(Hedgehog.Range.linear(0, 100), Gen.guid);
var outgen = Gen.int16(Hedgehog.Range.linear<short>(0,100));
var tuplegen = GenX.withMapTo(outgen, ingen);

but then I realized I didn't know how to get the value out of my monad in C# - and I hope you know how to :D

moodmosaic commented 2 years ago

Thank you 👍 I'll try it and let you know.