3

I would like to test a property where I use 2 probability rates p1 and p2 that must satisfy 0 < p1 < p2 < 1

let arraySizeCheck (p1:float, p2:float, xs:list<int>) =
(p1 < p2 && p1 > 0.0 && p1 < 1.0 && p2 > 0.0 && p2 < 1.0 && Seq.length xs > 0) ==>
(lazy
    (
        let bf1 = BloomFilter(p1, xs)
        let bf2 = BloomFilter(p2, xs)
        bf2.BitArraySize < bf1.BitArraySize
    )
)

Check.Quick arraySizeCheck

I have tried the above example but the test result seems to be

Arguments exhausted after 0 tests. val it : unit = ()

Furthermore, I would prefer is the list xs contains no duplicates. Any help towards writing a test for this property would be appreciated. Thanks.

carstenj
  • 693
  • 5
  • 13

1 Answers1

2

A more idiomatic approach would be to use the gen computation expression and combinator functions from the Gen module. Unfortunately, the function that returns a generator that generates a random floating point number is hidden (see source code.)

But we can create our own:

open System
open FsCheck
open FsCheck.Gen
open FsCheck.Prop

/// Generates a random float [0..1.0]
let rand = Gen.choose (0, Int32.MaxValue) |> Gen.map (fun x -> double x / (double Int32.MaxValue))

/// Generates a random float in the given range (inclusive at both sides)
let randRange a b = rand |> Gen.map (fun x -> a + (b - a) * x)

let arraySizeCheck =
    Prop.forAll
        (Arb.fromGen <| gen {
            // generate p1 and p2 such that 0 <= p1 <= p2 <= 1
            let! p1 = randRange 0.0 1.0
            let! p2 = randRange p1 1.0

            // generate non-empty Seq<int>
            let! xs = Gen.nonEmptyListOf Arb.generate<int> |> Gen.map Seq.ofList

            return BloomFilter(p1, xs), BloomFilter(p2, xs)
        })
        (fun (bf1, bf2) -> bf2.BitArraySize < bf1.BitArraySize)

Check.Quick arraySizeCheck

Note that p1 and p2 here are generated so that 0 <= p1 <= p2 <= 1, which is not exactly what you need. But I think a simple modification to the rand function can fix that.

MisterMetaphor
  • 5,900
  • 3
  • 24
  • 31
  • 1
    Arb.Default.Float() is the default float arbitrary instance. It has a property Generator to get at the random generator, and Shrinker to get at the default float shrinker. – Kurt Schelfthout Sep 09 '14 at 15:20
  • @KurtSchelfthout, how does one generate a value in the [0; 1] range with that? – MisterMetaphor Oct 18 '14 at 19:07
  • (sorry missed this). It's surprisingly subtle: let f = Arb.generate |> Gen.map float |> Gen.resize 5 |> Gen.map abs |> Gen.suchThat (fun f -> f < 5.) |> Gen.sample 100 10 – Kurt Schelfthout Oct 27 '14 at 22:09