-
Notifications
You must be signed in to change notification settings - Fork 4
/
script.cljc
154 lines (132 loc) · 4.91 KB
/
script.cljc
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
(ns darkleaf.effect.script
(:refer-clojure :exclude [test])
(:require
[clojure.test :as t]
[clojure.string :as str]
[clojure.data :as data]
[darkleaf.effect.internal :as i]))
(defprotocol Matcher
:extend-via-metadata true
(matcher-report [matcher actual]))
(defn- with-exceptions [continuation]
(when (some? continuation)
(fn [coeffect]
(try
(let [[effect continuation] (continuation coeffect)
continuation (with-exceptions continuation)]
[effect continuation])
(catch #?(:clj RuntimeException, :cljs js/Error) ex
[ex nil])))))
(defn- add-message-tag [message tag]
(->> [tag message]
(remove nil?)
(str/join " / ")))
(defn- test-first-item [{:keys [report continuation]} {:keys [args]}]
(let [[effect continuation] (continuation args)]
{:report report
:actual-effect effect
:continuation continuation}))
(defn- next-step [{:keys [report continuation]} coeffect]
(let [[actual-effect continuation] (continuation coeffect)]
{:report report
:actual-effect actual-effect
:continuation continuation}))
(defn- test-middle-item [{:keys [report actual-effect continuation] :as ctx}
{:keys [effect coeffect tag] :as item}]
(i/<<-
(if (not= :pass (:type report))
{:report report})
(if (nil? continuation)
{:report {:type :fail
:expected effect
:actual actual-effect
:message (add-message-tag "Unexpected return. An effect is expected." tag)}})
(if (contains? item :effect)
(if-some [report (matcher-report effect actual-effect)]
{:report (assoc report
:type :fail
:message (add-message-tag "Wrong effect" tag))}
(next-step ctx coeffect)))
{:report {:type :fail
:expected '(contains? script-item :effect)
:actual item
:message (add-message-tag "Wrong script item" tag)}}))
(defn- test-middle-items [ctx items]
(reduce test-middle-item ctx items))
(defn- test-last-item [{:keys [report actual-effect continuation]}
{:keys [return final-effect thrown tag] :as item}]
(i/<<-
(if (not= :pass (:type report))
{:report report})
(if (contains? item :final-effect)
(if-some [report (matcher-report final-effect actual-effect)]
{:report (assoc report
:type :fail
:message (add-message-tag "Wrong final effect" tag))}
{:report report}))
(if (contains? item :thrown)
(if-some [report (matcher-report thrown actual-effect)]
{:report (assoc report
:type :fail
:message (add-message-tag "Wrong exception" tag))}
{:report report}))
(if (some? continuation)
{:report {:type :fail
:expected nil
:actual actual-effect
:message (add-message-tag "Extra effect" tag)}})
(if (contains? item :return)
(if-some [report (matcher-report return actual-effect)]
{:report (assoc report
:type :fail
:message (add-message-tag "Wrong return" tag))}
{:report report}))
{:report {:type :fail
:expected '(or (contains? script-item :return)
(contains? script-item :final-effect)
(contains? script-item :thrown))
:actual item
:message (add-message-tag "Wrong script item" tag)}}))
(defn test* [continuation script]
{:pre [(<= 2 (count script))]}
(let [first-item (first script)
middle-items (-> script rest butlast)
last-item (last script)
continuation (-> continuation
(with-exceptions))]
(-> {:continuation continuation, :report {:type :pass}}
(test-first-item first-item)
(test-middle-items middle-items)
(test-last-item last-item)
:report)))
(defn test [continuation script]
(-> (test* continuation script)
(t/do-report)))
(defn- ex->data [ex]
{:type (type ex)
:message (ex-message ex)
:data (ex-data ex)})
(extend-protocol Matcher
nil
(matcher-report [_ actual]
(if-not (nil? actual)
{:expected nil
:actual actual}))
#?(:clj Object :cljs default)
(matcher-report [matcher actual]
(when (not= matcher actual)
{:expected matcher
:actual actual
:diffs [[actual (data/diff matcher actual)]]}))
#?(:clj Throwable, :cljs js/Error)
(matcher-report [matcher actual]
(i/<<-
(if-not (i/throwable? actual)
{:expected matcher
:actual actual})
(let [matcher-data (ex->data matcher)
actual-data (ex->data actual)])
(if-not (= matcher-data actual-data)
{:expected matcher
:actual actual
:diffs [[actual (data/diff matcher-data actual-data)]]}))))