184 lines
6.0 KiB
Clojure
184 lines
6.0 KiB
Clojure
; Copyright (c) Rich Hickey. All rights reserved.
|
|
; The use and distribution terms for this software are covered by the
|
|
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
; which can be found in the file epl-v10.html at the root of this distribution.
|
|
; By using this software in any fashion, you are agreeing to be bound by
|
|
; the terms of this license.
|
|
; You must not remove this notice, or any other, from this software.
|
|
|
|
(ns cljs.spec.gen.alpha
|
|
(:refer-clojure :exclude [boolean cat hash-map list map not-empty set vector
|
|
char double int keyword symbol string uuid delay shuffle])
|
|
(:require-macros [cljs.core :as c]
|
|
[cljs.spec.gen.alpha :as gen :refer [dynaload lazy-combinators lazy-prims]])
|
|
(:require [cljs.core :as c])
|
|
(:import (goog Uri)))
|
|
|
|
(deftype LazyVar [f ^:mutable cached]
|
|
IDeref
|
|
(-deref [this]
|
|
(if-not (nil? cached)
|
|
cached
|
|
(let [x (f)]
|
|
(when-not (nil? x)
|
|
(set! cached x))
|
|
x))))
|
|
|
|
(def ^:private quick-check-ref
|
|
(dynaload 'clojure.test.check/quick-check))
|
|
|
|
(defn quick-check
|
|
[& args]
|
|
(apply @quick-check-ref args))
|
|
|
|
(def ^:private for-all*-ref
|
|
(dynaload 'clojure.test.check.properties/for-all*))
|
|
|
|
(defn for-all*
|
|
"Dynamically loaded clojure.test.check.properties/for-all*."
|
|
[& args]
|
|
(apply @for-all*-ref args))
|
|
|
|
(let [g? (dynaload 'clojure.test.check.generators/generator?)
|
|
g (dynaload 'clojure.test.check.generators/generate)
|
|
mkg (dynaload 'clojure.test.check.generators/->Generator)]
|
|
(defn- generator?
|
|
[x]
|
|
(@g? x))
|
|
(defn- generator
|
|
[gfn]
|
|
(@mkg gfn))
|
|
(defn generate
|
|
"Generate a single value using generator."
|
|
[generator]
|
|
(@g generator)))
|
|
|
|
(defn ^:skip-wiki delay-impl
|
|
[gfnd]
|
|
;;N.B. depends on test.check impl details
|
|
(generator (fn [rnd size]
|
|
((:gen @gfnd) rnd size))))
|
|
|
|
;(defn gen-for-name
|
|
; "Dynamically loads test.check generator named s."
|
|
; [s]
|
|
; (let [g (dynaload s)]
|
|
; (if (generator? g)
|
|
; g
|
|
; (throw (js/Error. (str "Var " s " is not a generator"))))))
|
|
|
|
(lazy-combinators hash-map list map not-empty set vector vector-distinct fmap elements
|
|
bind choose one-of such-that tuple sample return
|
|
large-integer* double* frequency shuffle)
|
|
|
|
(lazy-prims any any-printable boolean char char-alpha char-alphanumeric char-ascii double
|
|
int keyword keyword-ns large-integer ratio simple-type simple-type-printable
|
|
string string-ascii string-alphanumeric symbol symbol-ns uuid)
|
|
|
|
(defn cat
|
|
"Returns a generator of a sequence catenated from results of
|
|
gens, each of which should generate something sequential."
|
|
[& gens]
|
|
(fmap #(apply concat %)
|
|
(apply tuple gens)))
|
|
|
|
(defn- ^boolean qualified? [ident] (not (nil? (namespace ident))))
|
|
|
|
(def ^:private
|
|
gen-builtins
|
|
(c/delay
|
|
(let [simple (simple-type-printable)]
|
|
{any? (one-of [(return nil) (any-printable)])
|
|
some? (such-that some? (any-printable))
|
|
number? (one-of [(large-integer) (double)])
|
|
integer? (large-integer)
|
|
int? (large-integer)
|
|
pos-int? (large-integer* {:min 1})
|
|
neg-int? (large-integer* {:max -1})
|
|
nat-int? (large-integer* {:min 0})
|
|
float? (double)
|
|
double? (double)
|
|
string? (string-alphanumeric)
|
|
ident? (one-of [(keyword-ns) (symbol-ns)])
|
|
simple-ident? (one-of [(keyword) (symbol)])
|
|
qualified-ident? (such-that qualified? (one-of [(keyword-ns) (symbol-ns)]))
|
|
keyword? (keyword-ns)
|
|
simple-keyword? (keyword)
|
|
qualified-keyword? (such-that qualified? (keyword-ns))
|
|
symbol? (symbol-ns)
|
|
simple-symbol? (symbol)
|
|
qualified-symbol? (such-that qualified? (symbol-ns))
|
|
uuid? (uuid)
|
|
uri? (fmap #(Uri. (str "http://" % ".com")) (uuid))
|
|
inst? (fmap #(js/Date. %)
|
|
(large-integer))
|
|
seqable? (one-of [(return nil)
|
|
(list simple)
|
|
(vector simple)
|
|
(map simple simple)
|
|
(set simple)
|
|
(string-alphanumeric)])
|
|
indexed? (vector simple)
|
|
map? (map simple simple)
|
|
vector? (vector simple)
|
|
list? (list simple)
|
|
seq? (list simple)
|
|
char? (char)
|
|
set? (set simple)
|
|
nil? (return nil)
|
|
false? (return false)
|
|
true? (return true)
|
|
boolean? (boolean)
|
|
zero? (return 0)
|
|
;rational? (one-of [(large-integer) (ratio)])
|
|
coll? (one-of [(map simple simple)
|
|
(list simple)
|
|
(vector simple)
|
|
(set simple)])
|
|
empty? (elements [nil '() [] {} #{}])
|
|
associative? (one-of [(map simple simple) (vector simple)])
|
|
sequential? (one-of [(list simple) (vector simple)])
|
|
;ratio? (such-that ratio? (ratio))
|
|
})))
|
|
|
|
(defn gen-for-pred
|
|
"Given a predicate, returns a built-in generator if one exists."
|
|
[pred]
|
|
(if (set? pred)
|
|
(elements pred)
|
|
(get @gen-builtins pred)))
|
|
|
|
(comment
|
|
(require 'clojure.test.check)
|
|
(require 'clojure.test.check.properties)
|
|
(require 'cljs.spec.gen)
|
|
(in-ns 'cljs.spec.gen)
|
|
|
|
;; combinators, see call to lazy-combinators above for complete list
|
|
(generate (one-of [(gen-for-pred integer?) (gen-for-pred string?)]))
|
|
(generate (such-that #(< 10000 %) (gen-for-pred integer?)))
|
|
(let [reqs {:a (gen-for-pred number?)
|
|
:b (gen-for-pred keyword?)}
|
|
opts {:c (gen-for-pred string?)}]
|
|
(generate (bind (choose 0 (count opts))
|
|
#(let [args (concat (seq reqs) (c/shuffle (seq opts)))]
|
|
(->> args
|
|
(take (+ % (count reqs)))
|
|
(mapcat identity)
|
|
(apply hash-map))))))
|
|
(generate (cat (list (gen-for-pred string?))
|
|
(list (gen-for-pred integer?))))
|
|
|
|
;; load your own generator
|
|
;(gen-for-name 'clojure.test.check.generators/int)
|
|
|
|
;; failure modes
|
|
;(gen-for-name 'unqualified)
|
|
;(gen-for-name 'clojure.core/+)
|
|
;(gen-for-name 'clojure.core/name-does-not-exist)
|
|
;(gen-for-name 'ns.does.not.exist/f)
|
|
|
|
)
|
|
|
|
|