Skip to content

Commit ffd4d05

Browse files
author
dnolen
committed
port of clojure.spec.gen namespaces
1 parent a5cf24c commit ffd4d05

File tree

2 files changed

+205
-0
lines changed

2 files changed

+205
-0
lines changed

src/main/cljs/cljs/spec/gen.clj

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
; Copyright (c) Rich Hickey. All rights reserved.
2+
; The use and distribution terms for this software are covered by the
3+
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4+
; which can be found in the file epl-v10.html at the root of this distribution.
5+
; By using this software in any fashion, you are agreeing to be bound by
6+
; the terms of this license.
7+
; You must not remove this notice, or any other, from this software.
8+
9+
(ns cljs.spec.gen
10+
(:refer-clojure :exclude [delay])
11+
(:require [cljs.core :as c]))
12+
13+
(defmacro dynaload [[quote s]]
14+
`(if (c/exists? ~s)
15+
~(vary-meta s assoc :cljs.analyzer/no-resolve true)
16+
(fn [& ~'args]
17+
(throw (js/Error. (str "Var " '~s " is not on the classpath"))))))
18+
19+
(defmacro delay
20+
"given body that returns a generator, returns a
21+
generator that delegates to that, but delays
22+
creation until used."
23+
[& body]
24+
`(cljs.spec.gen/delay-impl (c/delay ~@body)))
25+
26+
(defmacro ^:skip-wiki lazy-combinator
27+
"Implementation macro, do not call directly."
28+
[s]
29+
(let [fqn (symbol "clojure.test.check.generators" (name s))
30+
doc (str "Lazy loaded version of " fqn)]
31+
`(let [g# (c/delay (dynaload '~fqn))]
32+
(defn ~s
33+
~doc
34+
[& ~'args]
35+
(apply @g# ~'args)))))
36+
37+
(defmacro ^:skip-wiki lazy-combinators
38+
"Implementation macro, do not call directly."
39+
[& syms]
40+
`(do
41+
~@(map
42+
(fn [s] (list `lazy-combinator s))
43+
syms)))
44+
45+
(defmacro ^:skip-wiki lazy-prim
46+
"Implementation macro, do not call directly."
47+
[s]
48+
(let [fqn (symbol "clojure.test.check.generators" (name s))
49+
doc (str "Fn returning " fqn)]
50+
`(let [g# (c/delay (dynaload '~fqn))]
51+
(defn ~s
52+
~doc
53+
[& ~'args]
54+
@g#))))
55+
56+
(defmacro ^:skip-wiki lazy-prims
57+
"Implementation macro, do not call directly."
58+
[& syms]
59+
`(do
60+
~@(map
61+
(fn [s] (list `lazy-prim s))
62+
syms)))

src/main/cljs/cljs/spec/gen.cljs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
; Copyright (c) Rich Hickey. All rights reserved.
2+
; The use and distribution terms for this software are covered by the
3+
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4+
; which can be found in the file epl-v10.html at the root of this distribution.
5+
; By using this software in any fashion, you are agreeing to be bound by
6+
; the terms of this license.
7+
; You must not remove this notice, or any other, from this software.
8+
9+
(ns cljs.spec.gen
10+
(:refer-clojure :exclude [boolean cat hash-map list map not-empty set vector
11+
char double int keyword symbol string uuid delay])
12+
(:require-macros [cljs.core :as c]
13+
[cljs.spec.gen :as gen :refer [dynaload lazy-combinators lazy-prims]])
14+
(:require [cljs.core :as c]))
15+
16+
(def ^:private quick-check-ref
17+
(c/delay (dynaload 'clojure.test.check/quick-check)))
18+
19+
(defn quick-check
20+
[& args]
21+
(apply @quick-check-ref args))
22+
23+
(def ^:private for-all*-ref
24+
(c/delay (dynaload 'clojure.test.check.properties/for-all*)))
25+
26+
(defn for-all*
27+
"Dynamically loaded clojure.test.check.properties/for-all*."
28+
[& args]
29+
(apply @for-all*-ref args))
30+
31+
(let [g? (c/delay (dynaload 'clojure.test.check.generators/generator?))
32+
g (c/delay (dynaload 'clojure.test.check.generators/generate))
33+
mkg (c/delay (dynaload 'clojure.test.check.generators/->Generator))]
34+
(defn- generator?
35+
[x]
36+
(@g? x))
37+
(defn- generator
38+
[gfn]
39+
(@mkg gfn))
40+
(defn generate
41+
"Generate a single value using generator."
42+
[generator]
43+
(@g generator)))
44+
45+
(defn ^:skip-wiki delay-impl
46+
[gfnd]
47+
;;N.B. depends on test.check impl details
48+
(generator (fn [rnd size]
49+
((:gen @gfnd) rnd size))))
50+
51+
;(defn gen-for-name
52+
; "Dynamically loads test.check generator named s."
53+
; [s]
54+
; (let [g (dynaload s)]
55+
; (if (generator? g)
56+
; g
57+
; (throw (js/Error. (str "Var " s " is not a generator"))))))
58+
59+
(lazy-combinators hash-map list map not-empty set vector fmap elements
60+
bind choose one-of such-that tuple sample return)
61+
62+
(lazy-prims any any-printable boolean char char-alpha char-alphanumeric char-ascii double
63+
int keyword keyword-ns large-integer ratio simple-type simple-type-printable
64+
string string-ascii string-alphanumeric symbol symbol-ns uuid)
65+
66+
(defn cat
67+
"Returns a generator of a sequence catenated from results of
68+
gens, each of which should generate something sequential."
69+
[& gens]
70+
(fmap #(apply concat %)
71+
(apply tuple gens)))
72+
73+
(def ^:private
74+
gen-builtins
75+
(c/delay
76+
(let [simple (simple-type-printable)]
77+
{number? (one-of [(large-integer) (double)])
78+
integer? (large-integer)
79+
;float? (double)
80+
string? (string-alphanumeric)
81+
keyword? (keyword-ns)
82+
symbol? (symbol-ns)
83+
map? (map simple simple)
84+
vector? (vector simple)
85+
list? (list simple)
86+
seq? (list simple)
87+
char? (char)
88+
set? (set simple)
89+
nil? (return nil)
90+
false? (return false)
91+
true? (return true)
92+
zero? (return 0)
93+
;rational? (one-of [(large-integer) (ratio)])
94+
coll? (one-of [(map simple simple)
95+
(list simple)
96+
(vector simple)
97+
(set simple)])
98+
empty? (elements [nil '() [] {} #{}])
99+
associative? (one-of [(map simple simple) (vector simple)])
100+
sequential? (one-of [(list simple) (vector simple)])
101+
;ratio? (such-that ratio? (ratio))
102+
})))
103+
104+
(defn gen-for-pred
105+
"Given a predicate, returns a built-in generator if one exists."
106+
[pred]
107+
(if (set? pred)
108+
(elements pred)
109+
(get @gen-builtins pred)))
110+
111+
(comment
112+
(require 'clojure.test.check)
113+
(require 'clojure.test.check.properties)
114+
(require 'cljs.spec.gen)
115+
(in-ns 'cljs.spec.gen)
116+
117+
;; combinators, see call to lazy-combinators above for complete list
118+
(generate (one-of [(gen-for-pred integer?) (gen-for-pred string?)]))
119+
(generate (such-that #(< 10000 %) (gen-for-pred integer?)))
120+
(let [reqs {:a (gen-for-pred number?)
121+
:b (gen-for-pred ratio?)}
122+
opts {:c (gen-for-pred string?)}]
123+
(generate (bind (choose 0 (count opts))
124+
#(let [args (concat (seq reqs) (shuffle (seq opts)))]
125+
(->> args
126+
(take (+ % (count reqs)))
127+
(mapcat identity)
128+
(apply hash-map))))))
129+
(generate (cat (list (gen-for-pred string?))
130+
(list (gen-for-pred ratio?))))
131+
132+
;; load your own generator
133+
(gen-for-name 'clojure.test.check.generators/int)
134+
135+
;; failure modes
136+
(gen-for-name 'unqualified)
137+
(gen-for-name 'clojure.core/+)
138+
(gen-for-name 'clojure.core/name-does-not-exist)
139+
(gen-for-name 'ns.does.not.exist/f)
140+
141+
)
142+
143+

0 commit comments

Comments
 (0)