Share your thoughts in the 2024 State of Clojure Survey!

Welcome! Please see the About page for a little more info on how this works.

0 votes
in test.check by

There are a lot of generators that return a sequence (list, vector, tuple, map, string, etc). Sometimes it us useful to set size limits, either min or max, on the generated sequence. It would be nice to have a generator that would accept a sequence generator and ensure that the sequences are of a certain length. Three examples would be:
(of-length min max gen)
(of-max-length max gen) => (of-length 0 max gen)
(of-min-length min gen) => (of-length min nil gen)

of-length check the length of the generated sequence. If it is too small, use (take min (cycle s)) to extend the length of the sequence.
If it is too long, use (take max s) to return the max length
It will need to be careful to return the same type it received.
If it does not receive a sequence, treat it as though it was one element sequence.
If min is not 0, use such-that not nil to ensure a proper seq is generated.

22 Answers

0 votes
by

Comment made by: m0smith

TCHECK-99-3.patch is updated to support a :sep option. Also, had it based upon a new function 'apply-to' that will be useful for TCHECK-97 as well. See https://github.com/m0smith/test.check/tree/feature/TCHECK-97 for the proposed Unicode implementation

0 votes
by

Comment made by: m0smith

This one has support for suffix and prefix. Using this one makes thing like HTML much easier

0 votes
by
_Comment made by: m0smith_

Here is an example of how to-string would be used to simulate the lines in a tab-separated file


(def gen-char-upper (gen/fmap char (gen/choose \A \Z)))
(def gen-char-lower (gen/fmap char (gen/choose \a \z)))
(def gen-char-digit (gen/fmap char (gen/choose \0 \9)))
(def gen-char-postal-format (gen/fmap char (gen/one-of [(gen/return \#)
                                                        (gen/return \@)
                                                        gen-char-upper])))

(def gen-iso-2 (gen/to-string gen-char-upper {:num-elements 2}))
(def gen-iso-3 (gen/to-string gen-char-upper {:num-elements 3}))
(def gen-iso-numeric (gen/to-string gen-char-digit {:num-elements 3}))
(def gen-fips (gen/to-string gen-char-upper {:num-elements 2}))
(def gen-country gen/string)
(def gen-capital gen/string)
(def gen-area  (gen/large-integer* {:min 100 :max 999999999}))
(def gen-population (gen/large-integer* {:min 100 :max 999999999}))
(def gen-continent (gen/elements ["AS" "NA" "SA" "EU" "OC" "AF"]))
(def gen-tld (gen/to-string gen-char-lower {:num-elements 2 :prefix "."}))
(def gen-currency-code (gen/to-string gen-char-upper {:num-elements 3}))
(def gen-currency-name gen/string)
(def gen-phone  (gen/large-integer* {:min 1 :max 999}))
(def gen-simple-language (gen/to-string gen-char-lower {:num-elements 2}))
(def gen-language (gen/one-of [gen-simple-language
                               (gen/to-string [gen-simple-language (gen/return "-") gen-iso-2])]))
(def gen-languages (gen/to-string gen-language {:sep ","}))
(def gen-geonameid (gen/large-integer* {:min 1 :max 999999999}))
(def gen-neighbors (gen/to-string gen-iso-2 {:sep ","}))
(def gen-equivilent-fips (gen/to-string gen-char-upper {:num-elements 2}))

(defn to-postal-regex [postal-format]
  (str "^" (clojure.string/join (map #(cond (= \# %) "\\d" (= \@ %) "[A-Z]" :default %) postal-format)) "$"))

(def gen-country-line
  (gen/let [postal-format (gen/to-string gen-char-postal-format {:min-elements 2 :max-element 9})]
    (gen/to-string [gen-iso-2 gen-iso-3 gen-iso-numeric gen-fips
                    gen-country gen-capital gen-area gen-population gen-continent
                    gen-tld gen-currency-code gen-currency-name gen-phone
                    (gen/return postal-format) (gen/return (to-postal-regex postal-format))
                    gen-languages gen-geonameid gen-neighbors gen-equivilent-fips
                    ] {:sep \tab})))

0 votes
by

Comment made by: gfredericks

If I had been trying to write a generator like {{gen-country-line}} I probably would have combined {{gen/tuple}} and {{string/join}}, e.g.:

(gen/let [fields (gen/tuple gen-iso-2 gen-iso-3 ... gen-equivilent-fips)] (string/join \tab fields))

it looks like you wrote gen/to-string to accept either a generator or a vector of generators? I don't like that kind of ambiguity generally, and I think the example used of {{gen/let}} just above demonstrates that you can live without the vector feature without much pain.

Is the {{:sep}} feature still as compelling without the vector feature? I'm a little unsure about {{:sep}}, at least inasmuch as I'm hesitant to add features in general without a strong reason to.

0 votes
by
_Comment made by: m0smith_

I see what you are saying and that makes a lot of sense.  It would be good to include that in the docs as an example.

The :sep is used between the fields of the result regardless if one generator or multiple are provided.  It makes it nice to create a comma separated list of integers (to-string int {:sep ","})
0 votes
by

Comment made by: m0smith

I removed all the extra options to to-string. I also added an example of creating an XML generator.

0 votes
by
Reference: https://clojure.atlassian.net/browse/TCHECK-99 (reported by m0smith)
...