|
| 1 | +(ns net.vanderhart.test-runner |
| 2 | + (:require [clojure.tools.namespace.find :as find] |
| 3 | + [clojure.spec.alpha :as s] |
| 4 | + [clojure.java.io :as io] |
| 5 | + [clojure.test :as test] |
| 6 | + [clojure.tools.cli :refer [parse-opts]] |
| 7 | + [clojure.string :as str]) |
| 8 | + (:refer-clojure :exclude [test])) |
| 9 | + |
| 10 | +(defn- ns-filter |
| 11 | + [{:keys [namespace]}] |
| 12 | + (if namespace |
| 13 | + #(namespace %) |
| 14 | + (constantly true))) |
| 15 | + |
| 16 | +(defn- var-filter |
| 17 | + [{:keys [var include exclude]}] |
| 18 | + (let [test-specific (if var |
| 19 | + (set (map #(or (resolve %) |
| 20 | + (throw (ex-info (str "Could not resolve var: " %) |
| 21 | + {:symbol %}))))) |
| 22 | + (constantly true)) |
| 23 | + test-inclusion (if include |
| 24 | + #((apply some-fn include) (meta %)) |
| 25 | + (constantly true)) |
| 26 | + test-exclusion (if exclude |
| 27 | + #((complement (apply some-fn exclude)) (meta %)) |
| 28 | + (constantly true))] |
| 29 | + #(and (test-specific %) |
| 30 | + (test-inclusion %) |
| 31 | + (test-exclusion %)))) |
| 32 | + |
| 33 | +(defn- filter-vars! |
| 34 | + [nses filter-fn] |
| 35 | + (doseq [ns nses] |
| 36 | + (doseq [[name var] (ns-publics ns)] |
| 37 | + (when (:test (meta var)) |
| 38 | + (when (not (filter-fn var)) |
| 39 | + (alter-meta! var #(-> % |
| 40 | + (assoc ::test (:test %)) |
| 41 | + (dissoc :test)))))))) |
| 42 | + |
| 43 | +(defn- restore-vars! |
| 44 | + [nses] |
| 45 | + (doseq [ns nses] |
| 46 | + (doseq [[name var] (ns-publics ns)] |
| 47 | + (when (::test (meta var)) |
| 48 | + (alter-meta! var #(-> % |
| 49 | + (assoc :test (::test %)) |
| 50 | + (dissoc ::test))))))) |
| 51 | +(defn test |
| 52 | + [options] |
| 53 | + (let [dirs (or (:dir options) |
| 54 | + #{"test"}) |
| 55 | + nses (->> dirs |
| 56 | + (map io/file) |
| 57 | + (mapcat find/find-namespaces-in-dir)) |
| 58 | + nses (filter (ns-filter options) nses)] |
| 59 | + (println (format "\nRunning tests in %s" dirs)) |
| 60 | + (dorun (map require nses)) |
| 61 | + (try |
| 62 | + (filter-vars! nses (var-filter options)) |
| 63 | + (apply test/run-tests nses) |
| 64 | + (finally |
| 65 | + (restore-vars! nses))))) |
| 66 | + |
| 67 | +(defn- parse-kw |
| 68 | + [s] |
| 69 | + (if (str/starts-with? s ":") (read-string s) (keyword s))) |
| 70 | + |
| 71 | + |
| 72 | +(defn- accumulate [m k v] |
| 73 | + (update m k (fnil conj #{}) v)) |
| 74 | + |
| 75 | +(def cli-options |
| 76 | + [["-d" "--dir DIRNAME" "Name of the directory containing tests. Defaults to \"test\"." |
| 77 | + :parse-fn str |
| 78 | + :assoc-fn accumulate] |
| 79 | + ["-n" "--namespace SYMBOL" "Symbol indicating a specific namespace to test." |
| 80 | + :parse-fn symbol |
| 81 | + :assoc-fn accumulate] |
| 82 | + ["-v" "--var SYMBOL" "Symbol indicating the fully qualified name of a specific test." |
| 83 | + :parse-fn symbol |
| 84 | + :assoc-fn accumulate] |
| 85 | + ["-i" "--include KEYWORD" "Run only tests that have this metadata keyword." |
| 86 | + :parse-fn parse-kw |
| 87 | + :assoc-fn accumulate] |
| 88 | + ["-e" "--exclude KEYWORD" "Exclude tests with this metadata keyword." |
| 89 | + :parse-fn parse-kw |
| 90 | + :assoc-fn accumulate] |
| 91 | + ["-h" "--help" "Display this help message"]]) |
| 92 | + |
| 93 | +(defn- help |
| 94 | + [args] |
| 95 | + (println "\nUSAGE:\n") |
| 96 | + (println "clj -m" (namespace `help) "<test-dir> <options>\n") |
| 97 | + (println (:summary args)) |
| 98 | + (println "\nAll options may be repeated multiple times for a logical OR effect.") |
| 99 | + ) |
| 100 | + |
| 101 | +(defn -main |
| 102 | + "Entry point for the test runner" |
| 103 | + [& args] |
| 104 | + (let [args (parse-opts args cli-options)] |
| 105 | + (if (-> args :options :help) |
| 106 | + (help args) |
| 107 | + (test (:options args))))) |
| 108 | + |
| 109 | + |
| 110 | +(comment |
| 111 | + |
| 112 | + (-main "test" "-n" "net.vanderhart.test-runner.samples") |
| 113 | + (-main "test") |
| 114 | + |
| 115 | + |
| 116 | + (-main "test" "-v" "net.vanderhart.test-runner.sample-property/first-element-is-min-after-sorting" |
| 117 | + #_"-v" #_"net.vanderhart.test-runner.samples/foobar") |
| 118 | + |
| 119 | + (-main "test" "-i" "integration") |
| 120 | + |
| 121 | + (-main "test" "-e" "integration") |
| 122 | + |
| 123 | + (-main "test") |
| 124 | + |
| 125 | + (-main "-h") |
| 126 | + |
| 127 | + (->> (find/find-namespaces-in-dir (io/file "test")) |
| 128 | + (map #(do (require %) %)) |
| 129 | + (map ns-publics) |
| 130 | + (map vals) |
| 131 | + (apply concat) |
| 132 | + (test/test-vars) |
| 133 | + |
| 134 | + ) |
| 135 | + |
| 136 | + |
| 137 | + |
| 138 | + |
| 139 | + |
| 140 | + ) |
0 commit comments