Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FsCheck Generators by Selecting From Pools of Possibilities

Tags:

f#

fscheck

Is there a way to generate a string in FsCheck by selecting just one item from each of a list of strings and then concatenating the result?

I'm just completely stuck and can't seem to figure it out. I've looked at the docs and in the github repo for something similar. And I've done most of my reading on FsCheck from FSharpForFunAndProfit.

This is something like what I would be thinking of:

let rand = System.Random()
let randInt max = rand.Next(0, max)

let selectLetter (string: string) = 
    let whichLettersIndex = String.length string |> randInt
    string.Substring(whichLettersIndex, 1)

let generateOddlySpelledWord listOfStrings = 
    List.map selectLetter listOfStrings
    |> String.concat ""

let usingGenerateOddlySpelledWord =
    generateOddlySpelledWord ["zZ"; "oO0Ò"; "eEê"]

That should generate something like "Z0ê" or "zÒE".

like image 723
Thomas Sobieck Avatar asked Mar 01 '15 19:03

Thomas Sobieck


1 Answers

Does this do what you want?

open FsCheck

let createGenerators (l : string seq) =
    l |> Seq.map Gen.elements |> Seq.toList

type OddlySpelledWords =
    static member String() =
        ["zZ"; "oO0Ò"; "eEê"]
        |> createGenerators
        |> Gen.sequence
        |> Gen.map (List.map string >> String.concat "")
        |> Arb.fromGen

Ad-hoc test:

open FsCheck.Xunit

[<Property(Arbitrary = [| typeof<OddlySpelledWords> |])>]
let test (s : string) =
    printfn "%s" s

Output (truncated):

  z0ê
  ZÒe
  ZOe
  zoê
  ZÒe
  zoê
  Z0e
  zoê
  z0ê
  ZOe
  zÒê
  z0E
  zoe

Explanation

The createGenerators function has the type seq string -> Gen<char> list, and it creates a Gen from each string using Gen.elements, because a string is also a char seq; Gen.elements creates a Gen that will pick one of these char values from each string.

Then it uses Gen.sequence to convert the Gen<char> list into a Gen <char list>, and then maps from there.


BTW, you can also inline createGenerators:

type OddlySpelledWords =
    static member String() =
        ["zZ"; "oO0Ò"; "eEê"]
        |> List.map Gen.elements
        |> Gen.sequence
        |> Gen.map (List.map string >> String.concat "")
        |> Arb.fromGen
like image 61
Mark Seemann Avatar answered Oct 21 '22 00:10

Mark Seemann