-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathdoc_test.clj
54 lines (45 loc) · 1.72 KB
/
doc_test.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
(ns
#^{:author "Andy Kish"
:doc "Verifies correctness of example expressions in doc-strings."}
doc-test
(:use clojure.test)
(:use [clojure.contrib.str-utils :only (re-split)]))
(defn- read-expr-pair
"Read two expressions from expr-string and return a tuple of them.
=> (read-expr-pair \"(+ 1 2) 3\")
[(+ 1 2) 3]"
[expr-string]
(with-open [sreader (new java.io.StringReader expr-string)
pbreader (new java.io.PushbackReader sreader)]
[(read pbreader) (read pbreader)]))
(defn- find-expression-strings
"Finds expressions that they belong in a REPL. Namely, the => arrow
beginning a line followed by 2 expressions.
=> (find-expression-strings (str \\newline \"=> ((adder 1) 2) 3\"))
(\" ((adder 1) 2) 3\")"
[docstr]
(drop 1 (re-split #"\n\s*=>" docstr)))
(defn to-is
"Converts a doc-test to forms using clojure.test/is.
=> (to-is (:doc (meta (var read-expr-pair))))
((clojure.test/is (clojure.core/= (read-expr-pair \"(+ 1 2) 3\")
'[(+ 1 2) 3])))"
[doc]
(let [expr-strs (find-expression-strings doc)
exprs (map read-expr-pair expr-strs)]
(map (fn [[expr result]] `(is (= ~expr '~result)))
exprs)))
(defmacro doc-test
"Creates a (deftest ...) form based upon the examples in f's doc."
[f]
(let [f-meta (eval `(meta (var ~f)))
is-statments (to-is (:doc f-meta))]
(if (seq is-statments) ; only make a test if there are doc-tests
`(deftest ~(gensym (str (:name f-meta) "__doc-test__"))
~@is-statments))))
; what the doc-test macro output is shooting for, approximately
;(deftest adder__doc-test__...
; (is (= ((adder 1) 4) 3)))
(doc-test read-expr-pair)
(doc-test find-expression-strings)
(doc-test to-is)