1

I am trying to use the FsCheck library. I can do some tests but preconditions do not seem to work. The code below provides an example:

let sortEven (xs: int list) =
    xs
    |> List.filter (fun x -> x % 2 = 0)
    |> List.sort

let lengthGT0 (xs: int list) = (List.length xs) > 0

[<Property>]
let checkSortEven (xs: int list) =
    lengthGT0 xs ==>
    ((xs |> sortEven |> List.sum) = (xs |> List.rev |> sortEven |> List.max))

Calling the function checkSortEven with an empty list as its argument generates an exception because List.rev does the same. So, in the code above, I tried to use a precondition, using the ==> operator, to prevent the FsCheck from trying empty lists. However, whether the first list tested is empty or a shrunk list is empty checkSortEven is called an exception is generated:

Falsifiable, after 1 test (0 shrinks) (StdGen (843297067,296354622)):
Original:
[]
with exception:
System.ArgumentException: The input sequence was empty.

Falsifiable, after 1 test (1 shrink) (StdGen (1296284131,296354622)):
Original:
[-1]
Shrunk:
[]
with exception:

    System.ArgumentException: The input sequence was empty.

Falsifiable, after 1 test (2 shrinks) (StdGen (1865393009,296354622)):
Original:
[-1; 1]
Shrunk:
[]
with exception:
System.ArgumentException: The input sequence was empty.

There is a related question on SO at Why is my precondition being ignored on my Property-based test? but it seems to be a different case (nested functions).

I also tried Check.QuickThrowOnFailure instead of Check.Quick with basically the same results.

Any ideas on why preconditions are not working in this example?

Soldalma
  • 4,636
  • 3
  • 25
  • 38
  • 2
    You might have to make the right-hand-side lazy: `lengthGT0 xs ==> (lazy ((xs |> sortEven |> List.sum) = (xs |> List.rev |> sortEven |> List.max)))`. – Lee Sep 22 '17 at 12:23
  • @Lee - Thanks. Your suggestion works, although the output is not as clean as it could be. If the original list is long enough one gets a clear failure. However, when the original sequence is `[1]` I still get `Unhandled Exception: System.Exception: Falsifiable, after 1 test (0 shrinks) (StdGen (730361002,296354629)): Original: [1] with exception: System.ArgumentException: The input sequence was empty.` The difference from the results in the text is that now the empty sequence `[]` does not show up explicitly. – Soldalma Sep 22 '17 at 12:31
  • OTOH after trying many times I did not get a single failure with `[]` as the original list, so in that sense using `lazy` effectively fixes the problem. – Soldalma Sep 22 '17 at 12:35
  • 1
    `[1]` is a genuine failure case in your property though isn't it? You will end up calling `List.max` with an empty list. – Lee Sep 22 '17 at 12:36
  • You are right, I had forgotten that sortEven would change the list [1] into an empty list. – Soldalma Sep 22 '17 at 19:47

0 Answers0