|
| 1 | +(ns cloverage.coverage-test |
| 2 | + (:require [clojure.test :as t] |
| 3 | + [cloverage.coverage :as cov] |
| 4 | + [cloverage.instrument :as inst] |
| 5 | + [riddley.walk :as rw]) |
| 6 | + (:import java.io.File)) |
| 7 | + |
| 8 | +(defn- denamespace [tree] |
| 9 | + "Helper function to allow backticking w/o namespace interpolation." |
| 10 | + (cond (seq? tree) (map denamespace tree) |
| 11 | + (symbol? tree) (symbol (name tree)) |
| 12 | + :else tree)) |
| 13 | + |
| 14 | +(def sample-file |
| 15 | + "cloverage/sample.clj") |
| 16 | + |
| 17 | +(defn coverage-fixture [f] |
| 18 | + (binding [cov/*covered* (atom []) |
| 19 | + cov/*instrumented-ns* "NO_SUCH_NAMESPACE"] |
| 20 | + (f))) |
| 21 | + |
| 22 | +(t/use-fixtures :each coverage-fixture) |
| 23 | + |
| 24 | +(def output-dir "out") |
| 25 | + |
| 26 | +(defn expand= |
| 27 | + "Check that two expressions are equal modulo recursive macroexpansion." |
| 28 | + [f1 f2] |
| 29 | + (let [e1 (rw/macroexpand-all f1) |
| 30 | + e2 (rw/macroexpand-all f2)] |
| 31 | + (= e1 e2))) |
| 32 | + |
| 33 | +(t/deftest preserves-type-hint |
| 34 | + (t/is (= 'long |
| 35 | + ;; top level type hint always worked, but ones nested in :list did not |
| 36 | + (-> (inst/wrap #'cov/track-coverage 0 '(prn ^long (long 0))) |
| 37 | + rw/macroexpand-all |
| 38 | + (nth 2) ; (do (cloverage/cover 0) (...) |
| 39 | + (nth 1) ; ((do (cloverage/cover 1) prn) (...)) |
| 40 | + (nth 2) ; (do (cloverage/cover 2) (...)) |
| 41 | + meta |
| 42 | + :tag)))) |
| 43 | + |
| 44 | +;; TODO: all test-wrap-X tests should be split in one test that checks |
| 45 | +;; whether wrap works correctly, and one that checks track-coverage. |
| 46 | +(t/deftest test-wrap-primitives |
| 47 | + (t/is (expand= `(cov/capture 0 1) (inst/wrap #'cov/track-coverage 0 1))) |
| 48 | + (t/is (expand= `(cov/capture 1 "foo") (inst/wrap #'cov/track-coverage 0 "foo"))) |
| 49 | + (t/is (expand= `(cov/capture 2 ~'bar) (inst/wrap #'cov/track-coverage 0 'bar))) |
| 50 | + (t/is (expand= `(cov/capture 3 ~'true) (inst/wrap #'cov/track-coverage 0 'true))) |
| 51 | + (t/is (expand= '(1 "foo" bar true) |
| 52 | + (map :form @cov/*covered*)))) |
| 53 | + |
| 54 | +(t/deftest test-wrap-vector |
| 55 | + (t/is (expand= `(cov/capture 0 |
| 56 | + [(cov/capture 1 1) |
| 57 | + (cov/capture 2 "foo") |
| 58 | + (cov/capture 3 ~'bar)]) |
| 59 | + (inst/wrap #'cov/track-coverage 0 '[1 "foo" bar])))) |
| 60 | + |
| 61 | +(t/deftest test-wrap-map |
| 62 | + (let [wrapped (rw/macroexpand-all (inst/wrap #'cov/track-coverage 0 '{:a apple :b banana}))] |
| 63 | + (t/is (or |
| 64 | + (expand= `(cov/capture 0 {(cov/capture 1 :a) (cov/capture 2 ~'apple) |
| 65 | + (cov/capture 3 :b) (cov/capture 4 ~'banana)}) |
| 66 | + wrapped) |
| 67 | + (expand= `(cov/capture 0 {(cov/capture 1 :b) (cov/capture 2 ~'banana) |
| 68 | + (cov/capture 3 :a) (cov/capture 4 ~'apple)}) |
| 69 | + wrapped))))) |
| 70 | + |
| 71 | +(t/deftest test-wrap-list |
| 72 | + (t/is (expand= `(cov/capture 0 ((cov/capture 1 +) (cov/capture 2 1) |
| 73 | + (cov/capture 3 2))) |
| 74 | + (inst/wrap #'cov/track-coverage 0 `(+ 1 2))))) |
| 75 | + |
| 76 | +;; XXX: the order that forms are registered is now from outside in. Bad? |
| 77 | +(t/deftest test-wrap-fn |
| 78 | + (t/is (expand= `(cov/capture 0 (~(symbol "fn") |
| 79 | + [~'a] (cov/capture 1 ~'a))) |
| 80 | + (inst/wrap #'cov/track-coverage 0 |
| 81 | + '(fn [a] a))) |
| 82 | + "Unnamed fn with single overload") |
| 83 | + (t/is (expand= `(cov/capture 2 (~(symbol "fn") |
| 84 | + ([~'a] (cov/capture 3 ~'a)) |
| 85 | + ([~'a ~'b] (cov/capture 4 ~'b)))) |
| 86 | + (inst/wrap #'cov/track-coverage 0 '(fn ([a] a) ([a b] b)))) |
| 87 | + "Unnamed fn with multiple overloads") |
| 88 | + (t/is (expand= `(cov/capture 5 (~(symbol "fn") ~'foo |
| 89 | + [~'a] (cov/capture 6 ~'a))) |
| 90 | + (inst/wrap #'cov/track-coverage 0 |
| 91 | + '(fn foo [a] a))) |
| 92 | + "Named fn with single overload") |
| 93 | + (t/is (expand= `(cov/capture 7 (~(symbol "fn") ~'foo |
| 94 | + ([~'a] (cov/capture 8 ~'a)) |
| 95 | + ([~'a ~'b] (cov/capture 9 ~'b)))) |
| 96 | + (inst/wrap #'cov/track-coverage 0 '(fn foo ([a] a) ([a b] b)))) |
| 97 | + "Named fn with multiple overloads")) |
| 98 | + |
| 99 | +(t/deftest test-wrap-def |
| 100 | + (t/is (expand= `(cov/capture 0 (~(symbol "def") ~'foobar)) |
| 101 | + (inst/wrap #'cov/track-coverage 0 '(def foobar)))) |
| 102 | + (t/is (expand= `(cov/capture 1 (~(symbol "def") ~'foobar (cov/capture 2 1))) |
| 103 | + (inst/wrap #'cov/track-coverage 0 '(def foobar 1))))) |
| 104 | + |
| 105 | +(t/deftest test-wrap-let |
| 106 | + (t/is (expand= `(cov/capture 0 (~(symbol "let") [])) |
| 107 | + (inst/wrap #'cov/track-coverage 0 '(let [])))) |
| 108 | + (t/is (expand= `(cov/capture 1 (~(symbol "let") [~'a (cov/capture 2 1)])) |
| 109 | + (inst/wrap #'cov/track-coverage 0 '(let [a 1])))) |
| 110 | + (t/is (expand= `(cov/capture 3 (~(symbol "let") [~'a (cov/capture 4 1) |
| 111 | + ~'b (cov/capture 5 2)])) |
| 112 | + (inst/wrap #'cov/track-coverage 0 '(let [a 1 b 2])))) |
| 113 | + (t/is (expand= `(cov/capture 6 (~(symbol "let") [~'a (cov/capture 7 1) |
| 114 | + ~'b (cov/capture 8 2)] |
| 115 | + (cov/capture 9 ~'a))) |
| 116 | + (inst/wrap #'cov/track-coverage 0 '(let [a 1 b 2] a))))) |
| 117 | + |
| 118 | +(t/deftest test-wrap-cond |
| 119 | + (t/is (expand= `(cov/capture 0 nil) |
| 120 | + (inst/wrap #'cov/track-coverage 0 '(cond))))) |
| 121 | + |
| 122 | +(t/deftest test-wrap-overloads |
| 123 | + (t/is (expand= `(([~'a] (cov/capture 0 ~'a)) |
| 124 | + ([~'a ~'b] (cov/capture 1 ~'a) (cov/capture 2 ~'b))) |
| 125 | + (inst/wrap-overloads #'cov/track-coverage 0 '(([a] a) |
| 126 | + ([a b] a b))))) |
| 127 | + (t/is (expand= `([~'a] (cov/capture 3 ~'a)) |
| 128 | + (inst/wrap-overloads #'cov/track-coverage 0 '([a] a))))) |
| 129 | + |
| 130 | +(t/deftest test-wrap-for |
| 131 | + (t/is (not (nil? (inst/wrap #'cov/track-coverage 0 '(for [i (range 5)] i)))))) |
| 132 | + |
| 133 | +(t/deftest test-wrap-str |
| 134 | + (inst/wrap #'cov/track-coverage 0 |
| 135 | + '(defn -main [& args] |
| 136 | + (doseq [file (file-seq ".")] |
| 137 | + (println "File is" file))))) |
| 138 | + |
| 139 | +(t/deftest test-eval-atomic |
| 140 | + (t/is (= 1 (eval (inst/wrap #'cov/track-coverage 0 1)))) |
| 141 | + (t/is (= :foo (eval (inst/wrap #'cov/track-coverage 0 :foo)))) |
| 142 | + (t/is (= "foo" (eval (inst/wrap #'cov/track-coverage 0 "foo")))) |
| 143 | + (t/is (= + (eval (inst/wrap #'cov/track-coverage 0 '+))))) |
| 144 | + |
| 145 | +(t/deftest test-eval-expr |
| 146 | + (t/is (= 5 (eval (inst/wrap #'cov/track-coverage 0 '(+ 2 3)))))) |
| 147 | + |
| 148 | +(t/deftest test-eval-do |
| 149 | + (t/is (= 4 (eval (inst/wrap #'cov/track-coverage 0 '(do 4)))))) |
| 150 | + |
| 151 | +(t/deftest test-eval-ns |
| 152 | + (eval (inst/wrap #'cov/track-coverage 0 '(ns foo.bar))) |
| 153 | + (t/is (= (count (filter :tracked @cov/*covered*)) (count (filter :covered @cov/*covered*))))) |
| 154 | + |
| 155 | +(t/deftest test-eval-case |
| 156 | + (doseq [x '[a b 3 #{3} fallthrough]] |
| 157 | + (t/is (= (str x) (eval (inst/wrap #'cov/track-coverage 0 |
| 158 | + (denamespace `(case '~x |
| 159 | + a "a" |
| 160 | + b "b" |
| 161 | + 3 "3" |
| 162 | + #{3} "#{3}" |
| 163 | + "fallthrough"))))))) |
| 164 | + (t/is (thrown? IllegalArgumentException |
| 165 | + (eval (inst/wrap #'cov/track-coverage 0 (case 1)))))) |
| 166 | + |
| 167 | +;; non-local and public to easily access in eval |
| 168 | +;; tests are not ran from the namespace they're defined in |
| 169 | +(def ran-finally (atom nil)) |
| 170 | +(t/deftest test-eval-try |
| 171 | + (t/is (= :caught |
| 172 | + (eval (inst/wrap #'cov/track-coverage 0 |
| 173 | + '(try (swap! cloverage.coverage-test/ran-finally (constantly false)) |
| 174 | + (throw (Exception. "try-catch test")) |
| 175 | + (catch Exception e |
| 176 | + :caught) |
| 177 | + (finally |
| 178 | + (swap! cloverage.coverage-test/ran-finally (constantly true)))))))) |
| 179 | + (= true @ran-finally)) |
| 180 | + |
| 181 | +(defn find-form [cov form] |
| 182 | + (some #(and (= form (:form %)) %) cov)) |
| 183 | + |
| 184 | +(t/deftest test-instrument-gets-lines |
| 185 | + (inst/instrument #'cov/track-coverage |
| 186 | + 'cloverage.sample) |
| 187 | + (let [cov @cov/*covered* |
| 188 | + found (find-form cov '(+ 1 2))] |
| 189 | + #_(with-out-writer "out/foo" |
| 190 | + (doseq [form-info cov] |
| 191 | + (println form-info))) |
| 192 | + #_(println "Form is" ) |
| 193 | + (t/is found) |
| 194 | + (t/is (:line found)) |
| 195 | + (t/is (find-form cov '(inc (m c 0)))))) |
| 196 | + |
| 197 | +(t/deftest test-wrap-new |
| 198 | + (t/is (expand= `(cov/capture 0 (~'new java.io.File (cov/capture 1 "foo/bar"))) |
| 199 | + (inst/wrap #'cov/track-coverage 0 '(new java.io.File "foo/bar"))))) |
| 200 | + |
| 201 | +(defn- compare-colls [& colls] |
| 202 | + "Given N collections compares them. Returns true if collections have same |
| 203 | + elements (order does not matter)." |
| 204 | + (apply = (map frequencies colls))) |
| 205 | + |
| 206 | +(t/deftest test-find-nses |
| 207 | + (t/testing "empty sequence is returned when neither paths nor regexs are provided" |
| 208 | + (t/is (empty? (cov/find-nses nil [])))) |
| 209 | + (t/testing "all namespaces in a directory get returned when only path is provided" |
| 210 | + (t/is (compare-colls (cov/find-nses "test/cloverage/sample" []) |
| 211 | + ["cloverage.sample.dummy-sample" |
| 212 | + "cloverage.sample.read-eval-sample" |
| 213 | + "cloverage.sample.multibyte-sample"]))) |
| 214 | + (t/testing "only matching namespaces (from classpath) are returned when only |
| 215 | + regex patterns are provided:" |
| 216 | + (t/testing "single pattern case" |
| 217 | + (t/is (= (cov/find-nses nil [#"^cloverage\.sample\.read.*$"]) |
| 218 | + ["cloverage.sample.read-eval-sample"]))) |
| 219 | + (t/testing "multiple patterns case" |
| 220 | + (t/is (compare-colls (cov/find-nses nil [#"^cloverage\.sample\.read.*$" |
| 221 | + #"^cloverage\..*coverage.*$"]) |
| 222 | + ["cloverage.sample.read-eval-sample" |
| 223 | + "cloverage.coverage-test" |
| 224 | + "cloverage.coverage"])))) |
| 225 | + (t/testing "only matching namespaces from a directory are returned when both path |
| 226 | + and patterns are provided" |
| 227 | + (t/is (= (cov/find-nses "test/cloverage/sample" [#".*dummy.*"]) |
| 228 | + ["cloverage.sample.dummy-sample"])))) |
| 229 | + |
| 230 | +(t/deftest test-main |
| 231 | + (binding [cloverage.coverage/*exit-after-test* false] |
| 232 | + (t/is (= |
| 233 | + (cloverage.coverage/-main |
| 234 | + "-o" "out" |
| 235 | + "--text" "--html" "--raw" "--emma-xml" "--coveralls" |
| 236 | + "-x" "cloverage.sample" |
| 237 | + "cloverage.sample") |
| 238 | + 0)))) |
0 commit comments