babashka/test-resources/lib_tests/doric/test/doctest.clj

94 lines
2.3 KiB
Clojure
Raw Normal View History

(ns doric.test.doctest
(:use [clojure.java.io :only [file]]
[clojure.test])
(:import (java.io PushbackReader StringReader)))
(defn fenced-blocks
"detect and extract github-style fenced blocks in a file"
[s]
(map second
(re-seq #"(?m)(?s)^```clojure\n(.*?)\n^```" s)))
(def prompt
;; regex for finding 'foo.bar>' repl prompts
"(?m)\n*^\\S*>\\s*")
(defn skip?
"is a result skippable?"
;; if it's a comment, the answer is yes
[s]
(.startsWith s ";"))
(defn reps
"given a string of read-eval-print sequences, separate the different
'r-e-p's from each other"
[prompt s]
(rest (.split s prompt)))
(defn markdown-tests
"extract all the tests from a markdown file"
[f]
(->> f
slurp
fenced-blocks
(mapcat (partial reps prompt))))
(defn repl-tests
"extract all the tests from a repl-session-like file"
[f]
(->> f
slurp
(reps prompt)))
(defn temp-ns
"create a temporary ns, and return its name"
[]
(binding [*ns* *ns*]
(in-ns (gensym))
(use 'clojure.core)
;; BB-TEST-PATCH: bb can't .getName on ns
(str *ns*)))
(defn eval-in-ns
"evaluate a form inside the given ns-name"
[ns form]
(binding [*ns* *ns*]
(in-ns ns)
(eval form)))
(defn run-doctest
"run a single doctest, reporting success or failure"
[file idx ns test]
(let [r (PushbackReader. (StringReader. test))
form (read r)
expected (.trim (slurp r))
actual (when-not (skip? expected)
(.trim (try
(with-out-str
(pr (eval-in-ns ns form))
(flush))
(catch Exception _
(println _)
(.toString (gensym))))))]
(if (or (skip? expected)
(= actual expected))
(report {:type :pass})
(report {:type :fail
:file file :line idx
:expected expected :actual actual}))))
(defn run-doctests
"use text-extract-fn to get all the tests out of file, and run them
all, reporting success or failure"
[test-extract-fn file]
(let [ns (temp-ns)]
(doseq [[idx t] (map-indexed vector (test-extract-fn file))]
(run-doctest file idx ns t))
(remove-ns ns)))
(comment
;; example usage
(deftest bar-repl
(run-doctests repl-tests "test/bar.repl")))