Skip to content

Commit

Permalink
Merge pull request #2 from liquidz/dev
Browse files Browse the repository at this point in the history
1.3.0
  • Loading branch information
liquidz committed May 20, 2020
2 parents 1173c2f + 93f71e4 commit 25964a7
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 66 deletions.
47 changes: 37 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Yet another doctest implementation in Clojure

testdoc extends `clojure.test/is` macro.

### REPL style

```clojure
=> (require '[clojure.test :as t]
=> 'testdoc.core)
Expand All @@ -22,32 +24,57 @@ nil
=> => (myplus 1 2)
=> 3
=> => (myplus 2
=> => 3)
=> => 3)
=> 5"
=> [a b]
=> (+ a b))
any?
var?

=> (t/deftest myplus-test
=> (t/is (testdoc #'myplus)))
any?
var?

=> (t/test-var *1)
nil
```

### Code-first style

```clojure
(defn mymulti
"Multiply a and b
(mymulti 1 2)
;; => 2
(mymulti 2
3)
;; => 6"
[a b]
(* a b))
;; => var?

(t/deftest mymulti-test
(t/is (testdoc #'mymulti)))
;; => var?

(t/test-var *1)
;; => nil
```

### Testing external documents

This document is tested by this library, of course!

```clojure
=> (require '[clojure.java.io :as io])
nil
(require '[clojure.java.io :as io])
;; => nil

=> (t/deftest external-document-test
=> (t/is (testdoc (slurp (io/file "test/resources/README.adoc")))))
any?
(t/deftest external-document-test
(t/is (testdoc (slurp (io/file "test/resources/README.adoc")))))
;; => var?

=> (t/test-var *1)
nil
(t/test-var *1)
;; => nil
```

## Other works
Expand Down
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject testdoc "1.2.0"
(defproject testdoc "1.3.0"
:description "Yet another doctest implementation in Clojure"
:url "https://github.com/liquidz/testdoc"
:license {:name "Eclipse Public License"
Expand Down
64 changes: 33 additions & 31 deletions src/testdoc/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,18 @@
(:require
[clojure.string :as str]
[clojure.test :as t]
[clojure.walk :as walk]))
[clojure.walk :as walk]
[testdoc.style.code-first :as style.code-first]
[testdoc.style.repl :as style.repl]))

(defn- fn?'
[x]
(fn? (cond-> x (var? x) deref)))

(defn- form-line?
[s]
(str/starts-with? s "=>"))

(defn- join-forms
[lines]
(:result
(reduce (fn [{:keys [tmp result] :as m} line]
(cond
(form-line? line)
(assoc m :tmp (str/trim (str tmp "\n" (str/trim (subs line 2)))))

(seq tmp)
(assoc m :tmp "" :result (conj result tmp line))

:else m))
{:tmp "" :result []} lines)))

(defn- parse-doc
[doc]
(-> (str/trim doc)
(str/split #"[\r\n]+")
(->> (map str/trim)
(remove str/blank?)
(drop-while (complement form-line?))
join-forms
(map (comp read-string str/trim))
(partition 2))))
(concat (style.repl/parse-doc doc)
(style.code-first/parse-doc doc)))

(defn- replace-publics
[x publics]
Expand Down Expand Up @@ -70,18 +48,27 @@
:actual actual})))
[] tests)))

(defn testdoc
[msg x]
(defn extract-document
[x]
(cond
(var? x)
(let [{ns' :ns doc :doc} (meta x)
publics (ns-publics ns')]
(testdoc* msg doc publics))
[doc publics])

(string? x)
(testdoc* msg x {})
[x {}]

:else
nil))

(defn testdoc
[msg x]
(if-let [[doc publics] (extract-document x)]
(if (str/blank? doc)
[{:type :fail
:message (format "No document: %s" x)}]
(testdoc* msg doc publics))
[{:type :fail
:message (format "Unsupported document: %s" x)}]))

Expand All @@ -90,3 +77,18 @@
`(doseq [result# (testdoc ~msg ~form)]
(t/do-report result#)))

(defn debug
"Print parsed codes and expected results
```
=> (debug \"=> (+ 1 2)\\n3\")
nil
=> (debug \"(+ 1 2)\\n;; => 3\")
nil
```"
[x]
(doseq [[x y] (some-> x extract-document first parse-doc)]
(println "----")
(println "CODE >>" (pr-str x))
(println "EXPECTED >>" (pr-str y))))
50 changes: 50 additions & 0 deletions src/testdoc/style/code_first.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
(ns testdoc.style.code-first
(:require
[clojure.string :as str]))

(def ^:private output-line-re #"^;+ ?=>")

(defn- output-line?
[s]
(some? (re-seq output-line-re s)))

(defn- calc-level
[s]
(- (count (filter #{\(} s))
(count (filter #{\)} s))))

(defn- join-forms
[lines]
(:result
(reduce
(fn [{:keys [tmp result level] :as m} line]
(cond
(and (or (empty? tmp)
(= 1 (count tmp)))
(output-line? line))
(let [expected (str/trim (str/replace line output-line-re ""))]
(assoc m :tmp [expected] :level 0))

(seq tmp)
(let [next-level (+ level (calc-level line))
[expected & codes :as tmp] (conj tmp line)]
(if (= 0 next-level)
(assoc m :tmp [] :level 0 :result (conj result
(str/join "\n" (reverse codes))
expected))
(assoc m :tmp tmp :level next-level)))

:else m))
{:tmp [] :result [] :level 0}
(reverse lines))))

(defn parse-doc
[doc]
(-> (str/trim doc)
(str/split #"[\r\n]+")
(->> (map str/trim)
(remove str/blank?)
join-forms
(map (comp read-string str/trim))
(partition 2)
reverse)))
35 changes: 35 additions & 0 deletions src/testdoc/style/repl.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
(ns testdoc.style.repl
(:require
[clojure.string :as str]))

(def ^:private form-line-str "=>")

(defn- form-line?
[s]
(str/starts-with? s form-line-str))

(defn- join-forms
[lines]
(let [n (count form-line-str)]
(:result
(reduce (fn [{:keys [tmp result] :as m} line]
(cond
(form-line? line)
(assoc m :tmp (str/trim (str tmp "\n" (str/trim (subs line n)))))

(seq tmp)
(assoc m :tmp "" :result (conj result tmp line))

:else m))
{:tmp "" :result []} lines))))

(defn parse-doc
[doc]
(-> (str/trim doc)
(str/split #"[\r\n]+")
(->> (map str/trim)
(remove str/blank?)
(drop-while (complement form-line?))
join-forms
(map (comp read-string str/trim))
(partition 2))))
86 changes: 62 additions & 24 deletions test/testdoc/core_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,83 @@
[clojure.test :as t]
[testdoc.core :as sut]))

(t/deftest join-forms-test
(t/are [x y] (= x (#'sut/join-forms y))
["a" "b"] ["=> a" "b"]
["a\nb" "c"] ["=> a" "=> b" "c"]
["a" "b" "c" "d"] ["=> a" "b" "=> c" "d"]
[] ["=> a"]
["a" "b"] ["=> a" "b" "=> c"]
["a" "b"] ["=> a" "b" "c"]))

(defn- success-test-func
(defn- repl-styled-success-test-func
"foo bar
=> (+ 1 2 3)
6
=> (+ 1 2
=> 3 4)
10
=> *1
10
=> (inc *1)
11"
[])

(defn- partial-success-test-func
(defn- code-first-styled-success-test-func
"foo bar
(+ 1 2 3)
;; => 6
(+ 1 2
3 4)
;; => 10
*1
;; => 10
(inc *1)
;; => 11"
[])

(defn- repl-styled-partial-success-test-func
"foo bar
=> (+ 1 2 3)
6
=> (+ 1 2 3 4)
11"
999"
[])

(defn- code-first-styled-partial-success-test-func
"foo bar
(+ 1 2 3)
;; => 6
(+ 1 2 3 4)
;; => 999"
[])

(t/deftest testdoc-test
(t/is (= #{{:type :pass :expected 6 :actual 6}
{:type :pass :expected 10 :actual 10}
{:type :pass :expected 11 :actual 11}}
(->> (sut/testdoc nil #'success-test-func)
(map #(select-keys % [:type :expected :actual]))
set)))

(t/is (= #{{:type :pass :expected 6 :actual 6}
{:type :fail :expected 11 :actual 10}}
(->> (sut/testdoc nil #'partial-success-test-func)
(map #(select-keys % [:type :expected :actual]))
set))))
(t/testing "repl style"
(t/is (= [{:type :pass :expected 6 :actual 6}
{:type :pass :expected 10 :actual 10}
{:type :pass :expected 10 :actual 10}
{:type :pass :expected 11 :actual 11}]
(->> (sut/testdoc nil #'repl-styled-success-test-func)
(map #(select-keys % [:type :expected :actual]))
(sort-by :expected))))

(t/is (= [{:type :pass :expected 6 :actual 6}
{:type :fail :expected 999 :actual 10}]
(->> (sut/testdoc nil #'repl-styled-partial-success-test-func)
(map #(select-keys % [:type :expected :actual]))
(sort-by :expected)))))

(t/testing "code-first style"
(t/is (= [{:type :pass :expected 6 :actual 6}
{:type :pass :expected 10 :actual 10}
{:type :pass :expected 10 :actual 10}
{:type :pass :expected 11 :actual 11}]
(->> (sut/testdoc nil #'code-first-styled-success-test-func)
(map #(select-keys % [:type :expected :actual]))
(sort-by :expected))))

(t/is (= [{:type :pass :expected 6 :actual 6}
{:type :fail :expected 999 :actual 10}]
(->> (sut/testdoc nil #'code-first-styled-partial-success-test-func)
(map #(select-keys % [:type :expected :actual]))
(sort-by :expected))))))

(t/deftest testdoc-unsupported-test
(let [[result :as results] (sut/testdoc nil 123)]
Expand Down Expand Up @@ -77,5 +111,9 @@
=> 3)
5")))

(t/deftest debug-test
(with-out-str
(t/is (testdoc #'sut/debug))))

(t/deftest README-test
(t/is (testdoc (slurp (io/file "README.md")))))
Loading

0 comments on commit 25964a7

Please sign in to comment.