Hey Mayank!
What you've done here might appear to work, but it will get you into
trouble when test.check starts to shrink your inputs. When test.check runs
your generators it relies you you using it as your only source of
randomness, and so your use of `rand-int` will cause some problems.
The trick is to use the `bind` function to make a generator which depends
on the value of another generator (in this case, to capture the generated
map so you can call rand-subset with the correct set of keys):
(defn rand-subset
"Given a collection coll
it'll generate a random subset."
[coll]
(gen/fmap (fn [i] (combo/nth-subset coll i))
(gen/choose 0 (dec (combo/count-subsets coll)))))
(defn gen-varying-map
"Given a generator which generates a map,
it'll randomly select keys from it thus making it
varying-sized map.
Note: It can return empty maps as well."
[map-gen]
(gen/bind map-gen
(fn [map]
(gen/fmap (fn [keyseq]
(select-keys map keyseq))
(rand-subset (keys map))))))
(gen/sample (gen-varying-map (gen/hash-map
"user" (gen/such-that not-empty
gen/string-alpha-numeric)
"level" gen/nat
"timezone" gen/pos-int)))
=>
({"user" "e", "level" 0}
{"level" 1}
{"user" "M1"}
{"timezone" 2}
{"user" "2", "level" 2, "timezone" 0}
{"timezone" 3}
{"user" "W", "level" 5, "timezone" 0}
{"timezone" 5}
{}
{})
This output appears the same as yours, but it will produce predictable
shrink trees, and thus will shrink more effectively.
Carlo
On 26 July 2015 at 07:10, Mayank Jain <[email protected]> wrote:
> Hi,
>
> I would like to generate variable sized map using test.check i.e.
> given any generator which generates a map, it should randomly select-keys
> from it.
>
> Here's what I've come up with so far:
>
>
>> (ns proj.util
> (:require [clojure.test.check.generators :as gen]
> [clojure.math.combinatorics :as combo]))
>
> (defn rand-subset
> "Given a collection coll,
> it'll generate a random subset."
> [coll]
> (->> coll
> combo/count-subsets
> rand-int
> (combo/nth-subset coll)))
>
> (defn gen-varying-map
> "Given a generator which generates a map,
> it'll randomly select keys from it thus making it
> varying-sized map.
> Note: It can return empty maps as well."
> [map-gen]
> (gen/fmap (fn [m]
> (let [ks (rand-subset (keys m))]
> (select-keys m ks)))
> map-gen))
>
> Here's an example output,
> (gen/sample (gen-varying-map (gen/hash-map
> "user" (gen/such-that not-empty
>
> gen/string-alphanumeric)
> "level" gen/nat
> "timezone" gen/pos-int)))
> =>
> ({"user" "1"}
> {"user" "l8", "level" 0, "timezone" 1}
> {"level" 1}
> {"user" "oA", "timezone" 0}
> {"level" 2, "timezone" 1}
> {"level" 5}
> {"user" "8aP", "level" 5, "timezone" 6}
> {"user" "035rqi", "level" 7}
> {"timezone" 4}
> {"timezone" 2})
>
> My question is, is this the right way? Or is there a better way to do it?
>
> Thanks :)
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to [email protected]
> Note that posts from new members are moderated - please be patient with
> your first post.
> To unsubscribe from this group, send email to
> [email protected]
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> For more options, visit https://groups.google.com/d/optout.
>
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.