-
Notifications
You must be signed in to change notification settings - Fork 30
/
test_runner.clj
131 lines (119 loc) · 4.28 KB
/
test_runner.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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
(ns cognitect.test-runner
(:require [clojure.tools.namespace.find :as find]
[clojure.java.io :as io]
[clojure.test :as test]
[clojure.tools.cli :as cli])
(:refer-clojure :exclude [test]))
(defn- ns-filter
[{:keys [namespace namespace-regex]}]
(let [[include-ns include-regexes]
(if (or (seq namespace) (seq namespace-regex))
[namespace namespace-regex]
[nil [#".*\-test$"]])]
(fn [ns]
(or
(get include-ns ns)
(some #(re-matches % (name ns)) include-regexes)))))
(defn- var-filter
[{:keys [var include exclude]}]
(let [test-specific (if var
(set (map #(or (resolve %)
(throw (ex-info (str "Could not resolve var: " %)
{:symbol %})))
var))
(constantly true))
test-inclusion (if include
#((apply some-fn include) (meta %))
(constantly true))
test-exclusion (if exclude
#((complement (apply some-fn exclude)) (meta %))
(constantly true))]
#(and (test-specific %)
(test-inclusion %)
(test-exclusion %))))
(defn- filter-vars!
[nses filter-fn]
(doseq [ns nses]
(doseq [[_name var] (ns-publics ns)]
(when (:test (meta var))
(when (not (filter-fn var))
(alter-meta! var #(-> %
(assoc ::test (:test %))
(dissoc :test))))))))
(defn- restore-vars!
[nses]
(doseq [ns nses]
(doseq [[_name var] (ns-publics ns)]
(when (::test (meta var))
(alter-meta! var #(-> %
(assoc :test (::test %))
(dissoc ::test)))))))
(defn- contains-tests?
"Check if a namespace contains some tests to be executed."
[ns]
(some (comp :test meta)
(-> ns ns-publics vals)))
(defn test
[options]
(let [dirs (or (:dir options)
#{"test"})
nses (->> dirs
(map io/file)
(mapcat find/find-namespaces-in-dir))
nses (filter (ns-filter options) nses)]
(println (format "\nRunning tests in %s" dirs))
(dorun (map require nses))
(try
(filter-vars! nses (var-filter options))
(apply test/run-tests (filter contains-tests? nses))
(finally
(restore-vars! nses)))))
(defn- parse-kw
[^String s]
(if (.startsWith s ":") (read-string s) (keyword s)))
(defn- accumulate [m k v]
(update-in m [k] (fnil conj #{}) v))
(def cli-options
[["-d" "--dir DIRNAME" "Name of the directory containing tests. Defaults to \"test\"."
:parse-fn str
:assoc-fn accumulate]
["-n" "--namespace SYMBOL" "Symbol indicating a specific namespace to test."
:parse-fn symbol
:assoc-fn accumulate]
["-r" "--namespace-regex REGEX" "Regex for namespaces to test."
:parse-fn re-pattern
:assoc-fn accumulate]
["-v" "--var SYMBOL" "Symbol indicating the fully qualified name of a specific test."
:parse-fn symbol
:assoc-fn accumulate]
["-i" "--include KEYWORD" "Run only tests that have this metadata keyword."
:parse-fn parse-kw
:assoc-fn accumulate]
["-e" "--exclude KEYWORD" "Exclude tests with this metadata keyword."
:parse-fn parse-kw
:assoc-fn accumulate]
["-H" "--test-help" "Display this help message"]])
(defn- help
[args]
(println "\nUSAGE:\n")
(println "clj -m" (namespace `help) "<options>\n")
(println (:summary args))
(println "\nAll options may be repeated multiple times for a logical OR effect.")
(println "If neither -n nor -r is supplied, use -r #\".*-test$\" (ns'es ending in '-test')"))
(defn -main
"Entry point for the test runner"
[& args]
(let [args (cli/parse-opts args cli-options)]
(if (:errors args)
(do (doseq [e (:errors args)]
(println e))
(help args)
(System/exit 1))
(if (-> args :options :test-help)
(help args)
(try
(let [{:keys [fail error]} (test (:options args))]
(System/exit (if (zero? (+ fail error)) 0 1)))
(finally
;; Only called if `test` raises an exception
(shutdown-agents)))))))