diff --git a/.gitignore b/.gitignore index bf6116d..d2b0d57 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ +.calva/repl.calva-repl +.clj-kondo/.cache .cpcache +.lsp/.cache +.portal/vs-code.edn .rebl .rebl.sh .idea/ diff --git a/src/cognitect/test_runner.clj b/src/cognitect/test_runner.clj index 122e7e8..9263e67 100644 --- a/src/cognitect/test_runner.clj +++ b/src/cognitect/test_runner.clj @@ -1,7 +1,10 @@ (ns cognitect.test-runner - (:require [clojure.tools.namespace.find :as find] + (:require [cognitect.test-runner.protocols :as p] + [clojure.tools.namespace.find :as find] [clojure.java.io :as io] [clojure.test :as test] + [clojure.test.junit :as junit] + [clojure.test.tap :as tap] [clojure.tools.cli :as cli]) (:refer-clojure :exclude [test])) @@ -53,12 +56,36 @@ (assoc :test (::test %)) (dissoc ::test))))))) -(defn- contains-tests? +(defn- ns-contains-tests? "Check if a namespace contains some tests to be executed." [ns] (some (comp :test meta) (-> ns ns-publics vals))) +(defrecord ClojureTestRunner [] + p/TestRunner + (enable-filtering! [_ options nses] + (filter-vars! nses (var-filter options))) + (contains-tests? [_ _ ns] (ns-contains-tests? ns)) + (run-tests [this options nses] + ;; we only care about junit and tap (other test runners might care about + ;; other values) + (if-let [outputs (seq (filter #{'junit 'tap} (:output options)))] + (doseq [output outputs] + (case output + junit + (junit/with-junit-output + (apply test/run-tests (filter #(p/contains-tests? this options %) nses))) + tap + (tap/with-tap-output + (apply test/run-tests (filter #(p/contains-tests? this options %) nses))))) + (apply test/run-tests (filter #(p/contains-tests? this options %) nses)))) + (disable-filtering! [_ _ nses] + (restore-vars! nses))) + +(defn create-clojure-test-runner [] + (->ClojureTestRunner)) + (defn test [options] (let [dirs (or (:dir options) @@ -66,14 +93,24 @@ nses (->> dirs (map io/file) (mapcat find/find-namespaces-in-dir)) - nses (filter (ns-filter options) nses)] + nses (filter (ns-filter options) nses) + tsym (or (:test-runner-fn options) + 'cognitect.test-runner/create-clojure-test-runner) + t-fn (try + (require (symbol (namespace tsym))) + (resolve tsym) + (catch Throwable t + (println "Unable to find test runner function:" tsym) + (throw t))) + ;; create the test runner: + trun (t-fn)] (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)) + (p/enable-filtering! trun options nses) + (p/run-tests trun options nses) (finally - (restore-vars! nses))))) + (p/disable-filtering! trun options nses))))) (defn- parse-kw [^String s] @@ -102,6 +139,11 @@ ["-e" "--exclude KEYWORD" "Exclude tests with this metadata keyword." :parse-fn parse-kw :assoc-fn accumulate] + ["-t" "--test-runner-fn SYMBOL" "Symbol indicating the test runner function to use." + :parse-fn symbol] + [nil "--output SYMBOL" "Output format, specific to the selected test runner." + :parse-fn symbol + :assoc-fn (fn [m k v] (update m k (fnil conj []) v))] ["-H" "--test-help" "Display this help message"]]) (defn- help diff --git a/src/cognitect/test_runner/api.clj b/src/cognitect/test_runner/api.clj index 162211c..ef26be2 100644 --- a/src/cognitect/test_runner/api.clj +++ b/src/cognitect/test_runner/api.clj @@ -4,13 +4,15 @@ [cognitect.test-runner :as tr])) (defn- do-test - [{:keys [dirs nses patterns vars includes excludes]}] + [{:keys [dirs nses patterns vars includes excludes test-runner-fn outputs]}] (let [adapted {:dir (when (seq dirs) (set dirs)) :namespace (when (seq nses) (set nses)) :namespace-regex (when (seq patterns) (map re-pattern patterns)) :var (when (seq vars) (set vars)) :include (when (seq includes) (set includes)) - :exclude (when (seq excludes) (set excludes))}] + :exclude (when (seq excludes) (set excludes)) + :test-runner-fn test-runner-fn + :output (when (seq outputs) (vec outputs))}] (tr/test adapted))) (defn test @@ -22,9 +24,13 @@ * :vars - coll of fully qualified symbols to run tests on * :includes - coll of test metadata keywords to include * :excludes - coll of test metadata keywords to exclude + * :test-runner-fn - symbol identifying test runner creation function + * :outputs - coll of output formats (symbols) to use - If neither :nses nor :patterns is supplied, use `:patterns [\".*-test$\"]`." + If neither :nses nor :patterns is supplied, use `:patterns [\".*-test$\"]`. + + Not all output formats produce a fail/error summary." [opts] - (let [{:keys [fail error]} (do-test opts)] + (let [{:keys [fail error] :or {fail 0, error 0}} (do-test opts)] (when (> (+ fail error) 0) (throw (ex-info "Test failures or errors occurred." {}))))) diff --git a/src/cognitect/test_runner/protocols.clj b/src/cognitect/test_runner/protocols.clj new file mode 100644 index 0000000..dd2301c --- /dev/null +++ b/src/cognitect/test_runner/protocols.clj @@ -0,0 +1,7 @@ +(ns cognitect.test-runner.protocols) + +(defprotocol TestRunner :extend-via-metadata true + (enable-filtering! [this opts nses]) + (contains-tests? [this opts ns]) + (run-tests [this opts nses]) + (disable-filtering! [this opts nses]))