1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
5
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
6
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
7
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
8
|
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
9
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
10
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
11
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
12
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
13
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
14
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
15
|
|
|
* |
16
|
|
|
* The software is based on the Axon Framework project which is |
17
|
|
|
* licensed under the Apache 2.0 license. For more information on the Axon Framework |
18
|
|
|
* see <http://www.axonframework.org/>. |
19
|
|
|
* |
20
|
|
|
* This software consists of voluntary contributions made by many individuals |
21
|
|
|
* and is licensed under the MIT license. For more information, see |
22
|
|
|
* <http://www.governor-framework.org/>. |
23
|
|
|
*/ |
24
|
|
|
|
25
|
|
|
namespace Governor\Framework\Test; |
26
|
|
|
|
27
|
|
|
use Hamcrest\Description; |
28
|
|
|
use Governor\Framework\Domain\EventMessageInterface; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* The reporter generates extensive human readable reports of what the expected outcome of a test was, and what the |
32
|
|
|
* actual results were. |
33
|
|
|
* |
34
|
|
|
* @author "David Kalosi" <[email protected]> |
35
|
|
|
* @license <a href="http://www.opensource.org/licenses/mit-license.php">MIT License</a> |
36
|
|
|
*/ |
37
|
|
|
class Reporter |
38
|
|
|
{ |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Report a failed assertion due to a difference in the stored versus the published events. |
42
|
|
|
* |
43
|
|
|
* @param array $storedEvents The events that were stored |
44
|
|
|
* @param array $publishedEvents The events that were published |
45
|
|
|
* @param \Exception $probableCause An exception that might be the cause of the failure |
46
|
|
|
* @throws GovernorAssertionError |
47
|
|
|
*/ |
48
|
|
View Code Duplication |
public function reportDifferenceInStoredVsPublished(array $storedEvents, |
|
|
|
|
49
|
|
|
array $publishedEvents, \Exception $probableCause = null) |
50
|
|
|
{ |
51
|
|
|
$str = "The stored events do not match the published events."; |
52
|
|
|
$str .= $this->appendEventOverview($storedEvents, $publishedEvents, |
53
|
|
|
"Stored events", "Published events"); |
54
|
|
|
$str .= $this->appendProbableCause($probableCause); |
55
|
|
|
|
56
|
|
|
throw new GovernorAssertionError($str); |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Report an error in the ordering or count of events. This is typically a difference that can be shown to the user |
61
|
|
|
* by enumerating the expected and actual events |
62
|
|
|
* |
63
|
|
|
* @param array $actualEvents The events that were found |
64
|
|
|
* @param array $expectedEvents The events that were expected |
65
|
|
|
* @param \Exception $probableCause An optional exception that might be the reason for wrong events |
66
|
|
|
* @throws GovernorAssertionError |
67
|
|
|
*/ |
68
|
|
View Code Duplication |
public function reportWrongEvent(array $actualEvents, array $expectedEvents, |
|
|
|
|
69
|
|
|
\Exception $probableCause = null) |
70
|
|
|
{ |
71
|
|
|
$str = "The published events do not match the expected events"; |
72
|
|
|
$str .= $this->appendEventOverview($expectedEvents, $actualEvents, |
73
|
|
|
"Expected", "Actual"); |
74
|
|
|
$str .= $this->appendProbableCause($probableCause); |
75
|
|
|
|
76
|
|
|
throw new GovernorAssertionError($str); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Report an error in the ordering or count of events. This is typically a difference that can be shown to the user |
81
|
|
|
* by enumerating the expected and actual events |
82
|
|
|
* |
83
|
|
|
* @param array $actualEvents The events that were found |
84
|
|
|
* @param string $expectation A Description of what was expected |
85
|
|
|
* @param \Exception $probableCause An optional exception that might be the reason for wrong events |
86
|
|
|
* @throws GovernorAssertionError |
87
|
|
|
*/ |
88
|
|
|
public function reportWrongEventDescription(array $actualEvents, $expectation, |
89
|
|
|
\Exception $probableCause = null) |
90
|
|
|
{ |
91
|
|
|
$str = "The published events do not match the expected events."; |
92
|
|
|
$str .= "Expected :\n"; |
93
|
|
|
$str .= $expectation . "\n"; |
94
|
|
|
$str .= "But got"; |
95
|
|
|
|
96
|
|
|
$str .= empty($actualEvents) ? " none" : ":"; |
97
|
|
|
|
98
|
|
|
foreach ($actualEvents as $event) { |
99
|
|
|
$str .= "\n" . get_class($event); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
$this->appendProbableCause($probableCause); |
103
|
|
|
|
104
|
|
|
throw new GovernorAssertionError($str); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Reports an error due to an unexpected exception. This means a return value was expected, but an exception was |
109
|
|
|
* thrown by the command handler |
110
|
|
|
* |
111
|
|
|
* @param \Exception $actualException The actual exception |
112
|
|
|
* @param Description $expectation A text describing what was expected |
113
|
|
|
* @throws GovernorAssertionError |
114
|
|
|
*/ |
115
|
|
View Code Duplication |
public function reportUnexpectedException(\Exception $actualException, |
|
|
|
|
116
|
|
|
Description $expectation) |
117
|
|
|
{ |
118
|
|
|
$str = "The command handler threw an unexpected exception"; |
119
|
|
|
$str .= PHP_EOL . PHP_EOL; |
120
|
|
|
$str .= "Expected <" . $expectation . "> but got <exception of type ["; |
121
|
|
|
$str .= get_class($actualException) . "]>. Stack trace follows:" . PHP_EOL; |
122
|
|
|
$str .= $this->writeStackTrace($actualException) . PHP_EOL; |
123
|
|
|
|
124
|
|
|
throw new GovernorAssertionError($str); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Reports an error due to a wrong return value. |
129
|
|
|
* |
130
|
|
|
* @param mixed $actualReturnValue The actual return value |
131
|
|
|
* @param Description $expectation A description of the expected value |
132
|
|
|
* @throws GovernorAssertionError |
133
|
|
|
*/ |
134
|
|
View Code Duplication |
public function reportWrongResult($actualReturnValue, |
|
|
|
|
135
|
|
|
Description $expectation) |
136
|
|
|
{ |
137
|
|
|
$str = "The command handler returned an unexpected value"; |
138
|
|
|
$str .= PHP_EOL . PHP_EOL; |
139
|
|
|
$str .= "Expected <" . $expectation . "> but got <"; |
140
|
|
|
$str .= $this->describe($actualReturnValue) . ">"; |
141
|
|
|
$str .= PHP_EOL; |
142
|
|
|
|
143
|
|
|
throw new GovernorAssertionError($str); |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Report an error due to an unexpected return value, while an exception was expected. |
148
|
|
|
* |
149
|
|
|
* @param mixed $actualReturnValue The actual return value |
150
|
|
|
* @param Description $description A description describing the expected value |
151
|
|
|
* @throws GovernorAssertionError |
152
|
|
|
*/ |
153
|
|
View Code Duplication |
public function reportUnexpectedReturnValue($actualReturnValue, |
|
|
|
|
154
|
|
|
Description $description) |
155
|
|
|
{ |
156
|
|
|
$str = "The command handler returned normally, but an exception was expected"; |
157
|
|
|
$str .= PHP_EOL . PHP_EOL; |
158
|
|
|
$str .= "Expected <" . $description . "> but returned with <" . $actualReturnValue; |
159
|
|
|
$str .= $this->describe($actualReturnValue) . ">"; |
160
|
|
|
$str .= PHP_EOL; |
161
|
|
|
|
162
|
|
|
throw new GovernorAssertionError($str); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* Report an error due to a an exception of an unexpected type. |
167
|
|
|
* |
168
|
|
|
* @param \Exception $actualException The actual exception |
169
|
|
|
* @param Description $description A description describing the expected value |
170
|
|
|
* @throws GovernorAssertionError |
171
|
|
|
*/ |
172
|
|
View Code Duplication |
public function reportWrongException(\Exception $actualException, |
|
|
|
|
173
|
|
|
Description $description) |
174
|
|
|
{ |
175
|
|
|
$str = "The command handler threw an exception, but not of the expected type"; |
176
|
|
|
$str .= PHP_EOL . PHP_EOL; |
177
|
|
|
$str .= "Expected <" . $description . "> but got <exception of type ["; |
178
|
|
|
$str .= get_class($actualException) . "]>. Stacktrace follows: "; |
179
|
|
|
$str .= PHP_EOL . $this->writeStackTrace($actualException) . PHP_EOL; |
180
|
|
|
|
181
|
|
|
throw new GovernorAssertionError($str); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Report an error due to a difference in on of the fields of an event. |
186
|
|
|
* |
187
|
|
|
* @param string $eventType The (runtime) type of event the difference was found in |
188
|
|
|
* @param string $field The field that contains the difference |
189
|
|
|
* @param mixed $actual The actual value of the field |
190
|
|
|
* @param mixed $expected The expected value of the field |
191
|
|
|
* @throws GovernorAssertionError |
192
|
|
|
*/ |
193
|
|
|
public function reportDifferentEventContents($eventType, $field, $actual, |
194
|
|
|
$expected) |
195
|
|
|
{ |
196
|
|
|
$str = "One of the events contained different values than expected"; |
197
|
|
|
$str .= PHP_EOL . PHP_EOL; |
198
|
|
|
$str .= "In an event of type [" . $eventType . "], the property ["; |
199
|
|
|
$str .= $field . "] "; |
200
|
|
|
|
201
|
|
|
/* if (!strcmp($eventType.equals(field.getDeclaringClass())) { |
|
|
|
|
202
|
|
|
sb.append("(declared in [") |
203
|
|
|
.append(field.getDeclaringClass().getSimpleName()) |
204
|
|
|
.append("]) "); |
205
|
|
|
} */ |
206
|
|
|
|
207
|
|
|
$str .= "was not as expected." . PHP_EOL; |
208
|
|
|
$str .= "Expected <" . |
209
|
|
|
$this->nullSafeToString($expected) . |
210
|
|
|
"> but got <" . |
211
|
|
|
$this->nullSafeToString($actual) . |
212
|
|
|
">" . |
213
|
|
|
PHP_EOL; |
214
|
|
|
|
215
|
|
|
throw new GovernorAssertionError($str); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
private function appendProbableCause(\Exception $probableCause = null) |
219
|
|
|
{ |
220
|
|
|
$str = ""; |
221
|
|
|
|
222
|
|
|
if (null !== $probableCause) { |
223
|
|
|
$str .= PHP_EOL; |
224
|
|
|
$str .= "A probable cause for the wrong chain of events is an " |
225
|
|
|
. "exception that occurred while handling the command."; |
226
|
|
|
$str .= PHP_EOL; |
227
|
|
|
$str .= $probableCause->getTraceAsString(); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
return $str; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
private function writeStackTrace(\Exception $actualException) |
234
|
|
|
{ |
235
|
|
|
return $actualException->getTraceAsString(); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
private function nullSafeToString($value) |
239
|
|
|
{ |
240
|
|
|
if (null === $value) { |
241
|
|
|
return "<null>"; |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
if (is_array($value)) { |
245
|
|
|
return print_r($value, true); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
return $value; |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
private function describe($value) |
252
|
|
|
{ |
253
|
|
|
if (null === $value) { |
254
|
|
|
return "null"; |
255
|
|
|
} else { |
256
|
|
|
return $value; |
257
|
|
|
} |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
private function appendEventOverview(array $leftColumnEvents, |
261
|
|
|
array $rightColumnEvents, $leftColumnName, $rightColumnName) |
262
|
|
|
{ |
263
|
|
|
$actualTypes = array(); |
264
|
|
|
$expectedTypes = array(); |
265
|
|
|
$largestExpectedSize = strlen($leftColumnName); |
266
|
|
|
|
267
|
|
|
foreach ($rightColumnEvents as $event) { |
268
|
|
|
$actualTypes[] = $this->payloadContentType($event); |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
foreach ($leftColumnEvents as $event) { |
272
|
|
|
$simpleName = $this->payloadContentType($event); |
273
|
|
|
if (strlen($simpleName) > $largestExpectedSize) { |
274
|
|
|
$largestExpectedSize = strlen($simpleName); |
275
|
|
|
} |
276
|
|
|
$expectedTypes[] = $simpleName; |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
$str = PHP_EOL . PHP_EOL; |
280
|
|
|
$str .= $leftColumnName . $this->pad(strlen($leftColumnName), |
281
|
|
|
$largestExpectedSize, " "); |
282
|
|
|
$str .= " | " . $rightColumnName . PHP_EOL; |
283
|
|
|
$str .= $this->pad(0, $largestExpectedSize, "-") . "--|--"; |
284
|
|
|
$str .= $this->pad(0, $largestExpectedSize, "-") . PHP_EOL; |
285
|
|
|
|
286
|
|
|
$actualIterator = new \ArrayIterator($actualTypes); |
287
|
|
|
$expectedIterator = new \ArrayIterator($expectedTypes); |
288
|
|
|
|
289
|
|
|
while ($actualIterator->valid() || $expectedIterator->valid()) { |
290
|
|
|
$expected = ""; |
291
|
|
|
$difference = false; |
292
|
|
|
|
293
|
|
|
if (null !== $expectedIterator->current()) { |
294
|
|
|
$expected = $expectedIterator->current(); |
295
|
|
|
$str .= $expected; |
296
|
|
|
$str .= $this->pad(strlen($expected), $largestExpectedSize, " "); |
297
|
|
|
} else { |
298
|
|
|
$str .= $this->pad(0, $largestExpectedSize, " "); |
299
|
|
|
$difference = true; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
if (null !== $actualIterator->current()) { |
303
|
|
|
$actual = $actualIterator->current(); |
304
|
|
|
$difference = $difference || !strcmp($expected, $actual) === 0; |
305
|
|
|
|
306
|
|
|
if ($difference) { |
307
|
|
|
$str .= " <|> "; |
308
|
|
|
} else { |
309
|
|
|
$str .= " | "; |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
$str .= $actual; |
313
|
|
|
} else { |
314
|
|
|
$str .= " <|> "; |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
$str .= PHP_EOL; |
318
|
|
|
|
319
|
|
|
$actualIterator->next(); |
320
|
|
|
$expectedIterator->next(); |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
return $str; |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
private function payloadContentType($event) |
327
|
|
|
{ |
328
|
|
|
if ($event instanceof EventMessageInterface) { |
329
|
|
|
return $event->getPayloadType(); |
330
|
|
|
} else { |
331
|
|
|
return get_class($event); |
332
|
|
|
} |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
private function pad($currentLength, $targetLength, $character) |
336
|
|
|
{ |
337
|
|
|
$str = ""; |
338
|
|
|
for ($t = $currentLength; $t < $targetLength; $t++) { |
339
|
|
|
$str .= $character; |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
return $str; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
} |
346
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.