/
checkables.clj
130 lines (104 loc) · 5.62 KB
/
checkables.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
(ns ^{:doc "Core Midje functions that process expects and report on their results."}
midje.checking.checkables
(:require [such.types :as types]
[such.control-flow :refer [branch-on]]
[midje.checking.core :refer :all]
[midje.config :as config]
[midje.data.nested-facts :as nested-facts]
[midje.data.prerequisite-state :refer :all]
[midje.emission.api :as emit]
[midje.emission.boundaries :as emission-boundary]
[midje.parsing.1-to-explicit-form.parse-background :as parse-background]
[midje.util.exceptions :refer [captured-throwable]]
[midje.util.laziness :refer :all]
[such.sequences :as seq]))
(defn- minimal-failure-map
"Failure maps are created by adding on to parser-created maps"
[type actual existing]
(let [base (assoc existing :type type :actual actual)
table-bindings (nested-facts/table-bindings)]
(if (empty? table-bindings)
base
(assoc base :midje/table-bindings table-bindings))))
(def ^{:private true} has-function-checker? (comp types/extended-fn? :expected-result))
(defn map-record-mismatch-addition [actual expected]
{:notes [(inherently-false-map-to-record-comparison-note actual expected)]})
(defn- check-for-match [actual checkable-map]
(let [expected (:expected-result checkable-map)
[check-result failure-details] (detailed-extended-= actual expected)]
(cond check-result
(emit/pass)
;; checker logic threw an exception
(:thrown failure-details)
(emit/fail (merge (minimal-failure-map :checker-exception
actual checkable-map)
failure-details))
(has-function-checker? checkable-map)
(emit/fail (merge (minimal-failure-map :actual-result-did-not-match-checker
actual checkable-map)
failure-details))
(inherently-false-map-to-record-comparison? actual expected)
(emit/fail (merge (minimal-failure-map :actual-result-did-not-match-expected-value actual checkable-map)
(map-record-mismatch-addition actual expected)))
:else
(emit/fail (assoc (minimal-failure-map :actual-result-did-not-match-expected-value actual checkable-map)
:expected-result expected)))))
(defn- check-for-mismatch [actual checkable-map]
(let [expected (:expected-result checkable-map)
[check-result failure-details] (detailed-extended-= actual expected)]
(cond (inherently-false-map-to-record-comparison? actual expected)
(emit/fail (merge (minimal-failure-map :actual-result-should-not-have-matched-expected-value actual checkable-map)
(map-record-mismatch-addition actual expected)))
;; checker logic threw an exception
(:thrown failure-details)
(emit/fail (merge (minimal-failure-map :checker-exception
actual checkable-map)
failure-details))
(not check-result)
(emit/pass)
(has-function-checker? checkable-map)
(emit/fail (minimal-failure-map :actual-result-should-not-have-matched-checker actual checkable-map))
:else
(emit/fail (minimal-failure-map :actual-result-should-not-have-matched-expected-value actual checkable-map)))))
(defn- check-result [actual checkable-map]
(if (= (:check-expectation checkable-map) :expect-match)
(check-for-match actual checkable-map)
(check-for-mismatch actual checkable-map)))
(defmulti call-count-incorrect? :type)
(defmethod call-count-incorrect? :fake [fake]
(let [method (:times fake)
count @(:call-count-atom fake)]
(branch-on method
#(= % :default) (zero? count)
number? (not= method count)
coll? (not-any? (partial = count) method)
fn? (not (method count)))))
(defmethod call-count-incorrect? :not-called [fake]
(not (zero? @(:call-count-atom fake))))
(defn report-incorrect-call-counts [fakes]
(when-let [failures (seq (for [fake fakes
:when (call-count-incorrect? fake)]
{:actual-count @(:call-count-atom fake)
:expected-count (:times fake)
:expected-call (:call-text-for-failures fake)
:position (:position fake)
:expected-result-form (:call-text-for-failures fake)}))]
(emit/fail {:type :some-prerequisites-were-called-the-wrong-number-of-times
:failures failures
:position (:position (first failures))
:namespace *ns*})))
(defn check-one
"Takes a map describing a single checkable, plus some function-redefinition maps
and checks the checkable, reporting results through the emission interface."
[checkable-map local-fakes]
((config/choice :check-recorder) checkable-map local-fakes)
(let [[data-fakes fn-fakes] (seq/bifurcate :data-fake (parse-background/background-fakes))]
(with-installed-fakes (concat (reverse data-fakes) local-fakes fn-fakes)
(emission-boundary/around-check
(let [actual (try
(eagerly ((:function-under-test checkable-map)))
(catch Throwable ex
(captured-throwable ex)))]
(report-incorrect-call-counts local-fakes)
(check-result actual checkable-map)
:irrelevant-return-value)))))