Skip to content

Commit 546eb66

Browse files
authored
Merge pull request #769 from dvingo/add-dev-start-test
Add tests for malli.dev and malli.dev.cljs
2 parents 9abf52e + 20a1679 commit 546eb66

File tree

8 files changed

+142
-72
lines changed

8 files changed

+142
-72
lines changed

src/malli/dev/cljs.cljc

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"Collects defn schemas from all loaded namespaces and starts instrumentation for
2323
a filtered set of function Vars (e.g. `defn`s). See [[malli.core/-instrument]] for possible options.
2424
Differences from Clojure `malli.dev/start!`:
25+
- The :ns option must be a compile-time literal as the namespace symbol(s) must be available at compile time, not runtime.
2526
- Does not unstrument functions - this is handled by hot reloading.
2627
- Does not emit clj-kondo type annotations. See `malli.clj-kondo/print-cljs!` to print clj-kondo config.
2728
- Does not re-instrument functions if the function schemas change - use hot reloading to get a similar effect."
@@ -30,11 +31,13 @@
3031
;; register all function schemas and instrument them based on the options
3132
;; first clear out all metadata schemas to support dev-time removal of metadata schemas on functions - they should not be instrumented
3233
`(do
33-
(m/-deregister-metadata-function-schemas! :cljs)
34-
(malli.instrument/collect! {:ns ~(vec (ana-api/all-ns))})
35-
(js/console.groupCollapsed "Instrumentation done")
36-
(malli.instrument/instrument! (assoc ~options :data (m/function-schemas :cljs)))
37-
(js/console.groupEnd)))))
34+
(m/-deregister-metadata-function-schemas! :cljs)
35+
(malli.instrument/collect! {:ns ~(if (:ns options)
36+
(:ns options)
37+
(vec (ana-api/all-ns)))})
38+
(js/console.groupCollapsed "Instrumentation done")
39+
(malli.instrument/instrument! (assoc ~options :data (m/function-schemas :cljs)))
40+
(js/console.groupEnd)))))
3841

3942
;; only used by deprecated malli.instrument.cljs implementation
4043
#?(:clj (defmacro deregister-function-schemas! []

src/malli/instrument.clj

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -54,41 +54,43 @@
5454
(let [cljs-find-ns (fn [env] (when (:ns env) (ns-resolve 'cljs.analyzer.api 'find-ns)))
5555
cljs-ns-interns (fn [env] (when (:ns env) (ns-resolve 'cljs.analyzer.api 'ns-interns)))]
5656
(defn -cljs-collect!* [env simple-name {:keys [meta] :as var-map}]
57-
(let [ns (symbol (namespace (:name var-map)))
58-
find-ns' (cljs-find-ns env)
59-
ns-interns' (cljs-ns-interns env)
60-
schema (:malli/schema meta)]
61-
(when schema
62-
(let [-qualify-sym (fn [form]
63-
(if (symbol? form)
64-
(if (simple-symbol? form)
65-
(let [ns-data (find-ns' ns)
66-
intern-keys (set (keys (ns-interns' ns)))]
67-
(cond
68-
;; a referred symbol
69-
(get-in ns-data [:uses form])
70-
(let [form-ns (str (get-in ns-data [:uses form]))]
71-
(symbol form-ns (str form)))
72-
73-
;; interned var
74-
(contains? intern-keys form)
75-
(symbol (str ns) (str form))
76-
77-
:else
78-
;; a cljs.core var, do not qualify it
79-
form))
80-
(let [ns-part (symbol (namespace form))
81-
name-part (name form)
82-
full-ns (get-in (find-ns' ns) [:requires ns-part])]
83-
(symbol (str full-ns) name-part)))
84-
form))
85-
schema* (walk/postwalk -qualify-sym schema)
86-
metadata (assoc
87-
(walk/postwalk -qualify-sym (m/-unlift-keys meta "malli"))
88-
:metadata-schema? true)]
89-
`(do
90-
(m/-register-function-schema! '~ns '~simple-name ~schema* ~metadata :cljs identity)
91-
'~(:name var-map)))))))
57+
;; when collecting google closure or other js code symbols will not have namespaces
58+
(when (namespace (:name var-map))
59+
(let [ns (symbol (namespace (:name var-map)))
60+
find-ns' (cljs-find-ns env)
61+
ns-interns' (cljs-ns-interns env)
62+
schema (:malli/schema meta)]
63+
(when schema
64+
(let [-qualify-sym (fn [form]
65+
(if (symbol? form)
66+
(if (simple-symbol? form)
67+
(let [ns-data (find-ns' ns)
68+
intern-keys (set (keys (ns-interns' ns)))]
69+
(cond
70+
;; a referred symbol
71+
(get-in ns-data [:uses form])
72+
(let [form-ns (str (get-in ns-data [:uses form]))]
73+
(symbol form-ns (str form)))
74+
75+
;; interned var
76+
(contains? intern-keys form)
77+
(symbol (str ns) (str form))
78+
79+
:else
80+
;; a cljs.core var, do not qualify it
81+
form))
82+
(let [ns-part (symbol (namespace form))
83+
name-part (name form)
84+
full-ns (get-in (find-ns' ns) [:requires ns-part])]
85+
(symbol (str full-ns) name-part)))
86+
form))
87+
schema* (walk/postwalk -qualify-sym schema)
88+
metadata (assoc
89+
(walk/postwalk -qualify-sym (m/-unlift-keys meta "malli"))
90+
:metadata-schema? true)]
91+
`(do
92+
(m/-register-function-schema! '~ns '~simple-name ~schema* ~metadata :cljs identity)
93+
'~(:name var-map))))))))
9294

9395
(defmacro cljs-collect!
9496
([] `(cljs-collect! ~{:ns (symbol (str *ns*))}))
@@ -102,7 +104,12 @@
102104
(list? n) (second n)
103105
:else (symbol (str n)))]
104106
(ns-publics' ns-sym)))
105-
(-sequential (:ns opts)))))))
107+
;; support quoted vectors of ns symbols in cljs
108+
(let [nses (:ns opts)
109+
nses (if (and (= 'quote (first nses)) (coll? (second nses)))
110+
(second nses)
111+
nses)]
112+
(-sequential nses)))))))
106113

107114
;;
108115
;; public api

src/malli/instrument.cljs

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,23 @@
4141
(defn -replace-variadic-fn [original-fn n s opts]
4242
(let [accessor "cljs$core$IFn$_invoke$arity$variadic"
4343
arity-fn (g/get original-fn accessor)]
44-
(g/set original-fn "malli$instrument$instrumented?" true)
45-
;; the shape of the argument in the following apply calls are needed to match the call style of the cljs compiler
46-
;; so the user's function gets the arguments as expected
47-
(let [max-fixed-arity (-max-fixed-arity original-fn)
48-
instrumented-variadic-fn (m/-instrument opts (fn [& args]
49-
(let [[fixed-args rest-args] (split-at max-fixed-arity (vec args))
50-
final-args (into (vec fixed-args) [(not-empty rest-args)])]
51-
(apply arity-fn final-args))))
52-
instrumented-wrapper (fn [& args]
53-
(let [[fixed-args rest-args] (split-at max-fixed-arity (vec args))
54-
final-args (vec (apply list* (into (vec fixed-args) (not-empty rest-args))))]
55-
(apply instrumented-variadic-fn final-args)))]
56-
(g/set instrumented-wrapper "malli$instrument$original" arity-fn)
57-
(g/set (-get-prop n s) "malli$instrument$instrumented?" true)
58-
(g/set (-get-prop n s) accessor instrumented-wrapper)
59-
(g/set (-get-ns n) s (meta-fn original-fn {:instrumented-symbol (symbol n s)})))))
44+
(when arity-fn
45+
(g/set original-fn "malli$instrument$instrumented?" true)
46+
;; the shape of the argument in the following apply calls are needed to match the call style of the cljs compiler
47+
;; so the user's function gets the arguments as expected
48+
(let [max-fixed-arity (-max-fixed-arity original-fn)
49+
instrumented-variadic-fn (m/-instrument opts (fn [& args]
50+
(let [[fixed-args rest-args] (split-at max-fixed-arity (vec args))
51+
final-args (into (vec fixed-args) [(not-empty rest-args)])]
52+
(apply arity-fn final-args))))
53+
instrumented-wrapper (fn [& args]
54+
(let [[fixed-args rest-args] (split-at max-fixed-arity (vec args))
55+
final-args (vec (apply list* (into (vec fixed-args) (not-empty rest-args))))]
56+
(apply instrumented-variadic-fn final-args)))]
57+
(g/set instrumented-wrapper "malli$instrument$original" arity-fn)
58+
(g/set (-get-prop n s) "malli$instrument$instrumented?" true)
59+
(g/set (-get-prop n s) accessor instrumented-wrapper)
60+
(g/set (-get-ns n) s (meta-fn original-fn {:instrumented-symbol (symbol n s)}))))))
6061

6162
(defn -replace-multi-arity [original-fn n s opts]
6263
(let [schema (:schema opts)]
@@ -66,11 +67,12 @@
6667
(if (= arity :varargs)
6768
(-replace-variadic-fn original-fn n s opts)
6869
(let [accessor (str "cljs$core$IFn$_invoke$arity$" arity)
69-
arity-fn (g/get original-fn accessor)
70-
instrumented-fn (m/-instrument (assoc opts :schema f-schema) arity-fn)]
71-
(g/set instrumented-fn "malli$instrument$original" arity-fn)
72-
(g/set instrumented-fn "malli$instrument$instrumented?" true)
73-
(g/set (-get-prop n s) accessor instrumented-fn))))))
70+
arity-fn (g/get original-fn accessor)]
71+
(when arity-fn
72+
(let [instrumented-fn (m/-instrument (assoc opts :schema f-schema) arity-fn)]
73+
(g/set instrumented-fn "malli$instrument$original" arity-fn)
74+
(g/set instrumented-fn "malli$instrument$instrumented?" true)
75+
(g/set (-get-prop n s) accessor instrumented-fn))))))))
7476

7577
(defn -replace-fn [original-fn n s opts]
7678
(cond
@@ -92,15 +94,16 @@
9294
(case mode
9395
:instrument (let [original-fn (or (-original v) v)
9496
dgen (as-> (select-keys options [:scope :report :gen]) $
95-
(cond-> $ report (update :report (fn [r] (fn [t data] (r t (assoc data :fn-name (symbol n s)))))))
97+
(cond-> $ report (update :report (fn [r] (fn [t data] (r t (assoc data :fn-name (symbol (name n) (name s))))))))
9698
(merge $ d)
9799
(cond (and gen (true? (:gen d))) (assoc $ :gen gen)
98100
(true? (:gen d)) (dissoc $ :gen)
99101
:else $))]
100102
(if (and skip-instrumented? (-instrumented? v))
101103
(println "skipping" (symbol n s) "already instrumented")
102-
(do (-replace-fn original-fn n s dgen)
103-
(println "..instrumented" (symbol n s)))))
104+
(when original-fn
105+
(-replace-fn original-fn n s dgen)
106+
(println "..instrumented" (symbol n s)))))
104107

105108
:unstrument (when (-instrumented? v)
106109
(let [original-fn (or (-original v) v)]

src/malli/instrument/cljs.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@
157157
replace-var-code (when-let [fn-var (ana-api/resolve env fn-sym)]
158158
(-emit-replace-var-code fn-sym (:meta fn-var) schema-map-with-gen schema))]
159159
(if filters
160-
`(when (some #(% '~ns-sym (var ~fn-sym) ~schema-map) ~filters)
160+
`(when (some #(% '~ns-sym (resolve '~fn-sym) ~schema-map) ~filters)
161161
~replace-var-code)
162162
replace-var-code)))
163163

@@ -187,7 +187,7 @@
187187
(.log js/console "..unstrumented" '~fn-sym)
188188
'~fn-sym))]
189189
(if filters
190-
`(when (some #(% '~ns-sym (var ~fn-sym) ~opts) ~filters)
190+
`(when (some #(% '~ns-sym (resolve '~fn-sym) ~opts) ~filters)
191191
~replace-with-orig)
192192
replace-with-orig)))
193193

test/malli/dev/cljs_test.cljs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
(ns malli.dev.cljs-test
2+
(:require [clojure.test :refer [deftest is testing]]
3+
[malli.core :as m]
4+
[malli.dev.cljs :as md]
5+
[malli.instrument :as mi]
6+
[malli.dev.pretty :as pretty]))
7+
8+
(defn plus
9+
{:malli/schema [:=> [:cat :int] [:int {:max 6}]]}
10+
[x] (inc x))
11+
12+
(defn ->plus [] plus)
13+
14+
(deftest ^:simple start!-test
15+
(testing "malli.dev.cljs/start!"
16+
(testing "without starting"
17+
(is (= "21" ((->plus) "2")))
18+
(is (= 7 ((->plus) 6))))
19+
20+
(testing "instrumentation after starting"
21+
(md/start! {:ns 'malli.dev.cljs-test :filters [(mi/-filter-ns 'malli.dev.cljs-test)]})
22+
(is (thrown-with-msg? js/Error #":malli.core/invalid-input" ((->plus) "2")))
23+
(is (thrown-with-msg? js/Error #":malli.core/invalid-output" ((->plus) 6)))
24+
(m/-deregister-metadata-function-schemas! :cljs)
25+
(mi/unstrument! {:filters [(mi/-filter-ns 'malli.dev.cljs-test)]}))
26+
27+
(testing "instrumentation after starting with reporter"
28+
(md/start! {:ns 'malli.dev.cljs-test :report (pretty/thrower) :filters [(mi/-filter-ns 'malli.dev.cljs-test)]})
29+
(is (thrown-with-msg? js/Error #"Invalid function arguments" ((->plus) "2")))
30+
(is (thrown-with-msg? js/Error #"Invalid function return value" ((->plus) 6)))
31+
(m/-deregister-metadata-function-schemas! :cljs)
32+
(mi/unstrument! {:filters [(mi/-filter-ns 'malli.dev.cljs-test)]}))))

test/malli/dev_test.clj

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
(ns malli.dev-test
2+
(:require [clojure.test :refer [deftest is testing]]
3+
[malli.dev :as md]
4+
[malli.dev.pretty :as pretty]))
5+
6+
(defn plus
7+
{:malli/schema [:=> [:cat :int] [:int {:max 6}]]}
8+
[x] (inc x))
9+
10+
(defn ->plus [] plus)
11+
12+
(deftest start!-test
13+
(testing "malli.dev/start!"
14+
(testing "without starting"
15+
(is (thrown? ClassCastException ((->plus) "2")))
16+
(is (= 7 ((->plus) 6))))
17+
18+
(testing "instrumentation after starting"
19+
(md/start! {:ns *ns*})
20+
(is (thrown-with-msg? Exception #":malli.core/invalid-input" ((->plus) "2")))
21+
(is (thrown-with-msg? Exception #":malli.core/invalid-output" ((->plus) 6)))
22+
(md/stop!))
23+
24+
(testing "instrumentation after starting with reporter"
25+
(md/start! {:ns *ns* :report (pretty/thrower)})
26+
(is (thrown-with-msg? clojure.lang.ExceptionInfo #"Invalid function arguments" ((->plus) "2")))
27+
(is (thrown-with-msg? clojure.lang.ExceptionInfo #"Invalid function return value" ((->plus) 6)))
28+
(md/stop!))))

test/malli/instrument/cljs_test.cljs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@
121121
(is (= 4 (power "2")))
122122
(is (= 36 (power 6)))
123123

124-
125124
(is (= "2" (str-join-mx ["2"])))
126125
(is (= "6" (str-join-mx [6])))
127126

@@ -146,7 +145,6 @@
146145
(mi/collect! {:ns ['malli.instrument.cljs-test 'malli.instrument.fn-schemas]})
147146

148147
(deftest collect!-test
149-
150148
(testing "with instrumentation"
151149
(mi/instrument! {:filters [(mi/-filter-ns 'malli.instrument.cljs-test 'malli.instrument.fn-schemas)]})
152150
(is (thrown-with-msg? js/Error #":malli.core/invalid-input" (str-join [1 "2"])))

test/malli/instrument_test.cljs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,12 @@
142142
(is (= 4 (schemas/power-full "2")))
143143
(is (= 36 (schemas/power-full 6)))))
144144

145-
(mi/collect! {:ns ['malli.instrument-test 'malli.instrument.fn-schemas]})
146-
147145
(deftest ^:simple collect!-test
148146

147+
(mi/collect! {:ns ['malli.instrument-test 'malli.instrument.fn-schemas]})
148+
149149
(testing "with instrumentation"
150150
(mi/instrument! {:filters [(mi/-filter-ns 'malli.instrument-test 'malli.instrument.fn-schemas)]})
151-
(println "calling instrumented fn str-join")
152151
(is (thrown-with-msg? js/Error #":malli.core/invalid-input" (str-join [1 "2"])))
153152
(is (thrown-with-msg? js/Error #":malli.core/invalid-input" (str-join2 [1 "2"])))
154153
(is (thrown-with-msg? js/Error #":malli.core/invalid-input" (str-join3 [1 "2"])))

0 commit comments

Comments
 (0)