-
Notifications
You must be signed in to change notification settings - Fork 164
Description
FsCheck currently has inconsistent support for generating indirectly recursive records.
If a record type is immediately recursive, FsCheck raises an exception. No problem; that makes sense.
#r "nuget: FsCheck,3.3.2"
open FsCheck.FSharp
type R = { R : R }
ArbMap.defaults
|> ArbMap.generate<R>
|> Gen.sample 3
// System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
// ---> System.Exception: Recursive record types cannot be generated automatically: …But if the record type is not immediately recursive, the user experience is not ideal: the user either sees a stack overflow exception, or, if FsCheck is being run via another test runner (xUnit, NUnit), the test seems simply not to run at all, because the test process crashes.
#r "nuget: FsCheck,3.3.2"
open FsCheck.FSharp
type R = { Rs : R list }
ArbMap.defaults
|> ArbMap.generate<R>
|> Gen.sample 3
// Stack overflow 😭.FsCheck actually does support generating indirectly recursive records in one scenario: the record is recursive through a mutually-recursive multi-case union:
#r "nuget: FsCheck,3.3.2"
open FsCheck.FSharp
type R = { U : U }
and U = U of R | V of int
ArbMap.defaults
|> ArbMap.generate<R>
|> Gen.sample 3
// [|{ U = V -10 }; { U = U { U = U { U = U { U = V 27 } } } }; { U = V 8 }|]Is there a good reason for FsCheck not to apply size-control logic similar to what it already does for mutually-recursive unions to all indirectly-recursive records by default, instead of just blowing the stack and forcing the user to write a custom generator? (There could well be a good reason. That's why I'm asking.)