|
1 | 1 | (ns manifold.test-utils
|
2 | 2 | (:require
|
3 |
| - [criterium.core :as c])) |
| 3 | + [clojure.test :as test] |
| 4 | + [criterium.core :as c] |
| 5 | + [manifold.debug :as debug])) |
4 | 6 |
|
5 | 7 | (defmacro long-bench [name & body]
|
6 | 8 | `(do
|
|
16 | 18 | (do ~@body)
|
17 | 19 | :reduce-with #(and %1 %2))))
|
18 | 20 |
|
| 21 | +(defn report-dropped-errors! [dropped-errors] |
| 22 | + (when (pos? dropped-errors) |
| 23 | + ;; We include the assertion here within the `when` form so that we don't add a mystery assertion |
| 24 | + ;; to every passing test (which is the common case). |
| 25 | + (test/is (zero? dropped-errors) |
| 26 | + "Dropped errors detected! See log output for details."))) |
| 27 | + |
| 28 | +(defn instrument-test-fn-with-dropped-error-detection [tf] |
| 29 | + (if (or (::detect-dropped-errors? tf) |
| 30 | + (:ignore-dropped-errors tf)) |
| 31 | + tf |
| 32 | + (with-meta |
| 33 | + (fn [] |
| 34 | + (binding [debug/*leak-aware-deferred-rate* 1] |
| 35 | + (debug/with-dropped-error-detection tf report-dropped-errors!))) |
| 36 | + {::detect-dropped-errors? true}))) |
| 37 | + |
| 38 | +(defn instrument-tests-with-dropped-error-detection! |
| 39 | + "Instrument all tests in the current namespace dropped error detection by wrapping them in |
| 40 | + `manifold.debug/with-dropped-error-detection`. If dropped errors are detected, a corresponding (failing) |
| 41 | + assertion is injected into the test and the leak reports are logged at level `error`. |
| 42 | +
|
| 43 | + Usually placed at the end of a test namespace. |
| 44 | +
|
| 45 | + Add `:ignore-dropped-errors` to a test var's metadata to skip it from being instrumented. |
| 46 | +
|
| 47 | + Note that this is intentionally not implemented as a fixture since there is no clean way to make a |
| 48 | + test fail from within a fixture: Neither a failing assertion nor throwing an exception will |
| 49 | + preserve which particular test caused it. See |
| 50 | + e.g. https://github.com/technomancy/leiningen/issues/2694 for an example of this." |
| 51 | + [] |
| 52 | + (->> (ns-interns *ns*) |
| 53 | + vals |
| 54 | + (filter (comp :test meta)) |
| 55 | + (run! (fn [tv] |
| 56 | + (when-not (:ignore-dropped-errors (meta tv)) |
| 57 | + (alter-meta! tv update :test instrument-test-fn-with-dropped-error-detection)))))) |
| 58 | + |
| 59 | +(defmacro expect-dropped-errors |
| 60 | + "Expect n number of dropped errors after executing body in the form of a test assertion. |
| 61 | +
|
| 62 | + Add `:ignore-dropped-errors` to the a test's metadata to be able to use this macro in an |
| 63 | + instrumented namespace (see `instrument-tests-with-dropped-error-detection!`)." |
| 64 | + [n & body] |
| 65 | + `(debug/with-dropped-error-detection |
| 66 | + (fn [] ~@body) |
| 67 | + (fn [n#] |
| 68 | + (test/is (= ~n n#) "Expected number of dropped errors doesn't match detected number of dropped errors.")))) |
0 commit comments